changeset 732c7b588de3 in modules/production:default
details: 
https://hg.tryton.org/modules/production?cmd=changeset;node=732c7b588de3
description:
        Add cron task to reschedule past productions

        issue9264
        review325191002
diffstat:

 CHANGELOG                     |   1 +
 doc/index.rst                 |   7 ++++++
 ir.py                         |   1 +
 production.py                 |  50 +++++++++++++++++++++++++++++++++++-------
 tests/scenario_production.rst |  14 +++++++++++-
 5 files changed, 63 insertions(+), 10 deletions(-)

diffs (136 lines):

diff -r 56d0e5a4d621 -r 732c7b588de3 CHANGELOG
--- a/CHANGELOG Sun Oct 11 23:46:52 2020 +0200
+++ b/CHANGELOG Mon Oct 12 19:20:21 2020 +0200
@@ -1,3 +1,4 @@
+* Add cron task to reschedule past productions
 * Allow lost_found as output of production
 * Keep cost of unused input products
 * Use the shipment assign wizard for production
diff -r 56d0e5a4d621 -r 732c7b588de3 doc/index.rst
--- a/doc/index.rst     Sun Oct 11 23:46:52 2020 +0200
+++ b/doc/index.rst     Mon Oct 12 19:20:21 2020 +0200
@@ -63,3 +63,10 @@
 
 A cron task runs every day and updates the cost of productions if the cost
 price of the incoming products has changed.
+
+
+Rescheduling Production
+----------------------
+
+It is possible to setup a cron task to reschedule the productions that are
+planned to start in the past. By default they are rescheduled to today.
diff -r 56d0e5a4d621 -r 732c7b588de3 ir.py
--- a/ir.py     Sun Oct 11 23:46:52 2020 +0200
+++ b/ir.py     Mon Oct 12 19:20:21 2020 +0200
@@ -11,4 +11,5 @@
         super().__setup__()
         cls.method.selection.extend([
                 ('production|set_cost_from_moves', "Set Cost from Moves"),
+                ('production|reschedule', "Reschedule Productions"),
                 ])
diff -r 56d0e5a4d621 -r 732c7b588de3 production.py
--- a/production.py     Sun Oct 11 23:46:52 2020 +0200
+++ b/production.py     Mon Oct 12 19:20:21 2020 +0200
@@ -1,6 +1,7 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
 from collections import defaultdict
+from datetime import timedelta
 from decimal import Decimal
 from itertools import chain
 
@@ -294,20 +295,32 @@
     def default_company():
         return Transaction().context.get('company')
 
-    @fields.depends('planned_date', 'product', 'bom')
-    def on_change_with_planned_start_date(self, pattern=None):
-        if self.planned_date and self.product:
-            if pattern is None:
-                pattern = {}
+    @fields.depends('product', 'bom')
+    def compute_lead_time(self, pattern=None):
+        if pattern is None:
+            pattern = {}
+        if self.product:
             pattern.setdefault('bom', self.bom.id if self.bom else None)
             for line in self.product.lead_times:
                 if line.match(pattern):
-                    if line.lead_time:
-                        return self.planned_date - line.lead_time
-                    else:
-                        return self.planned_date
+                    return line.lead_time or timedelta()
+        return timedelta()
+
+    @fields.depends('planned_date', methods=['compute_lead_time'])
+    def on_change_with_planned_start_date(self, pattern=None):
+        if self.planned_date and self.product:
+            return self.planned_date - self.compute_lead_time()
         return self.planned_date
 
+    @fields.depends(
+        'planned_date', 'planned_start_date', methods=['compute_lead_time'])
+    def on_change_planned_start_date(self, pattern=None):
+        if self.planned_start_date and self.product:
+            planned_date = self.planned_start_date + self.compute_lead_time()
+            if (not self.planned_date
+                    or self.planned_date < planned_date):
+                self.planned_date = planned_date
+
     @classmethod
     def _get_origin(cls):
         'Return list of Model names for origin Reference'
@@ -748,3 +761,22 @@
             return True
         else:
             return False
+
+    @classmethod
+    def _get_reschedule_domain(cls, date):
+        return [
+            ('state', '=', 'waiting'),
+            ('planned_start_date', '<', date),
+            ]
+
+    @classmethod
+    def reschedule(cls, date=None):
+        pool = Pool()
+        Date = pool.get('ir.date')
+        if date is None:
+            date = Date.today()
+        productions = cls.search(cls._get_reschedule_domain(date))
+        for production in productions:
+            production.planned_start_date = date
+            production.on_change_planned_start_date()
+        cls.save(productions)
diff -r 56d0e5a4d621 -r 732c7b588de3 tests/scenario_production.rst
--- a/tests/scenario_production.rst     Sun Oct 11 23:46:52 2020 +0200
+++ b/tests/scenario_production.rst     Mon Oct 12 19:20:21 2020 +0200
@@ -210,12 +210,24 @@
     >>> bom_output.quantity = 0.0
     >>> bom.save()
     >>> production = Production()
-    >>> production.effective_date = yesterday
     >>> production.product = product
     >>> production.bom = bom
+    >>> production.planned_start_date = yesterday
     >>> production.quantity = 2
     >>> [i.quantity for i in production.inputs]
     [0.0, 0.0]
     >>> output, = production.outputs
     >>> output.quantity
     0.0
+
+Reschedule productions::
+
+    >>> production.click('wait')
+    >>> Cron = Model.get('ir.cron')
+    >>> cron = Cron(method='production|reschedule')
+    >>> cron.interval_number = 1
+    >>> cron.interval_type = 'months'
+    >>> cron.click('run_once')
+    >>> production.reload()
+    >>> production.planned_start_date == today
+    True

Reply via email to