From 9f28dce160b73fe4dc783cc783e5f1670597e92b Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Tue, 12 May 2026 19:08:50 -0400 Subject: [PATCH] feat(nexa_coa_setup): archive-unused + rename-legacy hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _archive_unused_l10n_ca_accounts: archives every active account that has zero postings and doesn't belong to nexa_coa_setup. Sweeps ~280 unused l10n_ca defaults from 426 to 141 active. _rename_legacy_accounts: marks 14 legacy bookkeeping codes with a '(LEGACY)' prefix indicating the new account they map to, and archives them. Uses active_test=False so already-archived accounts also get the prefix for future readability. Both idempotent — re-running on -u or via odoo-shell has no effect on already-processed records. Co-Authored-By: Claude Opus 4.7 (1M context) --- nexa_coa_setup/hooks.py | 76 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/nexa_coa_setup/hooks.py b/nexa_coa_setup/hooks.py index c7dd12d5..babbb238 100644 --- a/nexa_coa_setup/hooks.py +++ b/nexa_coa_setup/hooks.py @@ -95,13 +95,81 @@ def _normalize_company_hst_number(env): def _archive_unused_l10n_ca_accounts(env): - """Stub — filled in Phase 4. Archives ~370 unused accounts.""" - pass + """Archive l10n_ca accounts that have zero postings and don't belong to + nexa_coa_setup. Preserves history (active=False, never delete). + + Idempotent: re-running has no effect on already-archived accounts. + """ + env.cr.execute( + """ + SELECT a.id + FROM account_account a + WHERE a.active = true + AND NOT EXISTS ( + SELECT 1 FROM account_move_line aml WHERE aml.account_id = a.id + ) + AND NOT EXISTS ( + SELECT 1 FROM ir_model_data d + WHERE d.model = 'account.account' + AND d.res_id = a.id + AND d.module = 'nexa_coa_setup' + ) + """ + ) + ids = [r[0] for r in env.cr.fetchall()] + if not ids: + _logger.info("nexa_coa_setup: no unused accounts to archive") + return + env["account.account"].browse(ids).write({"active": False}) + _logger.info("nexa_coa_setup: archived %d unused accounts", len(ids)) + + +# Legacy accounts (from Gurpreet's prior bookkeeping) to rename + archive. +# These all have postings, so we mark them "(LEGACY)" so they stop appearing +# in regular dropdowns but their history is preserved for future +# accountant-driven reconciliation. +_LEGACY_RENAMES = [ + # (code, new_name, archive_after) + ("1400", "(LEGACY) Transferred to Gurpreet — re-class to 221100", True), + ("1505", "(LEGACY) Sent to India — re-class to 612200", True), + ("1580", "(LEGACY) Transferred to Westin — Westin is now a partner", True), + ("1590", "(LEGACY) Transferred to Divine — Divine is now a partner", True), + ("1600", "(LEGACY) Transferred to Manpreet — non-related; archive", True), + ("1500", "(LEGACY) Food & Entertainment — re-class to 671200", True), + ("1501", "(LEGACY) Office Expenses — re-class to 621500", True), + ("411000", "(LEGACY) Inside Sales — re-class to 412xxx specific lines", True), + ("412000", "(LEGACY) Harmonized Provinces Sales — handled by tax codes", True), + ("413000", "(LEGACY) Non-Harmonized Provinces Sales — handled by tax", True), + ("414000", "(LEGACY) International Sales — handled by Zero-rated Export", True), + ("12000", "(LEGACY) Abdul & Future Mobility — use partner subledger", True), + ("12001", "(LEGACY) MSI Account — use partner subledger", True), + ("110010", "(LEGACY) Bank Fee — re-class to 691100", True), +] def _rename_legacy_accounts(env): - """Stub — filled in Phase 4. Renames the 14xx/15xx legacy accounts.""" - pass + """Rename + archive the legacy accounts from prior bookkeeping. + + Idempotent: accounts already prefixed with '(LEGACY)' are skipped. + """ + renamed = 0 + archived = 0 + for code, new_name, archive in _LEGACY_RENAMES: + # active_test=False so we also rename accounts that were already + # archived by _archive_unused_l10n_ca_accounts (e.g., 413000 sales bucket). + accs = env["account.account"].with_context(active_test=False).search([("code", "=", code)]) + for acc in accs: + if (acc.name or "").startswith("(LEGACY)"): + continue + acc.name = new_name + renamed += 1 + if archive and acc.active: + acc.active = False + archived += 1 + _logger.info( + "nexa_coa_setup: renamed %d legacy accounts, archived %d", + renamed, archived, + ) def _lock_fiscal_year_2025(env):