From 029511f019f5086041e9b420d650d151c2904454 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 25 Sep 2024 14:25:42 +0530 Subject: [PATCH] fix: plant floor design changes --- .../doctype/bom_creator/bom_creator.js | 65 +----------------- .../doctype/bom_creator/bom_creator.json | 15 ++++- .../doctype/bom_creator/bom_creator.py | 5 +- .../doctype/plant_floor/plant_floor.js | 7 ++ .../doctype/plant_floor/plant_floor.json | 2 +- .../doctype/workstation/workstation.js | 41 ++++++------ .../doctype/workstation/workstation.json | 10 ++- .../doctype/workstation/workstation.py | 67 ++++++++++++++----- .../workstation/workstation_job_card.html | 53 +++++++-------- .../bom_configurator.bundle.js | 22 ++++++ .../js/plant_floor_visual/visual_plant.js | 24 +++++++ .../visual_plant_floor_template.html | 30 +++------ erpnext/public/scss/erpnext.scss | 12 +++- 13 files changed, 200 insertions(+), 153 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js index c83897aa7782..2c879a3f62d0 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js @@ -90,69 +90,10 @@ frappe.ui.form.on("BOM Creator", { }, { fieldtype: "Section Break" }, { - label: __("Track Operations"), - fieldtype: "Check", - fieldname: "track_operations", - onchange: (r) => { - let track_operations = dialog.get_value("track_operations"); - if (r.type === "input" && !track_operations) { - dialog.set_value("track_semi_finished_goods", 0); - } - }, - }, - { fieldtype: "Column Break" }, - { - label: __("Track Semi Finished Goods"), - fieldtype: "Check", - fieldname: "track_semi_finished_goods", - depends_on: "eval:doc.track_operations", - }, - { - fieldtype: "Section Break", - label: __("Final Product Operation"), - depends_on: "eval:doc.track_semi_finished_goods", - }, - { - label: __("Operation"), - fieldtype: "Link", - fieldname: "operation", - options: "Operation", - default: "Assembly", - mandatory_depends_on: "eval:doc.track_semi_finished_goods", - depends_on: "eval:doc.track_semi_finished_goods", - }, - { - label: __("Operation Time (in mins)"), - fieldtype: "Float", - fieldname: "operation_time", - mandatory_depends_on: "eval:doc.track_semi_finished_goods", - depends_on: "eval:doc.track_semi_finished_goods", - }, - { fieldtype: "Column Break" }, - { - label: __("Workstation Type"), + label: __("Routing"), fieldtype: "Link", - fieldname: "workstation_type", - options: "Workstation", - depends_on: "eval:doc.track_semi_finished_goods", - }, - { - label: __("Workstation"), - fieldtype: "Link", - fieldname: "workstation", - options: "Workstation", - depends_on: "eval:doc.track_semi_finished_goods", - get_query() { - let workstation_type = dialog.get_value("workstation_type"); - - if (workstation_type) { - return { - filters: { - workstation_type: dialog.get_value("workstation_type"), - }, - }; - } - }, + fieldname: "routing", + options: "Routing", }, ], primary_action_label: __("Create"), diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json index 47660741260d..3e4929c7af86 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.json +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.json @@ -19,6 +19,8 @@ "qty", "project", "uom", + "section_break_xvld", + "routing", "raw_materials_tab", "currency_detail", "rm_cost_as_per", @@ -393,6 +395,17 @@ { "fieldname": "column_break_buha", "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_xvld", + "fieldtype": "Section Break", + "label": "Operations Routing" + }, + { + "fieldname": "routing", + "fieldtype": "Link", + "label": "Routing", + "options": "Routing" } ], "hide_toolbar": 1, @@ -404,7 +417,7 @@ "link_fieldname": "bom_creator" } ], - "modified": "2024-09-20 09:05:52.945112", + "modified": "2024-09-26 17:07:32.111198", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Creator", diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py index a456b5af8be5..330152cfeef9 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py @@ -38,9 +38,8 @@ class BOMCreator(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: - from frappe.types import DF - from erpnext.manufacturing.doctype.bom_creator_item.bom_creator_item import BOMCreatorItem + from frappe.types import DF amended_from: DF.Link | None backflush_from_wip_warehouse: DF.Check @@ -64,6 +63,7 @@ class BOMCreator(Document): raw_material_cost: DF.Currency remarks: DF.TextEditor | None rm_cost_as_per: DF.Literal["Valuation Rate", "Last Purchase Rate", "Price List"] + routing: DF.Link | None set_rate_based_on_warehouse: DF.Check skip_material_transfer: DF.Check source_warehouse: DF.Link | None @@ -595,6 +595,7 @@ def add_sub_assembly(**kwargs): { "item_code": row.item_code, "qty": row.qty, + "operation": row.operation, "fg_item": bom_item.item_code, "uom": item_info.stock_uom, "fg_reference_id": name, diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js index 3d18900b70cb..f8e4d04c485f 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js @@ -40,6 +40,7 @@ frappe.ui.form.on("Plant Floor", { refresh(frm) { frm.trigger("prepare_stock_dashboard"); frm.trigger("prepare_workstation_dashboard"); + frm.trigger("update_realtime_status"); if (!frm.is_new()) { frm.trigger("add_workstation"); @@ -58,6 +59,12 @@ frappe.ui.form.on("Plant Floor", { }); }, + update_realtime_status(frm) { + frappe.realtime.on("update_workstation_status", (data) => { + frappe.visual_plant_floor.update_status(data); + }); + }, + prepare_stock_dashboard(frm) { if (!frm.doc.warehouse) { return; diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json index 4c4dfd8f6679..f595cfd5fc48 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json @@ -72,7 +72,7 @@ "hide_toolbar": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-09-19 18:06:36.481625", + "modified": "2024-09-25 10:27:41.139634", "modified_by": "Administrator", "module": "Manufacturing", "name": "Plant Floor", diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js index dbcb0113c8fe..4c1c9d2c9766 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.js +++ b/erpnext/manufacturing/doctype/workstation/workstation.js @@ -190,11 +190,11 @@ class WorkstationDashboard { setup_menu_actions() { let me = this; this.job_cards.forEach((data) => { - me.menu_actions = me.$wrapper.find(`.menu-actions[data-job-card='${data.name}']`); - $(me.menu_actions).find(".btn-start").hide(); - $(me.menu_actions).find(".btn-resume").hide(); - $(me.menu_actions).find(".btn-pause").hide(); - $(me.menu_actions).find(".btn-complete").hide(); + me.menu_btns = me.$wrapper.find(`.job-card-link[data-name='${data.name}']`); + + $(me.menu_btns).find(".btn-resume").hide(); + $(me.menu_btns).find(".btn-pause").hide(); + $(me.menu_btns).find(".btn-complete .btn").attr("disabled", true); if ( data.for_quantity + data.process_loss_qty > data.total_completed_qty && @@ -203,15 +203,18 @@ class WorkstationDashboard { !data.finished_good) ) { if (!data.time_logs?.length) { - $(me.menu_actions).find(".btn-start").show(); + $(me.menu_btns).find(".btn-start").show(); } else if (data.is_paused) { - $(me.menu_actions).find(".btn-resume").show(); + $(me.menu_btns).find(".btn-start").hide(); + $(me.menu_btns).find(".btn-resume").show(); } else if (data.for_quantity - data.manufactured_qty > 0) { + $(me.menu_btns).find(".btn-start").hide(); if (!data.is_paused) { - $(me.menu_actions).find(".btn-pause").show(); + $(me.menu_btns).find(".btn-pause").show(); } - $(me.menu_actions).find(".btn-complete").show(); + $(me.menu_btns).find(".btn-complete").show(); + $(me.menu_btns).find(".btn-complete .btn").attr("disabled", false); } } }); @@ -243,26 +246,26 @@ class WorkstationDashboard { }); this.$wrapper.find(".btn-start").on("click", (e) => { - let job_card = $(e.currentTarget).closest("ul").attr("data-job-card"); + let job_card = $(e.currentTarget).closest("div").attr("data-job-card"); this.start_job(job_card); }); this.$wrapper.find(".btn-pause").on("click", (e) => { - let job_card = $(e.currentTarget).closest("ul").attr("data-job-card"); + let job_card = $(e.currentTarget).closest("div").attr("data-job-card"); me.update_job_card(job_card, "pause_job", { end_time: frappe.datetime.now_datetime(), }); }); this.$wrapper.find(".btn-resume").on("click", (e) => { - let job_card = $(e.currentTarget).closest("ul").attr("data-job-card"); + let job_card = $(e.currentTarget).closest("div").attr("data-job-card"); me.update_job_card(job_card, "resume_job", { start_time: frappe.datetime.now_datetime(), }); }); this.$wrapper.find(".btn-complete").on("click", (e) => { - let job_card = $(e.currentTarget).closest("ul").attr("data-job-card"); + let job_card = $(e.currentTarget).closest("div").attr("data-job-card"); let for_quantity = $(e.currentTarget).attr("data-qty"); me.complete_job(job_card, for_quantity); }); @@ -315,6 +318,12 @@ class WorkstationDashboard { let me = this; return [ + { + label: __("Start Time"), + fieldname: "start_time", + fieldtype: "Datetime", + default: frappe.datetime.now_datetime(), + }, { label: __("Employee"), fieldname: "employee", @@ -337,12 +346,6 @@ class WorkstationDashboard { } }, }, - { - label: __("Start Time"), - fieldname: "start_time", - fieldtype: "Datetime", - default: frappe.datetime.now_datetime(), - }, { fieldtype: "Section Break" }, { label: __("Employees"), diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json index 846effe8ea18..34aa7f0f9159 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.json +++ b/erpnext/manufacturing/doctype/workstation/workstation.json @@ -15,6 +15,7 @@ "workstation_name", "workstation_type", "plant_floor", + "disabled", "column_break_3", "production_capacity", "warehouse", @@ -240,13 +241,20 @@ "fieldname": "section_break_mqqv", "fieldtype": "Section Break", "hide_border": 1 + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } ], + "hide_toolbar": 1, "icon": "icon-wrench", "idx": 1, "image_field": "on_status_image", "links": [], - "modified": "2024-06-20 14:17:13.806609", + "modified": "2024-09-26 13:41:12.279344", "modified_by": "Administrator", "module": "Manufacturing", "name": "Workstation", diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index 2b0125c3d68d..8f40e2fd31f7 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -42,13 +42,11 @@ class Workstation(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: + from erpnext.manufacturing.doctype.workstation_working_hour.workstation_working_hour import WorkstationWorkingHour from frappe.types import DF - from erpnext.manufacturing.doctype.workstation_working_hour.workstation_working_hour import ( - WorkstationWorkingHour, - ) - description: DF.Text | None + disabled: DF.Check holiday_list: DF.Link | None hour_rate: DF.Currency hour_rate_consumable: DF.Currency @@ -71,6 +69,11 @@ def before_save(self): self.set_data_based_on_workstation_type() self.set_hour_rate() self.set_total_working_hours() + self.disabled_workstation() + + def disabled_workstation(self): + if self.disabled: + self.status = "Off" def set_total_working_hours(self): self.total_working_hours = 0.0 @@ -124,6 +127,28 @@ def on_update(self): self.validate_overlap_for_operation_timings() self.update_bom_operation() + if self.plant_floor: + self.publish_workstation_status() + + def publish_workstation_status(self): + if not self._doc_before_save: + return + + if self._doc_before_save.get("status") == self.status: + return + + data = get_workstations(plant_floor=self.plant_floor, workstation_name=self.name)[0] + + color_map = get_color_map() + data["old_color"] = color_map.get(self._doc_before_save.get("status"), "red") + + frappe.publish_realtime( + "update_workstation_status", + data, + doctype="Plant Floor", + docname=self.plant_floor, + ) + def validate_overlap_for_operation_timings(self): """Check if there is no overlap in setting Workstation Operating Hours""" for d in self.get("working_hours"): @@ -238,10 +263,13 @@ def get_job_cards(workstation, job_card=None): user_employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, "name") for row in jc_data: + if row.status == "Open": + row.status = "Not Started" + item_code = row.finished_good or row.production_item row.fg_uom = frappe.get_cached_value("Item", item_code, "stock_uom") - row.status_color = get_status_color(row.status) + row.status_colour = get_status_color(row.status) row.job_card_link = f""" {row.name} """ @@ -423,8 +451,10 @@ def get_workstations(**kwargs): _workstation.on_status_image, _workstation.off_status_image, ) - .orderby(_workstation.workstation_type, _workstation.name) - .where(_workstation.plant_floor == kwargs.plant_floor) + .orderby(_workstation.creation, _workstation.workstation_type, _workstation.name) + .where( + (_workstation.plant_floor == kwargs.plant_floor) & (_workstation.disabled == 0) + ) ) if kwargs.workstation: @@ -436,16 +466,12 @@ def get_workstations(**kwargs): if kwargs.workstation_status: query = query.where(_workstation.status == kwargs.workstation_status) + if kwargs.workstation_name: + query = query.where(_workstation.name == kwargs.workstation_name) + data = query.run(as_dict=True) - color_map = { - "Production": "green", - "Off": "gray", - "Idle": "gray", - "Problem": "red", - "Maintenance": "yellow", - "Setup": "blue", - } + color_map = get_color_map() for d in data: d.workstation_name = get_link_to_form("Workstation", d.name) @@ -458,6 +484,17 @@ def get_workstations(**kwargs): return data +def get_color_map(): + return { + "Production": "green", + "Off": "gray", + "Idle": "gray", + "Problem": "red", + "Maintenance": "yellow", + "Setup": "blue", + } + + @frappe.whitelist() def update_job_card(job_card, method, **kwargs): if isinstance(kwargs, dict): diff --git a/erpnext/manufacturing/doctype/workstation/workstation_job_card.html b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html index 847963b70885..2049f3fe6a55 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_job_card.html +++ b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html @@ -17,7 +17,7 @@