Persist at_warehouse field.

This commit refs #24649
This commit is contained in:
jesusmr98 2022-11-03 17:36:22 +01:00 committed by Sergio Morillo
parent 52fb476bd9
commit 93fb949ecd
12 changed files with 270 additions and 74 deletions

View File

@ -234,6 +234,10 @@ msgctxt "field:stock.unit_load,warehouse:"
msgid "Warehouse"
msgstr "Almacén"
msgctxt "field:stock.unit_load,at_warehouse:"
msgid "Warehouse at date"
msgstr "Almacén a fecha"
msgctxt "field:stock.unit_load,warehouse_production:"
msgid "Warehouse production"
msgstr "Ub. producción almacén"

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
import argparse
def parse_commandline():
options = {}
parser = argparse.ArgumentParser(prog='update_bom_amount')
parser.add_argument("-c", "--config", dest="configfile", metavar='FILE',
default=None, help="specify config file")
parser.add_argument("-d", "--database", dest="database_name", default=None,
metavar='DATABASE', help="specify the database name")
parser.add_argument("-cp", "--company", dest="company_id", default=None,
metavar='COMPANY', help="specify the company ID")
options = parser.parse_args()
if not options.database_name:
parser.error('Missing database option')
if not options.company_id:
parser.error('Missing company option')
return options
if __name__ == '__main__':
options = parse_commandline()
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.config import config as CONFIG
from trytond.tools import grouped_slice, reduce_ids
CONFIG.update_etc(options.configfile)
Pool.start()
pool = Pool(options.database_name)
pool.init()
context = {'company': options.company_id}
with Transaction().start(options.database_name, 1, context=context):
UnitLoad = pool.get('stock.unit_load')
unit_load = UnitLoad.__table__()
cursor = Transaction().connection.cursor()
records = UnitLoad.search([
('at_warehouse', '=', None),
['OR',
[
('production_state', '=', 'running'),
('warehouse', '!=', None)
],
('available', '=', True)
]
])
print('Total records: %s' % len(records))
values = {}
for grouped_records in grouped_slice(records):
print('> Iterate grouped records')
for record in grouped_records:
wh = record.get_at_warehouse()
if wh:
values.setdefault(wh, []).append(record.id)
print('Start to update ULs')
for warehouse, uls in values.items():
print('Warehouse "%s"' % warehouse.rec_name)
cursor.execute(*unit_load.update(
columns=[unit_load.at_warehouse],
values=[warehouse.id],
where=reduce_ids(unit_load.id, uls)))
Transaction().commit()

View File

