2015-10-09 14:06:06 +02:00
|
|
|
# The COPYRIGHT file at the top level of
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
|
|
|
from trytond.model import fields, ModelView, Workflow
|
|
|
|
from trytond.pool import PoolMeta, Pool
|
|
|
|
from trytond.pyson import Eval
|
|
|
|
from trytond.transaction import Transaction
|
|
|
|
from trytond.wizard import Wizard, StateTransition, StateView, Button
|
|
|
|
|
|
|
|
__metaclass__ = PoolMeta
|
|
|
|
|
2015-11-03 17:15:48 +01:00
|
|
|
__all__ = ['Configuration', 'Load', 'LoadOrder', 'LoadOrderLine',
|
2015-11-30 13:42:21 +01:00
|
|
|
'LoadUnitLoad', 'LoadUnitLoadOrder', 'LoadUnitLoadData',
|
2015-11-30 19:23:56 +01:00
|
|
|
'LoadSheet', 'CMR', 'RoadTransportNote']
|
2015-10-13 16:47:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Configuration:
|
|
|
|
__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
|
2015-10-09 14:06:06 +02:00
|
|
|
|
|
|
|
|
2015-11-03 17:15:48 +01:00
|
|
|
class Load:
|
|
|
|
__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:])]
|
|
|
|
|
|
|
|
|
2015-10-09 14:06:06 +02:00
|
|
|
class LoadOrder:
|
|
|
|
__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']),
|
2015-10-23 18:55:28 +02:00
|
|
|
'get_unit_loads', setter='set_unit_loads', searcher='search_unit_loads')
|
2015-10-13 16:47:59 +02:00
|
|
|
ul_origin_restrict = fields.Boolean('Restrict UL origin',
|
2016-01-20 13:09:48 +01:00
|
|
|
states={'readonly': Eval('state').in_(['cancel', 'done'])},
|
2015-10-13 16:47:59 +02:00
|
|
|
depends=['state'])
|
2015-10-09 14:06:06 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(LoadOrder, cls).__setup__()
|
|
|
|
cls._buttons.update({
|
|
|
|
'run_try': {'icon': 'tryton-go-next',
|
2015-10-20 19:27:57 +02:00
|
|
|
'invisible': ~Eval('state').in_(['waiting', 'running'])},
|
2015-10-09 14:06:06 +02:00
|
|
|
})
|
|
|
|
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.',
|
2016-01-24 21:05:16 +01:00
|
|
|
'ul_data': 'Product data of UL "%s" does not match with any line of load order "%s".',
|
|
|
|
'draft_ul': 'Cannot change state to Draft for load order "%s" because it has loaded ULs.'})
|
2015-10-09 14:06:06 +02:00
|
|
|
|
2015-10-13 16:47:59 +02:00
|
|
|
@classmethod
|
|
|
|
def default_ul_origin_restrict(cls):
|
|
|
|
pool = Pool()
|
|
|
|
Configuration = pool.get('carrier.configuration')
|
|
|
|
|
|
|
|
conf = Configuration(1)
|
|
|
|
return conf.ul_origin_restrict
|
|
|
|
|
2015-10-09 14:06:06 +02:00
|
|
|
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:])]
|
|
|
|
|
2015-11-03 17:15:48 +01:00
|
|
|
def get_carrier_amount(self, name=None):
|
|
|
|
if not self.load.unit_price:
|
|
|
|
return 0
|
|
|
|
return self.load.currency.round(
|
|
|
|
(len(self.unit_loads) / len(self.load.unit_loads)) * self.load.unit_price)
|
|
|
|
|
2015-10-09 14:06:06 +02:00
|
|
|
@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
|
|
|
|
|
2015-11-23 12:44:52 +01:00
|
|
|
def _get_shipment_sale(self, sale):
|
|
|
|
res = super(LoadOrder, self)._get_shipment_sale(sale)
|
|
|
|
res.start_date = self.start_date
|
|
|
|
res.on_change_start_date()
|
|
|
|
res.end_date = self.end_date
|
|
|
|
return res
|
|
|
|
|
2015-10-20 19:27:57 +02:00
|
|
|
def _get_shipment_moves(self, sale_line, 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 = []
|
2015-10-20 19:27:57 +02:00
|
|
|
for item in grouped_items:
|
|
|
|
new_moves = item._get_new_moves({'from_location': sale_line.from_location.id,
|
|
|
|
'to_location': sale_line.to_location.id,
|
2016-01-07 18:57:44 +01:00
|
|
|
'start_date': self.start_date,
|
|
|
|
'end_date': self.end_date})
|
2015-10-20 19:27:57 +02:00
|
|
|
move, = [m for m in new_moves if m.product == item.product]
|
2015-10-23 18:55:28 +02:00
|
|
|
move.origin = sale_line
|
2015-10-26 20:39:11 +01:00
|
|
|
moves.append(move)
|
|
|
|
|
|
|
|
for new_move in new_moves:
|
|
|
|
if new_move.product.id == item.product.id:
|
|
|
|
continue
|
|
|
|
new_move.origin = item.load_line
|
|
|
|
other_moves.append(new_move)
|
|
|
|
|
|
|
|
if other_moves:
|
|
|
|
Move.save(other_moves)
|
2015-10-20 19:27:57 +02:00
|
|
|
return moves
|
|
|
|
|
|
|
|
def _get_items(self):
|
|
|
|
return self.unit_loads
|
|
|
|
|
2015-10-26 20:39:11 +01:00
|
|
|
@classmethod
|
|
|
|
def do(cls, records):
|
|
|
|
pool = Pool()
|
|
|
|
Move = pool.get('stock.move')
|
|
|
|
|
|
|
|
super(LoadOrder, cls).do(records)
|
|
|
|
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-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]
|
|
|
|
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)
|
|
|
|
)
|
|
|
|
|
2015-10-09 14:06:06 +02:00
|
|
|
@classmethod
|
|
|
|
@ModelView.button_action('carrier_load_ul.wizard_load_ul')
|
|
|
|
def run_try(cls, records):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def add_ul(self, unit_loads):
|
|
|
|
pool = Pool()
|
|
|
|
UL = pool.get('stock.unit_load')
|
|
|
|
|
|
|
|
order_lines = {}
|
|
|
|
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 = None
|
|
|
|
while not wh:
|
|
|
|
location = location.parent
|
|
|
|
if not location:
|
|
|
|
break
|
|
|
|
if location.type == 'warehouse':
|
|
|
|
wh = location
|
|
|
|
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
|
2015-10-13 16:47:59 +02:00
|
|
|
lines = [l for l in self.lines if l.origin and unit_load in l.origin.unit_loads]
|
2015-10-09 14:06:06 +02:00
|
|
|
if not lines:
|
2015-10-13 16:47:59 +02:00
|
|
|
if self.ul_origin_restrict:
|
|
|
|
self.raise_user_error('ul_origin', unit_load.rec_name)
|
|
|
|
self.raise_user_warning('loading_ul_origin_%s' % unit_load.id, 'ul_origin',
|
|
|
|
unit_load.rec_name)
|
|
|
|
lines = self.lines
|
2015-10-09 14:06:06 +02:00
|
|
|
|
|
|
|
# check data matches
|
|
|
|
lines = self.check_ul_data_match(lines, unit_load)
|
|
|
|
if not lines:
|
|
|
|
self.raise_user_error('ul_data', (unit_load.rec_name, self.rec_name))
|
|
|
|
|
|
|
|
# check overload line qty
|
2015-10-13 16:47:59 +02:00
|
|
|
line = None
|
2015-10-09 14:06:06 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
UL.save(unit_loads)
|
|
|
|
|
|
|
|
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:
|
2015-10-09 14:06:06 +02:00
|
|
|
valid_lines.append(line)
|
|
|
|
continue
|
|
|
|
if product.id == unit_load.product.id:
|
|
|
|
valid_lines.append(line)
|
|
|
|
return valid_lines
|
|
|
|
|
|
|
|
|
|
|
|
class LoadOrderLine:
|
|
|
|
__name__ = 'carrier.load.order.line'
|
|
|
|
|
|
|
|
ul_quantity = fields.Float('ULs', digits=(16, 0))
|
|
|
|
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')
|
2015-10-13 16:47:59 +02:00
|
|
|
unit_loads = fields.One2Many('stock.unit_load', 'load_line', 'Unit loads',
|
2015-10-09 14:06:06 +02:00
|
|
|
readonly=True)
|
|
|
|
|
|
|
|
@fields.depends('quantity', 'ul_quantity', 'uom')
|
|
|
|
def on_change_with_quantity_per_ul(self, name=None):
|
|
|
|
if self.quantity and self.ul_quantity:
|
|
|
|
return self.uom.round(self.quantity / self.ul_quantity, self.uom.rounding)
|
|
|
|
return None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_quantity_field(cls):
|
|
|
|
return 'ul_quantity'
|
|
|
|
|
|
|
|
|
|
|
|
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: domain of uls_to_load: filter by in stock (storage location).
|
|
|
|
# It is verified later in add_ul, but would be fine to filter here
|
|
|
|
# TODO: configure ul_code reading by a string pattern (ex: P${code}) in carrier.configuration
|
2015-10-13 16:47:59 +02:00
|
|
|
# to read it with barcode scanner
|
2015-10-09 14:06:06 +02:00
|
|
|
class LoadUnitLoadData(ModelView):
|
|
|
|
"""Carrier load unit load"""
|
|
|
|
__name__ = 'carrier.load_uls.data'
|
|
|
|
|
|
|
|
load_order = fields.Many2One('carrier.load.order', 'Order',
|
|
|
|
readonly=True)
|
|
|
|
ul_code = fields.Char('UL')
|
|
|
|
uls_to_load = fields.One2Many('stock.unit_load', None, 'ULs to load',
|
|
|
|
domain=[('state', '=', 'done')])
|
2015-10-23 18:55:28 +02:00
|
|
|
uls_loaded = fields.One2Many('stock.unit_load', None, 'Loaded ULs',
|
|
|
|
domain=[('load_order', '=', Eval('load_order'))],
|
|
|
|
context={'ul_loading': True},
|
|
|
|
depends=['load_order'])
|
2015-10-09 14:06:06 +02:00
|
|
|
loaded_uls = fields.Float('Loaded ULs', digits=(16, 0),
|
|
|
|
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'),
|
2015-10-23 18:55:28 +02:00
|
|
|
Button('Unload ULs', 'unload_', 'tryton-clear'),
|
2015-10-09 14:06:06 +02:00
|
|
|
Button('Do', 'do_', 'tryton-ok'),
|
2015-10-23 18:55:28 +02:00
|
|
|
Button('Load', 'load_', 'tryton-list-add', default=True)])
|
|
|
|
load_ = StateTransition()
|
|
|
|
unload_ = StateTransition()
|
2015-10-09 14:06:06 +02:00
|
|
|
do_ = StateTransition()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(LoadUnitLoad, cls).__setup__()
|
|
|
|
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.'})
|
2015-10-09 14:06:06 +02:00
|
|
|
|
|
|
|
def transition_start(self):
|
|
|
|
if Transaction().context.get('active_model') != LoadOrder.__name__:
|
|
|
|
return 'order'
|
|
|
|
return 'data'
|
|
|
|
|
|
|
|
def default_data(self, fields):
|
|
|
|
order = self._get_load_order()
|
|
|
|
res = {'load_order': order.id,
|
|
|
|
'loaded_uls': 0}
|
|
|
|
if order.unit_loads:
|
2015-10-23 18:55:28 +02:00
|
|
|
res['loaded_uls'] = len(order.unit_loads)
|
2015-10-09 14:06:06 +02:00
|
|
|
return res
|
|
|
|
|
2015-10-23 18:55:28 +02:00
|
|
|
def transition_load_(self):
|
2015-10-09 14:06:06 +02:00
|
|
|
pool = Pool()
|
|
|
|
UL = pool.get('stock.unit_load')
|
|
|
|
|
|
|
|
if not self.data.ul_code and not self.data.uls_to_load:
|
|
|
|
self.raise_user_error('ul_required')
|
|
|
|
|
|
|
|
order = self._get_load_order()
|
|
|
|
uls = []
|
|
|
|
if self.data.ul_code:
|
|
|
|
ul = UL.search([('code', '=', self.data.ul_code)])
|
|
|
|
if not ul:
|
|
|
|
self.raise_user_error('invalid_ul', self.data.ul_code)
|
|
|
|
uls.append(ul[0])
|
|
|
|
if self.data.uls_to_load:
|
|
|
|
uls.extend(self.data.uls_to_load)
|
|
|
|
if uls:
|
|
|
|
order.add_ul(uls)
|
|
|
|
if order.state != 'running':
|
2015-10-13 16:47:59 +02:00
|
|
|
order.run([order])
|
2015-10-09 14:06:06 +02:00
|
|
|
return 'data'
|
|
|
|
|
|
|
|
def transition_do_(self):
|
|
|
|
pool = Pool()
|
|
|
|
Order = pool.get('carrier.load.order')
|
|
|
|
|
2015-10-13 16:47:59 +02:00
|
|
|
Order.do([self._get_load_order()])
|
2015-10-09 14:06:06 +02:00
|
|
|
# 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__:
|
|
|
|
return Order(Transaction().context.get('active_id'))
|
|
|
|
return Order(self.order.load_order.id)
|
2015-10-23 18:55:28 +02:00
|
|
|
|
|
|
|
def transition_unload_(self):
|
|
|
|
pool = Pool()
|
|
|
|
UnitLoad = pool.get('stock.unit_load')
|
|
|
|
|
|
|
|
if not self.data.uls_loaded:
|
|
|
|
self.raise_user_error('unload_any')
|
|
|
|
UnitLoad.unload(list(self.data.uls_loaded))
|
|
|
|
return 'data'
|
2015-11-30 13:42:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
class LoadSheet:
|
|
|
|
__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
|
|
|
|
|
|
|
|
|
|
|
|
class CMR:
|
|
|
|
__name__ = 'carrier.load.order.cmr'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_products(cls, order):
|
2015-11-30 19:23:56 +01:00
|
|
|
return list(set([ul.product for ul in order.unit_loads]))
|
|
|
|
|
|
|
|
|
|
|
|
class RoadTransportNote:
|
|
|
|
__name__ = 'carrier.load.order.road_note'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _get_products(cls, order):
|
|
|
|
return list(set([ul.product for ul in order.unit_loads]))
|