TNCSC_Odoo/CommunityOS_Implementation_Plan_for_Claude_Code.md
metatroncubeswdev a848373429 feat(scaffold): Phase 0 - repo scaffold, Docker dev stack, CI guardrails
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>
2026-08-17 18:48:01 -04:00

37 KiB
Raw Permalink Blame History

CommunityOS — Odoo 19 Product Suite: Implementation Plan for Claude Code

A brandneutral, 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 (LGPL3) · 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 findreplace at the very end, not midbuild.


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 subphase. It is deliberately different from a singleclient build plan in one important way:

Everything is built twiceover in structure, once in effort. Each capability is built as a generic, brandneutral 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 copypaste 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 producthygiene 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 oneoff 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  (brandneutral, resellable, LGPL3)          │
│  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: productlayer modules never mention "TNCSC," never hardcode a price, a colour, an account code, or an email address. Anything clientspecific 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 cofounder:

  • 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 thirdparty addons 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 reporting.
  • Tradeoff 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.

Forwardportability rule: avoid private/underscoreprefixed 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, wellscoped task rather than a rewrite.


3. Licensing & resellability guardrails (nonnegotiable)

These are the rules that keep every module something you can legally brand and sell.

  1. License each product module LGPL-3 in 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 paidbutclosed module for the App Store, use OPL-1 (Odoo Proprietary License) — but only for modules that depend on Communityonly code.
  2. Never depend on an Odoo Enterprise module. The moment a depends entry 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.
  3. 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).
  4. Configuration over hardcoding. Tiers, fees, branding, accounts, and copy are records/settings, seeded by the deployment layer — never literals in Python.
  5. AppStoreready manifests. Every product module carries author, website, license, category, version (semantic, 19.0.1.0.0), summary, description, price, currency, and an images/ banner from day one. Filling these at the end is a scramble; fill them as you go.
  6. 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_school enhances event_qr_ticketing if present, via a soft check, rather than harddepending on it).
  7. 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.
  8. 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's demo/.

4. Module map

