diff --git a/stock_inventory_verification_request/README.rst b/stock_inventory_verification_request/README.rst index 4e804e087..6daaa11f3 100644 --- a/stock_inventory_verification_request/README.rst +++ b/stock_inventory_verification_request/README.rst @@ -12,9 +12,17 @@ discrepancies over the threshold for the location, a Slot Verification Request will be created for each line that exceed the maximum discrepancy allowed. -The Inventory Manager can then confirm the Slot verification and start to -check the involved Inventory Lines and Moves to find out some possible mistake -or problem. +A SVR must be created when warehouse operation (e.g. an inventory adjustment, +a cycle count...) uncovers a count discrepancy within a slot (a small stock +location), and the discrepancy is greater than the pre-defined acceptable +variance threshold. A stock manager should accept the SVR and assign it to +someone to perform it. + +The aim of SVR is to find and fix errors before they are transferred to +another location, so they will not be found again in similar stock operations. +In other words, a SVR helps to correct an already existing error in our stock +records the earliest possible. Many times, a SVR will likely lead to manual +actions (before being marked as solved) in order to fix the problems uncovered. Usage ===== diff --git a/stock_inventory_verification_request/models/stock_inventory.py b/stock_inventory_verification_request/models/stock_inventory.py index cf7f76e42..f8fe9e252 100644 --- a/stock_inventory_verification_request/models/stock_inventory.py +++ b/stock_inventory_verification_request/models/stock_inventory.py @@ -32,8 +32,8 @@ class StockInventory(models.Model): class StockInventoryLine(models.Model): _inherit = 'stock.inventory.line' + _rec_name = 'product_id' - # TODO: make this work slot_verification_ids = fields.One2many( comodel_name='stock.slot.verification.request', inverse_name='inventory_line_id', diff --git a/stock_inventory_verification_request/models/stock_slot_verification_request.py b/stock_inventory_verification_request/models/stock_slot_verification_request.py index 2e3b0e7bb..a200494d7 100644 --- a/stock_inventory_verification_request/models/stock_slot_verification_request.py +++ b/stock_inventory_verification_request/models/stock_slot_verification_request.py @@ -3,7 +3,7 @@ # (http://www.eficent.com) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from openerp import fields, models, api +from openerp import api, fields, models class SlotVerificationRequest(models.Model): diff --git a/stock_inventory_verification_request/security/ir.model.access.csv b/stock_inventory_verification_request/security/ir.model.access.csv index ac8d4d7b7..76e209043 100644 --- a/stock_inventory_verification_request/security/ir.model.access.csv +++ b/stock_inventory_verification_request/security/ir.model.access.csv @@ -1,3 +1,3 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_slot_verification_request_user,stock.slot.verification.request user,model_stock_slot_verification_request,stock.group_stock_user,1,0,0,0 +access_slot_verification_request_user,stock.slot.verification.request user,model_stock_slot_verification_request,stock.group_stock_user,1,0,1,0 access_slot_verification_request_manager,stock.slot.verification.request manager,model_stock_slot_verification_request,stock.group_stock_manager,1,1,1,1 diff --git a/stock_inventory_verification_request/tests/__init__.py b/stock_inventory_verification_request/tests/__init__.py new file mode 100644 index 000000000..4ff41560f --- /dev/null +++ b/stock_inventory_verification_request/tests/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_verification_request diff --git a/stock_inventory_verification_request/tests/test_verification_request.py b/stock_inventory_verification_request/tests/test_verification_request.py new file mode 100644 index 000000000..67ee8ed8b --- /dev/null +++ b/stock_inventory_verification_request/tests/test_verification_request.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import openerp.tests.common as common +from openerp.exceptions import AccessError + + +class TestStockVerificationRequest(common.TransactionCase): + + def setUp(self): + super(TestStockVerificationRequest, self).setUp() + self.obj_wh = self.env['stock.warehouse'] + self.obj_location = self.env['stock.location'] + self.obj_inventory = self.env['stock.inventory'] + self.obj_product = self.env['product.product'] + self.obj_svr = self.env['stock.slot.verification.request'] + self.obj_move = self.env['stock.move'] + + self.product1 = self.obj_product.create({ + 'name': 'Test Product 1', + 'type': 'product', + 'default_code': 'PROD1', + }) + self.product2 = self.obj_product.create({ + 'name': 'Test Product 2', + 'type': 'product', + 'default_code': 'PROD2', + }) + self.test_loc = self.obj_location.create({ + 'name': 'Test Location', + 'usage': 'internal', + 'discrepancy_threshold': 0.1 + }) + + # 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])] + }) + 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.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( + "product.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( + "product.product_uom_unit").id, + 'product_qty': 4.0, + 'location_id': self.test_loc.id, + }), + ], + }) + self.starting_inv.action_force_done() + + def test_svr_creation(self): + """Tests the creation of Slot Verification Requests.""" + inventory = self.obj_inventory.create({ + 'name': 'Generate over discrepancy in both lines.', + 'location_id': self.test_loc.id, + 'filter': 'none', + 'line_ids': [ + (0, 0, { + 'product_id': self.product1.id, + 'product_uom_id': self.env.ref( + "product.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( + "product.product_uom_unit").id, + 'product_qty': 3.0, + 'location_id': self.test_loc.id, + }) + ], + }) + inventory.with_context({'normal_view': True}).action_done() + self.assertEqual(inventory.state, 'pending', + 'Inventory Adjustment not changing to Pending to ' + 'Approve.') + previous_count = len(self.obj_svr.search([])) + inventory.sudo(self.user).action_request_verification() + current_count = len(self.obj_svr.search([])) + self.assertEqual(current_count, previous_count + 2, + 'Slot Verification Request not created.') + # Test the method to open SVR from inventory lines: + inventory.line_ids[0].action_open_svr() + + def test_svr_workflow(self): + """Tests workflow of Slot Verification Request.""" + test_svr = self.env['stock.slot.verification.request'].create({ + 'location_id': self.test_loc.id, + 'state': 'wait', + 'product_id': self.product1.id, + }) + self.assertEqual(test_svr.state, 'wait', + 'Slot Verification Request not created from scratch.') + with self.assertRaises(AccessError): + test_svr.sudo(self.user).action_confirm() + test_svr.sudo(self.manager).action_confirm() + self.assertEqual(test_svr.state, 'open', + 'Slot Verification Request not confirmed properly.') + test_svr.sudo(self.manager).action_solved() + self.assertEqual(test_svr.state, 'done', + 'Slot Verification Request not marked as solved.') + test_svr.sudo(self.manager).action_cancel() + self.assertEqual(test_svr.state, 'cancelled', + 'Slot Verification Request not marked as cancelled.') + + def test_view_methods(self): + """Tests the methods used to handle de UI.""" + test_svr = self.env['stock.slot.verification.request'].create({ + 'location_id': self.test_loc.id, + 'state': 'wait', + 'product_id': self.product1.id, + }) + test_svr.sudo(self.manager).action_confirm() + self.assertEqual(test_svr.involved_move_count, 1, + 'Unexpected involved move') + self.assertEqual(test_svr.involved_inv_line_count, 1, + 'Unexpected involved inventory line') + test_svr.action_view_inv_lines() + test_svr.action_view_moves() diff --git a/stock_inventory_verification_request/views/stock_inventory_view.xml b/stock_inventory_verification_request/views/stock_inventory_view.xml index 7d3895a6f..f8ccffdcc 100644 --- a/stock_inventory_verification_request/views/stock_inventory_view.xml +++ b/stock_inventory_verification_request/views/stock_inventory_view.xml @@ -15,7 +15,7 @@ string="Request Verification" type="object" class="oe_highlight" attrs="{'invisible': ['|',('state', '!=', 'pending'),('requested_verification', '=', True)]}" - groups="stock.group_stock_manager"/> + groups="stock.group_stock_user"/>