changeset d4d89d93371a in modules/stock_supply:default
details: 
https://hg.tryton.org/modules/stock_supply?cmd=changeset&node=d4d89d93371a
description:
        Compute shortage with a single call to products_by_location

        By grouping by date we can make a single call over the full date range.

        issue11640
        review437251003
diffstat:

 purchase_request.py                              |  52 +++++++++++++----------
 tests/scenario_stock_supply_purchase_request.rst |  38 ++++++++++++----
 2 files changed, 58 insertions(+), 32 deletions(-)

diffs (212 lines):

diff -r 3819439a2560 -r d4d89d93371a purchase_request.py
--- a/purchase_request.py       Thu May 19 22:03:45 2022 +0200
+++ b/purchase_request.py       Thu Sep 08 13:14:03 2022 +0200
@@ -84,21 +84,21 @@
                     ], order=[('id', 'ASC')])
         product_ids = [p.id for p in products]
         # aggregate product by minimum supply date
-        date2products = {}
+        date2products = defaultdict(list)
         for product in products:
             min_date, max_date = cls.get_supply_dates(
                 product, company=company.id)
-            date2products.setdefault((min_date, max_date), []).append(product)
+            date2products[min_date, max_date].append(product)
 
         # compute requests
         new_requests = []
-        for dates, dates_products in date2products.items():
-            min_date, max_date = dates
+        for (min_date, max_date), dates_products in date2products.items():
             for sub_products in grouped_slice(dates_products):
                 sub_products = list(sub_products)
                 product_ids = [p.id for p in sub_products]
-                with Transaction().set_context(forecast=True,
-                        stock_date_end=min_date or datetime.date.max):
+                with Transaction().set_context(
+                        forecast=True,
+                        stock_date_end=min_date):
                     pbl = Product.products_by_location(warehouse_ids,
                         with_childs=True, grouping_filter=(product_ids,))
                 for warehouse_id in warehouse_ids:
@@ -111,8 +111,9 @@
                         p.id for p in sub_products
                         if (warehouse_id, p.id) not in product2ops_other]
                     # Search for shortage between min-max
