trytond-stock_move_time/stock.py
2017-07-28 18:55:58 +02:00

268 lines
9.6 KiB
Python

# The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
from trytond.model import fields
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, In, Bool
__all__ = ['Move', 'ShipmentOut', 'ShipmentInternal']
DATE_FORMAT = '%H:%M:%S'
class Move:
__name__ = 'stock.move'
__metaclass__ = PoolMeta
time_ = fields.Time('Time', format=DATE_FORMAT,
states={'readonly': In(Eval('state'), ['cancel', 'assigned', 'done'])},
depends=['state'])
start_date = fields.Function(
fields.DateTime('Start date', format=DATE_FORMAT,
states={'readonly': In(Eval('state'),
['cancel', 'assigned', 'done'])},
depends=['state', 'planned_date', 'effective_date', 'time_']),
'on_change_with_start_date',
setter='set_start_date', searcher='search_start_date')
end_date = fields.DateTime('End date', format=DATE_FORMAT,
states={'readonly': In(Eval('state'),
['cancel', 'assigned', 'done'])},
depends=['state'])
@classmethod
def __setup__(cls):
super(Move, cls).__setup__()
if cls.effective_date.states.get('invisible'):
cls.effective_date.states['invisible'] |= Bool(True)
else:
cls.effective_date.states['invisible'] = Bool(True)
@classmethod
def create(cls, vlist):
vlist = [v.copy() for v in vlist]
for values in vlist:
if not values.get('time_'):
values.setdefault('time_', datetime.datetime.min.time())
if not values.get('end_date') and (
values.get('effective_date') or
values.get('planned_date')):
values.setdefault('end_date', datetime.datetime.combine(
values.get('effective_date') or values.get('planned_date'),
values.get('time_'))
)
return super(Move, cls).create(vlist)
@classmethod
def write(cls, *args):
super(Move, cls).write(*args)
actions = iter(args)
for records, values in zip(actions, actions):
for record in records:
if record.start_date and (
not record.end_date or
record.end_date < record.start_date):
cls.write([record], {'end_date': record.start_date})
@fields.depends('effective_date', 'planned_date', 'time_')
def on_change_with_start_date(self, name=None):
if self.effective_date or self.planned_date:
_date = datetime.datetime.combine(self.effective_date or
self.planned_date, self.time_ or datetime.datetime.min.time())
_date = _date.replace(microsecond=0)
return _date
return None
@fields.depends('start_date', 'end_date', 'planned_date')
def on_change_start_date(self):
if self.start_date:
self.effective_date = self.start_date.date()
self.time_ = self.start_date.time()
if not self.planned_date:
self.planned_date = self.start_date.date()
if not self.end_date:
self.end_date = self.start_date
@classmethod
def set_start_date(cls, records, name, value):
if not value:
return
cls.write(records, {'effective_date': value.date(),
'time_': value.time().strftime(DATE_FORMAT)})
@classmethod
def search_start_date(cls, name, clause):
if clause[1] not in ('>', '<', '=', '>=', '<='):
raise NotImplementedError(
'Date time filter operand not implemented.')
if not clause[2]:
raise NotImplementedError('Filter must be a date.')
date_op = {'>': '>=',
'<': '<=',
'=': '=',
'>=': '>=',
'<=': '<='}
return [('effective_date', date_op[clause[1]], clause[2].date()),
('time_', clause[1], clause[2].time())]
class ShipmentOut:
__name__ = 'stock.shipment.out'
__metaclass__ = PoolMeta
start_time = fields.Time('Start time', format=DATE_FORMAT,
states={'readonly': ~Eval('state').in_(['draft', 'waiting'])},
depends=['state'])
start_date = fields.Function(
fields.DateTime('Start date', format=DATE_FORMAT,
states={'readonly': ~Eval('state').in_(['draft', 'waiting'])},
depends=['state']),
'on_change_with_start_date', setter='set_start_date')
interval = fields.TimeDelta('Interval')
end_date = fields.Function(
fields.DateTime('End date', format=DATE_FORMAT,
states={'readonly': ~Eval('state').in_(
['draft', 'waiting', 'assigned'])},
depends=['state', 'start_date', 'start_time']),
'on_change_with_end_date', setter='set_end_date')
@classmethod
def __setup__(cls):
super(ShipmentOut, cls).__setup__()
cls._error_messages.update({
'end_date': 'End date in shipment "%s" must be greater '
'than start date.'})
@staticmethod
def default_start_date():
return datetime.datetime.now()
@staticmethod
def default_end_date():
return datetime.datetime.now()
@fields.depends('effective_date', 'planned_date', 'start_time')
def on_change_with_start_date(self, name=None):
if self.effective_date or self.planned_date:
return datetime.datetime.combine(
self.effective_date or self.planned_date,
self.start_time or datetime.datetime.min.time())
return None
@fields.depends('start_date')
def on_change_start_date(self):
if self.start_date:
self.effective_date = self.start_date.date()
self.start_time = self.start_date.time()
@classmethod
def set_start_date(cls, records, name, value):
if not value:
return
cls.write(records, {'effective_date': value.date(),
'start_time': value.time().strftime(DATE_FORMAT)})
@fields.depends('start_date', 'end_date')
def on_change_end_date(self):
if self.start_date and self.end_date:
_interval = self.end_date - self.start_date
self.interval = self._format_interval(_interval)
@fields.depends('start_date', 'interval')
def on_change_with_end_date(self, name=None):
if self.start_date:
if self.interval:
return self.start_date + self._format_interval(self.interval)
return self.start_date
return None
@classmethod
def set_end_date(cls, records, name, value):
for record in records:
if not record.start_date:
continue
_interval = value - record.start_date
record.interval = cls._format_interval(_interval)
cls.save(records)
@classmethod
def _format_interval(cls, value):
return value - datetime.timedelta(microseconds=value.microseconds)
def _get_inventory_move(self, move):
res = super(ShipmentOut, self)._get_inventory_move(move)
res.start_date = self.start_date
res.end_date = self.end_date
return res
def _get_outgoing_move(self, move):
res = super(ShipmentOut, self)._get_outgoing_move(move)
res.start_date = self.end_date
res.end_date = self.end_date
return res
def _move_time(self):
return self.end_date.time(), self.start_time
@property
def _move_planned_date(self):
return self.end_date.date(), self.planned_date
@classmethod
def _set_move_planned_date(cls, shipments):
Move = Pool().get('stock.move')
super(ShipmentOut, cls)._set_move_planned_date(shipments)
for shipment in shipments:
outgoing_time, inventory_time = shipment._move_time()
Move.write([x for x in shipment.outgoing_moves
if (x.state not in ('assigned', 'done', 'cancel') and
x.time_ != outgoing_time)],
{'time_': outgoing_time})
Move.write([x for x in shipment.inventory_moves
if (x.state not in ('assigned', 'done', 'cancel') and
x.time_ != inventory_time)], {
'time_': inventory_time,
})
@classmethod
def validate(cls, records):
super(ShipmentOut, cls).validate(records)
for record in records:
if record.start_date > record.end_date:
cls.raise_user_error('end_date', record.rec_name)
class ShipmentInternal:
__name__ = 'stock.shipment.internal'
__metaclass__ = PoolMeta
time_ = fields.Time('Time', format=DATE_FORMAT,
states={'readonly': ~Eval('state').in_(['draft', 'waiting'])},
depends=['state'])
date_time_ = fields.Function(
fields.DateTime('Effective date'),
'on_change_with_date_time_', setter='set_date_time_')
@staticmethod
def default_time_():
return datetime.datetime.now().time()
@staticmethod
def default_date_time_():
return datetime.datetime.now()
@fields.depends('planned_date', 'effective_date', 'time_')
def on_change_with_date_time_(self, name=None):
if self.effective_date or self.planned_date:
return datetime.datetime.combine(
self.effective_date or self.planned_date,
self.time_ or datetime.datetime.min.time())
return None
@classmethod
def set_date_time_(cls, records, name, value):
cls.write(records, {
'effective_date': value.date(),
'time_': value.time().strftime(DATE_FORMAT)})