143 lines
3.7 KiB
Python
143 lines
3.7 KiB
Python
"""
|
|
Migration utilities for E-Taxes Settings → Single doctype conversion.
|
|
|
|
backup_etaxes_settings() is called by the before_migrate hook — at that point
|
|
the doctype is still a regular doctype and ORM works normally.
|
|
|
|
restore_etaxes_settings() is called by the patch — at that point the doctype
|
|
is already Single, so we use raw SQL and restore everything from the backup file.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
|
|
import frappe
|
|
|
|
BACKUP_FILENAME = "etaxes_settings_migration.json"
|
|
|
|
SCALAR_FIELDS = [
|
|
"similarity_threshold",
|
|
"consider_azeri_chars",
|
|
"default_item_group",
|
|
"default_uom",
|
|
"default_item_tax_template",
|
|
"default_supplier_group",
|
|
"default_customer_group",
|
|
"default_payment_terms",
|
|
]
|
|
|
|
# SQL table name → parentfield name in E-Taxes Settings
|
|
CHILD_TABLES = {
|
|
"tabE-Taxes Item Mapping": "item_mappings",
|
|
"tabE-Taxes Customer Mappings": "customer_mappings",
|
|
"tabE-Taxes Supplier Mappings": "supplier_mappings",
|
|
"tabE-Taxes Unit Mapping": "unit_mappings",
|
|
"tabE-Taxes VAT Account Mapping": "vat_account_mappings",
|
|
}
|
|
|
|
|
|
def _backup_path():
|
|
return frappe.get_site_path("private", BACKUP_FILENAME)
|
|
|
|
|
|
def backup_etaxes_settings():
|
|
"""
|
|
Called before_migrate (before schema sync).
|
|
Dumps scalar fields + all child table rows to a JSON file.
|
|
"""
|
|
if not frappe.db.table_exists("tabE-Taxes Settings"):
|
|
return
|
|
|
|
# Find the best old record — ORM still works normally here
|
|
rows = None
|
|
for query in [
|
|
"SELECT * FROM `tabE-Taxes Settings` WHERE name != 'E-Taxes Settings' ORDER BY modified DESC LIMIT 1",
|
|
"SELECT * FROM `tabE-Taxes Settings` ORDER BY modified DESC LIMIT 1",
|
|
]:
|
|
try:
|
|
rows = frappe.db.sql(query, as_dict=True)
|
|
except Exception:
|
|
pass
|
|
if rows:
|
|
break
|
|
|
|
if not rows:
|
|
return
|
|
|
|
old_row = rows[0]
|
|
old_name = old_row["name"]
|
|
|
|
backup = {
|
|
"old_name": old_name,
|
|
"scalar": {f: old_row.get(f) for f in SCALAR_FIELDS},
|
|
"children": {},
|
|
}
|
|
|
|
# Save ALL columns of each child table row so we can re-insert them if needed
|
|
for table in CHILD_TABLES:
|
|
if frappe.db.table_exists(table):
|
|
child_rows = frappe.db.sql(
|
|
f"SELECT * FROM `{table}` WHERE parent = %s ORDER BY idx",
|
|
(old_name,),
|
|
as_dict=True,
|
|
)
|
|
backup["children"][table] = [dict(r) for r in child_rows]
|
|
|
|
with open(_backup_path(), "w", encoding="utf-8") as f:
|
|
json.dump(backup, f, default=str, ensure_ascii=False, indent=2)
|
|
|
|
|
|
def restore_etaxes_settings():
|
|
"""
|
|
Called from the migration patch (after schema sync).
|
|
Reads backup file → writes scalar fields to tabSingles →
|
|
deletes + re-inserts child table rows → deletes the file.
|
|
"""
|
|
path = _backup_path()
|
|
|
|
if not os.path.exists(path):
|
|
return
|
|
|
|
with open(path, encoding="utf-8") as f:
|
|
backup = json.load(f)
|
|
|
|
# --- Scalar fields → tabSingles ---
|
|
for field, value in backup.get("scalar", {}).items():
|
|
if value is None:
|
|
continue
|
|
frappe.db.sql(
|
|
"""
|
|
INSERT INTO `tabSingles` (doctype, field, value)
|
|
VALUES ('E-Taxes Settings', %s, %s)
|
|
ON DUPLICATE KEY UPDATE value = VALUES(value)
|
|
""",
|
|
(field, str(value)),
|
|
)
|
|
|
|
# --- Child tables: delete existing rows, then re-insert from backup ---
|
|
for table, rows in backup.get("children", {}).items():
|
|
if not frappe.db.table_exists(table) or not rows:
|
|
continue
|
|
|
|
# Remove any rows already parented to the Single (e.g., from a broken previous run)
|
|
frappe.db.sql(
|
|
f"DELETE FROM `{table}` WHERE parent = 'E-Taxes Settings'"
|
|
)
|
|
|
|
for row in rows:
|
|
row["parent"] = "E-Taxes Settings"
|
|
row["parenttype"] = "E-Taxes Settings"
|
|
|
|
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.clear_cache(doctype="E-Taxes Settings")
|
|
|
|
os.remove(path)
|