118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
|
|
PERIOD_DURATION_DAYS = {
|
|
'annual': 365,
|
|
'monthly': 30,
|
|
'one_time': None, # never expires
|
|
}
|
|
|
|
|
|
class CommunityMembershipTier(models.Model):
|
|
_name = 'community.membership.tier'
|
|
_description = 'Membership Tier'
|
|
_order = 'sequence, id'
|
|
|
|
name = fields.Char(required=True, translate=True)
|
|
code = fields.Char(required=True, help="Short internal code, e.g. 'IND', 'FAM'.")
|
|
price = fields.Monetary(currency_field='currency_id', required=True)
|
|
currency_id = fields.Many2one(
|
|
'res.currency', required=True,
|
|
default=lambda self: self.env.company.currency_id,
|
|
)
|
|
period = fields.Selection(
|
|
[
|
|
('annual', 'Annual'),
|
|
('one_time', 'One-time (lifetime)'),
|
|
('monthly', 'Monthly'),
|
|
('fixed_term', 'Fixed term (years)'),
|
|
],
|
|
default='annual', required=True,
|
|
)
|
|
term_years = fields.Integer(
|
|
string='Term (Years)', default=1,
|
|
help="Length of the membership in years, for fixed-term tiers (e.g. 10 for a long-term membership).",
|
|
)
|
|
description = fields.Text(
|
|
translate=True,
|
|
help="Who the tier is for and what it covers, shown to applicants on the signup page.",
|
|
)
|
|
min_age = fields.Integer(
|
|
string='Minimum Age',
|
|
help="Applicants must be at least this old to choose this tier. 0 means no age limit.",
|
|
)
|
|
requires_proof = fields.Boolean(
|
|
string='Requires Proof Document',
|
|
help="Applicants may be asked to upload proof of eligibility (e.g. age or student status).",
|
|
)
|
|
allows_family_members = fields.Boolean(
|
|
string='Includes Family Members',
|
|
help="The membership also covers family members, who can be listed on the member's record.",
|
|
)
|
|
member_state_logic = fields.Text(
|
|
string='State Notes',
|
|
help="Free-text notes for admins on any special membership-state handling "
|
|
"this tier needs (e.g. lifetime tiers never move to 'expired').",
|
|
)
|
|
sequence = fields.Integer(default=10)
|
|
product_id = fields.Many2one(
|
|
'product.product', string='Membership Product', readonly=True, copy=False,
|
|
help="Auto-created service product used to invoice this tier's dues.",
|
|
)
|
|
active = fields.Boolean(default=True)
|
|
|
|
_code_uniq = models.Constraint(
|
|
'unique(code)', 'A membership tier with this code already exists.',
|
|
)
|
|
_term_years_positive = models.Constraint(
|
|
"CHECK(period != 'fixed_term' OR term_years > 0)",
|
|
'A fixed-term tier needs a term of at least one year.',
|
|
)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
tiers = super().create(vals_list)
|
|
for tier in tiers:
|
|
if not tier.product_id:
|
|
tier.product_id = tier._create_membership_product()
|
|
return tiers
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
if 'price' in vals or 'name' in vals:
|
|
for tier in self:
|
|
if tier.product_id:
|
|
tier.product_id.write(tier._membership_product_values())
|
|
return res
|
|
|
|
def _membership_product_values(self):
|
|
self.ensure_one()
|
|
vals = {
|
|
'name': self.name,
|
|
'list_price': self.price,
|
|
'type': 'service',
|
|
'sale_ok': True,
|
|
'purchase_ok': False,
|
|
}
|
|
if 'invoice_policy' in self.env['product.product']._fields:
|
|
vals['invoice_policy'] = 'order'
|
|
return vals
|
|
|
|
def _create_membership_product(self):
|
|
self.ensure_one()
|
|
product = self.env['product.product'].create(self._membership_product_values())
|
|
return product
|
|
|
|
def duration_days(self):
|
|
self.ensure_one()
|
|
return PERIOD_DURATION_DAYS.get(self.period)
|
|
|
|
def get_expiry_date(self, start):
|
|
"""Expiry for a membership of this tier starting on `start`, or False if it never expires."""
|
|
self.ensure_one()
|
|
if self.period == 'fixed_term':
|
|
return start + relativedelta(years=self.term_years)
|
|
duration_days = self.duration_days()
|
|
return start + relativedelta(days=duration_days) if duration_days else False
|