Skip to content

Commit

Permalink
Merge PR #711 into 12.0
Browse files Browse the repository at this point in the history
Signed-off-by jgrandguillaume
  • Loading branch information
OCA-git-bot committed Sep 29, 2020
2 parents 2cf49b8 + 61c7f96 commit 42631f3
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 223 deletions.
5 changes: 1 addition & 4 deletions stock_location_zone/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@
'version': '12.0.1.0.0',
'author': "BCIM, Okia, Camptocamp, Odoo Community Association (OCA)",
'website': "https://github.com/OCA/stock-logistics-warehouse",
'summary': "Add coordinate attributes on stock location. "
"Define picking zone with links to picking type.",
'summary': "Classify locations with zones.",
'category': 'Stock Management',
'depends': [
'stock',
],
'data': [
'views/stock_picking_zone.xml',
'views/stock_location.xml',
'security/ir.model.access.csv',
],
'installable': True,
'development_status': 'Alpha',
Expand Down
1 change: 0 additions & 1 deletion stock_location_zone/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from . import stock_picking_zone
from . import stock_location
205 changes: 87 additions & 118 deletions stock_location_zone/models/stock_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,142 +3,111 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from psycopg2 import sql

from odoo import _, api, fields, models, SUPERUSER_ID
from odoo.tools.sql import index_exists, _schema


def create_unique_index_where(cr, indexname, tablename, expressions, where):
"""Create the given unique index unless it exists."""
if index_exists(cr, indexname):
return

args = ', '.join(expressions)
# pylint: disable=sql-injection
cr.execute(
sql.SQL(
'CREATE UNIQUE INDEX {} ON {} ({}) WHERE {}').format(
sql.Identifier(indexname),
sql.Identifier(tablename),
sql.SQL(args),
sql.SQL(where),
)
)
_schema.debug(
"Table %r: created unique index %r (%s) WHERE {}",
tablename, indexname, args, where
)
from odoo import api, fields, models, _


class StockLocation(models.Model):
_inherit = 'stock.location'

# FIXME: add in selection: shuttle, tray (module vertical lift)
kind = fields.Selection([
('zone', 'Picking Zone'),
('area', 'Area'),
('bin', 'Bin')],
string='Kind')
is_zone = fields.Boolean(
string='Is a Zone Location?',
help='Mark to define this location as a zone',
)

