trytond-carrier_load_ul/load.py

894 lines
31 KiB
Python
Raw Normal View History

# The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
2016-01-28 23:37:01 +01:00
from functools import partial
from itertools import groupby
from decimal import Decimal
2017-01-18 08:36:19 +01:00
from trytond.rpc import RPC
from sql import Null
from sql.operators import Concat
from trytond.model import fields, ModelView, Model
from trytond.pool import PoolMeta, Pool
from trytond.pyson import Eval, Bool, Id
from trytond.transaction import Transaction
from trytond.wizard import Wizard, StateTransition, StateView, Button
2017-01-18 08:36:19 +01:00
__all__ = ['Configuration', 'Load', 'LoadOrder', 'LoadOrderLine',
'LoadUnitLoad', 'LoadUnitLoadOrder', 'LoadUnitLoadData',
2018-01-18 18:58:14 +01:00
'LoadSheet', 'CMR', 'RoadTransportNote', 'CreateLoadDataMixin',
'CreateLoadDataLineMixin', 'LoadUnitLoadFailed']
2019-02-26 11:03:17 +01:00
class Configuration(metaclass=PoolMeta):
__name__ = 'carrier.configuration'
ul_origin_restrict = fields.Boolean('Restrict UL origin',
help='Restricts origin of UL when loading in a Load order.')
@classmethod
def default_ul_origin_restrict(cls):
return True
2019-02-26 11:03:17 +01:00
class Load(metaclass=PoolMeta):
__name__ = 'carrier.load'
unit_loads = fields.Function(
fields.One2Many('stock.unit_load', None, 'Unit loads'),
'get_unit_loads', searcher='search_unit_loads')
def get_unit_loads(self, name=None):
if not self.orders:
return []
return [ul.id for l in self.orders for ul in l.unit_loads
if l.unit_loads]
@classmethod
def search_unit_loads(cls, name, clause):
return [('orders.unit_loads', ) + tuple(clause[1:])]
2019-02-26 11:03:17 +01:00
class LoadOrder(metaclass=PoolMeta):
__name__ = 'carrier.load.order'
unit_loads = fields.Function(
2015-10-26 20:39:11 +01:00
fields.One2Many('stock.unit_load', None, 'Unit loads',
states={'readonly': Eval('state').in_(['cancel', 'done'])},
depends=['state']),
'get_unit_loads', setter='set_unit_loads',
searcher='search_unit_loads')
ul_origin_restrict = fields.Boolean('Restrict UL origin',
states={'readonly': Eval('state').in_(['cancel', 'done'])},
depends=['state'])
2016-01-28 23:37:01 +01:00
ul_quantity = fields.Function(fields.Float('ULs', digits=(16, 0)),
'get_ul_quantity')
@classmethod
def __setup__(cls):
super(LoadOrder, cls).__setup__()
cls._buttons.update({
'run_try': {
2019-02-26 11:03:17 +01:00
'icon': 'tryton-forward',
2018-07-19 10:06:27 +02:00
'invisible': ~Eval('state').in_(['waiting', 'running']),
'depends': ['state']},
})
cls._error_messages.update({
'cancel_ul': 'Cannot cancel load orders with loaded ULs',
'ul_state': 'UL "%s" must be in Done state.',
'ul_loaded': 'UL "%s" is already loaded.',
'ul_location': 'UL "%s" must be in a storage location.',
'ul_warehouse': 'UL "%s" must be in warehouse "%s" to be loaded '
'in order "%s".',
'ul_origin': 'UL "%s" does not belong to any origin of this '
'load order.',
'ul_overload': 'All valid lines of load order "%s" are complete. '
'Cannot load more ULs.',
'draft_ul': 'Cannot change state to Draft for load order "%s" '
'because it has loaded ULs.',
2016-01-28 23:37:01 +01:00
'pending_uls': 'You have loaded less ULs (%s) than expected (%s).',
'sale_confirmed': 'Cannot force loading ULs because sale "%s" is '
'confirmed.',
'unload_cancel': 'Cannot force unloading all ULs. Load order "%s" '
'must be cancelled completely.',
2016-04-13 08:55:20 +02:00
'no_uls': 'Load order "%s" must have some UL to be finished.'
2016-01-28 23:37:01 +01:00
})
@classmethod
def __register__(cls, module_name):
pool = Pool()
Sale = pool.get('sale.sale')
sale = Sale.__table__()
sql_table = cls.__table__()
cursor = Transaction().connection.cursor()
super(LoadOrder, cls).__register__(module_name)
# Migration from 4.0: set sale
cursor.execute(*sql_table.join(sale,
condition=Concat(
cls.__name__ + ',', sql_table.id) == sale.origin
).select(sql_table.id, sale.id,
where=(sql_table.sale == Null) &
(sql_table.type == 'out') &
(sql_table.state == 'done'),
group_by=[sql_table.id, sale.id])
)
for order_id, sale_id in cursor.fetchall():
cursor.execute(*sql_table.update([sql_table.sale], [sale_id],
where=sql_table.id == order_id))
@classmethod
def default_ul_origin_restrict(cls):
pool = Pool()
Configuration = pool.get('carrier.configuration')
conf = Configuration(1)
return conf.ul_origin_restrict
def get_unit_loads(self, name=None):
if not self.lines:
return []
return [ul.id for l in self.lines for ul in l.unit_loads
if l.unit_loads]
2015-10-23 18:55:28 +02:00
@classmethod
def set_unit_loads(cls, records, name, value):
pass
@classmethod
def search_unit_loads(cls, name, clause):
return [('lines.unit_loads', ) + tuple(clause[1:])]
def get_carrier_amount(self, name=None):
if not self.load.unit_price:
return 0
return self.load.currency.round(
(Decimal(len(self.unit_loads)) / Decimal(len(
self.load.unit_loads))) * self.load.unit_price)
@classmethod
def cancel(cls, records):
if any(r.unit_loads for r in records):
cls.raise_user_error('cancel_ul')
super(LoadOrder, cls).cancel(records)
2015-10-20 19:27:57 +02:00
def _get_load_sale(self, Sale):
res = super(LoadOrder, self)._get_load_sale(Sale)
res.shipment_method = 'manual'
return res
2015-10-23 18:55:28 +02:00
def _get_load_sale_line(self, key, grouped_items):
res = super(LoadOrder, self)._get_load_sale_line(key, grouped_items)
res.ul_quantity = len(grouped_items)
return res
def _get_shipment_out(self, sale):
res = super(LoadOrder, self)._get_shipment_out(sale)
res.start_date = self.start_date
res.on_change_start_date()
res.end_date = self.end_date
return res
def _get_shipment_moves(self, origin, grouped_items):
2015-10-26 20:39:11 +01:00
pool = Pool()
Move = pool.get('stock.move')
2015-10-20 19:27:57 +02:00
moves = []
2015-10-26 20:39:11 +01:00
other_moves = []
if self.type == 'out':
from_location = origin.from_location.id
to_location = origin.to_location.id
elif self.type == 'internal':
to_location = self.to_location.id
2015-10-20 19:27:57 +02:00
for item in grouped_items:
if self.type == 'internal':
from_location = item.location.id
new_moves = item._get_new_moves({
'from_location': from_location,
'to_location': to_location,
'start_date': self.end_date,
'end_date': self.end_date,
'state': 'draft'})
if self.type == 'out':
move, = [m for m in new_moves if m.product == item.product]
move.origin = origin
moves.append(move)
out_location = self._get_outgoing_moves_location(origin)
for new_move in new_moves:
if new_move.product.id == item.product.id:
continue
if out_location and out_location != new_move.to_location:
new_move.to_location = out_location
new_move.origin = item.load_line
other_moves.append(new_move)
elif self.type == 'internal':
moves.extend(new_moves)
2015-10-26 20:39:11 +01:00
if other_moves:
Move.save(other_moves)
2015-10-20 19:27:57 +02:00
return moves
def _get_outgoing_moves_location(self, origin=None):
if origin:
return origin.to_location
if self.party:
return self.party.customer_location
2016-04-05 12:10:42 +02:00
def _update_sale(self, uls):
2016-01-28 23:37:01 +01:00
pool = Pool()
Move = pool.get('stock.move')
2016-04-05 12:10:42 +02:00
Saleline = pool.get('sale.line')
assert (all(ul in self.unit_loads for ul in uls) or
all(ul not in self.unit_loads for ul in uls))
_add = uls[0] not in self.unit_loads
2016-01-28 23:37:01 +01:00
if not self.shipment:
2016-01-28 23:37:01 +01:00
return
if self.type == 'out':
if not self.sale:
return
if self.sale.state not in ('draft', 'quotation'):
self.raise_user_error('sale_confirmed', self.sale.rec_name)
2016-04-05 12:10:42 +02:00
if not _add and len(uls) == len(self.unit_loads):
self.raise_user_error('unload_cancel', self.rec_name)
2016-01-28 23:37:01 +01:00
keyfunc = partial(self._group_line_key, uls)
2016-04-05 12:10:42 +02:00
items = sorted(uls, key=keyfunc)
2016-01-28 23:37:01 +01:00
for key, grouped_items in groupby(items, key=keyfunc):
_groupitems = list(grouped_items)
key_dict = dict(key)
2019-02-26 11:03:17 +01:00
_fields = list(key_dict.keys())
2016-01-28 23:37:01 +01:00
def get_line_values(line):
line_values = []
for _field in _fields:
value = getattr(line, _field, None)
if isinstance(value, Model):
value = int(value)
line_values.append(value)
return line_values
sale_line = None
if self.sale:
sale_line = [l for l in self.sale.lines
2019-02-26 11:03:17 +01:00
if get_line_values(l) == list(key_dict.values())]
2016-01-28 23:37:01 +01:00
if self.type == 'out':
if not sale_line:
if not _add:
continue
sale_line = self._get_load_sale_line(key, _groupitems)
sale_line.sale = self.sale
else:
sale_line, = sale_line
self._update_sale_line(sale_line, _groupitems, _add)
sale_line.save()
2016-01-28 23:37:01 +01:00
shipment = self.shipment
2016-01-28 23:37:01 +01:00
2016-04-05 12:10:42 +02:00
if _add:
outgoing_moves = self._get_shipment_moves(sale_line,
_groupitems)
2016-04-05 12:10:42 +02:00
inventory_moves = []
for move in outgoing_moves:
move.shipment = shipment
if self.type == 'out':
_inventory = shipment._get_inventory_move(move)
_inventory.start_date = self.start_date
inventory_moves.append(_inventory)
2016-04-05 12:10:42 +02:00
Move.save(outgoing_moves + inventory_moves)
Move.assign(outgoing_moves)
if self.type == 'out':
Move.do(inventory_moves)
2016-04-05 12:10:42 +02:00
else:
shipment_moves = [m for m in shipment.moves
if m.unit_load in _groupitems]
load_moves = [m for m in self.inventory_moves
if m.unit_load in _groupitems]
load_moves += [m for m in self.outgoing_moves
if m.unit_load in _groupitems]
with Transaction().set_context(check_origin=False,
check_shipment=False):
2016-04-05 12:10:42 +02:00
Move.cancel(shipment_moves + load_moves)
Move.delete(shipment_moves + load_moves)
if sale_line and not sale_line.quantity:
2016-04-05 12:10:42 +02:00
Saleline.delete([sale_line])
2016-01-28 23:37:01 +01:00
to_do = []
for move in self.outgoing_moves:
if move.state == 'draft':
_new_move = self._get_inventory_move(move)
_new_move.save()
to_do.extend([move, _new_move])
if to_do:
Move.do(to_do)
2016-04-05 12:10:42 +02:00
def _update_sale_line(self, sale_line, items, _add=True):
_sign = 1 if _add else -1
sale_line.ul_quantity += _sign * len(items)
sale_line.quantity += _sign * sum(sale_line.unit.compute_qty(
2016-01-28 23:37:01 +01:00
item.uom, item.quantity, sale_line.unit) for item in items)
2015-10-20 19:27:57 +02:00
def _get_items(self):
return self.unit_loads
2016-01-28 23:37:01 +01:00
def get_ul_quantity(self, name=None):
if not self.lines:
return 0
return sum(l.ul_quantity or 0 for l in self.lines)
2015-10-26 20:39:11 +01:00
@classmethod
def do(cls, records):
pool = Pool()
Move = pool.get('stock.move')
2016-01-28 23:37:01 +01:00
cls._check_loaded_quantity(records)
super(LoadOrder, cls).do(records)
2015-10-26 20:39:11 +01:00
moves = []
for record in records:
moves.extend(
[record._get_inventory_move(m) for m in record.outgoing_moves])
if moves:
Move.save(moves)
Move.do([m for r in records for l in r.lines for m in l.moves])
2016-01-28 23:37:01 +01:00
@classmethod
def _check_loaded_quantity(cls, records):
for record in records:
2016-04-13 08:55:20 +02:00
if not record.unit_loads:
cls.raise_user_error('no_uls', record.rec_name)
2016-01-28 23:37:01 +01:00
if record.ul_quantity > len(record.unit_loads):
cls.raise_user_warning('pending_uls_%s' % record.id,
'pending_uls', (len(record.unit_loads),
int(record.ul_quantity)))
2016-01-28 23:37:01 +01:00
2016-01-24 21:05:16 +01:00
@classmethod
def draft(cls, records):
for record in records:
if record.state != 'waiting':
continue
if record.unit_loads:
cls.raise_user_error('draft_ul', record.rec_name)
super(LoadOrder, cls).draft(records)
2015-10-26 20:39:11 +01:00
def _get_inventory_move(self, move):
pool = Pool()
Move = pool.get('stock.move')
location = move.unit_load.get_location(
[move.unit_load], product_id=move.product.id,
type='storage')[move.unit_load.id]
2015-10-26 20:39:11 +01:00
return Move(
from_location=location,
to_location=move.from_location,
product=move.product,
uom=move.uom,
quantity=move.quantity,
2016-01-07 18:57:44 +01:00
start_date=self.start_date,
end_date=self.end_date,
2015-10-26 20:39:11 +01:00
company=move.company,
currency=move.currency,
unit_price=move.unit_price,
unit_load=move.unit_load,
origin=move.origin,
lot=getattr(move, 'lot', None)
)
@classmethod
@ModelView.button_action('carrier_load_ul.wizard_load_ul')
def run_try(cls, records):
pass
def get_failed_uls(self, unit_loads):
failed_uls = []
for unit_load in unit_loads:
lines = [l for l in self.lines if l.origin and
unit_load in l.origin.unit_loads]
if not self.check_ul_data_match(lines, unit_load):
failed_uls.append(unit_load)
return failed_uls
def add_ul(self, unit_loads, origin_restrict=None,
origin_restrict_warn=True, force=False):
pool = Pool()
UL = pool.get('stock.unit_load')
2017-01-18 08:36:19 +01:00
if origin_restrict is None:
origin_restrict = self.ul_origin_restrict
order_lines = {}
failed_uls = []
for unit_load in unit_loads:
# check state
if unit_load.state != 'done':
self.raise_user_error('ul_state', unit_load.rec_name)
# check it is not loaded yet
if unit_load.load_line:
self.raise_user_error('ul_loaded', unit_load.rec_name)
location = unit_load.location
# check it is in storage location
if location.type != 'storage':
self.raise_user_error('ul_location', unit_load.rec_name)
# check it is in warehouse
wh = location.warehouse
if not wh or wh.id != self.load.warehouse.id:
self.raise_user_error('ul_warehouse', (
unit_load.rec_name, self.load.warehouse.rec_name,
self.rec_name))
# check it is linked to origin lines
lines = [l for l in self.lines if l.origin and
unit_load in l.origin.unit_loads]
if not lines:
lines = self.check_origin_restrict(unit_load, origin_restrict,
origin_restrict_warn)
# check data matches
if not force and not self.check_ul_data_match(lines, unit_load):
failed_uls.append(unit_load)
# check overload line qty
line = None
for _line in lines:
order_lines.setdefault(_line.id,
_line.ul_quantity - len(_line.unit_loads))
if order_lines[_line.id] > 0:
line = _line
break
if not line:
self.raise_user_error('ul_overload', self.rec_name)
order_lines[line.id] -= 1
# load UL
unit_load.load_line = _line
if failed_uls:
return failed_uls
2016-01-28 23:37:01 +01:00
self._update_sale(unit_loads)
2016-04-05 12:10:42 +02:00
UL.save(unit_loads)
def check_origin_restrict(self, unit_load, origin_restrict,
origin_restrict_warn):
lines = []
warn = False
for line in self.lines:
if not line.origin:
lines.append(line)
elif line.origin.__name__ in self.valid_origin_restrict():
lines.append(line)
else:
if origin_restrict:
self.raise_user_error('ul_origin', unit_load.rec_name)
if origin_restrict_warn:
warn = True
lines.append(line)
if warn:
self.raise_user_warning('loading_ul_origin_%s' %
unit_load.id, 'ul_origin', unit_load.rec_name)
return lines
@classmethod
def valid_origin_restrict(cls):
return ['sale.line']
def check_ul_data_match(self, lines, unit_load):
valid_lines = []
for line in lines:
2015-10-20 19:27:57 +02:00
product = getattr(line.origin, 'product', None)
if not line.origin or not product:
valid_lines.append(line)
continue
if product.id == unit_load.product.id:
valid_lines.append(line)
return valid_lines
@classmethod
def _group_line_key(cls, items, item):
res = super(LoadOrder, cls)._group_line_key(items, item)
if (item.load_line.origin and
item.load_line.origin.__name__ == 'sale.line'):
return res + (('id', item.load_line.origin.id), )
return res
2019-02-26 11:03:17 +01:00
class LoadOrderLine(metaclass=PoolMeta):
__name__ = 'carrier.load.order.line'
2016-04-05 12:10:42 +02:00
ul_quantity = fields.Float('ULs', digits=(16, 0),
domain=[('ul_quantity', '>=', Eval('loaded_uls'))],
depends=['loaded_uls'])
quantity_per_ul = fields.Function(
fields.Float('Quantity per UL', digits=(16, Eval('unit_digits', 0)),
depends=['unit_digits']),
'on_change_with_quantity_per_ul')
unit_loads = fields.One2Many('stock.unit_load', 'load_line', 'Unit loads',
readonly=True)
2016-04-05 12:10:42 +02:00
loaded_uls = fields.Function(fields.Float('Loaded ULs', digits=(16, 0)),
'get_loaded_uls')
@fields.depends('quantity', 'ul_quantity', 'uom')
def on_change_with_quantity_per_ul(self, name=None):
if self.quantity and self.ul_quantity:
2016-08-23 09:10:49 +02:00
return self.uom.round(self.quantity / self.ul_quantity)
return None
@classmethod
def _get_quantity_field(cls):
return 'ul_quantity'
2016-04-05 12:10:42 +02:00
def get_loaded_uls(self, name=None):
if not self.unit_loads:
return 0
return len(self.unit_loads)
@classmethod
def _get_origin(cls):
res = super(LoadOrderLine, cls)._get_origin()
res.append('sale.line')
return res
class LoadUnitLoadOrder(ModelView):
"""Carrier load unit load"""
__name__ = 'carrier.load_uls.order'
load_order = fields.Many2One('carrier.load.order', 'Order',
required=True,
domain=[('state', 'in', ['waiting', 'running'])])
# TODO: configure ul_code reading by a string pattern (ex: P${code})
# in carrier.configuration to read it with barcode scanner
class LoadUnitLoadData(ModelView):
"""Carrier load unit load"""
__name__ = 'carrier.load_uls.data'
load_order = fields.Many2One('carrier.load.order', 'Order',
readonly=True,
depends=['standalone', 'order_state'])
2016-01-28 23:37:01 +01:00
order_state = fields.Char('State', readonly=True)
standalone = fields.Boolean('Standalone', readonly=True)
ul_code = fields.Char('UL')
uls_to_load = fields.One2Many('stock.unit_load', None, 'ULs to load',
domain=[('state', '=', 'done'),
('available', '=', True)])
uls_loaded = fields.Many2Many('stock.unit_load', None, None,
'Loaded ULs',
domain=[('id', 'in', Eval('uls_loaded_domain'))],
context={'ul_loading': True},
depends=['load_order', 'uls_loaded_domain'])
uls_loaded_domain = fields.One2Many('stock.unit_load', None,
'ULs loaded Domain')
loaded_uls = fields.Float('Loaded ULs', digits=(16, 0),
readonly=True)
class LoadUnitLoadFailed(ModelView):
"""Carrier load unit load failed"""
__name__ = 'carrier.load_uls.failed'
failed_uls = fields.One2Many('stock.unit_load', None, 'Failed ULs',
readonly=True)
class LoadUnitLoad(Wizard):
"""Carrier load unit load"""
__name__ = 'carrier.load_uls'
start = StateTransition()
order = StateView('carrier.load_uls.order',
'carrier_load_ul.load_uls_order_view_form',
[Button('Cancel', 'end', 'tryton-cancel'),
Button('OK', 'data', 'tryton-ok', default=True)])
data = StateView('carrier.load_uls.data',
'carrier_load_ul.load_uls_data_view_form', [
Button('Exit', 'end', 'tryton-cancel'),
2019-02-26 11:03:17 +01:00
Button('Unload ULs', 'unload_', 'tryton-undo'),
Button('Do', 'do_', 'tryton-ok', states={
'readonly': Eval('ul_code') | Eval('uls_to_load'),
'invisible': (Eval('order_state') == 'done')}),
Button('Load', 'load_', 'tryton-add', default=True)])
failed = StateView('carrier.load_uls.failed',
'carrier_load_ul.load_uls_failed_view_form', [
Button('Force load', 'force', 'tryton-forward',
states={
'readonly': ~Id('carrier_load_ul',
'group_force_load').in_(
Eval('context', {}).get('groups', [])),
}),
Button('Accept', 'end', 'tryton-ok', True)
])
force = StateTransition()
2015-10-23 18:55:28 +02:00
load_ = StateTransition()
unload_ = StateTransition()
do_ = StateTransition()
@classmethod
def __setup__(cls):
super(LoadUnitLoad, cls).__setup__()
2017-01-18 08:36:19 +01:00
cls.__rpc__.update({
'load': RPC(readonly=False),
'unload': RPC(readonly=False),
})
cls._error_messages.update({
'invalid_ul': 'Cannot find Unit load "%s".',
2015-10-23 18:55:28 +02:00
'ul_required': 'Must define an UL to load.',
'unload_any': 'Must select some Unit load from loaded ULs list '
'in order to unload them.'})
def transition_start(self):
2016-02-01 12:53:27 +01:00
pool = Pool()
Loadorder = pool.get('carrier.load.order')
if Transaction().context.get('active_model') != LoadOrder.__name__:
return 'order'
2016-02-01 12:53:27 +01:00
order = Loadorder(Transaction().context['active_id'])
2016-02-02 09:27:53 +01:00
if order.state == 'waiting' and len(order.unit_loads) > 0:
2016-02-01 12:53:27 +01:00
Loadorder.run([order])
return 'end'
return 'data'
def default_data(self, fields):
2016-01-28 23:37:01 +01:00
order, standalone = self._get_load_order()
res = {'load_order': order.id,
2016-01-28 23:37:01 +01:00
'loaded_uls': 0,
'standalone': standalone,
2019-02-26 11:03:17 +01:00
'uls_loaded_domain': list(map(int, order.unit_loads)) or [],
2016-01-28 23:37:01 +01:00
'order_state': order.state}
if order.unit_loads:
2015-10-23 18:55:28 +02:00
res['loaded_uls'] = len(order.unit_loads)
return res
2017-01-18 08:36:19 +01:00
@classmethod
def load(cls, order_id, ul_code, uls=[], origin_restrict=True,
origin_restrict_warn=True):
pool = Pool()
2017-01-18 08:36:19 +01:00
Order = pool.get('carrier.load.order')
UL = pool.get('stock.unit_load')
2017-07-28 18:55:55 +02:00
if uls is not None:
uls = list(uls)
2017-01-18 08:36:19 +01:00
if not ul_code and not uls:
cls.raise_user_error('ul_required')
if isinstance(order_id, int):
order = Order(order_id)
else:
order = order_id
if ul_code:
ul = UL.search([('code', '=', ul_code)])
if not ul:
2017-01-18 08:36:19 +01:00
cls.raise_user_error('invalid_ul', ul_code)
uls.append(ul[0])
if uls:
failed_uls = order.add_ul(uls,
origin_restrict_warn=origin_restrict_warn)
if failed_uls:
return failed_uls
2016-05-31 17:25:42 +02:00
if order.state in ('draft', 'waiting'):
order.run([order])
2017-01-18 08:36:19 +01:00
def transition_load_(self, order_id=None, code=None):
order, _ = self._get_load_order()
failed_uls = self.load(order, self.data.ul_code, self.data.uls_to_load)
if failed_uls:
return 'failed'
return 'data'
def default_failed(self, fields):
UL = Pool().get('stock.unit_load')
order, _ = self._get_load_order()
uls = UL.search([('code', '=', self.data.ul_code)])
failed_uls = order.get_failed_uls(uls)
return {
'failed_uls': [f.id for f in failed_uls]
}
def transition_do_(self):
pool = Pool()
Order = pool.get('carrier.load.order')
2016-01-28 23:37:01 +01:00
order, _ = self._get_load_order()
Order.do([order])
# TODO: print reports
return 'end'
def _get_load_order(self):
pool = Pool()
Order = pool.get('carrier.load.order')
if Transaction().context.get('active_model') == LoadOrder.__name__:
2016-01-28 23:37:01 +01:00
return Order(Transaction().context.get('active_id')), False
return Order(self.order.load_order.id), True
2015-10-23 18:55:28 +02:00
2017-01-18 08:36:19 +01:00
@classmethod
def unload(cls, order_id, uls=[]):
2015-10-23 18:55:28 +02:00
pool = Pool()
UnitLoad = pool.get('stock.unit_load')
2017-01-18 08:36:19 +01:00
Order = pool.get('carrier.load.order')
2015-10-23 18:55:28 +02:00
2017-01-18 08:36:19 +01:00
if not uls:
cls.raise_user_error('unload_any')
if isinstance(order_id, int):
order = Order(order_id)
else:
order = order_id
if isinstance(uls[0], int):
uls = UnitLoad.browse(uls)
2016-04-05 12:10:42 +02:00
order._update_sale(uls)
UnitLoad.unload(uls)
2017-01-18 08:36:19 +01:00
def transition_unload_(self):
order, _ = self._get_load_order()
self.unload(order, list(self.data.uls_loaded))
2015-10-23 18:55:28 +02:00
return 'data'
def transition_force(self):
uls = self.failed.failed_uls
order, _ = self._get_load_order()
order.add_ul(uls, origin_restrict_warn=False, force=True)
return 'data'
2019-02-26 11:03:17 +01:00
class LoadSheet(metaclass=PoolMeta):
__name__ = 'carrier.load.sheet'
@classmethod
def get_context(cls, records, data):
report_context = super(LoadSheet, cls).get_context(records, data)
report_context['product_ul_quantity'] = lambda order, product: \
cls.product_ul_quantity(order, product)
return report_context
@classmethod
def _get_products(cls, order):
return list(set([ul.product for ul in order.unit_loads]))
@classmethod
def product_ul_quantity(cls, order, product):
"""Returns product UL quantity in load order"""
value = 0
for line in order.lines:
value += sum(1 for ul in line.unit_loads
if ul.product.id == product.id) or 0
return value
2017-10-06 09:48:01 +02:00
class TransportReportMixin(object):
@classmethod
def _get_products(cls, order):
return list(set([ul.product for ul in order.unit_loads]))
2016-01-25 13:52:45 +01:00
@classmethod
def product_weight(cls, order, product, language):
pool = Pool()
Uom = pool.get('product.uom')
Modeldata = pool.get('ir.model.data')
cat_weight = Modeldata.get_id('product', 'uom_cat_weight')
kg_uom = Uom(Modeldata.get_id('product', 'uom_kilogram'))
if product.default_uom.category.id != cat_weight:
return None
res = sum(Uom.compute_qty(
ul.uom, ul.quantity, kg_uom) or 0 for ul in order.unit_loads
if ul.product.id == product.id) or None
2016-01-25 13:52:45 +01:00
if not res:
2017-10-06 09:48:01 +02:00
return super(TransportReportMixin, cls).product_weight(order,
product, language)
2016-01-25 13:52:45 +01:00
return res
@classmethod
2017-10-06 09:48:01 +02:00
def product_packages(cls, order, product, language):
return sum(ul.cases_quantity for ul in order.unit_loads
if ul.product.id == product.id) or None
2016-01-25 13:52:45 +01:00
2019-02-26 11:03:17 +01:00
class CMR(TransportReportMixin, metaclass=PoolMeta):
2017-10-06 09:48:01 +02:00
__name__ = 'carrier.load.order.cmr'
2016-01-25 13:52:45 +01:00
2017-10-06 09:48:01 +02:00
2019-02-26 11:03:17 +01:00
class RoadTransportNote(TransportReportMixin, metaclass=PoolMeta):
2017-10-06 09:48:01 +02:00
__name__ = 'carrier.load.order.road_note'
2016-04-05 12:10:42 +02:00
class CreateLoadDataMixin(object):
load_order = fields.Many2One('carrier.load.order', 'Load order',
domain=[
('type', '=', 'out'),
('state', 'in', ['draft', 'waiting', 'running']),
('lines.origin', 'like', 'agro.production.order,%')])
warehouse = fields.Many2One('stock.location', 'Warehouse',
readonly=True,
domain=[('type', '=', 'warehouse')],
states={'invisible': Bool(Eval('load_order'))},
depends=['load_order'])
dock = fields.Many2One('stock.location.dock', 'Dock',
required=True,
domain=[('location', '=', Eval('warehouse', 0))],
states={'required': ~Eval('load_order'),
'invisible': Bool(Eval('load_order'))},
depends=['warehouse', 'load_order'])
carrier = fields.Many2One('carrier', 'Carrier', required=True,
states={'required': ~Eval('load_order'),
'invisible': Bool(Eval('load_order'))},
depends=['load_order'])
vehicle_number = fields.Char('Vehicle reg. number', required=True,
states={
'required': ~Eval('load_order'),
'invisible': Bool(Eval('load_order'))
},
depends=['load_order'])
trailer_number = fields.Char('Trailer reg. number',
states={
'invisible': Bool(Eval('load_order'))
},
depends=['load_order'])
load_purchasable = fields.Boolean('Load purchasable',
states={'invisible': Bool(Eval('load_order'))},
depends=['load_order'])
driver = fields.Char('Driver',
states={
'invisible': Bool(Eval('load_order'))
})
driver_identifier = fields.Char('Driver identifier',
states={
'required': Bool(Eval('driver')),
'invisible': Bool(Eval('load_order'))},
depends=['driver', 'load_order'])
@classmethod
def default_load_purchasable(cls):
pool = Pool()
Configuration = pool.get('carrier.configuration')
conf = Configuration(1)
return conf.load_purchasable
@fields.depends('carrier', 'vehicle_number')
def autocomplete_vehicle_number(self):
Load = Pool().get('carrier.load')
return Load._autocomplete_registration_numbers(self.carrier,
'vehicle_number', self.vehicle_number)
@fields.depends('carrier', 'trailer_number')
def autocomplete_trailer_number(self):
Load = Pool().get('carrier.load')
return Load._autocomplete_registration_numbers(self.carrier,
'trailer_number', self.trailer_number)
@fields.depends('carrier', 'driver')
def autocomplete_driver(self):
Load = Pool().get('carrier.load')
return Load._autocomplete_registration_numbers(self.carrier,
'driver', self.driver)
@fields.depends('carrier', 'driver_identifier')
def autocomplete_driver_identifier(self):
Load = Pool().get('carrier.load')
return Load._autocomplete_registration_numbers(self.carrier,
'driver_identifier', self.driver_identifier)
class CreateLoadDataLineMixin(object):
available_ul_quantity = fields.Float('Available ULs', digits=(16, 0),
readonly=True)
ul_quantity = fields.Float('ULs', digits=(16, 0),
domain=[
('ul_quantity', '<=', Eval('available_ul_quantity')),
('ul_quantity', '>=', 0)],
depends=['available_ul_quantity'])