817 lines
32 KiB
Diff
817 lines
32 KiB
Diff
exporting patch:
|
|
# HG changeset patch
|
|
# User Cédric Krier <ced@b2ck.com>
|
|
# Date 1466498190 -7200
|
|
# Tue Jun 21 10:36:30 2016 +0200
|
|
# Node ID 48fdd650ed0c3a42495711a1a16eaba2aef30bae
|
|
# Parent 6de0e55a67d0dc6211d7a7d74d561a5cc8686640
|
|
Add lead time and transit location for internal shipments between warehouses
|
|
|
|
issue5587
|
|
review25331002
|
|
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c __init__.py
|
|
--- a/trytond/trytond/modules/stock/__init__.py Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/__init__.py Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -16,6 +16,7 @@
|
|
Location,
|
|
Party,
|
|
ProductsByLocationsStart,
|
|
+ LocationLeadTime,
|
|
Move,
|
|
ShipmentIn,
|
|
ShipmentInReturn,
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c configuration.py
|
|
--- a/trytond/trytond/modules/stock/configuration.py Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/configuration.py Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -39,6 +39,10 @@
|
|
[Eval('context', {}).get('company', -1), None]),
|
|
('code', '=', 'stock.shipment.internal'),
|
|
], required=True))
|
|
+ shipment_internal_transit = fields.Property(fields.Many2One(
|
|
+ 'stock.location', 'Internal Shipment Transit', domain=[
|
|
+ ('type', '=', 'storage'),
|
|
+ ], required=True))
|
|
inventory_sequence = fields.Property(fields.Many2One(
|
|
'ir.sequence', 'Inventory Sequence', domain=[
|
|
('company', 'in',
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c doc/index.rst
|
|
--- a/trytond/trytond/modules/stock/doc/index.rst Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/doc/index.rst Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -50,6 +50,12 @@
|
|
Locations are organised in tree structures, allowing to define
|
|
fine grained structures.
|
|
|
|
+Location Lead Time
|
|
+------------------
|
|
+
|
|
+It allows to define the time needed for an *Internal Shipment* between two
|
|
+warehouses.
|
|
+
|
|
|
|
Move
|
|
****
|
|
diff -r 4c390625785a location.py
|
|
--- a/trytond/trytond/modules/stock/location.py Tue Jun 14 16:17:48 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/location.py Thu Aug 04 01:14:15 2016 +0200
|
|
@@ -2,7 +2,11 @@
|
|
# this repository contains the full copyright notices and license terms.
|
|
import datetime
|
|
from decimal import Decimal
|
|
-from trytond.model import ModelView, ModelSQL, fields
|
|
+
|
|
+from sql import Null
|
|
+from sql.conditionals import Case
|
|
+
|
|
+from trytond.model import ModelView, ModelSQL, MatchMixin, fields
|
|
from trytond.wizard import Wizard, StateView, Button, StateAction
|
|
from trytond import backend
|
|
from trytond.pyson import Eval, PYSONEncoder, Date, If
|
|
@@ -11,7 +15,7 @@
|
|
from trytond.tools import grouped_slice
|
|
|
|
__all__ = ['Location', 'Party', 'ProductsByLocationsStart',
|
|
- 'ProductsByLocations']
|
|
+ 'ProductsByLocations', 'LocationLeadTime']
|
|
|
|
STATES = {
|
|
'readonly': ~Eval('active'),
|
|
@@ -456,3 +460,38 @@
|
|
action['name'] += ' - (%s) @ %s' % (
|
|
','.join(l.name for l in locations), date)
|
|
return action, {}
|
|
+
|
|
+
|
|
+
|
|
+class LocationLeadTime(ModelSQL, ModelView, MatchMixin):
|
|
+ 'Location Lead Time'
|
|
+ __name__ = 'stock.location.lead_time'
|
|
+
|
|
+ sequence = fields.Integer('Sequence')
|
|
+ warehouse_from = fields.Many2One('stock.location', 'Warehouse From',
|
|
+ ondelete='CASCADE',
|
|
+ domain=[
|
|
+ ('type', '=', 'warehouse'),
|
|
+ ])
|
|
+ warehouse_to = fields.Many2One('stock.location', 'Warehouse To',
|
|
+ ondelete='CASCADE',
|
|
+ domain=[
|
|
+ ('type', '=', 'warehouse'),
|
|
+ ])
|
|
+ lead_time = fields.TimeDelta('Lead Time')
|
|
+
|
|
+ @classmethod
|
|
+ def __setup__(cls):
|
|
+ super(LocationLeadTime, cls).__setup__()
|
|
+ cls._order.insert(0, ('sequence', 'ASC'))
|
|
+
|
|
+ @classmethod
|
|
+ def order_sequence(cls, tables):
|
|
+ table, _ = tables[None]
|
|
+ return [Case((table.sequence == Null, 0), else_=1), table.sequence]
|
|
+
|
|
+ @classmethod
|
|
+ def get_lead_time(cls, pattern):
|
|
+ for record in cls.search([]):
|
|
+ if record.match(pattern):
|
|
+ return record.lead_time
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c location.xml
|
|
--- a/trytond/trytond/modules/stock/location.xml Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/location.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -121,6 +121,56 @@
|
|
</record>
|
|
|
|
|
|
+ <record model="ir.ui.view" id="location_lead_time_view_list">
|
|
+ <field name="model">stock.location.lead_time</field>
|
|
+ <field name="type">tree</field>
|
|
+ <field name="name">location_lead_time_list</field>
|
|
+ </record>
|
|
+
|
|
+ <record model="ir.ui.view" id="location_lead_time_view_form">
|
|
+ <field name="model">stock.location.lead_time</field>
|
|
+ <field name="type">form</field>
|
|
+ <field name="name">location_lead_time_form</field>
|
|
+ </record>
|
|
+
|
|
+ <record model="ir.action.act_window" id="act_location_lead_time_form">
|
|
+ <field name="name">Location Lead Times</field>
|
|
+ <field name="res_model">stock.location.lead_time</field>
|
|
+ </record>
|
|
+ <record model="ir.action.act_window.view"
|
|
+ id="act_location_lead_time_form_view1">
|
|
+ <field name="sequence" eval="10"/>
|
|
+ <field name="view" ref="location_lead_time_view_list"/>
|
|
+ <field name="act_window" ref="act_location_lead_time_form"/>
|
|
+ </record>
|
|
+ <record model="ir.action.act_window.view"
|
|
+ id="act_location_lead_time_form_view2">
|
|
+ <field name="sequence" eval="20"/>
|
|
+ <field name="view" ref="location_lead_time_view_form"/>
|
|
+ <field name="act_window" ref="act_location_lead_time_form"/>
|
|
+ </record>
|
|
+
|
|
+ <menuitem parent="menu_location_form"
|
|
+ action="act_location_lead_time_form"
|
|
+ id="menu_location_lead_time_form"/>
|
|
+
|
|
+ <record model="ir.model.access" id="access_location_lead_time">
|
|
+ <field name="model"
|
|
+ search="[('model', '=', 'stock.location.lead_time')]"/>
|
|
+ <field name="perm_read" eval="True"/>
|
|
+ <field name="perm_write" eval="False"/>
|
|
+ <field name="perm_create" eval="False"/>
|
|
+ <field name="perm_delete" eval="False"/>
|
|
+ </record>
|
|
+ <record model="ir.model.access" id="access_location_lead_time_admin">
|
|
+ <field name="model"
|
|
+ search="[('model', '=', 'stock.location.lead_time')]"/>
|
|
+ <field name="group" ref="group_stock_admin"/>
|
|
+ <field name="perm_read" eval="True"/>
|
|
+ <field name="perm_write" eval="True"/>
|
|
+ <field name="perm_create" eval="True"/>
|
|
+ <field name="perm_delete" eval="True"/>
|
|
+ </record>
|
|
</data>
|
|
<data noupdate="1">
|
|
<!-- Default locations -->
|
|
@@ -161,6 +211,10 @@
|
|
<field name="name">Lost and Found</field>
|
|
<field name="type">lost_found</field>
|
|
</record>
|
|
+ <record model="stock.location" id="location_transit">
|
|
+ <field name="name">Transit</field>
|
|
+ <field name="type">storage</field>
|
|
+ </record>
|
|
|
|
<record model="ir.property" id="property_supplier_location">
|
|
<field name="field"
|
|
@@ -174,5 +228,12 @@
|
|
('name', '=', 'customer_location')]"/>
|
|
<field name="value" eval="'stock.location,' + str(ref('location_customer'))"/>
|
|
</record>
|
|
+
|
|
+ <record model="ir.property" id="property_shipment_internal_transit">
|
|
+ <field name="field"
|
|
+ search="[('model.model', '=', 'stock.configuration'),
|
|
+ ('name', '=', 'shipment_internal_transit')]"/>
|
|
+ <field name="value" eval="'stock.location,' + str(ref('location_transit'))"/>
|
|
+ </record>
|
|
</data>
|
|
</tryton>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c shipment.py
|
|
--- a/trytond/trytond/modules/stock/shipment.py Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/shipment.py Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -4,7 +4,9 @@
|
|
import itertools
|
|
import functools
|
|
import datetime
|
|
-from sql import Table
|
|
+from collections import defaultdict
|
|
+
|
|
+from sql import Table, Null
|
|
from sql.functions import Overlay, Position
|
|
from sql.aggregate import Max
|
|
from sql.operators import Concat
|
|
@@ -13,7 +15,7 @@
|
|
from trytond.modules.company import CompanyReport
|
|
from trytond.wizard import Wizard, StateTransition, StateView, Button
|
|
from trytond import backend
|
|
-from trytond.pyson import Eval, If, Id
|
|
+from trytond.pyson import Eval, If, Id, Bool
|
|
from trytond.transaction import Transaction
|
|
from trytond.pool import Pool, PoolMeta
|
|
from trytond.tools import reduce_ids, grouped_slice
|
|
@@ -1819,6 +1821,17 @@
|
|
states={
|
|
'readonly': Eval('state') != 'draft',
|
|
}, depends=['state'])
|
|
+ effective_start_date = fields.Date('Effective Start Date',
|
|
+ states={
|
|
+ 'readonly': Eval('state').in_(['cancel', 'shipped', 'done']),
|
|
+ },
|
|
+ depends=['state'])
|
|
+ planned_start_date = fields.Date('Planned Start Date',
|
|
+ states={
|
|
+ 'readonly': ~Eval('state').in_(['draft']),
|
|
+ 'required': Bool(Eval('planned_date')),
|
|
+ },
|
|
+ depends=['state'])
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
states={
|
|
'readonly': Eval('state') != 'draft',
|
|
@@ -1846,32 +1859,88 @@
|
|
}, domain=[
|
|
('type', 'in', ['view', 'storage', 'lost_found']),
|
|
], depends=['state'])
|
|
+ transit_location = fields.Function(fields.Many2One('stock.location',
|
|
+ 'Transit Location'), 'on_change_with_transit_location')
|
|
moves = fields.One2Many('stock.move', 'shipment', 'Moves',
|
|
states={
|
|
'readonly': (Eval('state').in_(['cancel', 'assigned', 'done'])
|
|
| ~Eval('from_location') | ~Eval('to_location')),
|
|
+ 'invisible': (Bool(Eval('transit_location'))
|
|
+ & (Eval('state') != 'draft')),
|
|
},
|
|
domain=[
|
|
If(Eval('state') == 'draft', [
|
|
('from_location', '=', Eval('from_location')),
|
|
('to_location', '=', Eval('to_location')),
|
|
- ], [
|
|
+ ],
|
|
+ If(~Eval('transit_location'),
|
|
+ [
|
|
+ ('from_location', 'child_of',
|
|
+ [Eval('from_location', -1)], 'parent'),
|
|
+ ('to_location', 'child_of',
|
|
+ [Eval('to_location', -1)], 'parent'),
|
|
+ ],
|
|
+ ['OR',
|
|
+ [
|
|
+ ('from_location', 'child_of',
|
|
+ [Eval('from_location', -1)], 'parent'),
|
|
+ ('to_location', '=', Eval('transit_location')),
|
|
+ ],
|
|
+ [
|
|
+ ('from_location', '=', Eval('transit_location')),
|
|
+ ('to_location', 'child_of',
|
|
+ [Eval('to_location', -1)], 'parent'),
|
|
+ ],
|
|
+ ])),
|
|
+ ('company', '=', Eval('company')),
|
|
+ ],
|
|
+ depends=['state', 'from_location', 'to_location', 'transit_location',
|
|
+ 'company'])
|
|
+ outgoing_moves = fields.Function(fields.One2Many('stock.move', 'shipment',
|
|
+ 'Outgoing Moves',
|
|
+ domain=[
|
|
+ ('from_location', 'child_of', [Eval('from_location', -1)],
|
|
+ 'parent'),
|
|
+ If(~Eval('transit_location'),
|
|
+ ('to_location', 'child_of', [Eval('to_location', -1)],
|
|
+ 'parent'),
|
|
+ ('to_location', '=', Eval('transit_location'))),
|
|
+ ],
|
|
+ states={
|
|
+ 'readonly': Eval('state').in_(
|
|
+ ['assigned', 'shipped', 'done', 'cancel']),
|
|
+ 'invisible': (~Eval('transit_location')
|
|
+ | (Eval('state') == 'draft')),
|
|
+ },
|
|
+ depends=['from_location', 'to_location', 'transit_location',
|
|
+ 'state']),
|
|
+ 'get_outgoing_moves', setter='set_moves')
|
|
+ incoming_moves = fields.Function(fields.One2Many('stock.move', 'shipment',
|
|
+ 'Incoming Moves',
|
|
+ domain=[
|
|
+ If(~Eval('transit_location'),
|
|
('from_location', 'child_of', [Eval('from_location', -1)],
|
|
'parent'),
|
|
- ('to_location', 'child_of', [Eval('to_location', -1)],
|
|
- 'parent'),
|
|
- ]),
|
|
- ('company', '=', Eval('company')),
|
|
- ],
|
|
- depends=['state', 'from_location', 'to_location', 'planned_date',
|
|
- 'company'])
|
|
+ ('from_location', '=', Eval('transit_location'))),
|
|
+ ('to_location', 'child_of', [Eval('to_location', -1)],
|
|
+ 'parent'),
|
|
+ ],
|
|
+ states={
|
|
+ 'readonly': Eval('state').in_(['done', 'cancel']),
|
|
+ 'invisible': (~Eval('transit_location')
|
|
+ | (Eval('state') == 'draft')),
|
|
+ },
|
|
+ depends=['from_location', 'to_location', 'transit_location',
|
|
+ 'state']),
|
|
+ 'get_incoming_moves', setter='set_moves')
|
|
state = fields.Selection([
|
|
- ('draft', 'Draft'),
|
|
- ('cancel', 'Canceled'),
|
|
- ('assigned', 'Assigned'),
|
|
- ('waiting', 'Waiting'),
|
|
- ('done', 'Done'),
|
|
- ], 'State', readonly=True)
|
|
+ ('draft', 'Draft'),
|
|
+ ('cancel', 'Canceled'),
|
|
+ ('waiting', 'Waiting'),
|
|
+ ('assigned', 'Assigned'),
|
|
+ ('shipped', 'Shipped'),
|
|
+ ('done', 'Done'),
|
|
+ ], 'State', readonly=True)
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
@@ -1885,7 +1954,9 @@
|
|
('draft', 'waiting'),
|
|
('waiting', 'waiting'),
|
|
('waiting', 'assigned'),
|
|
+ ('assigned', 'shipped'),
|
|
('assigned', 'done'),
|
|
+ ('shipped', 'done'),
|
|
('waiting', 'draft'),
|
|
('assigned', 'waiting'),
|
|
('draft', 'cancel'),
|
|
@@ -1895,7 +1966,8 @@
|
|
))
|
|
cls._buttons.update({
|
|
'cancel': {
|
|
- 'invisible': Eval('state').in_(['cancel', 'done']),
|
|
+ 'invisible': Eval('state').in_(
|
|
+ ['cancel', 'shipped', 'done']),
|
|
},
|
|
'draft': {
|
|
'invisible': ~Eval('state').in_(['cancel', 'waiting']),
|
|
@@ -1912,8 +1984,15 @@
|
|
'tryton-clear',
|
|
'tryton-go-next')),
|
|
},
|
|
+ 'ship': {
|
|
+ 'invisible': ((Eval('state') != 'assigned') |
|
|
+ ~Eval('transit_location')),
|
|
+ },
|
|
'done': {
|
|
- 'invisible': Eval('state') != 'assigned',
|
|
+ 'invisible': If(
|
|
+ ~Eval('transit_location'),
|
|
+ Eval('state') != 'assigned',
|
|
+ Eval('state') != 'shipped'),
|
|
},
|
|
'assign_wizard': {
|
|
'invisible': Eval('state') != 'waiting',
|
|
@@ -1971,6 +2050,14 @@
|
|
where=red_sql))
|
|
table.not_null_action('company', action='add')
|
|
|
|
+ # Migration from 4.0: fill planned_start_date
|
|
+ cursor = Transaction().connection.cursor()
|
|
+ cursor.execute(*sql_table.update(
|
|
+ [sql_table.planned_start_date],
|
|
+ [sql_table.planned_date],
|
|
+ where=(sql_table.planned_start_date == Null)
|
|
+ & (sql_table.planned_date != Null)))
|
|
+
|
|
# Add index on create_date
|
|
table = TableHandler(cls, module_name)
|
|
table.index_action('create_date', action='add')
|
|
@@ -1983,6 +2070,59 @@
|
|
def default_company():
|
|
return Transaction().context.get('company')
|
|
|
|
+ @fields.depends('planned_date', 'planned_start_date')
|
|
+ def on_change_with_transit_location(self, name=None):
|
|
+ pool = Pool()
|
|
+ Config = pool.get('stock.configuration')
|
|
+ if self.planned_date != self.planned_start_date:
|
|
+ return Config(1).shipment_internal_transit.id
|
|
+
|
|
+ @fields.depends('planned_date', 'from_location', 'to_location')
|
|
+ def on_change_with_planned_start_date(self, pattern=None):
|
|
+ pool = Pool()
|
|
+ LocationLeadTime = pool.get('stock.location.lead_time')
|
|
+ if self.planned_date:
|
|
+ if pattern is None:
|
|
+ pattern = {}
|
|
+ pattern.setdefault('warehouse_from',
|
|
+ self.from_location.warehouse.id
|
|
+ if self.from_location and self.from_location.warehouse
|
|
+ else None)
|
|
+ pattern.setdefault('warehouse_to',
|
|
+ self.to_location.warehouse.id
|
|
+ if self.to_location and self.to_location.warehouse
|
|
+ else None)
|
|
+ lead_time = LocationLeadTime.get_lead_time(pattern)
|
|
+ if lead_time:
|
|
+ return self.planned_date - lead_time
|
|
+ return self.planned_date
|
|
+
|
|
+ def get_outgoing_moves(self, name):
|
|
+ if not self.transit_location:
|
|
+ return [m.id for m in self.moves]
|
|
+ moves = []
|
|
+ for move in self.moves:
|
|
+ if move.to_location == self.transit_location:
|
|
+ moves.append(move.id)
|
|
+ return moves
|
|
+
|
|
+ def get_incoming_moves(self, name):
|
|
+ if not self.transit_location:
|
|
+ return [m.id for m in self.moves]
|
|
+ moves = []
|
|
+ for move in self.moves:
|
|
+ if move.from_location == self.transit_location:
|
|
+ moves.append(move.id)
|
|
+ return moves
|
|
+
|
|
+ @classmethod
|
|
+ def set_moves(cls, shipments, name, value):
|
|
+ if not value:
|
|
+ return
|
|
+ cls.write(shipments, {
|
|
+ 'moves': value,
|
|
+ })
|
|
+
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
pool = Pool()
|
|
@@ -2009,13 +2149,76 @@
|
|
super(ShipmentInternal, cls).delete(shipments)
|
|
|
|
@classmethod
|
|
+ def copy(cls, shipments, default=None):
|
|
+ pool = Pool()
|
|
+ Move = pool.get('stock.move')
|
|
+
|
|
+ if default is None:
|
|
+ default = {}
|
|
+ else:
|
|
+ default = default.copy()
|
|
+ default['outgoing_moves'] = None
|
|
+ default['incoming_moves'] = None
|
|
+ default['moves'] = None
|
|
+ default.setdefault('number')
|
|
+ copies = super(ShipmentInternal, cls).copy(shipments, default=default)
|
|
+ for shipment, copy in zip(shipments, copies):
|
|
+ Move.copy(shipment.outgoing_moves, default={
|
|
+ 'shipment': str(copy),
|
|
+ 'from_location': shipment.from_location.id,
|
|
+ 'to_location': shipment.to_location.id,
|
|
+ 'planned_date': shipment.planned_date,
|
|
+ })
|
|
+ return copies
|
|
+
|
|
+ @classmethod
|
|
+ def _sync_moves(cls, shipments):
|
|
+ 'Synchronise incoming moves with outgoing moves'
|
|
+ pool = Pool()
|
|
+ Move = pool.get('stock.move')
|
|
+ Uom = pool.get('product.uom')
|
|
+ to_delete = []
|
|
+ to_save = []
|
|
+ for shipment in shipments:
|
|
+ product_qty = defaultdict(lambda: 0)
|
|
+ for move in shipment.outgoing_moves:
|
|
+ if move.state == 'cancel':
|
|
+ continue
|
|
+ product_qty[move.product] += Uom.compute_qty(
|
|
+ move.uom, move.quantity, move.product.default_uom,
|
|
+ round=False)
|
|
+
|
|
+ for move in shipment.incoming_moves:
|
|
+ if move.state == 'cancel':
|
|
+ continue
|
|
+ if product_qty[move.product] <= 0:
|
|
+ to_delete.append(move)
|
|
+ else:
|
|
+ quantity = Uom.compute_qty(
|
|
+ move.uom, move.quantity, move.product.default_uom,
|
|
+ round=False)
|
|
+ quantity = min(product_qty[move.product], quantity)
|
|
+ move.quantity = Uom.compute_qty(
|
|
+ move.product.default_uom, quantity, move.uom)
|
|
+ product_qty[move.product] -= quantity
|
|
+ to_save.append(move)
|
|
+
|
|
+ if to_save:
|
|
+ Move.save(to_save)
|
|
+ if to_delete:
|
|
+ Move.delete(to_delete)
|
|
+
|
|
+ @classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
def draft(cls, shipments):
|
|
Move = Pool().get('stock.move')
|
|
+
|
|
# First reset state to draft to allow update from and to location
|
|
Move.draft([m for s in shipments for m in s.moves
|
|
if m.state != 'staging'])
|
|
+ Move.delete([m for s in shipments for m in s.moves
|
|
+ if m.from_location == s.transit_location])
|
|
for shipment in shipments:
|
|
Move.write([m for m in shipment.moves
|
|
if m.state != 'done'], {
|
|
@@ -2029,28 +2232,69 @@
|
|
@Workflow.transition('waiting')
|
|
def wait(cls, shipments):
|
|
Move = Pool().get('stock.move')
|
|
+
|
|
Move.draft([m for s in shipments for m in s.moves])
|
|
+
|
|
+ direct = []
|
|
+ transit = []
|
|
+ for shipment in shipments:
|
|
+ if not shipment.transit_location:
|
|
+ direct.append(shipment)
|
|
+ else:
|
|
+ transit.append(shipment)
|
|
+
|
|
moves = []
|
|
- for shipment in shipments:
|
|
+ for shipment in direct:
|
|
for move in shipment.moves:
|
|
if move.state != 'done':
|
|
move.planned_date = shipment.planned_date
|
|
moves.append(move)
|
|
Move.save(moves)
|
|
|
|
+ to_write = []
|
|
+ for shipment in transit:
|
|
+ moves = [m for m in shipment.moves
|
|
+ if m.state != 'done'
|
|
+ and m.from_location != shipment.transit_location
|
|
+ and m.to_location != shipment.transit_location]
|
|
+ Move.copy(moves, default={
|
|
+ 'from_location': shipment.transit_location.id,
|
|
+ 'planned_date': shipment.planned_date,
|
|
+ })
|
|
+ to_write.append(moves)
|
|
+ to_write.append({
|
|
+ 'to_location': shipment.transit_location.id,
|
|
+ 'planned_date': shipment.planned_start_date,
|
|
+ })
|
|
+ if to_write:
|
|
+ Move.write(*to_write)
|
|
+ cls._sync_moves(transit)
|
|
+
|
|
@classmethod
|
|
@Workflow.transition('assigned')
|
|
def assign(cls, shipments):
|
|
pass
|
|
|
|
@classmethod
|
|
+ @Workflow.transition('shipped')
|
|
+ def ship(cls, shipments):
|
|
+ pool = Pool()
|
|
+ Move = pool.get('stock.move')
|
|
+ Date = pool.get('ir.date')
|
|
+ Move.do([m for s in shipments for m in s.outgoing_moves])
|
|
+ cls._sync_moves(shipments)
|
|
+ cls.write([s for s in shipments if not s.effective_start_date], {
|
|
+ 'effective_start_date': Date.today(),
|
|
+ })
|
|
+
|
|
+ @classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('done')
|
|
def done(cls, shipments):
|
|
pool = Pool()
|
|
Move = pool.get('stock.move')
|
|
Date = pool.get('ir.date')
|
|
- Move.do([m for s in shipments for m in s.moves])
|
|
+ Move.do([m for s in shipments for m in s.incoming_moves])
|
|
cls.write([s for s in shipments if not s.effective_date], {
|
|
'effective_date': Date.today(),
|
|
})
|
|
@@ -2071,7 +2315,7 @@
|
|
@ModelView.button
|
|
def assign_try(cls, shipments):
|
|
Move = Pool().get('stock.move')
|
|
- to_assign = [m for s in shipments for m in s.moves
|
|
+ to_assign = [m for s in shipments for m in s.outgoing_moves
|
|
if m.from_location.type != 'lost_found']
|
|
if not to_assign or Move.assign_try(to_assign):
|
|
cls.assign(shipments)
|
|
@@ -2083,7 +2327,7 @@
|
|
@ModelView.button
|
|
def assign_force(cls, shipments):
|
|
Move = Pool().get('stock.move')
|
|
- Move.assign([m for s in shipments for m in s.moves])
|
|
+ Move.assign([m for s in shipments for m in s.outgoing_moves])
|
|
cls.assign(shipments)
|
|
|
|
|
|
@@ -2106,7 +2350,7 @@
|
|
if not shipment_id:
|
|
return []
|
|
shipment = ShipmentInternal(shipment_id)
|
|
- return [x.id for x in shipment.moves if x.state == 'draft']
|
|
+ return [x.id for x in shipment.outgoing_moves if x.state == 'draft']
|
|
|
|
|
|
class AssignShipmentInternal(Wizard):
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c shipment.xml
|
|
--- a/trytond/trytond/modules/stock/shipment.xml Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/shipment.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -263,6 +263,13 @@
|
|
<field name="act_window" ref="act_shipment_internal_form"/>
|
|
</record>
|
|
<record model="ir.action.act_window.domain"
|
|
+ id="act_shipment_internal_form_domain_shipped">
|
|
+ <field name="name">Shipped</field>
|
|
+ <field name="sequence" eval="40"/>
|
|
+ <field name="domain" eval="[('state', '=', 'shipped')]" pyson="1"/>
|
|
+ <field name="act_window" ref="act_shipment_internal_form"/>
|
|
+ </record>
|
|
+ <record model="ir.action.act_window.domain"
|
|
id="act_shipment_internal_form_domain_all">
|
|
<field name="name">All</field>
|
|
<field name="sequence" eval="9999"/>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c tests/scenario_stock_shipment_internal.rst
|
|
--- a/trytond/trytond/modules/stock/tests/scenario_stock_shipment_internal.rst Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/tests/scenario_stock_shipment_internal.rst Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -12,6 +12,7 @@
|
|
... get_company
|
|
>>> today = datetime.date.today()
|
|
>>> yesterday = today - relativedelta(days=1)
|
|
+ >>> tomorrow = today + relativedelta(days=1)
|
|
|
|
Create database::
|
|
|
|
@@ -103,7 +104,7 @@
|
|
>>> move = lost_found_shipment.moves.new()
|
|
>>> move.product = product
|
|
>>> move.oum = unit
|
|
- >>> move.quantity = 1
|
|
+ >>> move.quantity = 2
|
|
>>> move.from_location = lost_found_loc
|
|
>>> move.to_location = internal_loc
|
|
>>> move.currency = company.currency
|
|
@@ -123,3 +124,54 @@
|
|
>>> shipment.click('done')
|
|
>>> shipment.state
|
|
u'done'
|
|
+
|
|
+Add lead time inside the warehouse::
|
|
+
|
|
+ >>> config.user = 1
|
|
+ >>> LeadTime = Model.get('stock.location.lead_time')
|
|
+ >>> lead_time = LeadTime()
|
|
+ >>> lead_time.from_warehouse = storage_loc.warehouse
|
|
+ >>> lead_time.to_warehouse = storage_loc.warehouse
|
|
+ >>> lead_time.lead_time = datetime.timedelta(1)
|
|
+ >>> lead_time.save()
|
|
+
|
|
+Create Internal Shipment with lead time::
|
|
+
|
|
+ >>> config.user = stock_user.id
|
|
+ >>> shipment = Shipment()
|
|
+ >>> shipment.planned_date = tomorrow
|
|
+ >>> shipment.from_location = internal_loc
|
|
+ >>> shipment.to_location = storage_loc
|
|
+ >>> shipment.planned_start_date == today
|
|
+ True
|
|
+ >>> move = shipment.moves.new()
|
|
+ >>> move.product = product
|
|
+ >>> move.quantity = 1
|
|
+ >>> move.from_location = internal_loc
|
|
+ >>> move.to_location = storage_loc
|
|
+ >>> shipment.click('wait')
|
|
+ >>> len(shipment.moves)
|
|
+ 2
|
|
+ >>> outgoing_move, = shipment.outgoing_moves
|
|
+ >>> outgoing_move.quantity
|
|
+ 1.0
|
|
+ >>> outgoing_move.from_location == internal_loc
|
|
+ True
|
|
+ >>> outgoing_move.to_location == shipment.transit_location
|
|
+ True
|
|
+ >>> incoming_move, = shipment.incoming_moves
|
|
+ >>> incoming_move.quantity
|
|
+ 1.0
|
|
+ >>> incoming_move.from_location == shipment.transit_location
|
|
+ True
|
|
+ >>> incoming_move.to_location == storage_loc
|
|
+ True
|
|
+
|
|
+ >>> shipment.click('assign_try')
|
|
+ True
|
|
+ >>> shipment.click('ship')
|
|
+ >>> shipment.outgoing_moves[0].state
|
|
+ u'done'
|
|
+ >>> shipment.click('done')
|
|
+ >>> shipment.incoming_moves[0].state
|
|
+ u'done'
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c view/configuration_form.xml
|
|
--- a/trytond/trytond/modules/stock/view/configuration_form.xml Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/view/configuration_form.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -13,6 +13,8 @@
|
|
<field name="shipment_out_return_sequence"/>
|
|
<label name="shipment_internal_sequence"/>
|
|
<field name="shipment_internal_sequence"/>
|
|
+ <label name="shipment_internal_transit"/>
|
|
+ <field name="shipment_internal_transit"/>
|
|
<separator id="inventory" colspan="4" string="Inventory"/>
|
|
<label name="inventory_sequence"/>
|
|
<field name="inventory_sequence"/>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c view/location_lead_time_form.xml
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/trytond/trytond/modules/stock/view/location_lead_time_form.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -0,0 +1,13 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
+this repository contains the full copyright notices and license terms. -->
|
|
+<form string="Location Lead Time" col="6">
|
|
+ <label name="warehouse_from"/>
|
|
+ <field name="warehouse_from"/>
|
|
+ <label name="warehouse_to"/>
|
|
+ <field name="warehouse_to"/>
|
|
+ <label name="sequence"/>
|
|
+ <field name="sequence"/>
|
|
+ <label name="lead_time"/>
|
|
+ <field name="lead_time"/>
|
|
+</form>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c view/location_lead_time_list.xml
|
|
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
|
|
+++ b/trytond/trytond/modules/stock/view/location_lead_time_list.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -0,0 +1,8 @@
|
|
+<?xml version="1.0"?>
|
|
+<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
+this repository contains the full copyright notices and license terms. -->
|
|
+<tree string="Location Lead Times" sequence="sequence" editable="bottom">
|
|
+ <field name="warehouse_from"/>
|
|
+ <field name="warehouse_to"/>
|
|
+ <field name="lead_time"/>
|
|
+</tree>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c view/shipment_internal_form.xml
|
|
--- a/trytond/trytond/modules/stock/view/shipment_internal_form.xml Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/view/shipment_internal_form.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -12,18 +12,33 @@
|
|
<field name="to_location"/>
|
|
<label name="planned_date"/>
|
|
<field name="planned_date"/>
|
|
+ <label name="planned_start_date"/>
|
|
+ <field name="planned_start_date"/>
|
|
<label name="effective_date"/>
|
|
<field name="effective_date"/>
|
|
+ <label name="effective_start_date"/>
|
|
+ <field name="effective_start_date"/>
|
|
<label name="company"/>
|
|
<field name="company"/>
|
|
- <field name="moves" colspan="4"/>
|
|
+ <notebook colspan="4">
|
|
+ <page name="moves">
|
|
+ <field name="moves" colspan="4"/>
|
|
+ </page>
|
|
+ <page name="outgoing_moves">
|
|
+ <field name="outgoing_moves" colspan="4"/>
|
|
+ </page>
|
|
+ <page name="incoming_moves">
|
|
+ <field name="incoming_moves" colspan="4"/>
|
|
+ </page>
|
|
+ </notebook>
|
|
<label name="state"/>
|
|
<field name="state"/>
|
|
- <group col="5" colspan="2" id="buttons">
|
|
+ <group col="6" colspan="2" id="buttons">
|
|
<button string="Cancel" name="cancel" icon="tryton-cancel"/>
|
|
<button string="Draft" name="draft"/>
|
|
<button string="Wait" name="wait"/>
|
|
<button string="Assign" name="assign_wizard" icon="tryton-go-next"/>
|
|
+ <button string="Ship" name="ship" icon="tryton-ok"/>
|
|
<button string="Done" name="done" icon="tryton-ok"/>
|
|
</group>
|
|
</form>
|
|
diff -r 6de0e55a67d0 -r 48fdd650ed0c view/shipment_internal_tree.xml
|
|
--- a/trytond/trytond/modules/stock/view/shipment_internal_tree.xml Wed Jun 15 10:45:54 2016 +0200
|
|
+++ b/trytond/trytond/modules/stock/view/shipment_internal_tree.xml Tue Jun 21 10:36:30 2016 +0200
|
|
@@ -4,6 +4,7 @@
|
|
<tree string="Internal Shipments">
|
|
<field name="number"/>
|
|
<field name="reference"/>
|
|
+ <field name="planned_start_date"/>
|
|
<field name="planned_date"/>
|
|
<field name="effective_date"/>
|
|
<field name="from_location"/>
|