parent
381f1fed8f
commit
4c3f5275f9
|
@ -10,7 +10,8 @@ from .unit_load import (UnitLoad, UnitLoadMove, MoveUnitLoad,
|
|||
DropUnitLoadUL, DropUnitLoadEndDate, CaseLabel)
|
||||
from .stock import (Move, UnitLoadsByLocations,
|
||||
UnitLoadsByLocationsStart, Move2)
|
||||
from .shipment import ShipmentOut, ShipmentInternal, ShipmentOutReturn
|
||||
from .shipment import (ShipmentOut, ShipmentInternal, ShipmentOutReturn,
|
||||
ShipmentInReturn)
|
||||
from .res import User
|
||||
|
||||
|
||||
|
@ -32,6 +33,7 @@ def register():
|
|||
ShipmentOut,
|
||||
ShipmentInternal,
|
||||
ShipmentOutReturn,
|
||||
ShipmentInReturn,
|
||||
BatchDropUnitLoadData,
|
||||
BatchDropUnitLoadConfirm,
|
||||
User,
|
||||
|
|
16
locale/es.po
16
locale/es.po
|
@ -766,6 +766,10 @@ msgctxt "model:ir.action,name:act_unit_loads_shipment_internal"
|
|||
msgid "Internal Shipments"
|
||||
msgstr "Albaranes internos"
|
||||
|
||||
msgctxt "model:ir.action,name:act_unit_loads_shipment_in_return"
|
||||
msgid "Supplier Return Shipments"
|
||||
msgstr "Albaranes devolución de proveedor"
|
||||
|
||||
msgctxt "model:ir.action,name:act_relate_shipment_out_unit_load"
|
||||
msgid "Unit loads"
|
||||
msgstr "Unidades de carga"
|
||||
|
@ -774,6 +778,10 @@ msgctxt "model:ir.action,name:act_relate_shipment_out_return_unit_load"
|
|||
msgid "Unit loads"
|
||||
msgstr "Unidades de carga"
|
||||
|
||||
msgctxt "model:ir.action,name:act_relate_shipment_in_return_unit_load"
|
||||
msgid "Unit loads"
|
||||
msgstr "Unidades de carga"
|
||||
|
||||
msgctxt "field:stock.unit_load,shipment:"
|
||||
msgid "Shipment"
|
||||
msgstr "Albarán"
|
||||
|
@ -876,4 +884,12 @@ msgstr "Unidades de carga"
|
|||
|
||||
msgctxt "view:stock.shipment.out.return:"
|
||||
msgid "ULs"
|
||||
msgstr "UdCs"
|
||||
|
||||
msgctxt "field:stock.shipment.in.return,unit_loads:"
|
||||
msgid "Unit loads"
|
||||
msgstr "Unidades de carga"
|
||||
|
||||
msgctxt "view:stock.shipment.in.return:"
|
||||
msgid "ULs"
|
||||
msgstr "UdCs"
|
170
shipment.py
170
shipment.py
|
@ -3,29 +3,29 @@
|
|||
import datetime
|
||||
from trytond.model import fields
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.pyson import Eval, Not, Bool
|
||||
from trytond.pyson import Eval, Not, Bool, And
|
||||
|
||||
__all__ = ['ShipmentOut', 'ShipmentInternal', 'ShipmentOutReturn']
|
||||
__all__ = ['ShipmentOut', 'ShipmentInternal', 'ShipmentOutReturn',
|
||||
'ShipmentInReturn']
|
||||
|
||||
|
||||
class ShipmentOut(metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
|
||||
class ShipmentUnitLoadMixin(object):
|
||||
unit_loads = fields.Function(
|
||||
fields.One2Many('stock.unit_load', None, 'Unit loads', states={
|
||||
'readonly': (Eval('state') != 'draft') |
|
||||
Not(Bool('warehouse')),
|
||||
'invisible': (
|
||||
(Eval('state') != 'draft') & ~Eval('unit_loads', []))
|
||||
fields.One2Many('stock.unit_load', None, 'Unit loads',
|
||||
states={
|
||||
'readonly': Eval('state') != 'draft',
|
||||
'invisible': And(
|
||||
Eval('state') != 'draft',
|
||||
Not(Bool(Eval('unit_loads', []))))
|
||||
},
|
||||
depends=['state', 'warehouse']),
|
||||
depends=['state']),
|
||||
'get_unit_loads', setter='set_unit_loads',
|
||||
searcher='search_unit_loads')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(ShipmentOut, cls).__setup__()
|
||||
for _field_name in ('inventory_moves', 'outgoing_moves'):
|
||||
super().__setup__()
|
||||
for _field_name in cls._get_ul_readonly_fields_name():
|
||||
_field = getattr(cls, _field_name)
|
||||
if _field.states.get('readonly'):
|
||||
_field.states['readonly'] |= Eval('unit_loads')
|
||||
|
@ -35,10 +35,7 @@ class ShipmentOut(metaclass=PoolMeta):
|
|||
_field.depends.append('unit_loads')
|
||||
|
||||
def get_unit_loads(self, name=None):
|
||||
if not self.outgoing_moves:
|
||||
return []
|
||||
uls = set(m.unit_load.id for m in self.outgoing_moves if m.unit_load)
|
||||
return list(uls)
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def set_unit_loads(cls, records, name, value):
|
||||
|
@ -51,8 +48,28 @@ class ShipmentOut(metaclass=PoolMeta):
|
|||
_field += '.%s' % name[10:]
|
||||
return [(_field, ) + tuple(clause[1:])]
|
||||
|
||||
@classmethod
|
||||
def _get_ul_readonly_fields_name(cls):
|
||||
return []
|
||||
|
||||
|
||||
class ShipmentOut(ShipmentUnitLoadMixin, metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out'
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.unit_loads.states['readonly'] |= Not(Bool(Eval('warehouse')))
|
||||
cls.unit_loads.depends.append('warehouse')
|
||||
|
||||
def get_unit_loads(self, name=None):
|
||||
if not self.outgoing_moves:
|
||||
return []
|
||||
uls = set(m.unit_load.id for m in self.outgoing_moves if m.unit_load)
|
||||
return list(uls)
|
||||
|
||||
@fields.depends('unit_loads', 'outgoing_moves', 'warehouse_output',
|
||||
'customer_location', 'end_date')
|
||||
'customer_location', 'end_date')
|
||||
def on_change_unit_loads(self):
|
||||
moves = []
|
||||
ul_ids = [ul.id for ul in self.unit_loads]
|
||||
|
@ -78,33 +95,16 @@ class ShipmentOut(metaclass=PoolMeta):
|
|||
res.unit_load = move.unit_load
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def _get_ul_readonly_fields_name(cls):
|
||||
return ['inventory_moves', 'outgoing_moves']
|
||||
|
||||
class ShipmentInternal(metaclass=PoolMeta):
|
||||
|
||||
class ShipmentInternal(ShipmentUnitLoadMixin, metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.internal'
|
||||
|
||||
ul_quantity = fields.Function(
|
||||
fields.Float('ULs', digits=(16, 0)), 'get_ul_quantity')
|
||||
unit_loads = fields.Function(
|
||||
fields.One2Many('stock.unit_load', None, 'Unit loads', states={
|
||||
'readonly': Eval('state') != 'draft',
|
||||
'invisible': (
|
||||
(Eval('state') != 'draft') & ~Eval('unit_loads', []))
|
||||
},
|
||||
depends=['state']),
|
||||
'get_unit_loads', setter='set_unit_loads',
|
||||
searcher='search_unit_loads')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(ShipmentInternal, cls).__setup__()
|
||||
for _field_name in ('moves', ):
|
||||
_field = getattr(cls, _field_name)
|
||||
if _field.states.get('readonly'):
|
||||
_field.states['readonly'] |= Eval('unit_loads')
|
||||
else:
|
||||
_field.states['readonly'] = Eval('unit_loads')
|
||||
if 'unit_loads' not in _field.depends:
|
||||
_field.depends.append('unit_loads')
|
||||
|
||||
def get_ul_quantity(self, name=None):
|
||||
if not self.unit_loads:
|
||||
|
@ -117,19 +117,8 @@ class ShipmentInternal(metaclass=PoolMeta):
|
|||
uls = set(m.unit_load.id for m in self.moves if m.unit_load)
|
||||
return list(uls)
|
||||
|
||||
@classmethod
|
||||
def set_unit_loads(cls, records, name, value):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def search_unit_loads(cls, name, clause):
|
||||
_field = 'moves.unit_load'
|
||||
if '.' in name:
|
||||
_field += '.%s' % name[10:]
|
||||
return [(_field,) + tuple(clause[1:])]
|
||||
|
||||
@fields.depends('unit_loads', 'moves', 'date_time_',
|
||||
'from_location', 'to_location')
|
||||
@fields.depends('unit_loads', 'moves', 'date_time_', 'from_location',
|
||||
'to_location')
|
||||
def on_change_unit_loads(self):
|
||||
moves = []
|
||||
ul_ids = [ul.id for ul in self.unit_loads]
|
||||
|
@ -145,34 +134,19 @@ class ShipmentInternal(metaclass=PoolMeta):
|
|||
moves.extend(new_moves)
|
||||
self.moves = moves
|
||||
|
||||
@classmethod
|
||||
def _get_ul_readonly_fields_name(cls):
|
||||
return ['moves', 'outgoing_moves', 'incoming_moves']
|
||||
|
||||
class ShipmentOutReturn(metaclass=PoolMeta):
|
||||
|
||||
class ShipmentOutReturn(ShipmentUnitLoadMixin, metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.out.return'
|
||||
|
||||
unit_loads = fields.Function(
|
||||
fields.One2Many('stock.unit_load', None, 'Unit loads',
|
||||
states={
|
||||
'readonly': (Eval('state') != 'draft') |
|
||||
Not(Bool('warehouse')),
|
||||
'invisible': (
|
||||
(Eval('state') != 'draft') & Not(Bool(
|
||||
Eval('unit_loads', []))))
|
||||
},
|
||||
depends=['state', 'warehouse']),
|
||||
'get_unit_loads', setter='set_unit_loads',
|
||||
searcher='search_unit_loads')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
for _field_name in ('inventory_moves', 'incoming_moves'):
|
||||
_field = getattr(cls, _field_name)
|
||||
if _field.states.get('readonly'):
|
||||
_field.states['readonly'] |= Eval('unit_loads')
|
||||
else:
|
||||
_field.states['readonly'] = Eval('unit_loads')
|
||||
if 'unit_loads' not in _field.depends:
|
||||
_field.depends.append('unit_loads')
|
||||
cls.unit_loads.states['readonly'] |= Not(Bool(Eval('warehouse')))
|
||||
cls.unit_loads.depends.append('warehouse')
|
||||
|
||||
def get_unit_loads(self, name=None):
|
||||
if not self.incoming_moves:
|
||||
|
@ -180,17 +154,6 @@ class ShipmentOutReturn(metaclass=PoolMeta):
|
|||
uls = set(m.unit_load.id for m in self.incoming_moves if m.unit_load)
|
||||
return list(uls)
|
||||
|
||||
@classmethod
|
||||
def set_unit_loads(cls, records, name, value):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def search_unit_loads(cls, name, clause):
|
||||
_field = 'moves.unit_load'
|
||||
if '.' in name:
|
||||
_field += '.%s' % name[10:]
|
||||
return [(_field, ) + tuple(clause[1:])]
|
||||
|
||||
@fields.depends('unit_loads', 'incoming_moves', 'effective_date',
|
||||
'planned_date', 'start_time', 'warehouse_input', 'customer_location')
|
||||
def on_change_unit_loads(self):
|
||||
|
@ -217,3 +180,38 @@ class ShipmentOutReturn(metaclass=PoolMeta):
|
|||
if move and incoming_move.unit_load:
|
||||
move.unit_load = incoming_move.unit_load
|
||||
return move
|
||||
|
||||
@classmethod
|
||||
def _get_ul_readonly_fields_name(cls):
|
||||
return ['inventory_moves', 'incoming_moves']
|
||||
|
||||
|
||||
class ShipmentInReturn(ShipmentUnitLoadMixin, metaclass=PoolMeta):
|
||||
__name__ = 'stock.shipment.in.return'
|
||||
|
||||
def get_unit_loads(self, name=None):
|
||||
if not self.moves:
|
||||
return []
|
||||
uls = set(m.unit_load.id for m in self.moves if m.unit_load)
|
||||
return list(uls)
|
||||
|
||||
@fields.depends('unit_loads', 'moves', 'from_location', 'to_location',
|
||||
'end_date')
|
||||
def on_change_unit_loads(self):
|
||||
moves = []
|
||||
ul_ids = [ul.id for ul in self.unit_loads]
|
||||
if self.moves:
|
||||
moves.extend(
|
||||
[m for m in self.moves if m.unit_load.id in ul_ids])
|
||||
for ul in self.unit_loads:
|
||||
if ul.id in [m.unit_load.id for m in moves]:
|
||||
continue
|
||||
new_moves = ul._move(self.to_location,
|
||||
self.end_date,
|
||||
from_location=self.from_location)
|
||||
moves.extend(new_moves)
|
||||
self.moves = moves
|
||||
|
||||
@classmethod
|
||||
def _get_ul_readonly_fields_name(cls):
|
||||
return ['moves']
|
||||
|
|
28
shipment.xml
28
shipment.xml
|
@ -22,6 +22,12 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="name">shipment_internal_form</field>
|
||||
<field name="inherit" ref="stock.shipment_internal_view_form"/>
|
||||
</record>
|
||||
<!-- Shipment in return -->
|
||||
<record model="ir.ui.view" id="shipment_in_return_view_form">
|
||||
<field name="model">stock.shipment.in.return</field>
|
||||
<field name="name">shipment_in_return_form</field>
|
||||
<field name="inherit" ref="stock.shipment_in_return_view_form"/>
|
||||
</record>
|
||||
|
||||
<!-- form relate -->
|
||||
<record model="ir.action.act_window" id="act_unit_loads_shipment_out">
|
||||
|
@ -54,6 +60,16 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="model">stock.unit_load,-1</field>
|
||||
<field name="action" ref="act_unit_loads_shipment_internal"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_unit_loads_shipment_in_return">
|
||||
<field name="name">Supplier Return Shipments</field>
|
||||
<field name="res_model">stock.shipment.in.return</field>
|
||||
<field name="domain" eval="[('unit_loads', 'in', Eval('active_ids'))]" pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_unit_loads_shipment_in_return_keyword1">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.unit_load,-1</field>
|
||||
<field name="action" ref="act_unit_loads_shipment_in_return"/>
|
||||
</record>
|
||||
|
||||
<!-- Form relate -->
|
||||
<record model="ir.action.act_window" id="act_relate_shipment_out_unit_load">
|
||||
|
@ -78,5 +94,17 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<field name="model">stock.shipment.out.return,-1</field>
|
||||
<field name="action" ref="act_relate_shipment_out_return_unit_load"/>
|
||||
</record>
|
||||
|
||||
<!-- Form relate -->
|
||||
<record model="ir.action.act_window" id="act_relate_shipment_in_return_unit_load">
|
||||
<field name="name">Unit loads</field>
|
||||
<field name="res_model">stock.unit_load</field>
|
||||
<field name="domain" eval="[('moves.shipment.id', 'in', Eval('active_ids'), 'stock.shipment.in.return')]" pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_relate_shipment_in_return_unit_load_keyword">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">stock.shipment.in.return,-1</field>
|
||||
<field name="action" ref="act_relate_shipment_in_return_unit_load"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
|
@ -0,0 +1,114 @@
|
|||
===========================
|
||||
Shipment In Return Scenario
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
>>> from proteus import Model, Wizard, Report
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||
... get_company
|
||||
>>> now = datetime.datetime.now()
|
||||
>>> tomorrow = now + relativedelta(days=1)
|
||||
|
||||
|
||||
Install unit load Module::
|
||||
|
||||
>>> config = activate_modules('stock_unit_load')
|
||||
|
||||
Create company::
|
||||
|
||||
>>> _ = create_company()
|
||||
>>> company = get_company()
|
||||
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Product = Model.get('product.product')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> product = Product()
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.cost_price = Decimal('8')
|
||||
>>> template.save()
|
||||
>>> product.template = template
|
||||
>>> product.save()
|
||||
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> production_loc, = Location.find([('type', '=', 'production')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
|
||||
|
||||
Create Supplier::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
|
||||
Create an unit load::
|
||||
|
||||
>>> UnitLoad = Model.get('stock.unit_load')
|
||||
>>> unit_load = UnitLoad()
|
||||
>>> unit_load.end_date = unit_load.start_date + relativedelta(minutes=5)
|
||||
>>> unit_load.production_type = 'location'
|
||||
>>> unit_load.production_location = production_loc
|
||||
>>> unit_load.product = product
|
||||
>>> unit_load.cases_quantity = 5
|
||||
>>> unit_load.quantity = Decimal('35.0')
|
||||
>>> move = unit_load.production_moves[0]
|
||||
>>> move.to_location = storage_loc
|
||||
>>> unit_load.save()
|
||||
>>> unit_load.click('assign')
|
||||
>>> unit_load.click('do')
|
||||
>>> len(unit_load.ul_moves)
|
||||
1
|
||||
|
||||
|
||||
Create a shipment in return::
|
||||
|
||||
>>> ShipmentInReturn = Model.get('stock.shipment.in.return')
|
||||
>>> shipment_in_return = ShipmentInReturn()
|
||||
>>> shipment_in_return.company = company
|
||||
>>> shipment_in_return.start_date = tomorrow
|
||||
>>> shipment_in_return.supplier = supplier
|
||||
>>> shipment_in_return.from_location = storage_loc
|
||||
>>> shipment_in_return.save()
|
||||
>>> len(shipment_in_return.moves)
|
||||
0
|
||||
|
||||
|
||||
Add unit load to shipment::
|
||||
|
||||
>>> shipment_in_return.unit_loads.append(unit_load)
|
||||
>>> len(shipment_in_return.moves)
|
||||
1
|
||||
>>> shipment_in_return.moves[0].unit_load == unit_load
|
||||
True
|
||||
>>> shipment_in_return.save()
|
||||
>>> shipment_in_return.click('wait')
|
||||
>>> shipment_in_return.click('assign_try')
|
||||
True
|
||||
>>> shipment_in_return.click('done')
|
||||
|
||||
|
||||
Check unit load::
|
||||
|
||||
>>> unit_load.reload()
|
||||
>>> len(unit_load.ul_moves)
|
||||
2
|
||||
>>> unit_load.state
|
||||
'done'
|
||||
>>> unit_load.location.id == shipment_in_return.to_location.id
|
||||
True
|
|
@ -36,5 +36,10 @@ def suite():
|
|||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite(
|
||||
'scenario_shipment_in_return.rst',
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
# TODO: uls by locations wizard
|
||||
return suite
|
||||
|
|
|
@ -1505,6 +1505,7 @@ class UnitLoad(ModelSQL, ModelView):
|
|||
'stock.shipment.out',
|
||||
'stock.shipment.out.return',
|
||||
'stock.shipment.internal',
|
||||
'stock.shipment.in.return',
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- The COPYRIGHT file at the top level of this repository contains the full
|
||||
copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook/page[@name='moves']" position="before">
|
||||
<page name="unit_loads" string="ULs">
|
||||
<field name="unit_loads" colspan="4" widget="many2many"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</data>
|
Loading…
Reference in New Issue