From 7370ce43b95c92fe7f381447d4392cdba6dbbe1c Mon Sep 17 00:00:00 2001 From: Sergi Almacellas Abellana Date: Mon, 12 Apr 2021 18:33:33 +0200 Subject: [PATCH] Allow shipments to be automatically assigned issue9513 review318011002 --- CHANGELOG | 1 + ir.py | 4 ++++ move.py | 26 ++++++++++++++++++++++- shipment.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++----- shipment.xml | 49 ++++++++++++++++++++++++++++++++------------ 5 files changed, 119 insertions(+), 19 deletions(-) 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 - +