first commit
This commit is contained in:
267
modules/stock_location_move/stock.py
Normal file
267
modules/stock_location_move/stock.py
Normal file
@@ -0,0 +1,267 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import functools
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import ModelSQL, ModelView, Workflow, fields
|
||||
from trytond.modules.stock.exceptions import AssignError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class Location(metaclass=PoolMeta):
|
||||
__name__ = 'stock.location'
|
||||
|
||||
movable = fields.Boolean(
|
||||
"Movable",
|
||||
states={
|
||||
'invisible': Eval('type') != 'storage',
|
||||
})
|
||||
assigned_by = fields.Reference(
|
||||
"Assigned by", 'get_assigned_by', readonly=True)
|
||||
|
||||
@classmethod
|
||||
def _get_assigned_by(cls):
|
||||
"Return list of Model names for assigned_by Reference"
|
||||
return ['stock.shipment.internal', 'stock.shipment.out']
|
||||
|
||||
@classmethod
|
||||
def get_assigned_by(cls):
|
||||
pool = Pool()
|
||||
IrModel = pool.get('ir.model')
|
||||
get_name = IrModel.get_name
|
||||
models = cls._get_assigned_by()
|
||||
return [(None, '')] + [(m, get_name(m)) for m in models]
|
||||
|
||||
@classmethod
|
||||
def deactivate_empty(cls, locations=None):
|
||||
if locations is None:
|
||||
locations = cls.search([
|
||||
('movable', '=', True),
|
||||
])
|
||||
to_deactivate = cls.get_empty_locations(locations)
|
||||
if to_deactivate:
|
||||
cls.write(cls.browse(to_deactivate), {'active': False})
|
||||
|
||||
@classmethod
|
||||
def forecast_location_move(cls, date):
|
||||
"""Move temporary locations planned for the date
|
||||
and returns a method to restore the initial parent."""
|
||||
pool = Pool()
|
||||
ShipmentInternal = pool.get('stock.shipment.internal')
|
||||
Location = pool.get('stock.location')
|
||||
Date = pool.get('ir.date')
|
||||
|
||||
today = Date.today()
|
||||
shipments = ShipmentInternal.search([
|
||||
('locations', '!=', None),
|
||||
('state', 'not in', ['cancelled', 'done']),
|
||||
['OR', [
|
||||
('planned_date', '<=', date),
|
||||
('planned_date', '>=', today),
|
||||
('effective_date', '=', None),
|
||||
], [
|
||||
('effective_date', '<=', date),
|
||||
('effective_date', '>=', today),
|
||||
],
|
||||
],
|
||||
],
|
||||
order=[('planned_date', 'ASC'), ('id', 'ASC')])
|
||||
location_parents = {}
|
||||
locations = []
|
||||
|
||||
for shipment in shipments:
|
||||
for location in shipment.locations:
|
||||
location_parents.setdefault(location, location.parent)
|
||||
location.parent = shipment.to_location
|
||||
locations.append(location)
|
||||
Location.save(locations)
|
||||
|
||||
def restore():
|
||||
locations = []
|
||||
for location, parent in location_parents.items():
|
||||
location.parent = parent
|
||||
locations.append(location)
|
||||
Location.save(locations)
|
||||
return restore
|
||||
|
||||
|
||||
def clear_location_assignation(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(cls, shipments, *args, **kwargs):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
locations = []
|
||||
for shipment in shipments:
|
||||
for location in shipment.locations:
|
||||
if location.assigned_by == shipment:
|
||||
locations.append(location)
|
||||
Location.write(locations, {'assigned_by': None})
|
||||
return func(cls, shipments, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class ShipmentInternal(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.internal'
|
||||
|
||||
locations = fields.Many2Many(
|
||||
'stock.shipment.internal-location', 'shipment', 'location',
|
||||
"Locations",
|
||||
domain=[
|
||||
('type', '=', 'storage'),
|
||||
('movable', '=', True),
|
||||
If(Eval('state') == 'assigned',
|
||||
('parent', 'child_of', Eval('from_location', -1)),
|
||||
()),
|
||||
],
|
||||
states={
|
||||
'readonly': (~Eval('state').in_(['request', 'draft'])
|
||||
| ~Eval('from_location') | ~Eval('to_location')),
|
||||
})
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('draft')
|
||||
@clear_location_assignation
|
||||
def draft(cls, shipments):
|
||||
super().draft(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('waiting')
|
||||
@clear_location_assignation
|
||||
def wait(cls, shipments):
|
||||
super().wait(shipments)
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('assigned')
|
||||
def assign(cls, shipments):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
locations = {}
|
||||
for shipment in shipments:
|
||||
for location in shipment.locations:
|
||||
if not location.assigned_by:
|
||||
location.assigned_by = shipment
|
||||
if location in locations:
|
||||
raise AssignError(
|
||||
gettext('stock_location_move'
|
||||
'.msg_location_already_assigned') % {
|
||||
'location': location.rec_name,
|
||||
'assigned_by': locations[location].rec_name,
|
||||
})
|
||||
locations[location] = location.assigned_by
|
||||
elif location.assigned_by != shipment:
|
||||
raise AssignError(
|
||||
gettext('stock_location_move'
|
||||
'.msg_location_already_assigned') % {
|
||||
'location': location.rec_name,
|
||||
'assigned_by': location.assigned_by.rec_name,
|
||||
})
|
||||
if locations:
|
||||
Location.save(list(locations))
|
||||
super().assign(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('shipped')
|
||||
def ship(cls, shipments):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
to_write = []
|
||||
for shipment in shipments:
|
||||
if not shipment.transit_location or not shipment.locations:
|
||||
continue
|
||||
to_write.append(list(shipment.locations))
|
||||
to_write.append({
|
||||
'parent': shipment.transit_location.id,
|
||||
})
|
||||
if to_write:
|
||||
Location.write(*to_write)
|
||||
super().ship(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@clear_location_assignation
|
||||
def do(cls, shipments):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
to_write = []
|
||||
for shipment in shipments:
|
||||
if not shipment.locations:
|
||||
continue
|
||||
to_write.append(list(shipment.locations))
|
||||
to_write.append({
|
||||
'parent': shipment.to_location.id,
|
||||
})
|
||||
if to_write:
|
||||
Location.write(*to_write)
|
||||
super().do(shipments)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('cancelled')
|
||||
@clear_location_assignation
|
||||
def cancel(cls, shipments):
|
||||
super().cancel(shipments)
|
||||
|
||||
|
||||
class ShipmentInternal_Location(ModelSQL):
|
||||
__name__ = 'stock.shipment.internal-location'
|
||||
|
||||
shipment = fields.Many2One(
|
||||
'stock.shipment.internal', "Shipment", required=True)
|
||||
location = fields.Many2One(
|
||||
'stock.location', "Location", required=True,
|
||||
domain=[
|
||||
('type', '=', 'storage'),
|
||||
('movable', '=', True),
|
||||
])
|
||||
|
||||
|
||||
def deactivate_empty_location(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(cls, shipments, *args, **kwargs):
|
||||
pool = Pool()
|
||||
Location = pool.get('stock.location')
|
||||
result = func(cls, shipments, *args, **kwargs)
|
||||
locations = set()
|
||||
for shipment in shipments:
|
||||
locations.update(
|
||||
move.from_location for move in shipment.moves
|
||||
if move.from_location.movable)
|
||||
Location.deactivate_empty(list(locations))
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
class ShipmentOut(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@deactivate_empty_location
|
||||
def do(cls, shipments):
|
||||
super().do(shipments)
|
||||
|
||||
|
||||
class ShipmentInReturn(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in.return'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('done')
|
||||
@deactivate_empty_location
|
||||
def do(cls, shipments):
|
||||
super().do(shipments)
|
||||
|
||||
|
||||
class Supply(metaclass=PoolMeta):
|
||||
__name__ = 'stock.supply'
|
||||
|
||||
def transition_create_(self):
|
||||
with Transaction().set_context(forecast_location_move=True):
|
||||
return super().transition_create_()
|
||||
Reference in New Issue
Block a user