perf: timeout for auto material request through reorder level
(cherry picked from commit 951023f434a36ef03f874b3dcbd4f995168b7b5a)
This commit is contained in:
parent
3eac436e7b
commit
1b2831bdfe
@ -602,7 +602,7 @@ class SellingController(StockController):
|
|||||||
if self.doctype in ["Sales Order", "Quotation"]:
|
if self.doctype in ["Sales Order", "Quotation"]:
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
item.gross_profit = flt(
|
item.gross_profit = flt(
|
||||||
((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)
|
((item.base_rate - flt(item.valuation_rate)) * item.stock_qty), self.precision("amount", item)
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_customer_address(self):
|
def set_customer_address(self):
|
||||||
|
@ -1776,6 +1776,48 @@ class TestStockEntry(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, se1.cancel)
|
self.assertRaises(frappe.ValidationError, se1.cancel)
|
||||||
|
|
||||||
|
def test_auto_reorder_level(self):
|
||||||
|
from erpnext.stock.reorder_item import reorder_item
|
||||||
|
|
||||||
|
item_doc = make_item(
|
||||||
|
"Test Auto Reorder Item - 001",
|
||||||
|
properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1},
|
||||||
|
uoms=[{"uom": "Nos", "conversion_factor": 5}],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not frappe.db.exists("Item Reorder", {"parent": item_doc.name}):
|
||||||
|
item_doc.append(
|
||||||
|
"reorder_levels",
|
||||||
|
{
|
||||||
|
"warehouse_reorder_level": 0,
|
||||||
|
"warehouse_reorder_qty": 10,
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"material_request_type": "Purchase",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
item_doc.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
frappe.db.set_single_value("Stock Settings", "auto_indent", 1)
|
||||||
|
|
||||||
|
mr_list = reorder_item()
|
||||||
|
|
||||||
|
frappe.db.set_single_value("Stock Settings", "auto_indent", 0)
|
||||||
|
mrs = frappe.get_all(
|
||||||
|
"Material Request Item",
|
||||||
|
fields=["qty", "stock_uom", "stock_qty"],
|
||||||
|
filters={"item_code": item_doc.name, "uom": "Nos"},
|
||||||
|
)
|
||||||
|
|
||||||
|
for mri in mrs:
|
||||||
|
self.assertEqual(mri.stock_uom, "Kg")
|
||||||
|
self.assertEqual(mri.stock_qty, 10)
|
||||||
|
self.assertEqual(mri.qty, 2)
|
||||||
|
|
||||||
|
for mr in mr_list:
|
||||||
|
mr.cancel()
|
||||||
|
mr.delete()
|
||||||
|
|
||||||
|
|
||||||
def make_serialized_item(**args):
|
def make_serialized_item(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
@ -86,6 +86,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
|
|||||||
|
|
||||||
get_party_item_code(args, item, out)
|
get_party_item_code(args, item, out)
|
||||||
|
|
||||||
|
if args.get("doctype") in ["Sales Order", "Quotation"]:
|
||||||
set_valuation_rate(out, args)
|
set_valuation_rate(out, args)
|
||||||
|
|
||||||
update_party_blanket_order(args, out)
|
update_party_blanket_order(args, out)
|
||||||
@ -269,7 +270,9 @@ def get_basic_details(args, item, overwrite_warehouse=True):
|
|||||||
if not item:
|
if not item:
|
||||||
item = frappe.get_doc("Item", args.get("item_code"))
|
item = frappe.get_doc("Item", args.get("item_code"))
|
||||||
|
|
||||||
if item.variant_of and not item.taxes:
|
if (
|
||||||
|
item.variant_of and not item.taxes and frappe.db.exists("Item Tax", {"parent": item.variant_of})
|
||||||
|
):
|
||||||
item.update_template_tables()
|
item.update_template_tables()
|
||||||
|
|
||||||
item_defaults = get_item_defaults(item.name, args.company)
|
item_defaults = get_item_defaults(item.name, args.company)
|
||||||
|
@ -34,73 +34,157 @@ def _reorder_item():
|
|||||||
erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
|
erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
|
||||||
)
|
)
|
||||||
|
|
||||||
items_to_consider = frappe.db.sql_list(
|
items_to_consider = get_items_for_reorder()
|
||||||
"""select name from `tabItem` item
|
|
||||||
where is_stock_item=1 and has_variants=0
|
|
||||||
and disabled=0
|
|
||||||
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
|
|
||||||
and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
|
|
||||||
or (variant_of is not null and variant_of != ''
|
|
||||||
and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
|
|
||||||
)""",
|
|
||||||
{"today": nowdate()},
|
|
||||||
)
|
|
||||||
|
|
||||||
if not items_to_consider:
|
if not items_to_consider:
|
||||||
return
|
return
|
||||||
|
|
||||||
item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
|
item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
|
||||||
|
|
||||||
def add_to_material_request(
|
def add_to_material_request(**kwargs):
|
||||||
item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None
|
if isinstance(kwargs, dict):
|
||||||
):
|
kwargs = frappe._dict(kwargs)
|
||||||
if warehouse not in warehouse_company:
|
|
||||||
|
if kwargs.warehouse not in warehouse_company:
|
||||||
# a disabled warehouse
|
# a disabled warehouse
|
||||||
return
|
return
|
||||||
|
|
||||||
reorder_level = flt(reorder_level)
|
reorder_level = flt(kwargs.reorder_level)
|
||||||
reorder_qty = flt(reorder_qty)
|
reorder_qty = flt(kwargs.reorder_qty)
|
||||||
|
|
||||||
# projected_qty will be 0 if Bin does not exist
|
# projected_qty will be 0 if Bin does not exist
|
||||||
if warehouse_group:
|
if kwargs.warehouse_group:
|
||||||
projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group))
|
projected_qty = flt(
|
||||||
|
item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse_group)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse))
|
projected_qty = flt(
|
||||||
|
item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse)
|
||||||
|
)
|
||||||
|
|
||||||
if (reorder_level or reorder_qty) and projected_qty <= reorder_level:
|
if (reorder_level or reorder_qty) and projected_qty <= reorder_level:
|
||||||
deficiency = reorder_level - projected_qty
|
deficiency = reorder_level - projected_qty
|
||||||
if deficiency > reorder_qty:
|
if deficiency > reorder_qty:
|
||||||
reorder_qty = deficiency
|
reorder_qty = deficiency
|
||||||
|
|
||||||
company = warehouse_company.get(warehouse) or default_company
|
company = warehouse_company.get(kwargs.warehouse) or default_company
|
||||||
|
|
||||||
material_requests[material_request_type].setdefault(company, []).append(
|
material_requests[kwargs.material_request_type].setdefault(company, []).append(
|
||||||
{"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty}
|
{
|
||||||
|
"item_code": kwargs.item_code,
|
||||||
|
"warehouse": kwargs.warehouse,
|
||||||
|
"reorder_qty": reorder_qty,
|
||||||
|
"item_details": kwargs.item_details,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
for item_code in items_to_consider:
|
for item_code, reorder_levels in items_to_consider.items():
|
||||||
item = frappe.get_doc("Item", item_code)
|
for d in reorder_levels:
|
||||||
|
if d.has_variants:
|
||||||
|
continue
|
||||||
|
|
||||||
if item.variant_of and not item.get("reorder_levels"):
|
|
||||||
item.update_template_tables()
|
|
||||||
|
|
||||||
if item.get("reorder_levels"):
|
|
||||||
for d in item.get("reorder_levels"):
|
|
||||||
add_to_material_request(
|
add_to_material_request(
|
||||||
item_code,
|
item_code=item_code,
|
||||||
d.warehouse,
|
warehouse=d.warehouse,
|
||||||
d.warehouse_reorder_level,
|
reorder_level=d.warehouse_reorder_level,
|
||||||
d.warehouse_reorder_qty,
|
reorder_qty=d.warehouse_reorder_qty,
|
||||||
d.material_request_type,
|
material_request_type=d.material_request_type,
|
||||||
warehouse_group=d.warehouse_group,
|
warehouse_group=d.warehouse_group,
|
||||||
|
item_details=frappe._dict(
|
||||||
|
{
|
||||||
|
"item_code": item_code,
|
||||||
|
"name": item_code,
|
||||||
|
"item_name": d.item_name,
|
||||||
|
"item_group": d.item_group,
|
||||||
|
"brand": d.brand,
|
||||||
|
"description": d.description,
|
||||||
|
"stock_uom": d.stock_uom,
|
||||||
|
"purchase_uom": d.purchase_uom,
|
||||||
|
}
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if material_requests:
|
if material_requests:
|
||||||
return create_material_request(material_requests)
|
return create_material_request(material_requests)
|
||||||
|
|
||||||
|
|
||||||
|
def get_items_for_reorder() -> dict[str, list]:
|
||||||
|
reorder_table = frappe.qb.DocType("Item Reorder")
|
||||||
|
item_table = frappe.qb.DocType("Item")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(reorder_table)
|
||||||
|
.inner_join(item_table)
|
||||||
|
.on(reorder_table.parent == item_table.name)
|
||||||
|
.select(
|
||||||
|
reorder_table.warehouse,
|
||||||
|
reorder_table.warehouse_group,
|
||||||
|
reorder_table.material_request_type,
|
||||||
|
reorder_table.warehouse_reorder_level,
|
||||||
|
reorder_table.warehouse_reorder_qty,
|
||||||
|
item_table.name,
|
||||||
|
item_table.stock_uom,
|
||||||
|
item_table.purchase_uom,
|
||||||
|
item_table.description,
|
||||||
|
item_table.item_name,
|
||||||
|
item_table.item_group,
|
||||||
|
item_table.brand,
|
||||||
|
item_table.variant_of,
|
||||||
|
item_table.has_variants,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(item_table.disabled == 0)
|
||||||
|
& (item_table.is_stock_item == 1)
|
||||||
|
& (
|
||||||
|
(item_table.end_of_life.isnull())
|
||||||
|
| (item_table.end_of_life > nowdate())
|
||||||
|
| (item_table.end_of_life == "0000-00-00")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
data = query.run(as_dict=True)
|
||||||
|
itemwise_reorder = frappe._dict({})
|
||||||
|
for d in data:
|
||||||
|
itemwise_reorder.setdefault(d.name, []).append(d)
|
||||||
|
|
||||||
|
itemwise_reorder = get_reorder_levels_for_variants(itemwise_reorder)
|
||||||
|
|
||||||
|
return itemwise_reorder
|
||||||
|
|
||||||
|
|
||||||
|
def get_reorder_levels_for_variants(itemwise_reorder):
|
||||||
|
item_table = frappe.qb.DocType("Item")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(item_table)
|
||||||
|
.select(
|
||||||
|
item_table.name,
|
||||||
|
item_table.variant_of,
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(item_table.disabled == 0)
|
||||||
|
& (item_table.is_stock_item == 1)
|
||||||
|
& (
|
||||||
|
(item_table.end_of_life.isnull())
|
||||||
|
| (item_table.end_of_life > nowdate())
|
||||||
|
| (item_table.end_of_life == "0000-00-00")
|
||||||
|
)
|
||||||
|
& (item_table.variant_of.notnull())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
variants_item = query.run(as_dict=True)
|
||||||
|
for row in variants_item:
|
||||||
|
if not itemwise_reorder.get(row.name) and itemwise_reorder.get(row.variant_of):
|
||||||
|
itemwise_reorder.setdefault(row.name, []).extend(itemwise_reorder.get(row.variant_of, []))
|
||||||
|
|
||||||
|
return itemwise_reorder
|
||||||
|
|
||||||
|
|
||||||
def get_item_warehouse_projected_qty(items_to_consider):
|
def get_item_warehouse_projected_qty(items_to_consider):
|
||||||
item_warehouse_projected_qty = {}
|
item_warehouse_projected_qty = {}
|
||||||
|
items_to_consider = list(items_to_consider.keys())
|
||||||
|
|
||||||
for item_code, warehouse, projected_qty in frappe.db.sql(
|
for item_code, warehouse, projected_qty in frappe.db.sql(
|
||||||
"""select item_code, warehouse, projected_qty
|
"""select item_code, warehouse, projected_qty
|
||||||
@ -164,7 +248,7 @@ def create_material_request(material_requests):
|
|||||||
|
|
||||||
for d in items:
|
for d in items:
|
||||||
d = frappe._dict(d)
|
d = frappe._dict(d)
|
||||||
item = frappe.get_doc("Item", d.item_code)
|
item = d.get("item_details")
|
||||||
uom = item.stock_uom
|
uom = item.stock_uom
|
||||||
conversion_factor = 1.0
|
conversion_factor = 1.0
|
||||||
|
|
||||||
@ -190,6 +274,7 @@ def create_material_request(material_requests):
|
|||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
|
"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
|
"conversion_factor": conversion_factor,
|
||||||
"uom": uom,
|
"uom": uom,
|
||||||
"stock_uom": item.stock_uom,
|
"stock_uom": item.stock_uom,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user