TNCSC_Odoo/HANDOFF.md
metatroncubeswdev 24be6360e2 feat(tncsc_deployment): migrate real tncsc.com content into the website pages
Replaces the Phase 7 "content to be finalized" placeholder pages with
TNCSC's actual site content (captured 2026-08-20), verified against a
live Odoo container on a dedicated fresh database (tncsc_site):

- Home, About, About/Tamil Nadu, About/Board of Directors (12 real
  members), Membership hub, Membership Benefits, Tamil Class, and
  Contact pages, with nav restructured to match tncsc.com's real
  structure (About and Membership as dropdowns).
- Real images downloaded from tncsc.com and self-hosted under
  static/src/img/ instead of hotlinking the WordPress site.
- Home's "Upcoming Events" and the membership pricing tables are wired
  to live event.event / community.membership.tier records instead of
  being static copies; Tamil Class links to the real /school/register
  flow. One real event seeded (Summer Picnic 2026).
- Deliberately not reproduced: tncsc.com's own Sponsors page and Contact
  phone number are unmigrated WordPress theme demo placeholders on the
  live site itself (generic client-01..09 logos, a non-Canadian demo
  number) - confirmed by inspection, not assumed. Contact page uses the
  real, verified email and social links instead.
- post_init_hook now also fixes two Odoo website Copy-On-Write gotchas
  that silently prevented the new homepage and nav dropdowns from
  rendering at all (auto-forked per-website duplicates winning over
  this module's records) - see HANDOFF.md for the full explanation.
2026-08-20 12:32:59 -04:00

17 KiB
Raw Blame History

Handoff / Resume Notes

Read this first if you're picking this project back up — whether that's Claude Code in a future session or a human developer. The phase-by-phase build plan is in CommunityOS_Implementation_Plan_for_Claude_Code.md; this file is the "where things actually stand" companion to it.

Status at a glance

Phase What it is Status
0 Repo scaffold, Docker dev stack, CI + brand-leak guardrails Done, committed
1 community_membership Done, committed
2 event_qr_ticketing Done, committed
3 community_school Done, committed
4 community_classifieds + community_benefits Done, committed
5 community_interac (Interac e-Transfer payment provider) Done, committed
6 community_theme_base + community_portal Done, committed
7 tncsc_deployment (client config layer) Done, committed. Website pages content migrated from live tncsc.com in a later session — see "TNCSC website migration" below
8 TNCSC data migration scripts Done, committed (9a24691)
9 Packaging for resale + production go-live 🟡 9-A in progress (see below); 9-B not started

Every phase 07 commit was verified against a live Odoo 19 + Postgres 16 container before being committed — not just written and assumed to work. Run git log --oneline for the full commit list; each commit message documents what was built and how it was verified, including several real Odoo 19 API changes that don't match older docs/tutorials (see "Gotchas" below for the index).

Git remote is configured: origin -> https://git.metatroncube.in/admin/TNCSC_Odoo.git.

Resuming the dev environment

cd deploy
docker compose up -d
  • Odoo: http://localhost:8069, database communityos_dev, login admin / password admin.
  • This dev database has been used for all testing across every phase — it has real posted accounting entries in it (from Interac transaction confirmations, membership invoices, etc.). That's why Phase 7's tncsc_deployment module can't be installed on it directly (Odoo correctly refuses to change a company's currency once journal entries exist) — it was instead verified on throwaway fresh databases (tncsc_freshN, all dropped after verification). A real TNCSC production database should be a genuinely fresh install, not this dev one.
  • tncsc_site is another fresh DB (with tncsc_deployment installed), used for the website content migration below — kept around this time (not dropped) since it's the one with the real seeded event/tiers to look at. Since multiple DBs coexist, a plain http://localhost:8069/ won't know which to serve — select one explicitly first, e.g. http://localhost:8069/web?db=tncsc_site, then it's remembered by cookie.
  • After any module code change, the running deploy-odoo-1 container has a stale in-memory registry until you either upgrade the module (-u) and restart the container, or just restart it:
    docker compose restart odoo
    
    This bit repeatedly during development — don't skip it when testing.