@ -14,6 +14,7 @@ __all__ = ['ShipmentOut', 'ShipmentInternal', 'ShipmentOutReturn',
class ShipmentUnitLoadMixin(object):
unit_loads = fields.Function(
fields.One2Many('stock.unit_load', None, 'Unit loads',
states={
@ -57,14 +58,31 @@ class ShipmentUnitLoadMixin(object):
return []
class ShipmentOut(ShipmentUnitLoadMixin, metaclass=PoolMeta):
class AtWarehouseMixin(object):
@classmethod
def __setup__(cls):
super().__setup__()
add_remove_ = [
('at_warehouse', '=', Eval('warehouse', None)),
('available', '=', True),
('production_state', '=', 'done'),
('state', '=', 'done')
]
if cls.unit_loads.add_remove:
cls.unit_loads.add_remove += add_remove_
else:
cls.unit_loads.add_remove = add_remove_
cls.unit_loads.depends.append('warehouse')
class ShipmentOut(ShipmentUnitLoadMixin, AtWarehouseMixin, metaclass=PoolMeta):
__name__ = 'stock.shipment.out'
@classmethod
def __setup__(cls):
super().__setup__()
cls.unit_loads.states['readonly'] |= Not(Bool(Eval('warehouse')))
cls.unit_loads.depends.append('warehouse')
def get_unit_loads(self, name=None):
if not self.outgoing_moves:
@ -109,7 +127,8 @@ class ShipmentOut(ShipmentUnitLoadMixin, metaclass=PoolMeta):
super()._sync_inventory_to_outgoing(shipments, quantity=quantity)
class ShipmentInternal(ShipmentUnitLoadMixin, metaclass=PoolMeta):
class ShipmentInternal(ShipmentUnitLoadMixin, AtWarehouseMixin,
metaclass=PoolMeta):
__name__ = 'stock.shipment.internal'
ul_quantity = fields.Function(
@ -239,9 +258,18 @@ class ShipmentOutReturn(ShipmentUnitLoadMixin, metaclass=PoolMeta):
return ['inventory_moves', 'incoming_moves']
class ShipmentInReturn(ShipmentUnitLoadMixin, metaclass=PoolMeta):
class ShipmentInReturn(ShipmentUnitLoadMixin, AtWarehouseMixin,
metaclass=PoolMeta):
__name__ = 'stock.shipment.in.return'
warehouse = fields.Function(fields.Many2One('stock.location', 'Warehouse'),
'on_change_with_warehouse')
@fields.depends('from_location')
def on_change_with_warehouse(self, name=None):
if self.from_location and self.from_location.warehouse:
return self.from_location.warehouse.id
def get_unit_loads(self, name=None):
if not self.moves:
return []

View File

@ -6,6 +6,7 @@ from trytond.model import fields, Workflow
from trytond.pyson import Bool
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.transaction import Transaction
from itertools import groupby
@ -20,9 +21,11 @@ def set_unit_load_shipment(func):
func(cls, moves)
if uls_to_save:
if uls_to_save and not Transaction().context.get(
'skip_set_unit_load', False):
UnitLoad.set_state(uls_to_save)
UnitLoad.set_shipment(uls_to_save)
UnitLoad.set_at_warehouse(uls_to_save)
return wrapper
@ -37,8 +40,10 @@ def set_unit_load_state(func):
func(cls, moves)
if uls_to_save:
if uls_to_save and not Transaction().context.get(
'skip_set_unit_load', False):
UnitLoad.set_state(uls_to_save)
UnitLoad.set_at_warehouse(uls_to_save)
return wrapper

View File

@ -22,19 +22,23 @@ class UnitLoad(metaclass=PoolMeta):
return new_move
def get_last_moves(self, name=None, product_id=None, location_type=None,
at_date=None, check_start_date=False, **kwargs):
at_date=None, check_start_date=False, move_states=[],
return_ids=True, **kwargs):
pool = Pool()
Move = pool.get('stock.move')
last_moves = super().get_last_moves(name, product_id, location_type,
at_date, check_start_date, **kwargs)
last_moves = super().get_last_moves(
name=name, product_id=product_id, location_type=location_type,
at_date=at_date, check_start_date=check_start_date,
move_states=move_states, return_ids=return_ids, **kwargs)
products_lots = kwargs.get('products_lots', None)
if products_lots:
for move_id in last_moves:
move = Move(move_id)
for move in list(last_moves):
if return_ids:
move = Move(move)
product_lots = products_lots.get(move.product, None)
if product_lots and not product_lots.get(move.lot, 0):
last_moves.remove(move_id)
last_moves.remove(move.id if return_ids else move)
return last_moves
def _get_quantity_to_move(self, _grouped_moves, product, uom,

View File

@ -166,7 +166,7 @@ Batch drop wizard::
True
>>> drop_ul.form.location = new_prod
>>> len(drop_ul.form.warehouse_productions)
2
1
>>> drop_ul.form.start_date = datetime.datetime.now() - relativedelta(hours=2)
>>> drop_ul.form.delay_ = datetime.timedelta(minutes=25)
>>> drop_ul.form.start_date = datetime.datetime.now() - relativedelta(hours=20)

View File

@ -74,7 +74,8 @@ Create an unit load::
>>> unit_load.click('do')
>>> len(unit_load.ul_moves)
1
>>> unit_load.at_warehouse == storage_loc.warehouse
True
Create a shipment in return::
@ -109,8 +110,14 @@ Add unit load to shipment::
>>> bool(unit_load.available)
True
>>> shipment_in_return.click('wait')
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
True
>>> shipment_in_return.click('assign_try')
True
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
>>> try:
... shipment_in_return.click('done')
... except MoveFutureWarning as warning:
@ -127,6 +134,8 @@ Add unit load to shipment::
Check unit load::
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
>>> len(unit_load.ul_moves)
2
>>> unit_load.state

View File

@ -13,6 +13,7 @@ Imports::
... get_company
>>> today = datetime.date.today()
>>> tomorrow = today + relativedelta(days=1)
>>> yesterday = today - relativedelta(days=1)
>>> time_ = datetime.datetime.now().time()
>>> time_ = time_.replace(microsecond=0)
@ -58,6 +59,10 @@ Get stock locations::
>>> wh2.name = 'Warehouse 2'
>>> wh2.code = 'WH2'
>>> wh2.save()
>>> storage_loc2 = wh2.storage_location
>>> storage_loc2.name = '%s 2' % storage_loc2.name
>>> storage_loc2.code = 'STO2'
>>> storage_loc2.save()
Create an unit load::
@ -65,8 +70,7 @@ Create an unit load::
>>> unit_load = UnitLoad()
>>> unit_load.company != None
True
>>> unit_load.start_date != None
True
>>> unit_load.start_date = datetime.datetime.now() - relativedelta(days=1)
>>> unit_load.end_date != None
True
>>> unit_load.end_date = unit_load.start_date + relativedelta(minutes=5)
@ -84,6 +88,8 @@ Create an unit load::
>>> unit_load.save()
>>> unit_load.code != None
True
>>> unit_load.at_warehouse == None
True
Add moves::
@ -93,7 +99,7 @@ Add moves::
>>> len(unit_load.production_moves)
1
>>> move = unit_load.production_moves[0]
>>> move.planned_date == today
>>> move.planned_date == yesterday
True
>>> move.product == unit_load.product
True
@ -141,15 +147,17 @@ Check computed fields::
'Unit'
>>> unit_load.save()
>>> unit_load.click('do')
>>> unit_load.at_warehouse == warehouse_loc
True
Create a shipment internal::
>>> ShipmentInternal = Model.get('stock.shipment.internal')
>>> shipment_internal = ShipmentInternal()
>>> shipment_internal.company = company
>>> shipment_internal.date_time_ = datetime.datetime.now() + relativedelta(minutes=10)
>>> shipment_internal.date_time_ = datetime.datetime.now() - relativedelta(minutes=10)
>>> shipment_internal.from_location = unit_load.location
>>> shipment_internal.to_location = wh2.storage_location
>>> shipment_internal.to_location = storage_loc2
Add Unit load::
@ -177,4 +185,5 @@ Check unit load state::
'done'
>>> unit_load.location.id == wh2.storage_location.id
True
>>> unit_load.at_warehouse == wh2
True

View File

@ -83,6 +83,8 @@ Create an unit load::
True
>>> unit_load.shipment == None
True
>>> unit_load.at_warehouse == None
True
Add moves::
@ -130,6 +132,8 @@ Check computed fields::
>>> len(unit_load.last_moves) == 1
True
>>> unit_load.click('assign')
>>> unit_load.at_warehouse == warehouse_loc
True
>>> unit_load.state
'assigned'
>>> unit_load.moves[0].state
@ -236,6 +240,8 @@ Add Unit load::
>>> unit_load.available
True
>>> unit_load.at_warehouse == storage_loc.warehouse
True
>>> shipment_out.unit_loads.append(unit_load)
>>> len(shipment_out.outgoing_moves)
1
@ -249,11 +255,26 @@ Add Unit load::
1
>>> shipment_out.inventory_moves[0].unit_load.id == unit_load.id
True
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
True
>>> shipment_out.click('assign_try')
True
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
>>> shipment_out.click('pick')
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
>>> shipment_out.click('pack')
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
>>> shipment_out.click('done')
>>> unit_load.reload()
>>> bool(unit_load.at_warehouse)
False
Check unit load state::
@ -326,4 +347,21 @@ Create another unit load and try move to another warehouse::
>>> unit_load2.click('do') # doctest: +ELLIPSIS
Traceback (most recent call last):
...
trytond.exceptions.UserError: Cannot move or drop UL "..." to location "Storage Zone" because it is on Warehouse "Warehouse". -
trytond.exceptions.UserError: Cannot move or drop UL "..." to location "Storage Zone" because it is on Warehouse "Warehouse". -
Create an unit load in a warehouse and change it later::
>>> unit_load = UnitLoad()
>>> unit_load.end_date = unit_load.start_date + relativedelta(minutes=5)
>>> unit_load.warehouse = warehouse_loc
>>> unit_load.production_type = 'location'
>>> unit_load.production_location = warehouse_loc.production_location
>>> unit_load.product = product
>>> unit_load.cases_quantity = 5
>>> unit_load.save()
>>> unit_load.at_warehouse == warehouse_loc
True
>>> unit_load.warehouse = wh2
>>> unit_load.save()
>>> unit_load.at_warehouse == wh2
True

View File

@ -28,12 +28,6 @@ from .ir import cases_decimal
cases_digits = (16, cases_decimal)
__all__ = ['UnitLoad', 'UnitLoadMove', 'MoveUnitLoad',
'MoveUnitLoadStart', 'UnitLoadLabel', 'DropUnitLoadData',
'DropUnitLoad', 'DropUnitLoadFailed', 'DropUnitLoadFailedProduct',
'BatchDropUnitLoad', 'BatchDropUnitLoadData', 'BatchDropUnitLoadConfirm',
'DropUnitLoadUL', 'DropUnitLoadEndDate', 'CaseLabel']
MOVE_CHANGES = ['product', 'uom', 'production_type', 'production_location',
'warehouse', 'production_moves', 'moves', 'production_state', 'company',
'quantity', 'start_date', 'end_date', 'cases_quantity']
@ -228,9 +222,8 @@ class UnitLoad(ModelSQL, ModelView):
readonly=True, select=True)
production_time = fields.Function(fields.TimeDelta('Production time'),
'get_production_time')
at_warehouse = fields.Function(
fields.Many2One('stock.location', 'Warehouse at date'),
'get_at_warehouse')
at_warehouse = fields.Many2One('stock.location', 'Warehouse at date',
readonly=True)
in_production = fields.Function(
fields.Boolean('In production'),
'get_in_production', searcher='search_in_production')
@ -413,6 +406,7 @@ class UnitLoad(ModelSQL, ModelView):
'unit_load_sequence',
company=values.get('company', default_company)).get()
values['code_length'] = len(values['code'])
values.setdefault('at_warehouse', values.get('warehouse', None))
return super(UnitLoad, cls).create(vlist)
@classmethod
@ -423,6 +417,16 @@ class UnitLoad(ModelSQL, ModelView):
if values.get('code'):
values = values.copy()
values['code_length'] = len(values['code'])
if 'warehouse' in values:
wh_records = [r for r in records
if r.production_state == 'running'
and r.state == 'draft'
]
wh_values = {'at_warehouse': values['warehouse']}
if wh_records == records:
values.update(wh_values)
else:
args.extend((wh_records, wh_values))
_deny_done = cls._deny_modify_done
_deny_modify = cls._deny_modify_not_available
vals_set = set(values)
@ -728,17 +732,18 @@ class UnitLoad(ModelSQL, ModelView):
@classmethod
def get_location(cls, records, name=None, product_id=None, type=None,
at_date=None):
pool = Pool()
Move = pool.get('stock.move')
at_date=None, move_states=[], return_ids=True):
value = dict.fromkeys(list(map(int, records)), None)
for record in records:
moves = record.get_last_moves(product_id=product_id,
location_type=type, at_date=at_date)
location_type=type, at_date=at_date, move_states=move_states,
return_ids=False)
if moves:
value[record.id] = Move(moves[0]).to_location.id
location = moves[0].to_location
if return_ids:
location = location.id
value[record.id] = location
return value
def get_location_type(self, name=None):
@ -791,7 +796,8 @@ class UnitLoad(ModelSQL, ModelView):
return max(m.end_date for m in _moves)
def get_last_moves(self, name=None, product_id=None, location_type=None,
at_date=None, check_start_date=False, **kwargs):
at_date=None, check_start_date=False, move_states=[],
return_ids=True, **kwargs):
if not self.moves:
return []
@ -810,19 +816,25 @@ class UnitLoad(ModelSQL, ModelView):
x.end_date or x.start_date or datetime.datetime.min,
x.start_date or datetime.datetime.min)[::tup_rev],
reverse=True):
if move_states and move.state not in move_states:
continue
if move.state == 'cancelled':
continue
if location_type and location_type != move.to_location.type:
continue
max_date = checked_date(move)
location_id = move.to_location.id
if at_date and at_date < checked_date(move):
if at_date and checked_date(move) and at_date < checked_date(move):
continue
break
return [m.id for m in self.moves if
(check_start_date and m.start_date or m.end_date) == max_date and
m.to_location.id == location_id]
moves = [m for m in self.moves
if (check_start_date and m.start_date or m.end_date) == max_date
and m.to_location.id == location_id
]
if return_ids:
moves = list(map(int, moves))
return moves
def get_production_type(self, name=None):
if self.production_location:
@ -914,8 +926,7 @@ class UnitLoad(ModelSQL, ModelView):
return new_moves
def check_to_move(self, from_location, to_location, at_date):
pool = Pool()
Move = pool.get('stock.move')
if not from_location:
raise UserError(gettext(
'stock_unit_load.msg_stock_unit_load_missing_location',
@ -935,9 +946,9 @@ class UnitLoad(ModelSQL, ModelView):
to_location.type == 'production':
# allow overlapped drops
if self.drop_moves:
_last_moves = self.get_last_moves(location_type='storage')
_last_moves = self.get_last_moves(location_type='storage',
return_ids=False)
if _last_moves:
_last_moves = Move.browse(_last_moves)
_max_date = max(m.end_date for m in _last_moves)
if _max_date > at_date:
lzone = (dateutil.tz.gettz(self.company.timezone
@ -953,9 +964,6 @@ class UnitLoad(ModelSQL, ModelView):
def _get_new_moves(self, default_values={}, location_type=None,
cases_quantity=None, **kwargs):
pool = Pool()
Move = pool.get('stock.move')
default_values.update({
'unit_load': self.id,
'origin': None,
@ -964,8 +972,8 @@ class UnitLoad(ModelSQL, ModelView):
})
moves = []
_last_moves = Move.browse(self.get_last_moves(
location_type=location_type, **kwargs))
_last_moves = self.get_last_moves(
location_type=location_type, return_ids=False, **kwargs)
if not default_values.get('from_location'):
if not location_type:
default_values['from_location'] = self.location.id
@ -1295,8 +1303,9 @@ class UnitLoad(ModelSQL, ModelView):
move.unit_load.check_warehouse(move)
if moves:
Move.do(moves)
cls.set_drop_state(records)
with Transaction().set_context(skip_set_unit_load=True):
Move.do(moves)
cls.set_drop_state(records, update_warehouse=False)
move_changes = []
for record in records:
if record.return_moves:
@ -1313,6 +1322,7 @@ class UnitLoad(ModelSQL, ModelView):
# set done production state
cls.set_production_state(records)
cls.set_state(records)
cls.set_at_warehouse(records)
@classmethod
def set_production_state(cls, records, state='done'):
@ -1326,7 +1336,9 @@ class UnitLoad(ModelSQL, ModelView):
})
@classmethod
def set_drop_state(cls, records):
def set_drop_state(cls, records, update_warehouse=True):
"""Set dropping state in UL."""
"""update_warehouse: determines if at_warehouse must be computed"""
to_drop = []
to_undrop = []
changes = {
@ -1353,6 +1365,8 @@ class UnitLoad(ModelSQL, ModelView):
for key, values in changes.items():
if values:
cls.write(values, {'dropped': key})
if update_warehouse:
cls.set_at_warehouse(records)
def _get_dropped_quantity(self, product=None, to_uom=None, done=False):
if not self.drop_moves:
@ -1752,15 +1766,32 @@ class UnitLoad(ModelSQL, ModelView):
to_location=move.to_location.rec_name,
warehouse=from_wh[0].rec_name))
def get_at_warehouse(self, name=None, date=None):
pool = Pool()
Location = pool.get('stock.location')
def get_at_warehouse(self, date=None):
if self.production_state == 'running':
if self.warehouse:
return self.warehouse
elif not self.available:
return None
to_location = self.get_location([self],
at_date=date or datetime.datetime.now(),
type='storage',
move_states=['assigned', 'done'],
return_ids=False)[self.id]
if to_location and to_location.warehouse:
return to_location.warehouse
return None
to_location = self.get_location([self], at_date=date
or datetime.datetime.now(), type='storage')
to_location = Location(to_location[self.id])
if to_location.warehouse:
return to_location.warehouse.id
@classmethod
def set_at_warehouse(cls, records):
records = cls.browse(records)
to_save = []
for record in records:
wh = record.get_at_warehouse()
if wh != record.at_warehouse:
record.at_warehouse = wh
to_save.append(record)
if to_save:
cls.save(to_save)
@property
def cases_digits(self):
@ -1895,8 +1926,8 @@ class MoveUnitLoadStart(ModelView):
@fields.depends('planned_date', 'unit_load')
def on_change_with_warehouse(self):
if self.planned_date and self.unit_load:
return self.unit_load.get_at_warehouse(
date=self.planned_date)
wh = self.unit_load.get_at_warehouse(date=self.planned_date)
return wh.id if wh else None
class MoveUnitLoad(Wizard):
@ -1980,12 +2011,8 @@ class DropUnitLoadData(ModelView):
def on_change_start_date(self):
self.warehouse_production = None
if self.start_date and self.unit_load:
pool = Pool()
Location = pool.get('stock.location')
wh = self.unit_load.get_at_warehouse(date=self.start_date)
if wh:
wh = Location(wh)
self.warehouse_production = wh.production_location
@ -2428,16 +2455,12 @@ class BatchDropUnitLoadData(ModelView):
@fields.depends('start_date', 'unit_loads')
def on_change_with_warehouse_productions(self):
pool = Pool()
Location = pool.get('stock.location')
whs = []
whs = set()
if self.start_date and self.unit_loads:
for ul in self.unit_loads:
wh = ul.get_at_warehouse(date=self.start_date)
if wh:
whs.append(wh)
whs = Location.browse(whs)
whs.add(wh)
return [wh.production_location.id
for wh in whs if wh.production_location]
return []

View File

@ -52,6 +52,8 @@
<field name="location_type"/>
<label name="shipment"/>
<field name="shipment"/>
<label name="at_warehouse"/>
<field name="at_warehouse"/>
<newline/>
</page>
</notebook>

View File

@ -4,6 +4,7 @@
<tree>
<field name="code"/>
<field name="start_date" widget="date"/>
<field name="at_warehouse"/>
<field name="product"/>
<field name="cases_quantity" sum="Cases"/>
<field name="available_cases_quantity" sum="Available cases"/>
@ -12,5 +13,5 @@
<field name="available"/>
<field name="end_date" widget="date" tree_invisible="1"/>
<field name="production_location" tree_invisible="1"/>
<field name="warehouse"/>
<field name="warehouse" tree_invisible="1"/>
</tree>