# 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. 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 paid‑but‑closed module for the App Store, use `OPL-1` (Odoo Proprietary License) — but only for modules that depend on **Community‑only** 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. **App‑Store‑ready 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 hard‑depending 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 (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 `_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 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/'. Generate the QR with the python 'qrcode' library, embedded base64. 2. A public verification controller GET /membership/verify/ 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/ (detail, increments views), /classifieds/new (POST — requires portal login AND active membership if community_membership is installed; soft‑detect it), /classifieds/my, /classifieds//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_benefits` soft‑degrades if `community_membership` is 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` (or `OPL-1` if 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.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 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.*