Product layer (brandneutral, 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, checkin/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 Membergated classifieds board with moderation queue and autoexpiry website, portal
community_benefits Benefit centres / partner vendors offering member benefits; pertier benefit entitlements; redemption log contacts, community_membership
community_interac Interac eTransfer semiautomated payment provider (Canada) payment, account
community_portal Unified member portal dashboard aggregating the above (softdetects 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 + brandleak 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                           # LGPL3 text
└── README.md

Conventions to give Claude Code once, at the top of every session:

  • Odoo 19.0; Python 3.12; models use typeannotated fields where practical.
  • Every model _name uses the module's own namespace (community.membership.tier, not tncsc.*).
  • Every module: __manifest__.py, models/, views/, security/ir.model.access.csv, data/, demo/, tests/, README.rst, static/description/index.html.
  • Commit per moduledeliverable: 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 fullyscaffolded 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 0A (stack + scaffold)

We are building "CommunityOS", a suite of brandneutral 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 AppStoreready 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 (LGPL3) 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 0B (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 bannedwords 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/pylintodoo 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 brandleak 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: ~810 hrs

Objective

A fully generic membership engine: configurable tiers, family grouping, member IDs, renewal automation, selfservice portal, and a QR membership card — with zero client specifics in code.

Prompt for Claude Code — Session 1A (models & configuration)

Build the community_membership module for Odoo 19 CE. Brandneutral — no client names anywhere.

Models:
- community.membership.tier: name, code, price (Monetary), period (Selection: annual, one_time,
  monthly), member_state_logic, sequence, product_id (autocreated 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, autoformat 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, memberID format
  string, renewal reminder offsets (days), portal feature toggles.

Requirements:
- MemberID autogenerates 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 1B (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.
- Autocreate 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 1C (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, validuntil, 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 / notfound (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, memberID format, reminder offsets all come from settings.
  • Manifest AppStorecomplete; tests pass under --test-enable.

Phase 2 — event_qr_ticketing

Effort: ~68 hrs

Objective

Extend Odoo 19 CE events with a scannable QR ticket per registration and a staff checkin / verification screen — selfcontained and sellable on its own.

Prompt for Claude Code — Session 2A (ticket + QR)

Build event_qr_ticketing (depends: event, event_sale, website_event). Brandneutral.
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 perdb 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 2B (checkin / verification)

Add to event_qr_ticketing a staff checkin flow:
- Backend action + a mobilefriendly 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 + notyetcheckedin (mark checked_in, stamp time/user), alreadycheckedin (warn), or invalid.
- Guard against double checkin and forged/edited tokens (verify the signature).
- A simple perevent dashboard: registered vs checkedin 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 checkins; 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 perdatabase (not a constant in code). No client specifics. Manifest complete.

Phase 3 — community_school

Effort: ~1012 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 3A (core models)

Build community_school (depends: contacts, portal, website_slides, account). Brandneutral —
"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 3B (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 "atrisk" 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 3C (registration + LMS)

Add to community_school:
1. A multistep 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: autocreate a website_slides channel when a class is created; autoenrol 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 reupload 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 3step registration → invoice; teacher marks attendance from portal → absence email; slide channels autocreate/enrol. MCP gate: run one full parentregistration endtoend and one teacher attendance batch; confirm invoice, enrollment, channel enrolment, and the absence email.

Resellability check

  • Levels/grades/proficiency are admindefined data, so the module fits a Grade 112 school or a BeginnerAdvanced language school with no code change. No client names. Manifest complete.

Phase 4 — community_classifieds + community_benefits

Effort: ~79 hrs

Objective

Two more sellable modules: a membergated classifieds board, and a memberbenefits / benefitcentres engine (your "benefit centers per membership" requirement, done generically).

Prompt for Claude Code — Session 4A (classifieds)

Build community_classifieds (depends: website, portal). Brandneutral.
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; softdetect it), /classifieds/my, /classifieds/<id>/renew.
Admin moderation queue with Publish/Reject. Crons: expire pastdate listings; 7day 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 4B (benefits / benefit centres)

Build community_benefits (depends: contacts, community_membership). Brandneutral.
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 QRverified member. MCP gate: post+publish a classified; entitle a tier to a benefit and log one redemption for an active member; confirm a nonmember is blocked from posting.

Resellability check

  • community_benefits softdegrades if community_membership is absent (or harddepends by design — decide and document). Categories configurable. No client specifics. Manifests complete.

Phase 5 — community_interac (optional payment provider)

Effort: ~57 hrs · Highestrisk module — do it in isolation

Objective

A generic Interac eTransfer semiautomated payment provider for the Odoo 19 payment framework — sellable to any Canadian org.

Prompt for Claude Code — Session 5A

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' → autoemail configurable
  instructions (recipient etransfer address, amount, reference code 'REF-{tx}', deadline). Org name +
  etransfer address come from provider config, NOT hardcoded.
- Admin: a filtered "Pending Interac" view with a oneclick "Payment received" that sets the tx 'done'
  (which confirms the linked order/registration/invoice).
- Cron: autocancel + email if not confirmed within a configurable window.
Write the provider, templates, admin action, cron, and tests (pending on select; done on confirm;
autocancel after window). Verify against the v19 payment API, not older tutorials.

Deliverables + gate

Interac appears as a checkout option; instructions email fires; treasurer oneclick confirms; unconfirmed autocancels. MCP gate: run a pending→confirmed cycle and a pending→autocancel 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: ~56 hrs

Objective

The brandneutral presentation layer: a configurable theme (colours/logo/fonts as settings) and a unified member portal dashboard that softdetects whichever product modules are installed.

Prompt for Claude Code — Session 6A (theme base)

Build community_theme_base (depends: website). Brandneutral.
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 6B (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 softdetection and the QWeb dashboard. tests/: dashboard renders with any
subset of modules installed.

Deliverables + gate

Rebranding is dataonly; 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 hardcrashes on a missing module. Manifests complete.

Phase 7 — tncsc_deployment (the client layer)

Effort: ~56 hrs · This is where ALL TNCSC specifics finally live

Objective

A single dataonly 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 7A

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).
- MemberID format 'TNCSC-{year}-{seq}'.
- Chart of accounts + journals for a Canadian nonprofit (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, TNCSCconfigured 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 brandleak 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: ~45 hrs

Objective

Import TNCSC's existing members, students, and history into the deployed system.

Prompt for Claude Code — Session 8A

Write idempotent migration scripts (standalone, using Odoo JSONRPC — 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 + currentterm
  enrollment where the class exists.
- scripts/migrate_opening_balances.py: one posted journal entry dated the last fiscal yearend.
Inputs live in data/raw/ (gitignored). Each script rerunnable safely. Include a dryrun flag.

Deliverables + gate

Members and students imported with a report; opening balances posted. Gate: rerunning a script makes no duplicate records (idempotency proven).


Phase 9 — Packaging for resale + golive

Effort: ~68 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 9A (productize)

Prepare the product layer for sale:
1. For each community_* module: write static/description/index.html (AppStore listing page), a
   README.rst (features, install, configuration, screenshots), a CHANGELOG, and confirm the manifest
   price/currency/images/license are set.
2. Generate a onepage "CommunityOS" suite overview (features + which modules are independent vs bundled).
3. Run the full test suite and the brandleak 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 9B (TNCSC golive)

Production hardening + golive 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 + offsite sync; log rotation; uptime check.
- DNS cutover checklist and a postcutover 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.

Permodule resellability checklist (apply to every community_* module)

  • License LGPL-3 (or OPL-1 if intentionally paidclosed, Community deps only).
  • Manifest complete: name, version 19.0.x.y.z, author, website, category, summary, description, price, currency, images, license, depends (CEonly).
  • No client name / email / colour / price / account literal anywhere (CI grep passes).
  • All clientvariable 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 demoonly; no real client data.
  • static/description/index.html + README.rst written.
  • 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 Enterpriseonly.

Appendix B — Dependency graph (product layer)

community_theme_base  ─────────────┐
community_membership  ──┬──────────┤
event_qr_ticketing    ──┤          │
community_school      ──┤          ├──►  community_portal (softdetects 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: brandneutral, 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 AppStoreready 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.