diff --git a/nexa_coa_setup/README.md b/nexa_coa_setup/README.md
new file mode 100644
index 00000000..09a233ee
--- /dev/null
+++ b/nexa_coa_setup/README.md
@@ -0,0 +1,26 @@
+# Nexa Systems — Chart of Accounts Setup
+
+Custom Odoo 19 module that configures the chart of accounts, taxes,
+fiscal positions, analytic plans, and partner records for Nexa Systems Inc.
+
+## Install
+
+```
+docker exec odoo-nexa-app odoo -c /etc/odoo/odoo.conf -d nexamain \
+ -i nexa_coa_setup --no-http --stop-after-init
+```
+
+## Update
+
+```
+docker exec odoo-nexa-app odoo -c /etc/odoo/odoo.conf -d nexamain \
+ -u nexa_coa_setup --no-http --stop-after-init
+```
+
+## Design reference
+
+See `docs/superpowers/specs/2026-05-12-nexa-coa-design.md`.
+
+## Safety
+
+Always take a pg_dump BEFORE running `-i` or `-u`. See `docs/superpowers/plans/2026-05-12-nexa-coa-setup.md` Phase 0.
diff --git a/nexa_coa_setup/__init__.py b/nexa_coa_setup/__init__.py
new file mode 100644
index 00000000..cc6b6354
--- /dev/null
+++ b/nexa_coa_setup/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from .hooks import post_init_hook
diff --git a/nexa_coa_setup/__manifest__.py b/nexa_coa_setup/__manifest__.py
new file mode 100644
index 00000000..52b4e87a
--- /dev/null
+++ b/nexa_coa_setup/__manifest__.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+# Copyright 2026 Nexa Systems Inc.
+# License OPL-1
+{
+ "name": "Nexa Systems — Chart of Accounts Setup",
+ "version": "19.0.1.0.0",
+ "category": "Accounting/Localizations/Chart of Accounts",
+ "summary": "Custom CoA, taxes, fiscal positions, analytic plans, and intercompany partner setup for Nexa Systems Inc.",
+ "author": "Nexa Systems Inc.",
+ "website": "https://nexasystems.ca",
+ "license": "OPL-1",
+ "depends": [
+ "account",
+ "account_accountant",
+ "l10n_ca",
+ "analytic",
+ "sale_management",
+ "purchase",
+ "sale_subscription",
+ ],
+ "data": [
+ "security/ir.model.access.csv",
+ "data/01_account_account.xml",
+ "data/02_account_journal.xml",
+ "data/03_account_tax.xml",
+ "data/04_account_fiscal_position.xml",
+ "data/05_account_analytic_plan.xml",
+ "data/06_account_analytic_account.xml",
+ "data/07_product_category.xml",
+ "data/08_res_partner_category.xml",
+ "data/09_res_partner.xml",
+ "data/10_account_reconcile_model.xml",
+ ],
+ "post_init_hook": "post_init_hook",
+ "installable": True,
+ "application": False,
+ "auto_install": False,
+}
diff --git a/nexa_coa_setup/data/01_account_account.xml b/nexa_coa_setup/data/01_account_account.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/01_account_account.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/02_account_journal.xml b/nexa_coa_setup/data/02_account_journal.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/02_account_journal.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/03_account_tax.xml b/nexa_coa_setup/data/03_account_tax.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/03_account_tax.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/04_account_fiscal_position.xml b/nexa_coa_setup/data/04_account_fiscal_position.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/04_account_fiscal_position.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/05_account_analytic_plan.xml b/nexa_coa_setup/data/05_account_analytic_plan.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/05_account_analytic_plan.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/06_account_analytic_account.xml b/nexa_coa_setup/data/06_account_analytic_account.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/06_account_analytic_account.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/07_product_category.xml b/nexa_coa_setup/data/07_product_category.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/07_product_category.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/08_res_partner_category.xml b/nexa_coa_setup/data/08_res_partner_category.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/08_res_partner_category.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/09_res_partner.xml b/nexa_coa_setup/data/09_res_partner.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/09_res_partner.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/data/10_account_reconcile_model.xml b/nexa_coa_setup/data/10_account_reconcile_model.xml
new file mode 100644
index 00000000..7faba267
--- /dev/null
+++ b/nexa_coa_setup/data/10_account_reconcile_model.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/nexa_coa_setup/hooks.py b/nexa_coa_setup/hooks.py
new file mode 100644
index 00000000..28a12ed6
--- /dev/null
+++ b/nexa_coa_setup/hooks.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+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):
+ """Set fiscalyear_lock_date = 2025-12-31 on main company."""
+ from datetime import date
+ company = env.ref("base.main_company", raise_if_not_found=False)
+ if not company:
+ return
+ target = date(2025, 12, 31)
+ if not company.fiscalyear_lock_date or company.fiscalyear_lock_date < target:
+ company.fiscalyear_lock_date = target
+ _logger.info("nexa_coa_setup: fiscalyear_lock_date set to 2025-12-31")
diff --git a/nexa_coa_setup/models/__init__.py b/nexa_coa_setup/models/__init__.py
new file mode 100644
index 00000000..b8899ea4
--- /dev/null
+++ b/nexa_coa_setup/models/__init__.py
@@ -0,0 +1 @@
+# no custom models — placeholder for future extensions
diff --git a/nexa_coa_setup/security/ir.model.access.csv b/nexa_coa_setup/security/ir.model.access.csv
new file mode 100644
index 00000000..97dd8b91
--- /dev/null
+++ b/nexa_coa_setup/security/ir.model.access.csv
@@ -0,0 +1 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink