TNCSC_Odoo/addons/community_benefits/views/benefits_templates.xml
metatroncubeswdev 91e91fe1b8 feat(community_classifieds, community_benefits): Phase 4
community_classifieds: a member-gated classifieds board. community.classified
(title/category/description/up-to-3-images via a child .image model/contact
info/state) with a public board at /classifieds, detail pages that track
view_count, portal posting at /classifieds/new (soft-detects
community_membership - blocks non-active-members only if that module is
installed, otherwise anyone logged in can post), a "my listings" portal
page with self-service renew, and an admin moderation queue
(publish/reject). Daily crons expire past-due listings and send a 7-day
expiry warning; a new-submission notice goes out to the Classifieds
Moderator group. Listing duration is configurable (Settings), never
hardcoded.

community_benefits: community.benefit.partner (benefit centres) offer
community.benefit entitlements scoped to specific community_membership
tiers via tier_ids. community.benefit.redemption logs a redemption but
its create() is guarded by a constraint that re-checks the same
active-membership condition the membership QR verification page uses
(membership_state in active/renewal_due) plus tier entitlement, refusing
the redemption otherwise. Public directory at /benefits, portal page at
/my/benefits listing only benefits the member's tier actually entitles
them to.

Two more real Odoo 19 API changes hit here: search-view <group> elements
for "Group By" sections no longer accept a `string` attribute (must use
`name` only - same fix as community_membership's partner search view,
applied here to a fresh module), and res.groups.users was renamed to
user_ids.

Verified against a live Odoo 19 + Postgres 16 container: 7+4 automated
tests pass, plus a full manual live run covering the Phase 4 gate exactly -
confirmed a non-member is blocked from /classifieds/new, activated a real
membership, posted a classified (pending_review), published it via the
moderator action, confirmed it appears on the public board and detail
page with view_count incrementing, entitled a tier to a benefit, logged a
redemption for the active member, confirmed it shows on /my/benefits, and
confirmed redemption creation is refused for a non-member.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 21:51:38 -04:00

54 lines
2.4 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="benefits_directory_page" name="Benefit Centres Directory">
<t t-call="website.layout">
<div class="container" style="margin-top: 24px; margin-bottom: 60px;">
<h2>Participating Benefit Centres</h2>
<div class="row">
<t t-foreach="centres" t-as="centre">
<div class="col-md-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title" t-out="centre.name"/>
<p class="text-muted" t-out="dict(centre._fields['category'].selection).get(centre.category)"/>
<p t-out="centre.locations or ''"/>
</div>
</div>
</div>
</t>
<t t-if="not centres">
<p class="text-muted">No participating benefit centres yet.</p>
</t>
</div>
</div>
</t>
</template>
<template id="portal_my_benefits" name="My Benefits">
<t t-call="portal.portal_layout">
<div class="o_portal_my_doc_table">
<h3>My Benefits</h3>
<t t-if="not benefits">
<p class="alert alert-info">No benefits available for your current membership tier.</p>
</t>
<table class="table" t-if="benefits">
<thead><tr><th>Benefit</th><th>Centre</th><th>Value</th></tr></thead>
<tbody>
<t t-foreach="benefits" t-as="benefit">
<tr>
<td t-out="benefit.name"/>
<td t-out="benefit.benefit_partner_id.name"/>
<td>
<t t-if="benefit.discount_type == 'percent'"><t t-out="benefit.value"/>%</t>
<t t-elif="benefit.discount_type == 'amount'"><t t-out="benefit.value"/></t>
<t t-else="">Perk</t>
</td>
</tr>
</t>
</tbody>
</table>
</div>
</t>
</template>
</odoo>