From 4f52b86d7e577741f65932601c6ff2066bba1553 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 13 Dec 2021 16:50:55 +0530 Subject: [PATCH 1/7] refactor: update_serial_no function and code cleanup --- .../maintenance_schedule.py | 26 +++++---- .../maintenance_visit/maintenance_visit.js | 56 ++++++++----------- 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 2ffae1a4f2..9c35f4f245 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - import frappe from frappe import _, throw from frappe.utils import add_days, cint, cstr, date_diff, formatdate, getdate @@ -306,13 +305,18 @@ class MaintenanceSchedule(TransactionBase): return schedule.name @frappe.whitelist() -def update_serial_nos(s_id): - serial_nos = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'serial_no') +def update_serial_nos(item_code, schedule=None): + serial_nos = [] + if schedule: + serial_nos = frappe.db.get_value('Maintenance Schedule Item', { + 'parent': schedule, + 'item_code': item_code + }, 'serial_no') + if serial_nos: serial_nos = get_serial_nos(serial_nos) - return serial_nos - else: - return False + + return serial_nos @frappe.whitelist() def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=None): @@ -320,12 +324,9 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No def update_status_and_detail(source, target, parent): target.maintenance_type = "Scheduled" - target.maintenance_schedule = source.name target.maintenance_schedule_detail = s_id - def update_sales_and_serial(source, target, parent): - sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person') - target.service_person = sales_person + def update_serial(source, target, parent): serial_nos = get_serial_nos(target.serial_no) if len(serial_nos) == 1: target.serial_no = serial_nos[0] @@ -346,7 +347,10 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No "Maintenance Schedule Item": { "doctype": "Maintenance Visit Purpose", "condition": lambda doc: doc.item_name == item_name, - "postprocess": update_sales_and_serial + "field_map": { + "sales_person": "service_person" + }, + "postprocess": update_serial } }, target_doc) diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js index 7f983541f6..edfc5e423e 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js @@ -2,55 +2,46 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.maintenance"); -var serial_nos = []; frappe.ui.form.on('Maintenance Visit', { - refresh: function (frm) { - //filters for serial_no based on item_code - frm.set_query('serial_no', 'purposes', function (frm, cdt, cdn) { - let item = locals[cdt][cdn]; - if (serial_nos) { - return { - filters: { - 'item_code': item.item_code, - 'name': ["in", serial_nos] - } - }; - } else { - return { - filters: { - 'item_code': item.item_code - } - }; - } - }); - }, setup: function (frm) { frm.set_query('contact_person', erpnext.queries.contact_query); frm.set_query('customer_address', erpnext.queries.address_query); frm.set_query('customer', erpnext.queries.customer); }, - onload: function (frm, cdt, cdn) { - let item = locals[cdt][cdn]; + onload: function (frm) { + // filters for serial no based on item code if (frm.doc.maintenance_type === "Scheduled") { - const schedule_id = item.purposes[0].prevdoc_detail_docname || frm.doc.maintenance_schedule_detail; + let item_code = frm.doc.purposes[0].item_code; frappe.call({ method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.update_serial_nos", args: { - s_id: schedule_id - }, - callback: function (r) { - serial_nos = r.message; + schedule: frm.doc.maintenance_schedule, + item_code: item_code } + }).then((r) => { + let serial_nos = r.message; + frm.set_query('serial_no', 'purposes', () => { + if (serial_nos.length > 0) { + return { + filters: { + 'item_code': item_code, + 'name': ["in", serial_nos] + } + }; + } + return { + filters: { + 'item_code': item_code + } + }; + }); }); } - else { - frm.clear_table("purposes"); - } - if (!frm.doc.status) { frm.set_value({ status: 'Draft' }); } if (frm.doc.__islocal) { + frm.clear_table("purposes"); frm.set_value({ mntc_date: frappe.datetime.get_today() }); } }, @@ -63,7 +54,6 @@ frappe.ui.form.on('Maintenance Visit', { contact_person: function (frm) { erpnext.utils.get_contact_details(frm); } - }) // TODO commonify this code From c872aa43f871dbe8fb71b5584fefab52b42a2eda Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 15 Dec 2021 15:27:41 +0530 Subject: [PATCH 2/7] fix(patch): updates maintenance schedule field for old docs --- erpnext/patches.txt | 1 + ...ate_maintenance_schedule_field_in_visit.py | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ee9060b37d..937465b72e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -313,4 +313,5 @@ erpnext.patches.v13_0.update_category_in_ltds_certificate erpnext.patches.v13_0.create_pan_field_for_india #2 erpnext.patches.v14_0.delete_hub_doctypes erpnext.patches.v13_0.create_ksa_vat_custom_fields +erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit erpnext.patches.v14_0.migrate_crm_settings diff --git a/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py new file mode 100644 index 0000000000..450c00e421 --- /dev/null +++ b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py @@ -0,0 +1,22 @@ + +import frappe + + +def execute(): + # Updates the Maintenance Schedule link to fetch serial nos + from frappe.query_builder.functions import Coalesce + mvp = frappe.qb.DocType('Maintenance Visit Purpose') + mv = frappe.qb.DocType('Maintenance Visit') + + frappe.qb.update( + mv + ).join( + mvp + ).on(mvp.parent == mv.name).set( + mv.maintenance_schedule, + Coalesce(mvp.prevdoc_docname, '') + ).where( + (mv.maintenance_type == "Scheduled") + & (mvp.prevdoc_docname.notnull()) + & (mv.docstatus < 2) + ).run(as_dict=1) From 9109660a69056424a96bb9ba6d85ccf5214a45c6 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 17 Dec 2021 11:57:17 +0530 Subject: [PATCH 3/7] test: added test for serial no. filters --- .../test_maintenance_schedule.py | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 37ea3fdac3..7f1ea9b35c 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -8,7 +8,10 @@ from frappe.utils.data import add_days, formatdate, today from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import ( make_maintenance_visit, + update_serial_nos, ) +from erpnext.stock.doctype.item.test_item import create_item +from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item # test_records = frappe.get_test_records('Maintenance Schedule') @@ -75,6 +78,41 @@ class TestMaintenanceSchedule(unittest.TestCase): #checks if visit status is back updated in schedule self.assertTrue(ms.schedules[1].completion_status, "Partially Completed") + def test_serial_no_filters(self): + # Without serial no. set in schedule -> returns None + item_code = "_Test Serial Item" + make_serial_item_with_serial(item_code) + ms = make_maintenance_schedule(item_code=item_code) + ms.submit() + + s_item = ms.schedules[0] + mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name) + mvi = mv.purposes[0] + serial_nos = update_serial_nos(mvi.item_name, ms.name) + self.assertEqual(serial_nos, None) + + # With serial no. set in schedule -> returns serial nos. + make_serial_item_with_serial(item_code) + ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002") + ms.submit() + + s_item = ms.schedules[0] + mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name) + mvi = mv.purposes[0] + serial_nos = update_serial_nos(mvi.item_name, ms.name) + self.assertEqual(serial_nos, ["TEST001", "TEST002"]) + + frappe.db.rollback() + +def make_serial_item_with_serial(item_code): + serial_item_doc = create_item(item_code, is_stock_item=1) + if not serial_item_doc.has_serial_no or not serial_item_doc.serial_no_series: + serial_item_doc.has_serial_no = 1 + serial_item_doc.serial_no_series = "TEST.###" + serial_item_doc.save(ignore_permissions=True) + if frappe.db.exists('Serial No', {"status": "Active", "item_code": item_code}): + make_serialized_item(item_code=item_code) + def get_events(ms): return frappe.get_all("Event Participants", filters={ "reference_doctype": ms.doctype, @@ -82,17 +120,18 @@ def get_events(ms): "parenttype": "Event" }) -def make_maintenance_schedule(): +def make_maintenance_schedule(**args): ms = frappe.new_doc("Maintenance Schedule") ms.company = "_Test Company" ms.customer = "_Test Customer" ms.transaction_date = today() ms.append("items", { - "item_code": "_Test Item", + "item_code": args.get("item_code") or "_Test Item", "start_date": today(), "periodicity": "Weekly", "no_of_visits": 4, + "serial_no": args.get("serial_no"), "sales_person": "Sales Team", }) ms.insert(ignore_permissions=True) From 90d32006f42a95dba37c51924c5f62be7436dd6f Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 17 Dec 2021 13:32:30 +0530 Subject: [PATCH 4/7] fix(test): fixed test case not creating serials --- .../doctype/maintenance_schedule/test_maintenance_schedule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 7539505842..378d3238d7 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -115,7 +115,8 @@ def make_serial_item_with_serial(item_code): serial_item_doc.has_serial_no = 1 serial_item_doc.serial_no_series = "TEST.###" serial_item_doc.save(ignore_permissions=True) - if frappe.db.exists('Serial No', {"status": "Active", "item_code": item_code}): + active_serials = frappe.db.get_all('Serial No', {"status": "Active", "item_code": item_code}) + if len(active_serials) < 2: make_serialized_item(item_code=item_code) def get_events(ms): From b98c61f2a7247d9f27251b8e740664a77a8b51ef Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 17 Dec 2021 13:46:46 +0530 Subject: [PATCH 5/7] refactor: moved purpose table mandatory check to server --- .../doctype/maintenance_visit/maintenance_visit.js | 1 - .../doctype/maintenance_visit/maintenance_visit.json | 6 +++--- .../doctype/maintenance_visit/maintenance_visit.py | 5 +++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js index c54ef4aace..09af0cf1ad 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js @@ -41,7 +41,6 @@ frappe.ui.form.on('Maintenance Visit', { frm.set_value({ status: 'Draft' }); } if (frm.doc.__islocal) { - frm.doc.maintenance_type == 'Unscheduled' && frm.clear_table("purposes"); frm.set_value({ mntc_date: frappe.datetime.get_today() }); } }, diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json index ec32239518..4a6aa0a34b 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json @@ -179,8 +179,7 @@ "label": "Purposes", "oldfieldname": "maintenance_visit_details", "oldfieldtype": "Table", - "options": "Maintenance Visit Purpose", - "reqd": 1 + "options": "Maintenance Visit Purpose" }, { "fieldname": "more_info", @@ -294,10 +293,11 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-27 16:06:17.352572", + "modified": "2021-12-17 03:10:27.608112", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py index 5a87b162af..d5d87536da 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py @@ -18,6 +18,10 @@ class MaintenanceVisit(TransactionBase): if d.serial_no and not frappe.db.exists("Serial No", d.serial_no): frappe.throw(_("Serial No {0} does not exist").format(d.serial_no)) + def validate_purpose_table(self): + if not self.purposes: + frappe.throw(_("Add Items in the Purpose Table"), title="Purposes Required") + def validate_maintenance_date(self): if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail: item_ref = frappe.db.get_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'item_reference') @@ -29,6 +33,7 @@ class MaintenanceVisit(TransactionBase): def validate(self): self.validate_serial_no() self.validate_maintenance_date() + self.validate_purpose_table() def update_completion_status(self): if self.maintenance_schedule_detail: From dec751377302e504285e834afed6a64dd0f010bf Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 17 Jan 2022 18:25:42 +0530 Subject: [PATCH 6/7] fix: Serial No. filters for unscheduled visits --- .../maintenance_schedule/maintenance_schedule.py | 2 +- .../maintenance_schedule/test_maintenance_schedule.py | 6 +++--- .../doctype/maintenance_visit/maintenance_visit.js | 11 ++++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 9c35f4f245..07d928c221 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -305,7 +305,7 @@ class MaintenanceSchedule(TransactionBase): return schedule.name @frappe.whitelist() -def update_serial_nos(item_code, schedule=None): +def get_serial_nos_from_schedule(item_code, schedule=None): serial_nos = [] if schedule: serial_nos = frappe.db.get_value('Maintenance Schedule Item', { diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 378d3238d7..4d3c3f48f4 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -7,8 +7,8 @@ import frappe from frappe.utils.data import add_days, formatdate, today from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import ( + get_serial_nos_from_schedule, make_maintenance_visit, - update_serial_nos, ) from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item @@ -93,7 +93,7 @@ class TestMaintenanceSchedule(unittest.TestCase): s_item = ms.schedules[0] mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name) mvi = mv.purposes[0] - serial_nos = update_serial_nos(mvi.item_name, ms.name) + serial_nos = get_serial_nos_from_schedule(mvi.item_name, ms.name) self.assertEqual(serial_nos, None) # With serial no. set in schedule -> returns serial nos. @@ -104,7 +104,7 @@ class TestMaintenanceSchedule(unittest.TestCase): s_item = ms.schedules[0] mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name) mvi = mv.purposes[0] - serial_nos = update_serial_nos(mvi.item_name, ms.name) + serial_nos = get_serial_nos_from_schedule(mvi.item_name, ms.name) self.assertEqual(serial_nos, ["TEST001", "TEST002"]) frappe.db.rollback() diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js index 09af0cf1ad..72686e7403 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js @@ -13,7 +13,7 @@ frappe.ui.form.on('Maintenance Visit', { if (frm.doc.maintenance_type === "Scheduled") { let item_code = frm.doc.purposes[0].item_code; frappe.call({ - method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.update_serial_nos", + method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule", args: { schedule: frm.doc.maintenance_schedule, item_code: item_code @@ -36,6 +36,15 @@ frappe.ui.form.on('Maintenance Visit', { }; }); }); + } else { + frm.set_query('serial_no', 'purposes', (frm, cdt, cdn) => { + let row = locals[cdt][cdn]; + return { + filters: { + 'item_code': row.item_code + } + }; + }); } if (!frm.doc.status) { frm.set_value({ status: 'Draft' }); From 0efdbae7831e523ff71b242c39498265c0946691 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 18 Jan 2022 18:38:59 +0530 Subject: [PATCH 7/7] fix: minor onboarding fixes --- .../sales_taxes_and_charges_template.json | 6 +++-- .../module_onboarding/accounts/accounts.json | 5 +---- .../onboarding_step/company/company.json | 22 ------------------- 3 files changed, 5 insertions(+), 28 deletions(-) delete mode 100644 erpnext/accounts/onboarding_step/company/company.json diff --git a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json index 7de9ae1539..02e30c3835 100644 --- a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json +++ b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json @@ -2,15 +2,17 @@ "creation": "2021-08-24 12:28:18.044902", "docstatus": 0, "doctype": "Form Tour", + "first_document": 0, "idx": 0, + "include_name_field": 0, "is_standard": 1, - "modified": "2021-08-24 12:28:18.044902", + "modified": "2022-01-18 18:32:17.102330", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges Template", "owner": "Administrator", "reference_doctype": "Sales Taxes and Charges Template", - "save_on_complete": 0, + "save_on_complete": 1, "steps": [ { "description": "A name by which you will identify this template. You can change this later.", diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json index 2e0ab4305d..aa7cdf788b 100644 --- a/erpnext/accounts/module_onboarding/accounts/accounts.json +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -13,15 +13,12 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "idx": 0, "is_complete": 0, - "modified": "2021-08-13 11:59:35.690443", + "modified": "2022-01-18 18:35:52.326688", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", "owner": "Administrator", "steps": [ - { - "step": "Company" - }, { "step": "Chart of Accounts" }, diff --git a/erpnext/accounts/onboarding_step/company/company.json b/erpnext/accounts/onboarding_step/company/company.json deleted file mode 100644 index 4992e4d018..0000000000 --- a/erpnext/accounts/onboarding_step/company/company.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "action": "Go to Page", - "action_label": "Let's Review your Company", - "creation": "2021-06-29 14:47:42.497318", - "description": "# Company\n\nIn ERPNext, you can also create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company. \n", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2021-08-13 11:43:35.767341", - "modified_by": "Administrator", - "name": "Company", - "owner": "Administrator", - "path": "app/company", - "reference_document": "Company", - "show_form_tour": 0, - "show_full_form": 0, - "title": "Review Company", - "validate_action": 1 -} \ No newline at end of file