mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-20 03:47:11 +00:00
Merge pull request #57933 from frappe/mergify/bp/version-16-hotfix/pr-57687
fix(job_card): reject a completion split that cannot add up (backport #57687)
This commit is contained in:
@@ -251,6 +251,7 @@ frappe.ui.form.on("Job Card", {
|
|||||||
change() {
|
change() {
|
||||||
const dialog = frm.job_completion_dialog;
|
const dialog = frm.job_completion_dialog;
|
||||||
dialog.set_value("completed_qty", dialog.get_value("for_quantity"));
|
dialog.set_value("completed_qty", dialog.get_value("for_quantity"));
|
||||||
|
dialog.set_value("pending_qty", 0);
|
||||||
dialog.set_value("process_loss_qty", 0);
|
dialog.set_value("process_loss_qty", 0);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -262,8 +263,21 @@ frappe.ui.form.on("Job Card", {
|
|||||||
default: pending_qty,
|
default: pending_qty,
|
||||||
change() {
|
change() {
|
||||||
const dialog = frm.job_completion_dialog;
|
const dialog = frm.job_completion_dialog;
|
||||||
const remaining = dialog.get_value("for_quantity") - dialog.get_value("completed_qty");
|
const remaining =
|
||||||
if (remaining > 0 && remaining != dialog.get_value("pending_qty")) {
|
dialog.get_value("for_quantity") -
|
||||||
|
dialog.get_value("completed_qty") -
|
||||||
|
dialog.get_value("process_loss_qty");
|
||||||
|
|
||||||
|
if (remaining < 0) {
|
||||||
|
const max_completed_qty =
|
||||||
|
flt(dialog.get_value("for_quantity")) - flt(dialog.get_value("process_loss_qty"));
|
||||||
|
dialog.set_value("completed_qty", max_completed_qty);
|
||||||
|
frappe.throw(
|
||||||
|
__("Completed Quantity cannot be greater than {0}", [max_completed_qty])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining != dialog.get_value("pending_qty")) {
|
||||||
dialog.set_value("pending_qty", remaining);
|
dialog.set_value("pending_qty", remaining);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -279,7 +293,18 @@ frappe.ui.form.on("Job Card", {
|
|||||||
dialog.get_value("for_quantity") -
|
dialog.get_value("for_quantity") -
|
||||||
dialog.get_value("completed_qty") -
|
dialog.get_value("completed_qty") -
|
||||||
dialog.get_value("pending_qty");
|
dialog.get_value("pending_qty");
|
||||||
if (process_loss_qty >= 0 && process_loss_qty != dialog.get_value("process_loss_qty")) {
|
|
||||||
|
if (process_loss_qty < 0) {
|
||||||
|
dialog.set_value("pending_qty", 0);
|
||||||
|
frappe.throw(
|
||||||
|
__("Pending Quantity cannot be greater than {0}", [
|
||||||
|
flt(dialog.get_value("for_quantity")) -
|
||||||
|
flt(dialog.get_value("completed_qty")),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process_loss_qty != dialog.get_value("process_loss_qty")) {
|
||||||
dialog.set_value("process_loss_qty", process_loss_qty);
|
dialog.set_value("process_loss_qty", process_loss_qty);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -294,7 +319,18 @@ frappe.ui.form.on("Job Card", {
|
|||||||
dialog.get_value("for_quantity") -
|
dialog.get_value("for_quantity") -
|
||||||
dialog.get_value("completed_qty") -
|
dialog.get_value("completed_qty") -
|
||||||
dialog.get_value("process_loss_qty");
|
dialog.get_value("process_loss_qty");
|
||||||
if (remaining >= 0 && remaining != dialog.get_value("pending_qty")) {
|
|
||||||
|
if (remaining < 0) {
|
||||||
|
dialog.set_value("process_loss_qty", 0);
|
||||||
|
frappe.throw(
|
||||||
|
__("Process Loss Quantity cannot be greater than {0}", [
|
||||||
|
flt(dialog.get_value("for_quantity")) -
|
||||||
|
flt(dialog.get_value("completed_qty")),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining != dialog.get_value("pending_qty")) {
|
||||||
dialog.set_value("pending_qty", remaining);
|
dialog.set_value("pending_qty", remaining);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -895,22 +895,21 @@ class JobCard(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
precision = self.precision("total_completed_qty")
|
precision = self.precision("total_completed_qty")
|
||||||
total_completed_qty = flt(
|
accounted_qty = flt(
|
||||||
flt(self.total_completed_qty, precision)
|
flt(self.total_completed_qty, precision)
|
||||||
+ flt(self.process_loss_qty, precision)
|
+ flt(self.process_loss_qty, precision)
|
||||||
+ flt(self.pending_qty, precision)
|
+ flt(self.pending_qty, precision)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.for_quantity and flt(total_completed_qty, precision) != flt(self.for_quantity, precision):
|
if self.for_quantity and flt(accounted_qty, precision) != flt(self.for_quantity, precision):
|
||||||
total_completed_qty_label = bold(_("Total Completed Qty"))
|
|
||||||
qty_to_manufacture = bold(_("Qty to Manufacture"))
|
|
||||||
|
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("The {0} ({1}) must be equal to {2} ({3})").format(
|
_(
|
||||||
total_completed_qty_label,
|
"Total Completed Qty ({0}), Process Loss Qty ({1}) and Pending Qty ({2}) must add up to the Qty to Manufacture ({3})."
|
||||||
bold(flt(total_completed_qty, precision)),
|
).format(
|
||||||
qty_to_manufacture,
|
bold(flt(self.total_completed_qty, precision)),
|
||||||
bold(self.for_quantity),
|
bold(flt(self.process_loss_qty, precision)),
|
||||||
|
bold(flt(self.pending_qty, precision)),
|
||||||
|
bold(flt(self.for_quantity, precision)),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1605,6 +1604,8 @@ class JobCard(Document):
|
|||||||
if flt(kwargs.pending_qty) and flt(kwargs.pending_qty) > self.for_quantity:
|
if flt(kwargs.pending_qty) and flt(kwargs.pending_qty) > self.for_quantity:
|
||||||
frappe.throw(_("Pending quantity cannot be greater than the for quantity."))
|
frappe.throw(_("Pending quantity cannot be greater than the for quantity."))
|
||||||
|
|
||||||
|
self.validate_completion_qty_split(kwargs)
|
||||||
|
|
||||||
self.pending_qty = flt(kwargs.pending_qty)
|
self.pending_qty = flt(kwargs.pending_qty)
|
||||||
self.process_loss_qty = flt(kwargs.process_loss_qty)
|
self.process_loss_qty = flt(kwargs.process_loss_qty)
|
||||||
|
|
||||||
@@ -1633,6 +1634,31 @@ class JobCard(Document):
|
|||||||
_("Job Card {0} has been completed").format(get_link_to_form("Job Card", self.name))
|
_("Job Card {0} has been completed").format(get_link_to_form("Job Card", self.name))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def validate_completion_qty_split(self, kwargs):
|
||||||
|
if not flt(kwargs.for_quantity):
|
||||||
|
return
|
||||||
|
|
||||||
|
precision = self.precision("total_completed_qty")
|
||||||
|
accounted_qty = flt(
|
||||||
|
flt(kwargs.qty, precision)
|
||||||
|
+ flt(kwargs.pending_qty, precision)
|
||||||
|
+ flt(kwargs.process_loss_qty, precision)
|
||||||
|
)
|
||||||
|
|
||||||
|
if flt(accounted_qty, precision) == flt(kwargs.for_quantity, precision):
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Completed Quantity ({0}), Pending Quantity ({1}) and Process Loss Quantity ({2}) must add up to the Qty to Manufacture ({3})."
|
||||||
|
).format(
|
||||||
|
bold(flt(kwargs.qty, precision)),
|
||||||
|
bold(flt(kwargs.pending_qty, precision)),
|
||||||
|
bold(flt(kwargs.process_loss_qty, precision)),
|
||||||
|
bold(flt(kwargs.for_quantity, precision)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_stock_entry_for_semi_fg_item(self, auto_submit: bool = False):
|
def make_stock_entry_for_semi_fg_item(self, auto_submit: bool = False):
|
||||||
def get_consumed_process_loss():
|
def get_consumed_process_loss():
|
||||||
|
|||||||
@@ -2114,6 +2114,26 @@ class TestJobCard(ERPNextTestSuite):
|
|||||||
self.assertEqual(s.additional_costs[2].amount, 480)
|
self.assertEqual(s.additional_costs[2].amount, 480)
|
||||||
self.assertEqual(s.additional_costs[3].amount, 480)
|
self.assertEqual(s.additional_costs[3].amount, 480)
|
||||||
|
|
||||||
|
def test_completion_qty_split_must_add_up(self):
|
||||||
|
jc = frappe.new_doc("Job Card")
|
||||||
|
jc.for_quantity = 5
|
||||||
|
|
||||||
|
jc.validate_completion_qty_split(
|
||||||
|
frappe._dict(for_quantity=5, qty=3, pending_qty=2, process_loss_qty=0)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
frappe.ValidationError,
|
||||||
|
jc.validate_completion_qty_split,
|
||||||
|
frappe._dict(for_quantity=3, qty=3, pending_qty=2, process_loss_qty=0),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
frappe.ValidationError,
|
||||||
|
jc.validate_completion_qty_split,
|
||||||
|
frappe._dict(for_quantity=1, qty=0.3334, pending_qty=0.3334, process_loss_qty=0.3334),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_bom_with_multiple_operations():
|
def create_bom_with_multiple_operations():
|
||||||
"Create a BOM with multiple operations and Material Transfer against Job Card"
|
"Create a BOM with multiple operations and Material Transfer against Job Card"
|
||||||
|
|||||||
Reference in New Issue
Block a user