diff --git a/stock_cycle_count/README.rst b/stock_cycle_count/README.rst index 963b987e2..03287eaaf 100644 --- a/stock_cycle_count/README.rst +++ b/stock_cycle_count/README.rst @@ -18,6 +18,7 @@ can propose Zero-Confirmations which are simple and opportunistic counts to check whether a locations has actually became empty or not. With this strategy it is possible to: + * Remove the need to perform full physical inventories and to stop the production in the warehouse. * Measure the accuracy of the inventory records and improve it. @@ -36,14 +37,12 @@ Configuration You can configure the rules to compute the cycle count, acting as follow: -#. Go to "Inventory > Configuration > Cycle Count Rules" +#. Go to *Inventory > Configuration > Cycle Count Rules*. #. Create as much cycle count rules as you want. -#. Assign the rules to the Warehouse where you want to apply the rules in. -#. Set a "Cycle Count Planning Horizon" for each warehouse. - -.. figure:: path/to/local/image.png - :alt: alternative description - :width: 600 px +#. Assign the rules to the Warehouse or zones where you want to apply the rules + in. +#. Go to *Inventory > Configuration > Warehouse Management > Warehouses* and + set a *Cycle Count Planning Horizon* for each warehouse. Usage ===== @@ -53,11 +52,12 @@ is described below. #. Go to "Inventory > Configuration > Warehouse Management > Warehouses". #. Select all the warehouses you want to compute the rules in. -#. Click on "Action" and then in "Compute Cycle Count Rules". +#. Click on "Action" and then in "Compute Cycle Count Rules". (**note**: A + cron job will do this for every warehouse daily.) #. Go to "Inventory Control > Cycle Counts". -#. Select a Cycle Count planned an confirm it, this will create a draft +#. Select a planned Cycle Count and confirm it, this will create a draft Inventory Adjustment. -#. In the right top corner of the form view you can access the generated +#. In the right top corner of the form view you can access to the generated Inventory Adjustment. #. Proceed with the Inventory Adjustment as usual. @@ -65,9 +65,6 @@ is described below. :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/153/9.0 -.. repo_id is available in https://github.com/OCA/stock-logistics-warehouse -.. branch is "9.0" for example - Bug Tracker =========== diff --git a/stock_cycle_count/__openerp__.py b/stock_cycle_count/__openerp__.py index e903912fb..c3704e08a 100644 --- a/stock_cycle_count/__openerp__.py +++ b/stock_cycle_count/__openerp__.py @@ -6,7 +6,7 @@ "name": "Stock Cycle Count", "summary": "Adds the capability to schedule cycle counts in a " "warehouse through different rules defined by the user", - "version": "9.0.1.0.0", + "version": "9.0.1.1.0", "author": "Eficent, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/stock-logistics-warehouse", diff --git a/stock_cycle_count/models/stock_cycle_count.py b/stock_cycle_count/models/stock_cycle_count.py index a5cac5aa3..da65d8115 100644 --- a/stock_cycle_count/models/stock_cycle_count.py +++ b/stock_cycle_count/models/stock_cycle_count.py @@ -8,6 +8,7 @@ from openerp import api, fields, models class StockCycleCount(models.Model): _name = 'stock.cycle.count' + _description = "Stock Cycle Counts" _inherit = 'mail.thread' @api.one diff --git a/stock_cycle_count/models/stock_cycle_count_rule.py b/stock_cycle_count/models/stock_cycle_count_rule.py index a67c50161..00b988ead 100644 --- a/stock_cycle_count/models/stock_cycle_count_rule.py +++ b/stock_cycle_count/models/stock_cycle_count_rule.py @@ -11,6 +11,7 @@ from datetime import timedelta, datetime class StockCycleCountRule(models.Model): _name = 'stock.cycle.count.rule' + _description = "Stock Cycle Counts Rules" @api.one def _compute_currency(self): @@ -77,6 +78,15 @@ class StockCycleCountRule(models.Model): _('You cannot define a negative period.') ) + @api.onchange('location_ids') + def _get_warehouses(self): + """Get the warehouses for the selected locations.""" + wh_ids = [] + for loc in self.location_ids: + wh_ids.append(loc.get_warehouse(loc)) + wh_ids = list(set(wh_ids)) + self.warehouse_ids = self.env['stock.warehouse'].browse(wh_ids) + name = fields.Char('Name', required=True) rule_type = fields.Selection(selection="_selection_rule_types", string='Type of rule', @@ -94,11 +104,19 @@ class StockCycleCountRule(models.Model): compute=_compute_currency) accuracy_threshold = fields.Float(string='Minimum Accuracy Threshold', digits=(3, 2)) - warehouse_ids = fields.Many2many(comodel_name='stock.warehouse', - relation='warehouse_cycle_count_rule_rel', - column1='rule_id', - column2='warehouse_id', - string='Applied in') + apply_in = fields.Selection( + string='Apply this rule in:', + selection=[('warehouse', 'Selected warehouses'), + ('location', 'Selected Location Zones.')], + default='warehouse') + warehouse_ids = fields.Many2many( + comodel_name='stock.warehouse', + relation='warehouse_cycle_count_rule_rel', column1='rule_id', + column2='warehouse_id', string='Warehouses where applied') + location_ids = fields.Many2many( + comodel_name='stock.location', + relation='location_cycle_count_rule_rel', column1='rule_id', + column2='location_id', string='Zones where applied') def compute_rule(self, locs): if self.rule_type == 'periodic': diff --git a/stock_cycle_count/models/stock_warehouse.py b/stock_cycle_count/models/stock_warehouse.py index 01afabdea..4ecd20974 100644 --- a/stock_cycle_count/models/stock_warehouse.py +++ b/stock_cycle_count/models/stock_warehouse.py @@ -36,24 +36,30 @@ class StockWarehouse(models.Model): return date_horizon @api.model - def _get_cycle_count_locations_search_domain(self): - wh_parent_left = self.view_location_id.parent_left - wh_parent_right = self.view_location_id.parent_right - domain = [('parent_left', '>', wh_parent_left), - ('parent_right', '<', wh_parent_right), + def _get_cycle_count_locations_search_domain( + self, parent): + domain = [('parent_left', '>=', parent.parent_left), + ('parent_right', '<=', parent.parent_right), ('cycle_count_disabled', '=', False)] return domain @api.model - def _search_cycle_count_locations(self): - locations = self.env['stock.location'].search( - self._get_cycle_count_locations_search_domain()) + def _search_cycle_count_locations(self, rule): + locations = self.env['stock.location'] + if rule.apply_in == 'warehouse': + locations = self.env['stock.location'].search( + self._get_cycle_count_locations_search_domain( + self.view_location_id)) + elif rule.apply_in == 'location': + for loc in rule.location_ids: + locations += self.env['stock.location'].search( + self._get_cycle_count_locations_search_domain(loc)) return locations @api.model def _cycle_count_rules_to_compute(self): rules = self.cycle_count_rule_ids.search([ - ('rule_type', '!=', 'zero')]) + ('rule_type', '!=', 'zero'), ('warehouse_ids', '=', self.id)]) return rules @api.one @@ -62,10 +68,10 @@ class StockWarehouse(models.Model): returns a list with required dates for the cycle count of each location ''' proposed_cycle_counts = [] - locations = self._search_cycle_count_locations() rules = self._cycle_count_rules_to_compute() - if locations: - for rule in rules: + for rule in rules: + locations = self._search_cycle_count_locations(rule) + if locations: proposed_cycle_counts.extend(rule.compute_rule(locations)) if proposed_cycle_counts: locations = list(set([d['location'] for d in diff --git a/stock_cycle_count/tests/test_stock_cycle_count.py b/stock_cycle_count/tests/test_stock_cycle_count.py index 3f15c7fbe..461c1846e 100644 --- a/stock_cycle_count/tests/test_stock_cycle_count.py +++ b/stock_cycle_count/tests/test_stock_cycle_count.py @@ -35,6 +35,14 @@ class TestStockCycleCount(common.TransactionCase): self.user = self._create_user( 'user_2', [self.g_stock_user], self.company).id + # Create warehouses: + self.big_wh = self.stock_warehouse_model.create({ + 'name': 'BIG', + 'code': 'B', + 'cycle_count_planning_horizon': 30}) + self.small_wh = self.stock_warehouse_model.create({ + 'name': 'SMALL', 'code': 'S'}) + # Create rules: self.rule_periodic = \ self._create_stock_cycle_count_rule_periodic( @@ -44,30 +52,26 @@ class TestStockCycleCount(common.TransactionCase): self.manager, 'rule_2', [100]) self.rule_accuracy = \ self._create_stock_cycle_count_rule_accuracy( - self.manager, 'rule_3', [5]) + self.manager, 'rule_3', [5], self.big_wh.view_location_id.ids) self.zero_rule = self._create_stock_cycle_count_rule_zero( self.manager, 'rule_4') - # Create and configure warehouses: + # Configure warehouses: self.rule_ids = [ self.rule_periodic.id, self.rule_turnover.id, self.rule_accuracy.id, self.zero_rule.id] - self.big_wh = self.stock_warehouse_model.create({ - 'name': 'BIG', - 'code': 'B', - 'cycle_count_planning_horizon': 30, + self.big_wh.write({ 'cycle_count_rule_ids': [(6, 0, self.rule_ids)] }) - self.small_wh = self.stock_warehouse_model.create({ - 'name': 'SMALL', 'code': 'S'}) # Create a location: self.count_loc = self.stock_location_model.create({ 'name': 'Place', 'usage': 'production' }) + self.stock_location_model._parent_store_compute() # Create a cycle count: self.cycle_count_1 = self.cycle_count_model.sudo(self.manager).create({ @@ -113,11 +117,14 @@ class TestStockCycleCount(common.TransactionCase): }) return rule - def _create_stock_cycle_count_rule_accuracy(self, uid, name, values): + def _create_stock_cycle_count_rule_accuracy( + self, uid, name, values, zone_ids): rule = self.stock_cycle_count_rule_model.sudo(uid).create({ 'name': name, 'rule_type': 'accuracy', 'accuracy_threshold': values[0], + 'apply_in': 'location', + 'location_ids': [(6, 0, zone_ids)], }) return rule @@ -132,8 +139,10 @@ class TestStockCycleCount(common.TransactionCase): """Tests creation of cycle counts.""" # Common rules: wh = self.big_wh - self.stock_location_model._parent_store_compute() - locs = wh._search_cycle_count_locations() + locs = self.stock_location_model + for rule in self.big_wh.cycle_count_rule_ids: + locs += wh._search_cycle_count_locations(rule) + locs = locs.exists() # remove duplicated locations. counts = self.cycle_count_model.search([ ('location_id', 'in', locs.ids)]) self.assertFalse( @@ -225,6 +234,10 @@ class TestStockCycleCount(common.TransactionCase): for r in rules: r._get_rule_description() self.assertTrue(r.rule_description, 'No description provided') + self.rule_accuracy._get_warehouses() + self.assertEqual(self.rule_accuracy.warehouse_ids.ids, self.big_wh.ids, + 'Rules defined for zones are not getting the right ' + 'warehouse.') def test_user_security(self): """Tests user rights.""" diff --git a/stock_cycle_count/views/stock_cycle_count_rule_view.xml b/stock_cycle_count/views/stock_cycle_count_rule_view.xml index 94e9389de..1b4fb66af 100644 --- a/stock_cycle_count/views/stock_cycle_count_rule_view.xml +++ b/stock_cycle_count/views/stock_cycle_count_rule_view.xml @@ -11,6 +11,7 @@ + @@ -47,12 +48,19 @@ + +

You can apply the cycle count rules in complete + warehouses or specific zones. A zone it is + understood as a location and all its children.

+

In either case you can exclude specific locations + going to the locations form and checking the box + "Exclude from Cycle Count".

+ + + +
- - - - - diff --git a/stock_cycle_count/views/stock_warehouse_view.xml b/stock_cycle_count/views/stock_warehouse_view.xml index f0c4cedeb..26c7a6167 100644 --- a/stock_cycle_count/views/stock_warehouse_view.xml +++ b/stock_cycle_count/views/stock_warehouse_view.xml @@ -10,15 +10,14 @@ - - - - - - - Cycle - count rules - + + + +

+

Cycle Count Rules + applied in this Warehouse:

+ + Cycle count rules