From 77b044f1a6e7273b21ce5a884429875b10d3bb2e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:48:10 +0100 Subject: [PATCH 1/6] fix: don't overwrite existing terms in transaction --- erpnext/public/js/controllers/transaction.js | 34 +++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 6406735763..525e6463fc 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -795,16 +795,34 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe me.frm.set_value("letter_head", company_doc.default_letter_head); } } - let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"]; - if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && - selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + + if ( + company_doc.default_selling_terms && + frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + [ + "Sales Invoice", + "Quotation", + "Sales Order", + "Delivery Note", + ].includes(me.frm.doc.doctype) && + !me.frm.doc.tc_name + ) { me.frm.set_value("tc_name", company_doc.default_selling_terms); } - let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order", - "Material Request", "Purchase Receipt"]; - // Purchase Invoice is excluded as per issue #3345 - if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && - buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + + if ( + company_doc.default_buying_terms && + frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + [ + "Request for Quotation", + "Supplier Quotation", + "Purchase Order", + // Purchase Invoice is excluded as per issue #3345 + "Material Request", + "Purchase Receipt", + ].includes(me.frm.doc.doctype) && + !me.frm.doc.tc_name + ) { me.frm.set_value("tc_name", company_doc.default_buying_terms); } From 68c997aa067f342f6e432e9f9c84416a0a4cc1bf Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 6 Oct 2023 17:45:26 +0530 Subject: [PATCH 2/6] feat: visual plant floor --- .../doctype/plant_floor/__init__.py | 0 .../doctype/plant_floor/plant_floor.js | 19 ++ .../doctype/plant_floor/plant_floor.json | 81 ++++++++ .../doctype/plant_floor/plant_floor.py | 21 ++ .../doctype/plant_floor/test_plant_floor.py | 9 + .../doctype/workstation/workstation.js | 61 ++++++ .../doctype/workstation/workstation.json | 112 +++++++++- .../doctype/workstation/workstation.py | 150 ++++++++++++++ .../workstation/workstation_job_card.html | 97 +++++++++ .../doctype/workstation/workstation_list.js | 15 +- .../workstation_working_hour.json | 192 +++++------------- .../page/visual_plant_floor/__init__.py | 0 .../visual_plant_floor/visual_plant_floor.js | 13 ++ .../visual_plant_floor.json | 29 +++ .../manufacturing/manufacturing.json | 19 +- erpnext/public/js/erpnext.bundle.js | 2 + .../js/plant_floor_visual/visual_plant.js | 157 ++++++++++++++ .../visual_plant_floor_template.html | 19 ++ erpnext/public/scss/erpnext.scss | 51 +++++ 19 files changed, 896 insertions(+), 151 deletions(-) create mode 100644 erpnext/manufacturing/doctype/plant_floor/__init__.py create mode 100644 erpnext/manufacturing/doctype/plant_floor/plant_floor.js create mode 100644 erpnext/manufacturing/doctype/plant_floor/plant_floor.json create mode 100644 erpnext/manufacturing/doctype/plant_floor/plant_floor.py create mode 100644 erpnext/manufacturing/doctype/plant_floor/test_plant_floor.py create mode 100644 erpnext/manufacturing/doctype/workstation/workstation_job_card.html create mode 100644 erpnext/manufacturing/page/visual_plant_floor/__init__.py create mode 100644 erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.js create mode 100644 erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.json create mode 100644 erpnext/public/js/plant_floor_visual/visual_plant.js create mode 100644 erpnext/public/js/templates/visual_plant_floor_template.html diff --git a/erpnext/manufacturing/doctype/plant_floor/__init__.py b/erpnext/manufacturing/doctype/plant_floor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.js b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js new file mode 100644 index 0000000000..427893743a --- /dev/null +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.js @@ -0,0 +1,19 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Plant Floor", { + refresh(frm) { + frm.trigger('prepare_dashboard') + }, + + prepare_dashboard(frm) { + let wrapper = $(frm.fields_dict["plant_dashboard"].wrapper); + wrapper.empty(); + + frappe.visual_plant_floor = new frappe.ui.VisualPlantFloor({ + wrapper: wrapper, + skip_filters: true, + plant_floor: frm.doc.name, + }); + }, +}); diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.json b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json new file mode 100644 index 0000000000..aa6eb1dd40 --- /dev/null +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:floor_name", + "creation": "2023-10-06 15:06:07.976066", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "workstations_tab", + "plant_dashboard", + "details_tab", + "column_break_mvbx", + "floor_name", + "section_break_cczv", + "volumetric_weight" + ], + "fields": [ + { + "fieldname": "floor_name", + "fieldtype": "Data", + "label": "Floor Name", + "unique": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "workstations_tab", + "fieldtype": "Tab Break", + "label": "Dashboard" + }, + { + "fieldname": "plant_dashboard", + "fieldtype": "HTML", + "label": "Plant Dashboard" + }, + { + "fieldname": "details_tab", + "fieldtype": "Tab Break", + "label": "Details" + }, + { + "fieldname": "column_break_mvbx", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_cczv", + "fieldtype": "Section Break" + }, + { + "fieldname": "volumetric_weight", + "fieldtype": "Float", + "label": "Volumetric Weight" + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2023-12-04 15:36:09.641203", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Plant Floor", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/plant_floor/plant_floor.py b/erpnext/manufacturing/doctype/plant_floor/plant_floor.py new file mode 100644 index 0000000000..729cc3337a --- /dev/null +++ b/erpnext/manufacturing/doctype/plant_floor/plant_floor.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class PlantFloor(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + floor_name: DF.Data | None + volumetric_weight: DF.Float + # end: auto-generated types + + pass diff --git a/erpnext/manufacturing/doctype/plant_floor/test_plant_floor.py b/erpnext/manufacturing/doctype/plant_floor/test_plant_floor.py new file mode 100644 index 0000000000..2fac211336 --- /dev/null +++ b/erpnext/manufacturing/doctype/plant_floor/test_plant_floor.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPlantFloor(FrappeTestCase): + pass diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js index f830b170ed..4ffc506f52 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.js +++ b/erpnext/manufacturing/doctype/workstation/workstation.js @@ -2,6 +2,28 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Workstation", { + set_illustration_image(frm) { + let status_image_field = frm.doc.status == "Production" ? frm.doc.on_status_image : frm.doc.off_status_image; + if (status_image_field) { + frm.sidebar.image_wrapper.find(".sidebar-image").attr("src", status_image_field); + } + }, + + refresh(frm) { + frm.trigger("set_illustration_image"); + frm.trigger("prepapre_dashboard"); + }, + + prepapre_dashboard(frm) { + let $parent = $(frm.fields_dict["workstation_dashboard"].wrapper); + $parent.empty(); + + let workstation_dashboard = new WorkstationDashboard({ + wrapper: $parent, + frm: frm + }); + }, + onload(frm) { if(frm.is_new()) { @@ -54,3 +76,42 @@ frappe.tour['Workstation'] = [ ]; + + +class WorkstationDashboard { + constructor({ wrapper, frm }) { + this.$wrapper = $(wrapper); + this.frm = frm; + + this.prepapre_dashboard(); + } + + prepapre_dashboard() { + frappe.call({ + method: "erpnext.manufacturing.doctype.workstation.workstation.get_job_cards", + args: { + workstation: this.frm.doc.name + }, + callback: (r) => { + if (r.message) { + this.render_job_cards(r.message); + } + } + }); + } + + render_job_cards(job_cards) { + let template = frappe.render_template("workstation_job_card", { + data: job_cards + }); + + this.$wrapper.html(template); + 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")) + $(e.currentTarget).html(frappe.utils.icon("es-line-down", "sm", "mb-1")) + else + $(e.currentTarget).html(frappe.utils.icon("es-line-up", "sm", "mb-1")) + }); + } +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json index 881cba0cce..5912714052 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.json +++ b/erpnext/manufacturing/doctype/workstation/workstation.json @@ -8,10 +8,24 @@ "document_type": "Setup", "engine": "InnoDB", "field_order": [ + "dashboard_tab", + "workstation_dashboard", + "details_tab", "workstation_name", - "production_capacity", - "column_break_3", "workstation_type", + "plant_floor", + "column_break_3", + "production_capacity", + "warehouse", + "production_capacity_section", + "parts_per_hour", + "workstation_status_tab", + "status", + "column_break_glcv", + "illustration_section", + "on_status_image", + "column_break_etmc", + "off_status_image", "over_heads", "hour_rate_electricity", "hour_rate_consumable", @@ -24,7 +38,9 @@ "description", "working_hours_section", "holiday_list", - "working_hours" + "working_hours", + "total_working_hours", + "connections_tab" ], "fields": [ { @@ -120,9 +136,10 @@ }, { "default": "1", + "description": "Run parallel job cards in a workstation", "fieldname": "production_capacity", "fieldtype": "Int", - "label": "Production Capacity", + "label": "Job Capacity", "reqd": 1 }, { @@ -145,12 +162,97 @@ { "fieldname": "section_break_11", "fieldtype": "Section Break" + }, + { + "fieldname": "plant_floor", + "fieldtype": "Link", + "label": "Plant Floor", + "options": "Plant Floor" + }, + { + "fieldname": "workstation_status_tab", + "fieldtype": "Tab Break", + "label": "Workstation Status" + }, + { + "fieldname": "illustration_section", + "fieldtype": "Section Break", + "label": "Status Illustration" + }, + { + "fieldname": "column_break_etmc", + "fieldtype": "Column Break" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "options": "Production\nOff\nIdle\nProblem\nMaintenance\nSetup" + }, + { + "fieldname": "column_break_glcv", + "fieldtype": "Column Break" + }, + { + "fieldname": "on_status_image", + "fieldtype": "Attach Image", + "label": "Active Status" + }, + { + "fieldname": "off_status_image", + "fieldtype": "Attach Image", + "label": "Inactive Status" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "production_capacity_section", + "fieldtype": "Section Break", + "label": "Production Capacity" + }, + { + "fieldname": "parts_per_hour", + "fieldtype": "Float", + "label": "Parts Per Hour" + }, + { + "fieldname": "total_working_hours", + "fieldtype": "Float", + "label": "Total Working Hours" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "dashboard_tab", + "fieldtype": "Tab Break", + "label": "Job Cards" + }, + { + "fieldname": "details_tab", + "fieldtype": "Tab Break", + "label": "Details" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "workstation_dashboard", + "fieldtype": "HTML", + "label": "Workstation Dashboard" } ], "icon": "icon-wrench", "idx": 1, + "image_field": "on_status_image", "links": [], - "modified": "2022-11-04 17:39:01.549346", + "modified": "2023-11-30 12:43:35.808845", "modified_by": "Administrator", "module": "Manufacturing", "name": "Workstation", diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index 0f05eaac00..973c99421d 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -11,7 +11,11 @@ from frappe.utils import ( comma_and, flt, formatdate, + get_link_to_form, + get_time, + get_url_to_form, getdate, + time_diff_in_hours, time_diff_in_seconds, to_timedelta, ) @@ -60,6 +64,23 @@ class Workstation(Document): def before_save(self): self.set_data_based_on_workstation_type() self.set_hour_rate() + self.set_total_working_hours() + + def set_total_working_hours(self): + self.total_working_hours = 0.0 + for row in self.working_hours: + self.validate_working_hours(row) + + if row.start_time and row.end_time: + row.hours = flt(time_diff_in_hours(row.end_time, row.start_time), row.precision("hours")) + self.total_working_hours += row.hours + + def validate_working_hours(self, row): + if not (row.start_time and row.end_time): + frappe.throw(_("Row #{0}: Start Time and End Time are required").format(row.idx)) + + if get_time(row.start_time) >= get_time(row.end_time): + frappe.throw(_("Row #{0}: Start Time must be before End Time").format(row.idx)) def set_hour_rate(self): self.hour_rate = ( @@ -144,6 +165,86 @@ class Workstation(Document): return schedule_date +@frappe.whitelist() +def get_job_cards(workstation): + if frappe.has_permission("Job Card", "read"): + jc_data = frappe.get_all( + "Job Card", + fields=[ + "name", + "production_item", + "work_order", + "operation", + "total_completed_qty", + "for_quantity", + "status", + "expected_start_date", + "expected_end_date", + "time_required", + "wip_warehouse", + ], + filters={ + "workstation": workstation, + "docstatus": ("<", 2), + "status": ["not in", ["Completed", "Stopped"]], + }, + order_by="expected_start_date, expected_end_date", + ) + + job_cards = [row.name for row in jc_data] + raw_materials = get_raw_materials(job_cards) + + for row in jc_data: + row.progress_percent = ( + flt(row.total_completed_qty / row.for_quantity * 100, 2) if row.for_quantity else 0 + ) + row.progress_title = _("Total completed quantity: {0}").format(row.total_completed_qty) + row.status_color = get_status_color(row.status) + row.job_card_link = get_link_to_form("Job Card", row.name) + row.work_order_link = get_link_to_form("Work Order", row.work_order) + + row.raw_materials = raw_materials.get(row.name, []) + + return jc_data + + +def get_status_color(status): + colos_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)", + } + + return colos_map.get(status, "var(--bg-blue)") + + +def get_raw_materials(job_cards): + raw_materials = {} + + data = frappe.get_all( + "Job Card Item", + fields=[ + "parent", + "item_code", + "item_group", + "uom", + "item_name", + "source_warehouse", + "required_qty", + "transferred_qty", + ], + filters={"parent": ["in", job_cards]}, + ) + + for row in data: + raw_materials.setdefault(row.parent, []).append(row) + + return raw_materials + + @frappe.whitelist() def get_default_holiday_list(): return frappe.get_cached_value( @@ -201,3 +302,52 @@ def check_workstation_for_holiday(workstation, from_datetime, to_datetime): + "\n".join(applicable_holidays), WorkstationHolidayError, ) + + +@frappe.whitelist() +def get_workstations(**kwargs): + kwargs = frappe._dict(kwargs) + _workstation = frappe.qb.DocType("Workstation") + + query = ( + frappe.qb.from_(_workstation) + .select( + _workstation.name, + _workstation.description, + _workstation.status, + _workstation.on_status_image, + _workstation.off_status_image, + ) + .orderby(_workstation.workstation_type, _workstation.name) + .where(_workstation.plant_floor == kwargs.plant_floor) + ) + + if kwargs.workstation: + query = query.where(_workstation.name == kwargs.workstation) + + if kwargs.workstation_type: + query = query.where(_workstation.workstation_type == kwargs.workstation_type) + + if kwargs.workstation_status: + query = query.where(_workstation.status == kwargs.workstation_status) + + data = query.run(as_dict=True) + + color_map = { + "Production": "var(--green-600)", + "Off": "var(--gray-600)", + "Idle": "var(--gray-600)", + "Problem": "var(--red-600)", + "Maintenance": "var(--yellow-600)", + "Setup": "var(--blue-600)", + } + + for d in data: + d.workstation_name = get_link_to_form("Workstation", d.name) + d.status_image = d.on_status_image + d.background_color = color_map.get(d.status, "var(--red-600)") + d.workstation_link = get_url_to_form("Workstation", d.name) + if d.status != "Production": + d.status_image = d.off_status_image + + return data diff --git a/erpnext/manufacturing/doctype/workstation/workstation_job_card.html b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html new file mode 100644 index 0000000000..3c0ef6d837 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/workstation_job_card.html @@ -0,0 +1,97 @@ + + +
+{% $.each(data, (idx, d) => { %} + +{% }); %} +
\ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js index 61f2062ec0..86928cafcb 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_list.js +++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js @@ -1,5 +1,16 @@ frappe.listview_settings['Workstation'] = { - // add_fields: ["status"], - // filters:[["status","=", "Open"]] + add_fields: ["status"], + get_indicator: function(doc) { + let color_map = { + "Production": "green", + "Off": "gray", + "Idle": "gray", + "Problem": "red", + "Maintenance": "yellow", + "Setup": "blue", + } + + return [__(doc.status), color_map[doc.status], true]; + } }; diff --git a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json index a79182fb31..b185f7d29d 100644 --- a/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json +++ b/erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json @@ -1,150 +1,58 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-12-24 14:46:40.678236", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2014-12-24 14:46:40.678236", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "start_time", + "hours", + "column_break_2", + "end_time", + "enabled" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "start_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "Start Time", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "End Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "end_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "End Time", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enabled" + }, + { + "fieldname": "hours", + "fieldtype": "Float", + "label": "Hours", + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-12-13 05:02:36.754145", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Workstation Working Hour", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-10-25 14:48:29.697498", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Workstation Working Hour", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/manufacturing/page/visual_plant_floor/__init__.py b/erpnext/manufacturing/page/visual_plant_floor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.js b/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.js new file mode 100644 index 0000000000..38667e8d79 --- /dev/null +++ b/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.js @@ -0,0 +1,13 @@ + + +frappe.pages['visual-plant-floor'].on_page_load = function(wrapper) { + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Visual Plant Floor', + single_column: true + }); + + frappe.visual_plant_floor = new frappe.ui.VisualPlantFloor( + {wrapper: $(wrapper).find('.layout-main-section')}, wrapper.page + ); +} \ No newline at end of file diff --git a/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.json b/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.json new file mode 100644 index 0000000000..a907e973e3 --- /dev/null +++ b/erpnext/manufacturing/page/visual_plant_floor/visual_plant_floor.json @@ -0,0 +1,29 @@ +{ + "content": null, + "creation": "2023-10-06 15:17:39.215300", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2023-10-06 15:18:00.622073", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "visual-plant-floor", + "owner": "Administrator", + "page_name": "visual-plant-floor", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Operator" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Visual Plant Floor" +} \ No newline at end of file diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index 8e0785074f..e3b632dba2 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\":\"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\":\"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}}]", "creation": "2020-03-02 17:11:37.032604", "custom_blocks": [], "docstatus": 0, @@ -316,7 +316,7 @@ "type": "Link" } ], - "modified": "2023-08-08 22:28:39.633891", + "modified": "2023-11-30 15:21:14.577990", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -336,6 +336,14 @@ "type": "URL", "url": "https://frappe.school/courses/manufacturing?utm_source=in_app" }, + { + "color": "Grey", + "doc_view": "List", + "label": "Plant Floor", + "link_to": "Plant Floor", + "stats_filter": "[]", + "type": "DocType" + }, { "color": "Grey", "doc_view": "List", @@ -343,6 +351,13 @@ "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/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js index dee9a06f05..b847e5729f 100644 --- a/erpnext/public/js/erpnext.bundle.js +++ b/erpnext/public/js/erpnext.bundle.js @@ -5,6 +5,8 @@ import "./sms_manager"; import "./utils/party"; import "./controllers/stock_controller"; import "./payment/payments"; +import "./templates/visual_plant_floor_template.html"; +import "./plant_floor_visual/visual_plant"; import "./controllers/taxes_and_totals"; import "./controllers/transaction"; import "./templates/item_selector.html"; diff --git a/erpnext/public/js/plant_floor_visual/visual_plant.js b/erpnext/public/js/plant_floor_visual/visual_plant.js new file mode 100644 index 0000000000..b1d120fd93 --- /dev/null +++ b/erpnext/public/js/plant_floor_visual/visual_plant.js @@ -0,0 +1,157 @@ +class VisualPlantFloor { + constructor({wrapper, skip_filters=false, plant_floor=null}, page=null) { + this.wrapper = wrapper; + this.plant_floor = plant_floor; + this.skip_filters = skip_filters; + + this.make(); + if (!this.skip_filters) { + this.page = page; + this.add_filter(); + this.prepare_menu(); + } + } + + make() { + this.wrapper.append(` +
+
+
+
+
+
+ `); + + if (!this.skip_filters) { + this.filter_wrapper = this.wrapper.find('.plant-floor-filter'); + this.visualization_wrapper = this.wrapper.find('.plant-floor-visualization'); + } else if(this.plant_floor) { + this.prepare_data(); + } + } + + prepare_data() { + frappe.call({ + method: 'erpnext.manufacturing.doctype.workstation.workstation.get_workstations', + args: { + plant_floor: this.plant_floor, + }, + callback: (r) => { + this.workstations = r.message; + this.render_workstations(); + } + }); + } + + add_filter() { + this.plant_floor = frappe.ui.form.make_control({ + df: { + fieldtype: 'Link', + options: 'Plant Floor', + fieldname: 'plant_floor', + label: __('Plant Floor'), + reqd: 1, + onchange: () => { + this.render_plant_visualization(); + } + }, + parent: this.filter_wrapper, + render_input: true, + }); + + this.plant_floor.$wrapper.addClass('form-column col-sm-2'); + + this.workstation_type = frappe.ui.form.make_control({ + df: { + fieldtype: 'Link', + options: 'Workstation Type', + fieldname: 'workstation_type', + label: __('Machine Type'), + onchange: () => { + this.render_plant_visualization(); + } + }, + parent: this.filter_wrapper, + render_input: true, + }); + + this.workstation_type.$wrapper.addClass('form-column col-sm-2'); + + this.workstation = frappe.ui.form.make_control({ + df: { + fieldtype: 'Link', + options: 'Workstation', + fieldname: 'workstation', + label: __('Machine'), + onchange: () => { + this.render_plant_visualization(); + }, + get_query: () => { + if (this.workstation_type.get_value()) { + return { + filters: { + 'workstation_type': this.workstation_type.get_value() || '' + } + } + } + } + }, + parent: this.filter_wrapper, + render_input: true, + }); + + this.workstation.$wrapper.addClass('form-column col-sm-2'); + + this.workstation_status = frappe.ui.form.make_control({ + df: { + fieldtype: 'Select', + options: '\nProduction\nOff\nIdle\nProblem\nMaintenance\nSetup', + fieldname: 'workstation_status', + label: __('Status'), + onchange: () => { + this.render_plant_visualization(); + }, + }, + parent: this.filter_wrapper, + render_input: true, + }); + } + + render_plant_visualization() { + let plant_floor = this.plant_floor.get_value(); + + if (plant_floor) { + frappe.call({ + method: 'erpnext.manufacturing.doctype.workstation.workstation.get_workstations', + args: { + plant_floor: plant_floor, + workstation_type: this.workstation_type.get_value(), + workstation: this.workstation.get_value(), + workstation_status: this.workstation_status.get_value() + }, + callback: (r) => { + this.workstations = r.message; + this.render_workstations(); + } + }); + } + } + + 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 + }); + + $(template).appendTo(this.wrapper.find('.plant-floor-container')); + } + + prepare_menu() { + this.page.add_menu_item(__('Refresh'), () => { + this.render_plant_visualization(); + }); + } +} + +frappe.ui.VisualPlantFloor = VisualPlantFloor; \ No newline at end of file diff --git a/erpnext/public/js/templates/visual_plant_floor_template.html b/erpnext/public/js/templates/visual_plant_floor_template.html new file mode 100644 index 0000000000..2e67085c02 --- /dev/null +++ b/erpnext/public/js/templates/visual_plant_floor_template.html @@ -0,0 +1,19 @@ +{% $.each(workstations, (idx, row) => { %} +
+ +
+

{{row.status}}

+
{{row.workstation_name}}
+
+
+{% }); %} \ No newline at end of file diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss index 8ab5973deb..ef09854c08 100644 --- a/erpnext/public/scss/erpnext.scss +++ b/erpnext/public/scss/erpnext.scss @@ -490,3 +490,54 @@ body[data-route="pos"] { .exercise-col { padding: 10px; } + +.plant-floor, .workstation-wrapper, .workstation-card p { + border-radius: var(--border-radius-md); + border: 1px solid var(--border-color); + box-shadow: none; + background-color: var(--card-bg); + position: relative; +} + +.plant-floor { + padding-bottom: 25px; +} + +.plant-floor-filter { + padding-top: 10px; + display: flex; + flex-wrap: wrap; +} + +.plant-floor-container { + padding-top: 10px; + display: grid; + grid-template-columns: repeat(6,minmax(0,1fr)); + gap: var(--margin-xl); +} + +@media screen and (max-width: 620px) { + .plant-floor-container { + grid-template-columns: repeat(2,minmax(0,1fr)); + } +} + +.plant-floor-container .workstation-card { + padding: 5px; +} + +.plant-floor-container .workstation-image-link { + width: 100%; + font-size: 50px; + margin: var(--margin-sm); + min-height: 11rem; +} + +.workstation-abbr { + display: flex; + background-color: var(--control-bg); + height:100%; + width:100%; + align-items: center; + justify-content: center; +} \ No newline at end of file From 00a915b741413c97d0ef714f2e5a82e070334d64 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Sat, 20 Jan 2024 06:38:43 +0100 Subject: [PATCH 3/6] feat: hanled payment request failure reasons --- .../doctype/payment_request/payment_request.js | 4 ++++ .../doctype/payment_request/payment_request.json | 14 ++++++++++++-- .../payment_request/payment_request_list.js | 3 +++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e913912028..c85cd42e42 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -25,6 +25,10 @@ frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ }) frappe.ui.form.on("Payment Request", "refresh", function(frm) { + if(frm.doc.status == 'Failed'){ + frm.set_intro(__("Failure: {0}", [frm.doc.failed_reason]), "red"); + } + if(frm.doc.payment_request_type == 'Inward' && frm.doc.payment_channel !== "Phone" && !in_list(["Initiated", "Paid"], frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus==1){ frm.add_custom_button(__('Resend Payment Email'), function(){ diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 66b5c4b983..f62b6241c8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -7,6 +7,7 @@ "field_order": [ "payment_request_type", "transaction_date", + "failed_reason", "column_break_2", "naming_series", "mode_of_payment", @@ -389,13 +390,22 @@ "options": "Payment Request", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "failed_reason", + "fieldtype": "Data", + "hidden": 1, + "label": "Reason for Failure", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-27 09:51:42.277638", + "modified": "2024-01-20 00:37:06.988919", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", @@ -433,4 +443,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 85d729cd61..43f7856929 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -16,6 +16,9 @@ frappe.listview_settings['Payment Request'] = { else if(doc.status == "Paid") { return [__("Paid"), "blue", "status,=,Paid"]; } + else if(doc.status == "Failed") { + return [__("Failed"), "red", "status,=,Failed"]; + } else if(doc.status == "Cancelled") { return [__("Cancelled"), "red", "status,=,Cancelled"]; } From 6fea9d6dfe50513614e39a7b83ad4db59970e26e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 5 Dec 2023 15:28:40 +0530 Subject: [PATCH 4/6] feat: make material request for job card from workstation dashboard --- .../doctype/job_card/job_card.py | 14 + .../doctype/plant_floor/plant_floor.js | 243 +++++++++++++++++- .../doctype/plant_floor/plant_floor.json | 36 ++- .../doctype/plant_floor/plant_floor.py | 114 +++++++- .../plant_floor/stock_summary_template.html | 61 +++++ .../doctype/workstation/workstation.js | 207 ++++++++++++++- .../doctype/workstation/workstation.py | 59 ++++- .../workstation/workstation_job_card.html | 38 ++- .../manufacturing/manufacturing.json | 12 +- .../js/plant_floor_visual/visual_plant.js | 2 +- erpnext/public/scss/erpnext.scss | 3 +- 11 files changed, 750 insertions(+), 39 deletions(-) create mode 100644 erpnext/manufacturing/doctype/plant_floor/stock_summary_template.html 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 973c99421d..3d40a2dd90 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 { From cfd1666181ffdff8ae79bbbb7863e1b12b3c6090 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 31 Jan 2024 09:27:52 +0530 Subject: [PATCH 5/6] fix: Exchange rate on MR to PO creation for muticurrency POs (#39646) --- erpnext/stock/doctype/material_request/material_request.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index e784b700ed..02fbd3d920 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -462,6 +462,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): postprocess, ) + doclist.set_onload("load_after_mapping", False) return doclist From 6b8f046fb4cfef201464976f5b4c95dc79f96731 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 31 Jan 2024 12:15:52 +0530 Subject: [PATCH 6/6] chore: cleanup doctype descriptions (#39637) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../accounts_settings/accounts_settings.json | 3 +- .../doctype/fiscal_year/fiscal_year.json | 10 +- .../monthly_distribution.json | 226 ++++------- .../purchase_taxes_and_charges_template.json | 4 +- .../sales_taxes_and_charges_template.json | 4 +- .../buying_settings/buying_settings.json | 3 +- .../product_bundle/product_bundle.json | 4 +- .../setup/doctype/item_group/item_group.json | 4 +- .../doctype/sales_person/sales_person.json | 8 +- .../terms_and_conditions.json | 4 +- .../stock/doctype/item_price/item_price.json | 4 +- .../stock/doctype/price_list/price_list.json | 364 ++---------------- .../stock_settings/stock_settings.json | 4 +- 13 files changed, 124 insertions(+), 518 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index b10924ff38..0e238e08f6 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -1,7 +1,6 @@ { "actions": [], "creation": "2013-06-24 15:49:57", - "description": "Settings for Accounts", "doctype": "DocType", "document_type": "Other", "editable_grid": 1, @@ -462,7 +461,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-22 12:10:10.151819", + "modified": "2024-01-30 14:04:26.553554", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index bd2bfbd381..21091ebb7b 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -3,7 +3,7 @@ "allow_import": 1, "autoname": "field:year", "creation": "2013-01-22 16:50:25", - "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.", + "description": "Represents a Financial Year. All accounting entries and other major transactions are tracked against the Fiscal Year.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -82,11 +82,12 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2024-01-17 13:06:01.608953", + "modified": "2024-01-30 12:35:38.645968", "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year", - "owner": "Administrator", + "naming_rule": "By fieldname", + "owner": "Administrator", "permissions": [ { "create": 1, @@ -130,5 +131,6 @@ ], "show_name_in_global_search": 1, "sort_field": "name", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json index 14f2d80250..488e8b2620 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.json @@ -1,173 +1,77 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:distribution_id", - "beta": 0, - "creation": "2013-01-10 16:34:05", - "custom": 0, - "description": "**Monthly Distribution** helps you distribute the Budget/Target across months if you have seasonality in your business.", - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "autoname": "field:distribution_id", + "creation": "2013-01-10 16:34:05", + "description": "Helps you distribute the Budget/Target across months if you have seasonality in your business.", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "distribution_id", + "fiscal_year", + "percentages" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Name of the Monthly Distribution", - "fieldname": "distribution_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Distribution Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "distribution_id", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "Name of the Monthly Distribution", + "fieldname": "distribution_id", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Distribution Name", + "oldfieldname": "distribution_id", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Fiscal Year", - "length": 0, - "no_copy": 0, - "oldfieldname": "fiscal_year", - "oldfieldtype": "Select", - "options": "Fiscal Year", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "fiscal_year", + "fieldtype": "Link", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Fiscal Year", + "oldfieldname": "fiscal_year", + "oldfieldtype": "Select", + "options": "Fiscal Year", + "search_index": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "percentages", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Monthly Distribution Percentages", - "length": 0, - "no_copy": 0, - "oldfieldname": "budget_distribution_details", - "oldfieldtype": "Table", - "options": "Monthly Distribution Percentage", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "percentages", + "fieldtype": "Table", + "label": "Monthly Distribution Percentages", + "oldfieldname": "budget_distribution_details", + "oldfieldtype": "Table", + "options": "Monthly Distribution Percentage" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-bar-chart", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-11-21 14:54:35.998761", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Monthly Distribution", - "name_case": "Title Case", - "owner": "Administrator", + ], + "icon": "fa fa-bar-chart", + "idx": 1, + "links": [], + "modified": "2024-01-30 13:57:55.802744", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Monthly Distribution", + "naming_rule": "By fieldname", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 2, - "print": 0, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "permlevel": 2, + "read": 1, + "report": 1, + "role": "Accounts Manager" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json index c36efb89a3..2ff6a45d1e 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json @@ -3,7 +3,7 @@ "allow_import": 1, "allow_rename": 1, "creation": "2013-01-10 16:34:08", - "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", + "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain a list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\", etc.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -77,7 +77,7 @@ "icon": "fa fa-money", "idx": 1, "links": [], - "modified": "2022-05-16 16:15:29.059370", + "modified": "2024-01-30 13:08:09.537242", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges Template", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json index 408ecbf36d..736d283cdb 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json @@ -3,7 +3,7 @@ "allow_import": 1, "allow_rename": 1, "creation": "2013-01-10 16:34:09", - "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", + "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain a list of tax heads and also other expense/income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -79,7 +79,7 @@ "icon": "fa fa-money", "idx": 1, "links": [], - "modified": "2022-05-16 16:14:52.061672", + "modified": "2024-01-30 13:07:28.801104", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges Template", diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index ddcbd555ae..77ce5f7d19 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,7 +1,6 @@ { "actions": [], "creation": "2013-06-25 11:04:03", - "description": "Settings for Buying Module", "doctype": "DocType", "document_type": "Other", "engine": "InnoDB", @@ -214,7 +213,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-12 16:42:01.894346", + "modified": "2024-01-30 14:04:43.177427", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json index c4f21b61b9..1c37b854b9 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -2,7 +2,7 @@ "actions": [], "allow_import": 1, "creation": "2013-06-20 11:53:21", - "description": "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have \"Is Stock Item\" as \"No\" and \"Is Sales Item\" as \"Yes\".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials", + "description": "Aggregate a group of Items into another Item. This is useful if you are maintaining the stock of the packed items and not the bundled item", "doctype": "DocType", "engine": "InnoDB", "field_order": [ @@ -77,7 +77,7 @@ "icon": "fa fa-sitemap", "idx": 1, "links": [], - "modified": "2023-11-22 15:20:46.805114", + "modified": "2024-01-30 13:57:04.951788", "modified_by": "Administrator", "module": "Selling", "name": "Product Bundle", diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index dfa5a8ed0a..7c9233fa1f 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -4,7 +4,7 @@ "allow_rename": 1, "autoname": "field:item_group_name", "creation": "2013-03-28 10:35:29", - "description": "Item Classification", + "description": "An Item Group is a way to classify items based on types.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -135,7 +135,7 @@ "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2023-10-12 13:44:13.611287", + "modified": "2024-01-30 14:08:38.485616", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", diff --git a/erpnext/setup/doctype/sales_person/sales_person.json b/erpnext/setup/doctype/sales_person/sales_person.json index e526ac42ba..79bd8411ee 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.json +++ b/erpnext/setup/doctype/sales_person/sales_person.json @@ -4,7 +4,7 @@ "allow_rename": 1, "autoname": "field:sales_person_name", "creation": "2013-01-10 16:34:24", - "description": "All Sales Transactions can be tagged against multiple **Sales Persons** so that you can set and monitor targets.", + "description": "All Sales Transactions can be tagged against multiple Sales Persons so that you can set and monitor targets.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -145,10 +145,11 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:11:13.968024", + "modified": "2024-01-30 13:57:26.436991", "modified_by": "Administrator", "module": "Setup", "name": "Sales Person", + "naming_rule": "By fieldname", "nsm_parent_field": "parent_sales_person", "owner": "Administrator", "permissions": [ @@ -181,5 +182,6 @@ "search_fields": "parent_sales_person", "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index f884864acf..76e52aefeb 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -4,7 +4,7 @@ "allow_rename": 1, "autoname": "field:title", "creation": "2013-01-10 16:34:24", - "description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.", + "description": "Standard Terms and Conditions that can be added to Sales and Purchases. Examples: Validity of the offer, Payment Terms, Safety and Usage, etc.", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -77,7 +77,7 @@ "icon": "icon-legal", "idx": 1, "links": [], - "modified": "2023-02-01 14:33:39.246532", + "modified": "2024-01-30 12:47:52.325531", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 2390ee2438..707f346d39 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -3,7 +3,7 @@ "allow_import": 1, "autoname": "hash", "creation": "2013-05-02 16:29:48", - "description": "Multiple Item prices.", + "description": "Log the selling and buying rate of an Item", "doctype": "DocType", "document_type": "Setup", "engine": "InnoDB", @@ -220,7 +220,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-01-24 02:20:26.145996", + "modified": "2024-01-30 14:02:19.304854", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", diff --git a/erpnext/stock/doctype/price_list/price_list.json b/erpnext/stock/doctype/price_list/price_list.json index 56340fb05c..38cd1ee0c6 100644 --- a/erpnext/stock/doctype/price_list/price_list.json +++ b/erpnext/stock/doctype/price_list/price_list.json @@ -1,434 +1,134 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:price_list_name", - "beta": 0, "creation": "2013-01-25 11:35:09", - "custom": 0, - "description": "Price List Master", - "docstatus": 0, + "description": "A Price List is a collection of Item Prices either Selling, Buying, or both", "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "enabled", + "sb_1", + "price_list_name", + "currency", + "buying", + "selling", + "price_not_uom_dependent", + "column_break_3", + "countries" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", - "fetch_if_empty": 0, "fieldname": "enabled", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Enabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "sb_1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "price_list_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Price List Name", - "length": 0, "no_copy": 1, "oldfieldname": "price_list_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "currency", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Currency", - "length": 0, - "no_copy": 0, "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "buying", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Buying", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Buying" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "selling", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Selling", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Selling" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "price_not_uom_dependent", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price Not UOM Dependent", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Price Not UOM Dependent" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "countries", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Applicable for Countries", - "length": 0, - "no_copy": 0, - "options": "Price List Country", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Price List Country" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-tags", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, + "links": [], "max_attachments": 1, - "modified": "2019-06-24 17:16:28.027302", + "modified": "2024-01-30 14:39:26.328837", "modified_by": "Administrator", "module": "Stock", "name": "Price List", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, - "email": 0, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, "report": 1, "role": "Purchase Master Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Manufacturing User" } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "search_fields": "currency", "show_name_in_global_search": 1, + "sort_field": "modified", "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "states": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index f84456abae..dc2797457b 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -1,7 +1,7 @@ { "actions": [], "creation": "2013-06-24 16:37:54", - "description": "Settings", + "description": "Default settings for your stock-related transactions", "doctype": "DocType", "engine": "InnoDB", "field_order": [ @@ -427,7 +427,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-24 02:20:26.145996", + "modified": "2024-01-30 14:03:52.143457", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings",