diff --git a/CHANGELOG b/CHANGELOG
index 226b7fdd5d..05f2c27047 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Allow shipments to be automatically assigned
* Rename inventory count quantity_added to quantity
* Add document source on move of product quantities by warehouse
* Add relate from assigned shipment to products quantities by warehouse
diff --git a/ir.py b/ir.py
index 5238c64248..e6b83dd701 100644
--- a/ir.py
+++ b/ir.py
@@ -18,4 +18,8 @@ class Cron(metaclass=PoolMeta):
"Reschedule Supplier Return Shipments"),
('stock.shipment.internal|reschedule',
"Reschedule Internal Shipments"),
+ ('stock.shipment.out|assign_cron',
+ "Assign Customer Shipments"),
+ ('stock.shipment.internal|assign_cron',
+ "Assign Internal Shipments"),
])
diff --git a/move.py b/move.py
index 049bc6998e..c378285f2a 100644
--- a/move.py
+++ b/move.py
@@ -301,7 +301,8 @@ class Move(Workflow, ModelSQL, ModelView):
'on_change_with_cost_price_required')
assignation_required = fields.Function(
fields.Boolean('Assignation Required'),
- 'on_change_with_assignation_required')
+ 'on_change_with_assignation_required',
+ searcher='search_assignation_required')
@classmethod
def __setup__(cls):
@@ -488,6 +489,29 @@ class Move(Workflow, ModelSQL, ModelView):
self.quantity
and self.from_location.type in {'storage', 'view'})
+ @classmethod
+ def search_assignation_required(cls, name, clause):
+ operators = {
+ '=': 'in',
+ '!=': 'not in',
+ }
+ reverse = {
+ '=': '!=',
+ '!=': '=',
+ }
+ if clause[1] in operators:
+ if not clause[2]:
+ operator = reverse[clause[1]]
+ else:
+ operator = clause[1]
+ return [
+ ('quantity', '!=', 0),
+ ('from_location.type', operators[operator], [
+ 'storage', 'view']),
+ ]
+ else:
+ return []
+
@staticmethod
def _get_shipment():
'Return list of Model names for shipment Reference'
diff --git a/shipment.py b/shipment.py
index 329b37f569..735cedaba3 100644
--- a/shipment.py
+++ b/shipment.py
@@ -30,6 +30,12 @@ class ShipmentMixin:
class ShipmentAssignMixin(ShipmentMixin):
+ _assign_moves_field = None
+
+ partially_assigned = fields.Function(
+ fields.Boolean("Partially Assigned"),
+ 'get_partially_assigned',
+ searcher='search_partially_assigned')
@classmethod
def assign_wizard(cls, shipments):
@@ -37,7 +43,7 @@ class ShipmentAssignMixin(ShipmentMixin):
@property
def assign_moves(self):
- raise NotImplementedError
+ return getattr(self, self._assign_moves_field)
@dualmethod
@ModelView.button
@@ -65,6 +71,48 @@ class ShipmentAssignMixin(ShipmentMixin):
})
cls.assign(shipments)
+ @classmethod
+ def _get_assign_domain(cls):
+ pool = Pool()
+ Date = pool.get('ir.date')
+ return [
+ ('state', '=', 'waiting'),
+ ('planned_date', '=', Date.today()),
+ ]
+
+ @classmethod
+ def assign_cron(cls):
+ shipments = cls.search(cls._get_assign_domain())
+ cls.assign_try(shipments)
+
+ def get_partially_assigned(self, name):
+ return any(m.state == 'assigned' for m in self.assign_moves
+ if m.assignation_required)
+
+ @classmethod
+ def search_partially_assigned(cls, name, clause):
+ operators = {
+ '=': 'where',
+ '!=': 'not where',
+ }
+ reverse = {
+ '=': '!=',
+ '!=': '=',
+ }
+ if clause[1] in operators:
+ if not clause[2]:
+ operator = reverse[clause[1]]
+ else:
+ operator = clause[1]
+ return [
+ (cls._assign_moves_field, operators[operator], [
+ ('state', '=', 'assigned'),
+ ('assignation_required', '=', True),
+ ]),
+ ]
+ else:
+ return []
+
class ShipmentIn(ShipmentMixin, Workflow, ModelSQL, ModelView):
"Supplier Shipment"
@@ -515,6 +563,8 @@ class ShipmentInReturn(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Supplier Return Shipment"
__name__ = 'stock.shipment.in.return'
_rec_name = 'number'
+ _assign_moves_field = 'moves'
+
effective_date = fields.Date('Effective Date',
states={
'readonly': Eval('state').in_(['cancelled', 'done']),
@@ -830,10 +880,6 @@ class ShipmentInReturn(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
def assign_wizard(cls, shipments):
pass
- @property
- def assign_moves(self):
- return self.moves
-
@dualmethod
@ModelView.button
def assign_try(cls, shipments, with_childs=None):
@@ -880,6 +926,7 @@ class ShipmentOut(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Customer Shipment"
__name__ = 'stock.shipment.out'
_rec_name = 'number'
+ _assign_moves_field = 'moves'
effective_date = fields.Date('Effective Date',
states={
'readonly': Eval('state').in_(['cancelled', 'done']),
@@ -1934,6 +1981,7 @@ class ShipmentInternal(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
"Internal Shipment"
__name__ = 'stock.shipment.internal'
_rec_name = 'number'
+ _assign_moves_field = 'moves'
effective_date = fields.Date('Effective Date',
states={
'readonly': Eval('state').in_(['cancelled', 'done']),
diff --git a/shipment.xml b/shipment.xml
index 73fb7ec206..7e2d8eb029 100644
--- a/shipment.xml
+++ b/shipment.xml
@@ -92,14 +92,6 @@ this repository contains the full copyright notices and license terms. -->
-
- Assigned
-
-
-
-
-
Waiting
@@ -108,6 +100,22 @@ this repository contains the full copyright notices and license terms. -->
+
+ Partially Assigned
+
+
+
+
+
+
+ Assigned
+
+
+
+
+
All
@@ -163,23 +171,30 @@ this repository contains the full copyright notices and license terms. -->
+
+ Partially Assigned
+
+
+
+
+
Assigned
-
+
Picked
-
+
Packed
-
+
@@ -247,10 +262,18 @@ this repository contains the full copyright notices and license terms. -->
+
+ Partially Assigned
+
+
+
+
+
Assigned
-
+
@@ -258,7 +281,7 @@ this repository contains the full copyright notices and license terms. -->
Shipped
-
+