Data-only deployment layer seeding TNCSC's branding (navy/orange/electric-
blue from the plan), 5 membership tiers in CAD (Individual $50, Family $80,
Student $20, Senior $30, Life $500 - the plan's own example figures;
update via Settings once TNCSC confirms real pricing), the
'TNCSC-{year}-{seq}' member-ID format, a Canadian (Ontario/HST) chart of
accounts via l10n_ca plus non-profit-specific accounts (Membership Dues/
Event/Sponsorship/School Fees/Donations Revenue, Deferred Event Revenue
liability, a Stripe clearing account) and a Donations journal, bilingual
EN/Tamil overrides of two membership email templates (Tamil text is a
best-effort draft only, explicitly flagged as needing native-speaker
review before go-live), five placeholder website pages (Home/About/Tamil
School/Sponsors/Contact), and TNCSC-named role groups (Board Admin,
Treasurer, Events Officer, School Coordinator, Teacher, Classifieds
Moderator) that imply the existing generic product-layer groups rather
than defining new permission logic.
Chasing the chart-of-accounts setup down to a genuinely working state
took real digging: setting company.country_id on a brand-new company
auto-schedules this Odoo build's own chart-template installer via a
precommit hook (res.company.install_l10n_modules), which races an
explicit try_loading('ca_2023', ...) call made in the same install
transaction and silently replaces its result afterwards (reverting
currency to USD, chart_template to 'generic_coa', and deleting the custom
accounts) - confirmed via raw SQL checks that the correct state exists
right up until the post_init_hook transaction commits, and is gone by the
time the install process exits. Since the precommit auto-trigger only
ever fires once per company (guarded by chart_template being unset), a
second call from a separate transaction is immune to the race. The fix:
the actual setup logic lives in an idempotent res.company._tncsc_setup_
accounting() method (models/res_company.py - a narrow, documented
exception to "no models" in the deployment layer, since cramming this
into a sandboxed ir.cron code string wasn't practical), called as a
best-effort from post_init_hook and guaranteed by a daily safety-net cron
que runs in its own transaction. Also hit two smaller, separate bugs on
the way: ir.cron code execution forbids direct attribute assignment
(STORE_ATTR) in its sandbox, and Html config_parameter fields must not be
wrapped in CDATA in data XML.
Verified end-to-end on a genuinely fresh database (not the long-lived dev
DB, which already has posted entries and correctly refuses a currency
change): installed tncsc_deployment alone, pulling in all 8 product
modules plus l10n_ca as dependencies, confirmed the post-install state via
raw SQL, manually triggered the safety-net cron and confirmed it reached
the fully-correct state (CAD, ca_2023, 355 accounts including all 7
custom ones, the Donations journal, tier products linked to the dues
account), confirmed the cron is idempotent on a second run, and ran the
full test suite (9/9 passing) with the same deterministic setup called
from the test transaction directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Community OS
A brand-neutral, resellable suite of Odoo 19 Community modules for community and cultural organizations. First deployment: the Tamil Nadu Cultural Society of Canada (TNCSC).
Vendor: Metatroncube Software Solutions LLP · Waterloo, Ontario Target platform: Odoo 19 Community Edition (LGPL-3) · PostgreSQL 16 · Python 3.12
community_*is a placeholder module prefix. Replace it with a unique vendor prefix before publishing to the Odoo App Store so technical names never collide with anything already listed.
Layered architecture
┌─────────────────────────────────────────────────────────────┐
│ DEPLOYMENT LAYER (per client — data only, no logic) │
│ tncsc_deployment: branding, tiers & prices, chart of │
│ accounts, email copy, website pages, user groups │
└───────────────▲─────────────────────────────────────────────┘
│ depends on
┌───────────────┴─────────────────────────────────────────────┐
│ PRODUCT LAYER (brand-neutral, resellable, LGPL-3) │
│ community_membership event_qr_ticketing community_school │
│ community_classifieds community_benefits community_interac│
│ community_theme_base community_portal │
└───────────────▲─────────────────────────────────────────────┘
│ depends only on
┌───────────────┴─────────────────────────────────────────────┐
│ ODOO 19 COMMUNITY CORE (never Enterprise) │
└─────────────────────────────────────────────────────────────┘
Product-layer modules never mention a client name, never hardcode a price,
a colour, an account code, or an email address. Anything client-specific
is a configuration record, seeded only by a *_deployment module. Landing
a new client means writing a new clientname_deployment module — the
product code never changes.
Modules
Product layer (brand-neutral, sellable)
| Module | Purpose |
|---|---|
community_theme_base |
Configurable brand tokens (colours, logo, fonts) via settings |
community_membership |
Member profiles, tiers, family grouping, renewals, QR membership card |
event_qr_ticketing |
QR ticket per event registration, staff check-in / verification |
community_school |
Programs, classes, enrollment, attendance, LMS link, parent portal |
community_classifieds |
Member-gated classifieds board with moderation and auto-expiry |
community_benefits |
Benefit centres and per-tier member benefit entitlements |
community_interac |
Interac e-Transfer semi-automated payment provider (Canada) |
community_portal |
Unified member portal dashboard, soft-detects installed modules |
Deployment layer (per client — data only)
| Module | Purpose |
|---|---|
tncsc_deployment |
TNCSC branding, tiers/prices, chart of accounts, email copy, website pages, user groups |
Local development
cd deploy
docker compose up
Odoo will be available at http://localhost:8069. The ../addons folder is
mounted read-write, so changes to module code are picked up on restart (dev
mode reload is enabled in deploy/odoo.conf).
Licensing & resellability guardrails
- Every product module is licensed
LGPL-3. - No
community_*module may depend on an Odoo Enterprise module — seeAppendix Ain the project plan for the Community-only allowlist. - No client identity (name, email, colour, price, account code) may appear
in a product module. This is enforced in CI by
scripts/check_brand_leak.py. - All client-variable behaviour is configuration data, seeded by the deployment layer, never a literal in product code.
- Every product module ships an App-Store-ready manifest, a
tests/package, and astatic/description/index.htmllisting page.
See CommunityOS_Implementation_Plan_for_Claude_Code.md for the full,
phase-by-phase build plan.
CI
.github/workflows/ci.yml installs every module against Odoo 19 +
Postgres 16 with --test-enable, runs the brand-leak grep, and lints with
flake8 / pylint-odoo.