diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 23650b6873..079350b63b 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -955,6 +955,14 @@ class JobCard(Document): if update_status: self.db_set("status", self.status) + if self.status in ["Completed", "Work In Progress"]: + status = { + "Completed": "Off", + "Work In Progress": "Production", + }.get(self.status) + + self.update_status_in_workstation(status) + def set_wip_warehouse(self): if not self.wip_warehouse: self.wip_warehouse = frappe.db.get_single_value( @@ -1035,6 +1043,12 @@ class JobCard(Document): return False + def update_status_in_workstation(self, status): + if not self.workstation: + return + + frappe.db.set_value("Workstation", self.workstation, "status", status) + @frappe.whitelist() def make_time_log(args): diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js index 427893743a..67e5acd9da 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js @@ -2,11 +2,31 @@ // For license information, please see license.txt frappe.ui.form.on("Plant Floor", { - refresh(frm) { - frm.trigger('prepare_dashboard') + setup(frm) { + frm.trigger("setup_queries"); }, - prepare_dashboard(frm) { + setup_queries(frm) { + frm.set_query("warehouse", (doc) => { + if (!doc.company) { + frappe.throw(__("Please select Company first")); + } + + return { + filters: { + "is_group": 0, + "company": doc.company + } + } + }); + }, + + refresh(frm) { + frm.trigger('prepare_stock_dashboard') + frm.trigger('prepare_workstation_dashboard') + }, + + prepare_workstation_dashboard(frm) { let wrapper = $(frm.fields_dict["plant_dashboard"].wrapper); wrapper.empty(); @@ -16,4 +36,221 @@ frappe.ui.form.on("Plant Floor", { plant_floor: frm.doc.name, }); }, + + prepare_stock_dashboard(frm) { + if (!frm.doc.warehouse) { + return; + } + + let wrapper = $(frm.fields_dict["stock_summary"].wrapper); + wrapper.empty(); + + frappe.visual_stock = new VisualStock({ + wrapper: wrapper, + frm: frm, + }); + }, }); + + +class VisualStock { + constructor(opts) { + Object.assign(this, opts); + this.make(); + } + + make() { + this.prepare_filters(); + this.prepare_stock_summary({ + start:0 + }); + } + + prepare_filters() { + this.wrapper.append(` +
+
+ +
+
+ `); + + this.item_filter = frappe.ui.form.make_control({ + df: { + fieldtype: "Link", + fieldname: "item_code", + placeholder: __("Item"), + options: "Item", + onchange: () => this.prepare_stock_summary({ + start:0, + item_code: this.item_filter.value + }) + }, + parent: this.wrapper.find('.filter-section'), + render_input: true, + }); + + this.item_filter.$wrapper.addClass('form-column col-sm-3'); + this.item_filter.$wrapper.find('.clearfix').hide(); + + this.item_group_filter = frappe.ui.form.make_control({ + df: { + fieldtype: "Link", + fieldname: "item_group", + placeholder: __("Item Group"), + options: "Item Group", + change: () => this.prepare_stock_summary({ + start:0, + item_group: this.item_group_filter.value + }) + }, + parent: this.wrapper.find('.filter-section'), + render_input: true, + }); + + this.item_group_filter.$wrapper.addClass('form-column col-sm-3'); + this.item_group_filter.$wrapper.find('.clearfix').hide(); + } + + prepare_stock_summary(args) { + let {start, item_code, item_group} = args; + + this.get_stock_summary(start, item_code, item_group).then(stock_summary => { + this.wrapper.find('.stock-summary-container').remove(); + this.wrapper.append(`
`); + this.stock_summary = stock_summary.message; + this.render_stock_summary(); + this.bind_events(); + }); + } + + async get_stock_summary(start, item_code, item_group) { + let stock_summary = await frappe.call({ + method: "erpnext.manufacturing.doctype.plant_floor.plant_floor.get_stock_summary", + args: { + warehouse: this.frm.doc.warehouse, + start: start, + item_code: item_code, + item_group: item_group + } + }); + + return stock_summary; + } + + render_stock_summary() { + let template = frappe.render_template("stock_summary_template", { + stock_summary: this.stock_summary + }); + + this.wrapper.find('.stock-summary-container').append(template); + } + + bind_events() { + this.wrapper.find('.btn-add').click((e) => { + this.item_code = decodeURI($(e.currentTarget).attr('data-item-code')); + + this.make_stock_entry([ + { + label: __("For Item"), + fieldname: "item_code", + fieldtype: "Data", + read_only: 1, + default: this.item_code + }, + { + label: __("Quantity"), + fieldname: "qty", + fieldtype: "Float", + reqd: 1 + } + ], __("Add Stock"), "Material Receipt") + }); + + this.wrapper.find('.btn-move').click((e) => { + this.item_code = decodeURI($(e.currentTarget).attr('data-item-code')); + + this.make_stock_entry([ + { + label: __("For Item"), + fieldname: "item_code", + fieldtype: "Data", + read_only: 1, + default: this.item_code + }, + { + label: __("Quantity"), + fieldname: "qty", + fieldtype: "Float", + reqd: 1 + }, + { + label: __("To Warehouse"), + fieldname: "to_warehouse", + fieldtype: "Link", + options: "Warehouse", + reqd: 1, + get_query: () => { + return { + filters: { + "is_group": 0, + "company": this.frm.doc.company + } + } + } + } + ], __("Move Stock"), "Material Transfer") + }); + } + + make_stock_entry(fields, title, stock_entry_type) { + frappe.prompt(fields, + (values) => { + this.values = values; + this.stock_entry_type = stock_entry_type; + this.update_values(); + + this.frm.call({ + method: "make_stock_entry", + doc: this.frm.doc, + args: { + kwargs: this.values, + }, + callback: (r) => { + if (!r.exc) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }) + }, __(title), __("Create") + ); + } + + update_values() { + if (!this.values.qty) { + frappe.throw(__("Quantity is required")); + } + + let from_warehouse = ""; + let to_warehouse = ""; + + if (this.stock_entry_type == "Material Receipt") { + to_warehouse = this.frm.doc.warehouse; + } else { + from_warehouse = this.frm.doc.warehouse; + to_warehouse = this.values.to_warehouse; + } + + this.values = { + ...this.values, + ...{ + "company": this.frm.doc.company, + "item_code": this.item_code, + "from_warehouse": from_warehouse, + "to_warehouse": to_warehouse, + "purpose": this.stock_entry_type, + } + } + } +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json index aa6eb1dd40..be0052c47b 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json @@ -10,11 +10,13 @@ "field_order": [ "workstations_tab", "plant_dashboard", + "stock_summary_tab", + "stock_summary", "details_tab", "column_break_mvbx", "floor_name", - "section_break_cczv", - "volumetric_weight" + "company", + "warehouse" ], "fields": [ { @@ -27,7 +29,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "workstations_tab", "fieldtype": "Tab Break", - "label": "Dashboard" + "label": "Workstations" }, { "fieldname": "plant_dashboard", @@ -37,25 +39,39 @@ { "fieldname": "details_tab", "fieldtype": "Tab Break", - "label": "Details" + "label": "Floor" }, { "fieldname": "column_break_mvbx", "fieldtype": "Column Break" }, { - "fieldname": "section_break_cczv", - "fieldtype": "Section Break" + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" }, { - "fieldname": "volumetric_weight", - "fieldtype": "Float", - "label": "Volumetric Weight" + "depends_on": "eval:!doc.__islocal && doc.warehouse", + "fieldname": "stock_summary_tab", + "fieldtype": "Tab Break", + "label": "Stock Summary" + }, + { + "fieldname": "stock_summary", + "fieldtype": "HTML", + "label": "Stock Summary" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2023-12-04 15:36:09.641203", + "modified": "2024-01-30 11:59:07.508535", "modified_by": "Administrator", "module": "Manufacturing", "name": "Plant Floor", diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.py b/erpnext/manufacturing/doctype/plant_floor/plant_floor.py index 729cc3337a..d30b7d1049 100644 --- a/erpnext/manufacturing/doctype/plant_floor/plant_floor.py +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.py @@ -1,8 +1,10 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document +from frappe.query_builder import Order +from frappe.utils import get_link_to_form, nowdate, nowtime class PlantFloor(Document): @@ -14,8 +16,114 @@ class PlantFloor(Document): if TYPE_CHECKING: from frappe.types import DF + company: DF.Link | None floor_name: DF.Data | None - volumetric_weight: DF.Float + warehouse: DF.Link | None # end: auto-generated types - pass + @frappe.whitelist() + def make_stock_entry(self, kwargs): + if isinstance(kwargs, str): + kwargs = frappe.parse_json(kwargs) + + if isinstance(kwargs, dict): + kwargs = frappe._dict(kwargs) + + stock_entry = frappe.new_doc("Stock Entry") + stock_entry.update( + { + "company": kwargs.company, + "from_warehouse": kwargs.from_warehouse, + "to_warehouse": kwargs.to_warehouse, + "purpose": kwargs.purpose, + "stock_entry_type": kwargs.purpose, + "posting_date": nowdate(), + "posting_time": nowtime(), + "items": self.get_item_details(kwargs), + } + ) + + stock_entry.set_missing_values() + + return stock_entry + + def get_item_details(self, kwargs) -> list[dict]: + item_details = frappe.db.get_value( + "Item", kwargs.item_code, ["item_name", "stock_uom", "item_group", "description"], as_dict=True + ) + item_details.update( + { + "qty": kwargs.qty, + "uom": item_details.stock_uom, + "item_code": kwargs.item_code, + "conversion_factor": 1, + "s_warehouse": kwargs.from_warehouse, + "t_warehouse": kwargs.to_warehouse, + } + ) + + return [item_details] + + +@frappe.whitelist() +def get_stock_summary(warehouse, start=0, item_code=None, item_group=None): + stock_details = get_stock_details( + warehouse, start=start, item_code=item_code, item_group=item_group + ) + + max_count = 0.0 + for d in stock_details: + d.actual_or_pending = ( + d.projected_qty + + d.reserved_qty + + d.reserved_qty_for_production + + d.reserved_qty_for_sub_contract + ) + d.pending_qty = 0 + d.total_reserved = ( + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract + ) + if d.actual_or_pending > d.actual_qty: + d.pending_qty = d.actual_or_pending - d.actual_qty + + d.max_count = max(d.actual_or_pending, d.actual_qty, d.total_reserved, max_count) + max_count = d.max_count + d.item_link = get_link_to_form("Item", d.item_code) + + return stock_details + + +def get_stock_details(warehouse, start=0, item_code=None, item_group=None): + item_table = frappe.qb.DocType("Item") + bin_table = frappe.qb.DocType("Bin") + + query = ( + frappe.qb.from_(bin_table) + .inner_join(item_table) + .on(bin_table.item_code == item_table.name) + .select( + bin_table.item_code, + bin_table.actual_qty, + bin_table.projected_qty, + bin_table.reserved_qty, + bin_table.reserved_qty_for_production, + bin_table.reserved_qty_for_sub_contract, + bin_table.reserved_qty_for_production_plan, + bin_table.reserved_stock, + item_table.item_name, + item_table.item_group, + item_table.image, + ) + .where(bin_table.warehouse == warehouse) + .limit(20) + .offset(start) + .orderby(bin_table.actual_qty, order=Order.desc) + ) + + if item_code: + query = query.where(bin_table.item_code == item_code) + + if item_group: + query = query.where(item_table.item_group == item_group) + + return query.run(as_dict=True) diff --git a/erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html b/erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html new file mode 100644 index 0000000000..8824c9811b --- /dev/null +++ b/erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html @@ -0,0 +1,61 @@ +{% $.each(stock_summary, (idx, row) => { %} +
+
+ {% if(row.image) { %} + + {% } else { %} +
{{frappe.get_abbr(row.item_code, 2)}}
+ {% } %} +
+
+ {% if (row.item_code === row.item_name) { %} + {{row.item_link}} + {% } else { %} + {{row.item_link}} +

+ {{row.item_name}} +

+ {% } %} + +
+
+ {{ frappe.format(row.actual_qty, { fieldtype: "Float"})}} +
+
+ {{ frappe.format(row.reserved_stock, { fieldtype: "Float"})}} +
+
+ + + {{ row.total_reserved }} + + + + + + + + {{ row.actual_qty }} {{ (row.pending_qty > 0) ? ("(" + row.pending_qty+ ")") : "" }} + + + + + {% if row.pending_qty > 0 %} + + + {% endif %} + + + +
+
+ +
+
+ +
+
+{% }); %} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js index 4ffc506f52..e3ad3fe3cc 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.js +++ b/erpnext/manufacturing/doctype/workstation/workstation.js @@ -94,18 +94,25 @@ class WorkstationDashboard { }, callback: (r) => { if (r.message) { - this.render_job_cards(r.message); + this.job_cards = r.message; + this.render_job_cards(); } } }); } - render_job_cards(job_cards) { + render_job_cards() { let template = frappe.render_template("workstation_job_card", { - data: job_cards + data: this.job_cards }); this.$wrapper.html(template); + this.prepare_timer(); + this.toggle_job_card(); + this.bind_events(); + } + + toggle_job_card() { this.$wrapper.find(".collapse-indicator-job").on("click", (e) => { $(e.currentTarget).closest(".form-dashboard-section").find(".section-body-job-card").toggleClass("hide") if ($(e.currentTarget).closest(".form-dashboard-section").find(".section-body-job-card").hasClass("hide")) @@ -114,4 +121,198 @@ class WorkstationDashboard { $(e.currentTarget).html(frappe.utils.icon("es-line-up", "sm", "mb-1")) }); } + + bind_events() { + this.$wrapper.find(".make-material-request").on("click", (e) => { + let job_card = $(e.currentTarget).attr("job-card"); + this.make_material_request(job_card); + }); + + this.$wrapper.find(".btn-start").on("click", (e) => { + let job_card = $(e.currentTarget).attr("job-card"); + this.start_job(job_card); + }); + + this.$wrapper.find(".btn-complete").on("click", (e) => { + let job_card = $(e.currentTarget).attr("job-card"); + let pending_qty = flt($(e.currentTarget).attr("pending-qty")); + this.complete_job(job_card, pending_qty); + }); + } + + start_job(job_card) { + let me = this; + frappe.prompt([ + { + fieldtype: 'Datetime', + label: __('Start Time'), + fieldname: 'start_time', + reqd: 1, + default: frappe.datetime.now_datetime() + }, + { + label: __('Operator'), + fieldname: 'employee', + fieldtype: 'Link', + options: 'Employee', + } + ], data => { + this.frm.call({ + method: "start_job", + doc: this.frm.doc, + args: { + job_card: job_card, + from_time: data.start_time, + employee: data.employee, + }, + callback(r) { + if (r.message) { + me.job_cards = [r.message]; + me.prepare_timer() + me.update_job_card_details(); + } + } + }); + }, __("Enter Value"), __("Start Job")); + } + + complete_job(job_card, qty_to_manufacture) { + let me = this; + let fields = [ + { + fieldtype: 'Float', + label: __('Completed Quantity'), + fieldname: 'qty', + reqd: 1, + default: flt(qty_to_manufacture || 0) + }, + { + fieldtype: 'Datetime', + label: __('End Time'), + fieldname: 'end_time', + default: frappe.datetime.now_datetime() + }, + ]; + + frappe.prompt(fields, data => { + if (data.qty <= 0) { + frappe.throw(__("Quantity should be greater than 0")); + } + + this.frm.call({ + method: "complete_job", + doc: this.frm.doc, + args: { + job_card: job_card, + qty: data.qty, + to_time: data.end_time, + }, + callback: function(r) { + if (r.message) { + me.job_cards = [r.message]; + me.prepare_timer() + me.update_job_card_details(); + } + } + }); + }, __("Enter Value"), __("Submit")); + } + + make_material_request(job_card) { + frappe.call({ + method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request", + args: { + source_name: job_card, + }, + callback: (r) => { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + } + + prepare_timer() { + this.job_cards.forEach((data) => { + if (data.time_logs?.length) { + data._current_time = this.get_current_time(data); + if (data.time_logs[cint(data.time_logs.length) - 1].to_time) { + this.updateStopwatch(data); + } else { + this.initialiseTimer(data); + } + } + }); + } + + update_job_card_details() { + let color_map = { + "Pending": "var(--bg-blue)", + "In Process": "var(--bg-yellow)", + "Submitted": "var(--bg-blue)", + "Open": "var(--bg-gray)", + "Closed": "var(--bg-green)", + "Work In Progress": "var(--bg-orange)", + } + + this.job_cards.forEach((data) => { + let job_card_selector = this.$wrapper.find(` + [data-name='${data.name}']` + ); + + $(job_card_selector).find(".job-card-status").text(data.status); + $(job_card_selector).find(".job-card-status").css("backgroundColor", color_map[data.status]); + + if (data.status === "Work In Progress") { + $(job_card_selector).find(".btn-start").addClass("hide"); + $(job_card_selector).find(".btn-complete").removeClass("hide"); + } else if (data.status === "Completed") { + $(job_card_selector).find(".btn-start").addClass("hide"); + $(job_card_selector).find(".btn-complete").addClass("hide"); + } + }); + } + + initialiseTimer(data) { + setInterval(() => { + data._current_time += 1; + this.updateStopwatch(data); + }, 1000); + } + + updateStopwatch(data) { + let increment = data._current_time; + let hours = Math.floor(increment / 3600); + let minutes = Math.floor((increment - (hours * 3600)) / 60); + let seconds = cint(increment - (hours * 3600) - (minutes * 60)); + + let job_card_selector = `[data-job-card='${data.name}']` + let timer_selector = this.$wrapper.find(job_card_selector) + + $(timer_selector).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); + $(timer_selector).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); + $(timer_selector).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); + } + + get_current_time(data) { + let current_time = 0.0; + data.time_logs.forEach(d => { + if (d.to_time) { + if (d.time_in_mins) { + current_time += flt(d.time_in_mins, 2) * 60; + } else { + current_time += this.get_seconds_diff(d.to_time, d.from_time); + } + } else { + current_time += this.get_seconds_diff(frappe.datetime.now_datetime(), d.from_time); + } + }); + + return current_time; + } + + get_seconds_diff(d1, d2) { + return moment(d1).diff(d2, "seconds"); + } } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index c2b1e63067..90aa993d7e 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -164,6 +164,28 @@ class Workstation(Document): return schedule_date + @frappe.whitelist() + def start_job(self, job_card, from_time, employee): + doc = frappe.get_doc("Job Card", job_card) + doc.append("time_logs", {"from_time": from_time, "employee": employee}) + doc.save(ignore_permissions=True) + + return doc + + @frappe.whitelist() + def complete_job(self, job_card, qty, to_time): + doc = frappe.get_doc("Job Card", job_card) + for row in doc.time_logs: + if not row.to_time: + row.to_time = to_time + row.time_in_mins = time_diff_in_hours(row.to_time, row.from_time) / 60 + row.completed_qty = qty + + doc.save(ignore_permissions=True) + doc.submit() + + return doc + @frappe.whitelist() def get_job_cards(workstation): @@ -177,6 +199,7 @@ def get_job_cards(workstation): "operation", "total_completed_qty", "for_quantity", + "transferred_qty", "status", "expected_start_date", "expected_end_date", @@ -193,6 +216,11 @@ def get_job_cards(workstation): job_cards = [row.name for row in jc_data] raw_materials = get_raw_materials(job_cards) + time_logs = get_time_logs(job_cards) + + allow_excess_transfer = frappe.db.get_single_value( + "Manufacturing Settings", "job_card_excess_transfer" + ) for row in jc_data: row.progress_percent = ( @@ -204,12 +232,16 @@ def get_job_cards(workstation): row.work_order_link = get_link_to_form("Work Order", row.work_order) row.raw_materials = raw_materials.get(row.name, []) + row.time_logs = time_logs.get(row.name, []) + row.make_material_request = False + if row.for_quantity > row.transferred_qty or allow_excess_transfer: + row.make_material_request = True return jc_data def get_status_color(status): - colos_map = { + color_map = { "Pending": "var(--bg-blue)", "In Process": "var(--bg-yellow)", "Submitted": "var(--bg-blue)", @@ -218,7 +250,7 @@ def get_status_color(status): "Work In Progress": "var(--bg-orange)", } - return colos_map.get(status, "var(--bg-blue)") + return color_map.get(status, "var(--bg-blue)") def get_raw_materials(job_cards): @@ -245,6 +277,29 @@ def get_raw_materials(job_cards): return raw_materials +def get_time_logs(job_cards): + time_logs = {} + + data = frappe.get_all( + "Job Card Time Log", + fields=[ + "parent", + "name", + "employee", + "from_time", + "to_time", + "time_in_mins", + ], + filters={"parent": ["in", job_cards], "parentfield": "time_logs"}, + order_by="parent, idx", + ) + + for row in data: + time_logs.setdefault(row.parent, []).append(row) + + return time_logs + + @frappe.whitelist() def get_default_holiday_list(): return frappe.get_cached_value( diff --git a/erpnext/manufacturing/doctype/workstation/workstation_job_card.html b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html index 3c0ef6d837..97707855db 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_job_card.html +++ b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html @@ -27,13 +27,28 @@ {{ d.work_order_link }} -
-
- {{ frappe.format(d.expected_start_date, { fieldtype: 'Datetime' }) }} +
+
+ 00 + : + 00 + : + 00
+ + {% if(d.status === "Open") { %} +
+ {{ frappe.format(d.expected_start_date, { fieldtype: 'Datetime' }) }} +
+ {% } else { %} +
+ {{ frappe.format(d.expected_end_date, { fieldtype: 'Datetime' }) }} +
+ {% } %} +
-
+
{{ d.status }}
@@ -48,11 +63,24 @@ {{ d.for_quantity }} / {{ d.total_completed_qty }}
+
+ + +

- {{ __("Raw Materials") }} +
+
+ {{ __("Raw Materials") }} +
+ {% if(d.make_material_request) { %} +
+ +
+ {% } %} +
{% if(d.raw_materials) { %} diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index e3b632dba2..d2520d6b7e 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,6 +1,6 @@ { "charts": [], - "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"YHCQG3wAGv\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Creator\",\"col\":3}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"Bw3jwRMiei\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Plant Floor\",\"col\":3}},{\"id\":\"4hPVRQke_x\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Visual Plant Floor\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"YHCQG3wAGv\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Creator\",\"col\":3}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"Ubj6zXcmIQ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Plant Floor\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 17:11:37.032604", "custom_blocks": [], "docstatus": 0, @@ -316,7 +316,7 @@ "type": "Link" } ], - "modified": "2023-11-30 15:21:14.577990", + "modified": "2024-01-30 21:49:58.577218", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -341,7 +341,6 @@ "doc_view": "List", "label": "Plant Floor", "link_to": "Plant Floor", - "stats_filter": "[]", "type": "DocType" }, { @@ -351,13 +350,6 @@ "link_to": "BOM Creator", "type": "DocType" }, - { - "color": "Grey", - "doc_view": "List", - "label": "Visual Plant Floor", - "link_to": "visual-plant-floor", - "type": "Page" - }, { "color": "Grey", "doc_view": "List", diff --git a/erpnext/public/js/plant_floor_visual/visual_plant.js b/erpnext/public/js/plant_floor_visual/visual_plant.js index b1d120fd93..8cd73adc57 100644 --- a/erpnext/public/js/plant_floor_visual/visual_plant.js +++ b/erpnext/public/js/plant_floor_visual/visual_plant.js @@ -26,6 +26,7 @@ class VisualPlantFloor { this.filter_wrapper = this.wrapper.find('.plant-floor-filter'); this.visualization_wrapper = this.wrapper.find('.plant-floor-visualization'); } else if(this.plant_floor) { + this.wrapper.find('.plant-floor').css('border', 'none'); this.prepare_data(); } } @@ -138,7 +139,6 @@ class VisualPlantFloor { } render_workstations() { - console.log(this.wrapper.find('.plant-floor-container')) this.wrapper.find('.plant-floor-container').empty(); let template = frappe.render_template("visual_plant_floor_template", { workstations: this.workstations diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss index ef09854c08..1626b7c894 100644 --- a/erpnext/public/scss/erpnext.scss +++ b/erpnext/public/scss/erpnext.scss @@ -510,7 +510,6 @@ body[data-route="pos"] { } .plant-floor-container { - padding-top: 10px; display: grid; grid-template-columns: repeat(6,minmax(0,1fr)); gap: var(--margin-xl); @@ -530,7 +529,7 @@ body[data-route="pos"] { width: 100%; font-size: 50px; margin: var(--margin-sm); - min-height: 11rem; + min-height: 9rem; } .workstation-abbr {