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>
This commit is contained in:
gsinghpal
2026-05-12 19:15:21 -04:00
parent 113427f7e2
commit d2f8934a53
4 changed files with 222 additions and 0 deletions

View File

@@ -1,5 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="pc_services" model="product.category">
<field name="name">Services</field>
</record>
<record id="pc_saas" model="product.category">
<field name="name">SaaS Subscription</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_411100"/>
</record>
<record id="pc_hosting" model="product.category">
<field name="name">Hosting</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_411200"/>
</record>
<record id="pc_support" model="product.category">
<field name="name">Support Contract</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_411300"/>
</record>
<record id="pc_setup_fee" model="product.category">
<field name="name">Setup Fee</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_411500"/>
</record>
<record id="pc_custom_software" model="product.category">
<field name="name">Custom Software Development</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_412100"/>
</record>
<record id="pc_webapp" model="product.category">
<field name="name">Custom Web App Development</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_412200"/>
</record>
<record id="pc_website" model="product.category">
<field name="name">Custom Website Development</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_412300"/>
</record>
<record id="pc_erp" model="product.category">
<field name="name">ERP Implementation</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_412400"/>
</record>
<record id="pc_consulting" model="product.category">
<field name="name">Consulting &amp; Advisory</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_413100"/>
</record>
<record id="pc_training" model="product.category">
<field name="name">Training</field>
<field name="parent_id" ref="pc_services"/>
<field name="property_account_income_categ_id" ref="acct_413200"/>
</record>
<record id="pc_resale" model="product.category">
<field name="name">Resale</field>
</record>
<record id="pc_resale_software" model="product.category">
<field name="name">Software Resale</field>
<field name="parent_id" ref="pc_resale"/>
<field name="property_account_income_categ_id" ref="acct_414100"/>
<field name="property_account_expense_categ_id" ref="acct_513100"/>
</record>
<record id="pc_resale_hardware" model="product.category">
<field name="name">Hardware Resale</field>
<field name="parent_id" ref="pc_resale"/>
<field name="property_account_income_categ_id" ref="acct_414200"/>
<field name="property_account_expense_categ_id" ref="acct_513200"/>
</record>
</data>
</odoo>

View File

@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="rp_associated_tag" model="res.partner.category">
<field name="name">RP-Associated</field>
<field name="color">3</field>
<field name="parent_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -1,5 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="partner_westin_healthcare" model="res.partner">
<field name="name">Westin Healthcare Inc</field>
<field name="is_company" eval="True"/>
<field name="company_type">company</field>
<field name="customer_rank">1</field>
<field name="supplier_rank">1</field>
<field name="country_id" ref="base.ca"/>
<field name="state_id" ref="base.state_ca_on"/>
<field name="category_id" eval="[(6, 0, [ref('rp_associated_tag')])]"/>
<field name="property_account_position_id" ref="fp_ca_ontario"/>
<field name="comment">Associated corporation under common control with Nexa Systems Inc (Gurpreet, owner). Intercompany transactions must be priced at fair market value (ITA s.247). Shared SBD limit per ITA s.125(5.1).</field>
</record>
<record id="partner_divine_mobility" model="res.partner">
<field name="name">Divine Mobility Inc</field>
<field name="is_company" eval="True"/>
<field name="company_type">company</field>
<field name="customer_rank">1</field>
<field name="supplier_rank">1</field>
<field name="country_id" ref="base.ca"/>
<field name="state_id" ref="base.state_ca_on"/>
<field name="category_id" eval="[(6, 0, [ref('rp_associated_tag')])]"/>
<field name="property_account_position_id" ref="fp_ca_ontario"/>
<field name="comment">Associated corporation under common control with Nexa Systems Inc (Gurpreet, owner). See Westin Healthcare Inc for compliance notes.</field>
</record>
</data>
</odoo>

View File

@@ -1,5 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="rule_aws" model="account.reconcile.model">
<field name="name">AWS / Amazon Web Services → Cloud Infrastructure</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">AMAZON WEB SERVICES</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_511105'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'AWS — Cloud Infrastructure',
})]"/>
</record>
<record id="rule_hetzner" model="account.reconcile.model">
<field name="name">Hetzner → Cloud Infrastructure</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">HETZNER</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_511105'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'Hetzner — Cloud Infrastructure',
})]"/>
</record>
<record id="rule_digitalocean" model="account.reconcile.model">
<field name="name">DigitalOcean → Cloud Infrastructure</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">DIGITALOCEAN</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_511105'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'DigitalOcean — Cloud Infrastructure',
})]"/>
</record>
<record id="rule_cloudflare" model="account.reconcile.model">
<field name="name">Cloudflare → CDN &amp; Edge</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">CLOUDFLARE</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_511110'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'Cloudflare — CDN &amp; Edge',
})]"/>
</record>
<record id="rule_github" model="account.reconcile.model">
<field name="name">GitHub → Software (Dev Tools)</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">GITHUB</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_631200'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'GitHub — Software (Dev Tools)',
})]"/>
</record>
<record id="rule_microsoft" model="account.reconcile.model">
<field name="name">Microsoft / M365 → Software (Productivity)</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">MICROSOFT</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_631100'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'Microsoft — Software (Productivity)',
})]"/>
</record>
<record id="rule_stripe_fee" model="account.reconcile.model">
<field name="name">Stripe fee → Merchant Processing</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">STRIPE FEE</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_691200'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'Stripe — Merchant Processing',
})]"/>
</record>
<record id="rule_google_ads" model="account.reconcile.model">
<field name="name">Google Ads → Advertising (Digital)</field>
<field name="trigger">manual</field>
<field name="match_label">contains</field>
<field name="match_label_param">GOOGLE ADS</field>
<field name="line_ids" eval="[(0, 0, {
'account_id': ref('acct_641100'),
'amount_type': 'percentage',
'amount_string': '100',
'label': 'Google Ads — Advertising (Digital)',
})]"/>
</record>
</data>
</odoo>