e-taxes settings is single doctype now

This commit is contained in:
Ali 2026-03-03 16:52:15 +04:00
parent d78781d9b8
commit 0b46271628
1 changed files with 38 additions and 31 deletions

View File

@ -2,10 +2,10 @@
Migration utilities for E-Taxes Settings Single doctype conversion. Migration utilities for E-Taxes Settings Single doctype conversion.
backup_etaxes_settings() is called by the before_migrate hook at that point backup_etaxes_settings() is called by the before_migrate hook at that point
the doctype is still a regular doctype, so frappe.db works normally. the doctype is still a regular doctype and ORM works normally.
restore_etaxes_settings() is called by the patch at that point the doctype restore_etaxes_settings() is called by the patch at that point the doctype
is already Single, so we read from the backup file and write via raw SQL. is already Single, so we use raw SQL and restore everything from the backup file.
""" """
import json import json
@ -26,13 +26,14 @@ SCALAR_FIELDS = [
"default_payment_terms", "default_payment_terms",
] ]
CHILD_TABLES = [ # SQL table name → parentfield name in E-Taxes Settings
"tabE-Taxes Item Mapping", CHILD_TABLES = {
"tabE-Taxes Customer Mappings", "tabE-Taxes Item Mapping": "item_mappings",
"tabE-Taxes Supplier Mappings", "tabE-Taxes Customer Mappings": "customer_mappings",
"tabE-Taxes Unit Mapping", "tabE-Taxes Supplier Mappings": "supplier_mappings",
"tabE-Taxes VAT Account Mapping", "tabE-Taxes Unit Mapping": "unit_mappings",
] "tabE-Taxes VAT Account Mapping": "vat_account_mappings",
}
def _backup_path(): def _backup_path():
@ -41,14 +42,13 @@ def _backup_path():
def backup_etaxes_settings(): def backup_etaxes_settings():
""" """
Called before_migrate. Called before_migrate (before schema sync).
Dumps the active E-Taxes Settings record (scalar fields + child rows) to a JSON file. Dumps scalar fields + all child table rows to a JSON file.
""" """
if not frappe.db.table_exists("tabE-Taxes Settings"): if not frappe.db.table_exists("tabE-Taxes Settings"):
return return
# Find the best record using raw SQL — ORM still works normally here, # Find the best old record — ORM still works normally here
# but we avoid referencing is_default/is_active which may have been dropped.
rows = None rows = None
for query in [ for query in [
"SELECT * FROM `tabE-Taxes Settings` WHERE name != 'E-Taxes Settings' ORDER BY modified DESC LIMIT 1", "SELECT * FROM `tabE-Taxes Settings` WHERE name != 'E-Taxes Settings' ORDER BY modified DESC LIMIT 1",
@ -73,6 +73,7 @@ def backup_etaxes_settings():
"children": {}, "children": {},
} }
# Save ALL columns of each child table row so we can re-insert them if needed
for table in CHILD_TABLES: for table in CHILD_TABLES:
if frappe.db.table_exists(table): if frappe.db.table_exists(table):
child_rows = frappe.db.sql( child_rows = frappe.db.sql(
@ -89,8 +90,8 @@ def backup_etaxes_settings():
def restore_etaxes_settings(): def restore_etaxes_settings():
""" """
Called from the migration patch (after schema sync). Called from the migration patch (after schema sync).
Reads the backup file, writes scalar fields into tabSingles, Reads backup file writes scalar fields to tabSingles
re-parents child rows, then deletes the file. deletes + re-inserts child table rows deletes the file.
""" """
path = _backup_path() path = _backup_path()
@ -100,7 +101,7 @@ def restore_etaxes_settings():
with open(path, encoding="utf-8") as f: with open(path, encoding="utf-8") as f:
backup = json.load(f) backup = json.load(f)
# Write scalar fields into tabSingles # --- Scalar fields → tabSingles ---
for field, value in backup.get("scalar", {}).items(): for field, value in backup.get("scalar", {}).items():
if value is None: if value is None:
continue continue
@ -113,23 +114,29 @@ def restore_etaxes_settings():
(field, str(value)), (field, str(value)),
) )
# Re-parent child rows # --- Child tables: delete existing rows, then re-insert from backup ---
old_name = backup.get("old_name") for table, rows in backup.get("children", {}).items():
if old_name: if not frappe.db.table_exists(table) or not rows:
for table in CHILD_TABLES: continue
if frappe.db.table_exists(table):
# Remove any rows already parented to the Single (e.g., from a broken previous run)
frappe.db.sql( frappe.db.sql(
f""" f"DELETE FROM `{table}` WHERE parent = 'E-Taxes Settings'"
UPDATE `{table}` )
SET parent = 'E-Taxes Settings',
parenttype = 'E-Taxes Settings' for row in rows:
WHERE parent = %s row["parent"] = "E-Taxes Settings"
""", row["parenttype"] = "E-Taxes Settings"
(old_name,),
columns = ", ".join(f"`{col}`" for col in row)
placeholders = ", ".join(["%s"] * len(row))
frappe.db.sql(
f"INSERT INTO `{table}` ({columns}) VALUES ({placeholders})",
list(row.values()),
) )
frappe.db.commit() frappe.db.commit()
frappe.clear_cache(doctype="E-Taxes Settings") frappe.clear_cache(doctype="E-Taxes Settings")
# Clean up — file is no longer needed
os.remove(path) os.remove(path)