From dec9b80c1295034931beb1dfb7737f10d320ff4f Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Wed, 11 Sep 2024 13:53:00 +0500 Subject: [PATCH 1/6] refactor(employee): Query changes for age and number of year attributes --- erpnext/hr/doctype/employee/employee.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 39a903b67d1d..f7dabeceae4a 100644 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -433,10 +433,10 @@ def send_employee_birthday_notification(): today = getdate() employees = get_employees_who_have_birthday_today(today) - for name in employees: - doc = frappe.get_doc("Employee", name) - if doc.year_of_birth: - doc.age = today.year - doc.year_of_birth + for names in employees: + doc = frappe.get_doc("Employee", names) + if doc.date_of_birth: + doc.age = today.year - doc.date_of_birth.year doc.run_method("send_birthday_notification") @@ -461,8 +461,8 @@ def send_employee_anniversary_notification(): employees = get_employees_who_have_anniversary_today(today) for names in employees: doc = frappe.get_doc("Employee", names) - if doc.year_of_joining: - doc.number_of_years = today.year - doc.year_of_joining + if doc.date_of_joining: + doc.number_of_years = today.year - doc.date_of_joining.year doc.run_method("send_anniversary_notification") From 5e6dca5002cfa1a77930a13c31511655d14f6168 Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Wed, 11 Sep 2024 14:51:43 +0500 Subject: [PATCH 2/6] fix(employee): Improve date handling age and number of years attributes --- erpnext/hr/doctype/employee/employee.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index f7dabeceae4a..ee7762cf9e21 100644 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -436,7 +436,8 @@ def send_employee_birthday_notification(): for names in employees: doc = frappe.get_doc("Employee", names) if doc.date_of_birth: - doc.age = today.year - doc.date_of_birth.year + date_of_birth = getdate(doc.date_of_birth) + doc.age = today.year - date_of_birth.year doc.run_method("send_birthday_notification") @@ -462,7 +463,8 @@ def send_employee_anniversary_notification(): for names in employees: doc = frappe.get_doc("Employee", names) if doc.date_of_joining: - doc.number_of_years = today.year - doc.date_of_joining.year + date_of_joining = getdate(doc.date_of_joining) + doc.number_of_years = today.year - date_of_joining.year doc.run_method("send_anniversary_notification") From c2c0a90a52e8d5cf90129a92ae603b76f18f8931 Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Wed, 11 Sep 2024 16:00:09 +0500 Subject: [PATCH 3/6] chore(employee): typo --- erpnext/hr/doctype/employee/employee.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index ee7762cf9e21..fc37e6c6f501 100644 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -433,8 +433,8 @@ def send_employee_birthday_notification(): today = getdate() employees = get_employees_who_have_birthday_today(today) - for names in employees: - doc = frappe.get_doc("Employee", names) + for name in employees: + doc = frappe.get_doc("Employee", name) if doc.date_of_birth: date_of_birth = getdate(doc.date_of_birth) doc.age = today.year - date_of_birth.year @@ -460,8 +460,8 @@ def send_employee_anniversary_notification(): today = getdate() employees = get_employees_who_have_anniversary_today(today) - for names in employees: - doc = frappe.get_doc("Employee", names) + for name in employees: + doc = frappe.get_doc("Employee", name) if doc.date_of_joining: date_of_joining = getdate(doc.date_of_joining) doc.number_of_years = today.year - date_of_joining.year From 2cb4445fc69bcf225b94a3a1160459bf02e00f74 Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Thu, 19 Sep 2024 18:14:49 +0500 Subject: [PATCH 4/6] feat: Implement document checklist in Project Controller --- erpnext/projects/doctype/project/project.js | 25 ++++++++++++++++++- .../project_workshop/project_workshop.json | 11 ++++++-- .../project_workshop/project_workshop.py | 15 +++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index ab00ccd35169..62f9ecff8ee1 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -31,6 +31,7 @@ erpnext.projects.ProjectController = class ProjectController extends crm.QuickCo this.make_vehicle_checklist(); this.make_customer_request_checklist(); this.make_customer_vehicle_selector(); + this.make_document_checklist(); this.set_sales_data_html(); this.set_service_advisor_from_user(); this.setup_vehicle_panel_fields(); @@ -571,12 +572,31 @@ erpnext.projects.ProjectController = class ProjectController extends crm.QuickCo } } + make_document_checklist() { + if (this.frm.fields_dict.document_checklist_html) { + var is_read_only = cint(this.frm.doc.__onload && this.frm.doc.__onload.cant_change_fields && this.frm.doc.__onload.cant_change_fields.document_checklist); + + this.frm.document_checklist_editor = erpnext.vehicles.make_vehicle_checklist(this.frm, + 'document_checklist', + this.frm.fields_dict.document_checklist_html.wrapper, + this.frm.doc.__onload && this.frm.doc.__onload.default_document_checklist_items, + is_read_only, + __("Document Checklist")); + } + } + refresh_customer_request_checklist() { if (this.frm.customer_request_checklist_editor) { this.frm.customer_request_checklist_editor.refresh(); } } + render_document_checklist() { + if (this.frm.document_checklist_editor) { + this.frm.document_checklist_editor.render_checklist(); + } + } + make_customer_vehicle_selector() { if (this.frm.fields_dict.customer_vehicle_selector_html) { this.frm.customer_vehicle_selector = erpnext.vehicles.make_customer_vehicle_selector(this.frm, @@ -619,7 +639,10 @@ erpnext.projects.ProjectController = class ProjectController extends crm.QuickCo }, callback: function (r) { if (!r.exc) { - return me.frm.set_value(r.message); + return frappe.run_serially([ + () => me.frm.set_value(r.message), + () => me.render_document_checklist(), + ]); } } }); diff --git a/erpnext/projects/doctype/project_workshop/project_workshop.json b/erpnext/projects/doctype/project_workshop/project_workshop.json index 44ef2b13c341..39a13930775a 100644 --- a/erpnext/projects/doctype/project_workshop/project_workshop.json +++ b/erpnext/projects/doctype/project_workshop/project_workshop.json @@ -10,7 +10,8 @@ "workshop_name", "service_manager", "section_break_3", - "default_cost_centers" + "default_cost_centers", + "document_checklist" ], "fields": [ { @@ -35,10 +36,16 @@ "fieldtype": "Table", "label": "Default Cost Centers", "options": "Default Cost Center" + }, + { + "fieldname": "document_checklist", + "fieldtype": "Table", + "label": "Document Checklist", + "options": "Vehicle Checklist Item" } ], "links": [], - "modified": "2023-10-31 15:28:52.930477", + "modified": "2024-09-04 13:05:45.869493", "modified_by": "Administrator", "module": "Projects", "name": "Project Workshop", diff --git a/erpnext/projects/doctype/project_workshop/project_workshop.py b/erpnext/projects/doctype/project_workshop/project_workshop.py index 39f788969947..8ba90ce8d69d 100644 --- a/erpnext/projects/doctype/project_workshop/project_workshop.py +++ b/erpnext/projects/doctype/project_workshop/project_workshop.py @@ -24,4 +24,19 @@ def get_project_workshop_details(project_workshop, company): out.service_manager = doc.service_manager out.cost_center = doc.get_default_cost_center(company) + out.document_checklist = [] + checklist = get_project_workshop_document_checklist_items(project_workshop) + for item in checklist: + out.document_checklist.append({'checklist_item': item.checklist_item, 'checklist_item_checked': 0, "is_mandatory": item.is_mandatory}) + return out + + +@frappe.whitelist() +def get_project_workshop_document_checklist_items(project_workshop): + if not project_workshop: + return [] + + workshop_doc = frappe.get_cached_doc("Project Workshop", project_workshop) + checklist_items = [d for d in workshop_doc.get('document_checklist')] + return checklist_items From 176173b58697ed2dbfa66a3b1fd21b48f21f8b55 Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Thu, 19 Sep 2024 18:31:49 +0500 Subject: [PATCH 5/6] feat(project): Evaluating mandatory items before closing repair order --- erpnext/projects/doctype/project/project.py | 13 ++++++++++++- .../vehicle_checklist_item.json | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 65b81fee0595..08e307d4e132 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -15,7 +15,7 @@ from erpnext.controllers.status_updater import StatusUpdaterERP from erpnext.projects.doctype.project_status.project_status import get_auto_project_status, set_manual_project_status,\ get_valid_manual_project_status_names, is_manual_project_status, validate_project_status_for_transaction -from erpnext.projects.doctype.project_workshop.project_workshop import get_project_workshop_details +from erpnext.projects.doctype.project_workshop.project_workshop import get_project_workshop_details, get_project_workshop_document_checklist_items from erpnext.vehicles.vehicle_checklist import get_default_vehicle_checklist_items, set_missing_checklist from erpnext.vehicles.doctype.vehicle_log.vehicle_log import get_customer_vehicle_selector_data from frappe.model.meta import get_field_precision @@ -59,6 +59,7 @@ def onload(self): self.set_onload('activity_summary', self.get_activity_summary()) self.set_onload('default_vehicle_checklist_items', get_default_vehicle_checklist_items('vehicle_checklist')) self.set_onload('default_customer_request_checklist_items', get_default_vehicle_checklist_items('customer_request_checklist')) + self.set_onload('default_documents_checklist_items', get_project_workshop_document_checklist_items(self.project_workshop)) self.set_onload('cant_change_fields', self.get_cant_change_fields()) self.set_onload('valid_manual_project_status_names', get_valid_manual_project_status_names(self)) self.set_onload('is_manual_project_status', is_manual_project_status(self.project_status)) @@ -498,6 +499,15 @@ def validate_insurance_details(self): if not self.get('insurance_loss_no') and self.ready_to_close == 1: frappe.throw(_("Insurance Loss # is missing")) + def set_mandatory_items_check(self): + unchecked_items = [ + d.checklist_item for d in self.document_checklist + if d.is_mandatory and not d.checklist_item_checked + ] + + if unchecked_items and self.ready_to_close == 1: + frappe.throw(_("These mandatory items are not checked: {}").format(', '.join(unchecked_items))) + def reopen_status(self, update=True): self.ready_to_close = 0 self.ready_to_close_dt = None @@ -1604,6 +1614,7 @@ def set_project_ready_to_close(project): project.set_ready_to_close(update=True) project.validate_insurance_details() + project.set_mandatory_items_check() project.set_status(update=True) project.update_vehicle_booking_order_pdi_status() project.notify_update() diff --git a/erpnext/vehicles/doctype/vehicle_checklist_item/vehicle_checklist_item.json b/erpnext/vehicles/doctype/vehicle_checklist_item/vehicle_checklist_item.json index 46c4efd22e82..161320a4e34a 100644 --- a/erpnext/vehicles/doctype/vehicle_checklist_item/vehicle_checklist_item.json +++ b/erpnext/vehicles/doctype/vehicle_checklist_item/vehicle_checklist_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2022-01-22 01:23:04.488781", "doctype": "DocType", "editable_grid": 1, @@ -6,7 +7,8 @@ "field_order": [ "checklist_item", "checklist_item_checked", - "is_custom_checklist_item" + "is_custom_checklist_item", + "is_mandatory" ], "fields": [ { @@ -27,10 +29,17 @@ "fieldname": "is_custom_checklist_item", "fieldtype": "Check", "label": "Is Custom Checklist Item" + }, + { + "default": "0", + "fieldname": "is_mandatory", + "fieldtype": "Check", + "label": "Is Mandatory" } ], "istable": 1, - "modified": "2022-01-22 14:06:57.660110", + "links": [], + "modified": "2024-09-16 16:23:35.850191", "modified_by": "Administrator", "module": "Vehicles", "name": "Vehicle Checklist Item", @@ -39,5 +48,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From ab0b88e63754e58fd966ec5fbec6dc344da07a43 Mon Sep 17 00:00:00 2001 From: MAunZaidi Date: Thu, 19 Sep 2024 18:36:06 +0500 Subject: [PATCH 6/6] fix(vehicle_checklist): Handled reset and render functionality for checklist items --- erpnext/vehicles/vehicle_checklist.js | 38 +++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/erpnext/vehicles/vehicle_checklist.js b/erpnext/vehicles/vehicle_checklist.js index 6d91f7db9702..34398509b489 100644 --- a/erpnext/vehicles/vehicle_checklist.js +++ b/erpnext/vehicles/vehicle_checklist.js @@ -17,7 +17,7 @@ erpnext.vehicles.VehicleChecklistEditor = Class.extend({ me.checklist_wrapper = $(`
`).appendTo(me.wrapper); me.left_container = $(`
`).appendTo(me.checklist_wrapper); me.right_container = $(`
`).appendTo(me.checklist_wrapper); - me.buttons_container = $(`
`).appendTo(me.wrapper); + me.buttons_container = $(`
`).appendTo(me.wrapper); me.empty_checklist_container = $(`
`).appendTo(me.wrapper); me.frm = frm; @@ -26,10 +26,10 @@ erpnext.vehicles.VehicleChecklistEditor = Class.extend({ me.default_checklist_items = default_items || []; var checklist_items = me.get_checklist_items(); - if (checklist_items && checklist_items.length) { - me.render_checklist(); - } else { + if (!checklist_items?.length && ['vehicle_checklist', 'customer_request_checklist'].includes(parentfield)) { me.load_items_and_render(); + } else { + me.render_checklist(); } me.bind(); @@ -59,6 +59,27 @@ erpnext.vehicles.VehicleChecklistEditor = Class.extend({ }); }, + get_project_workshop_details() { + var me = this; + if (me.frm.doc.project_workshop) { + return frappe.call({ + method: "erpnext.projects.doctype.project_workshop.project_workshop.get_project_workshop_details", + args: { + project_workshop: me.frm.doc.project_workshop, + company: me.frm.doc.company, + }, + callback: function (r) { + if (!r.exc) { + return frappe.run_serially([ + () => me.frm.set_value(r.message), + () => me.render_checklist(), + ]); + } + } + }); + } + }, + set_from_default_checklist_items: function () { var me = this; if (me.can_write()) { @@ -257,7 +278,14 @@ erpnext.vehicles.VehicleChecklistEditor = Class.extend({ }, on_reset_checklist: function () { - frappe.confirm(__("Are you sure you want to reset the vehicle checklist?"), () => this.load_items_and_render()); + var parentfield = this.parentfield; + frappe.confirm(__("Are you sure you want to reset the vehicle checklist?"), () => { + if (['vehicle_checklist', 'customer_request_checklist'].includes(parentfield)){ + this.load_items_and_render(); + } else { + this.get_project_workshop_details(); + } + }); }, get_checklist_items: function () {