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
frappe.ui.form.on("Job Card", {
setup: function (frm) {
frm.set_query("operation", function () {
return {
query: "erpnext.manufacturing.doctype.job_card.job_card.get_operations",
filters: {
work_order: frm.doc.work_order,
},
};
});
setup(frm) {
frm.set_query("operation", () => ({
query: "erpnext.manufacturing.doctype.job_card.job_card.get_operations",
filters: { work_order: frm.doc.work_order },
}));
frm.set_query("serial_and_batch_bundle", () => {
return {
filters: {
item_code: frm.doc.production_item,
voucher_type: frm.doc.doctype,
voucher_no: ["in", [frm.doc.name, ""]],
is_cancelled: 0,
},
};
});
frm.set_query("serial_and_batch_bundle", () => ({
filters: {
item_code: frm.doc.production_item,
voucher_type: frm.doc.doctype,
voucher_no: ["in", [frm.doc.name, ""]],
is_cancelled: 0,
},
}));
frm.set_query("item_code", "secondary_items", () => {
return {
filters: {
disabled: 0,
},
};
});
frm.set_query("item_code", "secondary_items", () => ({
filters: { disabled: 0 },
}));
frm.set_query("operation", "time_logs", () => {
let operations = (frm.doc.sub_operations || []).map((d) => d.sub_operation);
return {
filters: {
name: ["in", operations],
},
};
const operations = (frm.doc.sub_operations || []).map((d) => d.sub_operation);
return { filters: { name: ["in", operations] } };
});
frm.set_query("work_order", function () {
return {
filters: {
status: ["not in", ["Cancelled", "Closed", "Stopped"]],
},
};
});
frm.set_query("work_order", () => ({
filters: { status: ["not in", ["Cancelled", "Closed", "Stopped"]] },
}));
frm.events.set_company_filters(frm, "target_warehouse");
frm.events.set_company_filters(frm, "source_warehouse");
frm.events.set_company_filters(frm, "wip_warehouse");
frm.set_query("source_warehouse", "items", () => {
return {
filters: {
company: frm.doc.company,
},
};
frm.set_query("source_warehouse", "items", () => ({
filters: { 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) {
if (doc.status == "Pending") {
return "red";
} else {
return doc.status === "Complete" ? "green" : "orange";
}
});
frm.set_query("employee", () => {
return {
filters: {
company: frm.doc.company,
status: "Active",
},
};
});
frm.set_query("employee", () => ({
filters: {
company: frm.doc.company,
status: "Active",
},
}));
},
pending_qty(frm) {
@@ -88,7 +62,7 @@ frappe.ui.form.on("Job Card", {
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) {
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]));
}
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;
refresh_field("process_loss_qty");
},
set_company_filters(frm, fieldname) {
frm.set_query(fieldname, () => {
return {
filters: {
company: frm.doc.company,
},
};
});
frm.set_query(fieldname, () => ({
filters: { company: frm.doc.company },
}));
},
make_fields_read_only(frm) {
@@ -123,33 +93,29 @@ frappe.ui.form.on("Job Card", {
},
setup_stock_entry(frm) {
if (
frm.doc.track_semi_finished_goods &&
frm.doc.docstatus === 1 &&
!frm.doc.is_subcontracted &&
(frm.doc.skip_material_transfer || frm.doc.transferred_qty > 0) &&
flt(frm.doc.manufactured_qty) + flt(frm.doc.process_loss_qty) < flt(frm.doc.for_quantity)
) {
frm.add_custom_button(__("Make Stock Entry"), () => {
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);
}
);
}).addClass("btn-primary");
}
const { doc } = frm;
const can_make_stock_entry =
doc.track_semi_finished_goods &&
doc.docstatus === 1 &&
!doc.is_subcontracted &&
(doc.skip_material_transfer || doc.transferred_qty > 0) &&
flt(doc.manufactured_qty) + flt(doc.process_loss_qty) < flt(doc.for_quantity);
if (!can_make_stock_entry) return;
frm.add_custom_button(__("Make Stock Entry"), () => {
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)
);
}).addClass("btn-primary");
},
make_manufacture_stock_entry(frm, submit_entry) {
frm.call({
method: "make_stock_entry_for_semi_fg_item",
args: {
auto_submit: submit_entry,
},
args: { auto_submit: submit_entry },
doc: frm.doc,
freeze: true,
callback() {
@@ -158,80 +124,58 @@ frappe.ui.form.on("Job Card", {
});
},
<<<<<<< HEAD
refresh: function (frm) {
frm.trigger("setup_stock_entry");
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");
if (!frm.is_new() && frm.doc.__onload?.work_order_closed) {
if (!frm.is_new() && doc.__onload?.work_order_closed) {
frm.disable_save();
return;
}
if (frm.doc.is_subcontracted) {
if (doc.is_subcontracted) {
frm.trigger("make_subcontracting_po");
return;
}
if (frm.doc.docstatus > 0) {
if (doc.docstatus > 0) {
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);
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("time_in_mins", "read_only", 1);
}
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 excess_transfer_allowed = frm.doc.__onload.job_card_excess_transfer;
frm.events.setup_material_transfer_buttons(frm, has_items);
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
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) {
if (doc.docstatus == 1 && !doc.is_corrective_job_card && !doc.finished_good) {
frm.trigger("setup_corrective_job_card");
}
frm.set_query("quality_inspection", function () {
return {
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
filters: {
item_code: frm.doc.production_item,
reference_name: frm.doc.name,
},
};
});
frm.set_query("quality_inspection", () => ({
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
filters: {
item_code: doc.production_item,
reference_name: doc.name,
},
}));
frm.trigger("toggle_operation_number");
<<<<<<< HEAD
if (
frm.doc.for_quantity + frm.doc.process_loss_qty > frm.doc.total_completed_qty &&
(frm.doc.skip_material_transfer ||
@@ -312,36 +256,165 @@ frappe.ui.form.on("Job Card", {
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");
if (frm.doc.work_order) {
frappe.db.get_value("Work Order", frm.doc.work_order, "transfer_material_against").then((r) => {
if (r.message.transfer_material_against == "Work Order" && !frm.doc.operation_row_id) {
if (doc.work_order) {
frappe.db.get_value("Work Order", doc.work_order, "transfer_material_against").then((r) => {
if (r.message.transfer_material_against == "Work Order" && !doc.operation_row_id) {
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) {
sbb_field.get_route_options_for_new_doc = () => {
return {
item_code: frm.doc.production_item,
warehouse: frm.doc.wip_warehouse,
voucher_type: frm.doc.doctype,
};
};
sbb_field.get_route_options_for_new_doc = () => ({
item_code: doc.production_item,
warehouse: doc.wip_warehouse,
voucher_type: 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) {
let pending_qty = frm.doc.for_quantity - frm.doc.total_completed_qty;
if (frm.doc.pending_qty > 0) {
pending_qty = frm.doc.pending_qty;
}
let fields = [
const fields = [
{
fieldtype: "Float",
label: __("Qty to Manufacture"),
@@ -349,10 +422,9 @@ frappe.ui.form.on("Job Card", {
reqd: 1,
default: pending_qty,
change() {
let doc = frm.job_completion_dialog;
doc.set_value("completed_qty", doc.get_value("for_quantity"));
doc.set_value("process_loss_qty", 0);
const dialog = frm.job_completion_dialog;
dialog.set_value("completed_qty", dialog.get_value("for_quantity"));
dialog.set_value("process_loss_qty", 0);
},
},
{
@@ -362,11 +434,10 @@ frappe.ui.form.on("Job Card", {
reqd: 1,
default: pending_qty,
change() {
let doc = frm.job_completion_dialog;
let pending_qty = doc.get_value("for_quantity") - doc.get_value("completed_qty");
if (pending_qty > 0 && pending_qty != doc.get_value("pending_qty")) {
doc.set_value("pending_qty", pending_qty);
const dialog = frm.job_completion_dialog;
const remaining = dialog.get_value("for_quantity") - dialog.get_value("completed_qty");
if (remaining > 0 && remaining != dialog.get_value("pending_qty")) {
dialog.set_value("pending_qty", remaining);
}
},
},
@@ -376,14 +447,13 @@ frappe.ui.form.on("Job Card", {
fieldname: "pending_qty",
default: 0.0,
change() {
let doc = frm.job_completion_dialog;
let process_loss_qty =
doc.get_value("for_quantity") -
doc.get_value("completed_qty") -
doc.get_value("pending_qty");
if (process_loss_qty >= 0 && process_loss_qty != doc.get_value("process_loss_qty")) {
doc.set_value("process_loss_qty", process_loss_qty);
const dialog = frm.job_completion_dialog;
const process_loss_qty =
dialog.get_value("for_quantity") -
dialog.get_value("completed_qty") -
dialog.get_value("pending_qty");
if (process_loss_qty >= 0 && process_loss_qty != dialog.get_value("process_loss_qty")) {
dialog.set_value("process_loss_qty", process_loss_qty);
}
},
},
@@ -392,14 +462,13 @@ frappe.ui.form.on("Job Card", {
label: __("Process Loss Quantity"),
fieldname: "process_loss_qty",
onchange() {
let doc = frm.job_completion_dialog;
let pending_qty =
doc.get_value("for_quantity") -
doc.get_value("completed_qty") -
doc.get_value("process_loss_qty");
if (pending_qty >= 0 && pending_qty != doc.get_value("pending_qty")) {
doc.set_value("pending_qty", pending_qty);
const dialog = frm.job_completion_dialog;
const remaining =
dialog.get_value("for_quantity") -
dialog.get_value("completed_qty") -
dialog.get_value("process_loss_qty");
if (remaining >= 0 && remaining != dialog.get_value("pending_qty")) {
dialog.set_value("pending_qty", remaining);
}
},
},
@@ -415,20 +484,16 @@ frappe.ui.form.on("Job Card", {
fieldname: "sub_operation",
options: "Operation",
get_query() {
let non_completed_operations = frm.doc.sub_operations.filter(
(d) => d.status === "Pending"
);
const non_completed = frm.doc.sub_operations.filter((d) => d.status === "Pending");
return {
filters: {
name: ["in", non_completed_operations.map((d) => d.sub_operation)],
},
filters: { name: ["in", non_completed.map((d) => d.sub_operation)] },
};
},
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 = {};
if (frm.doc.sub_operations?.length && frm.doc.time_logs?.length) {
last_row = get_last_row(frm.doc.time_logs);
@@ -461,7 +526,7 @@ frappe.ui.form.on("Job Card", {
end_time: data.end_time,
sub_operation: data.sub_operation,
},
callback: function (r) {
callback() {
frm.reload_doc();
},
});
@@ -487,11 +552,8 @@ frappe.ui.form.on("Job Card", {
frm.call({
method: "start_timer",
doc: frm.doc,
args: {
start_time: start_time,
employees: employees,
},
callback: function (r) {
args: { start_time, employees },
callback() {
frm.reload_doc();
frm.trigger("make_dashboard");
},
@@ -499,7 +561,7 @@ frappe.ui.form.on("Job Card", {
},
make_finished_good(frm) {
let fields = [
const fields = [
{
fieldtype: "Float",
label: __("Completed Quantity"),
@@ -525,12 +587,9 @@ frappe.ui.form.on("Job Card", {
frm.call({
method: "make_finished_good",
doc: frm.doc,
args: {
qty: data.qty,
end_time: data.end_time,
},
callback: function (r) {
var doc = frappe.model.sync(r.message);
args: { qty: data.qty, end_time: data.end_time },
callback(r) {
const doc = frappe.model.sync(r.message);
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) {
let quality_inspection_field = frm.get_docfield("quality_inspection");
setup_quality_inspection(frm) {
const quality_inspection_field = frm.get_docfield("quality_inspection");
quality_inspection_field.get_route_options_for_new_doc = function (frm) {
return {
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(
__("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",
label: __("Corrective Operation"),
options: "Operation",
fieldname: "operation",
get_query() {
return {
filters: {
is_corrective_operation: 1,
},
};
return { filters: { is_corrective_operation: 1 } };
},
},
{
@@ -583,20 +640,14 @@ frappe.ui.form.on("Job Card", {
options: "Operation",
fieldname: "for_operation",
get_query() {
return {
filters: {
name: ["in", operations],
},
};
return { filters: { name: ["in", operations] } };
},
},
];
frappe.prompt(
fields,
(d) => {
frm.events.make_corrective_job_card(frm, d.operation, d.for_operation);
},
(d) => frm.events.make_corrective_job_card(frm, d.operation, d.for_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({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card",
args: {
@@ -612,7 +663,7 @@ frappe.ui.form.on("Job Card", {
operation: operation,
for_operation: for_operation,
},
callback: function (r) {
callback(r) {
if (r.message) {
frappe.model.sync(r.message);
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");
if (frm.doc.operation && frm.doc.work_order) {
@@ -631,28 +682,22 @@ frappe.ui.form.on("Job Card", {
work_order: frm.doc.work_order,
operation: frm.doc.operation,
},
callback: function (r) {
if (r.message) {
if (r.message.length == 1) {
frm.set_value("operation_id", r.message[0].name);
} else {
let args = [];
callback(r) {
if (!r.message) return;
r.message.forEach((row) => {
args.push({ label: row.idx, value: row.name });
});
let description = __("Operation {0} added multiple times in the work order {1}", [
frm.doc.operation,
frm.doc.work_order,
]);
frm.set_df_property("operation_row_number", "options", args);
frm.set_df_property("operation_row_number", "description", description);
}
frm.trigger("toggle_operation_number");
if (r.message.length == 1) {
frm.set_value("operation_id", r.message[0].name);
} else {
const args = r.message.map((row) => ({ label: row.idx, value: row.name }));
const description = __("Operation {0} added multiple times in the work order {1}", [
frm.doc.operation,
frm.doc.work_order,
]);
frm.set_df_property("operation_row_number", "options", args);
frm.set_df_property("operation_row_number", "description", description);
}
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);
},
make_time_log: function (frm, args) {
make_time_log(frm, args) {
frm.events.update_sub_operation(frm, args);
frappe.call({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_time_log",
args: {
args: args,
},
args: { args },
freeze: true,
callback: function () {
callback() {
frm.reload_doc();
frm.trigger("make_dashboard");
},
});
},
update_sub_operation: function (frm, args) {
if (frm.doc.sub_operations && frm.doc.sub_operations.length) {
let sub_operations = frm.doc.sub_operations.filter((d) => d.status != "Complete");
if (sub_operations && sub_operations.length) {
args["sub_operation"] = sub_operations[0].sub_operation;
update_sub_operation(frm, args) {
if (frm.doc.sub_operations?.length) {
const pending_sub_ops = frm.doc.sub_operations.filter((d) => d.status != "Complete");
if (pending_sub_ops.length) {
args["sub_operation"] = pending_sub_ops[0].sub_operation;
}
}
},
make_dashboard: function (frm) {
make_dashboard(frm) {
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();
const timer = `
const timer_html = `
<div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px;
color:#545454;font-size:18px;display:inline-block;vertical-align:text-bottom;">
<span class="hours">00</span>
@@ -737,20 +752,44 @@ frappe.ui.form.on("Job Card", {
<span class="seconds">00</span>
</div>`;
let section;
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");
} 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);
if (frm.doc.time_logs?.length && frm.doc.time_logs[cint(frm.doc.time_logs.length) - 1].to_time) {
updateStopwatch(currentIncrement);
} else if (frm.doc.status == "On Hold") {
updateStopwatch(currentIncrement);
const pad = (n) => String(n).padStart(2, "0");
const update_stopwatch = (increment) => {
const hours = Math.floor(increment / 3600);
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 {
initialiseTimer();
start_timer();
}
},
@@ -772,22 +811,22 @@ frappe.ui.form.on("Job Card", {
return current_time;
},
hide_timer: function (frm) {
hide_timer(frm) {
frm.toolbar.page.inner_toolbar.find(".stopwatch").remove();
},
for_quantity: function (frm) {
for_quantity(frm) {
frm.doc.items = [];
frm.call({
method: "get_required_items",
doc: frm.doc,
callback: function () {
callback() {
refresh_field("items");
},
});
},
make_material_request: function (frm) {
make_material_request(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request",
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({
method: "erpnext.manufacturing.doctype.job_card.job_card.make_stock_entry",
frm: frm,
@@ -803,11 +842,11 @@ frappe.ui.form.on("Job Card", {
});
},
timer: function (frm) {
timer(frm) {
return `<button> Start </button>`;
},
set_total_completed_qty: function (frm) {
set_total_completed_qty(frm) {
frm.doc.total_completed_qty = 0;
frm.doc.time_logs.forEach((d) => {
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) {
let flt_precision = precision("for_quantity", frm.doc);
let process_loss_qty =
const flt_precision = precision("for_quantity", frm.doc);
const process_loss_qty =
flt(frm.doc.for_quantity, flt_precision) - flt(frm.doc.total_completed_qty, flt_precision);
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", {
completed_qty: function (frm, cdt, cdn) {
let row = locals[cdt][cdn];
completed_qty(frm, cdt, cdn) {
const row = locals[cdt][cdn];
if (!row.completed_qty) {
frappe.model.set_value(row.doctype, row.name, {
time_in_mins: 0,
@@ -854,12 +892,8 @@ function get_seconds_diff(d1, d2) {
}
function get_last_completed_row(time_logs) {
let completed_rows = time_logs.filter((d) => d.to_time);
if (completed_rows?.length) {
let last_completed_row = completed_rows[completed_rows.length - 1];
return last_completed_row;
}
const completed_rows = time_logs.filter((d) => d.to_time);
return completed_rows[completed_rows.length - 1];
}
function get_last_row(time_logs) {