diff --git a/setup/stock_account_inventory_discrepancy/odoo/addons/stock_account_inventory_discrepancy b/setup/stock_account_inventory_discrepancy/odoo/addons/stock_account_inventory_discrepancy new file mode 120000 index 000000000..b805695ab --- /dev/null +++ b/setup/stock_account_inventory_discrepancy/odoo/addons/stock_account_inventory_discrepancy @@ -0,0 +1 @@ +../../../../stock_account_inventory_discrepancy \ No newline at end of file diff --git a/setup/stock_account_inventory_discrepancy/setup.py b/setup/stock_account_inventory_discrepancy/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_account_inventory_discrepancy/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_account_inventory_discrepancy/__manifest__.py b/stock_account_inventory_discrepancy/__manifest__.py index b051910e2..d27c89ca3 100644 --- a/stock_account_inventory_discrepancy/__manifest__.py +++ b/stock_account_inventory_discrepancy/__manifest__.py @@ -3,11 +3,10 @@ { "name": "Stock Account Inventory Discrepancy", "summary": "Adds the capability to show the value discrepancy of every " - "line in an inventory and to block the inventory validation " - "when the discrepancy is over a user defined threshold.", + "line in an inventory and to block the inventory validation " + "when the discrepancy is over a user defined threshold.", "version": "12.0.1.0.0", - "author": "ForgeFlow, " - "Odoo Community Association (OCA)", + "author": "ForgeFlow, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/stock-logistics-warehouse", "category": "Warehouse", "depends": ["stock_inventory_discrepancy"], diff --git a/stock_account_inventory_discrepancy/models/stock_inventory_line.py b/stock_account_inventory_discrepancy/models/stock_inventory_line.py index 1da4843e8..ea4d49563 100644 --- a/stock_account_inventory_discrepancy/models/stock_inventory_line.py +++ b/stock_account_inventory_discrepancy/models/stock_inventory_line.py @@ -1,9 +1,10 @@ # Copyright 2021 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo.addons import decimal_precision as dp from odoo import api, fields, models +from odoo.addons import decimal_precision as dp + class StockInventoryLine(models.Model): _inherit = "stock.inventory.line" @@ -13,13 +14,16 @@ class StockInventoryLine(models.Model): compute="_compute_discrepancy_amount", currency_field="company_currency_id", help="The difference between the actual qty counted and the " - "theoretical quantity on hand expressed in the cost amount.", - digits=dp.get_precision("Product Unit of Measure"), default=0) + "theoretical quantity on hand expressed in the cost amount.", + digits=dp.get_precision("Product Unit of Measure"), + default=0, + ) discrepancy_amount_threshold = fields.Monetary( string="Amount Threshold", currency_field="company_currency_id", help="Maximum Discrepancy Amount Threshold", - compute="_compute_discrepancy_amount_threshold") + compute="_compute_discrepancy_amount_threshold", + ) company_currency_id = fields.Many2one( string="Company Currency", comodel_name="res.currency", @@ -40,16 +44,17 @@ class StockInventoryLine(models.Model): for line in self: whs = line.location_id.get_warehouse() if line.location_id.discrepancy_amount_threshold > 0.0: - line.discrepancy_amount_threshold = line.location_id.\ - discrepancy_amount_threshold + line.discrepancy_amount_threshold = ( + line.location_id.discrepancy_amount_threshold + ) elif whs.discrepancy_amount_threshold > 0.0: - line.discrepancy_amount_threshold = \ - whs.discrepancy_amount_threshold + line.discrepancy_amount_threshold = whs.discrepancy_amount_threshold else: line.discrepancy_amount_threshold = False @api.multi def _has_over_discrepancy(self): res = super()._has_over_discrepancy() - return res or abs( - self.discrepancy_amount) > self.discrepancy_amount_threshold > 0 + return ( + res or abs(self.discrepancy_amount) > self.discrepancy_amount_threshold > 0 + ) diff --git a/stock_account_inventory_discrepancy/models/stock_location.py b/stock_account_inventory_discrepancy/models/stock_location.py index dcfa0ab39..cee529ea4 100644 --- a/stock_account_inventory_discrepancy/models/stock_location.py +++ b/stock_account_inventory_discrepancy/models/stock_location.py @@ -11,11 +11,9 @@ class StockLocation(models.Model): string="Maximum Discrepancy Amount Threshold", currency_field="discrepancy_amount_threshold_currency_id", help="Maximum Discrepancy Amount allowed for any product when doing " - "an Inventory Adjustment. Thresholds defined in Locations have " - "preference over Warehouse's ones.", + "an Inventory Adjustment. Thresholds defined in Locations have " + "preference over Warehouse's ones.", ) discrepancy_amount_threshold_currency_id = fields.Many2one( - comodel_name="res.currency", - related="company_id.currency_id", - readonly=True, + comodel_name="res.currency", related="company_id.currency_id", readonly=True, ) diff --git a/stock_account_inventory_discrepancy/models/stock_warehouse.py b/stock_account_inventory_discrepancy/models/stock_warehouse.py index afde72ae6..e756d8174 100644 --- a/stock_account_inventory_discrepancy/models/stock_warehouse.py +++ b/stock_account_inventory_discrepancy/models/stock_warehouse.py @@ -11,11 +11,9 @@ class StockWarehouse(models.Model): string="Maximum Discrepancy Amount Threshold", currency_field="discrepancy_amount_threshold_currency_id", help="Maximum Discrepancy Amount allowed for any product when doing " - "an Inventory Adjustment. Threshold defined in involved Location " - "has preference.", + "an Inventory Adjustment. Threshold defined in involved Location " + "has preference.", ) discrepancy_amount_threshold_currency_id = fields.Many2one( - comodel_name="res.currency", - related="company_id.currency_id", - readonly=True, + comodel_name="res.currency", related="company_id.currency_id", readonly=True, ) diff --git a/stock_account_inventory_discrepancy/tests/test_inventory_discrepancy.py b/stock_account_inventory_discrepancy/tests/test_inventory_discrepancy.py index de69e32d4..cd94c365c 100644 --- a/stock_account_inventory_discrepancy/tests/test_inventory_discrepancy.py +++ b/stock_account_inventory_discrepancy/tests/test_inventory_discrepancy.py @@ -1,8 +1,8 @@ # Copyright 2021 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo.tests.common import TransactionCase from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase class TestInventoryDiscrepancy(TransactionCase): @@ -15,114 +15,143 @@ class TestInventoryDiscrepancy(TransactionCase): self.obj_warehouse = self.env["stock.warehouse"] self.obj_upd_qty_wizard = self.env["stock.change.product.qty"] - self.product1 = self.obj_product.create({ - "name": "Test Product 1", - "type": "product", - "default_code": "PROD1", - "standard_price": 110.0, - }) - self.product2 = self.obj_product.create({ - "name": "Test Product 2", - "type": "product", - "default_code": "PROD2", - "standard_price": 150.0, - }) - self.test_loc = self.obj_location.create({ - "name": "Test Location", - "usage": "internal", - "discrepancy_amount_threshold": 100, - }) - self.test_wh = self.obj_warehouse.create({ - "name": "Test WH", - "code": "T", - "discrepancy_amount_threshold": 300, - }) + self.product1 = self.obj_product.create( + { + "name": "Test Product 1", + "type": "product", + "default_code": "PROD1", + "standard_price": 110.0, + } + ) + self.product2 = self.obj_product.create( + { + "name": "Test Product 2", + "type": "product", + "default_code": "PROD2", + "standard_price": 150.0, + } + ) + self.test_loc = self.obj_location.create( + { + "name": "Test Location", + "usage": "internal", + "discrepancy_amount_threshold": 100, + } + ) + self.test_wh = self.obj_warehouse.create( + {"name": "Test WH", "code": "T", "discrepancy_amount_threshold": 300,} + ) self.obj_location._parent_store_compute() # Create Stock manager able to force validation on inventories. group_stock_man = self.env.ref("stock.group_stock_manager") group_inventory_all = self.env.ref( - "stock_inventory_discrepancy." - "group_stock_inventory_validation_always") - self.manager = self.env["res.users"].create({ - "name": "Test Manager", - "login": "manager", - "email": "test.manager@example.com", - "groups_id": [(6, 0, [group_stock_man.id, group_inventory_all.id])] - }) + "stock_inventory_discrepancy." "group_stock_inventory_validation_always" + ) + self.manager = self.env["res.users"].create( + { + "name": "Test Manager", + "login": "manager", + "email": "test.manager@example.com", + "groups_id": [(6, 0, [group_stock_man.id, group_inventory_all.id])], + } + ) group_stock_user = self.env.ref("stock.group_stock_user") - self.user = self.env["res.users"].create({ - "name": "Test User", - "login": "user", - "email": "test.user@example.com", - "groups_id": [(6, 0, [group_stock_user.id])] - }) + self.user = self.env["res.users"].create( + { + "name": "Test User", + "login": "user", + "email": "test.user@example.com", + "groups_id": [(6, 0, [group_stock_user.id])], + } + ) - starting_inv = self.obj_inventory.create({ - "name": "Starting inventory", - "filter": "product", - "line_ids": [ - (0, 0, { - "product_id": self.product1.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 2.0, - "location_id": self.test_loc.id, - }), - (0, 0, { - "product_id": self.product2.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 4.0, - "location_id": self.test_loc.id, - }), - ], - }) + starting_inv = self.obj_inventory.create( + { + "name": "Starting inventory", + "filter": "product", + "line_ids": [ + ( + 0, + 0, + { + "product_id": self.product1.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 2.0, + "location_id": self.test_loc.id, + }, + ), + ( + 0, + 0, + { + "product_id": self.product2.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 4.0, + "location_id": self.test_loc.id, + }, + ), + ], + } + ) starting_inv.action_force_done() def test_compute_discrepancy(self): """Tests if the amount discrepancy is correctly computed. """ - inventory = self.obj_inventory.create({ - "name": "Test Discrepancy Computation", - "location_id": self.test_loc.id, - "filter": "none", - "line_ids": [ - (0, 0, { - "product_id": self.product1.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 3.0, - "location_id": self.test_loc.id, - }), - (0, 0, { - "product_id": self.product2.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 3.0, - "location_id": self.test_loc.id, - }) - ], - }) + inventory = self.obj_inventory.create( + { + "name": "Test Discrepancy Computation", + "location_id": self.test_loc.id, + "filter": "none", + "line_ids": [ + ( + 0, + 0, + { + "product_id": self.product1.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 3.0, + "location_id": self.test_loc.id, + }, + ), + ( + 0, + 0, + { + "product_id": self.product2.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 3.0, + "location_id": self.test_loc.id, + }, + ), + ], + } + ) self.assertEqual(inventory.line_ids[0].discrepancy_amount, 110.0) - self.assertEqual(inventory.line_ids[1].discrepancy_amount, - 150.0) + self.assertEqual(inventory.line_ids[1].discrepancy_amount, -150.0) def test_amount_discrepancy_validation(self): """Tests the workflow with amount threshold.""" - inventory = self.obj_inventory.create({ - "name": "Test Forcing Validation Method", - "location_id": self.test_loc.id, - "filter": "none", - "line_ids": [ - (0, 0, { - "product_id": self.product1.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 3.0, - "location_id": self.test_loc.id, - }), - ], - }) + inventory = self.obj_inventory.create( + { + "name": "Test Forcing Validation Method", + "location_id": self.test_loc.id, + "filter": "none", + "line_ids": [ + ( + 0, + 0, + { + "product_id": self.product1.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 3.0, + "location_id": self.test_loc.id, + }, + ), + ], + } + ) self.assertEqual(inventory.state, "draft") self.assertEqual(inventory.line_ids.discrepancy_amount_threshold, 100) self.assertTrue(inventory.line_ids[0].has_over_discrepancy) @@ -132,31 +161,38 @@ class TestInventoryDiscrepancy(TransactionCase): def test_warehouse_amount_threshold(self): """Tests the behaviour if the threshold is set on the WH.""" - inventory = self.obj_inventory.create({ - "name": "Test Threshold Defined in WH", - "location_id": self.test_wh.view_location_id.id, - "filter": "none", - "line_ids": [ - (0, 0, { - "product_id": self.product1.id, - "product_uom_id": self.env.ref( - "uom.product_uom_unit").id, - "product_qty": 3.0, - "location_id": self.test_wh.lot_stock_id.id, - }), - ], - }) + inventory = self.obj_inventory.create( + { + "name": "Test Threshold Defined in WH", + "location_id": self.test_wh.view_location_id.id, + "filter": "none", + "line_ids": [ + ( + 0, + 0, + { + "product_id": self.product1.id, + "product_uom_id": self.env.ref("uom.product_uom_unit").id, + "product_qty": 3.0, + "location_id": self.test_wh.lot_stock_id.id, + }, + ), + ], + } + ) self.assertEqual(inventory.line_ids.discrepancy_amount_threshold, 300) def test_update_qty_user_error_amount(self): """Test if a user error raises when a stock user tries to update the qty for a product and the correction is a discrepancy over the threshold.""" - upd_qty = self.obj_upd_qty_wizard.sudo(self.user).create({ - "product_id": self.product1.id, - "product_tmpl_id": self.product1.product_tmpl_id.id, - "new_quantity": 10.0, - "location_id": self.test_loc.id, - }) + upd_qty = self.obj_upd_qty_wizard.sudo(self.user).create( + { + "product_id": self.product1.id, + "product_tmpl_id": self.product1.product_tmpl_id.id, + "new_quantity": 10.0, + "location_id": self.test_loc.id, + } + ) with self.assertRaises(UserError): upd_qty.change_product_qty() diff --git a/stock_account_inventory_discrepancy/views/stock_inventory_view.xml b/stock_account_inventory_discrepancy/views/stock_inventory_view.xml index af428534f..46c1e092c 100644 --- a/stock_account_inventory_discrepancy/views/stock_inventory_view.xml +++ b/stock_account_inventory_discrepancy/views/stock_inventory_view.xml @@ -1,21 +1,22 @@ - + - - - stock.inventory.form - stock_account_inventory_discrepancy + stock.inventory.form - stock_account_inventory_discrepancy stock.inventory - + - - - - + + + + - diff --git a/stock_account_inventory_discrepancy/views/stock_location_view.xml b/stock_account_inventory_discrepancy/views/stock_location_view.xml index bd2b58239..120b391c4 100644 --- a/stock_account_inventory_discrepancy/views/stock_location_view.xml +++ b/stock_account_inventory_discrepancy/views/stock_location_view.xml @@ -1,22 +1,24 @@ - + - - - stock.location.form - stock_account_inventory_discrepancy + stock.location.form - stock_account_inventory_discrepancy stock.location - + - - diff --git a/stock_account_inventory_discrepancy/views/stock_warehouse_view.xml b/stock_account_inventory_discrepancy/views/stock_warehouse_view.xml index f7124fe1f..252bd6cf6 100644 --- a/stock_account_inventory_discrepancy/views/stock_warehouse_view.xml +++ b/stock_account_inventory_discrepancy/views/stock_warehouse_view.xml @@ -1,22 +1,22 @@ - + - - stock.warehouse - stock_account_inventory_discrepancy stock.warehouse - + - -