Commit Graph

18 Commits

Author SHA1 Message Date
gsinghpal
2737bc481c docs(nexa_coa_setup): comprehensive operating runbook
Expands README into a full ops guide covering:
- Chart of accounts at a glance (4-digit ranges + examples)
- Standard products catalog with SKUs and income routing
- Fiscal positions with auto-detect rules
- Three analytic plans + their tag conventions
- Install / update / deploy / restore commands
- Yearly close calendar (HST Mar 31, T2 Jun 30, SR&ED prep timeline)
- Common tasks (add account, add product, add analytic, lock FY,
  reclassify invoice, pull SR&ED data)
- Compliance flags (associated-corp SBD sharing, s.15(2) loans,
  transfer pricing, HST cadence triggers, specified-employee SR&ED cap)
- Implementation scripts table (audit reference)
- Open items checklist for future manual follow-ups

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:52:18 -04:00
gsinghpal
0e595e6129 feat(nexa_coa_setup): batch-reclass 200 historical 411000 lines
All 123 historical out_invoices ($249k revenue, 2022-2023 mostly) had
been posted to the generic l10n_ca '411000 Inside Sales' account, since
the module they predated proper product setup and had no SKU attached.

Keyword-rule script (scripts/reclass_historical_411000.py) routes each
line by description text to the correct Nexa account:

  Pattern                                  -> Target account     Lines   Revenue
  Computer & Server Maintenance, Server   4030 Support &         165    $236,259
    Backup & Monitoring, Membership Fee    Maintenance Contracts
  [CUSTCOMP], Custom Computer, HP Desk,   4320 Hardware Resale    24    ~$8,200
    Server 2019, Server Rack, 16 Port
    POE, CPU:, Cleaning Supplies
  ONSITE-, OFFSITE-, Server Setup,        4230 Technical Support  11    ~$3,200
    Wiring for                             — Per-incident/Hourly

Match rate: 200/200 = 100%. Verified the legacy 411000 account now has
zero open-invoice lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:43:22 -04:00
gsinghpal
a0f783ab14 feat(nexa_coa_setup): seed 14 standard service products
Standard catalog covering Nexa's main service lines, each linked to the
appropriate product category so income posts to the right GL account
automatically:

  Recurring (per month/year)
    SAAS-BASIC    SaaS Subscription — Basic               $0    -> 4010
    HOST-S        Hosting — Small                         $49   -> 4020
    HOST-M        Hosting — Medium                        $149  -> 4020
    HOST-L        Hosting — Large                         $299  -> 4020
    SUPPORT-RET   Support Contract — 4 hrs retainer       $640  -> 4030
    SETUP-FEE     Setup / Onboarding Fee                  $500  -> 4050

  Project (hourly)
    DEV-SOFTWARE  Custom Software Development             $160  -> 4110
    DEV-WEBAPP    Custom Web App Development              $160  -> 4120
    DEV-WEBSITE   Custom Website Development              $160  -> 4130
    ERP-IMPL      ERP Implementation & Customization      $175  -> 4140

  Services (hourly)
    CONSULT       Consulting & Advisory                   $200  -> 4210
    TRAINING      Training & Workshop                     $120  -> 4220
    TECH-SUPPORT  Technical Support — Per-incident        $160  -> 4230

  Reseller (template)
    RESALE-SW     Third-party Software License (template) $0    -> 4310

File uses noupdate=1 so user price/description edits persist across
future -u runs.

Verified: creating an invoice for Westin with 10 hrs of DEV-SOFTWARE
auto-routes to 4110 Custom Software Development Revenue with 13% HST
applied via fiscal position.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:41:13 -04:00
gsinghpal
82a13b2ce5 feat(nexa_coa_setup): pc_tech_support category + fix Entech invoice 1127
Adds pc_tech_support product category (parent: Services, income default:
4230 Technical Support — Per-incident / Hourly Revenue). Existing
categories had no hourly-tech-support slot; SETUP-type hourly billing
products go here.

Also repoints the 17 product lines of invoice 1127 (Electroless Nickel
Technologies, ,985.48, posted 2026-04-29) from the legacy account
412000 to the correct Nexa accounts via direct UPDATE on
account_move_line:
  13 hardware lines (Lenovo, RTX, NAS drives, cabinets, UPS, ...)
    -> 4320 Hardware Resale Revenue
  4 SETUP hours lines (Cloud / Security / NAS / Network setup)
    -> 4230 Technical Support — Per-incident / Hourly Revenue
Invoice totals, tax, payment, customer PDF all unchanged.

Reassigns 14 product templates (P620, CUSPC, SETUP, etc.) to use the
new categories so future invoices auto-route correctly:
  Hardware SKUs -> pc_resale_hardware
  SETUP         -> pc_tech_support

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:21:43 -04:00
gsinghpal
0230670bdc feat(nexa_coa_setup): renumber l10n_ca bank/AR/AP/tax legacy accounts to 4-digit
Final batch of code conversions — 12 l10n_ca accounts that we kept active
because they have historical postings. Renaming preserves all FK
references (account_id stays the same), just changes the displayed code.

  112005 Scotia Current 9309     -> 1010  (primary operating bank)
  112004 BMO                     -> 1030
  112007 RBC                     -> 1040
  112008 Scotia Credit Card 5890 -> 1070
  112006 RBC VISA                -> 1071
  112002 Outstanding Receipts    -> 1080  (in-transit receipts)
  112003 Outstanding Payments    -> 1081  (in-transit payments)
  112001 Bank Suspense Account   -> 1090
  115100 Customers Account       -> 1100  (AR control)
  118310 HST receivable - 13%    -> 1215  (legacy HST receivable, near new 1210 ITC)
  211100 Vendors Account         -> 2010  (AP control)
  213310 HST to pay - 13%        -> 2115  (legacy HST collected, near new 2110)

