166 lines
5.4 KiB
Python
166 lines
5.4 KiB
Python
# The COPYRIGHT file at the top level of this repository contains the full
|
|
# copyright notices and license terms.
|
|
from datetime import date, timedelta
|
|
|
|
from trytond.model import Workflow, ModelView, fields
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.pyson import Bool, Eval, If
|
|
from trytond.transaction import Transaction
|
|
from trytond.exceptions import UserError
|
|
from trytond.i18n import gettext
|
|
|
|
__all__ = ['Lot', 'Location', 'Move']
|
|
|
|
|
|
class Lot(metaclass=PoolMeta):
|
|
__name__ = 'stock.lot'
|
|
|
|
expired = fields.Function(fields.Boolean('Expired'),
|
|
'get_expired', searcher='search_expired')
|
|
|
|
def get_rec_name(self, name):
|
|
rec_name = super(Lot, self).get_rec_name(name)
|
|
if self.expired:
|
|
rec_name += ' (%s)' % gettext('stock_lot_expiry.expired')
|
|
return rec_name
|
|
|
|
def get_expired(self, name):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
|
|
if not self.expiration_date:
|
|
return False
|
|
|
|
context = Transaction().context
|
|
date = Date.today()
|
|
if context.get('stock_move_date'):
|
|
date = context['stock_move_date']
|
|
elif context.get('stock_date_end'):
|
|
date = context['stock_date_end']
|
|
return self.expiration_date <= date
|
|
|
|
@classmethod
|
|
def search_expired(cls, name, domain=None):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
|
|
if not domain:
|
|
return []
|
|
|
|
context = Transaction().context
|
|
date = Date.today()
|
|
if context.get('stock_move_date'):
|
|
date = context['stock_move_date']
|
|
elif context.get('stock_date_end'):
|
|
date = context['stock_date_end']
|
|
_, op, operand = domain
|
|
search_expired = (op == '=' and operand
|
|
or op == '!=' and not operand)
|
|
if search_expired:
|
|
return [
|
|
('expiration_date', '!=', None),
|
|
('expiration_date', '<=', date),
|
|
]
|
|
else:
|
|
return [
|
|
'OR', [
|
|
('expiration_date', '=', None),
|
|
], [
|
|
('expiration_date', '>', date),
|
|
]]
|
|
|
|
|
|
class Location(metaclass=PoolMeta):
|
|
__name__ = 'stock.location'
|
|
|
|
expired = fields.Boolean('Expired Products\' Location',
|
|
help='This option identifies this location as a container for expired '
|
|
'products (provably it is a temporal location until the product is '
|
|
'returned or removed).\n'
|
|
'Take care that if you set this location as a child of the Storage '
|
|
'location of a Warehouse, the products in this location will be '
|
|
'computed as available stock.')
|
|
allow_expired = fields.Boolean('Allow Expired', states={
|
|
'invisible': Eval('expired', False),
|
|
}, depends=['expired'],
|
|
help='Check this option to allow move expired lots to this location.')
|
|
|
|
@fields.depends('expired', 'allow_expired')
|
|
def on_change_expired(self):
|
|
if self.expired:
|
|
self.allow_expired = True
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
for vals in vlist:
|
|
if vals.get('expired'):
|
|
vals['allow_expired'] = True
|
|
return super(Location, cls).create(vlist)
|
|
|
|
@classmethod
|
|
def write(cls, *args):
|
|
actions = iter(args)
|
|
args = []
|
|
for locations, values in zip(actions, actions):
|
|
if values.get('expired'):
|
|
values['allow_expired'] = True
|
|
args.extend((locations, values))
|
|
super(Location, cls).write(*args)
|
|
|
|
|
|
class Move(metaclass=PoolMeta):
|
|
__name__ = 'stock.move'
|
|
|
|
to_location_allow_expired = fields.Function(
|
|
fields.Boolean('Destination Allow Expired'),
|
|
'on_change_with_to_location_allow_expired')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super(Move, cls).__setup__()
|
|
cls.lot.domain.append(
|
|
If((Eval('state', 'draft') == 'draft')
|
|
& ~Eval('to_location_allow_expired'),
|
|
('expired', '=', False),
|
|
()),
|
|
)
|
|
if not cls.lot.context:
|
|
cls.lot.context = {}
|
|
cls.lot.context['stock_move_date'] = If(
|
|
Bool(Eval('effective_date', False)),
|
|
Eval('effective_date'),
|
|
Eval('planned_date'))
|
|
|
|
cls.lot.loading = 'lazy'
|
|
|
|
for fname in ('state', 'to_location_allow_expired', 'effective_date',
|
|
'planned_date'):
|
|
if fname not in cls.lot.depends:
|
|
cls.lot.depends.append(fname)
|
|
|
|
@fields.depends('to_location')
|
|
def on_change_with_to_location_allow_expired(self, name=None):
|
|
return self.to_location and self.to_location.allow_expired or False
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('done')
|
|
def do(cls, moves):
|
|
for move in moves:
|
|
move.check_allow_lot_expired()
|
|
super(Move, cls).do(moves)
|
|
|
|
def check_allow_lot_expired(self):
|
|
if self.to_location.allow_expired or not self.lot:
|
|
return
|
|
|
|
error = False
|
|
with Transaction().set_context(stock_move_date=self.effective_date):
|
|
error = not self.to_location.allow_expired and self.lot.expired
|
|
if error:
|
|
self.raise_user_error('expired_lot_invalid_destination', {
|
|
'move': self.rec_name,
|
|
'lot': self.lot.rec_name,
|
|
'to_location': self.to_location.rec_name,
|
|
})
|