# -*- coding: utf-8 -*- import logging _logger = logging.getLogger(__name__) # l10n_ca account codes that collide with the Nexa CoA design and that # l10n_ca pre-loads with 'income_other'/'expense'/etc. types we don't want. # Each of these is checked at pre_init: if it has zero postings we suffix # its code with '.OLD' and archive it so our XML can claim the code. # Codes with postings are LEFT ALONE — we renumbered the Nexa code instead # (115100 stays as l10n_ca 'Customers Account' AR; Nexa shareholder receivable # moved to 119100. 511100 stays as l10n_ca 'Inside Purchases'; Nexa Cloud # Infrastructure moved to 511105). _L10N_CA_COLLISION_CODES = [ "118100", "118200", "118300", "213100", "214100", "221200", "311100", "311200", "311300", "411100", "411200", "411300", "413100", "413200", "413300", "511110", "511120", "511130", "511140", "511200", "511210", "512100", "512110", "512200", "611100", "611200", "611300", "612100", "612200", ] def pre_init_hook(env): """Run BEFORE XML data is loaded. Clear l10n_ca account codes that would collide with Nexa's chart of accounts.""" _logger.info("nexa_coa_setup: pre_init_hook starting") _clear_l10n_ca_collisions(env) _logger.info("nexa_coa_setup: pre_init_hook complete") def _clear_l10n_ca_collisions(env): """For each colliding code: if it has zero postings, rename to NNNNNN.OLD and set inactive. If it has postings, leave alone (Nexa code was renumbered in the XML to avoid the conflict).""" cleared = 0 kept_with_postings = 0 not_found = 0 for code in _L10N_CA_COLLISION_CODES: acc = env["account.account"].search([("code", "=", code)], limit=1) if not acc: not_found += 1 continue usage = env["account.move.line"].search_count([("account_id", "=", acc.id)]) if usage > 0: _logger.info( "nexa_coa_setup: keeping l10n_ca account %s (%s) — %d postings exist", code, acc.name, usage, ) kept_with_postings += 1 continue new_code = f"{code}.OLD" # Skip if already suffixed (idempotency) if acc.code.endswith(".OLD"): continue acc.write({ "code": new_code, "name": f"(l10n_ca LEGACY) {acc.name or acc.display_name}", "active": False, }) cleared += 1 _logger.info( "nexa_coa_setup: collision sweep — cleared %d, kept-with-postings %d, not-found %d", cleared, kept_with_postings, not_found, ) def post_init_hook(env): """Imperative one-shot operations after module data is loaded. Each helper is idempotent — safe to re-run on -u. """ _logger.info("nexa_coa_setup: post_init_hook starting") _normalize_company_hst_number(env) _archive_unused_l10n_ca_accounts(env) _rename_legacy_accounts(env) _lock_fiscal_year_2025(env) _logger.info("nexa_coa_setup: post_init_hook complete") def _normalize_company_hst_number(env): """Convert '741224877' to '741224877 RT0001' if not already in full form.""" company = env.ref("base.main_company", raise_if_not_found=False) if not company: return vat = (company.partner_id.vat or "").strip() if vat == "741224877": company.partner_id.vat = "741224877 RT0001" _logger.info("nexa_coa_setup: normalized HST# to '741224877 RT0001'") def _archive_unused_l10n_ca_accounts(env): """Stub — filled in Phase 4. Archives ~370 unused accounts.""" pass def _rename_legacy_accounts(env): """Stub — filled in Phase 4. Renames the 14xx/15xx legacy accounts.""" pass def _lock_fiscal_year_2025(env): """Try to set fiscalyear_lock_date = 2025-12-31 on main company. If Odoo blocks the lock because unreconciled bank statement lines or other open items exist in the period, log a clear warning and continue. The user can set the lock manually via Accounting > Configuration > Settings > Lock Dates once those items are cleaned up. """ from datetime import date from odoo.exceptions import RedirectWarning, UserError, ValidationError company = env.ref("base.main_company", raise_if_not_found=False) if not company: return target = date(2025, 12, 31) if company.fiscalyear_lock_date and company.fiscalyear_lock_date >= target: _logger.info("nexa_coa_setup: fiscalyear_lock_date already at or after 2025-12-31") return try: company.fiscalyear_lock_date = target _logger.info("nexa_coa_setup: fiscalyear_lock_date set to 2025-12-31") except (RedirectWarning, UserError, ValidationError) as exc: _logger.warning( "nexa_coa_setup: could not auto-lock fiscal year 2025-12-31. " "Reason: %s. Set the lock manually via Accounting > Configuration > " "Settings > Lock Dates after the unreconciled items in the period " "are cleaned up.", exc, )