Scaffolds the CommunityOS monorepo per the implementation plan: 8 brand-neutral product modules (community_theme_base, community_membership, event_qr_ticketing, community_school, community_classifieds, community_benefits, community_interac, community_portal) plus the tncsc_deployment client layer, each with an App-Store-ready manifest, LGPL-3 license, and empty security/data/demo/tests/views scaffolding. Adds deploy/docker-compose.yml (Odoo 19 CE + Postgres 16), CI workflow that installs all modules with --test-enable, and scripts/check_brand_leak.py + check_manifests.py enforcing the no-client-identity-in-product-code and manifest-completeness rules. Verified locally: all 9 modules install clean on a fresh Odoo 19 database, and the brand-leak check correctly fails when a client term is added to a product module and passes once removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 KiB
CommunityOS — Odoo 19 Product Suite: Implementation Plan for Claude Code
A brand‑neutral, resellable set of Odoo Community modules for community / cultural organizations First deployment: 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
Working product name: "CommunityOS" and the module prefix
community_used throughout this document are placeholders. Before you publish anything, replace them with your real product brand and a unique vendor prefix (e.g.mtc_community_membership) so your module technical names never collide with anything on the Odoo App Store. Do a global find‑replace at the very end, not mid‑build.
0. How to use this document (read this first)
This plan is written to be executed by Claude Code + an Odoo MCP server, one session per sub‑phase. It is deliberately different from a single‑client build plan in one important way:
Everything is built twice‑over in structure, once in effort. Each capability is built as a generic, brand‑neutral module that you can sell to any community organization, and TNCSC's specific names, colours, prices, and accounts live in a separate deployment layer that carries no reusable logic — only configuration data. You do the work once; the second sale costs you almost nothing.
Each session below has:
- Objective — what exists when the session is done.
- Module(s) — which package you are working in.
- Prompt for Claude Code — a copy‑paste starting instruction.
- Deliverables — what gets committed before you move on.
- Validation gate — how you prove it works (via the Odoo MCP) before the next session.
- Resellability check — the product‑hygiene rules specific to that module.
Do not skip the gates, and do not skip the resellability checks — a single hardcoded client name or a single Enterprise dependency is what turns a sellable product back into a one‑off project.
1. Why this architecture (the layered model)
┌─────────────────────────────────────────────────────────────┐
│ 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) │
│ base contacts account website website_sale event event_sale │
│ website_slides crm purchase mass_mailing portal payment │
└─────────────────────────────────────────────────────────────┘
The rule that keeps it sellable: product‑layer modules never mention "TNCSC," never hardcode a
price, a colour, an account code, or an email address. Anything client‑specific is a configuration
record (in res.config.settings, a data file, or a database record the admin edits), and the
TNCSC values live only in tncsc_deployment. When you land client #2, you write a new
clientname_deployment and ship — you touch no product code.
2. Version decision — Odoo 19 Community Edition
Target Odoo 19 CE. Reasoning, so you can defend it to a client or a co‑founder:
- It is the current stable release, giving the longest support runway of any released version. Odoo actively supports roughly the latest three majors, so a build on 17 is the closest to falling out of support.
- The usual reason to stay one version back — waiting for third‑party add‑ons to be ported — does not apply to you, because you are building your own modules and depend only on Odoo core.
- The payment framework, website asset pipeline, and portal all changed across 17→18→19; building on 19 means you write against the current APIs once instead of writing against 17 and re‑porting.
- Trade‑off to accept: public tutorials and StackOverflow answers lag the release. Mitigation:
point Claude Code at the actual v19 source (
https://github.com/odoo/odoo/tree/19.0) whenever it is unsure of an API, rather than trusting older examples.
Forward‑portability rule: avoid private/underscore‑prefixed core methods where a public API exists,
keep custom JS in OWL components (not legacy widgets), and keep every module's depends list minimal.
This makes the eventual jump to Odoo 20 a small, well‑scoped task rather than a rewrite.
3. Licensing & resellability guardrails (non‑negotiable)
These are the rules that keep every module something you can legally brand and sell.
- License each product module
LGPL-3in its manifest. LGPL lets your customers install and use it freely, lets you sell it, and (unlike a proprietary license) is fully compatible with building on Odoo Community. If you later want a paid‑but‑closed module for the App Store, useOPL-1(Odoo Proprietary License) — but only for modules that depend on Community‑only code. - Never depend on an Odoo Enterprise module. The moment a
dependsentry points at an Enterprise app, you inherit Enterprise licensing constraints and lose the right to sell freely. The allowed dependency allowlist is in the Appendix — do not add to it without checking the license. - No client identity in product code. No "TNCSC", no client email, no client colour hex, no
client price, no client account code anywhere under a
community_*module. Enforce this with a grep in CI (see Phase 0). - Configuration over hardcoding. Tiers, fees, branding, accounts, and copy are records/settings, seeded by the deployment layer — never literals in Python.
- App‑Store‑ready manifests. Every product module carries
author,website,license,category,version(semantic,19.0.1.0.0),summary,description,price,currency, and animages/banner from day one. Filling these at the end is a scramble; fill them as you go. - Each module is independently installable and independently sellable. No product module may hard‑
depend on another product module unless that dependency is itself a saleable bundle you intend.
Prefer optional glue (e.g.
community_schoolenhancesevent_qr_ticketingif present, via a soft check, rather than hard‑depending on it). - Tests ship with the product. Each module has an Odoo
tests/package with at least smoke tests (install, create core record, run the key compute). A product without tests is not a product. - Demo data ≠ config data. Demo records go under
demo/and load only in demo mode. Real client config goes in the deployment layer. Never ship TNCSC data inside a product module'sdemo/.
4. Module map
Product layer (brand‑neutral, sellable)
| Module | Purpose | Core deps (CE only) |
|---|---|---|
community_theme_base |
Configurable brand tokens (colours, logo, fonts) via settings; base website layout hooks | website |
community_membership |
Member profiles, configurable tiers, family grouping, renewal automation, portal, QR membership card | contacts, account, portal, website |
event_qr_ticketing |
Extends CE events: QR ticket per registration, check‑in/verification screen, scan endpoint | event, event_sale, website_event |
community_school |
Programs/levels, classes & schedules, students, enrollment, attendance, LMS link, parent portal | contacts, portal, website_slides, account |
community_classifieds |
Member‑gated classifieds board with moderation queue and auto‑expiry | website, portal |
community_benefits |
Benefit centres / partner vendors offering member benefits; per‑tier benefit entitlements; redemption log | contacts, community_membership |
community_interac |
Interac e‑Transfer semi‑automated payment provider (Canada) | payment, account |
community_portal |
Unified member portal dashboard aggregating the above (soft‑detects installed modules) | portal |
Deployment layer (per client — data only)
| Module | Purpose |
|---|---|
tncsc_deployment |
Depends on the product modules TNCSC uses; seeds TNCSC branding, tiers/prices, chart of accounts, email copy (EN + Tamil), website pages, user groups, and demo/import glue. No models, no business logic. |
5. Repo & naming conventions
communityos/ # one git repo, all modules
├── .github/workflows/ci.yml # lint + brand‑leak grep + module install test
├── addons/
│ ├── community_theme_base/
│ ├── community_membership/
│ ├── event_qr_ticketing/
│ ├── community_school/
│ ├── community_classifieds/
│ ├── community_benefits/
│ ├── community_interac/
│ ├── community_portal/
│ └── tncsc_deployment/
├── deploy/
│ ├── docker-compose.yml # Odoo 19 + Postgres 16 for local dev
│ ├── odoo.conf
│ └── nginx-communityos.conf
├── scripts/
│ ├── migrate_members.py
│ ├── migrate_students.py
│ └── migrate_classroom.py
├── .claude/mcp_servers.json # Odoo MCP config
├── LICENSE # LGPL‑3 text
└── README.md
Conventions to give Claude Code once, at the top of every session:
- Odoo 19.0; Python 3.12; models use type‑annotated fields where practical.
- Every model
_nameuses the module's own namespace (community.membership.tier, nottncsc.*). - Every module:
__manifest__.py,models/,views/,security/ir.model.access.csv,data/,demo/,tests/,README.rst,static/description/index.html. - Commit per module‑deliverable:
feat(community_membership): tier model + renewal cron.
Phase 0 — Dev environment, repo, and CI guardrails
Effort: ~3 hrs · Modules: repo scaffolding
Objective
A running Odoo 19 dev stack in Docker, an empty but fully‑scaffolded monorepo, and a CI check that fails the build if a product module leaks a client name or an Enterprise dependency.
Prompt for Claude Code — Session 0‑A (stack + scaffold)
We are building "CommunityOS", a suite of brand‑neutral Odoo 19 Community modules.
Target: Odoo 19.0 CE, Postgres 16, Python 3.12.
Tasks:
1. Create deploy/docker-compose.yml running Odoo 19 CE + Postgres 16, mounting ./addons
as a custom addons path, with a dev odoo.conf (addons_path, admin_passwd, dev=reload).
2. Create the repo structure exactly as in section 5 of the plan (empty module folders with
placeholder __init__.py and __manifest__.py where noted).
3. For EACH product module, generate a complete __manifest__.py with App‑Store‑ready metadata:
name, version '19.0.1.0.0', category, summary, author 'Metatroncube', website, license 'LGPL-3',
depends (from the module map), and empty data/assets lists.
4. Add LICENSE (LGPL‑3) and a README.md describing the suite and the layered architecture.
Do not write business logic yet. Confirm `docker compose up` starts Odoo and the modules list appears.
Prompt for Claude Code — Session 0‑B (CI guardrails)
Add .github/workflows/ci.yml that, on every push:
1. Spins up Odoo 19 + Postgres 16 and installs every module in addons/ with --test-enable, failing on
any install or test error.
2. Runs a "brand leak" check: grep the community_* modules (code, data, views) for a banned‑words list
(tncsc, tamil nadu, and any client email/domain). If found in a product module, fail the build.
The deployment modules (*_deployment) are exempt.
3. Runs flake8/pylint‑odoo and validates each manifest has license, version, author, summary.
Write the workflow and a scripts/check_brand_leak.py it calls.
Deliverables
docker compose up serves Odoo 19; all modules appear in Apps; CI is green on the empty scaffold.
Validation gate
- Odoo 19 login loads locally.
- Every module installs clean (empty).
- CI brand‑leak check fails when you temporarily add the word "TNCSC" to a product module, and
passes when it's only in
tncsc_deployment. (Prove the guardrail actually works.)
Phase 1 — community_membership (the foundation)
Effort: ~8–10 hrs
Objective
A fully generic membership engine: configurable tiers, family grouping, member IDs, renewal automation, self‑service portal, and a QR membership card — with zero client specifics in code.
Prompt for Claude Code — Session 1‑A (models & configuration)
Build the community_membership module for Odoo 19 CE. Brand‑neutral — no client names anywhere.
Models:
- community.membership.tier: name, code, price (Monetary), period (Selection: annual, one_time,
monthly), member_state_logic, sequence, product_id (auto‑created membership product), active.
Tiers are DATA the admin manages — never hardcode tier names or prices.
- Extend res.partner with membership fields, all namespaced:
membership_member_id (Char, auto‑format configurable via settings, default 'MEM-{year}-{seq}'),
membership_tier_id (M2o community.membership.tier), membership_state (Selection: none, invoiced,
active, renewal_due, expired), membership_start, membership_expiry,
family_head_id (M2o res.partner), is_volunteer (Bool), volunteer_hours (Float).
- community.membership.settings via res.config.settings: organization display name, member‑ID format
string, renewal reminder offsets (days), portal feature toggles.
Requirements:
- Member‑ID auto‑generates on transition to 'active' using the configurable format string (ir.sequence).
- ir.model.access.csv + record rules (a member reads only their own partner in portal).
- Basic list/form/kanban views + a Membership settings page.
- tests/: install, create tier, create member, assert member_id generated on activation.
Write complete, working code following Odoo 19 conventions.
Prompt for Claude Code — Session 1‑B (renewal automation)
Add to community_membership:
- ir.cron (daily) that flags members to 'renewal_due' at the configured offsets and 'expired' on expiry.
- Configurable email templates (reference by XML id, content overridable by the deployment layer):
renewal_upcoming, renewal_due, membership_expired. Templates use ${object...} placeholders only —
no hardcoded org name; pull org name from settings.
- Auto‑create a draft renewal invoice for the member's tier product on expiry (state=draft for review).
- tests/: set expiry within an offset, run cron, assert state change + mail.mail queued + draft invoice.
Prompt for Claude Code — Session 1‑C (QR membership card + portal)
Add to community_membership:
1. A QWeb PDF report "Membership Card" (85mm×54mm landscape): org logo (from theme settings), member
name, member ID (monospace), tier badge, valid‑until, and a QR code encoding a verification URL
'/membership/verify/<member_id>'. Generate the QR with the python 'qrcode' library, embedded base64.
2. A public verification controller GET /membership/verify/<member_id> that returns a minimal page
showing valid / expired / not‑found (no personal data beyond name + status).
3. Portal: /my/membership showing status, tier, expiry, a Renew button (opens the draft invoice),
and a "Download card" button. Use Odoo 19 portal mixins; no custom login.
Write report, controller, portal templates, and tests for the verify endpoint (valid + expired + unknown).
Deliverables
Installable membership module: tiers as data, auto member IDs, renewal crons + emails, draft renewal invoices, QR card PDF, portal page, public verify endpoint, passing tests.
Validation gate (via Odoo MCP)
- Create 3 tiers and 3 members; confirm member IDs generate on activation.
- Force one member's expiry near an offset; run the cron; confirm state + queued email + draft invoice.
- Render a card PDF; scan the QR; confirm the verify page shows the right status.
Resellability check
- Grep: no client name, email, colour, price literal, or account code in the module.
- Org name, member‑ID format, reminder offsets all come from settings.
- Manifest App‑Store‑complete; tests pass under
--test-enable.
Phase 2 — event_qr_ticketing
Effort: ~6–8 hrs
Objective
Extend Odoo 19 CE events with a scannable QR ticket per registration and a staff check‑in / verification screen — self‑contained and sellable on its own.
Prompt for Claude Code — Session 2‑A (ticket + QR)
Build event_qr_ticketing (depends: event, event_sale, website_event). Brand‑neutral.
Extend event.registration:
- ticket_ref (Char, unique, auto e.g. 'TIX-{event}-{seq}'), ticket_qr (Binary, computed QR PNG),
checked_in (Bool), check_in_time (Datetime), check_in_user_id (M2o res.users).
QR encodes a signed token (event_id + registration_id + ticket_ref) — use a per‑db secret so tickets
can't be forged. Add the QR + ticket ref to the CE registration confirmation email as an attachment.
Add a "Ticket" QWeb PDF. tests/: create registration, assert ticket_ref + qr + valid token.
Prompt for Claude Code — Session 2‑B (check‑in / verification)
Add to event_qr_ticketing a staff check‑in flow:
- Backend action + a mobile‑friendly portal/website page /event/checkin (group: event user) that
accepts a scanned token (camera via html5 QR lib from CDN, or manual ticket_ref entry) and returns:
valid + not‑yet‑checked‑in (mark checked_in, stamp time/user), already‑checked‑in (warn), or invalid.
- Guard against double check‑in and forged/edited tokens (verify the signature).
- A simple per‑event dashboard: registered vs checked‑in count.
Write controller, OWL/JS for the scanner page, and tests for valid / duplicate / forged token.
Deliverables + gate
Installable ticketing module; email carries QR; /event/checkin validates, prevents double/forged
check‑ins; tests pass. MCP gate: register an attendee, scan the token → checked in; scan again →
"already checked in"; tamper with the token → "invalid".
Resellability check
- Works with a stock CE event, no other product module required.
- Signing secret is per‑database (not a constant in code). No client specifics. Manifest complete.
Phase 3 — community_school
Effort: ~10–12 hrs
Objective
A generic education module (programs, levels, classes, students, enrollment, attendance, LMS link, parent portal) suitable for any weekend/community/language school — not just Tamil school.
Prompt for Claude Code — Session 3‑A (core models)
Build community_school (depends: contacts, portal, website_slides, account). Brand‑neutral —
"levels" and "grades" are configurable data, never hardcoded to any curriculum.
Models:
- community.school.term: name, start_date, end_date, registration_open, registration_deadline, state.
- community.school.level: name, code, sequence, min_age, max_age (configurable — e.g. Beginner..Advanced
OR Grade 1..12; the admin defines them).
- community.school.class: name (computed level+term), level_id, term_id, teacher_id (res.partner with
is_teacher), max_students, enrolled_count (computed), weekday, start_time, end_time, location,
slide_channel_id (website_slides course), state.
- community.school.student: partner_id (child), parent_partner_id, date_of_birth, age (computed),
proficiency (Selection, configurable), grade_ref (Char/Selection configurable), health_notes,
emergency_contact_name/phone, enrollment_ids.
- community.school.enrollment: student_id, class_id, term_id (related), enrollment_date, state
(waitlist/enrolled/completed/withdrawn), payment_state, invoice_id, attendance_rate (computed).
- community.school.attendance: enrollment_id, class_id, date, state (present/absent/late/excused), notes.
Add is_teacher Bool to res.partner. Full security + basic views + tests (create term→level→class→student
→enroll, assert enrolled_count + attendance_rate compute).
Prompt for Claude Code — Session 3‑B (teacher attendance, portal)
Add to community_school:
- Portal page /school/attendance (teacher login) listing the teacher's classes for today/this week,
a roster with Present/Absent/Late/Excused per student, and a batch save.
- On 'absent', queue a configurable email to the parent (org name from settings, not hardcoded).
- Admin: attendance report (pivot) + an "at‑risk" flag below a configurable attendance threshold.
Write controller, QWeb, email template, and tests (batch save creates records; absence queues mail).
Prompt for Claude Code — Session 3‑C (registration + LMS)
Add to community_school:
1. A multi‑step website registration at /school/register (parent → student → class+payment) using
session state, creating child res.partner + student + enrollment + a fee invoice. On a full class,
set state=waitlist and notify the first waitlisted when a seat frees (hourly cron).
2. LMS glue: auto‑create a website_slides channel when a class is created; auto‑enrol the student in
the channel when their enrollment confirms; unenrol on withdrawal.
3. A standalone scripts/migrate_classroom.py: input CSV (assignment title + Drive/YouTube URL) → create
slide records (YouTube → embed, docs → note to re‑upload as PDF). This replaces Google Classroom.
Write controllers, templates, the LMS hooks, and the migration script with tests where feasible.
Deliverables + gate
Installable school module; parent completes 3‑step registration → invoice; teacher marks attendance from portal → absence email; slide channels auto‑create/enrol. MCP gate: run one full parent‑registration end‑to‑end and one teacher attendance batch; confirm invoice, enrollment, channel enrolment, and the absence email.
Resellability check
- Levels/grades/proficiency are admin‑defined data, so the module fits a Grade 1–12 school or a Beginner–Advanced language school with no code change. No client names. Manifest complete.
Phase 4 — community_classifieds + community_benefits
Effort: ~7–9 hrs
Objective
Two more sellable modules: a member‑gated classifieds board, and a member‑benefits / benefit‑centres engine (your "benefit centers per membership" requirement, done generically).
Prompt for Claude Code — Session 4‑A (classifieds)
Build community_classifieds (depends: website, portal). Brand‑neutral.
Model community.classified: title, category (configurable Selection), description (Html), image_ids
(One2many, up to 3), contact_method, contact_email/phone, poster_partner_id (auto), post_date,
expiry_date (auto = post_date + configurable days), state (pending_review/published/expired/rejected),
admin_notes, view_count.
Routes: /classifieds (public list + filter/search), /classifieds/<id> (detail, increments views),
/classifieds/new (POST — requires portal login AND active membership if community_membership is
installed; soft‑detect it), /classifieds/my, /classifieds/<id>/renew.
Admin moderation queue with Publish/Reject. Crons: expire past‑date listings; 7‑day expiry warning;
notify admin on new submission. Access rules: public reads published only; members create/edit own.
Write full module + tests (post → pending; publish → visible; expiry cron archives).
Prompt for Claude Code — Session 4‑B (benefits / benefit centres)
Build community_benefits (depends: contacts, community_membership). Brand‑neutral.
Models:
- community.benefit.partner: a benefit centre / vendor (res.partner link, category, description, logo,
locations, active). These are businesses that give members a benefit.
- community.benefit: name, benefit_partner_id, description, tier_ids (M2m community.membership.tier —
which tiers get it), discount_type (percent/amount/perk), value, valid_from/valid_to, active.
- community.benefit.redemption: member_id, benefit_id, date, verified_by, notes — a log so a benefit
centre can verify + record a member using a benefit (reuse the membership QR verify endpoint to
confirm the member is active before redemption).
Portal: /my/benefits lists the benefits the member's tier entitles them to. Public directory
/benefits lists participating centres. tests/: entitlement resolves by tier; redemption logs.
Deliverables + gate
Both modules installable; classifieds gated to active members; benefits resolve by tier and a redemption can be logged against a QR‑verified member. MCP gate: post+publish a classified; entitle a tier to a benefit and log one redemption for an active member; confirm a non‑member is blocked from posting.
Resellability check
community_benefitssoft‑degrades ifcommunity_membershipis absent (or hard‑depends by design — decide and document). Categories configurable. No client specifics. Manifests complete.
Phase 5 — community_interac (optional payment provider)
Effort: ~5–7 hrs · Highest‑risk module — do it in isolation
Objective
A generic Interac e‑Transfer semi‑automated payment provider for the Odoo 19 payment framework — sellable to any Canadian org.
Prompt for Claude Code — Session 5‑A
Build community_interac (depends: payment, account) as a proper Odoo 19 payment provider.
IMPORTANT: read the Odoo 19 source for an existing provider (addons/payment_* on the 19.0 branch)
before coding, because the provider/transaction API is strict. Implement:
- A payment.provider of a new custom code; a payment.method; the required _get_specific_* hooks.
- Flow: user picks Interac at checkout → transaction state 'pending' → auto‑email configurable
instructions (recipient e‑transfer address, amount, reference code 'REF-{tx}', deadline). Org name +
e‑transfer address come from provider config, NOT hardcoded.
- Admin: a filtered "Pending Interac" view with a one‑click "Payment received" that sets the tx 'done'
(which confirms the linked order/registration/invoice).
- Cron: auto‑cancel + email if not confirmed within a configurable window.
Write the provider, templates, admin action, cron, and tests (pending on select; done on confirm;
auto‑cancel after window). Verify against the v19 payment API, not older tutorials.
Deliverables + gate
Interac appears as a checkout option; instructions email fires; treasurer one‑click confirms; unconfirmed auto‑cancels. MCP gate: run a pending→confirmed cycle and a pending→auto‑cancel cycle.
Resellability check
- Recipient address, org name, deadline, and copy are provider config, not literals. No client names. Registers cleanly without patching core payment code. Manifest complete.
Phase 6 — community_portal + community_theme_base
Effort: ~5–6 hrs
Objective
The brand‑neutral presentation layer: a configurable theme (colours/logo/fonts as settings) and a unified member portal dashboard that soft‑detects whichever product modules are installed.
Prompt for Claude Code — Session 6‑A (theme base)
Build community_theme_base (depends: website). Brand‑neutral.
Expose brand tokens via res.config.settings: primary colour, secondary colour, accent colour, logo,
heading font, body font. Inject them as CSS variables into the website + portal + PDF reports so a
deployment can rebrand with data only — no SCSS edits. Provide a clean default navbar/footer layout
that reads these tokens. No client colours in code; ship neutral defaults.
Prompt for Claude Code — Session 6‑B (unified portal)
Build community_portal (depends: portal). It renders a member dashboard at /my that aggregates cards
from whichever modules are installed, using SOFT detection (check the registry / try/except import),
so it works if only some products are installed:
- Membership card (if community_membership), Upcoming registered events + tickets (if event_qr_ticketing),
School: child schedule/attendance (if community_school), My benefits (if community_benefits),
My classifieds (if community_classifieds).
Write the controller with soft‑detection and the QWeb dashboard. tests/: dashboard renders with any
subset of modules installed.
Deliverables + gate
Rebranding is data‑only; the portal shows exactly the cards for installed modules. MCP gate: change theme colours in settings → site + card PDF reflect them; uninstall one product module → its portal card disappears without error.
Resellability check
- Neutral default palette; all brand values are settings. Portal never hard‑crashes on a missing module. Manifests complete.
Phase 7 — tncsc_deployment (the client layer)
Effort: ~5–6 hrs · This is where ALL TNCSC specifics finally live
Objective
A single data‑only module that turns the generic suite into TNCSC's system: branding, tiers, prices, chart of accounts, email copy (English + Tamil), website pages, and user groups. No models, no logic.
Prompt for Claude Code — Session 7‑A
Build tncsc_deployment. It depends on the product modules TNCSC uses and contains DATA ONLY
(no Python models, no business logic — data/ XML and CSV, plus assets for branding).
Seed:
- Branding (community_theme_base settings): navy #05091E, electric blue #0EA5FF, orange #F97316; TNCSC
logo; Inter font.
- Membership tiers/prices: Individual $50, Family $80, Student $20, Senior $30, Life $500 (CAD).
- Member‑ID format 'TNCSC-{year}-{seq}'.
- Chart of accounts + journals for a Canadian non‑profit (membership dues, event revenue, sponsorship,
school fees, donations; deferred event revenue liability; HST codes; the bank/Stripe/AR accounts).
- Email template overrides with TNCSC voice, bilingual EN/Tamil subject+body.
- Website pages: Home, About, Tamil School, Sponsors, Contact (using the theme + dynamic blocks).
- User groups: Board Admin, Treasurer, Events Officer, School Coordinator, Teacher (portal), Classifieds
Moderator, Member (portal), with ir.model.access + record rules.
Confirm installing ONLY tncsc_deployment (which pulls the product modules as deps) yields a fully
branded, TNCSC‑configured system. Verify no business logic crept into this module.
Deliverables + gate
Installing tncsc_deployment on a clean DB produces a complete, branded TNCSC deployment. Gate: the
brand‑leak CI still passes (client data is confined to this module); a fresh DB + tncsc_deployment
boots into TNCSC's configured system.
Phase 8 — TNCSC data migration
Effort: ~4–5 hrs
Objective
Import TNCSC's existing members, students, and history into the deployed system.
Prompt for Claude Code — Session 8‑A
Write idempotent migration scripts (standalone, using Odoo JSON‑RPC — not MCP) against the deployed DB:
- scripts/migrate_members.py: clean (phone/email/date normalise, dedupe by email), upsert res.partner +
membership tier/state, output a migration report CSV (processed/created/updated/failed + reasons).
- scripts/migrate_students.py: upsert child + parent partners, community.school.student + current‑term
enrollment where the class exists.
- scripts/migrate_opening_balances.py: one posted journal entry dated the last fiscal year‑end.
Inputs live in data/raw/ (gitignored). Each script re‑runnable safely. Include a dry‑run flag.
Deliverables + gate
Members and students imported with a report; opening balances posted. Gate: re‑running a script makes no duplicate records (idempotency proven).
Phase 9 — Packaging for resale + go‑live
Effort: ~6–8 hrs
Objective
Two outcomes at once: TNCSC goes live, and the product layer is packaged so you can sell it.
Prompt for Claude Code — Session 9‑A (productize)
Prepare the product layer for sale:
1. For each community_* module: write static/description/index.html (App‑Store listing page), a
README.rst (features, install, configuration, screenshots), a CHANGELOG, and confirm the manifest
price/currency/images/license are set.
2. Generate a one‑page "CommunityOS" suite overview (features + which modules are independent vs bundled).
3. Run the full test suite and the brand‑leak check across the whole repo; produce a release report.
4. Tag a release v1.0.0 and document the exact steps to install the suite on a fresh Odoo 19.
Prompt for Claude Code — Session 9‑B (TNCSC go‑live)
Production hardening + go‑live for TNCSC (deploy/ configs):
- odoo.conf: workers, proxy_mode, list_db=False, admin password; nginx TLS + gzip + static caching;
UFW (22/80/443), fail2ban; daily pg_dump + off‑site sync; log rotation; uptime check.
- DNS cutover checklist and a post‑cutover smoke test of the 4 critical flows (membership+pay, event
register+checkin, school register, portal login). Output as a runnable, commented script.
Also write short role guides (admin, treasurer, teacher, parent) as Markdown.
Deliverables — final gate
- TNCSC live on production, SSL + backups verified, 4 critical flows tested by a board member.
- Product layer: every
community_*module has a listing page, README, passing tests, complete manifest, and a tagged v1.0.0 release — i.e. it is ready to list/sell independently of TNCSC.
Per‑module resellability checklist (apply to every community_* module)
- License
LGPL-3(orOPL-1if intentionally paid‑closed, Community deps only). - Manifest complete: name, version
19.0.x.y.z, author, website, category, summary, description, price, currency, images, license, depends (CE‑only). - No client name / email / colour / price / account literal anywhere (CI grep passes).
- All client‑variable behaviour is settings/data, not code.
security/ir.model.access.csv+ record rules present and correct.tests/present; passes under--test-enable.demo/data is generic and demo‑only; no real client data.static/description/index.html+README.rstwritten.- Installs standalone on a clean Odoo 19 with only its declared deps.
Appendix A — Allowed dependency allowlist (Odoo 19 Community only)
base, web, mail, contacts, portal, website, website_sale, event, event_sale,
website_event, website_slides, account, crm, purchase, mass_mailing, payment,
payment_stripe. If a depends entry is not on this list, verify it is Community (not Enterprise)
before adding it. Never depend on: sale_subscription, documents, helpdesk, planning,
appointment, marketing_automation, social, voip, web_studio, or anything else Enterprise‑only.
Appendix B — Dependency graph (product layer)
community_theme_base ─────────────┐
community_membership ──┬──────────┤
event_qr_ticketing ──┤ │
community_school ──┤ ├──► community_portal (soft‑detects all)
community_classifieds ──┤ │
community_benefits ──► community_membership
community_interac (independent; enhances checkout wherever payment is used)
tncsc_deployment ──► (depends on whichever of the above TNCSC ships)
Appendix C — Session context to paste at the top of every Claude Code session
We are building CommunityOS: brand‑neutral, resellable Odoo 19 Community modules.
Repo: monorepo under addons/. Target Odoo 19.0 CE, Python 3.12, Postgres 16. Odoo MCP is connected.
RULES: (1) product modules (community_*) contain NO client names/prices/colours/accounts — those are
settings/data only; (2) depend on Community core only (see allowlist), never Enterprise; (3) every
module keeps an App‑Store‑ready manifest and a tests/ package; (4) read the Odoo 19 source when unsure
of an API instead of trusting older tutorials. Read existing module files before editing. Commit per
deliverable.
Plan version 1.0 · Target Odoo 19 CE · Metatroncube Software Solutions LLP Built for execution by Claude Code + Odoo MCP · TNCSC = first deployment of a resellable product suite.