236 lines
8.0 KiB
Python
236 lines
8.0 KiB
Python
# 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):
|
|
__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
|
|
|
|
def compute_fields(self, field_names=None):
|
|
cls = self.__class__
|
|
values = super().compute_fields(field_names=field_names)
|
|
if getattr(self, 'state', None) not in {'done', 'cancelled'}:
|
|
if (field_names is None
|
|
or cls.from_place.on_change_with & field_names):
|
|
from_place = self.on_change_with_from_place()
|
|
if getattr(self, 'from_place', None) != from_place:
|
|
values['from_place'] = from_place
|
|
if (field_names is None
|
|
or cls.to_place.on_change_with & field_names):
|
|
to_place = self.on_change_with_to_place()
|
|
if getattr(self, 'to_place', None) != to_place:
|
|
values['to_place'] = to_place
|
|
return values
|
|
|
|
@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)
|
|
|
|
|
|
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'))
|
|
|
|
|
|
class InventoryLine(metaclass=PoolMeta):
|
|
__name__ = 'stock.inventory.line'
|
|
|
|
place = fields.Many2One(
|
|
'stock.product.location.place', "Place",
|
|
domain=['OR',
|
|
('template.products', '=', Eval('product', -1)),
|
|
('product', '=', Eval('product', -1)),
|
|
])
|
|
|
|
@fields.depends(
|
|
'inventory_location', 'product',
|
|
methods=['on_change_with_inventory_location'])
|
|
def on_change_with_place(self):
|
|
location = (self.inventory_location
|
|
or self.on_change_with_inventory_location())
|
|
if self.product and location:
|
|
return self.product.get_place(location)
|
|
|
|
@fields.depends(methods=['on_change_with_place'])
|
|
def update_for_complete(self, quantity):
|
|
super().update_for_complete(quantity)
|
|
self.place = self.on_change_with_place()
|