Adds _delete_unused_accounts hook that hard-deletes (not archives) every
account that's safe to remove — not owned by nexa_coa_setup AND not
referenced by:
- account.move.line postings
- account.tax.repartition.line
- account.journal default/suspense/profit/loss accounts
- account.fiscal.position.account substitution maps
- product.category and product.template JSONB property_account_* fields
- res.partner JSONB property_account_payable_id/receivable_id
- res.company exchange/transfer/POS receivable accounts
Tries bulk unlink first; falls back to per-record if a batch fails so
the rest still get cleaned.
Result on staging: 554 -> 172 total accounts (deleted 382). The 31 still
archived are blocked by references (historical postings, tax repartition
links, bank journal defaults, etc.) — left as archived so they're hidden
from dropdowns but preserve audit history.
Verified all 4 test invoices still post correctly (ON 113, US 100, QC
114.98, Westin intercompany 169.50).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three fixes that unblock end-to-end invoice tests on staging:
1. Switched company default sale/purchase tax from '5% GST' to '13% HST'
(Ontario is the home province). New products auto-get 13% HST; fiscal
positions substitute OUT to other rates per customer location.
2. Added _migrate_tax_repartition_accounts hook. The post_init archive sweep
correctly archived legacy l10n_ca tax-tracking accounts (118100.OLD,
231000, 232000, 233000, 118400, 118500, etc.) but active taxes still
referenced them via repartition lines, causing invoice posting to fail
with 'account is archived'. Hook repoints repartition to Nexa's
consolidated 118100 (ITC) / 213100 (HST collected) / 213500 (QST
collected) accounts.
3. Odoo 19 fiscal position behavior change: empty tax_ids now means
'remove all taxes' (was 'pass-through' in v17/18). For ON home position
we now add a self-mapping placeholder (13% HST -> 13% HST) so the FP
has a non-empty tax_ids and map_tax falls through to pass-through
semantics on the 13% HST source.
Verified with 4 invoice tests on staging:
ON -> 13% HST total 113.00
US -> 0% GST total 100.00 (zero-rated export)
QC -> 14.975% total 114.98
Westin -> 13% HST total 169.50 (intercompany, RP-Associated tag)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 7 — 14 product categories under Services/Resale parents, each wired
to the appropriate default income (and expense for Resale) accounts.
Phase 8 — RP-Associated partner tag + Westin Healthcare Inc + Divine
Mobility Inc partner records, both as Customer+Vendor, both tagged
RP-Associated, both with CA-Ontario fiscal position pre-applied.
Phase 9 — 8 bank reconciliation rules for common vendors (AWS, Hetzner,
DigitalOcean, Cloudflare, GitHub, Microsoft, Stripe fee, Google Ads)
that auto-suggest the correct category account when reconciling bank
statement lines. Uses Odoo 19's 'trigger' field (replaces old
'rule_type').
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XML defines 8 positions with auto-detection by country/state:
- CA Ontario (default), CA Atlantic, CA Quebec, CA BC, CA Prairies/Territories
- Export US, Export International, Tax Exempt
post_init hook _configure_fiscal_position_tax_maps sets up bidirectional
tax routing (sale + purchase) from the default '5% GST' to the appropriate
provincial tax via Odoo 19's account.fiscal.position.tax_ids /
account.tax.original_tax_ids relation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds _archive_unused_taxes hook that archives all active taxes whose
name is not in the curated keep-set (GST/HST/QST/PST per province + zero
rated + exempt) AND that have zero usage on existing move lines.
Reduces active taxes from 49 to 30 on staging. The 'HST for sales/
purchases - 13%' pair is kept active because of historical postings
(215 sales lines + 1 purchase line) — new invoicing routes to the
cleaner '13% HST' via fiscal positions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_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) <noreply@anthropic.com>
- 'Customer Project' plan (renamed from 'Project' to avoid duplicate with
project module's auto-created plan) — mandatory
- 'Department' plan (mandatory) — seeded with DEPT-DEV, DEPT-SALES,
DEPT-ADMIN, DEPT-HOSTING
- 'SR&ED Tag' plan (optional) — seeded with 7 tag values:
SRED-T4-DEV-SALARY, SRED-SPECIFIED-EMPLOYEE,
SRED-CONTRACTOR-CA-ARM-LENGTH, SRED-CONTRACTOR-CA-NON-ARM-LENGTH,
SRED-MATERIALS-CONSUMED, SRED-OVERHEAD-PROXY-BASIS, NOT-ELIGIBLE
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bakes the staging-side one-off collision clearing into the module install
itself so production install will execute the same sweep automatically.
For each of the 29 l10n_ca codes that conflict with Nexa's planned chart:
- If the account has zero postings: suffix code with '.OLD', mark inactive,
rename to '(l10n_ca LEGACY) <original>'
- If the account has postings (currently 115100 AR control with 240 lines
and 511100 Inside Purchases with 1 line): leave alone (Nexa renumbered
to 119100 / 511105 in the XML)
Idempotent — pre_init_hook re-running has no effect (already-suffixed
codes are skipped).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renumbered to avoid collisions with pre-loaded l10n_ca codes:
- Due From Shareholder/Associated: 115xxx → 119xxx range (115100/115110 already
held l10n_ca AR control accounts with 240 postings)
- Cloud Infrastructure: 511100 → 511105 (511100 was l10n_ca 'Inside Purchases'
with 1 historical posting)
All other 28 colliding l10n_ca codes (118xxx, 213xxx, 214xxx, 221xxx, 311xxx,
411xxx, 413xxx, 511110-511210, 512100-512200, 611100-300, 612xxx) had zero
postings and were cleared in-place by suffixing existing codes with '.OLD'
via a one-off odoo-shell script on staging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The post_init_hook attempt to set fiscalyear_lock_date=2025-12-31 fails
with RedirectWarning when unreconciled bank statement lines exist in
the period. Catch RedirectWarning/UserError/ValidationError, log a
clear instruction to set the lock manually after reconciliation, and
let install continue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>