diff --git a/scrap_reason_code/models/reason_code.py b/scrap_reason_code/models/reason_code.py index a412f79f5..af4d517e1 100644 --- a/scrap_reason_code/models/reason_code.py +++ b/scrap_reason_code/models/reason_code.py @@ -1,5 +1,6 @@ # Copyright (C) 2019 IBM Corp. # Copyright (C) 2019 Open Source Integrators +# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models @@ -16,3 +17,10 @@ class ScrapReasonCode(models.Model): string="Scrap Location", domain="[('scrap_location', '=', True)]", ) + product_category_ids = fields.Many2many( + string="Allowed Product Categories", + comodel_name="product.category", + help="Indicate the cateogories of products that can use this reason code " + "when doing a scrap. If left empy, this reason code can be used " + "with any product.", + ) diff --git a/scrap_reason_code/models/stock_scrap.py b/scrap_reason_code/models/stock_scrap.py index 6f00f178c..7d8dc4ce3 100644 --- a/scrap_reason_code/models/stock_scrap.py +++ b/scrap_reason_code/models/stock_scrap.py @@ -1,18 +1,53 @@ # Copyright (C) 2019 IBM Corp. # Copyright (C) 2019 Open Source Integrators +# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class StockScrap(models.Model): _inherit = "stock.scrap" reason_code_id = fields.Many2one( - "scrap.reason.code", states={"done": [("readonly", True)]} + comodel_name="scrap.reason.code", + states={"done": [("readonly", True)]}, + domain="[('id', 'in', allowed_reason_code_ids)]", + ) + allowed_reason_code_ids = fields.Many2many( + comodel_name="scrap.reason.code", + compute="_compute_allowed_reason_code_ids", ) scrap_location_id = fields.Many2one(readonly=True) + @api.depends("product_id", "product_id.categ_id") + def _compute_allowed_reason_code_ids(self): + for rec in self: + codes = self.env["scrap.reason.code"] + if rec.product_id: + codes = codes.search( + [ + "|", + ("product_category_ids", "=", False), + ("product_category_ids", "in", rec.product_id.categ_id.id), + ] + ) + rec.allowed_reason_code_ids = codes + + @api.constrains("reason_code_id", "product_id") + def _check_reason_code_id(self): + for rec in self: + if ( + rec.reason_code_id + and rec.reason_code_id not in rec.allowed_reason_code_ids + ): + raise ValidationError( + _( + "The selected reason code is not allowed for this product category." + ) + ) + def _prepare_move_values(self): res = super(StockScrap, self)._prepare_move_values() res["reason_code_id"] = self.reason_code_id.id diff --git a/scrap_reason_code/readme/CONTRIBUTORS.rst b/scrap_reason_code/readme/CONTRIBUTORS.rst index ad224128e..a4aeea6ee 100644 --- a/scrap_reason_code/readme/CONTRIBUTORS.rst +++ b/scrap_reason_code/readme/CONTRIBUTORS.rst @@ -4,3 +4,4 @@ * Serpent Consulting Services Pvt. Ltd. * Chandresh Thakkar * Hughes Damry +* Lois Rilo diff --git a/scrap_reason_code/tests/test_scrap_reason_code.py b/scrap_reason_code/tests/test_scrap_reason_code.py index 27851bf8a..96ea12b4e 100644 --- a/scrap_reason_code/tests/test_scrap_reason_code.py +++ b/scrap_reason_code/tests/test_scrap_reason_code.py @@ -1,7 +1,9 @@ # Copyright (C) 2019 IBM Corp. # Copyright (C) 2019 Open Source Integrators +# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.exceptions import ValidationError from odoo.tests.common import TransactionCase @@ -11,6 +13,8 @@ class StockScrap(TransactionCase): self.stock_location = self.env.ref("stock.stock_location_stock") self.customer_location = self.env.ref("stock.stock_location_customers") + self.categ_1 = self.env.ref("product.product_category_all") + self.categ_2 = self.env["product.category"].create({"name": "Test category"}) stock_location_locations_virtual = self.env["stock.location"].create( {"name": "Virtual Locations", "usage": "view", "posz": 1} ) @@ -27,7 +31,14 @@ class StockScrap(TransactionCase): { "name": "Scrap Product A", "type": "product", - "categ_id": self.env.ref("product.product_category_all").id, + "categ_id": self.categ_1.id, + } + ) + self.scrap_product_2 = self.env["product.product"].create( + { + "name": "Scrap Product A", + "type": "product", + "categ_id": self.categ_2.id, } ) @@ -38,6 +49,13 @@ class StockScrap(TransactionCase): "location_id": self.scrapped_location.id, } ) + self.reason_code_only_categ_2 = self.env["scrap.reason.code"].create( + { + "name": "Test Code 2", + "description": "Test description", + "product_category_ids": [(6, 0, self.categ_2.ids)], + } + ) self.uom_unit = self.env.ref("uom.product_uom_unit") @@ -164,3 +182,40 @@ class StockScrap(TransactionCase): scrapped_move.quantity_done = 8 self.assertEqual(scrap2.scrap_qty, 8, "Scrap quantity is not updated.") + + def test_allowed_reason_codes(self): + with self.assertRaises(ValidationError): + self.env["stock.scrap"].create( + { + "product_id": self.scrap_product.id, + "product_uom_id": self.scrap_product_2.uom_id.id, + "scrap_qty": 5, + "reason_code_id": self.reason_code_only_categ_2.id, + } + ) + scrap = self.env["stock.scrap"].create( + { + "product_id": self.scrap_product.id, + "product_uom_id": self.scrap_product.uom_id.id, + "scrap_qty": 5, + "reason_code_id": self.reason_code.id, + } + ) + self.assertEqual(scrap.allowed_reason_code_ids, self.reason_code) + with self.assertRaises(ValidationError): + scrap.write({"reason_code_id": self.reason_code_only_categ_2.id}) + scrap = self.env["stock.scrap"].create( + { + "product_id": self.scrap_product_2.id, + "product_uom_id": self.scrap_product_2.uom_id.id, + "scrap_qty": 5, + "reason_code_id": self.reason_code_only_categ_2.id, + } + ) + with self.assertRaises(ValidationError): + scrap.write({"product_id": self.scrap_product.id}) + self.assertEqual( + scrap.allowed_reason_code_ids, + (self.reason_code + self.reason_code_only_categ_2), + ) + scrap.write({"reason_code_id": self.reason_code.id}) diff --git a/scrap_reason_code/views/reason_code_view.xml b/scrap_reason_code/views/reason_code_view.xml index 478614e91..acd011acd 100644 --- a/scrap_reason_code/views/reason_code_view.xml +++ b/scrap_reason_code/views/reason_code_view.xml @@ -1,4 +1,5 @@ @@ -7,15 +8,22 @@ scrap.reason.code
- - - - - - - - - + +
+

+
+ + + + + + + +
@@ -27,6 +35,7 @@ + diff --git a/scrap_reason_code/views/stock_scrap_views.xml b/scrap_reason_code/views/stock_scrap_views.xml index 4b60ee1f3..6ed37bdaa 100644 --- a/scrap_reason_code/views/stock_scrap_views.xml +++ b/scrap_reason_code/views/stock_scrap_views.xml @@ -1,4 +1,5 @@ @@ -8,6 +9,7 @@ + @@ -18,6 +20,7 @@ + @@ -28,6 +31,7 @@ +