Verification: 140/140 active accounts now use 4-digit codes. All four
end-to-end test invoices still post correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:10:47 -04:00
gsinghpal
86e89ca419 feat(nexa_coa_setup): convert chart of accounts to 4-digit codes
Renumbered all 128 Nexa accounts from 6-digit (l10n_ca style) to clean
4-digit codes for readability:

  1000-1999  Assets
    1120  Due From Shareholder
    1210  HST/GST ITC Receivable
    1510-1750  Capital assets + accumulated depreciation
  2000-2999  Liabilities
    2110  HST/GST Collected
    2510  Due To Shareholder
  3000-3999  Equity
    3010  Common Shares
    3510  Retained Earnings — Current
  4000-4999  Revenue
    4010-4050  Recurring (SaaS, Hosting, Support, ...)
    4110-4160  Project work
    4210-4230  Hourly services
    4310-4320  Reseller
  5000-5999  COGS
    5010-5120  Infrastructure & APIs
    5210-5250  Project direct costs
    5310-5320  Resold goods
  6000-6999  Operating expenses
    6010-6092  Personnel (T4)
    6110-6120  Contract labour
    6210-6960  Office/Tech/Marketing/Professional/Insurance/Travel/Training/Banking
  7000+  Other (bad debt, donations, FX, depreciation)

Applied to prod via scripts/convert_to_4digit.py (now committed). XML
codes updated in 01_account_account.xml; XMLIDs preserved so existing
ir.model.data rows on prod stay valid.

Hook constants updated:
- _TAX_REPARTITION_REMAP targets: 118100 -> 1210, 213100 -> 2110, etc.
- _LEGACY_RENAMES new_name strings: 're-class to NNNN' guidance updated
  to 4-digit targets.

Verified -u on prod completes cleanly + all 4 test invoices still post:
  ON     -> 4010 SaaS, total 113.00
  US     -> 4010 SaaS, total 100.00 (zero-rated)
  QC     -> 4010 SaaS, total 114.98
  Westin -> 4210 Consulting, total 169.50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:09:01 -04:00
gsinghpal
749c0335fa fix(nexa_coa_setup): clean GL codes — 119100/119900->115200/115900, 511105->511100
Three odd code positions that were chosen to dodge l10n_ca collisions are
now cleaned up:
- Due From Shareholder       119100 -> 115200 (115xxx is where receivables belong)
- Due From Associated Corps  119900 -> 115900
- Cloud Infrastructure       511105 -> 511100 (legacy 'Inside Purchases'
                                       renamed to 511100.OLD)

Applied to prod via scripts/fix_gl_codes.py (now committed).

Module XML updated: <field name='code'> values match new codes; XMLIDs
(acct_119100, acct_119900, acct_511105) preserved so existing
ir.model.data rows on prod still map to the right records.

pre_init_hook augmented with _L10N_CA_FORCE_CLEAR_CODES set so a fresh
install on a new DB also force-clears 511100 (which would otherwise be
blocked by the postings-exist guard).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:02:54 -04:00
gsinghpal
092423d7de feat(nexa_coa_setup): hard-delete unused accounts
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>
2026-05-12 19:49:00 -04:00
gsinghpal
9c52fac9ba fix(nexa_coa_setup): default tax = 13% HST, tax repartition migration, FP pass-through
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>
2026-05-12 19:19:49 -04:00
gsinghpal
d2f8934a53 feat(nexa_coa_setup): product categories, partner records, bank reconcile rules
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>
2026-05-12 19:15:21 -04:00
gsinghpal
113427f7e2 feat(nexa_coa_setup): 8 fiscal positions + tax substitution maps
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>
2026-05-12 19:12:19 -04:00
gsinghpal
3559eb1fd5 feat(nexa_coa_setup): archive unused taxes
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>
2026-05-12 19:09:37 -04:00
gsinghpal
9f28dce160 feat(nexa_coa_setup): archive-unused + rename-legacy hooks
_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>
2026-05-12 19:08:50 -04:00
gsinghpal
169e97af02 feat(nexa_coa_setup): analytic plans + seed accounts
- '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>
2026-05-12 18:53:21 -04:00
gsinghpal
3c959771ae feat(nexa_coa_setup): pre_init_hook to clear l10n_ca code collisions
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>
2026-05-12 18:51:25 -04:00
gsinghpal
3c2fb22346 feat(nexa_coa_setup): chart of accounts — 128 accounts across 1-6xxxxx
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>
2026-05-12 18:50:00 -04:00
gsinghpal
3a41370189 fix(nexa_coa_setup): tolerant fiscal-year lock hook
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>
2026-05-12 18:41:15 -04:00
gsinghpal
d6513ff7ab feat(nexa_coa_setup): module skeleton with hooks stub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 18:39:24 -04:00