feat(scaffold): Phase 0 - repo scaffold, Docker dev stack, CI guardrails
Scaffolds the CommunityOS monorepo per the implementation plan: 8 brand-neutral product modules (community_theme_base, community_membership, event_qr_ticketing, community_school, community_classifieds, community_benefits, community_interac, community_portal) plus the tncsc_deployment client layer, each with an App-Store-ready manifest, LGPL-3 license, and empty security/data/demo/tests/views scaffolding. Adds deploy/docker-compose.yml (Odoo 19 CE + Postgres 16), CI workflow that installs all modules with --test-enable, and scripts/check_brand_leak.py + check_manifests.py enforcing the no-client-identity-in-product-code and manifest-completeness rules. Verified locally: all 9 modules install clean on a fresh Odoo 19 database, and the brand-leak check correctly fails when a client term is added to a product module and passes once removed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
commit
a848373429
15
.claude/mcp_servers.json
Normal file
15
.claude/mcp_servers.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
.github/workflows/ci.yml
vendored
Normal file
69
.github/workflows/ci.yml
vendored
Normal file
@ -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
|
||||
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -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
|
||||
664
CommunityOS_Implementation_Plan_for_Claude_Code.md
Normal file
664
CommunityOS_Implementation_Plan_for_Claude_Code.md
Normal file
@ -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/<member_id>'. Generate the QR with the python 'qrcode' library, embedded base64.
|
||||
2. A public verification controller GET /membership/verify/<member_id> that returns a minimal page
|
||||
showing valid / expired / not‑found (no personal data beyond name + status).
|
||||
3. Portal: /my/membership showing status, tier, expiry, a Renew button (opens the draft invoice),
|
||||
and a "Download card" button. Use Odoo 19 portal mixins; no custom login.
|
||||
Write report, controller, portal templates, and tests for the verify endpoint (valid + expired + unknown).
|
||||
```
|
||||
|
||||
### Deliverables
|
||||
Installable membership module: tiers as data, auto member IDs, renewal crons + emails, draft renewal
|
||||
invoices, QR card PDF, portal page, public verify endpoint, passing tests.
|
||||
|
||||
### Validation gate (via Odoo MCP)
|
||||
- [ ] Create 3 tiers and 3 members; confirm member IDs generate on activation.
|
||||
- [ ] Force one member's expiry near an offset; run the cron; confirm state + queued email + draft invoice.
|
||||
- [ ] Render a card PDF; scan the QR; confirm the verify page shows the right status.
|
||||
|
||||
### Resellability check
|
||||
- [ ] Grep: no client name, email, colour, price literal, or account code in the module.
|
||||
- [ ] Org name, member‑ID format, reminder offsets all come from settings.
|
||||
- [ ] Manifest App‑Store‑complete; tests pass under `--test-enable`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — `event_qr_ticketing`
|
||||
**Effort: ~6–8 hrs**
|
||||
|
||||
### Objective
|
||||
Extend Odoo 19 CE events with a scannable QR ticket per registration and a staff check‑in /
|
||||
verification screen — self‑contained and sellable on its own.
|
||||
|
||||
### Prompt for Claude Code — Session 2‑A (ticket + QR)
|
||||
```
|
||||
Build event_qr_ticketing (depends: event, event_sale, website_event). Brand‑neutral.
|
||||
Extend event.registration:
|
||||
- ticket_ref (Char, unique, auto e.g. 'TIX-{event}-{seq}'), ticket_qr (Binary, computed QR PNG),
|
||||
checked_in (Bool), check_in_time (Datetime), check_in_user_id (M2o res.users).
|
||||
QR encodes a signed token (event_id + registration_id + ticket_ref) — use a per‑db secret so tickets
|
||||
can't be forged. Add the QR + ticket ref to the CE registration confirmation email as an attachment.
|
||||
Add a "Ticket" QWeb PDF. tests/: create registration, assert ticket_ref + qr + valid token.
|
||||
```
|
||||
|
||||
### Prompt for Claude Code — Session 2‑B (check‑in / verification)
|
||||
```
|
||||
Add to event_qr_ticketing a staff check‑in flow:
|
||||
- Backend action + a mobile‑friendly portal/website page /event/checkin (group: event user) that
|
||||
accepts a scanned token (camera via html5 QR lib from CDN, or manual ticket_ref entry) and returns:
|
||||
valid + not‑yet‑checked‑in (mark checked_in, stamp time/user), already‑checked‑in (warn), or invalid.
|
||||
- Guard against double check‑in and forged/edited tokens (verify the signature).
|
||||
- A simple per‑event dashboard: registered vs checked‑in count.
|
||||
Write controller, OWL/JS for the scanner page, and tests for valid / duplicate / forged token.
|
||||
```
|
||||
|
||||
### Deliverables + gate
|
||||
Installable ticketing module; email carries QR; `/event/checkin` validates, prevents double/forged
|
||||
check‑ins; tests pass. **MCP gate:** register an attendee, scan the token → checked in; scan again →
|
||||
"already checked in"; tamper with the token → "invalid".
|
||||
|
||||
### Resellability check
|
||||
- [ ] Works with a stock CE event, no other product module required.
|
||||
- [ ] Signing secret is per‑database (not a constant in code). No client specifics. Manifest complete.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — `community_school`
|
||||
**Effort: ~10–12 hrs**
|
||||
|
||||
### Objective
|
||||
A generic education module (programs, levels, classes, students, enrollment, attendance, LMS link,
|
||||
parent portal) suitable for any weekend/community/language school — not just Tamil school.
|
||||
|
||||
### Prompt for Claude Code — Session 3‑A (core models)
|
||||
```
|
||||
Build community_school (depends: contacts, portal, website_slides, account). Brand‑neutral —
|
||||
"levels" and "grades" are configurable data, never hardcoded to any curriculum.
|
||||
Models:
|
||||
- community.school.term: name, start_date, end_date, registration_open, registration_deadline, state.
|
||||
- community.school.level: name, code, sequence, min_age, max_age (configurable — e.g. Beginner..Advanced
|
||||
OR Grade 1..12; the admin defines them).
|
||||
- community.school.class: name (computed level+term), level_id, term_id, teacher_id (res.partner with
|
||||
is_teacher), max_students, enrolled_count (computed), weekday, start_time, end_time, location,
|
||||
slide_channel_id (website_slides course), state.
|
||||
- community.school.student: partner_id (child), parent_partner_id, date_of_birth, age (computed),
|
||||
proficiency (Selection, configurable), grade_ref (Char/Selection configurable), health_notes,
|
||||
emergency_contact_name/phone, enrollment_ids.
|
||||
- community.school.enrollment: student_id, class_id, term_id (related), enrollment_date, state
|
||||
(waitlist/enrolled/completed/withdrawn), payment_state, invoice_id, attendance_rate (computed).
|
||||
- community.school.attendance: enrollment_id, class_id, date, state (present/absent/late/excused), notes.
|
||||
Add is_teacher Bool to res.partner. Full security + basic views + tests (create term→level→class→student
|
||||
→enroll, assert enrolled_count + attendance_rate compute).
|
||||
```
|
||||
|
||||
### Prompt for Claude Code — Session 3‑B (teacher attendance, portal)
|
||||
```
|
||||
Add to community_school:
|
||||
- Portal page /school/attendance (teacher login) listing the teacher's classes for today/this week,
|
||||
a roster with Present/Absent/Late/Excused per student, and a batch save.
|
||||
- On 'absent', queue a configurable email to the parent (org name from settings, not hardcoded).
|
||||
- Admin: attendance report (pivot) + an "at‑risk" flag below a configurable attendance threshold.
|
||||
Write controller, QWeb, email template, and tests (batch save creates records; absence queues mail).
|
||||
```
|
||||
|
||||
### Prompt for Claude Code — Session 3‑C (registration + LMS)
|
||||
```
|
||||
Add to community_school:
|
||||
1. A multi‑step website registration at /school/register (parent → student → class+payment) using
|
||||
session state, creating child res.partner + student + enrollment + a fee invoice. On a full class,
|
||||
set state=waitlist and notify the first waitlisted when a seat frees (hourly cron).
|
||||
2. LMS glue: auto‑create a website_slides channel when a class is created; auto‑enrol the student in
|
||||
the channel when their enrollment confirms; unenrol on withdrawal.
|
||||
3. A standalone scripts/migrate_classroom.py: input CSV (assignment title + Drive/YouTube URL) → create
|
||||
slide records (YouTube → embed, docs → note to re‑upload as PDF). This replaces Google Classroom.
|
||||
Write controllers, templates, the LMS hooks, and the migration script with tests where feasible.
|
||||
```
|
||||
|
||||
### Deliverables + gate
|
||||
Installable school module; parent completes 3‑step registration → invoice; teacher marks attendance from
|
||||
portal → absence email; slide channels auto‑create/enrol. **MCP gate:** run one full parent‑registration
|
||||
end‑to‑end and one teacher attendance batch; confirm invoice, enrollment, channel enrolment, and the
|
||||
absence email.
|
||||
|
||||
### Resellability check
|
||||
- [ ] Levels/grades/proficiency are admin‑defined data, so the module fits a Grade 1–12 school *or* a
|
||||
Beginner–Advanced language school with no code change. No client names. Manifest complete.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — `community_classifieds` + `community_benefits`
|
||||
**Effort: ~7–9 hrs**
|
||||
|
||||
### Objective
|
||||
Two more sellable modules: a member‑gated classifieds board, and a member‑benefits / benefit‑centres
|
||||
engine (your "benefit centers per membership" requirement, done generically).
|
||||
|
||||
### Prompt for Claude Code — Session 4‑A (classifieds)
|
||||
```
|
||||
Build community_classifieds (depends: website, portal). Brand‑neutral.
|
||||
Model community.classified: title, category (configurable Selection), description (Html), image_ids
|
||||
(One2many, up to 3), contact_method, contact_email/phone, poster_partner_id (auto), post_date,
|
||||
expiry_date (auto = post_date + configurable days), state (pending_review/published/expired/rejected),
|
||||
admin_notes, view_count.
|
||||
Routes: /classifieds (public list + filter/search), /classifieds/<id> (detail, increments views),
|
||||
/classifieds/new (POST — requires portal login AND active membership if community_membership is
|
||||
installed; soft‑detect it), /classifieds/my, /classifieds/<id>/renew.
|
||||
Admin moderation queue with Publish/Reject. Crons: expire past‑date listings; 7‑day expiry warning;
|
||||
notify admin on new submission. Access rules: public reads published only; members create/edit own.
|
||||
Write full module + tests (post → pending; publish → visible; expiry cron archives).
|
||||
```
|
||||
|
||||
### Prompt for Claude Code — Session 4‑B (benefits / benefit centres)
|
||||
```
|
||||
Build community_benefits (depends: contacts, community_membership). Brand‑neutral.
|
||||
Models:
|
||||
- community.benefit.partner: a benefit centre / vendor (res.partner link, category, description, logo,
|
||||
locations, active). These are businesses that give members a benefit.
|
||||
- community.benefit: name, benefit_partner_id, description, tier_ids (M2m community.membership.tier —
|
||||
which tiers get it), discount_type (percent/amount/perk), value, valid_from/valid_to, active.
|
||||
- community.benefit.redemption: member_id, benefit_id, date, verified_by, notes — a log so a benefit
|
||||
centre can verify + record a member using a benefit (reuse the membership QR verify endpoint to
|
||||
confirm the member is active before redemption).
|
||||
Portal: /my/benefits lists the benefits the member's tier entitles them to. Public directory
|
||||
/benefits lists participating centres. tests/: entitlement resolves by tier; redemption logs.
|
||||
```
|
||||
|
||||
### Deliverables + gate
|
||||
Both modules installable; classifieds gated to active members; benefits resolve by tier and a redemption
|
||||
can be logged against a QR‑verified member. **MCP gate:** post+publish a classified; entitle a tier to a
|
||||
benefit and log one redemption for an active member; confirm a non‑member is blocked from posting.
|
||||
|
||||
### Resellability check
|
||||
- [ ] `community_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.*
|
||||
165
LICENSE
Normal file
165
LICENSE
Normal file
@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
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.
|
||||
93
README.md
Normal file
93
README.md
Normal file
@ -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.
|
||||
22
addons/community_benefits/README.rst
Normal file
22
addons/community_benefits/README.rst
Normal file
@ -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
|
||||
0
addons/community_benefits/__init__.py
Normal file
0
addons/community_benefits/__init__.py
Normal file
33
addons/community_benefits/__manifest__.py
Normal file
33
addons/community_benefits/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_benefits/data/.gitkeep
Normal file
0
addons/community_benefits/data/.gitkeep
Normal file
0
addons/community_benefits/demo/.gitkeep
Normal file
0
addons/community_benefits/demo/.gitkeep
Normal file
1
addons/community_benefits/security/ir.model.access.csv
Normal file
1
addons/community_benefits/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
9
addons/community_benefits/static/description/index.html
Normal file
9
addons/community_benefits/static/description/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Benefits</h2>
|
||||
<h3 class="oe_slogan">Partner benefit centres, entitled per membership tier</h3>
|
||||
<p>List partner vendors offering member perks, entitle benefits by
|
||||
membership tier, and log redemptions verified against an active
|
||||
membership.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_benefits/tests/__init__.py
Normal file
0
addons/community_benefits/tests/__init__.py
Normal file
0
addons/community_benefits/views/.gitkeep
Normal file
0
addons/community_benefits/views/.gitkeep
Normal file
21
addons/community_classifieds/README.rst
Normal file
21
addons/community_classifieds/README.rst
Normal file
@ -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
|
||||
0
addons/community_classifieds/__init__.py
Normal file
0
addons/community_classifieds/__init__.py
Normal file
33
addons/community_classifieds/__manifest__.py
Normal file
33
addons/community_classifieds/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_classifieds/data/.gitkeep
Normal file
0
addons/community_classifieds/data/.gitkeep
Normal file
0
addons/community_classifieds/demo/.gitkeep
Normal file
0
addons/community_classifieds/demo/.gitkeep
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
@ -0,0 +1,8 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Classifieds</h2>
|
||||
<h3 class="oe_slogan">A member-only classifieds board, moderated and self-expiring</h3>
|
||||
<p>Members post listings from the portal; admins moderate a queue before
|
||||
publishing. Listings expire automatically on a configurable schedule.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_classifieds/tests/__init__.py
Normal file
0
addons/community_classifieds/tests/__init__.py
Normal file
0
addons/community_classifieds/views/.gitkeep
Normal file
0
addons/community_classifieds/views/.gitkeep
Normal file
22
addons/community_interac/README.rst
Normal file
22
addons/community_interac/README.rst
Normal file
@ -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
|
||||
0
addons/community_interac/__init__.py
Normal file
0
addons/community_interac/__init__.py
Normal file
35
addons/community_interac/__manifest__.py
Normal file
35
addons/community_interac/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_interac/data/.gitkeep
Normal file
0
addons/community_interac/data/.gitkeep
Normal file
0
addons/community_interac/demo/.gitkeep
Normal file
0
addons/community_interac/demo/.gitkeep
Normal file
1
addons/community_interac/security/ir.model.access.csv
Normal file
1
addons/community_interac/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
10
addons/community_interac/static/description/index.html
Normal file
10
addons/community_interac/static/description/index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Interac e-Transfer</h2>
|
||||
<h3 class="oe_slogan">Semi-automated Interac e-Transfer for Odoo checkout</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_interac/tests/__init__.py
Normal file
0
addons/community_interac/tests/__init__.py
Normal file
0
addons/community_interac/views/.gitkeep
Normal file
0
addons/community_interac/views/.gitkeep
Normal file
26
addons/community_membership/README.rst
Normal file
26
addons/community_membership/README.rst
Normal file
@ -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
|
||||
0
addons/community_membership/__init__.py
Normal file
0
addons/community_membership/__init__.py
Normal file
37
addons/community_membership/__manifest__.py
Normal file
37
addons/community_membership/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_membership/data/.gitkeep
Normal file
0
addons/community_membership/data/.gitkeep
Normal file
0
addons/community_membership/demo/.gitkeep
Normal file
0
addons/community_membership/demo/.gitkeep
Normal file
1
addons/community_membership/security/ir.model.access.csv
Normal file
1
addons/community_membership/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Membership</h2>
|
||||
<h3 class="oe_slogan">Tiers, renewals, and a QR membership card — fully self-service</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_membership/tests/__init__.py
Normal file
0
addons/community_membership/tests/__init__.py
Normal file
0
addons/community_membership/views/.gitkeep
Normal file
0
addons/community_membership/views/.gitkeep
Normal file
22
addons/community_portal/README.rst
Normal file
22
addons/community_portal/README.rst
Normal file
@ -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
|
||||
0
addons/community_portal/__init__.py
Normal file
0
addons/community_portal/__init__.py
Normal file
33
addons/community_portal/__manifest__.py
Normal file
33
addons/community_portal/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_portal/data/.gitkeep
Normal file
0
addons/community_portal/data/.gitkeep
Normal file
0
addons/community_portal/demo/.gitkeep
Normal file
0
addons/community_portal/demo/.gitkeep
Normal file
1
addons/community_portal/security/ir.model.access.csv
Normal file
1
addons/community_portal/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
9
addons/community_portal/static/description/index.html
Normal file
9
addons/community_portal/static/description/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Unified Portal</h2>
|
||||
<h3 class="oe_slogan">One dashboard for everything a member needs</h3>
|
||||
<p>Aggregates membership, events, school, benefits, and classifieds
|
||||
into a single member dashboard — automatically adapting to
|
||||
whichever Community OS modules are installed.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_portal/tests/__init__.py
Normal file
0
addons/community_portal/tests/__init__.py
Normal file
0
addons/community_portal/views/.gitkeep
Normal file
0
addons/community_portal/views/.gitkeep
Normal file
22
addons/community_school/README.rst
Normal file
22
addons/community_school/README.rst
Normal file
@ -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
|
||||
0
addons/community_school/__init__.py
Normal file
0
addons/community_school/__init__.py
Normal file
37
addons/community_school/__manifest__.py
Normal file
37
addons/community_school/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_school/data/.gitkeep
Normal file
0
addons/community_school/data/.gitkeep
Normal file
0
addons/community_school/demo/.gitkeep
Normal file
0
addons/community_school/demo/.gitkeep
Normal file
1
addons/community_school/security/ir.model.access.csv
Normal file
1
addons/community_school/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
9
addons/community_school/static/description/index.html
Normal file
9
addons/community_school/static/description/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — School</h2>
|
||||
<h3 class="oe_slogan">Programs, classes, attendance, and a parent portal</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_school/tests/__init__.py
Normal file
0
addons/community_school/tests/__init__.py
Normal file
0
addons/community_school/views/.gitkeep
Normal file
0
addons/community_school/views/.gitkeep
Normal file
27
addons/community_theme_base/README.rst
Normal file
27
addons/community_theme_base/README.rst
Normal file
@ -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
|
||||
0
addons/community_theme_base/__init__.py
Normal file
0
addons/community_theme_base/__init__.py
Normal file
33
addons/community_theme_base/__manifest__.py
Normal file
33
addons/community_theme_base/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/community_theme_base/data/.gitkeep
Normal file
0
addons/community_theme_base/data/.gitkeep
Normal file
0
addons/community_theme_base/demo/.gitkeep
Normal file
0
addons/community_theme_base/demo/.gitkeep
Normal file
1
addons/community_theme_base/security/ir.model.access.csv
Normal file
1
addons/community_theme_base/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Theme Base</h2>
|
||||
<h3 class="oe_slogan">Configurable brand tokens for community & cultural organizations</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/community_theme_base/tests/__init__.py
Normal file
0
addons/community_theme_base/tests/__init__.py
Normal file
0
addons/community_theme_base/views/.gitkeep
Normal file
0
addons/community_theme_base/views/.gitkeep
Normal file
22
addons/event_qr_ticketing/README.rst
Normal file
22
addons/event_qr_ticketing/README.rst
Normal file
@ -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
|
||||
0
addons/event_qr_ticketing/__init__.py
Normal file
0
addons/event_qr_ticketing/__init__.py
Normal file
36
addons/event_qr_ticketing/__manifest__.py
Normal file
36
addons/event_qr_ticketing/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
addons/event_qr_ticketing/data/.gitkeep
Normal file
0
addons/event_qr_ticketing/data/.gitkeep
Normal file
0
addons/event_qr_ticketing/demo/.gitkeep
Normal file
0
addons/event_qr_ticketing/demo/.gitkeep
Normal file
1
addons/event_qr_ticketing/security/ir.model.access.csv
Normal file
1
addons/event_qr_ticketing/security/ir.model.access.csv
Normal file
@ -0,0 +1 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
|
9
addons/event_qr_ticketing/static/description/index.html
Normal file
9
addons/event_qr_ticketing/static/description/index.html
Normal file
@ -0,0 +1,9 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<h2 class="oe_slogan">Community OS — Event QR Ticketing</h2>
|
||||
<h3 class="oe_slogan">Signed QR tickets and a mobile check-in screen</h3>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</section>
|
||||
0
addons/event_qr_ticketing/tests/__init__.py
Normal file
0
addons/event_qr_ticketing/tests/__init__.py
Normal file
0
addons/event_qr_ticketing/views/.gitkeep
Normal file
0
addons/event_qr_ticketing/views/.gitkeep
Normal file
0
addons/tncsc_deployment/__init__.py
Normal file
0
addons/tncsc_deployment/__init__.py
Normal file
42
addons/tncsc_deployment/__manifest__.py
Normal file
42
addons/tncsc_deployment/__manifest__.py
Normal file
@ -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,
|
||||
}
|
||||
0
data/raw/.gitkeep
Normal file
0
data/raw/.gitkeep
Normal file
31
deploy/docker-compose.yml
Normal file
31
deploy/docker-compose.yml
Normal file
@ -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:
|
||||
43
deploy/nginx-communityos.conf
Normal file
43
deploy/nginx-communityos.conf
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
13
deploy/odoo.conf
Normal file
13
deploy/odoo.conf
Normal file
@ -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
|
||||
|
||||
101
scripts/check_brand_leak.py
Normal file
101
scripts/check_brand_leak.py
Normal file
@ -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())
|
||||
62
scripts/check_manifests.py
Normal file
62
scripts/check_manifests.py
Normal file
@ -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())
|
||||
Loading…
x
Reference in New Issue
Block a user