Phase 8 — done, committed (9a24691)

All three migration scripts exist, are committed, and have been verified against a live Odoo instance (see "How each was tested" below).

  • scripts/_migration_common.py — shared JSON-RPC helpers (auth, execute_kw, CSV normalization, a MigrationReport class) for the migration scripts.
  • scripts/migrate_members.py — idempotent upsert by email, dry-run flag, tier lookup by code, report CSV.
  • scripts/migrate_students.py — idempotent upsert of parent partner (by email, reusing a partner created by migrate_members.py if one exists)
    • child partner + community.school.student, matched by (parent, student name) since students don't have their own email in the source data. Enrolls into the matching class in the current open term (matched by community.school.level code) when one exists; otherwise still creates/updates the student record and reports why no enrollment happened.
  • scripts/migrate_opening_balances.py — posts one account.move in the Miscellaneous Operations journal, dated the last fiscal year-end (computed from res.company.fiscalyear_last_day/month, or --date override). Refuses to post anything if the CSV doesn't balance (sum(debit) != sum(credit)) or references an unknown account code — checked before any Odoo write happens. Idempotent via a fixed ref ("Opening Balances Import"): re-running finds the existing posted entry and does nothing.

How each was tested

Followed the same pattern as scripts/migrate_classroom.py (Phase 3): dry-run first, then a real run, then a second real run to prove idempotency — against a fresh database, not communityos_dev. See "Why a fresh DB, not communityos_dev" below for why.

  1. docker compose up -d, then created a throwaway DB with both product modules and the client layer: odoo -d tncsc_migration_test -i community_membership,tncsc_deployment --stop-after-init (installing tncsc_deployment alone pulls in community_school and everything else as dependencies). Restarted the container afterward.
  2. migrate_members.py: synthetic CSV at data/raw/members_export_sample.csv (gitignored, left in place as a fixture) with 4 valid rows + 2 deliberately-bad rows (missing email, unknown tier code). Dry-run matched real-run output; second real run reported updated not created for all 4; confirmed via RPC that exactly one res.partner per email existed after both runs and membership_state was active.
  3. migrate_students.py: needed a community.school.term (state=open), community.school.level (code=BEG), and community.school.class seeded first (the fresh DB has none) — also had to grant the admin user the School Coordinator group, since a fresh install doesn't put admin in it. Synthetic CSV at data/raw/students_export_sample.csv with 3 valid rows (2 matching the open term's class, 1 with no level_code) + 1 deliberately-bad row (missing parent). Confirmed via RPC after 2 runs: exactly 3 community.school.student records, exactly 2 community.school.enrollment records (no duplicates), parent partners correctly reused from step 2 instead of duplicated.
  4. migrate_opening_balances.py: synthetic CSV at data/raw/opening_balances_sample.csv using real account codes from the fresh DB's TNCSC chart of accounts (111100 Cash, 112110 Trade AR, 322000 Retained Earnings as the balancing line). Verified the out-of-balance guard refuses to post (tested with a deliberately mismatched debit/credit CSV). Real run posted one balanced, posted account.move; second run detected the existing entry and did nothing. Confirmed via RPC: exactly one move, three correctly-valued lines, AR line correctly linked to the migrated Ravi Kumar partner.

Real TNCSC member/student/trial-balance data doesn't exist yet — all three scripts were intentionally designed against a documented, assumed CSV schema (see each script's docstring) since the real export formats aren't known yet. Confirm the assumed column names match the real exports before relying on them, or adjust the scripts to match.

Why a fresh DB, not communityos_dev

communityos_dev doesn't have tncsc_deployment installed (see the top of this file — it can't be, since it already has posted journal entries and Odoo refuses to change a company's currency once those exist). Testing the Phase 8 scripts there would exercise generic community_membership/ community_school behavior, not the actual TNCSC-configured target (TNCSC's real chart of accounts, company currency, groups). Since Phase 8's entire purpose is rehearsing the real TNCSC data load, a fresh DB with tncsc_deployment installed is the more representative target — and it avoids adding more synthetic test data to the one long-lived dev DB used across every other phase. tncsc_migration_test was not dropped after this session (unlike the Phase 7 tncsc_freshN throwaways) — it's left in place in case the next session wants to keep testing against it; drop it before it's mistaken for anything resembling real data.

