Merge pull request #46801 from rohitwaghchaure/fixed-ux-for-job-card

fix: UX for partial job card completion
This commit is contained in:
rohitwaghchaure
2025-03-30 22:51:30 +05:30
committed by GitHub
3 changed files with 76 additions and 14 deletions

View File

@@ -45,6 +45,7 @@ frappe.ui.form.on("Job Card", {
setup_stock_entry(frm) { setup_stock_entry(frm) {
if ( if (
frm.doc.manufactured_qty &&
frm.doc.finished_good && frm.doc.finished_good &&
frm.doc.docstatus === 1 && frm.doc.docstatus === 1 &&
!frm.doc.is_subcontracted && !frm.doc.is_subcontracted &&
@@ -91,11 +92,11 @@ frappe.ui.form.on("Job Card", {
frm.fields_dict["time_logs"].grid.update_docfield_property("time_in_mins", "read_only", 1); frm.fields_dict["time_logs"].grid.update_docfield_property("time_in_mins", "read_only", 1);
} }
if (!frm.is_new() && !frm.doc.skip_material_transfer && has_items && frm.doc.docstatus < 2) { if (!frm.is_new() && !frm.doc.skip_material_transfer && frm.doc.docstatus < 2) {
let to_request = frm.doc.for_quantity > frm.doc.transferred_qty; let to_request = frm.doc.for_quantity > frm.doc.transferred_qty;
let excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer; let excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer;
if (to_request || excess_transfer_allowed) { if (has_items && (to_request || excess_transfer_allowed)) {
frm.add_custom_button(__("Material Request"), () => { frm.add_custom_button(__("Material Request"), () => {
frm.trigger("make_material_request"); frm.trigger("make_material_request");
}); });
@@ -105,7 +106,7 @@ frappe.ui.form.on("Job Card", {
// in case of multiple items in JC // in case of multiple items in JC
let to_transfer = frm.doc.items.some((row) => row.transferred_qty < row.required_qty); let to_transfer = frm.doc.items.some((row) => row.transferred_qty < row.required_qty);
if (to_transfer || excess_transfer_allowed) { if (has_items && (to_transfer || excess_transfer_allowed)) {
frm.add_custom_button(__("Material Transfer"), () => { frm.add_custom_button(__("Material Transfer"), () => {
frm.trigger("make_stock_entry"); frm.trigger("make_stock_entry");
}); });
@@ -132,7 +133,8 @@ frappe.ui.form.on("Job Card", {
frm.doc.for_quantity + frm.doc.process_loss_qty > frm.doc.total_completed_qty && frm.doc.for_quantity + frm.doc.process_loss_qty > frm.doc.total_completed_qty &&
(frm.doc.skip_material_transfer || (frm.doc.skip_material_transfer ||
frm.doc.transferred_qty >= frm.doc.for_quantity + frm.doc.process_loss_qty || frm.doc.transferred_qty >= frm.doc.for_quantity + frm.doc.process_loss_qty ||
!frm.doc.finished_good) !frm.doc.finished_good ||
!has_items?.length)
) { ) {
if (!frm.doc.time_logs?.length) { if (!frm.doc.time_logs?.length) {
frm.add_custom_button(__("Start Job"), () => { frm.add_custom_button(__("Start Job"), () => {
@@ -168,7 +170,8 @@ frappe.ui.form.on("Job Card", {
}); });
}); });
} else { } else {
if (frm.doc.for_quantity - frm.doc.manufactured_qty > 0) { let manufactured_qty = frm.doc.manufactured_qty || frm.doc.total_completed_qty;
if (frm.doc.for_quantity - (manufactured_qty + frm.doc.process_loss_qty) > 0) {
if (!frm.doc.is_paused) { if (!frm.doc.is_paused) {
frm.add_custom_button(__("Pause Job"), () => { frm.add_custom_button(__("Pause Job"), () => {
frm.call({ frm.call({
@@ -217,12 +220,48 @@ frappe.ui.form.on("Job Card", {
complete_job_card(frm) { complete_job_card(frm) {
let fields = [ let fields = [
{
fieldtype: "Float",
label: __("Qty to Manufacture"),
fieldname: "for_quantity",
reqd: 1,
default: frm.doc.for_quantity,
change() {
let doc = frm.job_completion_dialog;
doc.set_value("completed_qty", doc.get_value("for_quantity"));
doc.set_value("process_loss_qty", 0);
},
},
{ {
fieldtype: "Float", fieldtype: "Float",
label: __("Completed Quantity"), label: __("Completed Quantity"),
fieldname: "qty", fieldname: "completed_qty",
reqd: 1, reqd: 1,
default: frm.doc.for_quantity - frm.doc.total_completed_qty, default: frm.doc.for_quantity - frm.doc.total_completed_qty,
change() {
let doc = frm.job_completion_dialog;
let process_loss_qty = doc.get_value("for_quantity") - doc.get_value("completed_qty");
if (process_loss_qty > 0 && process_loss_qty != doc.get_value("process_loss_qty")) {
doc.set_value("process_loss_qty", process_loss_qty);
}
},
},
{
fieldtype: "Float",
label: __("Process Loss Quantity"),
fieldname: "process_loss_qty",
reqd: 1,
onchange() {
let doc = frm.job_completion_dialog;
let completed_qty = doc.get_value("for_quantity") - doc.get_value("process_loss_qty");
doc.set_value("completed_qty", completed_qty);
},
},
{
fieldtype: "Section Break",
}, },
]; ];
@@ -236,7 +275,7 @@ frappe.ui.form.on("Job Card", {
}); });
} }
frappe.prompt( frm.job_completion_dialog = frappe.prompt(
fields, fields,
(data) => { (data) => {
if (data.qty <= 0) { if (data.qty <= 0) {
@@ -247,7 +286,8 @@ frappe.ui.form.on("Job Card", {
method: "complete_job_card", method: "complete_job_card",
doc: frm.doc, doc: frm.doc,
args: { args: {
qty: data.qty, qty: data.completed_qty,
for_quantity: data.for_quantity,
end_time: data.end_time, end_time: data.end_time,
}, },
callback: function (r) { callback: function (r) {
@@ -628,7 +668,15 @@ frappe.ui.form.on("Job Card", {
}); });
frappe.ui.form.on("Job Card Time Log", { frappe.ui.form.on("Job Card Time Log", {
completed_qty: function (frm) { completed_qty: function (frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (!row.completed_qty) {
frappe.model.set_value(row.doctype, row.name, {
time_in_mins: 0,
to_time: "",
});
}
frm.events.set_total_completed_qty(frm); frm.events.set_total_completed_qty(frm);
}, },

View File

@@ -73,6 +73,7 @@
"status", "status",
"operation_row_id", "operation_row_id",
"is_paused", "is_paused",
"track_semi_finished_goods",
"column_break_20", "column_break_20",
"operation_row_number", "operation_row_number",
"operation_id", "operation_id",
@@ -530,10 +531,11 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:doc.track_semi_finished_goods",
"fieldname": "target_warehouse", "fieldname": "target_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Target Warehouse", "label": "Target Warehouse",
"mandatory_depends_on": "eval:doc.finished_good", "mandatory_depends_on": "eval:doc.track_semi_finished_goods",
"options": "Warehouse" "options": "Warehouse"
}, },
{ {
@@ -610,12 +612,19 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Paused", "label": "Is Paused",
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fetch_from": "work_order.track_semi_finished_goods",
"fieldname": "track_semi_finished_goods",
"fieldtype": "Check",
"label": "Track Semi Finished Goods"
} }
], ],
"grid_page_length": 50, "grid_page_length": 50,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-03-25 17:50:18.608869", "modified": "2025-03-30 18:53:38.206399",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Job Card", "name": "Job Card",

View File

@@ -129,6 +129,7 @@ class JobCard(Document):
time_required: DF.Float time_required: DF.Float
total_completed_qty: DF.Float total_completed_qty: DF.Float
total_time_in_mins: DF.Float total_time_in_mins: DF.Float
track_semi_finished_goods: DF.Check
transferred_qty: DF.Float transferred_qty: DF.Float
wip_warehouse: DF.Link | None wip_warehouse: DF.Link | None
work_order: DF.Link work_order: DF.Link
@@ -723,7 +724,7 @@ class JobCard(Document):
) )
def validate_job_card(self): def validate_job_card(self):
if self.finished_good: if self.track_semi_finished_goods:
return return
if self.work_order and frappe.get_cached_value("Work Order", self.work_order, "status") == "Stopped": if self.work_order and frappe.get_cached_value("Work Order", self.work_order, "status") == "Stopped":
@@ -794,7 +795,7 @@ class JobCard(Document):
) )
def update_work_order(self): def update_work_order(self):
if self.finished_good: if self.track_semi_finished_goods:
return return
if not self.work_order: if not self.work_order:
@@ -1037,7 +1038,7 @@ class JobCard(Document):
if self.docstatus == 0 and self.time_logs: if self.docstatus == 0 and self.time_logs:
self.status = "Work In Progress" self.status = "Work In Progress"
if not self.finished_good and self.docstatus < 2: if not self.track_semi_finished_goods and self.docstatus < 2:
if flt(self.for_quantity) <= flt(self.transferred_qty): if flt(self.for_quantity) <= flt(self.transferred_qty):
self.status = "Material Transferred" self.status = "Material Transferred"
@@ -1254,6 +1255,10 @@ class JobCard(Document):
if kwargs.end_time: if kwargs.end_time:
self.add_time_logs(to_time=kwargs.end_time, completed_qty=kwargs.qty, employees=self.employee) self.add_time_logs(to_time=kwargs.end_time, completed_qty=kwargs.qty, employees=self.employee)
if kwargs.for_quantity:
self.for_quantity = kwargs.for_quantity
self.save() self.save()
else: else:
self.add_time_logs(completed_qty=kwargs.qty, employees=self.employee) self.add_time_logs(completed_qty=kwargs.qty, employees=self.employee)