Compare commits

...

6 Commits

Author SHA1 Message Date
metatroncubeswdev
35e62e8c61 docs: add standalone HTML version of the TNCSC handbook
Some checks failed
CI / Brand-leak check (push) Has been cancelled
CI / flake8 / pylint-odoo / manifest completeness (push) Has been cancelled
CI / Install all modules with --test-enable (push) Has been cancelled
Same content as TNCSC_Handbook.md, styled to match the version published
as a shareable Artifact web page. Self-contained (Google Fonts is the
only external dependency) so it works opened directly in a browser or
served from any static host, without relying on the Artifact platform.
2026-08-21 19:50:36 -04:00
metatroncubeswdev
91c4580ae2 docs: add TNCSC client handbook (settings + day-to-day operations)
A plain-language guide for TNCSC board members/volunteers covering
role/permission setup, branding, membership tiers & renewal, events,
Tamil Class terms/classes/attendance, classifieds moderation, benefits,
Interac payment confirmation, website content editing, accounting
guardrails, and troubleshooting. Menu paths and field labels verified
against the live tncsc_site container rather than assumed from the
module source.

Also published as a shareable web page for non-technical staff (no repo
access needed) - see the artifact link.
2026-08-21 19:39:37 -04:00
metatroncubeswdev
5596f41377 fix(tncsc_deployment): grant admin the Board Admin role so CommunityOS apps show
Every product module's backend app (Membership, School, Benefits,
Classifieds, Interac Payments) has its root menu restricted to its own
manager/coordinator group. A fresh tncsc_deployment install left the
admin user in none of them, so admin's app switcher only showed stock
apps (Events, eLearning, ...) - which looks exactly like the modules
weren't installed, even though ir.module.module.state is 'installed' for
all of them (menu visibility is group-based, not install-state-based).

Fixed by seeding admin into tncsc_deployment.group_tncsc_board_admin,
whose implied_ids already cover every product manager group - this
automates what Phase 8 previously described doing by hand for just the
School Coordinator group. Verified on a fresh install: admin's visible
root menus now include Membership/School/Benefits/Classifieds/Interac
Payments alongside the stock apps, tests still 9/9.

Also documents in HANDOFF.md that `user in group.user_ids` doesn't see
implied-group membership - use has_group() instead, which resolves the
transitive closure correctly.
2026-08-21 18:59:34 -04:00
metatroncubeswdev
745e147de9 fix: mark all product modules as application:True so they show in Apps
Only community_membership and community_school had application: True;
the other 6 product modules (community_benefits, community_classifieds,
community_interac, community_portal, community_theme_base,
event_qr_ticketing) had application: False despite being fully installed
and functional. Odoo's Apps screen filters to application=True by
default, so they were invisible there even though Technical > Installed
Modules showed them correctly - looked like they weren't installed.

Per the plan's own resellability rule ("each module is independently
installable and independently sellable"), every community_* product
module should be a discoverable Apps-grid entry. tncsc_deployment stays
application: False - it's the hidden client-config layer, not something
anyone browses to install on its own.

Verified on tncsc_site: -u upgrade picked up the flag for all 6, and the
site still serves every page (/, /about, /membership, /tamil-school,
/contact, /event) after the upgrade.
2026-08-21 18:40:25 -04:00
metatroncubeswdev
aeeb355948 feat(tncsc_deployment): complete the home page migration - missing sections + real copy
Re-extracted the raw HTML of tncsc.com's homepage (the earlier pass used a
summarizing tool that dropped whole sections and paraphrased copy). The
home page was missing entire sections and had shortened text throughout:

- Hero was a single static banner; the real site is a 4-slide carousel
  (Bootstrap carousel, real headline/subhead/CTA/image per slide).
- "Watch Our Story" section was missing entirely.
- "Why Join TNCSC?" FAQ section (Where is TNCSC based / How can I join)
  was missing entirely.
- The 3 program cards, the "Speak the Language" section, and the final
  CTA all had shortened/paraphrased copy - replaced with the real text.
- About section and Upcoming Events now use the real image grids instead
  of a single photo each.
- Final CTA copy corrected to "Be a Part of the Legacy" (was invented
  copy); kept the live membership-tier price strip merged into it rather
  than dropped, since that's real dynamic data the static site can't show.

8 more real images downloaded from tncsc.com for the carousel and events
section. Verified against a live container: fresh install + module test
suite (9/9 passing) + HTTP 200 with correct content on every page.
2026-08-20 13:12:01 -04:00
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
55 changed files with 2120 additions and 54 deletions

View File

@ -16,7 +16,7 @@ file is the "where things actually stand" companion to it.
| 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 |
| 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 |
@ -46,6 +46,12 @@ docker compose up -d
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:
@ -159,11 +165,10 @@ Two sub-parts with very different risk profiles:
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 and the full
`--test-enable` suite has actually been run against live Odoo (this
session had no Docker available, so only the static checks —
brand-leak grep, manifest completeness — were re-verified, not the
live module install/tests).
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
@ -172,6 +177,59 @@ Two sub-parts with very different risk profiles:
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
@ -214,6 +272,41 @@ re-discovering it:
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.
- Each product module's backend app (Membership, School, Benefits,
Classifieds, Interac Payments) has its root `ir.ui.menu` restricted to
its own manager/coordinator group, and a fresh `tncsc_deployment`
install doesn't put the `admin` user in any of them by default. Result:
admin's backend app switcher only shows stock apps (Events, eLearning,
Settings, ...) and none of the CommunityOS ones, which looks exactly
like "the modules aren't installed" even though
`ir.module.module.state` is `installed` for all of them — confirmed
by checking `env['ir.ui.menu'].with_user(admin).search([('parent_id',
'=', False)])`, which is the right way to check what a user actually
sees (menu visibility is group-based, not just install state). Fixed
by adding `admin` to `tncsc_deployment.group_tncsc_board_admin` in
`security/tncsc_roles.xml` (that group's `implied_ids` already covers
every product manager group) — this used to be the manual per-group
step described above for Phase 8's School Coordinator grant; it's now
automatic on any fresh install. Note `res.groups.implied_ids` doesn't
populate the implied group's own `user_ids` — checking membership via
`user in group.user_ids` will wrongly say "no" for an implied group;
use `user.has_group('module.xml_id')` instead, which does resolve the
transitive closure correctly.
## Handing off to a team

View File

@ -35,6 +35,6 @@ entitled per membership tier:
],
'demo': [],
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
}

View File

@ -36,6 +36,6 @@ Soft-detects Community OS Membership; runs standalone without it.
],
'demo': [],
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
}

View File

@ -38,6 +38,6 @@ Sellable to any Canadian organization accepting Interac e-Transfer.
],
'demo': [],
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
}

View File

@ -31,6 +31,6 @@ a missing module:
],
'demo': [],
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
}

View File

@ -31,6 +31,6 @@ layout that reads these tokens.
],
'demo': [],
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
}

View File