To pick this back up

Either wait for real TNCSC export samples to validate the assumed CSV schemas against, or move on to Phase 9.

Phase 9 — 9-A in progress, 9-B not started

Two sub-parts with very different risk profiles:

  • 9-A (productize): safe to do autonomously. Done so far — every community_* module has static/description/index.html, a real README.rst (Usage sections filled in past the Phase-0 placeholder), and a CHANGELOG.rst; manifests pass scripts/check_manifests.py. Still open:
    • Every product manifest's images key points at static/description/banner.png, but no module actually has that file — the Apps-list/App-Store banner image doesn't exist yet. Needs a real designed asset per module, not a placeholder.
    • No v1.0.0 git tag yet, and no release report — holding off on both until the banner-image gap above is resolved. (A later session did run --test-enable for tncsc_deployment specifically against live Odoo while doing the website migration below — 9/9 passing — but that wasn't a full suite-wide run across every community_* module.)
    • No role guides (admin/treasurer/teacher/parent) written yet.
  • 9-B (go-live): not started. deploy/odoo.conf is still dev config (list_db = True, proxy_mode = False, no workers) and deploy/nginx-communityos.conf is an explicit "development skeleton" — SSL, DNS cutover, firewall rules, and backups on a real production server all remain. This needs a human in the loop with actual server access/credentials — don't let an agent run this unattended.

TNCSC website migration (from live tncsc.com, WordPress -> Odoo)

addons/tncsc_deployment/views/website_pages.xml was rewritten from the Phase 7 placeholder pages ("content to be finalized") into the real site content, adapted from tncsc.com (captured 2026-08-20). Tested against a dedicated fresh database, tncsc_site (not communityos_dev or tncsc_migration_test) — docker compose exec odoo odoo -d tncsc_site -i tncsc_deployment --stop-after-init recreates it from scratch.

  • Real pages built: Home, About, About/Tamil Nadu (bilingual EN/Tamil), About/Board of Directors (12 real named members + photos), Membership hub, Membership Benefits (real 8-point benefit copy), Tamil Class, Contact, plus the pre-existing Sponsors placeholder. Nav restructured to match tncsc.com's real structure (Home / About dropdown / Events / Membership dropdown / Tamil Class / Contact) instead of the old flat Classifieds/Benefits/Sponsors list.
  • Real assets pulled from tncsc.com and committed under addons/tncsc_deployment/static/src/img/ (~1.1MB: logo, board photos, section imagery) rather than hotlinking the WordPress site.
  • Events, Membership, and Tamil Class are wired to the real product modules (event.event for the homepage's "Upcoming Events", live community.membership.tier records for pricing, /school/register for Tamil Class signup) instead of being static copies — see each page's QWeb for the exact query.
  • One real event was seeded (data/events_data.xml): TNCSC's Summer Picnic 2026, matching what's live on tncsc.com/events.
  • Deliberately not reproduced, because it isn't real: tncsc.com's own Sponsors page (generic client-01..09 stock logos, dead links) and its Contact page phone number (+7 (800) 123 45 69, a non-Canadian WordPress theme demo placeholder) are themselves unmigrated theme demo content on the live site, not real TNCSC data — confirmed by inspecting alt text/hrefs, not assumed. Contact page ships with the real, verified email (support@tncsc.ca) and social links (Facebook/YouTube/Instagram) instead.
  • Real gap, not paved over: community_membership has no public self-service "join online" flow (portal only handles renewal for partners a staff member already created) — every "Become a Member" CTA points at /contact rather than a fake/broken signup link. Building that flow is follow-on work, not done here.
  • Known cosmetic leftover: Odoo's own stock header/footer snippets ship a placeholder phone number (+1 555-555-5556) baked into core website module views (website.header_text_element, website.footer_custom, website.s_contact_info), independent of anything in this module. It's normal, editable-in-the-website-builder content on any fresh Odoo site, not something this module introduced — left for whoever does the go-live content pass to delete via the Website editor (Edit -> click the text -> delete), rather than patched with a defensive XML override here.
  • Text throughout was reconstructed via automated extraction from the live pages, not copy-pasted from raw HTML — treat it as a faithful draft. Have TNCSC proofread the migrated copy against the original site before go-live.

Gotchas discovered this build (Odoo 19 API drift + environment quirks)

This Odoo 19 build (19.0-20260817, essentially a nightly) diverges from older tutorials/docs in several places that cost real debugging time. Each is documented in detail in the commit message where it was found — this is just the index so you know to search git log for it instead of re-discovering it:

  • res.groups.category_id → replaced by privilege_idres.groups.privilege.category_id (Session 1-A commit).
  • Settings pages use a new <app>/<block>/<setting> structure inherited from base.res_config_settings_view_form, not the old raw-div layout (Session 1-A commit).
  • ir.cron dropped numbercall entirely (Session 1-B commit).
  • event.event has no state field anymore (replaced by a stage_id/event.stage kanban system) (Phase 2 commit).
  • Route type='json' should be type='jsonrpc' ('json' still works but is deprecated) (Phase 2 commit).
  • Search-view <group> elements for "Group By" sections no longer accept a string attribute, only name (Phase 3 / Phase 4 commits).
  • res.groups.users → renamed to user_ids (Phase 4 commit).
  • res.config.settings fields backed by config_parameter= only support boolean/integer/float/char/selection/many2one/datetime — not Binary. Using it on a Binary field breaks every Settings tab, not just the one you're adding, since they share one model (fix: commit after Phase 6).
  • Anonymous visitors hitting an auth='user' + website=True route in this specific build crash with a raw 500 instead of a clean redirect to /web/login (Request._serve_db's finally: self.env = None runs before the website error handler tries to build the redirect). Worked around by switching those routes to auth='public' with a manual if request.env.user._is_public(): return request.redirect(...) check (fix: commit after Phase 6 — this is the fix to reach for if you add new member-only pages later, don't reintroduce auth='user' on a website=True route without it).
  • Setting res.company.country_id on a brand-new company auto-schedules this build's own chart-template installer via a precommit hook, which races an explicit try_loading() call made in the same install transaction and silently reverts it afterwards. ir.cron code execution is also sandboxed and forbids direct attribute assignment (STORE_ATTR) — use .write(...). Both fully explained in the Phase 7 commit message, fix pattern is in addons/tncsc_deployment/models/res_company.py.
  • Not version drift, but a real trap worth indexing here since it cost significant debugging time during the website migration: the website module auto-forks its own website-bound ("Copy-On-Write") copy of both the homepage (website.homepage) and the top nav tree (website.menu_id) the first time a website exists. A generic (website_id-unset) page or menu record renders fine at ordinary URLs, but is silently ignored for the homepage route and the rendered nav specifically — those two routes only resolve records explicitly bound to the current website_id, and on a same-URL tie the auto-forked stock copy wins over a same-priority custom one by insertion order. Confirmed empirically against a live container (not from docs). Fix pattern — explicitly bind website_id, and where a stock auto-forked duplicate exists, remove it — is in addons/tncsc_deployment/__init__.py (_remove_stock_homepage_page, _fix_website_menus), both run from post_init_hook. Relevant if you add more top-level pages/menu items later.

Handing off to a team

  1. Repo is already pushed to origin (see above) — confirm the team has access to git.metatroncube.in.
  2. Point them at CommunityOS_Implementation_Plan_for_Claude_Code.md (the plan) and this file (current status).
  3. They'll need Docker Desktop (or equivalent) to run deploy/docker-compose.yml locally — no other local dependencies.
  4. If they're using Claude Code to continue: just point it at this repo and this file. It doesn't need conversation history — everything load-bearing is either in a commit message or in this document.