diff --git a/invoice_az/hooks.py b/invoice_az/hooks.py index ff61ecd..7e393e3 100644 --- a/invoice_az/hooks.py +++ b/invoice_az/hooks.py @@ -78,10 +78,8 @@ def after_install(): def after_migrate(): """Run migration tasks""" from invoice_az.auth import setup_token_renewal - from invoice_az.migrate_utils import migrate_etaxes_settings_to_single setup_token_renewal() - migrate_etaxes_settings_to_single() # Fixtures for master data # ------------------------ diff --git a/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js b/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js index 40bb596..62691b5 100644 --- a/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js +++ b/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js @@ -12,31 +12,6 @@ frappe.ui.form.on('E-Taxes Settings', { }, refresh: function(frm) { - frm.add_custom_button(__('Restore from file'), function() { - frappe.confirm( - __('Restore all settings and mappings from backup file? Current data will be overwritten.'), - function() { - frappe.call({ - method: 'invoice_az.migrate_utils.restore_etaxes_from_file', - freeze: true, - freeze_message: __('Restoring...'), - callback: function(r) { - if (r.message && r.message.success) { - frappe.show_alert({ message: r.message.message, indicator: 'green' }, 5); - frm.reload_doc(); - } else { - frappe.msgprint({ - title: __('Restore Failed'), - indicator: 'red', - message: r.message ? r.message.message : __('Unknown error') - }); - } - } - }); - } - ); - }, __('Data Loading')); - // Main Load All Data button frm.add_custom_button(__('Load All Data'), function() { show_load_all_data_dialog(frm); diff --git a/invoice_az/migrate_utils.py b/invoice_az/migrate_utils.py deleted file mode 100644 index 80ec690..0000000 --- a/invoice_az/migrate_utils.py +++ /dev/null @@ -1,220 +0,0 @@ -import json -import os - -import frappe - -BACKUP_FILENAME = "etaxes_settings_backup.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", -] - -CHILD_TABLES = { - "item_mappings": "tabE-Taxes Item Mapping", - "customer_mappings": "tabE-Taxes Customer Mappings", - "supplier_mappings": "tabE-Taxes Supplier Mappings", - "unit_mappings": "tabE-Taxes Unit Mapping", - "vat_account_mappings": "tabE-Taxes VAT Account Mapping", -} - - -def _backup_path(): - return frappe.get_site_path("private", BACKUP_FILENAME) - - -def _sql_table_exists(table_name): - """Check if table exists via raw SQL (bypasses Frappe metadata).""" - result = frappe.db.sql( - "SHOW TABLES LIKE %s", - (table_name,), - ) - return bool(result) - - -@frappe.whitelist() -def export_etaxes_to_file(): - """ - Export E-Taxes Settings data to a JSON file. - Works even after git pull (when doctype JSON already says issingle=1 - but the old multi-record table still exists in MySQL). - - Usage: - bench --site execute invoice_az.migrate_utils.export_etaxes_to_file - """ - # Try old multi-record table first - if _sql_table_exists("tabE-Taxes Settings"): - return _export_from_table() - - # Fallback: already migrated to Single — export from tabSingles - singles = frappe.db.sql( - "SELECT field, value FROM tabSingles WHERE doctype = 'E-Taxes Settings'", - as_dict=True, - ) - if singles: - return _export_from_singles(singles) - - print("ERROR: No data found — neither tabE-Taxes Settings table nor tabSingles entries exist") - - -def _export_from_table(): - """Export from the old multi-record table.""" - rows = frappe.db.sql("SELECT * FROM `tabE-Taxes Settings` LIMIT 1", as_dict=True) - if not rows: - print("ERROR: tabE-Taxes Settings table exists but is empty") - return - - old_row = rows[0] - old_name = old_row["name"] - - backup = { - "old_name": old_name, - "scalar": {}, - "children": {}, - } - - for field in SCALAR_FIELDS: - backup["scalar"][field] = old_row.get(field) - - for fieldname, table in CHILD_TABLES.items(): - if _sql_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"][fieldname] = [dict(r) for r in child_rows] - - return _save_backup(backup, old_name) - - -def _export_from_singles(singles): - """Export from tabSingles (already a Single doctype).""" - backup = { - "old_name": "E-Taxes Settings", - "scalar": {}, - "children": {}, - } - - singles_map = {r["field"]: r["value"] for r in singles} - for field in SCALAR_FIELDS: - backup["scalar"][field] = singles_map.get(field) - - for fieldname, table in CHILD_TABLES.items(): - if _sql_table_exists(table): - child_rows = frappe.db.sql( - f"SELECT * FROM `{table}` WHERE parent = 'E-Taxes Settings' ORDER BY idx", - as_dict=True, - ) - backup["children"][fieldname] = [dict(r) for r in child_rows] - - return _save_backup(backup, "E-Taxes Settings") - - -def _save_backup(backup, source_name): - """Save backup dict to JSON file.""" - path = _backup_path() - with open(path, "w", encoding="utf-8") as f: - json.dump(backup, f, default=str, ensure_ascii=False, indent=2) - - total_children = sum(len(v) for v in backup["children"].values()) - print(f"Saved to: {path}") - print(f"Source: '{source_name}'") - print(f"Scalar fields: {sum(1 for v in backup['scalar'].values() if v is not None)}") - print(f"Child rows: {total_children}") - for fieldname, rows in backup["children"].items(): - if rows: - print(f" {fieldname}: {len(rows)}") - return path - - -@frappe.whitelist() -def restore_etaxes_from_file(): - """ - Restore E-Taxes Settings from backup JSON file. - - Usage (CLI): - bench --site execute invoice_az.migrate_utils.restore_etaxes_from_file - - Also callable from the E-Taxes Settings form button. - """ - path = _backup_path() - - if not os.path.exists(path): - msg = f"Backup file not found: {path}" - print(f"ERROR: {msg}") - return {"success": False, "message": msg} - - with open(path, encoding="utf-8") as f: - backup = json.load(f) - - # Scalar fields → tabSingles - restored_scalars = 0 - 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)), - ) - restored_scalars += 1 - - # Child tables → update parent for existing rows + insert missing ones - restored_children = 0 - old_name = backup.get("old_name", "") - for fieldname, rows in backup.get("children", {}).items(): - table = CHILD_TABLES.get(fieldname) - if not table or not _sql_table_exists(table) or not rows: - continue - - # First: update parent/parenttype/parentfield for ALL rows that came from old name - if old_name and old_name != "E-Taxes Settings": - frappe.db.sql( - f"UPDATE `{table}` SET parent = 'E-Taxes Settings', " - f"parenttype = 'E-Taxes Settings', parentfield = %s " - f"WHERE parent = %s", - (fieldname, old_name), - ) - updated = frappe.db.sql("SELECT ROW_COUNT() as cnt")[0][0] - if updated: - print(f" {table}: updated parent for {updated} existing rows") - restored_children += updated - - # Then: insert any rows that don't exist yet - for row in rows: - row["parent"] = "E-Taxes Settings" - row["parenttype"] = "E-Taxes Settings" - row["parentfield"] = fieldname - columns = ", ".join(f"`{col}`" for col in row) - placeholders = ", ".join(["%s"] * len(row)) - try: - frappe.db.sql( - f"INSERT IGNORE INTO `{table}` ({columns}) VALUES ({placeholders})", - list(row.values()), - ) - if frappe.db.sql("SELECT ROW_COUNT() as cnt")[0][0]: - restored_children += 1 - except Exception as e: - print(f" Warning: failed to insert row in {table}: {e}") - - frappe.db.commit() - frappe.clear_cache(doctype="E-Taxes Settings") - - msg = f"Restored {restored_scalars} fields, {restored_children} child rows" - print(f"OK: {msg}") - return {"success": True, "message": msg} - - -def migrate_etaxes_settings_to_single(): - """Called from after_migrate hook — no-op if old table is gone.""" - pass diff --git a/invoice_az/patches.txt b/invoice_az/patches.txt index 18be6e7..8b13789 100644 --- a/invoice_az/patches.txt +++ b/invoice_az/patches.txt @@ -1 +1 @@ -invoice_az.patches.v1_0.migrate_etaxes_settings_to_single + diff --git a/invoice_az/patches/v1_0/migrate_etaxes_settings_to_single.py b/invoice_az/patches/v1_0/migrate_etaxes_settings_to_single.py deleted file mode 100644 index 5de7f03..0000000 --- a/invoice_az/patches/v1_0/migrate_etaxes_settings_to_single.py +++ /dev/null @@ -1,7 +0,0 @@ -import frappe - - -def execute(): - from invoice_az.migrate_utils import migrate_etaxes_settings_to_single - - migrate_etaxes_settings_to_single()