refactor: job_card.js code for better readability

(cherry picked from commit 0a215b0717)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.js
This commit is contained in:
Rohit Waghchaure
2026-05-20 22:59:00 +05:30
committed by Mergify
parent b372e6f118
commit 3081368aad

View File

@@ -2,79 +2,53 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Job Card", { frappe.ui.form.on("Job Card", {
setup: function (frm) { setup(frm) {
frm.set_query("operation", function () { frm.set_query("operation", () => ({
return { query: "erpnext.manufacturing.doctype.job_card.job_card.get_operations",
query: "erpnext.manufacturing.doctype.job_card.job_card.get_operations", filters: { work_order: frm.doc.work_order },
filters: { }));
work_order: frm.doc.work_order,
},
};
});
frm.set_query("serial_and_batch_bundle", () => { frm.set_query("serial_and_batch_bundle", () => ({
return { filters: {
filters: { item_code: frm.doc.production_item,
item_code: frm.doc.production_item, voucher_type: frm.doc.doctype,
voucher_type: frm.doc.doctype, voucher_no: ["in", [frm.doc.name, ""]],
voucher_no: ["in", [frm.doc.name, ""]], is_cancelled: 0,
is_cancelled: 0, },
}, }));
};
});
frm.set_query("item_code", "secondary_items", () => { frm.set_query("item_code", "secondary_items", () => ({
return { filters: { disabled: 0 },
filters: { }));
disabled: 0,
},
};
});
frm.set_query("operation", "time_logs", () => { frm.set_query("operation", "time_logs", () => {
let operations = (frm.doc.sub_operations || []).map((d) => d.sub_operation); const operations = (frm.doc.sub_operations || []).map((d) => d.sub_operation);
return { return { filters: { name: ["in", operations] } };
filters: {
name: ["in", operations],
},
};
}); });
frm.set_query("work_order", function () { frm.set_query("work_order", () => ({
return { filters: { status: ["not in", ["Cancelled", "Closed", "Stopped"]] },
filters: { }));
status: ["not in", ["Cancelled", "Closed", "Stopped"]],
},
};
});
frm.events.set_company_filters(frm, "target_warehouse"); frm.events.set_company_filters(frm, "target_warehouse");
frm.events.set_company_filters(frm, "source_warehouse"); frm.events.set_company_filters(frm, "source_warehouse");
frm.events.set_company_filters(frm, "wip_warehouse"); frm.events.set_company_filters(frm, "wip_warehouse");
frm.set_query("source_warehouse", "items", () => {
return { frm.set_query("source_warehouse", "items", () => ({
filters: { filters: { company: frm.doc.company },
company: frm.doc.company, }));
},
}; frm.set_indicator_formatter("sub_operation", (doc) => {
if (doc.status === "Pending") return "red";
return doc.status === "Complete" ? "green" : "orange";
}); });
frm.set_indicator_formatter("sub_operation", function (doc) { frm.set_query("employee", () => ({
if (doc.status == "Pending") { filters: {
return "red"; company: frm.doc.company,
} else { status: "Active",
return doc.status === "Complete" ? "green" : "orange"; },
} }));
});
frm.set_query("employee", () => {
return {
filters: {
company: frm.doc.company,
status: "Active",
},
};
});
}, },
pending_qty(frm) { pending_qty(frm) {
@@ -88,7 +62,7 @@ frappe.ui.form.on("Job Card", {
frappe.throw(__("Pending Quantity cannot be less than 0")); frappe.throw(__("Pending Quantity cannot be less than 0"));
} }
let remaining_qty = flt(frm.doc.for_quantity) - flt(frm.doc.total_completed_qty); const remaining_qty = flt(frm.doc.for_quantity) - flt(frm.doc.total_completed_qty);
if (remaining_qty < frm.doc.pending_qty) { if (remaining_qty < frm.doc.pending_qty) {
frm.doc.pending_qty = 0.0; frm.doc.pending_qty = 0.0;
@@ -96,19 +70,15 @@ frappe.ui.form.on("Job Card", {
frappe.throw(__("Pending Quantity cannot be greater than {0}", [remaining_qty])); frappe.throw(__("Pending Quantity cannot be greater than {0}", [remaining_qty]));
} }
let process_loss_qty = flt(remaining_qty) - flt(frm.doc.pending_qty); const process_loss_qty = flt(remaining_qty) - flt(frm.doc.pending_qty);
frm.doc.process_loss_qty = process_loss_qty >= 0 ? process_loss_qty : 0; frm.doc.process_loss_qty = process_loss_qty >= 0 ? process_loss_qty : 0;
refresh_field("process_loss_qty"); refresh_field("process_loss_qty");
}, },
set_company_filters(frm, fieldname) { set_company_filters(frm, fieldname) {
frm.set_query(fieldname, () => { frm.set_query(fieldname, () => ({
return { filters: { company: frm.doc.company },
filters: { }));
company: frm.doc.company,
},
};
});
}, },
make_fields_read_only(frm) { make_fields_read_only(frm) {
@@ -123,33 +93,29 @@ frappe.ui.form.on("Job Card", {
}, },
setup_stock_entry(frm) { setup_stock_entry(frm) {
if ( const { doc } = frm;
frm.doc.track_semi_finished_goods && const can_make_stock_entry =
frm.doc.docstatus === 1 && doc.track_semi_finished_goods &&
!frm.doc.is_subcontracted && doc.docstatus === 1 &&
(frm.doc.skip_material_transfer || frm.doc.transferred_qty > 0) && !doc.is_subcontracted &&
flt(frm.doc.manufactured_qty) + flt(frm.doc.process_loss_qty) < flt(frm.doc.for_quantity) (doc.skip_material_transfer || doc.transferred_qty > 0) &&
) { flt(doc.manufactured_qty) + flt(doc.process_loss_qty) < flt(doc.for_quantity);
frm.add_custom_button(__("Make Stock Entry"), () => {
frappe.confirm( if (!can_make_stock_entry) return;
__("Do you want to submit the stock entry?"),
() => { frm.add_custom_button(__("Make Stock Entry"), () => {
frm.events.make_manufacture_stock_entry(frm, 1); frappe.confirm(
}, __("Do you want to submit the stock entry?"),
() => { () => frm.events.make_manufacture_stock_entry(frm, 1),
frm.events.make_manufacture_stock_entry(frm, 0); () => frm.events.make_manufacture_stock_entry(frm, 0)
} );
); }).addClass("btn-primary");
}).addClass("btn-primary");
}
}, },
make_manufacture_stock_entry(frm, submit_entry) { make_manufacture_stock_entry(frm, submit_entry) {
frm.call({ frm.call({
method: "make_stock_entry_for_semi_fg_item", method: "make_stock_entry_for_semi_fg_item",
args: { args: { auto_submit: submit_entry },
auto_submit: submit_entry,
},
doc: frm.doc, doc: frm.doc,
freeze: true, freeze: true,
callback() { callback() {
@@ -158,80 +124,58 @@ frappe.ui.form.on("Job Card", {
}); });
}, },
<<<<<<< HEAD
refresh: function (frm) { refresh: function (frm) {
frm.trigger("setup_stock_entry"); frm.trigger("setup_stock_entry");
let has_items = frm.doc.items && frm.doc.items.length; let has_items = frm.doc.items && frm.doc.items.length;
=======
refresh(frm) {
const { doc } = frm;
const has_items = doc.items && doc.items.length;
>>>>>>> 0a215b0717 (refactor: job_card.js code for better readability)
frm.trigger("make_fields_read_only"); frm.trigger("make_fields_read_only");
if (!frm.is_new() && frm.doc.__onload?.work_order_closed) { if (!frm.is_new() && doc.__onload?.work_order_closed) {
frm.disable_save(); frm.disable_save();
return; return;
} }
if (frm.doc.is_subcontracted) { if (doc.is_subcontracted) {
frm.trigger("make_subcontracting_po"); frm.trigger("make_subcontracting_po");
return; return;
} }
if (frm.doc.docstatus > 0) { if (doc.docstatus > 0) {
frm.set_df_property("pending_qty", "read_only", 1); frm.set_df_property("pending_qty", "read_only", 1);
} }
let has_stock_entry = frm.doc.__onload && frm.doc.__onload.has_stock_entry ? true : false; const has_stock_entry = !!doc.__onload?.has_stock_entry;
frm.toggle_enable("for_quantity", !has_stock_entry); frm.toggle_enable("for_quantity", !has_stock_entry);
if (frm.doc.docstatus != 0) { if (doc.docstatus != 0) {
frm.fields_dict["time_logs"].grid.update_docfield_property("completed_qty", "read_only", 1); frm.fields_dict["time_logs"].grid.update_docfield_property("completed_qty", "read_only", 1);
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 && frm.doc.docstatus < 2) { frm.events.setup_material_transfer_buttons(frm, has_items);
let to_request = frm.doc.for_quantity > frm.doc.transferred_qty;
let excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer;
if (has_items && (to_request || excess_transfer_allowed)) { if (doc.docstatus == 1 && !doc.is_corrective_job_card && !doc.finished_good) {
frm.add_custom_button(
__("Material Request"),
() => {
frm.trigger("make_material_request");
},
__("Create")
);
}
// check if any row has untransferred materials
// in case of multiple items in JC
let to_transfer = frm.doc.items.some((row) => row.transferred_qty < row.required_qty);
if (has_items && (to_transfer || excess_transfer_allowed)) {
frm.add_custom_button(
__("Material Transfer"),
() => {
frm.trigger("make_stock_entry");
},
__("Create")
);
}
}
if (frm.doc.docstatus == 1 && !frm.doc.is_corrective_job_card && !frm.doc.finished_good) {
frm.trigger("setup_corrective_job_card"); frm.trigger("setup_corrective_job_card");
} }
frm.set_query("quality_inspection", function () { frm.set_query("quality_inspection", () => ({
return { query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query", filters: {
filters: { item_code: doc.production_item,
item_code: frm.doc.production_item, reference_name: doc.name,
reference_name: frm.doc.name, },
}, }));
};
});
frm.trigger("toggle_operation_number"); frm.trigger("toggle_operation_number");
<<<<<<< HEAD
if ( if (
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 ||
@@ -312,36 +256,165 @@ frappe.ui.form.on("Job Card", {
frm.trigger("make_dashboard"); frm.trigger("make_dashboard");
} }
} }
=======
const is_timer_running = frm.events.setup_job_action_buttons(frm, has_items);
>>>>>>> 0a215b0717 (refactor: job_card.js code for better readability)
frm.trigger("setup_quality_inspection"); frm.trigger("setup_quality_inspection");
if (frm.doc.work_order) { if (doc.work_order) {
frappe.db.get_value("Work Order", frm.doc.work_order, "transfer_material_against").then((r) => { frappe.db.get_value("Work Order", doc.work_order, "transfer_material_against").then((r) => {
if (r.message.transfer_material_against == "Work Order" && !frm.doc.operation_row_id) { if (r.message.transfer_material_against == "Work Order" && !doc.operation_row_id) {
frm.set_df_property("items", "hidden", 1); frm.set_df_property("items", "hidden", 1);
} }
}); });
} }
let sbb_field = frm.get_docfield("serial_and_batch_bundle"); const sbb_field = frm.get_docfield("serial_and_batch_bundle");
if (sbb_field) { if (sbb_field) {
sbb_field.get_route_options_for_new_doc = () => { sbb_field.get_route_options_for_new_doc = () => ({
return { item_code: doc.production_item,
item_code: frm.doc.production_item, warehouse: doc.wip_warehouse,
warehouse: frm.doc.wip_warehouse, voucher_type: doc.doctype,
voucher_type: frm.doc.doctype, });
};
};
} }
}, },
// Adds Material Request and Material Transfer buttons when items need to be transferred.
setup_material_transfer_buttons(frm, has_items) {
const { doc } = frm;
if (frm.is_new() || doc.skip_material_transfer || doc.docstatus >= 2) return;
const excess_transfer_allowed = doc.__onload.job_card_excess_transfer;
const to_request = doc.for_quantity > doc.transferred_qty;
if (has_items && (to_request || excess_transfer_allowed)) {
frm.add_custom_button(
__("Material Request"),
() => frm.trigger("make_material_request"),
__("Create")
);
}
// check if any row has untransferred materials in case of multiple items in JC
const to_transfer = doc.items.some((row) => row.transferred_qty < row.required_qty);
if (has_items && (to_transfer || excess_transfer_allowed)) {
frm.add_custom_button(
__("Material Transfer"),
() => frm.trigger("make_stock_entry"),
__("Create")
);
}
},
// Renders the correct action button (Start / Resume / Pause + Complete) based on job state.
// Returns true if the job timer is actively running, so the caller can skip the stock entry button.
setup_job_action_buttons(frm, has_items) {
const { doc } = frm;
const has_remaining_qty = doc.for_quantity + doc.process_loss_qty > doc.total_completed_qty;
const materials_ready =
doc.skip_material_transfer ||
doc.transferred_qty >= doc.for_quantity + doc.process_loss_qty ||
!doc.finished_good ||
!has_items?.length;
if (!has_remaining_qty || !materials_ready) return false;
let last_row = {};
const has_sub_ops_or_pending_qty = doc.sub_operations?.length || doc.pending_qty > 0;
if (has_sub_ops_or_pending_qty && doc.time_logs?.length) {
last_row = get_last_row(doc.time_logs);
}
const no_time_logs_yet = !doc.time_logs?.length;
const pending_qty_cycle_done = flt(doc.pending_qty) > 0.0 && last_row?.to_time;
const sub_operation_cycle_done = doc.sub_operations?.length && last_row?.to_time;
const should_show_start =
(no_time_logs_yet || pending_qty_cycle_done || sub_operation_cycle_done) && !doc.is_paused;
if (should_show_start) {
frm.events.add_start_job_button(frm);
return false;
}
if (doc.is_paused) {
frm.add_custom_button(__("Resume Job"), () => {
frm.call({
method: "resume_job",
doc: frm.doc,
args: { start_time: frappe.datetime.now_datetime() },
callback() {
frm.reload_doc();
},
});
});
return false;
}
// Job is actively running — show Pause and Complete buttons.
const manufactured_qty = doc.manufactured_qty || doc.total_completed_qty;
const qty_yet_to_manufacture = doc.for_quantity - (manufactured_qty + doc.process_loss_qty);
if (qty_yet_to_manufacture > 0) {
if (!doc.is_paused) {
frm.add_custom_button(__("Pause Job"), () => {
frm.call({
method: "pause_job",
doc: frm.doc,
args: { end_time: frappe.datetime.now_datetime() },
callback() {
frm.reload_doc();
},
});
});
}
frm.add_custom_button(__("Complete Job"), () => {
frm.trigger("complete_job_card");
});
frm.trigger("make_dashboard");
return true;
}
frm.trigger("make_dashboard");
return false;
},
add_start_job_button(frm) {
frm.add_custom_button(__("Start Job"), () => {
const from_time = frappe.datetime.now_datetime();
const has_no_employee = (frm.doc.employee && !frm.doc.employee.length) || !frm.doc.employee;
if (has_no_employee) {
frappe.prompt(
{
fieldtype: "Table MultiSelect",
label: __("Select Employees"),
options: "Job Card Time Log",
fieldname: "employees",
reqd: 1,
filters: { status: "Active" },
},
(d) => frm.events.start_timer(frm, from_time, d.employees),
__("Assign Job to Employee")
);
} else {
frm.events.start_timer(frm, from_time, frm.doc.employee);
}
});
},
complete_job_card(frm) { complete_job_card(frm) {
let pending_qty = frm.doc.for_quantity - frm.doc.total_completed_qty; let pending_qty = frm.doc.for_quantity - frm.doc.total_completed_qty;
if (frm.doc.pending_qty > 0) { if (frm.doc.pending_qty > 0) {
pending_qty = frm.doc.pending_qty; pending_qty = frm.doc.pending_qty;
} }
let fields = [ const fields = [
{ {
fieldtype: "Float", fieldtype: "Float",
label: __("Qty to Manufacture"), label: __("Qty to Manufacture"),
@@ -349,10 +422,9 @@ frappe.ui.form.on("Job Card", {
reqd: 1, reqd: 1,
default: pending_qty, default: pending_qty,
change() { change() {
let doc = frm.job_completion_dialog; const dialog = frm.job_completion_dialog;
dialog.set_value("completed_qty", dialog.get_value("for_quantity"));
doc.set_value("completed_qty", doc.get_value("for_quantity")); dialog.set_value("process_loss_qty", 0);
doc.set_value("process_loss_qty", 0);
}, },
}, },
{ {
@@ -362,11 +434,10 @@ frappe.ui.form.on("Job Card", {
reqd: 1, reqd: 1,
default: pending_qty, default: pending_qty,
change() { change() {
let doc = frm.job_completion_dialog; const dialog = frm.job_completion_dialog;
const remaining = dialog.get_value("for_quantity") - dialog.get_value("completed_qty");
let pending_qty = doc.get_value("for_quantity") - doc.get_value("completed_qty"); if (remaining > 0 && remaining != dialog.get_value("pending_qty")) {
if (pending_qty > 0 && pending_qty != doc.get_value("pending_qty")) { dialog.set_value("pending_qty", remaining);
doc.set_value("pending_qty", pending_qty);
} }
}, },
}, },
@@ -376,14 +447,13 @@ frappe.ui.form.on("Job Card", {
fieldname: "pending_qty", fieldname: "pending_qty",
default: 0.0, default: 0.0,
change() { change() {
let doc = frm.job_completion_dialog; const dialog = frm.job_completion_dialog;
const process_loss_qty =
let process_loss_qty = dialog.get_value("for_quantity") -
doc.get_value("for_quantity") - dialog.get_value("completed_qty") -
doc.get_value("completed_qty") - dialog.get_value("pending_qty");
doc.get_value("pending_qty"); if (process_loss_qty >= 0 && process_loss_qty != dialog.get_value("process_loss_qty")) {
if (process_loss_qty >= 0 && process_loss_qty != doc.get_value("process_loss_qty")) { dialog.set_value("process_loss_qty", process_loss_qty);
doc.set_value("process_loss_qty", process_loss_qty);
} }
}, },
}, },
@@ -392,14 +462,13 @@ frappe.ui.form.on("Job Card", {
label: __("Process Loss Quantity"), label: __("Process Loss Quantity"),
fieldname: "process_loss_qty", fieldname: "process_loss_qty",
onchange() { onchange() {
let doc = frm.job_completion_dialog; const dialog = frm.job_completion_dialog;
const remaining =
let pending_qty = dialog.get_value("for_quantity") -
doc.get_value("for_quantity") - dialog.get_value("completed_qty") -
doc.get_value("completed_qty") - dialog.get_value("process_loss_qty");
doc.get_value("process_loss_qty"); if (remaining >= 0 && remaining != dialog.get_value("pending_qty")) {
if (pending_qty >= 0 && pending_qty != doc.get_value("pending_qty")) { dialog.set_value("pending_qty", remaining);
doc.set_value("pending_qty", pending_qty);
} }
}, },
}, },
@@ -415,20 +484,16 @@ frappe.ui.form.on("Job Card", {
fieldname: "sub_operation", fieldname: "sub_operation",
options: "Operation", options: "Operation",
get_query() { get_query() {
let non_completed_operations = frm.doc.sub_operations.filter( const non_completed = frm.doc.sub_operations.filter((d) => d.status === "Pending");
(d) => d.status === "Pending"
);
return { return {
filters: { filters: { name: ["in", non_completed.map((d) => d.sub_operation)] },
name: ["in", non_completed_operations.map((d) => d.sub_operation)],
},
}; };
}, },
reqd: 1, reqd: 1,
}); });
} }
let last_completed_row = get_last_completed_row(frm.doc.time_logs); const last_completed_row = get_last_completed_row(frm.doc.time_logs);
let last_row = {}; let last_row = {};
if (frm.doc.sub_operations?.length && frm.doc.time_logs?.length) { if (frm.doc.sub_operations?.length && frm.doc.time_logs?.length) {
last_row = get_last_row(frm.doc.time_logs); last_row = get_last_row(frm.doc.time_logs);
@@ -461,7 +526,7 @@ frappe.ui.form.on("Job Card", {
end_time: data.end_time, end_time: data.end_time,
sub_operation: data.sub_operation, sub_operation: data.sub_operation,
}, },
callback: function (r) { callback() {
frm.reload_doc(); frm.reload_doc();
}, },
}); });
@@ -487,11 +552,8 @@ frappe.ui.form.on("Job Card", {
frm.call({ frm.call({
method: "start_timer", method: "start_timer",
doc: frm.doc, doc: frm.doc,
args: { args: { start_time, employees },
start_time: start_time, callback() {
employees: employees,
},
callback: function (r) {
frm.reload_doc(); frm.reload_doc();
frm.trigger("make_dashboard"); frm.trigger("make_dashboard");
}, },
@@ -499,7 +561,7 @@ frappe.ui.form.on("Job Card", {
}, },
make_finished_good(frm) { make_finished_good(frm) {
let fields = [ const fields = [
{ {
fieldtype: "Float", fieldtype: "Float",
label: __("Completed Quantity"), label: __("Completed Quantity"),
@@ -525,12 +587,9 @@ frappe.ui.form.on("Job Card", {
frm.call({ frm.call({
method: "make_finished_good", method: "make_finished_good",
doc: frm.doc, doc: frm.doc,
args: { args: { qty: data.qty, end_time: data.end_time },
qty: data.qty, callback(r) {
end_time: data.end_time, const doc = frappe.model.sync(r.message);
},
callback: function (r) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", doc[0].doctype, doc[0].name); frappe.set_route("Form", doc[0].doctype, doc[0].name);
}, },
}); });
@@ -541,8 +600,8 @@ frappe.ui.form.on("Job Card", {
); );
}, },
setup_quality_inspection: function (frm) { setup_quality_inspection(frm) {
let quality_inspection_field = frm.get_docfield("quality_inspection"); const quality_inspection_field = frm.get_docfield("quality_inspection");
quality_inspection_field.get_route_options_for_new_doc = function (frm) { quality_inspection_field.get_route_options_for_new_doc = function (frm) {
return { return {
inspection_type: "In Process", inspection_type: "In Process",
@@ -557,24 +616,22 @@ frappe.ui.form.on("Job Card", {
}; };
}, },
setup_corrective_job_card: function (frm) { setup_corrective_job_card(frm) {
frm.add_custom_button( frm.add_custom_button(
__("Corrective Job Card"), __("Corrective Job Card"),
() => { () => {
let operations = frm.doc.sub_operations.map((d) => d.sub_operation).concat(frm.doc.operation); const operations = frm.doc.sub_operations
.map((d) => d.sub_operation)
.concat(frm.doc.operation);
let fields = [ const fields = [
{ {
fieldtype: "Link", fieldtype: "Link",
label: __("Corrective Operation"), label: __("Corrective Operation"),
options: "Operation", options: "Operation",
fieldname: "operation", fieldname: "operation",
get_query() { get_query() {
return { return { filters: { is_corrective_operation: 1 } };
filters: {
is_corrective_operation: 1,
},
};
}, },
}, },
{ {
@@ -583,20 +640,14 @@ frappe.ui.form.on("Job Card", {
options: "Operation", options: "Operation",
fieldname: "for_operation", fieldname: "for_operation",
get_query() { get_query() {
return { return { filters: { name: ["in", operations] } };
filters: {
name: ["in", operations],
},
};
}, },
}, },
]; ];
frappe.prompt( frappe.prompt(
fields, fields,
(d) => { (d) => frm.events.make_corrective_job_card(frm, d.operation, d.for_operation),
frm.events.make_corrective_job_card(frm, d.operation, d.for_operation);
},
__("Select Corrective Operation") __("Select Corrective Operation")
); );
}, },
@@ -604,7 +655,7 @@ frappe.ui.form.on("Job Card", {
); );
}, },
make_corrective_job_card: function (frm, operation, for_operation) { make_corrective_job_card(frm, operation, for_operation) {
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card", method: "erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card",
args: { args: {
@@ -612,7 +663,7 @@ frappe.ui.form.on("Job Card", {
operation: operation, operation: operation,
for_operation: for_operation, for_operation: for_operation,
}, },
callback: function (r) { callback(r) {
if (r.message) { if (r.message) {
frappe.model.sync(r.message); frappe.model.sync(r.message);
frappe.set_route("Form", r.message.doctype, r.message.name); frappe.set_route("Form", r.message.doctype, r.message.name);
@@ -621,7 +672,7 @@ frappe.ui.form.on("Job Card", {
}); });
}, },
operation: function (frm) { operation(frm) {
frm.trigger("toggle_operation_number"); frm.trigger("toggle_operation_number");
if (frm.doc.operation && frm.doc.work_order) { if (frm.doc.operation && frm.doc.work_order) {
@@ -631,28 +682,22 @@ frappe.ui.form.on("Job Card", {
work_order: frm.doc.work_order, work_order: frm.doc.work_order,
operation: frm.doc.operation, operation: frm.doc.operation,
}, },
callback: function (r) { callback(r) {
if (r.message) { if (!r.message) return;
if (r.message.length == 1) {
frm.set_value("operation_id", r.message[0].name);
} else {
let args = [];
r.message.forEach((row) => { if (r.message.length == 1) {
args.push({ label: row.idx, value: row.name }); frm.set_value("operation_id", r.message[0].name);
}); } else {
const args = r.message.map((row) => ({ label: row.idx, value: row.name }));
let description = __("Operation {0} added multiple times in the work order {1}", [ const description = __("Operation {0} added multiple times in the work order {1}", [
frm.doc.operation, frm.doc.operation,
frm.doc.work_order, frm.doc.work_order,
]); ]);
frm.set_df_property("operation_row_number", "options", args);
frm.set_df_property("operation_row_number", "options", args); frm.set_df_property("operation_row_number", "description", description);
frm.set_df_property("operation_row_number", "description", description);
}
frm.trigger("toggle_operation_number");
} }
frm.trigger("toggle_operation_number");
}, },
}); });
} }
@@ -669,65 +714,35 @@ frappe.ui.form.on("Job Card", {
frm.toggle_reqd("operation_row_number", !frm.doc.operation_id && frm.doc.operation); frm.toggle_reqd("operation_row_number", !frm.doc.operation_id && frm.doc.operation);
}, },
make_time_log: function (frm, args) { make_time_log(frm, args) {
frm.events.update_sub_operation(frm, args); frm.events.update_sub_operation(frm, args);
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_time_log", method: "erpnext.manufacturing.doctype.job_card.job_card.make_time_log",
args: { args: { args },
args: args,
},
freeze: true, freeze: true,
callback: function () { callback() {
frm.reload_doc(); frm.reload_doc();
frm.trigger("make_dashboard"); frm.trigger("make_dashboard");
}, },
}); });
}, },
update_sub_operation: function (frm, args) { update_sub_operation(frm, args) {
if (frm.doc.sub_operations && frm.doc.sub_operations.length) { if (frm.doc.sub_operations?.length) {
let sub_operations = frm.doc.sub_operations.filter((d) => d.status != "Complete"); const pending_sub_ops = frm.doc.sub_operations.filter((d) => d.status != "Complete");
if (sub_operations && sub_operations.length) { if (pending_sub_ops.length) {
args["sub_operation"] = sub_operations[0].sub_operation; args["sub_operation"] = pending_sub_ops[0].sub_operation;
} }
} }
}, },
make_dashboard: function (frm) { make_dashboard(frm) {
if (frm.doc.__islocal) return; if (frm.doc.__islocal) return;
var section = "";
function setCurrentIncrement() {
currentIncrement += 1;
return currentIncrement;
}
function updateStopwatch(increment) {
var hours = Math.floor(increment / 3600);
var minutes = Math.floor((increment - hours * 3600) / 60);
var seconds = Math.floor(flt(increment - hours * 3600 - minutes * 60, 2));
$(section)
.find(".hours")
.text(hours < 10 ? "0" + hours.toString() : hours.toString());
$(section)
.find(".minutes")
.text(minutes < 10 ? "0" + minutes.toString() : minutes.toString());
$(section)
.find(".seconds")
.text(seconds < 10 ? "0" + seconds.toString() : seconds.toString());
}
function initialiseTimer() {
const interval = setInterval(function () {
var current = setCurrentIncrement();
updateStopwatch(current);
}, 1000);
}
frm.dashboard.refresh(); frm.dashboard.refresh();
const timer = `
const timer_html = `
<div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px; <div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px;
color:#545454;font-size:18px;display:inline-block;vertical-align:text-bottom;"> color:#545454;font-size:18px;display:inline-block;vertical-align:text-bottom;">
<span class="hours">00</span> <span class="hours">00</span>
@@ -737,20 +752,44 @@ frappe.ui.form.on("Job Card", {
<span class="seconds">00</span> <span class="seconds">00</span>
</div>`; </div>`;
let section;
if (frappe.utils.is_xs()) { if (frappe.utils.is_xs()) {
frm.dashboard.add_comment(timer, "white", true); frm.dashboard.add_comment(timer_html, "white", true);
section = frm.layout.wrapper.find(".form-message-container"); section = frm.layout.wrapper.find(".form-message-container");
} else { } else {
section = frm.toolbar.page.add_inner_message(timer); section = frm.toolbar.page.add_inner_message(timer_html);
} }
let currentIncrement = frm.events.get_current_time(frm); const pad = (n) => String(n).padStart(2, "0");
if (frm.doc.time_logs?.length && frm.doc.time_logs[cint(frm.doc.time_logs.length) - 1].to_time) {
updateStopwatch(currentIncrement); const update_stopwatch = (increment) => {
} else if (frm.doc.status == "On Hold") { const hours = Math.floor(increment / 3600);
updateStopwatch(currentIncrement); const minutes = Math.floor((increment - hours * 3600) / 60);
const seconds = Math.floor(flt(increment - hours * 3600 - minutes * 60, 2));
section.find(".hours").text(pad(hours));
section.find(".minutes").text(pad(minutes));
section.find(".seconds").text(pad(seconds));
};
let current_increment = frm.events.get_current_time(frm);
const start_timer = () => {
setInterval(() => {
current_increment += 1;
update_stopwatch(current_increment);
}, 1000);
};
const { time_logs, status } = frm.doc;
const last_log_complete = time_logs?.length && time_logs[cint(time_logs.length) - 1].to_time;
if (last_log_complete) {
update_stopwatch(current_increment);
} else if (status == "On Hold") {
update_stopwatch(current_increment);
} else { } else {
initialiseTimer(); start_timer();
} }
}, },
@@ -772,22 +811,22 @@ frappe.ui.form.on("Job Card", {
return current_time; return current_time;
}, },
hide_timer: function (frm) { hide_timer(frm) {
frm.toolbar.page.inner_toolbar.find(".stopwatch").remove(); frm.toolbar.page.inner_toolbar.find(".stopwatch").remove();
}, },
for_quantity: function (frm) { for_quantity(frm) {
frm.doc.items = []; frm.doc.items = [];
frm.call({ frm.call({
method: "get_required_items", method: "get_required_items",
doc: frm.doc, doc: frm.doc,
callback: function () { callback() {
refresh_field("items"); refresh_field("items");
}, },
}); });
}, },
make_material_request: function (frm) { make_material_request(frm) {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request", method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request",
frm: frm, frm: frm,
@@ -795,7 +834,7 @@ frappe.ui.form.on("Job Card", {
}); });
}, },
make_stock_entry: function (frm) { make_stock_entry(frm) {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_stock_entry", method: "erpnext.manufacturing.doctype.job_card.job_card.make_stock_entry",
frm: frm, frm: frm,
@@ -803,11 +842,11 @@ frappe.ui.form.on("Job Card", {
}); });
}, },
timer: function (frm) { timer(frm) {
return `<button> Start </button>`; return `<button> Start </button>`;
}, },
set_total_completed_qty: function (frm) { set_total_completed_qty(frm) {
frm.doc.total_completed_qty = 0; frm.doc.total_completed_qty = 0;
frm.doc.time_logs.forEach((d) => { frm.doc.time_logs.forEach((d) => {
if (d.completed_qty) { if (d.completed_qty) {
@@ -816,10 +855,9 @@ frappe.ui.form.on("Job Card", {
}); });
if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) { if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) {
let flt_precision = precision("for_quantity", frm.doc); const flt_precision = precision("for_quantity", frm.doc);
let process_loss_qty = const process_loss_qty =
flt(frm.doc.for_quantity, flt_precision) - flt(frm.doc.total_completed_qty, flt_precision); flt(frm.doc.for_quantity, flt_precision) - flt(frm.doc.total_completed_qty, flt_precision);
frm.set_value("process_loss_qty", process_loss_qty); frm.set_value("process_loss_qty", process_loss_qty);
} }
@@ -836,8 +874,8 @@ 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, cdt, cdn) { completed_qty(frm, cdt, cdn) {
let row = locals[cdt][cdn]; const row = locals[cdt][cdn];
if (!row.completed_qty) { if (!row.completed_qty) {
frappe.model.set_value(row.doctype, row.name, { frappe.model.set_value(row.doctype, row.name, {
time_in_mins: 0, time_in_mins: 0,
@@ -854,12 +892,8 @@ function get_seconds_diff(d1, d2) {
} }
function get_last_completed_row(time_logs) { function get_last_completed_row(time_logs) {
let completed_rows = time_logs.filter((d) => d.to_time); const completed_rows = time_logs.filter((d) => d.to_time);
return completed_rows[completed_rows.length - 1];
if (completed_rows?.length) {
let last_completed_row = completed_rows[completed_rows.length - 1];
return last_completed_row;
}
} }
function get_last_row(time_logs) { function get_last_row(time_logs) {