picking_zone_id = fields.Many2one(
'stock.picking.zone',
string='Picking zone',
zone_location_id = fields.Many2one(
'stock.location',
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,
)

picking_type_id = fields.Many2one(
related='picking_zone_id.picking_type_id',
help="Picking type for operations from this location",
oldname='barcode_picking_type_id')

area = fields.Char(
'Area',
compute='_compute_area', store=True,
oldname='zone')

@api.depends('name', 'kind', 'location_id.area')
def _compute_area(self):
for location in self:
if location.kind == 'area':
location.area = location.name
else:
location.area = location.location_id.area

corridor = fields.Char('Corridor', help="Street")
row = fields.Char('Row', help="Side in the street")
rack = fields.Char('Rack', oldname='shelf', help="House number")
level = fields.Char('Level', help="Height on the shelf")
posx = fields.Integer('Box (X)')
posy = fields.Integer('Box (Y)')
posz = fields.Integer('Box (Z)')

location_name_format = fields.Char(
'Location Name Format',
help="Format string that will compute the name of the location. "
"Use location fields. Example: "
"'{area}-{corridor:0>2}.{rack:0>3}"
".{level:0>2}'")
location_kind = fields.Selection(
[
('zone', 'Zone'),
('area', 'Area'),
('bin', 'Bin'),
('stock', 'Main Stock'),
('other', 'Other'),
],
string='Location Kind',
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.multi
@api.onchange('corridor', 'row', 'rack', 'level',
'posx', 'posy', 'posz')
def _compute_name(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:
if not location.kind == 'bin':
location.zone_location_id = self.browse()
location.area_location_id = self.browse()
if location.is_zone:
location.zone_location_id = location
continue
area = location
while area and not area.location_name_format:
area = area.location_id
if not area:
parent = location.location_id
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

@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
template = area.location_name_format
# We don't want to use the full browse record as it would
# give too much access to internals for the users.
# We cannot use location.read() as we may have a NewId.
# We should have the record's values in the cache at this
# point. We must be cautious not to leak an environment through
# relational fields.
location.name = template.format(**location._cache)

parent = location.location_id
if (
location.usage == 'internal'
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'
elif (
location.usage == 'internal'
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'
else:
# All the rest are other locations
location.location_kind = 'other'

@api.multi
@api.returns('self', lambda value: value.id)
def copy(self, default=None):
self.ensure_one()
default = dict(default or {})
if 'name' not in default:
default['name'] = _("%s (copy)") % (self.name)
default['name'] = _("%s (copy)") % self.name
return super().copy(default=default)

@api.model_cr
def init(self):
env = api.Environment(self._cr, SUPERUSER_ID, {})
self._init_zone_index(env)

def _init_zone_index(self, env):
"""Add unique index on name per zone
We cannot use _sql_constraints because it doesn't support
WHERE conditions. We need to apply the unique constraint
only within the same zone, otherwise the constraint fails
even on demo data (locations created automatically for
warehouses).
"""
index_name = 'stock_location_unique_name_zone_index'
create_unique_index_where(
env.cr, index_name, self._table,
['name', 'picking_zone_id'],
'picking_zone_id IS NOT NULL'
)

@classmethod
def _init_constraints_onchanges(cls):
# As the unique index created in this model acts as a unique
# constraints but cannot be registered in '_sql_constraints'
# (it doesn't support WHERE clause), associate an error
# message manually (reproduce what _sql_constraints does).
key = 'unique_name_zone'
message = ('Another location with the same name exists in the same'
' zone. Please rename the location.')
cls.pool._sql_error[cls._table + '_' + key] = message
super()._init_constraints_onchanges()
26 changes: 0 additions & 26 deletions stock_location_zone/models/stock_picking_zone.py

This file was deleted.

5 changes: 0 additions & 5 deletions stock_location_zone/readme/CONFIGURE.rst

This file was deleted.

1 change: 1 addition & 0 deletions stock_location_zone/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* Syvain Van Hoof (Okia sprl) <[email protected]>
* Jacques-Etienne Baudoux (BCIM) <[email protected]>
* Guewen Baconnier (Camptocamp) <[email protected]>
* Akim Juillerat <[email protected]>
12 changes: 10 additions & 2 deletions stock_location_zone/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Add coordinate attributes on stock location.
Define picking zone with links to picking type.
This module introduces Zone concept on stock locations to allow better
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. 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
3 changes: 0 additions & 3 deletions stock_location_zone/security/ir.model.access.csv

This file was deleted.

38 changes: 15 additions & 23 deletions stock_location_zone/views/stock_location.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,14 @@
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_form"/>
<field name="arch" type="xml">

<field name="usage" position="after">
<field name="kind" />
<field name="location_name_format" attrs="{'invisible': [('kind', '=', 'bin')]}" />
<field name="picking_type_id" options="{'no_create': True}" attrs="{'readonly': [('kind', '!=', 'zone')]}" />
<field name="scrap_location" position="before">
<field name="is_zone" />
</field>

<field name="posx" position="before">
<field name="picking_zone_id" />
<field name="area" />
<field name="corridor" />
<field name="row" />
<field name="rack" />
<field name="level" />
<field name="usage" position="after">
<field name="location_kind" />
<field name="zone_location_id" />
<field name="area_location_id" />
</field>

</field>
</record>

Expand All @@ -30,17 +22,17 @@
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_search"/>
<field name="arch" type="xml">

<xpath expr="//search" position="inside">
<field name="kind"/>
<field name="picking_zone_id"/>
<field name="area" />
<field name="corridor" />
<field name="row" />
<field name="rack" />
<field name="level" />
<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>
</record>

Expand Down
Loading

0 comments on commit 42631f3

Please sign in to comment.