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
This commit is contained in:
+30
-22
@@ -84,21 +84,21 @@ class PurchaseRequest(metaclass=PoolMeta):
|
||||
], 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 @@ class PurchaseRequest(metaclass=PoolMeta):
|
||||
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 @@ class PurchaseRequest(metaclass=PoolMeta):
|
||||
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 @@ class PurchaseRequest(metaclass=PoolMeta):
|
||||
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}
|
||||
|
||||
@@ -38,6 +38,13 @@ Create parties::
|
||||
>>> 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 @@ Create product::
|
||||
>>> 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 @@ Get stock locations::
|
||||
>>> 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 @@ Create a need for missing product::
|
||||
>>> 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 @@ There is now a draft purchase request::
|
||||
>>> pr.product == product
|
||||
True
|
||||
>>> pr.quantity
|
||||
1.0
|
||||
2.0
|
||||
|
||||
Create an order point with negative minimal quantity::
|
||||
|
||||
@@ -168,22 +188,22 @@ Create an order point with negative minimal quantity::
|
||||
>>> 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 @@ There is now a draft purchase request::
|
||||
>>> 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 @@ There is now a draft purchase request::
|
||||
>>> 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'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user