@ -40,7 +40,7 @@ OS product module required.
],
},
'images': ['static/description/banner.png'],
'application': False,
'application': True,
'installable': True,
'external_dependencies': {
'python': ['qrcode'],

View File

@ -9,3 +9,80 @@ def post_init_hook(env):
transaction isn't reliable on this Odoo build.
"""
env.company._tncsc_setup_accounting()
_remove_stock_homepage_page(env)
_fix_website_menus(env)
def _remove_stock_homepage_page(env):
"""The website module auto-creates its own website-bound "homepage"
page/view (key 'website.homepage') for every website, independently of
this module. That page ties on priority with data/../views/website_pages.xml's
own website-bound page_home record (same url "/", same website_id) -
confirmed empirically against a live container that the url "/" route
(unlike every other page url) only resolves a website-bound page, so an
unbound generic page or an inherited view has no effect here, and on a
tie the stock page wins by insertion order (it's created before this
module installs). Removing it is what makes page_home actually serve.
"""
website = env.ref('website.default_website', raise_if_not_found=False)
if not website:
return
stock_view = env['ir.ui.view'].sudo().search([
('key', '=', 'website.homepage'),
('website_id', '=', website.id),
])
stock_page = env['website.page'].sudo().search([
('url', '=', '/'),
('website_id', '=', website.id),
('view_id', 'in', stock_view.ids),
])
# unlinking the page cascades to its underlying view - unlinking the
# (already-cascaded) view afterward would raise a MissingError.
stock_page.unlink()
# xmlid (without module prefix) of each top-level nav entry -> xmlids of its
# children, as declared in views/website_pages.xml.
_MENU_TREE = {
'menu_home': [],
'menu_about': ['menu_about_tamil_nadu', 'menu_about_board'],
'menu_events': [],
'menu_membership': ['menu_membership_benefits', 'menu_membership_registration'],
'menu_tamil_class': [],
'menu_contact': [],
}
def _fix_website_menus(env):
"""Same Copy-On-Write pattern as _remove_stock_homepage_page, applied to
website.menu: the website module auto-forks its own website-bound copy
of the top nav tree (a "Top Menu for Website <n>" record, referenced by
website.menu_id) independently of this module. website_pages.xml's menu
records attach to the generic tree (website.main_menu) instead, so they
never render, while Odoo's auto-forked copy renders as flat top-level
items with none of our children - confirmed empirically against a live
container. Fix: re-parent our records onto the actual rendered tree, and
drop every other child Odoo forked there so only ours show.
"""
website = env.ref('website.default_website', raise_if_not_found=False)
if not website:
return
root = website.menu_id
if not root:
return
mine = env['website.menu']
for top_xmlid, child_xmlids in _MENU_TREE.items():
top_menu = env.ref(f'tncsc_deployment.{top_xmlid}', raise_if_not_found=False)
if not top_menu:
continue
top_menu.write({'website_id': website.id, 'parent_id': root.id})
mine |= top_menu
for child_xmlid in child_xmlids:
child_menu = env.ref(f'tncsc_deployment.{child_xmlid}', raise_if_not_found=False)
if child_menu:
child_menu.write({'website_id': website.id, 'parent_id': top_menu.id})
mine |= child_menu
stray = env['website.menu'].sudo().search([('parent_id', '=', root.id), ('id', 'not in', mine.ids)])
stray.unlink()

View File

@ -42,6 +42,7 @@ that applies to the community_* product modules.
'data/theme_data.xml',
'data/membership_data.xml',
'data/mail_template_overrides.xml',
'data/events_data.xml',
'data/ir_cron.xml',
'views/website_pages.xml',
],

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- Real upcoming event, matching what's live on tncsc.com/events (2026-08-20).
Update/replace once TNCSC confirms the venue address. -->
<record id="partner_summer_picnic_venue" model="res.partner">
<field name="name">Mississauga, Ontario</field>
<field name="city">Mississauga</field>
<field name="state_id" ref="base.state_ca_on"/>
<field name="country_id" ref="base.ca"/>
<field name="type">other</field>
</record>
<record id="event_summer_picnic_2026" model="event.event">
<field name="name">TNCSC's Summer Picnic 2026</field>
<field name="date_begin" eval="'2026-07-11 14:00:00'"/>
<field name="date_end" eval="'2026-07-11 20:00:00'"/>
<field name="address_id" ref="partner_summer_picnic_venue"/>
<field name="website_published">True</field>
</record>
</odoo>

View File

@ -14,8 +14,25 @@
suppresses that auto-trigger so only our explicit load runs. -->
<record id="base.main_company" model="res.company">
<field name="name">Tamil Nadu Cultural Society of Canada</field>
<field name="email">info@tncsc.example.org</field>
<field name="website">https://www.tncsc.example.org</field>
<field name="email">support@tncsc.ca</field>
<field name="website">https://tncsc.com</field>
<field name="logo" type="base64" file="tncsc_deployment/static/src/img/brand/logo.png"/>
</record>
<!-- Public social links, confirmed live on tncsc.com/contact (2026-08-20).
No street address or phone number is set: tncsc.com does not publish
either (its "Contact" page phone number is unmigrated WordPress
theme demo content, not a real TNCSC number) - add both here once
TNCSC provides them. -->
<record id="website.default_website" model="website">
<field name="name">Tamil Nadu Cultural Society of Canada</field>
<field name="social_facebook">https://www.facebook.com/TNCSCCanada/</field>
<field name="social_youtube">https://www.youtube.com/@tamilnaduculturalsocietyof5866</field>
<field name="social_instagram">https://www.instagram.com/tncsc_canada</field>
<!-- website.logo is its own stored field (defaults to Odoo's own
placeholder logo), not a related/computed fallback to
res.company.logo - both need to be set explicitly. -->
<field name="logo" type="base64" file="tncsc_deployment/static/src/img/brand/logo.png"/>
</record>
</odoo>

View File

@ -27,6 +27,16 @@
(4, ref('community_interac.group_interac_verifier')),
(4, ref('event.group_event_manager')),
]"/>
<!-- Without this, a fresh install leaves the installing admin user
out of every product-module manager group, so their backend
app switcher only shows stock apps (Events, eLearning, ...)
and none of the CommunityOS ones - each app's root menu is
group-restricted and admin isn't a member of any of those
groups by default. Confirmed empirically against a live
container; this is the automated fix for the manual step
HANDOFF.md previously described doing by hand for the School
Coordinator group alone. -->
<field name="user_ids" eval="[(4, ref('base.user_admin'))]"/>
</record>
<record id="group_tncsc_treasurer" model="res.groups">

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -67,7 +67,12 @@ class TestTncscDeployment(TransactionCase):
self.assertEqual(tier.product_id.property_account_income_id, dues_account)
def test_website_pages_published(self):
for url in ('/', '/about', '/tamil-school', '/sponsors', '/contact'):
urls = (
'/', '/about', '/about/tamil-nadu', '/about/board-of-directors',
'/membership', '/membership/benefits', '/tamil-school',
'/sponsors', '/contact',
)
for url in urls:
page = self.env['website.page'].search([('url', '=', url)], limit=1)
self.assertTrue(page, f"Expected a website.page for {url}")
self.assertTrue(page.is_published)

View File

@ -1,21 +1,325 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- ================================================================
Content on this page is adapted from the live tncsc.com site
(captured 2026-08-20) as part of migrating it into this Odoo
website. It should be proofread by TNCSC against the original
before go-live - text was reconstructed via automated extraction,
not copy-pasted from raw HTML, so treat it as a faithful draft,
not a byte-exact mirror. Two known gaps, deliberately not
fabricated here:
- No public self-service "join online" flow exists yet in
community_membership (portal only handles renewal for partners
already created by staff) - every "Become a Member" CTA below
points at the /contact form instead of a broken/fake signup link.
- tncsc.com's own Sponsors page and Contact-page phone number are
unmigrated WordPress theme demo placeholders (generic
"client-01..09" logos, a non-Canadian demo phone number) with no
real data behind them - neither is reproduced here.
================================================================ -->
<!-- Home -->
<!-- Home. The website module's homepage route ("/") is special-cased:
it only serves a website.page whose website_id explicitly matches
the current website. A generic (website_id-unset) page at "/"
renders fine at every OTHER url but is silently ignored for the
homepage specifically, and inheriting the stock "website.homepage"
view has no effect either, because Odoo already auto-forks a
website-specific copy of that view (Copy-On-Write) with no
inherit_id of its own the first time the website is customized -
confirmed empirically against a live container. So both records
below explicitly bind website_id to the default website, matching
what the homepage route actually looks up. -->
<record id="view_page_home" model="ir.ui.view">
<field name="name">TNCSC Home</field>
<field name="type">qweb</field>
<field name="key">tncsc_deployment.page_home</field>
<field name="website_id" ref="website.default_website"/>
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_home">
<t t-call="website.layout">
<div class="container" style="margin-top: 40px; margin-bottom: 60px;">
<h1>Tamil Nadu Cultural Society of Canada</h1>
<p class="lead">Celebrating Tamil language, arts, and culture in Canada.</p>
<p>Content to be finalized by TNCSC - this is a placeholder home page
generated by the deployment layer.</p>
<a href="/school/register" class="btn btn-primary me-2">Register for Tamil School</a>
<a href="/classifieds" class="btn btn-outline-secondary">Browse Classifieds</a>
<div id="wrap">
<!-- Hero: 4-slide carousel, matching tncsc.com's real slider content/order. -->
<section id="tncscHero" class="carousel slide carousel-fade" data-bs-ride="carousel">
<div class="carousel-indicators">
<button type="button" data-bs-target="#tncscHero" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"/>
<button type="button" data-bs-target="#tncscHero" data-bs-slide-to="1" aria-label="Slide 2"/>
<button type="button" data-bs-target="#tncscHero" data-bs-slide-to="2" aria-label="Slide 3"/>
<button type="button" data-bs-target="#tncscHero" data-bs-slide-to="3" aria-label="Slide 4"/>
</div>
<div class="carousel-inner">
<div class="carousel-item active">
<div class="d-flex align-items-center text-white" style="min-height: 480px; background: linear-gradient(135deg, rgba(5,9,30,0.88) 0%, rgba(16,25,63,0.82) 100%), url('/tncsc_deployment/static/src/img/hero/slide-1.webp') center/cover;">
<div class="container text-center py-5">
<h1 class="display-4 fw-bold">Connecting Tamil Hearts Across Canada</h1>
<p class="lead">Join Canada's Largest Tamil Nadu Cultural Community</p>
<a href="/about" class="btn btn-lg" style="background-color: var(--community-secondary); color: #fff;">Learn More</a>
</div>
</div>
</div>
<div class="carousel-item">
<div class="d-flex align-items-center text-white" style="min-height: 480px; background: linear-gradient(135deg, rgba(5,9,30,0.88) 0%, rgba(16,25,63,0.82) 100%), url('/tncsc_deployment/static/src/img/hero/slide-2.webp') center/cover;">
<div class="container text-center py-5">
<h1 class="display-4 fw-bold">Honouring Our Roots, Growing Together</h1>
<p class="lead">Where Tamil Culture Thrives Across Generations</p>
<a href="/tamil-school" class="btn btn-lg" style="background-color: var(--community-secondary); color: #fff;">Explore Our Community</a>
</div>
</div>
</div>
<div class="carousel-item">
<div class="d-flex align-items-center text-white" style="min-height: 480px; background: linear-gradient(135deg, rgba(5,9,30,0.88) 0%, rgba(16,25,63,0.82) 100%), url('/tncsc_deployment/static/src/img/hero/slide-3.webp') center/cover;">
<div class="container text-center py-5">
<h1 class="display-4 fw-bold">Celebrating 28+ Years of Culture &amp; Connection</h1>
<p class="lead">Canada's Home for Tamil Tradition, Unity &amp; Pride</p>
<a href="/event" class="btn btn-lg" style="background-color: var(--community-secondary); color: #fff;">Book Now</a>
</div>
</div>
</div>
<div class="carousel-item">
<div class="d-flex align-items-center text-white" style="min-height: 480px; background: linear-gradient(135deg, rgba(5,9,30,0.88) 0%, rgba(16,25,63,0.82) 100%), url('/tncsc_deployment/static/src/img/hero/slide-4.webp') center/cover;">
<div class="container text-center py-5">
<h1 class="display-4 fw-bold">Join Canada's Tamil Cultural Heartbeat</h1>
<p class="lead">Tradition unites, heritage shines globally.</p>
<a href="/contact" class="btn btn-lg" style="background-color: var(--community-secondary); color: #fff;">Contact</a>
</div>
</div>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#tncscHero" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"/>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#tncscHero" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"/>
<span class="visually-hidden">Next</span>
</button>
</section>
<section class="pt56 pb56">
<div class="container">
<div class="row align-items-center g-4">
<div class="col-lg-6">
<span class="text-uppercase fw-bold" style="color: var(--community-accent);">Celebrating Cultural Excellence</span>
<h2>About TNCSC</h2>
<h3 class="h4">Canada's Largest Tamil Cultural Organization — Bridging Heritage &amp; Community Since 1997</h3>
<p>
The Tamil Nadu Cultural Society of Canada (TNCSC) has been a cornerstone of the
Tamil community since 1997, connecting generations of Tamil families through
culture, education, and service.
</p>
<p>
As a registered non-profit organization, TNCSC preserves the rich heritage of
Tamil Nadu while fostering a sense of belonging among Tamil Canadians across
provinces. From vibrant Pongal festivals to Tamil language schools, every
initiative is a celebration of identity, resilience, and community spirit.
</p>
<p>
With programs designed for youth, seniors, newcomers, and families, TNCSC
continues to build a vibrant cultural bridge between Tamil traditions and
Canadian values.
</p>
<a href="/about" class="btn btn-outline-primary">Learn More</a>
</div>
<div class="col-lg-6">
<div class="row g-2">
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-01.webp" class="img-fluid rounded" alt="TNCSC community"/></div>
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-02.webp" class="img-fluid rounded" alt="TNCSC community"/></div>
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-03.webp" class="img-fluid rounded" alt="TNCSC community"/></div>
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-04.webp" class="img-fluid rounded" alt="TNCSC community"/></div>
</div>
</div>
</div>
</div>
</section>
<section class="pt56 pb56 bg-light">
<div class="container">
<div class="row align-items-center g-4">
<div class="col-lg-6">
<div class="row g-2">
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-01.webp" class="img-fluid rounded" alt="TNCSC story"/></div>
<div class="col-6"><img src="/tncsc_deployment/static/src/img/home/about-03.webp" class="img-fluid rounded mt-4" alt="TNCSC story"/></div>
</div>
</div>
<div class="col-lg-6">
<span class="text-uppercase fw-bold" style="color: var(--community-accent);">Watch Our Story</span>
<h2>Proudly Celebrating Tamil Heritage in Canada</h2>
<p class="lead">
From Pongal Festivals to Youth Empowerment — Experience Culture That Connects
Generations.
</p>
<p>
For over 28+ years, TNCSC has brought together Tamil families from all across
Canada to preserve, celebrate, and pass on the vibrant culture of Tamil Nadu.
</p>
<p>
Through traditional festivals, language education, youth leadership, and
community support, our organization is more than just a gathering — it's a
living, growing cultural legacy.
</p>
<p class="fw-bold">Discover the heartbeat of our community.</p>
</div>
</div>
</div>
</section>
<section class="pt56 pb56">
<div class="container">
<div class="row g-4 text-center">
<div class="col-md-4">
<img src="/tncsc_deployment/static/src/img/home/cultural-events-icon.webp" alt="" style="height:64px;"/>
<h4 class="mt-3">Cultural Networking Events</h4>
<p>
Connect with fellow Tamil professionals, entrepreneurs, artists, and families
through curated meetups and celebrations that strengthen our cultural bond and
open new doors in Canada.
</p>
</div>
<div class="col-md-4">
<img src="/tncsc_deployment/static/src/img/home/youth-mentorship.webp" alt="" style="height:64px;"/>
<h4 class="mt-3">Youth &amp; Career Mentorship</h4>
<p>
Our mentorship program pairs talented students and young professionals with
experienced Tamil-Canadian leaders who provide valuable guidance in education,
career, leadership, and life skills.
</p>
</div>
<div class="col-md-4">
<img src="/tncsc_deployment/static/src/img/home/frends-gathering.webp" alt="" style="height:64px;"/>
<h4 class="mt-3">Festivals &amp; Family Gatherings</h4>
<p>
From Pongal feasts to Deepavali musical nights and summer picnics, TNCSC
events go beyond formality — they bring vibrant joy, rich culture, and true
community spirit into every celebration.
</p>
</div>
</div>
</div>
</section>
<section class="pt56 pb56 bg-light">
<div class="container">
<div class="row align-items-center g-4">
<div class="col-lg-5">
<span class="text-uppercase fw-bold" style="color: var(--community-accent);">Get Involved</span>
<h2>Why Join TNCSC?</h2>
<p class="lead">Experience Culture, Connection &amp; Community — All Year Round.</p>
<img src="/tncsc_deployment/static/src/img/home/hero.webp" class="img-fluid rounded d-none d-lg-block" alt="TNCSC community"/>
</div>
<div class="col-lg-7">
<div class="accordion" id="tncscWhyJoin">
<div class="accordion-item">
<h3 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#tncscFaqWhere">
Where Is TNCSC Based?
</button>
</h3>
<div id="tncscFaqWhere" class="accordion-collapse collapse show" data-bs-parent="#tncscWhyJoin">
<div class="accordion-body">
Tamil Nadu Cultural Society of Canada — Community Centre, Ontario,
Canada, with regional chapters across major Canadian cities.
</div>
</div>
</div>
<div class="accordion-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#tncscFaqJoin">
How Can I Join?
</button>
</h3>
<div id="tncscFaqJoin" class="accordion-collapse collapse" data-bs-parent="#tncscWhyJoin">
<div class="accordion-body">
Membership is open year-round! Choose from annual, lifetime, or
family plans. Join today and be part of our vibrant upcoming
celebrations and events.
<a href="/membership" class="d-block mt-2">See membership plans →</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<t t-set="upcoming_events" t-value="env['event.event'].sudo().search([('website_published', '=', True)], order='date_begin', limit=3)"/>
<section class="pt56 pb56" t-if="upcoming_events">
<div class="container">
<h2 class="text-center mb-4">Upcoming Events &amp; Celebrations</h2>
<div class="row g-2 mb-5 d-none d-md-flex">
<div class="col-3"><img src="/tncsc_deployment/static/src/img/events/about-01-1.webp" class="img-fluid rounded" alt="TNCSC event"/></div>
<div class="col-3"><img src="/tncsc_deployment/static/src/img/events/about-02-1.webp" class="img-fluid rounded" alt="TNCSC event"/></div>
<div class="col-3"><img src="/tncsc_deployment/static/src/img/events/about-03-1.webp" class="img-fluid rounded" alt="TNCSC event"/></div>
<div class="col-3"><img src="/tncsc_deployment/static/src/img/events/about-04-1.webp" class="img-fluid rounded" alt="TNCSC event"/></div>
</div>
<div class="row g-4">
<t t-foreach="upcoming_events" t-as="upcoming_event">
<div class="col-md-4">
<div class="card h-100">
<div class="card-body">
<p class="text-uppercase small fw-bold mb-1" style="color: var(--community-accent);">Events</p>
<h5 class="card-title" t-out="upcoming_event.name"/>
<p class="card-text text-muted mb-1" t-field="upcoming_event.date_begin" t-options="{'widget': 'date'}"/>
<p class="card-text" t-if="upcoming_event.address_id" t-out="upcoming_event.address_id.city"/>
<a t-attf-href="/event/#{ slug(upcoming_event) }" class="btn btn-outline-primary btn-sm">Book Now</a>
</div>
</div>
</div>
</t>
</div>
</div>
</section>
<section class="pt56 pb56 bg-light">
<div class="container">
<div class="row align-items-center g-4">
<div class="col-lg-6">
<span class="text-uppercase fw-bold" style="color: var(--community-accent);">Language &amp; Culture</span>
<h2>Speak the Language of Your Heritage</h2>
<p>At TNCSC, our mission is to pass the beauty of Tamil to every generation in Canada.</p>
<p>
Whether you're a parent hoping to pass on your language, a student preparing
for cultural competitions, or a newcomer eager to connect — TNCSC offers
welcoming, effective Tamil learning programs for all ages.
</p>
<p>
Our community-led classes are designed to blend traditional Tamil values with
modern teaching methods. From the alphabet to ancient poetry, from
conversational Tamil to cultural idioms — we make language learning fun,
meaningful, and rooted in real community connection.
</p>
<p class="fst-italic">Because to speak Tamil… is to feel Tamil.</p>
<a href="/tamil-school" class="btn btn-primary">Start Learning Today</a>
</div>
<div class="col-lg-6">
<img src="/tncsc_deployment/static/src/img/tamil_class/tamil_class.webp" class="img-fluid rounded" alt="Tamil class"/>
</div>
</div>
</div>
</section>
<section class="pt56 pb56 text-white text-center" style="background-color: var(--community-primary);">
<div class="container">
<h2>Be a Part of the Legacy</h2>
<p class="lead" style="max-width: 720px; margin-left: auto; margin-right: auto;">
Join the Tamil Nadu Cultural Society of Canada and help preserve Tamil heritage,
empower youth, and bring our vibrant culture to life in every corner of Canada. Your
support keeps our language, traditions, and community thriving.
</p>
<a href="/membership" class="btn btn-lg mt-2 mb-4" style="background-color: var(--community-secondary); color: #fff;">Join TNCSC Today</a>
<div class="row justify-content-center g-3">
<t t-foreach="env['community.membership.tier'].sudo().search([('active', '=', True)], order='sequence')" t-as="tier">
<div class="col-6 col-md-2">
<div class="card bg-transparent border-light text-white h-100">
<div class="card-body">
<h5 class="card-title" t-out="tier.name"/>
<p class="card-text" t-out="'%s %s' % (tier.currency_id.symbol, int(tier.price))"/>
</div>
</div>
</div>
</t>
</div>
</div>
</section>
</div>
</t>
</t>
@ -24,6 +328,7 @@
<record id="page_home" model="website.page">
<field name="url">/</field>
<field name="view_id" ref="view_page_home"/>
<field name="website_id" ref="website.default_website"/>
<field name="is_published">True</field>
</record>
@ -35,10 +340,55 @@
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_about">
<t t-call="website.layout">
<div class="container" style="margin-top: 40px; margin-bottom: 60px;">
<h1>About Us</h1>
<p>Placeholder content - to be filled in with TNCSC's real history, mission,
and board information.</p>
<div id="wrap" class="container pt56 pb56">
<h1>Celebrating Community, Culture, and Connection Since 1997</h1>
<h2 class="h4 text-muted">Tamil Nadu Cultural Society of Canada</h2>
<p class="lead">
TNCSC is a one and only largest Indian Tamil Organisation in Canada for the
people of Tamilnadu as a whole.
</p>
<p>
Tamil Nadu Cultural Society of Canada (TNCSC) is a Not-for-Profit Ontario
Corporation serving Indian Tamils in Canada. It was established in May 1997
on an informal basis as an association of like minded Indian Tamils. It was
registered in February 1998 as a not-for-profit corporation in the province
of Ontario to connect Tamil Diasporas of Indian Tamils in Canada.
</p>
<h3 class="mt-5">Preserving Tamil Heritage</h3>
<p>
The purpose is to serve Tamils of Indian origin in Canada and preserve and
promote the heritage of our culture in Canada by celebrating festivals like
Pongal combined with Christmas and English New Year, Tamil New Year and
Deepavali combined with Eid. This will facilitate the people of Tamil Nadu
from different religion to feel Canada as a home away from home. We also help
Tamil kids of Indo-Canadian Tamils to understand our rich heritage and the
culture. Association is also assisting the new immigrants from Tamilnadu to
settle down fast in Canada by providing necessary information relating to
Canadian society, settlement, job search and community support by means of
networking.
</p>
<div class="row g-4 mt-4">
<div class="col-md-6">
<div class="card h-100">
<img src="/tncsc_deployment/static/src/img/tamil_nadu/tamilnadu-1.webp" class="card-img-top" alt="Tamil Nadu"/>
<div class="card-body">
<h4 class="card-title">Tamil Nadu</h4>
<p class="card-text">The land whose heritage and language TNCSC exists to celebrate.</p>
<a href="/about/tamil-nadu" class="btn btn-outline-primary">Discover Tamil Nadu</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<img src="/tncsc_deployment/static/src/img/about/unity.webp" class="card-img-top" alt="Board of Directors"/>
<div class="card-body">
<h4 class="card-title">Board of Directors</h4>
<p class="card-text">Meet the volunteers leading TNCSC.</p>
<a href="/about/board-of-directors" class="btn btn-outline-primary">Meet the Board</a>
</div>
</div>
</div>
</div>
</div>
</t>
</t>
@ -50,7 +400,203 @@
<field name="is_published">True</field>
</record>
<!-- Tamil School -->
<!-- About / Tamil Nadu -->
<record id="view_page_tamil_nadu" model="ir.ui.view">
<field name="name">TNCSC Tamil Nadu</field>
<field name="type">qweb</field>
<field name="key">tncsc_deployment.page_tamil_nadu</field>
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_tamil_nadu">
<t t-call="website.layout">
<div id="wrap" class="container pt56 pb56">
<h1>தமிழ்நாடு <small class="text-muted">/ Tamil Nadu</small></h1>
<p class="lead" lang="ta">
தமிழ்நாடு — தமிழர்களின் ஆன்மாவும் அடையாளமும்! இது ஆயிரக்கணக்கான ஆண்டுகள்
பழமை வாய்ந்த வரலாற்றைக் கொண்ட நிலம்.
</p>
<img src="/tncsc_deployment/static/src/img/tamil_nadu/tamilnadu-1.webp" class="img-fluid rounded my-4" alt="Tamil Nadu coastline"/>
<p>
Tamil Nadu is the land of the Tamils, and it has a history that dates back
several thousand years. It is an enchanting and ancient Dravidian land in the
extreme south of peninsular India, where traditions and culture blend and
continue to live in harmony — from its temples and sculptures to its living
classical language.
</p>
<p>
The state's coastline runs nearly 1,000 km along the Coromandel Coast and the
Bay of Bengal, dotted with coconut palms, fishing villages, and rich maritime
wildlife. It is this land — its language, its coast, its temples, and its
people — that TNCSC exists to keep alive for Tamil families across Canada.
</p>
<img src="/tncsc_deployment/static/src/img/tamil_nadu/tamilnadu-2.webp" class="img-fluid rounded my-4" alt="Tamil Nadu heritage"/>
<a href="/about" class="btn btn-outline-secondary">Back to About</a>
</div>
</t>
</t>
</field>
</record>
<record id="page_tamil_nadu" model="website.page">
<field name="url">/about/tamil-nadu</field>
<field name="view_id" ref="view_page_tamil_nadu"/>
<field name="is_published">True</field>
</record>
<!-- About / Board of Directors -->
<record id="view_page_board" model="ir.ui.view">
<field name="name">TNCSC Board of Directors</field>
<field name="type">qweb</field>
<field name="key">tncsc_deployment.page_board</field>
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_board">
<t t-call="website.layout">
<div id="wrap" class="container pt56 pb56">
<h1>Board of Directors</h1>
<p class="lead">The volunteers currently leading TNCSC.</p>
<t t-set="board_members" t-value="[
{'name': 'Anand Babu Gurusamy', 'title': 'President', 'img': 'anand.webp'},
{'name': 'Rachel Daniel', 'title': 'Vice President', 'img': 'rachel.webp'},
{'name': 'Murugan Sriramulu', 'title': 'Secretary', 'img': 'murugan.webp'},
{'name': 'Palanimurugan Palanichamy', 'title': 'Treasurer', 'img': 'palani.webp'},
{'name': 'Karunamoorthy Samu', 'title': 'Joint Secretary', 'img': 'karuna.webp'},
{'name': 'Nandhini Senthil Kumar', 'title': 'Joint Treasurer', 'img': 'nandhini.webp'},
{'name': 'Amirthavarshini Thirugnanasambandam', 'title': 'Director, Muthamizh Paadasaalai', 'img': 'amirtha.webp'},
{'name': 'Ashraph Sulaiman', 'title': 'Director, IT Wing', 'img': 'asraph.webp'},
{'name': 'Brindha Rajamanickam', 'title': 'Director, Muthamizh Paadasaalai', 'img': 'brindha.webp'},
{'name': 'Mari Mutharasan', 'title': 'Director, Muthamizh Paadasaalai', 'img': 'mari.webp'},
{'name': 'Shanmuga', 'title': 'Director, IT Wing', 'img': 'shanmuga.webp'},
{'name': 'Sekar Karuppan', 'title': 'Director, IT Wing', 'img': 'sekar.webp'},
]"/>
<div class="row g-4">
<t t-foreach="board_members" t-as="member">
<div class="col-6 col-md-3 text-center">
<img t-attf-src="/tncsc_deployment/static/src/img/board/#{member['img']}" class="rounded-circle mb-2" style="width:120px;height:120px;object-fit:cover;" t-att-alt="member['name']"/>
<h5 t-out="member['name']"/>
<p class="text-muted small" t-out="member['title']"/>
</div>
</t>
</div>
<a href="/about" class="btn btn-outline-secondary mt-4">Back to About</a>
</div>
</t>
</t>
</field>
</record>
<record id="page_board" model="website.page">
<field name="url">/about/board-of-directors</field>
<field name="view_id" ref="view_page_board"/>
<field name="is_published">True</field>
</record>
<!-- Membership hub -->
<record id="view_page_membership" model="ir.ui.view">
<field name="name">TNCSC Membership</field>
<field name="type">qweb</field>
<field name="key">tncsc_deployment.page_membership</field>
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_membership">
<t t-call="website.layout">
<div id="wrap" class="container pt56 pb56">
<h1>Membership</h1>
<p class="lead">
For over 28+ years, TNCSC has been a pillar for Tamil families across
Canada — preserving language, celebrating tradition, and uniting generations
through community events and cultural programs.
</p>
<div class="row g-4 my-2">
<div class="col-md-6">
<div class="card h-100">
<img src="/tncsc_deployment/static/src/img/membership/membership-benefits.webp" class="card-img-top" alt="Membership benefits"/>
<div class="card-body">
<h4 class="card-title">Membership Benefits</h4>
<p class="card-text">Festivals, networking, Tamil school priority, and more.</p>
<a href="/membership/benefits" class="btn btn-outline-primary">See Benefits</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<img src="/tncsc_deployment/static/src/img/membership/membership-registration.webp" class="card-img-top" alt="Become a member"/>
<div class="card-body">
<h4 class="card-title">Become a Member</h4>
<p class="card-text">Online sign-up is launching soon — for now, contact us to get started.</p>
<a href="/contact" class="btn btn-primary">Contact Us to Join</a>
</div>
</div>
</div>
</div>
<h3 class="mt-5">Membership Plans</h3>
<div class="row g-3">
<t t-foreach="env['community.membership.tier'].sudo().search([('active', '=', True)], order='sequence')" t-as="tier">
<div class="col-6 col-lg-2 col-md-4">
<div class="card h-100 text-center">
<div class="card-body">
<h5 class="card-title" t-out="tier.name"/>
<p class="card-text fw-bold" t-out="'%s %s' % (tier.currency_id.symbol, int(tier.price))"/>
<p class="card-text text-muted small" t-out="dict(tier._fields['period'].selection).get(tier.period)"/>
</div>
</div>
</div>
</t>
</div>
<p class="text-muted small mt-3">Already a member? <a href="/my/membership">Manage your membership</a>.</p>
</div>
</t>
</t>
</field>
</record>
<record id="page_membership" model="website.page">
<field name="url">/membership</field>
<field name="view_id" ref="view_page_membership"/>
<field name="is_published">True</field>
</record>
<!-- Membership benefits -->
<record id="view_page_membership_benefits" model="ir.ui.view">
<field name="name">TNCSC Membership Benefits</field>
<field name="type">qweb</field>
<field name="key">tncsc_deployment.page_membership_benefits</field>
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_membership_benefits">
<t t-call="website.layout">
<div id="wrap" class="container pt56 pb56">
<h1>Membership Benefits</h1>
<h2 class="h5 text-muted">TNCSC Memberships</h2>
<p class="lead">Join Hands to Keep Our Tamil Heritage Alive — Right Here in Canada!</p>
<p>
Becoming a member of Tamil Nadu Cultural Society of Canada (TNCSC) is more
than just signing up — it's becoming part of a family that celebrates Tamil
identity, language, and culture in every heartbeat.
</p>
<div class="row g-4 align-items-center">
<div class="col-md-6">
<ul class="list-group list-group-flush">
<li class="list-group-item">Experience the joy of celebrating Tamil festivals like Pongal, Tamil New Year, and Deepavali just the way we do back home.</li>
<li class="list-group-item">Meet new friends, families, and professionals who share your values and heritage.</li>
<li class="list-group-item">Our community events bring together people from all walks of life — from entrepreneurs to IT professionals to artists.</li>
<li class="list-group-item">Members' children get priority access to our Muthamizh Padasaalai, where they learn Tamil reading, writing, and speaking.</li>
<li class="list-group-item">Every member is welcome to participate as a volunteer in TNCSC events.</li>
<li class="list-group-item">Enjoy free or discounted entry to select TNCSC programs, workshops, and cultural events.</li>
<li class="list-group-item">Voting rights and board election participation privileges.</li>
<li class="list-group-item">Your membership keeps Tamil culture alive — your involvement keeps our community strong.</li>
</ul>
</div>
<div class="col-md-6">
<img src="/tncsc_deployment/static/src/img/membership/members-img.webp" class="img-fluid rounded" alt="TNCSC members"/>
</div>
</div>
<a href="/contact" class="btn btn-primary btn-lg mt-4">Become a Member</a>
</div>
</t>
</t>
</field>
</record>
<record id="page_membership_benefits" model="website.page">
<field name="url">/membership/benefits</field>
<field name="view_id" ref="view_page_membership_benefits"/>
<field name="is_published">True</field>
</record>
<!-- Tamil School / Tamil Class -->
<record id="view_page_tamil_school" model="ir.ui.view">
<field name="name">TNCSC Tamil School</field>
<field name="type">qweb</field>
@ -58,11 +604,29 @@
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_tamil_school">
<t t-call="website.layout">
<div class="container" style="margin-top: 40px; margin-bottom: 60px;">
<h1>Tamil School</h1>
<p>Weekend Tamil language classes for children and youth. See current terms,
levels, and class schedules below.</p>
<a href="/school/register" class="btn btn-primary">Register</a>
<div id="wrap" class="container pt56 pb56">
<h1>Tamil Class</h1>
<h2 class="h5 text-muted">முத்தமிழ் பாடசாலை</h2>
<div class="row g-4 align-items-center">
<div class="col-md-6">
<p class="lead">
தமிழ்மொழி, தமிழ்க் கலாச்சாரம், தமிழ் மரபு — the Tamil language, culture,
and heritage form the identity and foundation of our community.
</p>
<p>
Operating since 2013, our Tamil classes serve children ages 4 and older,
building speaking, reading, and writing skills through a curriculum based
on Tamil Nadu's own education system, taught from custom textbooks
developed for accessibility abroad. Our teachers are experienced educators
from Tamil Nadu who bring language, history, culture, and tradition into
every class.
</p>
<a href="/school/register" class="btn btn-primary">Register</a>
</div>
<div class="col-md-6">
<img src="/tncsc_deployment/static/src/img/tamil_class/tamil_class.webp" class="img-fluid rounded" alt="Tamil class"/>
</div>
</div>
</div>
</t>
</t>
@ -74,7 +638,10 @@
<field name="is_published">True</field>
</record>
<!-- Sponsors -->
<!-- Sponsors: tncsc.com's own Sponsors page is unmigrated WordPress
theme demo content (generic client-01..09 stock logos, dead links,
no real sponsor names) - left as a placeholder, deliberately not
linked from the main nav, until TNCSC provides real sponsor data. -->
<record id="view_page_sponsors" model="ir.ui.view">
<field name="name">TNCSC Sponsors</field>
<field name="type">qweb</field>
@ -105,10 +672,51 @@
<field name="arch" type="xml">
<t t-name="tncsc_deployment.page_contact">
<t t-call="website.layout">
<div class="container" style="margin-top: 40px; margin-bottom: 60px;">
<div id="wrap" class="container pt56 pb56">
<h1>Contact Us</h1>
<p>Email: <t t-out="env.company.email or ''"/></p>
<p>Placeholder content - add a real address, phone number, and contact form.</p>
<p class="lead">We'd Love to Hear From You</p>
<div class="row g-5">
<div class="col-lg-5">
<h5>Visit / Call Us Anytime</h5>
<p>
Email:
<a t-attf-href="mailto:#{env.company.email}" t-out="env.company.email"/>
</p>
<p class="text-muted small">
No public phone line or street address is published yet — email or
social media is the fastest way to reach us.
</p>
<p>
<a href="https://www.facebook.com/TNCSCCanada/" target="_blank" class="me-3">Facebook</a>
<a href="https://www.youtube.com/@tamilnaduculturalsocietyof5866" target="_blank" class="me-3">YouTube</a>
<a href="https://www.instagram.com/tncsc_canada" target="_blank">Instagram</a>
</p>
</div>
<div class="col-lg-7">
<section class="s_website_form" data-snippet="s_website_form">
<form id="contactus_form" action="/website/form/" method="post" enctype="multipart/form-data" class="o_mark_required" data-mark="*" data-model_name="mail.mail" data-success-mode="redirect" data-success-page="/contactus-thank-you">
<input type="hidden" name="email_to" t-att-value="env.company.email"/>
<div class="mb-3">
<label class="form-label" for="contact_name">Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="name" id="contact_name" required="1"/>
</div>
<div class="mb-3">
<label class="form-label" for="contact_email">Email <span class="text-danger">*</span></label>
<input type="email" class="form-control" name="email_from" id="contact_email" required="1"/>
</div>
<div class="mb-3">
<label class="form-label" for="contact_subject">Subject</label>
<input type="text" class="form-control" name="subject" id="contact_subject"/>
</div>
<div class="mb-3">
<label class="form-label" for="contact_description">Message <span class="text-danger">*</span></label>
<textarea class="form-control" name="description" id="contact_description" rows="5" required="1"/>
</div>
<button type="submit" class="btn btn-primary">Submit Now</button>
</form>
</section>
</div>
</div>
</div>
</t>
</t>
@ -120,41 +728,72 @@
<field name="is_published">True</field>
</record>
<!-- Main nav menu entries -->
<!-- Main nav menu, matching tncsc.com's real structure:
Home / About (Tamil Nadu, Board Of Directors) / Events /
Membership (Membership Benefits, Membership Registration) /
Tamil Class / Contact.
Classifieds and the vendor Benefits directory are still fully
functional CommunityOS features - they're reachable from the
member portal (/my), just not on the public marketing nav, matching
tncsc.com's own structure. -->
<record id="menu_home" model="website.menu">
<field name="name">Home</field>
<field name="url">/</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">5</field>
</record>
<record id="menu_about" model="website.menu">
<field name="name">About</field>
<field name="url">/about</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">10</field>
</record>
<record id="menu_tamil_school" model="website.menu">
<field name="name">Tamil School</field>
<field name="url">/tamil-school</field>
<record id="menu_about_tamil_nadu" model="website.menu">
<field name="name">Tamil Nadu</field>
<field name="url">/about/tamil-nadu</field>
<field name="parent_id" ref="menu_about"/>
<field name="sequence">10</field>
</record>
<record id="menu_about_board" model="website.menu">
<field name="name">Board Of Directors</field>
<field name="url">/about/board-of-directors</field>
<field name="parent_id" ref="menu_about"/>
<field name="sequence">20</field>
</record>
<record id="menu_events" model="website.menu">
<field name="name">Events</field>
<field name="url">/event</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">20</field>
</record>
<record id="menu_classifieds" model="website.menu">
<field name="name">Classifieds</field>
<field name="url">/classifieds</field>
<record id="menu_membership" model="website.menu">
<field name="name">Membership</field>
<field name="url">/membership</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">30</field>
</record>
<record id="menu_benefits" model="website.menu">
<field name="name">Benefits</field>
<field name="url">/benefits</field>
<record id="menu_membership_benefits" model="website.menu">
<field name="name">Membership Benefits</field>
<field name="url">/membership/benefits</field>
<field name="parent_id" ref="menu_membership"/>
<field name="sequence">10</field>
</record>
<record id="menu_membership_registration" model="website.menu">
<field name="name">Membership Registration</field>
<field name="url">/contact</field>
<field name="parent_id" ref="menu_membership"/>
<field name="sequence">20</field>
</record>
<record id="menu_tamil_class" model="website.menu">
<field name="name">Tamil Class</field>
<field name="url">/tamil-school</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">40</field>
</record>
<record id="menu_sponsors" model="website.menu">
<field name="name">Sponsors</field>
<field name="url">/sponsors</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">50</field>
</record>
<record id="menu_contact" model="website.menu">
<field name="name">Contact</field>
<field name="url">/contact</field>
<field name="parent_id" ref="website.main_menu"/>
<field name="sequence">60</field>
<field name="sequence">50</field>
</record>
</odoo>

846
docs/TNCSC_Handbook.html Normal file
View File

@ -0,0 +1,846 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="A settings and day-to-day operations guide for TNCSC board members and volunteers running their CommunityOS system.">
<title>TNCSC Handbook</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Zilla+Slab:wght@400;500;600;700&family=Inter:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:wght@500;600&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
h1, h2, h3, h4, p, ul, ol { margin: 0; }
:root {
--ink: #0c1230;
--ink-soft: #333a5c;
--muted: #5b6280;
--paper: #f4f5fb;
--surface: #ffffff;
--surface-2: #eef0fa;
--border: #dfe2f0;
--border-soft: #ebedf7;
--accent: #0d7fd6;
--accent-ink: #ffffff;
--accent-soft: #e6f2fd;
--warn: #c2550a;
--warn-soft: #fdece0;
--code-surface: #eef0fa;
--code-ink: #2a2f52;
--shadow: 0 1px 2px rgba(12, 18, 48, 0.04), 0 8px 24px -12px rgba(12, 18, 48, 0.18);
--focus: #0d7fd6;
--font-display: "Zilla Slab", Georgia, "Times New Roman", serif;
--font-body: "Inter", -apple-system, "Segoe UI", sans-serif;
--font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--ink: #e9ebfa;
--ink-soft: #c3c7e6;
--muted: #9599c0;
--paper: #0a0e24;
--surface: #11162f;
--surface-2: #171d3d;
--border: #262c4f;
--border-soft: #1b2142;
--accent: #4fb2ff;
--accent-ink: #05121f;
--accent-soft: #12233b;
--warn: #ff9d5c;
--warn-soft: #2e1c10;
--code-surface: #171d3d;
--code-ink: #cfd3f2;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px -14px rgba(0, 0, 0, 0.55);
}
}
:root[data-theme="dark"] {
--ink: #e9ebfa;
--ink-soft: #c3c7e6;
--muted: #9599c0;
--paper: #0a0e24;
--surface: #11162f;
--surface-2: #171d3d;
--border: #262c4f;
--border-soft: #1b2142;
--accent: #4fb2ff;
--accent-ink: #05121f;
--accent-soft: #12233b;
--warn: #ff9d5c;
--warn-soft: #2e1c10;
--code-surface: #171d3d;
--code-ink: #cfd3f2;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px -14px rgba(0, 0, 0, 0.55);
}
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--font-body);
font-size: 16.5px;
line-height: 1.65;
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration-thickness: 1.5px; text-underline-offset: 2px; }
a:focus-visible, button:focus-visible, summary:focus-visible {
outline: 2.5px solid var(--focus);
outline-offset: 2px;
border-radius: 3px;
}
h1, h2, h3, h4 {
font-family: var(--font-display);
font-weight: 600;
color: var(--ink);
text-wrap: balance;
line-height: 1.15;
}
p { margin: 0 0 1.05em; max-width: 66ch; }
ul, ol { max-width: 66ch; padding-left: 1.35em; }
li { margin-bottom: 0.45em; }
li:last-child { margin-bottom: 0; }
li > ul, li > ol { margin-top: 0.45em; }
/* ---------- Masthead ---------- */
.masthead {
border-bottom: 1px solid var(--border);
background:
radial-gradient(1100px 420px at 12% -20%, var(--accent-soft), transparent 60%),
var(--surface);
}
.masthead-inner {
max-width: 1180px;
margin: 0 auto;
padding: 3.4rem 2rem 2.6rem;
}
.kicker {
display: inline-flex;
align-items: baseline;
gap: 0.6em;
font-family: var(--font-mono);
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 1rem;
}
.kicker::before {
content: "";
display: inline-block;
width: 1.6em;
height: 2px;
background: var(--accent);
transform: translateY(-3px);
}
h1.title {
font-size: clamp(2.2rem, 4.4vw, 3.15rem);
margin: 0 0 0.55em;
max-width: 16ch;
}
.dek {
font-size: 1.14rem;
color: var(--ink-soft);
max-width: 56ch;
margin: 0 0 1.6rem;
}
.login-note {
display: inline-flex;
align-items: center;
gap: 0.65em;
background: var(--surface-2);
border: 1px solid var(--border);
border-radius: 10px;
padding: 0.65em 1em;
font-size: 0.92rem;
color: var(--ink-soft);
max-width: 66ch;
}
.login-note b { color: var(--ink); }
/* ---------- App grid on masthead ---------- */
.app-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.6rem;
margin-top: 2.2rem;
max-width: 900px;
}
.app-chip {
border: 1px solid var(--border);
background: var(--surface);
border-radius: 10px;
padding: 0.7em 0.85em;
font-size: 0.86rem;
font-weight: 600;
color: var(--ink-soft);
text-align: center;
}
/* ---------- Layout ---------- */
.layout {
max-width: 1180px;
margin: 0 auto;
padding: 2.6rem 2rem 6rem;
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 3.2rem;
align-items: start;
}
nav.toc {
position: sticky;
top: 1.6rem;
align-self: start;
font-size: 0.89rem;
}
nav.toc .toc-label {
font-family: var(--font-mono);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 0.9rem;
}
nav.toc ol {
list-style: none;
margin: 0;
padding: 0;
counter-reset: toc;
border-left: 1px solid var(--border);
}
nav.toc li { margin: 0; }
nav.toc a {
counter-increment: toc;
display: flex;
gap: 0.65em;
align-items: baseline;
padding: 0.5em 0 0.5em 1em;
margin-left: -1px;
border-left: 2px solid transparent;
color: var(--ink-soft);
text-decoration: none;
}
nav.toc a::before {
content: counter(toc, decimal-leading-zero);
font-family: var(--font-mono);
font-size: 0.76rem;
color: var(--muted);
flex-shrink: 0;
}
nav.toc a:hover { color: var(--accent); border-left-color: var(--border); }
nav.toc a.active { color: var(--accent); border-left-color: var(--accent); font-weight: 600; }
main { min-width: 0; }
section.chapter {
padding-top: 0.4rem;
margin-bottom: 3.6rem;
scroll-margin-top: 1.4rem;
}
section.chapter:last-child { margin-bottom: 0; }
.chapter-head {
display: flex;
align-items: baseline;
gap: 0.85rem;
margin-bottom: 1.3rem;
padding-bottom: 1.1rem;
border-bottom: 1px solid var(--border);
}
.chapter-num {
font-family: var(--font-mono);
font-size: 1.05rem;
font-weight: 600;
color: var(--accent);
flex-shrink: 0;
}
h2.chapter-title {
font-size: clamp(1.5rem, 2.6vw, 1.9rem);
margin: 0;
}
h3.sub {
font-size: 1.12rem;
margin: 1.9rem 0 0.75rem;
}
section.chapter > h3.sub:first-of-type,
.chapter-body > h3.sub:first-child { margin-top: 0.4rem; }
h4.microhead {
font-family: var(--font-mono);
font-size: 0.76rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
margin: 1.6rem 0 0.6rem;
}
/* ---------- Menu path chips ---------- */
.path {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 0;
font-family: var(--font-mono);
font-size: 0.86em;
font-weight: 500;
background: var(--code-surface);
color: var(--code-ink);
border: 1px solid var(--border-soft);
border-radius: 7px;
padding: 0.18em 0.15em;
line-height: 1.5;
}
.path span { padding: 0.05em 0.5em; white-space: nowrap; }
.path span:not(:last-child)::after {
content: "";
margin-left: 0.55em;
color: var(--muted);
}
p .path, li .path { margin: 0.15em 0; }
p:has(> .path:only-child) { max-width: none; }
.field {
font-family: var(--font-mono);
font-size: 0.86em;
background: var(--surface-2);
border: 1px solid var(--border-soft);
border-radius: 5px;
padding: 0.08em 0.4em;
color: var(--ink-soft);
}
/* ---------- Cards / role & app tables ---------- */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
gap: 0.85rem;
margin: 1.2rem 0 1.6rem;
}
.card {
border: 1px solid var(--border);
background: var(--surface);
border-radius: 12px;
padding: 1.1rem 1.15rem;
box-shadow: var(--shadow);
}
.card b {
display: block;
font-family: var(--font-display);
font-size: 1.02rem;
margin-bottom: 0.3rem;
}
.card p { margin: 0; font-size: 0.92rem; color: var(--ink-soft); max-width: none; }
table.roles {
width: 100%;
border-collapse: collapse;
margin: 1.2rem 0 1.6rem;
font-size: 0.94rem;
}
table.roles th, table.roles td {
text-align: left;
padding: 0.75em 0.9em;
border-bottom: 1px solid var(--border);
vertical-align: top;
}
table.roles th {
font-family: var(--font-mono);
font-size: 0.74rem;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
border-bottom: 1px solid var(--border);
}
table.roles td:first-child, table.roles th:first-child { font-weight: 600; white-space: nowrap; }
table.roles tr:last-child td { border-bottom: none; }
.role-badge {
display: inline-block;
font-family: var(--font-mono);
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.03em;
padding: 0.22em 0.6em;
border-radius: 999px;
background: var(--accent-soft);
color: var(--accent);
white-space: nowrap;
}
/* ---------- Steps ---------- */
ol.steps {
list-style: none;
padding: 0;
margin: 1.1rem 0 1.6rem;
counter-reset: step;
display: flex;
flex-direction: column;
gap: 0.9rem;
}
ol.steps li {
counter-increment: step;
display: grid;
grid-template-columns: 2rem 1fr;
gap: 0.9rem;
margin: 0;
max-width: 66ch;
}
ol.steps li::before {
content: counter(step);
font-family: var(--font-display);
font-weight: 600;
font-size: 0.95rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
background: var(--surface-2);
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
/* ---------- Callouts ---------- */
.callout {
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
background: var(--accent-soft);
border-radius: 0 10px 10px 0;
padding: 0.9em 1.15em;
margin: 1.3rem 0;
font-size: 0.94rem;
}
.callout p { margin: 0; max-width: none; color: var(--ink-soft); }
.callout p + p { margin-top: 0.6em; }
.callout b { color: var(--ink); }
.callout.warn {
border-left-color: var(--warn);
background: var(--warn-soft);
}
.callout-label {
font-family: var(--font-mono);
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--accent);
display: block;
margin-bottom: 0.4em;
}
.callout.warn .callout-label { color: var(--warn); }
/* ---------- Troubleshooting table ---------- */
table.trouble {
width: 100%;
border-collapse: collapse;
margin: 1.2rem 0 0.4rem;
font-size: 0.93rem;
}
table.trouble th {
text-align: left;
font-family: var(--font-mono);
font-size: 0.72rem;
letter-spacing: 0.07em;
text-transform: uppercase;
color: var(--muted);
font-weight: 600;
padding: 0 0.9em 0.6em 0;
border-bottom: 1px solid var(--border);
}
table.trouble td {
padding: 0.95em 0.9em 0.95em 0;
border-bottom: 1px solid var(--border-soft);
vertical-align: top;
}
table.trouble td:first-child { font-weight: 600; width: 34%; }
table.trouble tr:last-child td { border-bottom: none; }
/* ---------- Footer ---------- */
footer.colophon {
max-width: 1180px;
margin: 0 auto;
padding: 2.2rem 2rem 3.5rem;
border-top: 1px solid var(--border);
color: var(--muted);
font-size: 0.88rem;
}
/* ---------- Responsive ---------- */
@media (max-width: 880px) {
.layout { grid-template-columns: 1fr; }
nav.toc {
position: static;
border: 1px solid var(--border);
border-radius: 12px;
padding: 1rem 1.2rem;
background: var(--surface);
}
nav.toc ol { border-left: none; }
nav.toc a { padding-left: 0; margin-left: 0; border-left: none; }
.app-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
body { overflow-x: hidden; }
}
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}
</style>
</head>
<body>
<header class="masthead">
<div class="masthead-inner">
<span class="kicker">Community OS · Operations Guide</span>
<h1 class="title">TNCSC Handbook</h1>
<p class="dek">How the Tamil Nadu Cultural Society of Canada configures, runs, and troubleshoots its own system — written for board members and volunteers, not developers.</p>
<div class="login-note">📍 <span><b>Where to log in:</b> your production site's <span class="field">/web/login</span> page. Ask your technical contact for the exact address if you don't have it.</span></div>
<div class="app-grid">
<div class="app-chip">Membership</div>
<div class="app-chip">Events</div>
<div class="app-chip">School</div>
<div class="app-chip">Classifieds</div>
<div class="app-chip">Benefits</div>
<div class="app-chip">Interac Payments</div>
<div class="app-chip">Website</div>
<div class="app-chip">Settings</div>
</div>
</div>
</header>
<div class="layout">
<nav class="toc" aria-label="Table of contents">
<div class="toc-label">Contents</div>
<ol>
<li><a href="#understanding">Understanding the system</a></li>
<li><a href="#roles">User roles</a></li>
<li><a href="#branding">Branding</a></li>
<li><a href="#membership">Membership</a></li>
<li><a href="#events">Events</a></li>
<li><a href="#school">Tamil Class (School)</a></li>
<li><a href="#classifieds">Classifieds</a></li>
<li><a href="#benefits">Benefits</a></li>
<li><a href="#interac">Interac Payments</a></li>
<li><a href="#website">Website content</a></li>
<li><a href="#accounting">Accounting basics</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ol>
</nav>
<main>
<section class="chapter" id="understanding">
<div class="chapter-head"><span class="chapter-num">01</span><h2 class="chapter-title">Understanding the system</h2></div>
<div class="chapter-body">
<p>Once you log in, you'll see an <b>app switcher</b> — a grid of icons, one per area of the system. Each app is where a specific part of TNCSC's work lives:</p>
<div class="card-grid">
<div class="card"><b>Membership</b><p>Members, tiers/pricing, renewals</p></div>
<div class="card"><b>Events</b><p>Creating events, ticketing, check-in</p></div>
<div class="card"><b>School</b><p>Tamil Class terms, classes, attendance</p></div>
<div class="card"><b>Classifieds</b><p>The member board and its moderation queue</p></div>
<div class="card"><b>Benefits</b><p>Benefit centres and member perks</p></div>
<div class="card"><b>Interac Payments</b><p>Confirming e-Transfer payments</p></div>
<div class="card"><b>Website</b><p>The public website — pages, menus, content</p></div>
<div class="card"><b>Settings</b><p>Branding, org configuration, user accounts</p></div>
</div>
<div class="callout">
<span class="callout-label">If an app is missing</span>
<p>It's almost always a permissions issue, not a broken install. See <a href="#roles">User roles</a> and <a href="#troubleshooting">Troubleshooting</a> below.</p>
</div>
<p>Two databases exist during development/testing (<span class="field">tncsc_migration_test</span>, <span class="field">tncsc_site</span>) — your technical contact will tell you which one is live production once you go live. Everything in this guide applies the same way regardless of which database you're in.</p>
</div>
</section>
<section class="chapter" id="roles">
<div class="chapter-head"><span class="chapter-num">02</span><h2 class="chapter-title">User roles — who can do what</h2></div>
<div class="chapter-body">
<p>Go to <span class="path"><span>Settings</span><span>Users &amp; Companies</span><span>Users</span></span>, click a person's name, and under their groups you'll find <b>TNCSC Roles</b>. Check the box for the role that matches what they do:</p>
<table class="roles">
<tr><th>Role</th><th>Grants</th></tr>
<tr><td><span class="role-badge">Board Admin</span></td><td>Everything — membership, school, classifieds, benefits, events, and Interac payments. Give this only to whoever runs the system day-to-day.</td></tr>
<tr><td><span class="role-badge">Treasurer</span></td><td>Confirms Interac payments and has invoicing access.</td></tr>
<tr><td><span class="role-badge">Events&nbsp;Officer</span></td><td>Creates/manages events and staffs check-in.</td></tr>
<tr><td><span class="role-badge">School&nbsp;Coordinator</span></td><td>Manages Tamil Class terms, classes, students, enrollment.</td></tr>
<tr><td><span class="role-badge">Teacher</span></td><td>Can only take attendance for their own classes.</td></tr>
<tr><td><span class="role-badge">Classifieds&nbsp;Moderator</span></td><td>Reviews and publishes classified listings.</td></tr>
</table>
<p>A person can hold more than one role — e.g. someone can be both <b>School Coordinator</b> and <b>Teacher</b>. Members themselves don't need a role checked here at all: once someone is a member, they get their own self-service <b>member portal</b> automatically (see <a href="#membership">Membership</a>) — separate from these backend roles, and it doesn't require a staff login.</p>
<h4 class="microhead">Adding a new staff or volunteer account</h4>
<ol class="steps">
<li>Go to <span class="path"><span>Settings</span><span>Users &amp; Companies</span><span>Users</span><span>New</span></span></li>
<li>Fill in their name and email, then save.</li>
<li>Check the role(s) above for them. They'll get an email to set their own password.</li>
</ol>
</div>
</section>
<section class="chapter" id="branding">
<div class="chapter-head"><span class="chapter-num">03</span><h2 class="chapter-title">Branding: colours, logo, fonts</h2></div>
<div class="chapter-body">
<p><span class="path"><span>Settings</span><span>General Settings</span><span>Theme</span></span> (left-hand tab list).</p>
<ul>
<li><span class="field">Primary / Secondary / Accent Colour</span> — enter a colour as a hex code (e.g. <span class="field">#05091E</span>). These apply instantly across the website, the member portal, and PDF documents like the membership card — no other setting to touch.</li>
<li><span class="field">Logo</span> — upload the image file to use in the site header and on generated PDFs. If left blank, the company logo (<span class="path"><span>Settings</span><span>General Settings</span><span>Companies</span></span>) is used instead.</li>
<li><span class="field">Heading Font / Body Font</span> — enter a font name, e.g. <span class="field">Inter, sans-serif</span>. Needs to be a font available to browsers — ask your technical contact before entering something unusual.</li>
</ul>
<p>Click <b>Save</b> at the top of the page after any change.</p>
</div>
</section>
<section class="chapter" id="membership">
<div class="chapter-head"><span class="chapter-num">04</span><h2 class="chapter-title">Membership</h2></div>
<div class="chapter-body">
<h3 class="sub">Settings</h3>
<p><span class="path"><span>Settings</span><span>General Settings</span><span>Membership</span></span></p>
<ul>
<li><span class="field">Organization Name</span> — used in renewal emails and the membership card.</li>
<li><span class="field">Member ID Format</span> — the pattern new member IDs follow, e.g. <span class="field">TNCSC-{year}-{seq}</span> produces <span class="field">TNCSC-2026-014</span>. Don't change the placeholders unless your technical contact confirms it's safe — they drive the auto-numbering.</li>
<li><span class="field">Renewal Reminder Offsets</span> — comma-separated days-before-expiry to send reminders, e.g. <span class="field">30,14,7</span>.</li>
<li><span class="field">Member Portal</span> — toggle the self-service portal on or off site-wide.</li>
</ul>
<h3 class="sub">Membership tiers and pricing</h3>
<p><span class="path"><span>Membership</span><span>Configuration</span><span>Membership Tiers</span></span></p>
<p>Each row is a plan (Individual, Family, Student, Senior, Life, or whatever TNCSC offers). To <b>change a price</b>, open the tier and edit <span class="field">Price</span> — this is the only place pricing lives. To <b>add a new tier</b>, click New and fill in:</p>
<ul>
<li><span class="field">Name</span> — shown to members, e.g. "Corporate Sponsor"</li>
<li><span class="field">Code</span> — a short internal code, e.g. <span class="field">CORP</span> (no spaces)</li>
<li><span class="field">Price</span> and <span class="field">Currency</span></li>
<li><span class="field">Period</span> — Annual, One-Time, or Monthly</li>
<li><span class="field">Sequence</span> — controls display order (lower numbers show first)</li>
</ul>
<p>Deactivating a tier (rather than deleting it) keeps historical records intact for members who already hold it — use the tier's kebab (⋮) menu → <b>Archive</b>, instead of deleting.</p>
<h3 class="sub">Day-to-day membership work</h3>
<ul>
<li><span class="path"><span>Membership</span><span>Members</span></span> — the full member list. Open a member to see their tier, status, and expiry date.</li>
<li>Renewal reminders and draft renewal invoices are generated <b>automatically</b> by a daily background job — nothing to run manually. What you do need to do periodically: check <span class="path"><span>Invoicing</span><span>Customer Invoices</span></span> for draft renewal invoices and confirm/send them once payment is expected.</li>
<li>Each member can view their status, download their QR membership card, and renew from <span class="field">/my/membership</span> once logged into the website — no need to email cards out manually.</li>
<li>The membership card's QR code links to a public verification page (<span class="field">/membership/verify/&lt;member ID&gt;</span>) — anyone scanning it sees only "valid" / "expired" / "not found," never personal details.</li>
</ul>
</div>
</section>
<section class="chapter" id="events">
<div class="chapter-head"><span class="chapter-num">05</span><h2 class="chapter-title">Events</h2></div>
<div class="chapter-body">
<p><span class="path"><span>Events</span><span>Events</span><span>New</span></span> — this is standard Odoo event creation:</p>
<ol class="steps">
<li>Name, start/end date and time, location.</li>
<li>Under <b>Website</b>, check <span class="field">Is Published</span> to make it visible on the public Events page and the homepage's "Upcoming Events" section — <b>unpublished events won't appear anywhere on the public site</b>, even though they exist in the system.</li>
<li>Add ticket types under the event's <b>Registration</b> tab if you're charging for entry (e.g. "General $0$30").</li>
</ol>
<p>Every registration automatically gets a signed QR ticket, emailed with the confirmation. At the door, staff with the <span class="role-badge">Events Officer</span> role use <span class="path"><span>Events</span><span>Registration Desk</span></span> to scan tickets (or type the ticket reference manually) — it shows valid / already checked-in / invalid, and a live count of registered vs. checked-in.</p>
<p><span class="path"><span>Events</span><span>Reporting</span><span>Attendees / Revenues</span></span> gives you numbers after the fact without any setup.</p>
</div>
</section>
<section class="chapter" id="school">
<div class="chapter-head"><span class="chapter-num">06</span><h2 class="chapter-title">Tamil Class (School)</h2></div>
<div class="chapter-body">
<h3 class="sub">Settings</h3>
<p><span class="path"><span>Settings</span><span>General Settings</span><span>School</span></span>: <span class="field">At-Risk Attendance Threshold</span> — the attendance percentage below which a student is flagged "at-risk" in the report.</p>
<h3 class="sub">Configuration — set these up before each school year</h3>
<ul>
<li><span class="path"><span>School</span><span>Configuration</span><span>Terms</span></span> — create a term (e.g. "2026-27"), set start/end dates, and open it (<span class="field">State = Open</span>) once registration should begin. Only one term should normally be open at a time.</li>
<li><span class="path"><span>School</span><span>Configuration</span><span>Levels</span></span> — define your levels however TNCSC teaches (e.g. "Beginner" through "Advanced", or "Grade 1" through "Grade 12"). Each needs a short <span class="field">Code</span> and optional min/max age.</li>
<li><b>Classes</b> aren't under Configuration — create them from <span class="path"><span>School</span><span>Classes</span><span>New</span></span>: pick the term, level, teacher, weekday, time, location, and max students. This is what shows up as an option when a parent registers online.</li>
</ul>
<h3 class="sub">Day-to-day</h3>
<ul>
<li><span class="path"><span>School</span><span>Students / Enrollments</span></span> — see who's enrolled where.</li>
<li><span class="path"><span>School</span><span>Attendance Report</span></span> — a pivot table of attendance by class.</li>
<li><span class="path"><span>School</span><span>At-Risk Students</span></span> — students below the configured threshold, so you can follow up.</li>
<li>Teachers take attendance themselves from their own portal page (<span class="field">/school/attendance</span>) — a roster with a batch Present/Absent/Late/Excused save. An absence automatically emails the parent. Teachers only see their own classes.</li>
<li>Parents register their child at <span class="field">/school/register</span> on the public website, in three steps. A full class waitlists the student automatically and notifies them when a seat frees up — no manual waitlist management needed.</li>
<li>Each class auto-creates and manages its own online course/materials channel — enrolling or withdrawing a student automatically enrols/unenrols them there too.</li>
</ul>
</div>
</section>
<section class="chapter" id="classifieds">
<div class="chapter-head"><span class="chapter-num">07</span><h2 class="chapter-title">Classifieds</h2></div>
<div class="chapter-body">
<p><span class="path"><span>Settings</span><span>General Settings</span><span>Classifieds</span></span>: <span class="field">Listing Duration</span> — how many days a published listing stays live before auto-expiring.</p>
<p><span class="path"><span>Classifieds</span><span>Moderation</span></span> is where every new submission lands first (state: Pending Review). Open one to <b>Publish</b> or <b>Reject</b> it — only published listings appear on the public <span class="field">/classifieds</span> board. Posting requires the member to be logged in with an active membership, so this queue should only ever contain genuine member submissions.</p>
</div>
</section>
<section class="chapter" id="benefits">
<div class="chapter-head"><span class="chapter-num">08</span><h2 class="chapter-title">Benefits</h2></div>
<div class="chapter-body">
<p>Two things to set up, then one thing to log ongoing:</p>
<ul>
<li><span class="path"><span>Benefits</span><span>Benefit Centres</span></span> — the businesses/organizations offering a member benefit. Add their name, category, description, and logo.</li>
<li><span class="path"><span>Benefits</span><span>Benefits</span></span> — the actual discount/perk, linked to one or more <b>Membership Tiers</b> (only members holding one of those tiers will see it). Set the discount type and validity dates.</li>
<li><span class="path"><span>Benefits</span><span>Redemptions</span></span> — a log a benefit centre (or staff) creates when a member actually uses a benefit, after confirming the member is active. Mostly for your own record-keeping, not something to set up in advance.</li>
</ul>
<p>Members see only what their own tier qualifies them for at <span class="field">/my/benefits</span>; the public directory of participating centres is at <span class="field">/benefits</span>.</p>
</div>
</section>
<section class="chapter" id="interac">
<div class="chapter-head"><span class="chapter-num">09</span><h2 class="chapter-title">Interac e-Transfer Payments</h2></div>
<div class="chapter-body">
<h3 class="sub">One-time setup</h3>
<p><span class="path"><span>Invoicing</span><span>Configuration</span><span>Online Payments</span><span>Payment Providers</span></span> → open <b>Interac e-Transfer</b>:</p>
<ul>
<li><span class="field">Interac Recipient Email</span> — the real e-transfer address payments should be sent to.</li>
<li><span class="field">Interac Deadline Hours</span> — how long an unconfirmed payment stays pending before it's auto-cancelled (defaults to 48 hours if left blank).</li>
<li>Set the provider's <span class="field">State</span> to <span class="field">Enabled</span> so it appears as a checkout option.</li>
</ul>
<h3 class="sub">Day-to-day <span style="font-weight:400;color:var(--muted);font-family:var(--font-body);font-size:0.72em;">— Treasurer role</span></h3>
<p>When someone chooses Interac at checkout, they're emailed instructions (recipient address, amount, a reference code, and the deadline) automatically — nothing for you to send.</p>
<p>Once you actually receive the e-transfer in your real bank account, go to <span class="path"><span>Interac Payments</span><span>Pending Payments</span></span>, find the matching transaction (match by amount and reference code), and click <b>Payment Received</b>. This confirms the linked invoice/registration/order. If nobody confirms it before the deadline, it auto-cancels and the payer is notified by email.</p>
</div>
</section>
<section class="chapter" id="website">
<div class="chapter-head"><span class="chapter-num">10</span><h2 class="chapter-title">Website content</h2></div>
<div class="chapter-body">
<p>The <b>Website</b> app, then click <b>Edit</b> on any page while viewing it on the public site to change text, images, and layout directly (drag-and-drop editor) — no code for routine copy changes. <span class="path"><span>Site</span><span>Menu Editor</span></span> lets you add, remove, or reorder navigation items.</p>
<div class="callout warn">
<span class="callout-label">Known placeholder content</span>
<p>The <b>Sponsors</b> page and a few header/footer text blocks (e.g. a placeholder phone number) still carry content from the original site design — safe to edit or delete through the Website editor whenever real sponsor logos or a real phone number are ready.</p>
</div>
<div class="callout warn">
<span class="callout-label">No public signup yet</span>
<p><b>"Become a Member" buttons currently point to the Contact page</b>, not a self-service signup form — TNCSC doesn't have public online membership signup yet (only renewal, for existing members). Don't redirect these to a form that doesn't exist; talk to your technical contact if/when online signup becomes a priority.</p>
</div>
<p>For anything beyond content edits — new page types, new sections with custom behaviour, structural changes — that's a developer task, not a Website-editor task.</p>
</div>
</section>
<section class="chapter" id="accounting">
<div class="chapter-head"><span class="chapter-num">11</span><h2 class="chapter-title">Accounting basics</h2></div>
<div class="chapter-body">
<p>The chart of accounts, tax setup, and journals were seeded for a Canadian non-profit when the system was set up, including dedicated revenue accounts (Membership Dues, Event Revenue, Sponsorship, School Fees, Donations) and a <b>Donations</b> journal. You generally don't need to create new accounts for normal operations — membership tiers, event tickets, and school fees are already wired to the right ones.</p>
<div class="callout warn">
<span class="callout-label">Don't, without sign-off</span>
<p>Rename or delete existing accounts, or change the company's currency/country, without your accountant <i>and</i> technical contact both signing off — several of these are load-bearing for other parts of the system (e.g. renaming the dues account could silently break where membership payments post to).</p>
</div>
<p>Day-to-day invoicing/payment work happens under the <b>Invoicing</b> app as normal.</p>
</div>
</section>
<section class="chapter" id="troubleshooting">
<div class="chapter-head"><span class="chapter-num">12</span><h2 class="chapter-title">Troubleshooting &amp; getting help</h2></div>
<div class="chapter-body">
<table class="trouble">
<tr><th>Symptom</th><th>Likely cause / fix</th></tr>
<tr><td>An app I should have access to isn't in my switcher</td><td>You're missing the matching role — see <a href="#roles">User roles</a>. Ask a Board Admin to check the box for you.</td></tr>
<tr><td>I changed a setting and nothing changed on the site</td><td>Make sure you clicked <b>Save</b>. If it still looks wrong after a minute, it may need a technical restart — contact support rather than repeating the change.</td></tr>
<tr><td>A member says their QR card doesn't work</td><td>Check their membership <b>state</b> on their member record — the verify page only shows "valid" for members currently <i>active</i>.</td></tr>
<tr><td>An event isn't showing on the website</td><td>Confirm <span class="field">Is Published</span> is checked on the event — unpublished events are invisible on the public site by design.</td></tr>
<tr><td>A parent can't register for a class</td><td>Check the term is <b>Open</b> and the class isn't already full — full classes waitlist automatically rather than blocking registration outright.</td></tr>
<tr><td>An Interac payment doesn't confirm</td><td>Double-check the reference code and amount match exactly before clicking Payment Received — mismatches usually mean it's a different transaction.</td></tr>
</table>
<p style="margin-top:1.4rem;">For anything not covered here — a genuine error message, something that looks broken rather than misconfigured, or a request for new functionality — contact your technical support contact rather than experimenting on the live site. Screenshots and the exact steps you took help them fix it faster.</p>
</div>
</section>
</main>
</div>
<footer class="colophon">
This handbook covers the CommunityOS suite as configured for TNCSC. It does not cover server maintenance, backups, or deployment — those remain your technical contact's responsibility.
</footer>
<script>
(function () {
var links = Array.prototype.slice.call(document.querySelectorAll('nav.toc a'));
var sections = links.map(function (a) {
return document.getElementById(a.getAttribute('href').slice(1));
});
function onScroll() {
var pos = window.scrollY + 140;
var activeIndex = 0;
for (var i = 0; i < sections.length; i++) {
if (sections[i] && sections[i].offsetTop <= pos) activeIndex = i;
}
links.forEach(function (a, i) {
a.classList.toggle('active', i === activeIndex);
});
}
document.addEventListener('scroll', onScroll, { passive: true });
onScroll();
})();
</script>
</body>
</html>

359
docs/TNCSC_Handbook.md Normal file
View File

@ -0,0 +1,359 @@
# TNCSC CommunityOS Handbook
A plain-language guide for TNCSC board members and volunteers to configure
settings and run the system day-to-day — no developer required for any of
this. If something isn't covered here, or doesn't work the way this guide
describes, that's a question for your technical support contact, not
something to guess at.
**Where to log in:** your production site's `/web/login` page (ask your
technical contact for the exact address if you don't have it). Use the
account and password you were given.
---
## Contents
1. [Understanding the system](#1-understanding-the-system)
2. [User roles — who can do what](#2-user-roles--who-can-do-what)
3. [Branding: colours, logo, fonts](#3-branding-colours-logo-fonts)
4. [Membership](#4-membership)
5. [Events](#5-events)
6. [Tamil Class (School)](#6-tamil-class-school)
7. [Classifieds](#7-classifieds)
8. [Benefits](#8-benefits)
9. [Interac e-Transfer Payments](#9-interac-e-transfer-payments)
10. [Website content](#10-website-content)
11. [Accounting basics](#11-accounting-basics)
12. [Troubleshooting & getting help](#12-troubleshooting--getting-help)
---
## 1. Understanding the system
Once you log in, you'll see an **app switcher** — a grid of icons, one per
area of the system:
| App | What it's for |
|---|---|
| **Membership** | Members, tiers/pricing, renewals |
| **Events** | Creating events, ticketing, check-in |
| **School** | Tamil Class terms, classes, students, attendance |
| **Classifieds** | The member classifieds board and its moderation queue |
| **Benefits** | Benefit centres (partner businesses) and member benefits |
| **Interac Payments** | Confirming e-Transfer payments |
| **Website** | The public website — pages, menus, content |
| **Settings** | Branding, org-wide configuration, user accounts |
**If an app is missing from your switcher**, it's almost always a
permissions issue, not a broken install — see
[section 2](#2-user-roles--who-can-do-what) and
[troubleshooting](#12-troubleshooting--getting-help).
Two databases exist during development/testing (`tncsc_migration_test`,
`tncsc_site`) — your technical contact will tell you which one is live
production once you go live. Everything in this guide applies the same way
regardless of which database you're in.
---
## 2. User roles — who can do what
Go to **Settings → Users & Companies → Users**, click a person's name, and
under their groups you'll find **TNCSC Roles**. Check the box for the role
that matches what they do:
| Role | What it grants |
|---|---|
| **Board Admin** | Everything — membership, school, classifieds, benefits, events, and Interac payments. Give this only to whoever runs the system day-to-day. |
| **Treasurer** | Confirms Interac payments and has invoicing access. |
| **Events Officer** | Creates/manages events and staffs check-in. |
| **School Coordinator** | Manages Tamil Class terms, classes, students, enrollment. |
| **Teacher** | Can only take attendance for their own classes. |
| **Classifieds Moderator** | Reviews and publishes classified listings. |
A person can hold more than one role — e.g. someone can be both **School
Coordinator** and **Teacher**. Members themselves don't need a role
checked here at all: once someone is a member, they get their own
self-service **member portal** automatically (see
[section 4](#4-membership)) — that's separate from these backend roles,
and doesn't require a staff login.
**Adding a new staff/volunteer account:** Settings → Users & Companies →
Users → **New**, fill in their name and email, save, then check the
role(s) above. They'll get an email to set their own password.
---
## 3. Branding: colours, logo, fonts
**Settings → General Settings → Theme** (left-hand tab list).
- **Primary / Secondary / Accent Colour** — enter a colour as a hex code
(e.g. `#05091E`). These apply instantly across the website, the member
portal, and PDF documents like the membership card — no other setting
to touch.
- **Logo** — upload the image file to use in the site header and on
generated PDFs. If left blank, the company logo (Settings → General
Settings → Companies) is used instead.
- **Heading Font / Body Font** — enter a font name, e.g. `Inter,
sans-serif`. Needs to be a font available to browsers (a "web-safe"
font, or one already loaded via Google Fonts) — ask your technical
contact before entering something unusual.
Click **Save** at the top of the page after any change.
---
## 4. Membership
### 4.1 Settings
**Settings → General Settings → Membership**:
- **Organization Name** — used in renewal emails and the membership card.
- **Member ID Format** — the pattern new member IDs follow, e.g.
`TNCSC-{year}-{seq}` produces `TNCSC-2026-014`. Don't change the
`{year}`/`{seq}` placeholders unless your technical contact confirms
the change is safe — they drive the auto-numbering.
- **Renewal Reminder Offsets** — comma-separated days-before-expiry to
send reminder emails, e.g. `30,14,7` sends reminders 30, 14, and 7 days
before a membership expires.
- **Member Portal** — toggle to turn the self-service portal (`/my/membership`)
on or off site-wide.
### 4.2 Membership tiers and pricing
**Membership → Configuration → Membership Tiers.**
Each row is a plan (Individual, Family, Student, Senior, Life, or
whatever TNCSC offers). To **change a price**, open the tier and edit
**Price** — this is the only place pricing lives; there's no separate
price to update anywhere else. To **add a new tier**, click New and fill
in:
- **Name** — shown to members, e.g. "Corporate Sponsor"
- **Code** — a short internal code, e.g. `CORP` (no spaces)
- **Price** and **Currency**
- **Period** — Annual, One-Time, or Monthly
- **Sequence** — controls display order (lower numbers show first)
Deactivating a tier (rather than deleting it) keeps historical records
intact for members who already hold it — use the **Active** toggle,
found via the tier's kebab (⋮) menu → "Archive", instead of deleting.
### 4.3 Day-to-day membership work
- **Membership → Members** — the full member list. Open a member to see
their tier, status (active/renewal due/expired), and expiry date.
- Renewal reminders and draft renewal invoices are generated
**automatically** by a daily background job — you don't need to run
anything manually. What you *do* need to do periodically: check
**Invoicing → Customer Invoices** for draft renewal invoices and
confirm/send them once payment is expected.
- Each member can view their status, download their QR membership card,
and renew from `/my/membership` once logged into the website — you
don't need to email cards out manually.
- The membership card's QR code links to a public verification page
(`/membership/verify/<member ID>`) — anyone scanning it (e.g. at an
event door) sees only "valid" / "expired" / "not found," never personal
details.
---
## 5. Events
**Events → Events → New.** This is standard Odoo event creation:
1. Name, start/end date and time, location.
2. Under **Website**, check **Is Published** to make it visible on the
public Events page and the homepage's "Upcoming Events" section —
*unpublished events won't appear anywhere on the public site,* even
though they exist in the system.
3. Add ticket types under the event's **Registration** tab if you're
charging for entry (e.g. "General $0$30").
Every registration automatically gets a signed QR ticket, emailed with
the confirmation. At the door, staff with the **Events Officer** role use
**Events → Registration Desk** to scan tickets (or type the ticket
reference manually) — it shows valid / already checked-in / invalid, and
a live count of registered vs. checked-in.
**Events → Reporting → Attendees / Revenues** gives you numbers after the
fact without any setup.
---
## 6. Tamil Class (School)
### 6.1 Settings
**Settings → General Settings → School**: **At-Risk Attendance
Threshold** — the attendance percentage below which a student is flagged
"at-risk" in the report (see below).
### 6.2 Configuration — set these up before each school year
**School → Configuration → Terms** — create a term (e.g. "2026-27"),
set start/end dates, and open it (**State = Open**) once registration
should begin. Only one term should normally be open at a time.
**School → Configuration → Levels** — define your levels however TNCSC
teaches (e.g. "Beginner" through "Advanced", or "Grade 1" through "Grade
12"). Each needs a short **Code** (e.g. `BEG`) and optional min/max age.
**Classes** aren't under Configuration — create them directly from
**School → Classes → New**: pick the term, level, teacher, weekday,
time, location, and max students. This is what shows up as an option
when a parent registers online.
### 6.3 Day-to-day
- **School → Students / Enrollments** — see who's enrolled where.
- **School → Attendance Report** — a pivot table of attendance by class.
- **School → At-Risk Students** — students below the configured
attendance threshold, so you can follow up.
- Teachers take attendance themselves from their own portal page
(`/school/attendance`) — a roster of today's/this-week's classes with a
batch Present/Absent/Late/Excused save. An absence automatically emails
the parent. Teachers only see their own classes there.
- Parents register their child at `/school/register` on the public
website (parent → student → class + payment, in three steps). If a
class is full, the student is waitlisted and notified automatically
when a seat frees up — no manual waitlist management needed.
- Each class auto-creates and manages its own online course/materials
channel — enrolling or withdrawing a student automatically
enrols/unenrols them there too.
---
## 7. Classifieds
**Settings → General Settings → Classifieds**: **Listing Duration** — how
many days a published listing stays live before auto-expiring.
**Classifieds → Moderation** is where every new submission lands first
(state: Pending Review). Open one to **Publish** or **Reject** it — only
published listings appear on the public `/classifieds` board. Posting
requires the member to be logged in with an active membership, so this
queue should only ever contain genuine member submissions.
---
## 8. Benefits
Two things to set up, then one thing to log ongoing:
- **Benefits → Benefit Centres** — the businesses/organizations offering
a member benefit. Add their name, category, description, and logo.
- **Benefits → Benefits** — the actual discount/perk, linked to one or
more **Membership Tiers** (only members holding one of those tiers will
see it). Set the discount type (percent / fixed amount / perk) and
validity dates.
- **Benefits → Redemptions** — a log a benefit centre (or staff) creates
when a member actually uses a benefit, after confirming the member is
active. This is mostly for your own record-keeping of usage, not
something you need to set up in advance.
Members see only what their own tier qualifies them for at
`/my/benefits`; the public directory of participating centres is at
`/benefits`.
---
## 9. Interac e-Transfer Payments
### 9.1 One-time setup
**Invoicing → Configuration → Online Payments → Payment Providers** →
open **Interac e-Transfer**:
- **Interac Recipient Email** — the real e-transfer address payments
should be sent to.
- **Interac Deadline Hours** — how long an unconfirmed payment stays
pending before it's automatically cancelled (defaults to 48 hours if
left blank).
- Set the provider's **State** to **Enabled** so it appears as a
checkout option.
### 9.2 Day-to-day (Treasurer role)
When someone chooses Interac at checkout, they're emailed instructions
(recipient address, amount, a reference code, and the deadline) — this
happens automatically, nothing for you to send.
Once you actually receive the e-transfer in your real bank/e-transfer
account, go to **Interac Payments → Pending Payments**, find the matching
transaction (match by amount and reference code), and click **Payment
Received**. This confirms the linked invoice/registration/order. If
nobody confirms it before the deadline, it auto-cancels and the payer is
notified by email.
---
## 10. Website content
**Website** app, then click **Edit** on any page while viewing it on the
public site to change text, images, and layout directly (drag-and-drop
editor) — no code involved for routine copy changes. **Site → Menu
Editor** lets you add, remove, or reorder navigation items.
Two things worth knowing about the current site before you edit it:
- The **Sponsors** page and a few header/footer text blocks (e.g. a
placeholder phone number) still contain placeholder content carried
over from the original site design — safe to edit or delete through
the Website editor whenever real sponsor logos or a real phone number
are ready.
- **"Become a Member" buttons currently point to the Contact page**, not
a self-service signup form — TNCSC doesn't have public online
membership signup yet (only renewal, for existing members). Don't
redirect these to a form that doesn't exist; talk to your technical
contact if/when online signup becomes a priority.
For anything beyond content edits — new page types, new sections with
custom behaviour, structural changes — that's a developer task, not a
Website-editor task.
---
## 11. Accounting basics
The chart of accounts, tax setup, and journals were seeded for a Canadian
non-profit when the system was set up, including dedicated revenue
accounts (Membership Dues, Event Revenue, Sponsorship, School Fees,
Donations) and a **Donations** journal. You generally don't need to
create new accounts for normal operations — membership tiers, event
tickets, and school fees are already wired to the right ones.
**Do not** rename or delete existing accounts, or change the company's
currency/country, without your accountant and technical contact both
signing off — several of these are load-bearing for other parts of the
system (e.g. renaming the dues account could silently break where
membership payments post to). Day-to-day invoicing/payment work happens
under the **Invoicing** app as normal.
---
## 12. Troubleshooting & getting help
| Symptom | Likely cause / fix |
|---|---|
| An app I should have access to isn't in my switcher | You're missing the matching role — see [section 2](#2-user-roles--who-can-do-what). Ask a Board Admin to check the box for you. |
| I changed a setting and nothing changed on the site | Make sure you clicked **Save**. If it still looks wrong after a minute, it may need a technical restart — contact support rather than repeating the change. |
| A member says their QR card doesn't work | Check their membership **state** on their member record — the verify page only shows "valid" for members currently in the *active* state. |
| An event isn't showing on the website | Confirm **Is Published** is checked on the event (see [section 5](#5-events)) — unpublished events are invisible on the public site by design. |
| A parent can't register for a class | Check the term is **Open** and the class isn't already full (School → Classes) — full classes waitlist automatically rather than blocking registration outright. |
| An Interac payment doesn't confirm | Double-check the reference code and amount match exactly before clicking Payment Received — mismatches usually mean it's a different transaction. |
For anything not covered here — a genuine error message, something that
looks broken rather than misconfigured, or a request for new
functionality — contact your technical support contact rather than
experimenting on the live site. Screenshots and the exact steps you took
help them fix it faster.
---
*This handbook covers the CommunityOS suite as configured for TNCSC. It
does not cover server maintenance, backups, or deployment — those remain
your technical contact's responsibility.*