From 15e97a14dcfc348698452848ca6a3b4414e2e0dc Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Feb 2022 12:16:31 +0530 Subject: [PATCH 01/11] refactor: trigger generate schedule when any change made in items table --- .../maintenance_schedule.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index 035290d8f196..864437ba0576 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -152,12 +152,26 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap this.set_no_of_visits(doc, cdt, cdn); } + serial_no(doc, cdt, cdn) { + let me = this; + if (doc.docstatus == 0) { + me.frm.call('generate_schedule'); + } + } + + sales_person(doc, cdt, cdn) { + let me = this; + if (doc.docstatus == 0) { + me.frm.call('generate_schedule'); + } + } + set_no_of_visits(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); let me = this; - if (item.start_date && item.periodicity) { + if (item.start_date && item.periodicity && doc.docstatus == 0) { me.frm.call('validate_end_date_visits'); - + me.frm.call('generate_schedule'); } } }; From 817d2aeb61b4fd9ca3c6fe2b6bb120454d1700c2 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Feb 2022 13:08:23 +0530 Subject: [PATCH 02/11] chore: added serial validation on server side --- .../doctype/maintenance_schedule/maintenance_schedule.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 07d928c221fd..0e2ef92d0396 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -200,11 +200,18 @@ def validate_sales_order(self): def validate_no_of_visits(self): return len(self.schedules) != sum(d.no_of_visits for d in self.items) + def validate_serials_updated(self): + items_serials = {item.item_code: item.serial_no for item in self.items} + schedules_serials = {item.item_code: item.serial_no for item in self.schedules} + if items_serials != schedules_serials: + self.generate_schedule() + def validate(self): self.validate_end_date_visits() self.validate_maintenance_detail() self.validate_dates_with_periodicity() self.validate_sales_order() + self.validate_serials_updated() if not self.schedules or self.validate_no_of_visits(): self.generate_schedule() From b027e9b27b8dc4813723c79e834e888cdf6a8d90 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Feb 2022 13:08:50 +0530 Subject: [PATCH 03/11] test: serials updated in schedules after save --- .../test_maintenance_schedule.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 6e727e53efd9..e7382b135323 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -117,6 +117,21 @@ def test_serial_no_filters(self): frappe.db.rollback() + def test_schedule_with_serials(self): + # Checks whether serials are automatically updated when changing in items table. + item_code = "_Test Serial Item" + make_serial_item_with_serial(item_code) + ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002") + ms.save() + ms.items[0].serial_no = "TEST001" + # Before Save + self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002") + # After Save + ms.save() + self.assertEqual(ms.schedules[0].serial_no, "TEST001") + + 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: From 2baec97da3dae50168a0fdcfab2f5c97ef905ef3 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 18 Feb 2022 13:26:57 +0530 Subject: [PATCH 04/11] fix: schedule not generating after updating some fields --- .../doctype/maintenance_schedule/maintenance_schedule.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index 864437ba0576..c95b1e498747 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -152,14 +152,14 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap this.set_no_of_visits(doc, cdt, cdn); } - serial_no(doc, cdt, cdn) { + serial_no(doc) { let me = this; if (doc.docstatus == 0) { me.frm.call('generate_schedule'); } } - sales_person(doc, cdt, cdn) { + sales_person(doc) { let me = this; if (doc.docstatus == 0) { me.frm.call('generate_schedule'); @@ -170,8 +170,9 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap var item = frappe.get_doc(cdt, cdn); let me = this; if (item.start_date && item.periodicity && doc.docstatus == 0) { - me.frm.call('validate_end_date_visits'); - me.frm.call('generate_schedule'); + me.frm.call('validate_end_date_visits').then(() => { + me.frm.call('generate_schedule'); + }); } } }; From ae4a51ac2372d0ed6ae7ebd0e8d95f464da47365 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 10 Mar 2022 11:09:30 +0530 Subject: [PATCH 05/11] feat: generate_schedule is triggered on_save when items table is changed --- .../maintenance_schedule.js | 35 ------------------- .../maintenance_schedule.py | 25 ++++++++++--- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index c95b1e498747..5252798ba57e 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -140,41 +140,6 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap } } - start_date(doc, cdt, cdn) { - this.set_no_of_visits(doc, cdt, cdn); - } - - end_date(doc, cdt, cdn) { - this.set_no_of_visits(doc, cdt, cdn); - } - - periodicity(doc, cdt, cdn) { - this.set_no_of_visits(doc, cdt, cdn); - } - - serial_no(doc) { - let me = this; - if (doc.docstatus == 0) { - me.frm.call('generate_schedule'); - } - } - - sales_person(doc) { - let me = this; - if (doc.docstatus == 0) { - me.frm.call('generate_schedule'); - } - } - - set_no_of_visits(doc, cdt, cdn) { - var item = frappe.get_doc(cdt, cdn); - let me = this; - if (item.start_date && item.periodicity && doc.docstatus == 0) { - me.frm.call('validate_end_date_visits').then(() => { - me.frm.call('generate_schedule'); - }); - } - } }; extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm})); diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 0e2ef92d0396..ae8a2901333c 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -197,23 +197,38 @@ def validate_sales_order(self): if chk: throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order)) - def validate_no_of_visits(self): - return len(self.schedules) != sum(d.no_of_visits for d in self.items) - def validate_serials_updated(self): items_serials = {item.item_code: item.serial_no for item in self.items} schedules_serials = {item.item_code: item.serial_no for item in self.schedules} if items_serials != schedules_serials: self.generate_schedule() + def validate_items_table_change(self): + doc_before_save = self.get_doc_before_save() + if not doc_before_save: + return + for prev_item, item in zip(doc_before_save.items, self.items): + fields = ['item_code', 'start_date', 'end_date', 'periodicity', 'sales_person', 'no_of_visits'] + schedule_generated = False + for field in fields: + b_doc = prev_item.as_dict() + doc = item.as_dict() + if cstr(b_doc[field]) != cstr(doc[field]): + schedule_generated = True + self.generate_schedule() + break + if schedule_generated: + break + def validate(self): self.validate_end_date_visits() self.validate_maintenance_detail() self.validate_dates_with_periodicity() self.validate_sales_order() - self.validate_serials_updated() - if not self.schedules or self.validate_no_of_visits(): + self.validate_items_table_change() + if not self.schedules: self.generate_schedule() + self.validate_serials_updated() def on_update(self): frappe.db.set(self, 'status', 'Draft') From e3e9d55ff3332faf78b45ab7f0f4b0a32010fdcd Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 10 Mar 2022 11:46:25 +0530 Subject: [PATCH 06/11] test: updated tests to check other field changes on save --- .../maintenance_schedule/test_maintenance_schedule.py | 9 ++++++++- 1 file changed, 8 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 e7382b135323..dbafbb0e324f 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -119,16 +119,23 @@ def test_serial_no_filters(self): def test_schedule_with_serials(self): # Checks whether serials are automatically updated when changing in items table. + # Also checks if other fields trigger generate schdeule if changed in items table. item_code = "_Test Serial Item" make_serial_item_with_serial(item_code) ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002") ms.save() - ms.items[0].serial_no = "TEST001" # Before Save self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002") + self.assertEqual(ms.schedules[0].sales_person, "Sales Team") + self.assertEqual(len(ms.schedules), 4) # After Save + ms.items[0].serial_no = "TEST001" + ms.items[0].sales_person = "_Test Sales Person" + ms.items[0].no_of_visits = 2 ms.save() self.assertEqual(ms.schedules[0].serial_no, "TEST001") + self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person") + self.assertEqual(len(ms.schedules), 2) frappe.db.rollback() From 8705e8ceb08f8eebd4f1c8a6eaf31b1b90289fd6 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 10 Mar 2022 12:37:27 +0530 Subject: [PATCH 07/11] chore: removed serial validation function for schedules table and added no_of_visits validation function --- .../maintenance_schedule/maintenance_schedule.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index ae8a2901333c..d31596d3486c 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -197,18 +197,12 @@ def validate_sales_order(self): if chk: throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order)) - def validate_serials_updated(self): - items_serials = {item.item_code: item.serial_no for item in self.items} - schedules_serials = {item.item_code: item.serial_no for item in self.schedules} - if items_serials != schedules_serials: - self.generate_schedule() - def validate_items_table_change(self): doc_before_save = self.get_doc_before_save() if not doc_before_save: return for prev_item, item in zip(doc_before_save.items, self.items): - fields = ['item_code', 'start_date', 'end_date', 'periodicity', 'sales_person', 'no_of_visits'] + fields = ['item_code', 'start_date', 'end_date', 'periodicity', 'sales_person', 'no_of_visits', 'serial_no'] schedule_generated = False for field in fields: b_doc = prev_item.as_dict() @@ -220,15 +214,17 @@ def validate_items_table_change(self): if schedule_generated: break + def validate_no_of_visits(self): + return len(self.schedules) != sum(d.no_of_visits for d in self.items) + def validate(self): self.validate_end_date_visits() self.validate_maintenance_detail() self.validate_dates_with_periodicity() self.validate_sales_order() self.validate_items_table_change() - if not self.schedules: + if not self.schedules or self.validate_no_of_visits(): self.generate_schedule() - self.validate_serials_updated() def on_update(self): frappe.db.set(self, 'status', 'Draft') From d58dac9d38ed54441874b2c9d3edec01470300a4 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 10 Mar 2022 12:38:08 +0530 Subject: [PATCH 08/11] test: updated for manually deleted schedele rows --- .../maintenance_schedule/test_maintenance_schedule.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index dbafbb0e324f..6fbe7a5140a1 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -136,6 +136,11 @@ def test_schedule_with_serials(self): self.assertEqual(ms.schedules[0].serial_no, "TEST001") self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person") self.assertEqual(len(ms.schedules), 2) + # When user manually deleted a row from schedules table. + ms.schedules.pop() + self.assertEqual(len(ms.schedules), 1) + ms.save() + self.assertEqual(len(ms.schedules), 2) frappe.db.rollback() From fac763adade1dc901d027883b68dad558f9ecdc6 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 15 Mar 2022 10:27:29 +0530 Subject: [PATCH 09/11] refactor: updated validate_items_table_change to return bool --- .../maintenance_schedule/maintenance_schedule.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index d31596d3486c..7e50cfee7a79 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -203,16 +203,11 @@ def validate_items_table_change(self): return for prev_item, item in zip(doc_before_save.items, self.items): fields = ['item_code', 'start_date', 'end_date', 'periodicity', 'sales_person', 'no_of_visits', 'serial_no'] - schedule_generated = False for field in fields: b_doc = prev_item.as_dict() doc = item.as_dict() if cstr(b_doc[field]) != cstr(doc[field]): - schedule_generated = True - self.generate_schedule() - break - if schedule_generated: - break + return True def validate_no_of_visits(self): return len(self.schedules) != sum(d.no_of_visits for d in self.items) @@ -222,8 +217,7 @@ def validate(self): self.validate_maintenance_detail() self.validate_dates_with_periodicity() self.validate_sales_order() - self.validate_items_table_change() - if not self.schedules or self.validate_no_of_visits(): + if not self.schedules or self.validate_items_table_change() or self.validate_no_of_visits(): self.generate_schedule() def on_update(self): From ea22530a9c1b2b856743351532ab11965c68adad Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 21 Mar 2022 17:22:16 +0530 Subject: [PATCH 10/11] test: updated test_schedule_with_serials to cover validate_items_table_change --- .../doctype/maintenance_schedule/test_maintenance_schedule.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 6fbe7a5140a1..93e394881769 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -124,14 +124,17 @@ def test_schedule_with_serials(self): make_serial_item_with_serial(item_code) ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002") ms.save() + # Before Save self.assertEqual(ms.schedules[0].serial_no, "TEST001, TEST002") self.assertEqual(ms.schedules[0].sales_person, "Sales Team") self.assertEqual(len(ms.schedules), 4) + self.assertFalse(ms.validate_items_table_change()) # After Save ms.items[0].serial_no = "TEST001" ms.items[0].sales_person = "_Test Sales Person" ms.items[0].no_of_visits = 2 + self.assertTrue(ms.validate_items_table_change()) ms.save() self.assertEqual(ms.schedules[0].serial_no, "TEST001") self.assertEqual(ms.schedules[0].sales_person, "_Test Sales Person") From f2a32b8b0c729d782a96acf24b6d72ed297354cf Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 30 Mar 2022 14:52:48 +0530 Subject: [PATCH 11/11] fix: linting --- .../maintenance_schedule/maintenance_schedule.py | 10 +++++++++- .../maintenance_schedule/test_maintenance_schedule.py | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 06fa76c72532..d5991a8365c6 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -218,7 +218,15 @@ def validate_items_table_change(self): if not doc_before_save: return for prev_item, item in zip(doc_before_save.items, self.items): - fields = ['item_code', 'start_date', 'end_date', 'periodicity', 'sales_person', 'no_of_visits', 'serial_no'] + fields = [ + "item_code", + "start_date", + "end_date", + "periodicity", + "sales_person", + "no_of_visits", + "serial_no", + ] for field in fields: b_doc = prev_item.as_dict() doc = item.as_dict() diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index f2aee7aec22e..2268e0f7afab 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -153,6 +153,7 @@ def test_schedule_with_serials(self): 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: