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'