diff --git a/contract/__manifest__.py b/contract/__manifest__.py index 644a317788..8ca90ff22e 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -11,7 +11,7 @@ { "name": "Recurring - Contracts Management", - "version": "19.0.1.0.0", + "version": "19.0.1.1.3", "category": "Contract Management", "license": "AGPL-3", "author": "Tecnativa, ACSONE SA/NV, Odoo Community Association (OCA)", @@ -39,6 +39,9 @@ "views/contract_portal_templates.xml", "wizards/contract_manually_create_invoice.xml", ], + "demo": [ + "demo/contract_demo.xml", + ], "assets": { "web.assets_frontend": ["contract/static/src/scss/frontend.scss"], "web.assets_tests": ["contract/static/src/js/contract_portal_tour.esm.js"], diff --git a/contract/demo/contract_demo.xml b/contract/demo/contract_demo.xml new file mode 100644 index 0000000000..b00d2cdd69 --- /dev/null +++ b/contract/demo/contract_demo.xml @@ -0,0 +1,145 @@ + + + + + + Premium + + + + Renewal Required + + + + Internal + + + + + + Monthly Subscription Template + sale + monthly + post-paid + 1 + + + + + + Demo Running Contract + DEMO/RUN/0001 + + sale + True + + + + + + + + Demo Expired Contract + DEMO/EXP/0001 + + sale + True + + + + + + + + Demo Upcoming Contract + DEMO/UP/0001 + + sale + True + + + + + + diff --git a/contract/models/contract.py b/contract/models/contract.py index 8008e24fa9..b6cbdcd5a2 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -39,15 +39,23 @@ class ContractContract(models.Model): default=lambda self: self.env.user, ) group_id = fields.Many2one( - string="Group", + string="Analytic Account", comodel_name="account.analytic.account", compute="_compute_group_id", store=True, readonly=False, ondelete="restrict", + help=( + "Analytic account shared by every line of this contract. " + "Computed from the lines' analytic distribution when all lines " + "point at the same account; left empty when the lines use " + "different accounts. Stored compute, editable: clearing or " + "setting it manually overrides the computed value." + ), ) tag_ids = fields.Many2many(comodel_name="contract.tag", string="Tags") - note = fields.Text(string="Notes") + color = fields.Integer(string="Color Index") + note = fields.Html(string="Notes", sanitize=True) # === Partner and Commercial Info === partner_id = fields.Many2one( @@ -124,6 +132,13 @@ class ContractContract(models.Model): # === Dates === date_end = fields.Date(compute="_compute_date_end", store=True, readonly=False) + last_date_invoiced = fields.Date( + string="Date of Last Invoice", + compute="_compute_last_date_invoiced", + store=True, + readonly=True, + copy=False, + ) # === Compute Methods === @@ -210,6 +225,37 @@ def _compute_date_end(self): if date_end and all(date_end): contract.date_end = max(date_end) + @api.constrains("date_start", "date_end") + def _check_contract_start_end_dates(self): + # When lines are present, the line-level _check_start_end_dates + # constraint covers per-line validation, and contract.date_end is + # computed from the lines while contract.date_start may legitimately + # default to today even for an existing-line contract — so the + # contract-level pair is not strictly comparable. An empty date_end + # (open-ended contract) is also allowed. + for rec in self: + if rec.contract_line_ids: + continue + if rec.date_start and rec.date_end and rec.date_start > rec.date_end: + raise ValidationError( + self.env._("Contract end date must be after the start date.") + ) + + @api.depends( + "contract_line_ids.last_date_invoiced", + "contract_line_ids.is_canceled", + ) + def _compute_last_date_invoiced(self): + for contract in self: + dates = contract.contract_line_ids.filtered( + lambda line: ( + line.last_date_invoiced + and not line.is_canceled + and (not line.display_type or line.is_recurring_note) + ) + ).mapped("last_date_invoiced") + contract.last_date_invoiced = max(dates) if dates else False + def _inverse_partner_id(self): for rec in self: if not rec.invoice_partner_id: @@ -655,6 +701,7 @@ def _recurring_create_invoice(self, date_ref=False): self._add_contract_origin(moves) self._invoice_followers(moves) self._compute_recurring_next_date() + self._compute_last_date_invoiced() return moves @api.model diff --git a/contract/models/contract_recurring_mixin.py b/contract/models/contract_recurring_mixin.py index 88b1659fb3..34dedfd1b0 100644 --- a/contract/models/contract_recurring_mixin.py +++ b/contract/models/contract_recurring_mixin.py @@ -70,6 +70,7 @@ class ContractRecurringMixin(models.AbstractModel): # Define when and how invoices should be issued within the recurrence. last_date_invoiced = fields.Date( + string="Date of Last Invoice", readonly=True, copy=False, ) diff --git a/contract/models/contract_tag.py b/contract/models/contract_tag.py index 9cd98aeaa7..60673d5bb6 100644 --- a/contract/models/contract_tag.py +++ b/contract/models/contract_tag.py @@ -1,6 +1,8 @@ # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from random import randint + from odoo import fields, models @@ -8,8 +10,13 @@ class ContractTag(models.Model): _name = "contract.tag" _description = "Contract Tag" + def _get_default_color(self): + return randint(1, 11) + name = fields.Char(required=True) company_id = fields.Many2one( "res.company", string="Company", default=lambda self: self.env.company ) - color = fields.Integer("Color Index", default=0) + color = fields.Integer( + string="Color Index", default=lambda self: self._get_default_color() + ) diff --git a/contract/models/contract_template.py b/contract/models/contract_template.py index b886071e26..dcc7878f50 100644 --- a/contract/models/contract_template.py +++ b/contract/models/contract_template.py @@ -20,6 +20,7 @@ class ContractTemplate(models.Model): # === Basic Info === + active = fields.Boolean(default=True) name = fields.Char(required=True) partner_id = fields.Many2one( comodel_name="res.partner", diff --git a/contract/static/description/icon.png b/contract/static/description/icon.png index 3a0328b516..41af807e5b 100644 Binary files a/contract/static/description/icon.png and b/contract/static/description/icon.png differ diff --git a/contract/static/description/icon.svg b/contract/static/description/icon.svg new file mode 100644 index 0000000000..93bbb22aee --- /dev/null +++ b/contract/static/description/icon.svg @@ -0,0 +1,87 @@ + + + + + + + +