diff --git a/product_contract/models/sale_order.py b/product_contract/models/sale_order.py index 0832b7b1b..680505f0a 100644 --- a/product_contract/models/sale_order.py +++ b/product_contract/models/sale_order.py @@ -25,20 +25,37 @@ class SaleOrder(models.Model): _("You can't upsell or downsell a terminated contract") ) + def _get_line_to_create_contract(self): + """ + Override this method to define more filter criteria of line for which we create contract + :return: line to create contract + """ + self.ensure_one() + line_to_create_contract = self.order_line.filtered( + lambda r: not r.contract_id and r.is_contract + ) + return line_to_create_contract + + def _get_line_to_update_contract(self): + """ + Override this method to define more filter criteria of line for which we update contract + :return: line to update contract + """ + self.ensure_one() + line_to_update_contract = self.order_line.filtered( + lambda r: r.contract_id + and r.is_contract + and r not in r.contract_id.contract_line_ids.mapped("sale_order_line_id") + ) + return line_to_update_contract + @api.depends("order_line.contract_id", "state") def _compute_need_contract_creation(self): self.update({"need_contract_creation": False}) for rec in self: if rec.state in ("sale", "done"): - line_to_create_contract = rec.order_line.filtered( - lambda r: not r.contract_id and r.product_id.is_contract - ) - line_to_update_contract = rec.order_line.filtered( - lambda r: r.contract_id - and r.product_id.is_contract - and r - not in r.contract_id.contract_line_ids.mapped("sale_order_line_id") - ) + line_to_create_contract = rec._get_line_to_create_contract() + line_to_update_contract = rec._get_line_to_update_contract() if line_to_create_contract or line_to_update_contract: rec.need_contract_creation = True @@ -66,15 +83,8 @@ class SaleOrder(models.Model): contract_model = self.env["contract.contract"] contracts = [] for rec in self.filtered("is_contract"): - line_to_create_contract = rec.order_line.filtered( - lambda r: not r.contract_id and r.product_id.is_contract - ) - line_to_update_contract = rec.order_line.filtered( - lambda r: r.contract_id - and r.product_id.is_contract - and r - not in r.contract_id.contract_line_ids.mapped("sale_order_line_id") - ) + line_to_create_contract = rec._get_line_to_create_contract() + line_to_update_contract = rec._get_line_to_update_contract() contract_templates = self.env["contract.template"] for order_line in line_to_create_contract: contract_template = order_line.product_id.with_company( diff --git a/product_contract/models/sale_order_line.py b/product_contract/models/sale_order_line.py index 038dd6d6f..ed9e91347 100644 --- a/product_contract/models/sale_order_line.py +++ b/product_contract/models/sale_order_line.py @@ -12,7 +12,7 @@ class SaleOrderLine(models.Model): _inherit = "sale.order.line" is_contract = fields.Boolean( - string="Is a contract", related="product_id.is_contract" + string="Is a contract", ) contract_id = fields.Many2one( comodel_name="contract.contract", string="Contract", copy=False @@ -120,10 +120,10 @@ class SaleOrderLine(models.Model): ) return date_end - @api.depends("product_id") + @api.depends("product_id", "is_contract") def _compute_auto_renew(self): for rec in self: - if rec.product_id.is_contract: + if rec.is_contract: rec.product_uom_qty = rec.product_id.default_qty rec.recurring_rule_type = rec.product_id.recurring_rule_type rec.recurring_invoicing_type = rec.product_id.recurring_invoicing_type @@ -135,11 +135,21 @@ class SaleOrderLine(models.Model): rec.auto_renew_interval = rec.product_id.auto_renew_interval rec.auto_renew_rule_type = rec.product_id.auto_renew_rule_type - @api.onchange("date_start", "product_uom_qty", "recurring_rule_type") + @api.onchange("date_start", "product_uom_qty", "recurring_rule_type", "is_contract") def onchange_date_start(self): - for rec in self.filtered("product_id.is_contract"): + for rec in self.filtered("is_contract"): rec.date_end = rec._get_date_end() if rec.date_start else False + @api.onchange("product_id") + def product_id_change(self): + super().product_id_change() + for rec in self: + if rec.product_id.is_contract: + rec.is_contract = True + else: + # Don't initialize wrong values + rec.is_contract = False + def _get_contract_line_qty(self): """Returns the quantity to be put on new contract lines.""" self.ensure_one() @@ -259,6 +269,16 @@ class SaleOrderLine(models.Model): _("Contract product has different contract template") ) + @api.constrains("product_id", "contract_id") + def _check_contract_sale_line_is_contract(self): + for rec in self: + if rec.is_contract and not rec.product_id.is_contract: + raise ValidationError( + _( + 'You can check the field "Is a contract" only for Contract product' + ) + ) + def _compute_invoice_status(self): res = super()._compute_invoice_status() self.filtered("contract_id").update({"invoice_status": "no"}) @@ -274,12 +294,12 @@ class SaleOrderLine(models.Model): "qty_delivered", "product_uom_qty", "order_id.state", - "product_id.is_contract", + "is_contract", ) def _get_to_invoice_qty(self): """ sale line linked to contracts must not be invoiced from sale order """ res = super()._get_to_invoice_qty() - self.filtered("product_id.is_contract").update({"qty_to_invoice": 0.0}) + self.filtered("is_contract").update({"qty_to_invoice": 0.0}) return res diff --git a/product_contract/tests/test_sale_order.py b/product_contract/tests/test_sale_order.py index 804863f99..f7c31380c 100644 --- a/product_contract/tests/test_sale_order.py +++ b/product_contract/tests/test_sale_order.py @@ -65,8 +65,15 @@ class TestSaleOrder(SavepointCase): cls.order_line1 = cls.sale.order_line.filtered( lambda l: l.product_id == cls.product1 ) + cls.order_line1.date_start = "2018-01-01" cls.order_line1.product_uom_qty = 12 + cls.order_line1.product_id_change() + cls.order_line2 = cls.sale.order_line.filtered( + lambda l: l.product_id == cls.product2 + ) + cls.order_line2.product_id_change() + pricelist = cls.sale.partner_id.property_product_pricelist.id cls.contract = cls.env["contract.contract"].create( { @@ -194,7 +201,7 @@ class TestSaleOrder(SavepointCase): { "name": "Contract", "contract_template_id": self.contract_template2.id, - "partner_id": self.sale.partner_id.id, + "partner_id": self.env.user.partner_id.id, "line_recurrence": True, } ) @@ -218,6 +225,7 @@ class TestSaleOrder(SavepointCase): def test_no_contract_proudct(self): """it should create contract for only product contract""" self.product1.is_contract = False + self.order_line1.product_id_change() self.sale.action_confirm() self.assertFalse(self.order_line1.contract_id) @@ -241,6 +249,7 @@ class TestSaleOrder(SavepointCase): def test_sale_order_create_invoice(self): """Should not invoice contract product on sale order create invoice""" self.product2.is_contract = False + self.order_line2.product_id_change() self.product2.invoice_policy = "order" self.order_line1._compute_auto_renew() self.sale.action_confirm() diff --git a/product_contract/views/sale_order.xml b/product_contract/views/sale_order.xml index a0ea683d0..2b08163fd 100644 --- a/product_contract/views/sale_order.xml +++ b/product_contract/views/sale_order.xml @@ -38,6 +38,7 @@ position="after" > +