refactor: shift auto entry of is process loss check, update validations
diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js
index a5ce8c6..dd437dd 100644
--- a/erpnext/manufacturing/doctype/bom/bom.js
+++ b/erpnext/manufacturing/doctype/bom/bom.js
@@ -379,9 +379,6 @@
child.bom_no = '';
}
- if (scrap_items) {
- set_is_process_loss(doc, cdt, cdn)
- }
get_bom_material_detail(doc, cdt, cdn, scrap_items);
}
@@ -450,9 +447,10 @@
callback: function(r) {
d = locals[cdt][cdn];
if (d.is_process_loss) {
- r.message.rate = 0
- r.message.base_rate = 0
+ r.message.rate = 0;
+ r.message.base_rate = 0;
}
+
$.extend(d, r.message);
refresh_field("items");
refresh_field("scrap_items");
@@ -661,12 +659,37 @@
if(!cint(frm.doc.with_operations)) {
frm.set_value("operations", []);
}
+ toggle_operations(frm);
});
-function set_is_process_loss(doc, cdt, cdn) {
- const row = locals[cdt][cdn]
- if (row.item_code === doc.item) {
- row.is_process_loss = 1
- frappe.msgprint(__("Item:") + ` ${row.item_code} ` + __("set as process loss."))
- }
+frappe.ui.form.on("BOM Scrap Item", {
+ item_code(frm, cdt, cdn) {
+ const { item_code } = locals[cdt][cdn];
+ if (item_code === frm.doc.item) {
+ locals[cdt][cdn].is_process_loss = 1;
+ trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code)
+ }
+ },
+});
+
+function trigger_process_loss_qty_prompt(frm, cdt, cdn, item_code) {
+ frappe.prompt(
+ {
+ fieldname: "percent",
+ fieldtype: "Percent",
+ label: __("% Finished Item Quantity"),
+ description:
+ __("Set quantity of process loss item:") +
+ ` ${item_code} ` +
+ __("as a percentage of finished item quantity"),
+ },
+ (data) => {
+ const row = locals[cdt][cdn];
+ row.stock_qty = (frm.doc.quantity * data.percent) / 100;
+ row.qty = row.stock_qty / (row.conversion_factor ?? 1);
+ refresh_field("scrap_items");
+ },
+ __("Set Process Loss Item Quantity"),
+ __("Set Quantity")
+ );
}
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index de0c521..b90d54d 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -695,11 +695,25 @@
def validate_scrap_items(self):
for item in self.scrap_items:
if item.item_code == self.item and not item.is_process_loss:
- frappe.throw(_('Item:') + f' {item.item_code} ' +\
- _('in Scrap/Loss Items table should have Is Process Loss checked.'))
+ frappe.throw(_('Scrap/Loss Item:') + f' {frappe.bold(item.item_code)} ' +\
+ _('should have') + ' ' + frappe.bold(_('Is Process Loss')) + ' ' + ('checked.'))
elif item.item_code != self.item and item.is_process_loss:
- frappe.throw(_('Item:') + f' {item.item_code} ' +\
- _('in Scrap/Loss Items table should not have Is Process Loss checked.'))
+ frappe.throw(_('Scrap/Loss Item:') + f' {frappe.bold(item.item_code)} ' +\
+ _('should not have') + ' ' + frappe.bold(_('Is Process Loss')) + ' ' + ('checked.'))
+
+ stock_uom = item.stock_uom
+ must_be_whole_number = frappe.get_value("UOM", stock_uom, "must_be_whole_number")
+ if item.is_process_loss and must_be_whole_number:
+ frappe.throw(_('Item:') + f' {frappe.bold(item.item_code)} ' +\
+ _('with Stock UOM:') + f' {frappe.bold(stock_uom)} '+\
+ _('cannot be a Scrap/Loss Item.'))
+
+ if item.is_process_loss and (item.stock_qty >= self.quantity):
+ frappe.throw(_('Scrap/Loss Item:') + f' {item.item_code} ' +\
+ _('should have') +' '+frappe.bold(_('Qty')) +\
+ ' ' + _('less than finished goods') + ' ' +\
+ frappe.bold(_('Quantity.')))
+
def get_bom_item_rate(args, bom_doc):
if bom_doc.rm_cost_as_per == 'Valuation Rate':