Merge pull request #39659 from rohitwaghchaure/fixed-timeout-error-while-making-auto-mr

perf: timeout for auto material request through reorder level
This commit is contained in:
rohitwaghchaure 2024-02-05 11:38:11 +05:30 committed by GitHub
commit 675a0b810f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 172 additions and 42 deletions

View File

@ -599,7 +599,7 @@ class SellingController(StockController):
if self.doctype in ["Sales Order", "Quotation"]:
for item in self.items:
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):

View File

@ -1785,6 +1785,48 @@ class TestStockEntry(FrappeTestCase):
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):
args = frappe._dict(args)

View File

@ -86,7 +86,8 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
get_party_item_code(args, item, out)
set_valuation_rate(out, args)
if args.get("doctype") in ["Sales Order", "Quotation"]:
set_valuation_rate(out, args)
update_party_blanket_order(args, out)
@ -269,7 +270,9 @@ def get_basic_details(args, item, overwrite_warehouse=True):
if not item:
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_defaults = get_item_defaults(item.name, args.company)

View File

@ -34,73 +34,157 @@ def _reorder_item():
erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0]
)
items_to_consider = frappe.db.sql_list(
"""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()},
)
items_to_consider = get_items_for_reorder()
if not items_to_consider:
return
item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
def add_to_material_request(
item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None
):
if warehouse not in warehouse_company:
def add_to_material_request(**kwargs):
if isinstance(kwargs, dict):
kwargs = frappe._dict(kwargs)
if kwargs.warehouse not in warehouse_company:
# a disabled warehouse
return
reorder_level = flt(reorder_level)
reorder_qty = flt(reorder_qty)
reorder_level = flt(kwargs.reorder_level)
reorder_qty = flt(kwargs.reorder_qty)
# projected_qty will be 0 if Bin does not exist
if warehouse_group:
projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group))
if kwargs.warehouse_group:
projected_qty = flt(
item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse_group)
)
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:
deficiency = reorder_level - projected_qty
if deficiency > reorder_qty:
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(
{"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty}
material_requests[kwargs.material_request_type].setdefault(company, []).append(
{
"item_code": kwargs.item_code,
"warehouse": kwargs.warehouse,
"reorder_qty": reorder_qty,
"item_details": kwargs.item_details,
}
)
for item_code in items_to_consider:
item = frappe.get_doc("Item", item_code)
for item_code, reorder_levels in items_to_consider.items():
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(
item_code,
d.warehouse,
d.warehouse_reorder_level,
d.warehouse_reorder_qty,
d.material_request_type,
warehouse_group=d.warehouse_group,
)
add_to_material_request(
item_code=item_code,
warehouse=d.warehouse,
reorder_level=d.warehouse_reorder_level,
reorder_qty=d.warehouse_reorder_qty,
material_request_type=d.material_request_type,
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:
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):
item_warehouse_projected_qty = {}
items_to_consider = list(items_to_consider.keys())
for item_code, warehouse, projected_qty in frappe.db.sql(
"""select item_code, warehouse, projected_qty
@ -164,7 +248,7 @@ def create_material_request(material_requests):
for d in items:
d = frappe._dict(d)
item = frappe.get_doc("Item", d.item_code)
item = d.get("item_details")
uom = item.stock_uom
conversion_factor = 1.0
@ -190,6 +274,7 @@ def create_material_request(material_requests):
"item_code": d.item_code,
"schedule_date": add_days(nowdate(), cint(item.lead_time_days)),
"qty": qty,
"conversion_factor": conversion_factor,
"uom": uom,
"stock_uom": item.stock_uom,
"warehouse": d.warehouse,