community_membership: - Tiers get a fixed-term period (term_years), minimum age, a proof-document flag, a family-member flag and a signup description. - Members get first/last name (composing the contact name), month and year of birth with computed age, board approval state with approve/reject buttons, terms acceptance, a proof-of-eligibility upload and an inline family members list. Birth date and proof document are restricted to Membership Managers. - Constraints: plausible birth year; a tier's minimum age is enforced when the birth year is known. - Fix pre-existing tests that collided with deployment-seeded tier codes and member ID format. tncsc_deployment: - Five Yes/No eligibility questions from the tncsc.com registration form (manager-only), shown in an Eligibility section of the member form. - Tiers now match tncsc.com/membership-registration: Annual/Family $30, Long-Term (10 yr) $250, Life $500, Senior Annual (65+) $15, Senior Life (60+) $250, Student Annual $10 (CAD). - 19.0.1.1.0 post-migrate rewrites existing tiers (noupdate data) and archives the unused Individual tier. scripts/migrate_wp_members.py maps WordPress levels to the new LONG and SENLIFE tiers. .mcp.json (holds credentials) is now gitignored. Verified on the local Docker stack: -u on tncsc_site ran the migration cleanly, and a fresh install with --test-enable passed 32/32 tests for community_membership and tncsc_deployment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
6.8 KiB
XML
119 lines
6.8 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<record id="view_partner_form_membership" model="ir.ui.view">
|
|
<field name="name">res.partner.form.membership</field>
|
|
<field name="model">res.partner</field>
|
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
|
<field name="arch" type="xml">
|
|
<notebook position="inside">
|
|
<page string="Membership" name="membership">
|
|
<group>
|
|
<group string="Member">
|
|
<field name="first_name"/>
|
|
<field name="last_name"/>
|
|
<label for="birth_month" string="Month / Year of Birth"
|
|
groups="community_membership.group_membership_manager"/>
|
|
<div class="o_row" groups="community_membership.group_membership_manager">
|
|
<field name="birth_month" placeholder="Month"/>
|
|
<field name="birth_year" placeholder="Year" options="{'format': false}"/>
|
|
</div>
|
|
<field name="member_age" invisible="not birth_year"
|
|
groups="community_membership.group_membership_manager"/>
|
|
</group>
|
|
<group string="Application">
|
|
<field name="membership_approval_state"/>
|
|
<field name="membership_terms_accepted"/>
|
|
<field name="membership_proof_document" filename="membership_proof_filename"
|
|
groups="community_membership.group_membership_manager"/>
|
|
<field name="membership_proof_filename" invisible="1"
|
|
groups="community_membership.group_membership_manager"/>
|
|
</group>
|
|
</group>
|
|
<group>
|
|
<group string="Membership">
|
|
<field name="membership_member_id"/>
|
|
<field name="membership_tier_id"/>
|
|
<field name="membership_state"/>
|
|
<field name="family_head_id"/>
|
|
</group>
|
|
<group string="Dates">
|
|
<field name="membership_start"/>
|
|
<field name="membership_expiry"/>
|
|
<field name="is_volunteer"/>
|
|
<field name="volunteer_hours" invisible="not is_volunteer"/>
|
|
</group>
|
|
</group>
|
|
<group name="eligibility" string="Eligibility"
|
|
groups="community_membership.group_membership_manager"/>
|
|
<separator string="Family Members" invisible="family_head_id"/>
|
|
<field name="family_member_ids" invisible="family_head_id"
|
|
context="{'default_family_head_id': id}">
|
|
<list editable="bottom">
|
|
<field name="first_name"/>
|
|
<field name="last_name"/>
|
|
<field name="name" column_invisible="1"/>
|
|
<field name="email" optional="show"/>
|
|
<field name="phone" optional="hide"/>
|
|
</list>
|
|
</field>
|
|
<div class="d-flex gap-2 mt-3">
|
|
<button name="action_approve_membership" type="object"
|
|
string="Approve" class="btn-secondary"
|
|
groups="community_membership.group_membership_manager"
|
|
invisible="membership_approval_state != 'pending'"/>
|
|
<button name="action_reject_membership" type="object"
|
|
string="Reject" class="btn-secondary"
|
|
groups="community_membership.group_membership_manager"
|
|
invisible="membership_approval_state != 'pending'"/>
|
|
<button name="action_activate_membership" type="object"
|
|
string="Activate Membership" class="btn-primary"
|
|
invisible="membership_state == 'active'"/>
|
|
</div>
|
|
</page>
|
|
</notebook>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="view_partner_list_membership" model="ir.ui.view">
|
|
<field name="name">res.partner.list.membership</field>
|
|
<field name="model">res.partner</field>
|
|
<field name="inherit_id" ref="base.view_partner_tree"/>
|
|
<field name="arch" type="xml">
|
|
<field name="display_name" position="after">
|
|
<field name="membership_member_id" optional="show"/>
|
|
<field name="membership_tier_id" optional="show"/>
|
|
<field name="membership_state" optional="show"/>
|
|
</field>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="view_partner_kanban_membership" model="ir.ui.view">
|
|
<field name="name">res.partner.kanban.membership.search</field>
|
|
<field name="model">res.partner</field>
|
|
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
|
<field name="arch" type="xml">
|
|
<filter name="type_person" position="after">
|
|
<separator/>
|
|
<filter string="Members" name="is_member" domain="[('membership_state', '!=', 'none')]"/>
|
|
<filter string="Active Members" name="membership_active" domain="[('membership_state', '=', 'active')]"/>
|
|
<filter string="Renewal Due" name="membership_renewal_due" domain="[('membership_state', '=', 'renewal_due')]"/>
|
|
<filter string="Expired Members" name="membership_expired" domain="[('membership_state', '=', 'expired')]"/>
|
|
<filter string="Pending Approval" name="membership_pending_approval"
|
|
domain="[('membership_approval_state', '=', 'pending')]"/>
|
|
<group name="group_by" position="inside">
|
|
<filter string="Membership Tier" name="group_by_tier" context="{'group_by': 'membership_tier_id'}"/>
|
|
<filter string="Membership State" name="group_by_state" context="{'group_by': 'membership_state'}"/>
|
|
</group>
|
|
</filter>
|
|
</field>
|
|
</record>
|
|
|
|
<record id="action_membership_members" model="ir.actions.act_window">
|
|
<field name="name">Members</field>
|
|
<field name="res_model">res.partner</field>
|
|
<field name="view_mode">list,kanban,form</field>
|
|
<field name="domain">[('membership_state', '!=', 'none')]</field>
|
|
<field name="context">{'default_membership_state': 'invoiced'}</field>
|
|
</record>
|
|
</odoo>
|