217 lines
7.1 KiB
Python
Executable File
217 lines
7.1 KiB
Python
Executable File
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
|
|
from trytond.model import ModelSQL, ModelView, Unique, fields
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.pyson import Eval, If
|
|
|
|
|
|
class ProductLocationPlace(ModelSQL, ModelView):
|
|
"Product Location Place"
|
|
__name__ = 'stock.product.location.place'
|
|
_rec_name = 'place'
|
|
|
|
template = fields.Many2One(
|
|
'product.template', "Product",
|
|
required=True, ondelete='CASCADE',
|
|
domain=[
|
|
If(Eval('product'),
|
|
('products', '=', Eval('product', -1)),
|
|
()),
|
|
])
|
|
product = fields.Many2One(
|
|
'product.product', "Variant", ondelete='CASCADE',
|
|
domain=[
|
|
If(Eval('template'),
|
|
('template', '=', Eval('template', -1)),
|
|
()),
|
|
])
|
|
location = fields.Many2One(
|
|
'stock.location', "Storage Location",
|
|
required=True, ondelete='CASCADE',
|
|
domain=[
|
|
('type', '=', 'storage'),
|
|
])
|
|
place = fields.Char(
|
|
"Place", required=True,
|
|
help="The place where the product is always stored in the location.")
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
t = cls.__table__()
|
|
cls._sql_constraints += [
|
|
('template_product_location_unique',
|
|
Unique(t, t.template, t.product, t.location),
|
|
'stock_product_location_place.'
|
|
'msg_stock_product_location_unique'),
|
|
]
|
|
|
|
@fields.depends('product', '_parent_product.template')
|
|
def on_change_product(self):
|
|
if self.product:
|
|
self.template = self.product.template
|
|
|
|
@classmethod
|
|
def default_location(cls):
|
|
pool = Pool()
|
|
Location = pool.get('stock.location')
|
|
warehouse = Location.get_default_warehouse()
|
|
if warehouse:
|
|
warehouse = Location(warehouse)
|
|
if (warehouse.storage_location
|
|
and warehouse.storage_location.type == 'storage'):
|
|
return warehouse.storage_location.id
|
|
elif (warehouse.picking_location
|
|
and warehouse.picking_location.type == 'storage'):
|
|
return warehouse.picking_location.id
|
|
|
|
|
|
class Move(metaclass=PoolMeta):
|
|
__name__ = 'stock.move'
|
|
|
|
from_place = fields.Many2One(
|
|
'stock.product.location.place', "From Place", readonly=True,
|
|
domain=[
|
|
If(~Eval('state').in_(['done', 'cancelled']),
|
|
['OR',
|
|
('template.products', '=', Eval('product', -1)),
|
|
('product', '=', Eval('product', -1)),
|
|
],
|
|
('location', '=', Eval('from_location', -1)),
|
|
),
|
|
])
|
|
to_place = fields.Many2One(
|
|
'stock.product.location.place', "To Place", readonly=True,
|
|
domain=[
|
|
If(~Eval('state').in_(['done', 'cancelled']),
|
|
['OR',
|
|
('template.products', '=', Eval('product', -1)),
|
|
('product', '=', Eval('product', -1)),
|
|
],
|
|
('location', '=', Eval('to_location', -1)),
|
|
),
|
|
])
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._allow_modify_closed_period |= {'from_place', 'to_place'}
|
|
|
|
@fields.depends('from_location', 'product')
|
|
def on_change_with_from_place(self):
|
|
if self.product and self.from_location:
|
|
return self.product.get_place(self.from_location)
|
|
|
|
@fields.depends('to_location', 'product')
|
|
def on_change_with_to_place(self):
|
|
if self.product and self.to_location:
|
|
return self.product.get_place(self.to_location)
|
|
|
|
@fields.depends('from_place')
|
|
def on_change_with_from_location_name(self, name=None):
|
|
name = super().on_change_with_from_location_name(name=name)
|
|
if self.from_place:
|
|
name = ' @ '.join(
|
|
filter(None, [name, self.from_place.rec_name])).strip()
|
|
return name
|
|
|
|
@fields.depends('to_place')
|
|
def on_change_with_to_location_name(self, name=None):
|
|
name = super().on_change_with_to_location_name(name=name)
|
|
if self.to_place:
|
|
name = ' @ '.join(
|
|
filter(None, [name, self.to_place.rec_name])).strip()
|
|
return name
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
moves = super().create(vlist)
|
|
cls._sync_places(moves)
|
|
return moves
|
|
|
|
@classmethod
|
|
def write(cls, *args):
|
|
# clean places as they maybe no more valid
|
|
actions = iter(args)
|
|
args = []
|
|
for moves, values in zip(actions, actions):
|
|
if {'product', 'from_location', 'to_location'} & values.keys():
|
|
values = values.copy()
|
|
values.setdefault('from_place')
|
|
values.setdefault('to_place')
|
|
args.extend((moves, values))
|
|
|
|
super().write(*args)
|
|
moves = sum(args[0:None:2], [])
|
|
cls._sync_places(moves)
|
|
|
|
@classmethod
|
|
def _sync_places(cls, moves):
|
|
for move in moves:
|
|
if move.state in {'done', 'cancelled'}:
|
|
continue
|
|
from_place = move.on_change_with_from_place()
|
|
to_place = move.on_change_with_to_place()
|
|
if (move.from_place != from_place
|
|
or move.to_place != to_place):
|
|
move.from_place = from_place
|
|
move.to_place = to_place
|
|
cls.save(moves)
|
|
|
|
|
|
class ShipmentIn(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.in'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
i = cls.inventory_moves.order.index(('to_location', 'ASC'))
|
|
cls.inventory_moves.order.insert(i + 1, ('to_place', 'ASC'))
|
|
|
|
|
|
class ShipmentInReturn(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.in.return'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
i = cls.moves.order.index(('from_location', 'ASC'))
|
|
cls.moves.order.insert(i + 1, ('from_place', 'ASC'))
|
|
|
|
|
|
class ShipmentOut(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.out'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
i = cls.inventory_moves.order.index(('from_location', 'ASC'))
|
|
cls.inventory_moves.order.insert(i + 1, ('from_place', 'ASC'))
|
|
|
|
|
|
class ShipmentOutReturn(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.out.return'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
i = cls.inventory_moves.order.index(('to_location', 'ASC'))
|
|
cls.inventory_moves.order.insert(i + 1, ('to_place', 'ASC'))
|
|
|
|
|
|
class ShipmentInternal(metaclass=PoolMeta):
|
|
__name__ = 'stock.shipment.internal'
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
i = cls.moves.order.index(('from_location', 'ASC'))
|
|
cls.moves.order.insert(i + 1, ('from_place', 'ASC'))
|
|
|
|
i = cls.outgoing_moves.order.index(('from_location', 'ASC'))
|
|
cls.outgoing_moves.order.insert(i + 1, ('from_place', 'ASC'))
|
|
|
|
i = cls.incoming_moves.order.index(('to_location', 'ASC'))
|
|
cls.incoming_moves.order.insert(i + 1, ('to_place', 'ASC'))
|