fix: Add non-existent Item check and cleanup in validate_for_items
- Added a validation if invalid item code ia passed via data import/API, etc. - Used DB APIs instead of raw sql - Separated checks into separate functions - Added return types to functions - Better variable naming and removed redundant utils import in function
This commit is contained in:
parent
b981fae5a4
commit
982a246eec
@ -3,19 +3,19 @@
|
|||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint, cstr, flt
|
from frappe.utils import cint, cstr, flt, getdate
|
||||||
|
|
||||||
from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
|
from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_end_of_life
|
||||||
|
|
||||||
|
|
||||||
def update_last_purchase_rate(doc, is_submit):
|
def update_last_purchase_rate(doc, is_submit) -> None:
|
||||||
"""updates last_purchase_rate in item table for each item"""
|
"""updates last_purchase_rate in item table for each item"""
|
||||||
import frappe.utils
|
|
||||||
|
|
||||||
this_purchase_date = frappe.utils.getdate(doc.get("posting_date") or doc.get("transaction_date"))
|
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
|
||||||
|
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
# get last purchase details
|
# get last purchase details
|
||||||
@ -41,7 +41,7 @@ def update_last_purchase_rate(doc, is_submit):
|
|||||||
frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
|
frappe.db.set_value("Item", d.item_code, "last_purchase_rate", flt(last_purchase_rate))
|
||||||
|
|
||||||
|
|
||||||
def validate_for_items(doc):
|
def validate_for_items(doc) -> None:
|
||||||
items = []
|
items = []
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
if not d.qty:
|
if not d.qty:
|
||||||
@ -49,40 +49,11 @@ def validate_for_items(doc):
|
|||||||
continue
|
continue
|
||||||
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
||||||
|
|
||||||
# update with latest quantities
|
set_stock_levels(row=d) # update with latest quantities
|
||||||
bin = frappe.db.sql(
|
item = validate_item_and_get_basic_data(row=d)
|
||||||
"""select projected_qty from `tabBin` where
|
validate_stock_item_warehouse(row=d, item=item)
|
||||||
item_code = %s and warehouse = %s""",
|
|
||||||
(d.item_code, d.warehouse),
|
|
||||||
as_dict=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
f_lst = {
|
|
||||||
"projected_qty": bin and flt(bin[0]["projected_qty"]) or 0,
|
|
||||||
"ordered_qty": 0,
|
|
||||||
"received_qty": 0,
|
|
||||||
}
|
|
||||||
if d.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
|
|
||||||
f_lst.pop("received_qty")
|
|
||||||
for x in f_lst:
|
|
||||||
if d.meta.get_field(x):
|
|
||||||
d.set(x, f_lst[x])
|
|
||||||
|
|
||||||
item = frappe.db.sql(
|
|
||||||
"""select is_stock_item,
|
|
||||||
is_sub_contracted_item, end_of_life, disabled from `tabItem` where name=%s""",
|
|
||||||
d.item_code,
|
|
||||||
as_dict=1,
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
|
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
|
||||||
|
|
||||||
# validate stock item
|
|
||||||
if item.is_stock_item == 1 and d.qty and not d.warehouse and not d.get("delivered_by_supplier"):
|
|
||||||
frappe.throw(
|
|
||||||
_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx)
|
|
||||||
)
|
|
||||||
|
|
||||||
items.append(cstr(d.item_code))
|
items.append(cstr(d.item_code))
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -93,7 +64,57 @@ def validate_for_items(doc):
|
|||||||
frappe.throw(_("Same item cannot be entered multiple times."))
|
frappe.throw(_("Same item cannot be entered multiple times."))
|
||||||
|
|
||||||
|
|
||||||
def check_on_hold_or_closed_status(doctype, docname):
|
def set_stock_levels(row) -> None:
|
||||||
|
projected_qty = frappe.db.get_value(
|
||||||
|
"Bin",
|
||||||
|
{
|
||||||
|
"item_code": row.item_code,
|
||||||
|
"warehouse": row.warehouse,
|
||||||
|
},
|
||||||
|
"projected_qty",
|
||||||
|
)
|
||||||
|
|
||||||
|
qty_data = {
|
||||||
|
"projected_qty": flt(projected_qty),
|
||||||
|
"ordered_qty": 0,
|
||||||
|
"received_qty": 0,
|
||||||
|
}
|
||||||
|
if row.doctype in ("Purchase Receipt Item", "Purchase Invoice Item"):
|
||||||
|
qty_data.pop("received_qty")
|
||||||
|
|
||||||
|
for field in qty_data:
|
||||||
|
if row.meta.get_field(field):
|
||||||
|
row.set(field, qty_data[field])
|
||||||
|
|
||||||
|
|
||||||
|
def validate_item_and_get_basic_data(row) -> Dict:
|
||||||
|
item = frappe.db.get_values(
|
||||||
|
"Item",
|
||||||
|
filters={"name": row.item_code},
|
||||||
|
fieldname=["is_stock_item", "is_sub_contracted_item", "end_of_life", "disabled"],
|
||||||
|
as_dict=1,
|
||||||
|
)
|
||||||
|
if not item:
|
||||||
|
frappe.throw(_("Row #{0}: Item {1} does not exist").format(row.idx, frappe.bold(row.item_code)))
|
||||||
|
|
||||||
|
return item[0]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_stock_item_warehouse(row, item) -> None:
|
||||||
|
if (
|
||||||
|
item.is_stock_item == 1
|
||||||
|
and row.qty
|
||||||
|
and not row.warehouse
|
||||||
|
and not row.get("delivered_by_supplier")
|
||||||
|
):
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{1}: Warehouse is mandatory for stock Item {0}").format(
|
||||||
|
frappe.bold(row.item_code), row.idx
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_on_hold_or_closed_status(doctype, docname) -> None:
|
||||||
status = frappe.db.get_value(doctype, docname, "status")
|
status = frappe.db.get_value(doctype, docname, "status")
|
||||||
|
|
||||||
if status in ("Closed", "On Hold"):
|
if status in ("Closed", "On Hold"):
|
||||||
|
Loading…
Reference in New Issue
Block a user