From a848373429f7e721a0605b0a60d05191ca90f489 Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Mon, 17 Aug 2026 18:48:01 -0400 Subject: [PATCH] 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 --- .claude/mcp_servers.json | 15 + .github/workflows/ci.yml | 69 ++ .gitignore | 19 + ...yOS_Implementation_Plan_for_Claude_Code.md | 664 ++++++++++++++++++ LICENSE | 165 +++++ README.md | 93 +++ addons/community_benefits/README.rst | 22 + addons/community_benefits/__init__.py | 0 addons/community_benefits/__manifest__.py | 33 + addons/community_benefits/data/.gitkeep | 0 addons/community_benefits/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/community_benefits/tests/__init__.py | 0 addons/community_benefits/views/.gitkeep | 0 addons/community_classifieds/README.rst | 21 + addons/community_classifieds/__init__.py | 0 addons/community_classifieds/__manifest__.py | 33 + addons/community_classifieds/data/.gitkeep | 0 addons/community_classifieds/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 8 + .../community_classifieds/tests/__init__.py | 0 addons/community_classifieds/views/.gitkeep | 0 addons/community_interac/README.rst | 22 + addons/community_interac/__init__.py | 0 addons/community_interac/__manifest__.py | 35 + addons/community_interac/data/.gitkeep | 0 addons/community_interac/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 10 + addons/community_interac/tests/__init__.py | 0 addons/community_interac/views/.gitkeep | 0 addons/community_membership/README.rst | 26 + addons/community_membership/__init__.py | 0 addons/community_membership/__manifest__.py | 37 + addons/community_membership/data/.gitkeep | 0 addons/community_membership/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/community_membership/tests/__init__.py | 0 addons/community_membership/views/.gitkeep | 0 addons/community_portal/README.rst | 22 + addons/community_portal/__init__.py | 0 addons/community_portal/__manifest__.py | 33 + addons/community_portal/data/.gitkeep | 0 addons/community_portal/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/community_portal/tests/__init__.py | 0 addons/community_portal/views/.gitkeep | 0 addons/community_school/README.rst | 22 + addons/community_school/__init__.py | 0 addons/community_school/__manifest__.py | 37 + addons/community_school/data/.gitkeep | 0 addons/community_school/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/community_school/tests/__init__.py | 0 addons/community_school/views/.gitkeep | 0 addons/community_theme_base/README.rst | 27 + addons/community_theme_base/__init__.py | 0 addons/community_theme_base/__manifest__.py | 33 + addons/community_theme_base/data/.gitkeep | 0 addons/community_theme_base/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/community_theme_base/tests/__init__.py | 0 addons/community_theme_base/views/.gitkeep | 0 addons/event_qr_ticketing/README.rst | 22 + addons/event_qr_ticketing/__init__.py | 0 addons/event_qr_ticketing/__manifest__.py | 36 + addons/event_qr_ticketing/data/.gitkeep | 0 addons/event_qr_ticketing/demo/.gitkeep | 0 .../security/ir.model.access.csv | 1 + .../static/description/index.html | 9 + addons/event_qr_ticketing/tests/__init__.py | 0 addons/event_qr_ticketing/views/.gitkeep | 0 addons/tncsc_deployment/__init__.py | 0 addons/tncsc_deployment/__manifest__.py | 42 ++ data/raw/.gitkeep | 0 deploy/docker-compose.yml | 31 + deploy/nginx-communityos.conf | 43 ++ deploy/odoo.conf | 13 + scripts/check_brand_leak.py | 101 +++ scripts/check_manifests.py | 62 ++ 86 files changed, 1858 insertions(+) create mode 100644 .claude/mcp_servers.json create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 CommunityOS_Implementation_Plan_for_Claude_Code.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 addons/community_benefits/README.rst create mode 100644 addons/community_benefits/__init__.py create mode 100644 addons/community_benefits/__manifest__.py create mode 100644 addons/community_benefits/data/.gitkeep create mode 100644 addons/community_benefits/demo/.gitkeep create mode 100644 addons/community_benefits/security/ir.model.access.csv create mode 100644 addons/community_benefits/static/description/index.html create mode 100644 addons/community_benefits/tests/__init__.py create mode 100644 addons/community_benefits/views/.gitkeep create mode 100644 addons/community_classifieds/README.rst create mode 100644 addons/community_classifieds/__init__.py create mode 100644 addons/community_classifieds/__manifest__.py create mode 100644 addons/community_classifieds/data/.gitkeep create mode 100644 addons/community_classifieds/demo/.gitkeep create mode 100644 addons/community_classifieds/security/ir.model.access.csv create mode 100644 addons/community_classifieds/static/description/index.html create mode 100644 addons/community_classifieds/tests/__init__.py create mode 100644 addons/community_classifieds/views/.gitkeep create mode 100644 addons/community_interac/README.rst create mode 100644 addons/community_interac/__init__.py create mode 100644 addons/community_interac/__manifest__.py create mode 100644 addons/community_interac/data/.gitkeep create mode 100644 addons/community_interac/demo/.gitkeep create mode 100644 addons/community_interac/security/ir.model.access.csv create mode 100644 addons/community_interac/static/description/index.html create mode 100644 addons/community_interac/tests/__init__.py create mode 100644 addons/community_interac/views/.gitkeep create mode 100644 addons/community_membership/README.rst create mode 100644 addons/community_membership/__init__.py create mode 100644 addons/community_membership/__manifest__.py create mode 100644 addons/community_membership/data/.gitkeep create mode 100644 addons/community_membership/demo/.gitkeep create mode 100644 addons/community_membership/security/ir.model.access.csv create mode 100644 addons/community_membership/static/description/index.html create mode 100644 addons/community_membership/tests/__init__.py create mode 100644 addons/community_membership/views/.gitkeep create mode 100644 addons/community_portal/README.rst create mode 100644 addons/community_portal/__init__.py create mode 100644 addons/community_portal/__manifest__.py create mode 100644 addons/community_portal/data/.gitkeep create mode 100644 addons/community_portal/demo/.gitkeep create mode 100644 addons/community_portal/security/ir.model.access.csv create mode 100644 addons/community_portal/static/description/index.html create mode 100644 addons/community_portal/tests/__init__.py create mode 100644 addons/community_portal/views/.gitkeep create mode 100644 addons/community_school/README.rst create mode 100644 addons/community_school/__init__.py create mode 100644 addons/community_school/__manifest__.py create mode 100644 addons/community_school/data/.gitkeep create mode 100644 addons/community_school/demo/.gitkeep create mode 100644 addons/community_school/security/ir.model.access.csv create mode 100644 addons/community_school/static/description/index.html create mode 100644 addons/community_school/tests/__init__.py create mode 100644 addons/community_school/views/.gitkeep create mode 100644 addons/community_theme_base/README.rst create mode 100644 addons/community_theme_base/__init__.py create mode 100644 addons/community_theme_base/__manifest__.py create mode 100644 addons/community_theme_base/data/.gitkeep create mode 100644 addons/community_theme_base/demo/.gitkeep create mode 100644 addons/community_theme_base/security/ir.model.access.csv create mode 100644 addons/community_theme_base/static/description/index.html create mode 100644 addons/community_theme_base/tests/__init__.py create mode 100644 addons/community_theme_base/views/.gitkeep create mode 100644 addons/event_qr_ticketing/README.rst create mode 100644 addons/event_qr_ticketing/__init__.py create mode 100644 addons/event_qr_ticketing/__manifest__.py create mode 100644 addons/event_qr_ticketing/data/.gitkeep create mode 100644 addons/event_qr_ticketing/demo/.gitkeep create mode 100644 addons/event_qr_ticketing/security/ir.model.access.csv create mode 100644 addons/event_qr_ticketing/static/description/index.html create mode 100644 addons/event_qr_ticketing/tests/__init__.py create mode 100644 addons/event_qr_ticketing/views/.gitkeep create mode 100644 addons/tncsc_deployment/__init__.py create mode 100644 addons/tncsc_deployment/__manifest__.py create mode 100644 data/raw/.gitkeep create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/nginx-communityos.conf create mode 100644 deploy/odoo.conf create mode 100644 scripts/check_brand_leak.py create mode 100644 scripts/check_manifests.py diff --git a/.claude/mcp_servers.json b/.claude/mcp_servers.json new file mode 100644 index 0000000..de8b648 --- /dev/null +++ b/.claude/mcp_servers.json @@ -0,0 +1,15 @@ +{ + "_comment": "No Odoo MCP server is connected in this environment yet. This is a template — fill in a real Odoo MCP server command/args (or url) and Claude Code will pick it up. Until then, module validation is done directly against the deploy/docker-compose.yml stack via JSON-RPC / docker exec.", + "mcpServers": { + "odoo": { + "command": "REPLACE_ME", + "args": [], + "env": { + "ODOO_URL": "http://localhost:8069", + "ODOO_DB": "communityos_dev", + "ODOO_USER": "admin", + "ODOO_PASSWORD": "admin" + } + } + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7d154c5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,69 @@ +name: CI + +on: + push: + pull_request: + +jobs: + brand-leak: + name: Brand-leak check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Run brand-leak check on community_* modules + run: python scripts/check_brand_leak.py + + lint: + name: flake8 / pylint-odoo / manifest completeness + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install linters + run: pip install flake8 pylint pylint-odoo + - name: flake8 + run: flake8 addons --max-line-length=120 --extend-ignore=E203,W503 + - name: pylint-odoo + run: pylint --load-plugins=pylint_odoo -d all -e odoolint --rcfile=.pylintrc-odoo addons || true + - name: Manifest completeness + run: python scripts/check_manifests.py + + install-and-test: + name: Install all modules with --test-enable + runs-on: ubuntu-latest + services: + db: + image: postgres:16 + env: + POSTGRES_USER: odoo + POSTGRES_PASSWORD: odoo + POSTGRES_DB: postgres + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U odoo" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + + - name: Determine module list + id: modules + run: echo "list=$(ls addons | paste -sd, -)" >> "$GITHUB_OUTPUT" + + - name: Install all Community OS modules with tests + run: | + docker run --rm \ + --network host \ + -v "${{ github.workspace }}/addons:/mnt/extra-addons" \ + -e HOST=localhost -e USER=odoo -e PASSWORD=odoo \ + odoo:19.0 \ + odoo -d communityos_ci --addons-path=/mnt/extra-addons,/usr/lib/python3/dist-packages/odoo/addons \ + -i "${{ steps.modules.outputs.list }}" \ + --test-enable --stop-after-init --max-cron-threads=0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..918e572 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +__pycache__/ +*.pyc +*.pyo +.venv/ +venv/ +*.egg-info/ +.vscode/ +.idea/ + +# Odoo runtime +*.log +odoo-filestore/ + +# Client data, never committed +data/raw/* +!data/raw/.gitkeep +*.csv.bak + +.env diff --git a/CommunityOS_Implementation_Plan_for_Claude_Code.md b/CommunityOS_Implementation_Plan_for_Claude_Code.md new file mode 100644 index 0000000..d639c69 --- /dev/null +++ b/CommunityOS_Implementation_Plan_for_Claude_Code.md @@ -0,0 +1,664 @@ +# 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.* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8a7604 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Community OS + +A brand-neutral, resellable suite of Odoo 19 Community modules for +community and cultural organizations. First deployment: the Tamil Nadu +Cultural Society of Canada (TNCSC). + +**Vendor:** Metatroncube Software Solutions LLP · Waterloo, Ontario +**Target platform:** Odoo 19 Community Edition (LGPL-3) · PostgreSQL 16 · Python 3.12 + +> `community_*` is a placeholder module prefix. Replace it with a unique +> vendor prefix before publishing to the Odoo App Store so technical names +> never collide with anything already listed. + +## Layered architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ DEPLOYMENT LAYER (per client — data only, no logic) │ +│ tncsc_deployment: branding, tiers & prices, chart of │ +│ accounts, email copy, website pages, user groups │ +└───────────────▲─────────────────────────────────────────────┘ + │ depends on +┌───────────────┴─────────────────────────────────────────────┐ +│ PRODUCT LAYER (brand-neutral, resellable, LGPL-3) │ +│ community_membership event_qr_ticketing community_school │ +│ community_classifieds community_benefits community_interac│ +│ community_theme_base community_portal │ +└───────────────▲─────────────────────────────────────────────┘ + │ depends only on +┌───────────────┴─────────────────────────────────────────────┐ +│ ODOO 19 COMMUNITY CORE (never Enterprise) │ +└─────────────────────────────────────────────────────────────┘ +``` + +Product-layer modules never mention a client name, never hardcode a price, +a colour, an account code, or an email address. Anything client-specific +is a configuration record, seeded only by a `*_deployment` module. Landing +a new client means writing a new `clientname_deployment` module — the +product code never changes. + +## Modules + +### Product layer (brand-neutral, sellable) + +| Module | Purpose | +|---|---| +| `community_theme_base` | Configurable brand tokens (colours, logo, fonts) via settings | +| `community_membership` | Member profiles, tiers, family grouping, renewals, QR membership card | +| `event_qr_ticketing` | QR ticket per event registration, staff check-in / verification | +| `community_school` | Programs, classes, enrollment, attendance, LMS link, parent portal | +| `community_classifieds` | Member-gated classifieds board with moderation and auto-expiry | +| `community_benefits` | Benefit centres and per-tier member benefit entitlements | +| `community_interac` | Interac e-Transfer semi-automated payment provider (Canada) | +| `community_portal` | Unified member portal dashboard, soft-detects installed modules | + +### Deployment layer (per client — data only) + +| Module | Purpose | +|---|---| +| `tncsc_deployment` | TNCSC branding, tiers/prices, chart of accounts, email copy, website pages, user groups | + +## Local development + +```bash +cd deploy +docker compose up +``` + +Odoo will be available at http://localhost:8069. The `../addons` folder is +mounted read-write, so changes to module code are picked up on restart (dev +mode reload is enabled in `deploy/odoo.conf`). + +## Licensing & resellability guardrails + +1. Every product module is licensed `LGPL-3`. +2. No `community_*` module may depend on an Odoo Enterprise module — see + `Appendix A` in the project plan for the Community-only allowlist. +3. No client identity (name, email, colour, price, account code) may appear + in a product module. This is enforced in CI by + `scripts/check_brand_leak.py`. +4. All client-variable behaviour is configuration data, seeded by the + deployment layer, never a literal in product code. +5. Every product module ships an App-Store-ready manifest, a `tests/` + package, and a `static/description/index.html` listing page. + +See `CommunityOS_Implementation_Plan_for_Claude_Code.md` for the full, +phase-by-phase build plan. + +## CI + +`.github/workflows/ci.yml` installs every module against Odoo 19 + +Postgres 16 with `--test-enable`, runs the brand-leak grep, and lints with +flake8 / pylint-odoo. diff --git a/addons/community_benefits/README.rst b/addons/community_benefits/README.rst new file mode 100644 index 0000000..3256a78 --- /dev/null +++ b/addons/community_benefits/README.rst @@ -0,0 +1,22 @@ +====================== +Community OS Benefits +====================== + +Benefit centres / partner vendors offering member benefits, entitled per +membership tier, with a redemption log. + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 4). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_benefits/__init__.py b/addons/community_benefits/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_benefits/__manifest__.py b/addons/community_benefits/__manifest__.py new file mode 100644 index 0000000..bdfb588 --- /dev/null +++ b/addons/community_benefits/__manifest__.py @@ -0,0 +1,33 @@ +{ + 'name': 'Community OS - Benefits', + 'version': '19.0.1.0.0', + 'category': 'Sales/Members', + 'summary': 'Benefit centres / partner vendors offering per-tier member benefits, with redemption logging', + 'description': """ +Community OS - Benefits +========================= + +Benefit centres (partner vendors) offering discounts or perks to members, +entitled per membership tier: + +* Benefit centre directory (public) +* Benefits entitled by membership tier +* Redemption log, verified against an active membership (reuses the + Community OS Membership QR verification endpoint) +* Member portal page listing the benefits their tier entitles them to +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'contacts', + 'community_membership', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/community_benefits/data/.gitkeep b/addons/community_benefits/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_benefits/demo/.gitkeep b/addons/community_benefits/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_benefits/security/ir.model.access.csv b/addons/community_benefits/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_benefits/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_benefits/static/description/index.html b/addons/community_benefits/static/description/index.html new file mode 100644 index 0000000..21c7480 --- /dev/null +++ b/addons/community_benefits/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — Benefits

+

Partner benefit centres, entitled per membership tier

+

List partner vendors offering member perks, entitle benefits by + membership tier, and log redemptions verified against an active + membership.

+
+
diff --git a/addons/community_benefits/tests/__init__.py b/addons/community_benefits/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_benefits/views/.gitkeep b/addons/community_benefits/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_classifieds/README.rst b/addons/community_classifieds/README.rst new file mode 100644 index 0000000..dd4191f --- /dev/null +++ b/addons/community_classifieds/README.rst @@ -0,0 +1,21 @@ +======================== +Community OS Classifieds +======================== + +Member-gated classifieds board with a moderation queue and auto-expiry. + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 4). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_classifieds/__init__.py b/addons/community_classifieds/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_classifieds/__manifest__.py b/addons/community_classifieds/__manifest__.py new file mode 100644 index 0000000..c3bc7df --- /dev/null +++ b/addons/community_classifieds/__manifest__.py @@ -0,0 +1,33 @@ +{ + 'name': 'Community OS - Classifieds', + 'version': '19.0.1.0.0', + 'category': 'Website/Website', + 'summary': 'Member-gated classifieds board with moderation queue and auto-expiry', + 'description': """ +Community OS - Classifieds +============================ + +A member-gated classifieds board: + +* Public listing with search/filter, private posting for logged-in portal + users (requires active membership if Community OS Membership is installed) +* Admin moderation queue (publish / reject) +* Configurable auto-expiry, expiry-warning, and new-submission notifications + +Soft-detects Community OS Membership; runs standalone without it. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'website', + 'portal', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/community_classifieds/data/.gitkeep b/addons/community_classifieds/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_classifieds/demo/.gitkeep b/addons/community_classifieds/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_classifieds/security/ir.model.access.csv b/addons/community_classifieds/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_classifieds/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_classifieds/static/description/index.html b/addons/community_classifieds/static/description/index.html new file mode 100644 index 0000000..285c31d --- /dev/null +++ b/addons/community_classifieds/static/description/index.html @@ -0,0 +1,8 @@ +
+
+

Community OS — Classifieds

+

A member-only classifieds board, moderated and self-expiring

+

Members post listings from the portal; admins moderate a queue before + publishing. Listings expire automatically on a configurable schedule.

+
+
diff --git a/addons/community_classifieds/tests/__init__.py b/addons/community_classifieds/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_classifieds/views/.gitkeep b/addons/community_classifieds/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_interac/README.rst b/addons/community_interac/README.rst new file mode 100644 index 0000000..a045473 --- /dev/null +++ b/addons/community_interac/README.rst @@ -0,0 +1,22 @@ +================================= +Community OS Interac e-Transfer +================================= + +Semi-automated Interac e-Transfer payment provider for Canadian +organizations, built on the Odoo 19 payment framework. + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 5). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_interac/__init__.py b/addons/community_interac/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_interac/__manifest__.py b/addons/community_interac/__manifest__.py new file mode 100644 index 0000000..4ab2b59 --- /dev/null +++ b/addons/community_interac/__manifest__.py @@ -0,0 +1,35 @@ +{ + 'name': 'Community OS - Interac e-Transfer Payments', + 'version': '19.0.1.0.0', + 'category': 'Accounting/Payment Providers', + 'summary': 'Semi-automated Interac e-Transfer payment provider for Canadian organizations', + 'description': """ +Community OS - Interac e-Transfer Payments +============================================= + +A generic Interac e-Transfer payment provider for the Odoo 19 payment +framework: + +* Configurable recipient e-transfer address, org name, and deadline + (provider config, never hardcoded) +* Automated instructions email with a unique reference code +* Treasurer "Pending Interac" view with one-click "Payment received" +* Configurable auto-cancel window for unconfirmed transfers + +Sellable to any Canadian organization accepting Interac e-Transfer. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'payment', + 'account', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/community_interac/data/.gitkeep b/addons/community_interac/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_interac/demo/.gitkeep b/addons/community_interac/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_interac/security/ir.model.access.csv b/addons/community_interac/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_interac/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_interac/static/description/index.html b/addons/community_interac/static/description/index.html new file mode 100644 index 0000000..e7bf5fd --- /dev/null +++ b/addons/community_interac/static/description/index.html @@ -0,0 +1,10 @@ +
+
+

Community OS — Interac e-Transfer

+

Semi-automated Interac e-Transfer for Odoo checkout

+

Accept Interac e-Transfer at checkout with automated instructions, + a treasurer one-click confirmation screen, and configurable + auto-cancel for unconfirmed transfers. Built for Canadian + non-profits and community organizations.

+
+
diff --git a/addons/community_interac/tests/__init__.py b/addons/community_interac/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_interac/views/.gitkeep b/addons/community_interac/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_membership/README.rst b/addons/community_membership/README.rst new file mode 100644 index 0000000..fe770ec --- /dev/null +++ b/addons/community_membership/README.rst @@ -0,0 +1,26 @@ +====================== +Community OS Membership +====================== + +Generic membership engine: configurable tiers, family grouping, member IDs, +renewal automation, self-service portal, and a QR membership card. + +Installation +============ +Install as any Odoo module. + +Configuration +============= +Settings > Members > Membership Settings. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 1). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_membership/__init__.py b/addons/community_membership/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_membership/__manifest__.py b/addons/community_membership/__manifest__.py new file mode 100644 index 0000000..15a84c5 --- /dev/null +++ b/addons/community_membership/__manifest__.py @@ -0,0 +1,37 @@ +{ + 'name': 'Community OS - Membership', + 'version': '19.0.1.0.0', + 'category': 'Sales/Members', + 'summary': 'Configurable membership tiers, family grouping, renewals, QR membership card and self-service portal', + 'description': """ +Community OS - Membership +=========================== + +A fully generic membership engine for community and cultural organizations: + +* Admin-configurable membership tiers (name, price, period) - never hardcoded +* Family grouping and auto-generated member IDs (configurable format) +* Automated renewal reminders, expiry handling, and draft renewal invoices +* Self-service member portal (status, tier, expiry, renew, download card) +* QR-coded membership card (PDF) with a public verification endpoint + +No client names, prices, or branding are hardcoded - everything is a +configuration record managed by the deployment layer. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'contacts', + 'account', + 'portal', + 'website', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': True, + 'installable': True, +} diff --git a/addons/community_membership/data/.gitkeep b/addons/community_membership/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_membership/demo/.gitkeep b/addons/community_membership/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_membership/security/ir.model.access.csv b/addons/community_membership/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_membership/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_membership/static/description/index.html b/addons/community_membership/static/description/index.html new file mode 100644 index 0000000..2563900 --- /dev/null +++ b/addons/community_membership/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — Membership

+

Tiers, renewals, and a QR membership card — fully self-service

+

Configurable membership tiers, family grouping, auto-generated member IDs, + automated renewal reminders and draft invoices, a self-service portal, and + a scannable QR membership card with public verification.

+
+
diff --git a/addons/community_membership/tests/__init__.py b/addons/community_membership/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_membership/views/.gitkeep b/addons/community_membership/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_portal/README.rst b/addons/community_portal/README.rst new file mode 100644 index 0000000..5c0b6ca --- /dev/null +++ b/addons/community_portal/README.rst @@ -0,0 +1,22 @@ +============================ +Community OS Unified Portal +============================ + +Unified member portal dashboard aggregating cards from whichever Community +OS product modules are installed (soft-detected). + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 6). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_portal/__init__.py b/addons/community_portal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_portal/__manifest__.py b/addons/community_portal/__manifest__.py new file mode 100644 index 0000000..fb77ab5 --- /dev/null +++ b/addons/community_portal/__manifest__.py @@ -0,0 +1,33 @@ +{ + 'name': 'Community OS - Unified Portal', + 'version': '19.0.1.0.0', + 'category': 'Website/Website', + 'summary': 'Unified member portal dashboard that soft-detects whichever Community OS modules are installed', + 'description': """ +Community OS - Unified Portal +================================ + +A single /my dashboard that aggregates cards from whichever Community OS +product modules are installed, using soft detection so it never crashes on +a missing module: + +* Membership card (Community OS Membership) +* Upcoming registered events + tickets (Event QR Ticketing) +* School schedule / attendance (Community OS School) +* My benefits (Community OS Benefits) +* My classifieds (Community OS Classifieds) +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'portal', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/community_portal/data/.gitkeep b/addons/community_portal/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_portal/demo/.gitkeep b/addons/community_portal/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_portal/security/ir.model.access.csv b/addons/community_portal/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_portal/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_portal/static/description/index.html b/addons/community_portal/static/description/index.html new file mode 100644 index 0000000..effea22 --- /dev/null +++ b/addons/community_portal/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — Unified Portal

+

One dashboard for everything a member needs

+

Aggregates membership, events, school, benefits, and classifieds + into a single member dashboard — automatically adapting to + whichever Community OS modules are installed.

+
+
diff --git a/addons/community_portal/tests/__init__.py b/addons/community_portal/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_portal/views/.gitkeep b/addons/community_portal/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_school/README.rst b/addons/community_school/README.rst new file mode 100644 index 0000000..0f76402 --- /dev/null +++ b/addons/community_school/README.rst @@ -0,0 +1,22 @@ +=================== +Community OS School +=================== + +Programs/levels, classes & schedules, students, enrollment, attendance, LMS +link, and parent portal for any weekend/community/language school. + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 3). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_school/__init__.py b/addons/community_school/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_school/__manifest__.py b/addons/community_school/__manifest__.py new file mode 100644 index 0000000..83c0ba1 --- /dev/null +++ b/addons/community_school/__manifest__.py @@ -0,0 +1,37 @@ +{ + 'name': 'Community OS - School', + 'version': '19.0.1.0.0', + 'category': 'Website/Education', + 'summary': 'Programs, levels, classes, enrollment, attendance and parent portal for any weekend/community school', + 'description': """ +Community OS - School +======================= + +A generic education module for weekend, language, cultural, or community +schools - levels/grades/proficiency are admin-defined data, so the same +module fits a Grade 1-12 school or a Beginner-Advanced language school +with no code change. + +* Terms, levels, classes, students, and enrollment +* Teacher attendance portal with parent absence notifications +* Multi-step online registration (parent -> student -> class + payment) +* Waitlist handling with automatic seat-freed notifications +* Optional LMS glue via website_slides (auto channel per class) +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'contacts', + 'portal', + 'website_slides', + 'account', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': True, + 'installable': True, +} diff --git a/addons/community_school/data/.gitkeep b/addons/community_school/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_school/demo/.gitkeep b/addons/community_school/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_school/security/ir.model.access.csv b/addons/community_school/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_school/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_school/static/description/index.html b/addons/community_school/static/description/index.html new file mode 100644 index 0000000..4e1e265 --- /dev/null +++ b/addons/community_school/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — School

+

Programs, classes, attendance, and a parent portal

+

Runs any weekend, language, or cultural school: admin-defined levels and + grades, online registration with waitlisting, teacher attendance from the + portal, and optional LMS integration.

+
+
diff --git a/addons/community_school/tests/__init__.py b/addons/community_school/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_school/views/.gitkeep b/addons/community_school/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_theme_base/README.rst b/addons/community_theme_base/README.rst new file mode 100644 index 0000000..b305070 --- /dev/null +++ b/addons/community_theme_base/README.rst @@ -0,0 +1,27 @@ +======================== +Community OS Theme Base +======================== + +Configurable brand tokens (colours, logo, fonts) for community and cultural +organizations, exposed as settings and injected as CSS variables across the +website, member portal, and PDF reports. + +Installation +============ +Install as any Odoo module. + +Configuration +============= +Settings > Website > Community OS Theme. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 6). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/community_theme_base/__init__.py b/addons/community_theme_base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_theme_base/__manifest__.py b/addons/community_theme_base/__manifest__.py new file mode 100644 index 0000000..a7bb227 --- /dev/null +++ b/addons/community_theme_base/__manifest__.py @@ -0,0 +1,33 @@ +{ + 'name': 'Community OS - Theme Base', + 'version': '19.0.1.0.0', + 'category': 'Website/Theme', + 'summary': 'Configurable brand tokens (colours, logo, fonts) for community organizations', + 'description': """ +Community OS - Theme Base +========================== + +Brand-neutral theming foundation for the Community OS suite. + +Exposes primary/secondary/accent colours, logo, and heading/body fonts as +settings, and injects them as CSS variables across the website, the member +portal, and PDF reports - so a deployment can be rebranded with data only, +no SCSS edits required. + +Ships with a clean, neutral default palette and a simple navbar/footer +layout that reads these tokens. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'website', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/community_theme_base/data/.gitkeep b/addons/community_theme_base/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_theme_base/demo/.gitkeep b/addons/community_theme_base/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_theme_base/security/ir.model.access.csv b/addons/community_theme_base/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/community_theme_base/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/community_theme_base/static/description/index.html b/addons/community_theme_base/static/description/index.html new file mode 100644 index 0000000..f85dae0 --- /dev/null +++ b/addons/community_theme_base/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — Theme Base

+

Configurable brand tokens for community & cultural organizations

+

Set primary/secondary/accent colours, logo, and fonts once in Settings — + they flow into your website, member portal, and PDF reports automatically. + No SCSS edits, no developer needed to rebrand.

+
+
diff --git a/addons/community_theme_base/tests/__init__.py b/addons/community_theme_base/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/community_theme_base/views/.gitkeep b/addons/community_theme_base/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/event_qr_ticketing/README.rst b/addons/event_qr_ticketing/README.rst new file mode 100644 index 0000000..a77ccc5 --- /dev/null +++ b/addons/event_qr_ticketing/README.rst @@ -0,0 +1,22 @@ +=========================== +Community OS Event QR Ticketing +=========================== + +Signed QR ticket per event registration, with a staff check-in and +verification screen. Works standalone on a stock Community Edition event. + +Installation +============ +Install as any Odoo module. + +Usage +===== +Content to be completed as the module is built out (see project plan, +Phase 2). + +Credits +======= + +Authors +------- +* Metatroncube Software Solutions LLP diff --git a/addons/event_qr_ticketing/__init__.py b/addons/event_qr_ticketing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/event_qr_ticketing/__manifest__.py b/addons/event_qr_ticketing/__manifest__.py new file mode 100644 index 0000000..6590ca8 --- /dev/null +++ b/addons/event_qr_ticketing/__manifest__.py @@ -0,0 +1,36 @@ +{ + 'name': 'Community OS - Event QR Ticketing', + 'version': '19.0.1.0.0', + 'category': 'Marketing/Events', + 'summary': 'Signed QR tickets per registration with a staff check-in and verification screen', + 'description': """ +Community OS - Event QR Ticketing +=================================== + +Extends standard Odoo Community events with: + +* A signed QR ticket per registration (forgery-resistant, per-database secret) +* Ticket PDF and QR attachment on the registration confirmation email +* A mobile-friendly staff check-in page: scan or manual entry, duplicate and + forged-token protection +* A simple per-event registered vs checked-in dashboard + +Works standalone with a stock Community Edition event - no other Community +OS product module required. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'price': 0.00, + 'currency': 'USD', + 'depends': [ + 'event', + 'event_sale', + 'website_event', + ], + 'data': [], + 'demo': [], + 'images': ['static/description/banner.png'], + 'application': False, + 'installable': True, +} diff --git a/addons/event_qr_ticketing/data/.gitkeep b/addons/event_qr_ticketing/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/event_qr_ticketing/demo/.gitkeep b/addons/event_qr_ticketing/demo/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/event_qr_ticketing/security/ir.model.access.csv b/addons/event_qr_ticketing/security/ir.model.access.csv new file mode 100644 index 0000000..97dd8b9 --- /dev/null +++ b/addons/event_qr_ticketing/security/ir.model.access.csv @@ -0,0 +1 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink diff --git a/addons/event_qr_ticketing/static/description/index.html b/addons/event_qr_ticketing/static/description/index.html new file mode 100644 index 0000000..c37476f --- /dev/null +++ b/addons/event_qr_ticketing/static/description/index.html @@ -0,0 +1,9 @@ +
+
+

Community OS — Event QR Ticketing

+

Signed QR tickets and a mobile check-in screen

+

Every registration gets a forgery-resistant QR ticket, emailed automatically. + Staff scan attendees in from any phone or tablet — with duplicate and + tamper protection built in.

+
+
diff --git a/addons/event_qr_ticketing/tests/__init__.py b/addons/event_qr_ticketing/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/event_qr_ticketing/views/.gitkeep b/addons/event_qr_ticketing/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addons/tncsc_deployment/__init__.py b/addons/tncsc_deployment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/addons/tncsc_deployment/__manifest__.py b/addons/tncsc_deployment/__manifest__.py new file mode 100644 index 0000000..397417b --- /dev/null +++ b/addons/tncsc_deployment/__manifest__.py @@ -0,0 +1,42 @@ +{ + 'name': 'TNCSC Deployment', + 'version': '19.0.1.0.0', + 'category': 'Hidden', + 'summary': 'Tamil Nadu Cultural Society of Canada - client configuration for the Community OS suite', + 'description': """ +TNCSC Deployment +================== + +Deployment layer for the Tamil Nadu Cultural Society of Canada (TNCSC). + +This module contains DATA ONLY - no models, no business logic. It seeds: + +* Branding (Community OS Theme Base settings) +* Membership tiers and prices +* Chart of accounts and journals for a Canadian non-profit +* Bilingual (EN/Tamil) email template overrides +* Website pages +* User groups and access rules + +All client-specific values (names, prices, colours, account codes, email +copy) live only in this module. It is exempt from the brand-leak CI check +that applies to the community_* product modules. +""", + 'author': 'Metatroncube Software Solutions LLP', + 'website': 'https://www.metatroncube.com', + 'license': 'LGPL-3', + 'depends': [ + 'community_theme_base', + 'community_membership', + 'event_qr_ticketing', + 'community_school', + 'community_classifieds', + 'community_benefits', + 'community_interac', + 'community_portal', + ], + 'data': [], + 'demo': [], + 'application': False, + 'installable': True, +} diff --git a/data/raw/.gitkeep b/data/raw/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..fc51af6 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,31 @@ +services: + db: + image: postgres:16 + environment: + POSTGRES_USER: odoo + POSTGRES_PASSWORD: odoo + POSTGRES_DB: postgres + volumes: + - communityos_db:/var/lib/postgresql/data + restart: unless-stopped + + odoo: + image: odoo:19.0 + depends_on: + - db + ports: + - "8069:8069" + environment: + HOST: db + USER: odoo + PASSWORD: odoo + volumes: + - ../addons:/mnt/extra-addons + - ./odoo.conf:/etc/odoo/odoo.conf + - communityos_data:/var/lib/odoo + command: ["odoo", "-c", "/etc/odoo/odoo.conf"] + restart: unless-stopped + +volumes: + communityos_db: + communityos_data: diff --git a/deploy/nginx-communityos.conf b/deploy/nginx-communityos.conf new file mode 100644 index 0000000..75f0ff0 --- /dev/null +++ b/deploy/nginx-communityos.conf @@ -0,0 +1,43 @@ +# Community OS - nginx reverse proxy for Odoo 19 +# Development skeleton. Production hardening (TLS, gzip, static caching, +# security headers) is completed in Phase 9 (go-live). + +upstream odoo { + server 127.0.0.1:8069; +} + +upstream odoo-chat { + server 127.0.0.1:8072; +} + +server { + listen 80; + server_name _; + + proxy_read_timeout 720s; + proxy_connect_timeout 720s; + proxy_send_timeout 720s; + + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + + location /websocket { + proxy_pass http://odoo-chat; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_redirect off; + proxy_pass http://odoo; + } + + location ~* /web/static/ { + proxy_cache_valid 200 90m; + proxy_buffering on; + expires 864000; + proxy_pass http://odoo; + } +} diff --git a/deploy/odoo.conf b/deploy/odoo.conf new file mode 100644 index 0000000..544df98 --- /dev/null +++ b/deploy/odoo.conf @@ -0,0 +1,13 @@ +[options] +admin_passwd = $pbkdf2-sha512$600000$l5KS8v6/977XOgcAIITwvg$F92iQue/QiuEv1Jgf8xefn.ikVIanCsopqLdhpWbFal1DnM7UqCRtZyPwFr4DsVL5hHEOuFcfkrRH5Wv6sDSuQ +db_host = db +db_port = 5432 +db_user = odoo +db_password = odoo +addons_path = /mnt/extra-addons +dev_mode = reload,qweb,werkzeug,xml +list_db = True +proxy_mode = False +logfile = +log_level = info + diff --git a/scripts/check_brand_leak.py b/scripts/check_brand_leak.py new file mode 100644 index 0000000..dd8a244 --- /dev/null +++ b/scripts/check_brand_leak.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""Fail if a client-specific term appears inside a product (community_*) module. + +Deployment modules (matching *_deployment) are exempt, since client identity +is expected to live there. Run from the repo root: + + python scripts/check_brand_leak.py +""" +import re +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +ADDONS_DIR = REPO_ROOT / "addons" + +BANNED_WORDS = [ + "tncsc", + "tamil nadu", + "tamil nadu cultural society", +] + +BANNED_PATTERNS = [ + re.compile(r"\b" + re.escape(word).replace(r"\ ", r"\s+") + r"\b", re.IGNORECASE) + for word in BANNED_WORDS +] + +# Any email/domain that identifies the specific client deployment. +BANNED_DOMAIN_PATTERN = re.compile(r"\btncsc\.(ca|org|com)\b", re.IGNORECASE) + +SCAN_EXTENSIONS = {".py", ".xml", ".csv", ".js", ".rst", ".md", ".html", ".txt"} + +# Never scan the plan itself, or this checker's own word list. +EXCLUDE_FILENAMES = {"check_brand_leak.py"} + + +def is_deployment_module(module_dir: Path) -> bool: + return module_dir.name.endswith("_deployment") + + +def is_product_module(module_dir: Path) -> bool: + return module_dir.is_dir() and not module_dir.name.startswith(".") + + +def iter_product_files(): + if not ADDONS_DIR.exists(): + return + for module_dir in sorted(ADDONS_DIR.iterdir()): + if not is_product_module(module_dir) or is_deployment_module(module_dir): + continue + for path in module_dir.rglob("*"): + if not path.is_file(): + continue + if path.name in EXCLUDE_FILENAMES: + continue + if path.suffix.lower() not in SCAN_EXTENSIONS: + continue + yield path + + +def scan_file(path: Path): + """Return a list of (line_no, line_text, matched_term) violations.""" + violations = [] + try: + text = path.read_text(encoding="utf-8", errors="ignore") + except OSError: + return violations + + for line_no, line in enumerate(text.splitlines(), start=1): + for pattern in BANNED_PATTERNS: + if pattern.search(line): + violations.append((line_no, line.strip(), pattern.pattern)) + if BANNED_DOMAIN_PATTERN.search(line): + violations.append((line_no, line.strip(), BANNED_DOMAIN_PATTERN.pattern)) + return violations + + +def main() -> int: + if not ADDONS_DIR.exists(): + print(f"No addons/ directory found at {ADDONS_DIR}; nothing to check.") + return 0 + + any_violation = False + for path in iter_product_files(): + for line_no, line_text, term in scan_file(path): + any_violation = True + rel_path = path.relative_to(REPO_ROOT) + print(f"BRAND LEAK: {rel_path}:{line_no}: matched /{term}/ -> {line_text}") + + if any_violation: + print( + "\nOne or more community_* (product) modules reference client-specific " + "identity. Move this content into the appropriate *_deployment module." + ) + return 1 + + print("Brand-leak check passed: no client-specific terms found in product modules.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/check_manifests.py b/scripts/check_manifests.py new file mode 100644 index 0000000..0017fc9 --- /dev/null +++ b/scripts/check_manifests.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +"""Validate that every product module manifest is App-Store-ready. + +Checks community_* modules for the required keys (license, version, author, +summary, category, website, depends). *_deployment modules are exempt from +price/currency/images since they are never sold independently. +""" +import ast +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +ADDONS_DIR = REPO_ROOT / "addons" + +REQUIRED_KEYS = ["name", "version", "author", "website", "license", "category", "summary", "depends"] +PRODUCT_ONLY_KEYS = ["price", "currency", "images"] + + +def load_manifest(path: Path) -> dict: + return ast.literal_eval(path.read_text(encoding="utf-8")) + + +def main() -> int: + if not ADDONS_DIR.exists(): + print(f"No addons/ directory found at {ADDONS_DIR}; nothing to check.") + return 0 + + errors = [] + for module_dir in sorted(ADDONS_DIR.iterdir()): + manifest_path = module_dir / "__manifest__.py" + if not manifest_path.exists(): + continue + + manifest = load_manifest(manifest_path) + is_deployment = module_dir.name.endswith("_deployment") + + required = REQUIRED_KEYS + ([] if is_deployment else PRODUCT_ONLY_KEYS) + for key in required: + if key not in manifest or manifest[key] in (None, "", []): + errors.append(f"{module_dir.name}: missing or empty required manifest key '{key}'") + + if not is_deployment and manifest.get("license") not in ("LGPL-3", "OPL-1"): + errors.append( + f"{module_dir.name}: license must be 'LGPL-3' (or 'OPL-1' for an " + f"intentionally paid-closed module), got {manifest.get('license')!r}" + ) + + if manifest.get("version") and not str(manifest["version"]).startswith("19.0."): + errors.append(f"{module_dir.name}: version should start with '19.0.', got {manifest.get('version')!r}") + + if errors: + print("Manifest completeness check FAILED:\n") + for error in errors: + print(f" - {error}") + return 1 + + print("Manifest completeness check passed.") + return 0 + + +if __name__ == "__main__": + sys.exit(main())