mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[9.0][IMP] stock_cycle_count: add the possibility to define rules for specific zones.
This commit is contained in:
@@ -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
|
||||
===========
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<tree string="Stock Cycle Count">
|
||||
<field name="name"/>
|
||||
<field name="warehouse_ids"/>
|
||||
<field name="location_ids"/>
|
||||
<field name="rule_type"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -47,12 +48,19 @@
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group name="applied_in" string="Applied in:">
|
||||
<p colspan="4">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.</p>
|
||||
<p colspan="4">In either case you can exclude specific locations
|
||||
going to the locations form and checking the box
|
||||
"Exclude from Cycle Count".</p>
|
||||
<field name="apply_in"/>
|
||||
<field name="warehouse_ids" widget="many2many_tags"/>
|
||||
<field name="location_ids"
|
||||
attrs="{'invisible': [('apply_in', '!=', 'location')]}"/>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
<page string="Applied in">
|
||||
<field name="warehouse_ids"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -10,15 +10,14 @@
|
||||
<field name="inherit_id" ref="stock.view_warehouse"/>
|
||||
<field name="arch" type="xml">
|
||||
<notebook position="before">
|
||||
<group string="Cycle Counting">
|
||||
<group >
|
||||
<field name="cycle_count_planning_horizon"/>
|
||||
<field name="counts_for_accuracy_qty"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="cycle_count_rule_ids" nolabel="1">Cycle
|
||||
count rules</field>
|
||||
</group>
|
||||
<group string="Cycle Counting" colspan="4">
|
||||
<field name="cycle_count_planning_horizon"/>
|
||||
<field name="counts_for_accuracy_qty"/>
|
||||
<br></br>
|
||||
<center colspan="4"><h3 colspan="4">Cycle Count Rules
|
||||
applied in this Warehouse:</h3></center>
|
||||
<field name="cycle_count_rule_ids" nolabel="1" colspan="4">
|
||||
Cycle count rules</field>
|
||||
</group>
|
||||
</notebook>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user