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

115 lines
5.0 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_benefit_partner_list" model="ir.ui.view">
<field name="name">community.benefit.partner.list</field>
<field name="model">community.benefit.partner</field>
<field name="arch" type="xml">
<list string="Benefit Centres">
<field name="name"/>
<field name="category"/>
<field name="active" column_invisible="1"/>
</list>
</field>
</record>
<record id="view_benefit_partner_form" model="ir.ui.view">
<field name="name">community.benefit.partner.form</field>
<field name="model">community.benefit.partner</field>
<field name="arch" type="xml">
<form string="Benefit Centre">
<sheet>
<div class="oe_title"><h1><field name="partner_id"/></h1></div>
<group>
<field name="category"/>
<field name="locations"/>
<field name="active"/>
</group>
<group string="Description"><field name="description" nolabel="1"/></group>
<notebook>
<page string="Benefits">
<field name="benefit_ids">
<list><field name="name"/><field name="discount_type"/><field name="value"/></list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_benefit_partner" model="ir.actions.act_window">
<field name="name">Benefit Centres</field>
<field name="res_model">community.benefit.partner</field>
<field name="view_mode">list,form</field>
</record>
<record id="view_benefit_list" model="ir.ui.view">
<field name="name">community.benefit.list</field>
<field name="model">community.benefit</field>
<field name="arch" type="xml">
<list string="Benefits">
<field name="name"/>
<field name="benefit_partner_id"/>
<field name="discount_type"/>
<field name="value"/>
<field name="active" column_invisible="1"/>
</list>
</field>
</record>
<record id="view_benefit_form" model="ir.ui.view">
<field name="name">community.benefit.form</field>
<field name="model">community.benefit</field>
<field name="arch" type="xml">
<form string="Benefit">
<sheet>
<div class="oe_title"><h1><field name="name"/></h1></div>
<group>
<group>
<field name="benefit_partner_id"/>
<field name="tier_ids" widget="many2many_tags"/>
<field name="active"/>
</group>
<group>
<field name="discount_type"/>
<field name="value" invisible="discount_type == 'perk'"/>
<field name="valid_from"/>
<field name="valid_to"/>
</group>
</group>
<group string="Description"><field name="description" nolabel="1"/></group>
</sheet>
</form>
</field>
</record>
<record id="action_benefit" model="ir.actions.act_window">
<field name="name">Benefits</field>
<field name="res_model">community.benefit</field>
<field name="view_mode">list,form</field>
</record>
<record id="view_benefit_redemption_list" model="ir.ui.view">
<field name="name">community.benefit.redemption.list</field>
<field name="model">community.benefit.redemption</field>
<field name="arch" type="xml">
<list string="Redemptions">
<field name="date"/>
<field name="member_id"/>
<field name="benefit_id"/>
<field name="verified_by"/>
</list>
</field>
</record>
<record id="action_benefit_redemption" model="ir.actions.act_window">
<field name="name">Redemptions</field>
<field name="res_model">community.benefit.redemption</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_benefits_root" name="Benefits" sequence="28"
groups="community_benefits.group_benefits_manager"/>
<menuitem id="menu_benefits_redemptions" name="Redemptions"
parent="menu_benefits_root" action="action_benefit_redemption" sequence="10"/>
<menuitem id="menu_benefits_list" name="Benefits"
parent="menu_benefits_root" action="action_benefit" sequence="20"/>
<menuitem id="menu_benefit_partners" name="Benefit Centres"
parent="menu_benefits_root" action="action_benefit_partner" sequence="30"/>
</odoo>