From 039713b974fb98e5db48c881a77882d8c4a68215 Mon Sep 17 00:00:00 2001 From: AaronHForgeFlow Date: Mon, 22 Sep 2025 09:59:49 +0200 Subject: [PATCH] [IMP] repair_service: sync on existing quotation, similar Odoo does for new components added --- repair_service/models/__init__.py | 1 + repair_service/models/repair_service.py | 55 +++++++++++++++++++-- repair_service/models/sale_order_line.py | 10 ++++ repair_service/tests/test_repair_service.py | 21 ++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 repair_service/models/sale_order_line.py diff --git a/repair_service/models/__init__.py b/repair_service/models/__init__.py index c5c31165..e849c694 100644 --- a/repair_service/models/__init__.py +++ b/repair_service/models/__init__.py @@ -2,3 +2,4 @@ from . import repair_service from . import repair_order +from . import sale_order_line diff --git a/repair_service/models/repair_service.py b/repair_service/models/repair_service.py index bbd16325..021eae44 100644 --- a/repair_service/models/repair_service.py +++ b/repair_service/models/repair_service.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). from odoo import api, fields, models +from odoo.tools.misc import groupby class RepairService(models.Model): @@ -36,6 +37,9 @@ class RepairService(models.Model): "Quantity", digits="Product Unit of Measure", required=True, default=1.0 ) company_id = fields.Many2one(related="repair_id.company_id") + sale_line_id = fields.Many2one( + comodel_name="sale.order.line", string="Sale Line", copy=False + ) @api.depends("product_id") def _compute_display_name(self): @@ -70,7 +74,6 @@ def _prepare_sale_order_line_vals(self, product_qty): def _create_repair_sale_order_line(self): if not self: return - so_line_vals = [] for service in self: if not service.repair_id.sale_order_id: continue @@ -80,5 +83,51 @@ def _create_repair_sale_order_line(self): else service.product_uom_qty ) vals = service._prepare_sale_order_line_vals(product_qty) - so_line_vals.append(vals) - self.env["sale.order.line"].create(so_line_vals) + sale_order_line = self.env["sale.order.line"].sudo().create(vals) + service.sale_line_id = sale_order_line.id + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get("repair_id") or "repair_line_type" not in vals: + continue + services = super().create(vals_list) + services_to_create_so_line = self.env["repair.service"] + for service in services: + if not service.repair_id: + continue + if not service.sale_line_id: + services_to_create_so_line |= service + services_to_create_so_line._create_repair_sale_order_line() + return services + + def write(self, vals): + res = super().write(vals) + repair_services = self.env["repair.service"] + services_to_create_so_line = self.env["repair.service"] + for service in self: + if not service.repair_id: + continue + # checks vals update + if not service.sale_line_id and "sale_line_id" not in vals: + services_to_create_so_line |= service + if service.sale_line_id and "product_uom_qty" in vals: + repair_services |= service + + repair_services._update_repair_sale_order_line() + services_to_create_so_line._create_repair_sale_order_line() + return res + + def _update_repair_sale_order_line(self): + if not self: + return + services_to_update = self.env["repair.service"] + for service in self: + if not service.repair_id: + continue + if service.sale_line_id: + services_to_update |= service + for sale_line, _ in groupby(services_to_update, lambda m: m.sale_line_id): + sale_line.product_uom_qty = sum( + sale_line.repair_service_ids.mapped("product_uom_qty") + ) diff --git a/repair_service/models/sale_order_line.py b/repair_service/models/sale_order_line.py new file mode 100644 index 00000000..ec8e1b46 --- /dev/null +++ b/repair_service/models/sale_order_line.py @@ -0,0 +1,10 @@ +from odoo import fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + repair_service_ids = fields.One2many( + comodel_name="repair.service", + inverse_name="sale_line_id", + ) diff --git a/repair_service/tests/test_repair_service.py b/repair_service/tests/test_repair_service.py index 0b77b475..8a5fbc1e 100644 --- a/repair_service/tests/test_repair_service.py +++ b/repair_service/tests/test_repair_service.py @@ -117,3 +117,24 @@ def test_04_copy_display_name_to_sale_order_line(self): # Check that the description was copied into the sale order line name self.assertEqual(sale_order_line.name, "Custom service description") + def test_05_add_services_on_created_sale_order(self): + """Check that new services are added on created sale order""" + self.repair_order.action_create_sale_order() + sale_order = self.repair_order.sale_order_id + self.RepairService.create( + { + "repair_id": self.repair_order.id, + "product_id": self.service_product.id, + "product_uom_qty": 3.0, + "product_uom": self.service_product.uom_id.id, + } + ) + # Check that both services exist as sale order lines for the same product + sale_order_lines = sale_order.order_line.filtered( + lambda line: line.product_id == self.service_product + ) + self.assertEqual(len(sale_order_lines), 2) + self.assertIn(2.0, sale_order_lines.mapped("product_uom_qty")) + self.assertIn(3.0, sale_order_lines.mapped("product_uom_qty")) + # Check that both lines are linked to the same sale order + self.assertTrue(all(line.order_id == sale_order for line in sale_order_lines))