Add area location relation

So we can use it in group-by and filters. Note that we can have several
levels of areas... only the top-level will be used.

This commit fixes the computed field dependencies:
_compute_location_kind should have a dependency on both it's parent's
location_kind and on its child_ids to know if we are in a bin. This
can't work without triggering an infinite loop. The trick used here is
to split the computation of 'zone_location_id + area_location_id' in one
computed method, and move the computation of the kind in a different
method with triggers an the current record's zone_location +
area_location_id, but not on the parent.

Plus the zone_location_id and area_location_id do not depend anymore on
the parent's kind, which is the reason for the infinite loop.
This commit is contained in:
Guewen Baconnier
2019-09-20 15:20:42 +02:00
committed by Denis Roussel
parent 817c0032f2
commit 463c3c7fff
3 changed files with 59 additions and 40 deletions

View File

@@ -16,11 +16,17 @@ class StockLocation(models.Model):
zone_location_id = fields.Many2one(
'stock.location',
string='Location zone',
compute='_compute_location_zone',
string='Location Zone',
compute='_compute_zone_location_id',
store=True,
index=True,
)
area_location_id = fields.Many2one(
'stock.location',
string='Location Area',
compute='_compute_zone_location_id',
store=True,
)
location_kind = fields.Selection(
[
@@ -31,61 +37,71 @@ class StockLocation(models.Model):
('other', 'Other'),
],
string='Location Kind',
compute='_compute_location_zone',
help='Group location according to their kinds:'
'* Zone: locations that are flagged as being zones'
'* Area: locations with children that are part of a zone'
'* Bin: locations without children that are part of a zone'
'* Stock: internal locations whose parent is a view'
compute='_compute_location_kind',
store=True,
help='Group location according to their kinds: '
'* Zone: locations that are flagged as being zones '
'* Area: locations with children that are part of a zone '
'* Bin: locations without children that are part of a zone '
'* Stock: internal locations whose parent is a view '
'* Other: any other location',
)
@api.depends('is_zone', 'usage', 'location_id.usage', 'child_ids',
'location_id.is_zone')
def _compute_location_zone(self):
@api.depends('is_zone', 'location_id.zone_location_id',
'location_id.area_location_id')
def _compute_zone_location_id(self):
for location in self:
location.zone_location_id = self.browse()
location.area_location_id = self.browse()
if location.is_zone:
location.location_kind = 'zone'
location.zone_location_id = location
continue
# Get the zone from the parents
parent = location.location_id
while parent:
if parent.is_zone:
zone_location = parent
break
parent = parent.location_id
else:
zone_location = self.browse()
if parent.zone_location_id:
location.zone_location_id = parent.zone_location_id
# If we have more than one level of area in a zone,
# the grouping is done by the first level
if parent.area_location_id:
location.area_location_id = parent.area_location_id
else:
location.area_location_id = location
location.zone_location_id = zone_location
# Internal locations whose parent is view are main stocks
if (
location.usage == 'internal'
and location.location_id.usage == 'view'
):
location.location_kind = 'stock'
@api.depends('usage', 'location_id.usage',
'child_ids',
'area_location_id',
'zone_location_id')
def _compute_location_kind(self):
for location in self:
if location.zone_location_id and not location.area_location_id:
location.location_kind = 'zone'
continue
# Internal locations having a zone and no children are bins
parent = location.location_id
if (
location.usage == 'internal'
and zone_location
and parent.usage == 'view'
):
# Internal locations whose parent is view are main stocks
location.location_kind = 'stock'
elif (
# Internal locations having a zone and no children are bins
location.usage == 'internal'
and location.zone_location_id
and location.area_location_id
and not location.child_ids
):
location.location_kind = 'bin'
continue
# Internal locations having a zone and children are areas
if (
elif (
location.usage == 'internal'
and zone_location
and location.zone_location_id
and location.area_location_id
and location.child_ids
):
# Internal locations having a zone and children are areas
location.location_kind = 'area'
continue
# All the rest are other locations
location.location_kind = 'other'
else:
# All the rest are other locations
location.location_kind = 'other'
@api.multi
@api.returns('self', lambda value: value.id)

View File

@@ -3,8 +3,8 @@ classification of stock locations in a warehouse.
Locations are then classified by location kinds that could be:
* Zone: locations that are flagged as being zones
* Area: locations with children that are part of a zone
* Zone: locations that are flagged as being zones. Zones are subdivisions of the warehouse for splitting picking operations. A picking operator work in a zone and can pick from any location of the zone.
* Area: locations with children that are part of a zone. Areas are subdivisions of the warehouse for put-away operations. Each area has storage characteristics and strategy, e.g. area for pallets, shelf for boxes...
* Bin: locations without children that are part of a zone
* Stock: internal locations whose parent is a view
* Other: any other location

View File

@@ -12,6 +12,7 @@
<field name="usage" position="after">
<field name="location_kind" />
<field name="zone_location_id" />
<field name="area_location_id" />
</field>
</field>
</record>
@@ -25,9 +26,11 @@
<field name="location_kind"/>
<field name="is_zone" />
<field name="zone_location_id" />
<field name="area_location_id" />
<group expand="0" string="Group By">
<filter string="Location Kind" name="location_kind" domain="[]" context="{'group_by':'location_kind'}"/>
<filter string="Zone location" name="zone_location" domain="[]" context="{'group_by':'zone_location_id'}"/>
<filter string="Area location" name="area_location" domain="[]" context="{'group_by':'area_location_id'}"/>
</group>
</xpath>
</field>