-                    shortages = cls.get_shortage(warehouse_id, product_ids,
-                        min_date, max_date, min_date_qties=min_date_qties,
+                    shortages = cls.get_shortage(
+                        warehouse_id, product_ids, min_date, max_date,
+                        min_date_qties=min_date_qties,
                         order_points=product2ops)
 
                     for product in sub_products:
@@ -307,18 +308,30 @@
         res_dates = {}
         res_qties = {}
 
-        min_quantities = {}
+        min_quantities = defaultdict(float)
         for product_id in product_ids:
             order_point = order_points.get((location_id, product_id))
             if order_point:
                 min_quantities[product_id] = order_point.min_quantity
-            else:
-                min_quantities[product_id] = 0.0
+
+        with Transaction().set_context(
+                forecast=True,
+                stock_date_start=min_date,
+                stock_date_end=max_date):
+            pbl = Product.products_by_location(
+                [location_id], with_childs=True,
+                grouping=('date', 'product'),
+                grouping_filter=(None, product_ids))
+        pbl_dates = defaultdict(dict)
+        for key, qty in pbl.items():
+            date, product_id = key[1:]
+            pbl_dates[date][product_id] = qty
 
         current_date = min_date
         current_qties = min_date_qties.copy()
+        products_to_check = product_ids.copy()
         while (current_date < max_date) or (current_date == min_date):
-            for product_id in product_ids:
+            for product_id in products_to_check:
                 current_qty = current_qties[product_id]
                 min_quantity = min_quantities[product_id]
                 res_qty = res_qties.get(product_id)
@@ -333,15 +346,10 @@
                 break
             current_date += datetime.timedelta(1)
 
-            # Update current quantities with next moves
-            with Transaction().set_context(forecast=True,
-                    stock_date_start=current_date,
-                    stock_date_end=current_date):
-                pbl = Product.products_by_location([location_id],
-                    with_childs=True, grouping_filter=(product_ids,))
-            for key, qty in pbl.items():
-                _, product_id = key
+            pbl = pbl_dates[current_date]
+            products_to_check.clear()
+            for product_id, qty in pbl.items():
                 current_qties[product_id] += qty
+                products_to_check.append(product_id)
 
-        return dict((x, (res_dates.get(x), res_qties.get(x)))
-            for x in product_ids)
+        return {x: (res_dates.get(x), res_qties.get(x)) for x in product_ids}
diff -r 3819439a2560 -r d4d89d93371a 
tests/scenario_stock_supply_purchase_request.rst
--- a/tests/scenario_stock_supply_purchase_request.rst  Thu May 19 22:03:45 
2022 +0200
+++ b/tests/scenario_stock_supply_purchase_request.rst  Thu Sep 08 13:14:03 
2022 +0200
@@ -38,6 +38,13 @@
     >>> supplier = Party(name='Supplier')
     >>> supplier.save()
 
+Configure supply period::
+
+    >>> PurchaseConfig = Model.get('purchase.configuration')
+    >>> purchase_config = PurchaseConfig(1)
+    >>> purchase_config.supply_period = datetime.timedelta(days=30)
+    >>> purchase_config.save()
+
 Create stock admin user::
 
     >>> User = Model.get('res.user')
@@ -107,6 +114,15 @@
     >>> template.save()
     >>> product, = template.products
 
+Define a product supplier::
+
+    >>> set_user(purchase_user)
+    >>> ProductSupplier = Model.get('purchase.product_supplier')
+    >>> product_supplier = ProductSupplier(template=template)
+    >>> product_supplier.party = supplier
+    >>> product_supplier.lead_time = datetime.timedelta(days=1)
+    >>> product_supplier.save()
+
 Get stock locations::
 
     >>> set_user(stock_admin_user)
@@ -117,7 +133,7 @@
     >>> output_loc, = Location.find([('code', '=', 'OUT')])
     >>> storage_loc, = Location.find([('code', '=', 'STO')])
 
-Create a need for missing product::
+Create needs for missing product::
 
     >>> set_user(stock_user)
     >>> ShipmentOut = Model.get('stock.shipment.out')
@@ -138,6 +154,10 @@
     >>> move.currency = company.currency
     >>> shipment_out.click('wait')
 
+    >>> shipment_out, = shipment_out.duplicate(
+    ...     default={'planned_date': today + datetime.timedelta(days=10)})
+    >>> shipment_out.click('wait')
+
 There is no purchase request::
 
     >>> PurchaseRequest = Model.get('purchase.request')
@@ -158,7 +178,7 @@
     >>> pr.product == product
     True
     >>> pr.quantity
-    1.0
+    2.0
 
 Create an order point with negative minimal quantity::
 
@@ -168,22 +188,22 @@
     >>> order_point.type = 'purchase'
     >>> order_point.product = product
     >>> order_point.warehouse_location = warehouse_loc
-    >>> order_point.min_quantity = -1
+    >>> order_point.min_quantity = -2
     >>> order_point.target_quantity = 10
     >>> order_point.save()
 
-Create production request::
+Create purchase request::
 
     >>> create_pr = Wizard('stock.supply')
     >>> create_pr.execute('create_')
 
-There is no more production request::
+There is no more purchase request::
 
     >>> set_user(purchase_user)
     >>> PurchaseRequest.find([])
     []
 
-Set a postive minimal quantity on order point create purchase request::
+Set a positive minimal quantity on order point create purchase request::
 
     >>> set_user(stock_admin_user)
     >>> order_point.min_quantity = 5
@@ -198,7 +218,7 @@
     >>> pr.product == product
     True
     >>> pr.quantity
-    11.0
+    12.0
 
 Using zero as minimal quantity on order point also creates purchase request::
 
@@ -215,13 +235,11 @@
     >>> pr.product == product
     True
     >>> pr.quantity
-    11.0
+    12.0
 
 Re-run with purchased request::
 
     >>> create_purchase = Wizard('purchase.request.create_purchase', [pr])
-    >>> create_purchase.form.party = supplier
-    >>> create_purchase.execute('start')
     >>> pr.state
     'purchased'
 

Reply via email to