trytond-stock_lot_expiry/stock.py

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,
})