From d881dc47840234b37efd53ee3c45cef310115505 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 24 Apr 2020 14:33:34 +0530 Subject: [PATCH 01/57] feat: metrics for Issue --- erpnext/support/doctype/issue/issue.json | 33 ++++++++++++++-- erpnext/support/doctype/issue/issue.py | 49 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index c12cef4a5f..79729f2902 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -34,6 +34,8 @@ "response", "mins_to_first_response", "first_responded_on", + "column_break_26", + "avg_response_time", "additional_info", "lead", "contact", @@ -50,7 +52,9 @@ "resolution_date", "content_type", "attachment", - "via_customer_portal" + "via_customer_portal", + "operational_time", + "user_operational_time" ], "fields": [ { @@ -362,12 +366,35 @@ "label": "Issue Split From", "options": "Issue", "read_only": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "avg_response_time", + "fieldtype": "Time", + "label": "Average Response Time", + "read_only": 1 + }, + { + "fieldname": "operational_time", + "fieldtype": "Time", + "label": "Operational Time", + "read_only": 1 + }, + { + "fieldname": "user_operational_time", + "fieldtype": "Time", + "label": "User Operational Time", + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-03-13 02:19:49.477928", + "modified": "2020-04-24 09:58:13.499635", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -395,4 +422,4 @@ "title_field": "subject", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 117267f1a4..62b87ff552 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime +from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user @@ -63,6 +63,9 @@ class Issue(Document): self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) + set_average_response_time(issue=self) + set_operational_time(issue=self) + set_user_operational_time(issue=self) self.update_agreement_status() if self.status=="Open" and status !="Open": @@ -311,6 +314,50 @@ def set_service_level_agreement_variance(issue=None): if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) +def set_average_response_time(issue): + communications = frappe.get_list("Communication", filters={ + "reference_doctype": issue.doctype, + "reference_name": issue.name + }, + fields=["sent_or_received", "name", "creation"], + order_by="creation" + ) + + response_times = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": + response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if response_time > 0: + response_times.append(response_time) + avg_response_time = sum(response_times) / len(response_times) + avg_response_time = str(timedelta(seconds=avg_response_time)).split(".")[0] + issue.db_set('avg_response_time', avg_response_time) + +def set_operational_time(issue): + operational_time = time_diff(now_datetime(), issue.creation) + issue.db_set('operational_time', str(operational_time).split(".")[0]) + +def set_user_operational_time(issue): + communications = frappe.get_list("Communication", filters={ + "reference_doctype": issue.doctype, + "reference_name": issue.name + }, + fields=["sent_or_received", "name", "creation"], + order_by="creation" + ) + + pending_time = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent": + wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if wait_time > 0: + pending_time.append(wait_time) + total_pending_time = timedelta(seconds=sum(pending_time)) + operational_time = frappe.db.get_value('Issue', issue.name, 'operational_time') + user_operational_time = time_diff(operational_time, total_pending_time) + issue.db_set('user_operational_time', str(user_operational_time)) + + def get_list_context(context=None): return { "title": _("Issues"), From 2c3f1677f8f7f2cd19bd97a3460492366f201292 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 28 Apr 2020 14:11:58 +0530 Subject: [PATCH 02/57] fix: commonify SLA and Service Level Doctypes --- .../service_level_agreement.js | 25 +----- .../service_level_agreement.json | 44 ++++----- .../service_level_agreement.py | 89 ++++++++++++++++++- .../service_level_priority.json | 23 ++--- .../support_settings/support_settings.json | 4 +- 5 files changed, 122 insertions(+), 63 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 1d486f4834..7aeaae48ba 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -2,28 +2,5 @@ // For license information, please see license.txt frappe.ui.form.on('Service Level Agreement', { - service_level: function(frm) { - frm.fields_dict.support_and_resolution.grid.remove_all(); - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Service Level", - name: frm.doc.service_level - }, - callback: function(data){ - let count = Math.max(data.message.priorities.length, data.message.support_and_resolution.length); - let i = 0; - while (i < count){ - if (data.message.priorities[i]) { - frm.add_child("priorities", data.message.priorities[i]); - } - if (data.message.support_and_resolution[i]) { - frm.add_child("support_and_resolution", data.message.support_and_resolution[i]); - } - i++; - } - frm.refresh(); - } - }); - }, + }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 9a83ca7ac0..3725e15a54 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:SLA-{service_level}-{####}", "creation": "2018-12-26 21:08:15.448812", "doctype": "DocType", @@ -6,12 +7,12 @@ "engine": "InnoDB", "field_order": [ "enable", + "section_break_2", "service_level", "default_service_level_agreement", - "holiday_list", "column_break_2", "employee_group", - "default_priority", + "holiday_list", "entity_section", "entity_type", "column_break_10", @@ -27,43 +28,31 @@ "support_and_resolution" ], "fields": [ - { - "default": "0", - "depends_on": "eval: !doc.customer;", - "fieldname": "default_service_level_agreement", - "fieldtype": "Check", - "label": "Default Service Level Agreement" - }, { "fieldname": "service_level", - "fieldtype": "Link", + "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, "label": "Service Level", - "options": "Service Level", "reqd": 1 }, { - "fetch_from": "service_level.holiday_list", "fieldname": "holiday_list", "fieldtype": "Link", "label": "Holiday List", - "options": "Holiday List", - "read_only": 1 + "options": "Holiday List" }, { "fieldname": "column_break_2", "fieldtype": "Column Break" }, { - "fetch_from": "service_level.employee_group", "fieldname": "employee_group", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Employee Group", - "options": "Employee Group", - "read_only": 1 + "options": "Employee Group" }, { "fieldname": "agreement_details_section", @@ -111,14 +100,6 @@ "label": "Priorities", "options": "Service Level Priority" }, - { - "fetch_from": "service_level.default_priority", - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 - }, { "default": "1", "fieldname": "active", @@ -156,9 +137,20 @@ "fieldname": "enable", "fieldtype": "Check", "label": "Enable" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "default_service_level_agreement", + "fieldtype": "Check", + "label": "Default Service Level Agreement" } ], - "modified": "2019-07-09 17:22:16.402939", + "links": [], + "modified": "2020-04-28 14:10:18.767202", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index a399c58b16..9fa0e238f9 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,11 +6,85 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate +from frappe.utils import getdate, get_weekdays +from datetime import datetime class ServiceLevelAgreement(Document): def validate(self): + self.validate_doc() + self.check_priorities() + self.check_support_and_resolution() + + def check_priorities(self): + default_priority = [] + priorities = [] + + for priority in self.priorities: + # Check if response and resolution time is set for every priority + if not (priority.response_time or priority.resolution_time): + frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) + + priorities.append(priority.priority) + + if priority.default_priority: + default_priority.append(priority.default_priority) + + if priority.response_time_period == "Hour": + response = priority.response_time * 0.0416667 + elif priority.response_time_period == "Day": + response = priority.response_time + elif priority.response_time_period == "Week": + response = priority.response_time * 7 + + if priority.resolution_time_period == "Hour": + resolution = priority.resolution_time * 0.0416667 + elif priority.resolution_time_period == "Day": + resolution = priority.resolution_time + elif priority.resolution_time_period == "Week": + resolution = priority.resolution_time * 7 + + if response > resolution: + frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) + + # Check if repeated priority + if not len(set(priorities)) == len(priorities): + repeated_priority = get_repeated(priorities) + frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) + + # Check if repeated default priority + if not len(set(default_priority)) == len(default_priority): + frappe.throw(_("Select only one Priority as Default.")) + + # set default priority from priorities + try: + self.default_priority = next(d.priority for d in self.priorities if d.default_priority) + except Exception: + frappe.throw(_("Select a Default Priority.")) + + def check_support_and_resolution(self): + week = get_weekdays() + support_days = [] + + for support_and_resolution in self.support_and_resolution: + # Check if start and end time is set for every support day + if not (support_and_resolution.start_time or support_and_resolution.end_time): + frappe.throw(_("Set Start Time and End Time for \ + Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) + + support_days.append(support_and_resolution.workday) + support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 + + if support_and_resolution.start_time >= support_and_resolution.end_time: + frappe.throw(_("Start Time can't be greater than or equal to End Time \ + for {0}.".format(support_and_resolution.workday))) + + # Check for repeated workday + if not len(set(support_days)) == len(support_days): + repeated_days = get_repeated(support_days) + frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) + + def validate_doc(self): if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): frappe.throw(_("Service Level Agreement tracking is not enabled.")) @@ -110,4 +184,15 @@ def get_service_level_agreement_filters(name, customer=None): return { "priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])], "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)] - } \ No newline at end of file + } + +def get_repeated(values): + unique_list = [] + diff = [] + for value in values: + if value not in unique_list: + unique_list.append(str(value)) + else: + if value not in diff: + diff.append(str(value)) + return " ".join(diff) diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index cd87a1c113..f56008eb4c 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-04 05:54:03.658991", "doctype": "DocType", "editable_grid": 1, @@ -16,7 +17,7 @@ ], "fields": [ { - "columns": 2, + "columns": 1, "fieldname": "priority", "fieldtype": "Link", "in_list_view": 1, @@ -28,14 +29,7 @@ "fieldtype": "Section Break" }, { - "columns": 1, - "fieldname": "response_time", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Response Time" - }, - { - "columns": 1, + "columns": 2, "fieldname": "resolution_time", "fieldtype": "Int", "in_list_view": 1, @@ -66,15 +60,24 @@ "fieldtype": "Column Break" }, { + "columns": 1, "default": "0", "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, "label": "Default Priority" + }, + { + "columns": 2, + "fieldname": "response_time", + "fieldtype": "Int", + "in_list_view": 1, + "label": "First Response Time" } ], "istable": 1, - "modified": "2019-05-21 06:54:42.674377", + "links": [], + "modified": "2020-04-24 14:50:13.774308", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index be9e064591..3f52181a46 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2017-02-17 13:07:35.686409", "doctype": "DocType", "editable_grid": 1, @@ -128,7 +129,8 @@ } ], "issingle": 1, - "modified": "2019-07-10 22:52:39.663873", + "links": [], + "modified": "2020-04-28 14:11:15.117019", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 0e3a16e2670eb68bfca31d702d7ce38d348902bd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 29 Apr 2020 00:13:33 +0530 Subject: [PATCH 03/57] fix: set metrics duration --- erpnext/support/doctype/issue/issue.json | 18 +++---- erpnext/support/doctype/issue/issue.py | 61 +++++++++++++++++------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 79729f2902..dfe0647cfa 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -53,7 +53,7 @@ "content_type", "attachment", "via_customer_portal", - "operational_time", + "resolution_time", "user_operational_time" ], "fields": [ @@ -374,27 +374,27 @@ { "bold": 1, "fieldname": "avg_response_time", - "fieldtype": "Time", + "fieldtype": "Data", "label": "Average Response Time", "read_only": 1 }, { - "fieldname": "operational_time", - "fieldtype": "Time", - "label": "Operational Time", + "fieldname": "user_operational_time", + "fieldtype": "Data", + "label": "User Operational Time", "read_only": 1 }, { - "fieldname": "user_operational_time", - "fieldtype": "Time", - "label": "User Operational Time", + "fieldname": "resolution_time", + "fieldtype": "Data", + "label": "Resolution Time", "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-04-24 09:58:13.499635", + "modified": "2020-04-28 23:42:28.576580", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 62b87ff552..027225942c 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,12 +59,12 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed" and status !="Closed": + if self.status=="Closed": self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) set_average_response_time(issue=self) - set_operational_time(issue=self) + set_resolution_time(issue=self) set_user_operational_time(issue=self) self.update_agreement_status() @@ -323,19 +323,25 @@ def set_average_response_time(issue): order_by="creation" ) - response_times = [] - for i in range(len(communications)-1): - if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": - response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) - if response_time > 0: - response_times.append(response_time) - avg_response_time = sum(response_times) / len(response_times) - avg_response_time = str(timedelta(seconds=avg_response_time)).split(".")[0] - issue.db_set('avg_response_time', avg_response_time) + if len(communications): + response_times = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": + response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if response_time > 0: + response_times.append(response_time) + + avg_response_time = sum(response_times) / len(response_times) + avg_response_time = timedelta(seconds=avg_response_time) + duration = get_duration(avg_response_time) + issue.db_set('avg_response_time', duration) + + +def set_resolution_time(issue): + resolution_time = time_diff(now_datetime(), issue.creation) + duration = get_duration(resolution_time) + issue.db_set('resolution_time', duration) -def set_operational_time(issue): - operational_time = time_diff(now_datetime(), issue.creation) - issue.db_set('operational_time', str(operational_time).split(".")[0]) def set_user_operational_time(issue): communications = frappe.get_list("Communication", filters={ @@ -352,10 +358,31 @@ def set_user_operational_time(issue): wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if wait_time > 0: pending_time.append(wait_time) + total_pending_time = timedelta(seconds=sum(pending_time)) - operational_time = frappe.db.get_value('Issue', issue.name, 'operational_time') - user_operational_time = time_diff(operational_time, total_pending_time) - issue.db_set('user_operational_time', str(user_operational_time)) + resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time = timedelta(seconds=resolution_time_in_secs) + user_operational_time = resolution_time - total_pending_time + duration = get_duration(user_operational_time) + issue.db_set('user_operational_time', duration) + + +def get_duration(time): + days = time.days + seconds = time.seconds + hours = time.seconds // 3600 + mins = (time.seconds // 60) % 60 + duration = "" + if days: + duration += str(days) + " day" + duration += "s " if days > 1 else " " + if hours: + duration += str(hours) + " hour" + duration += "s " if hours > 1 else " " + if mins: + duration += str(mins) + " min" + duration += "s" if mins > 1 else "" + return duration def get_list_context(context=None): From ec24afb283d2af641266dd250f70479f0b8eb43b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 5 May 2020 10:02:14 +0530 Subject: [PATCH 04/57] fix: change response and resolution fieldtype to Duration in SLA --- .../service_level_agreement.py | 19 +++----------- .../service_level_priority.json | 26 +++---------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 9fa0e238f9..530230e1e8 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -30,19 +30,8 @@ class ServiceLevelAgreement(Document): if priority.default_priority: default_priority.append(priority.default_priority) - if priority.response_time_period == "Hour": - response = priority.response_time * 0.0416667 - elif priority.response_time_period == "Day": - response = priority.response_time - elif priority.response_time_period == "Week": - response = priority.response_time * 7 - - if priority.resolution_time_period == "Hour": - resolution = priority.resolution_time * 0.0416667 - elif priority.resolution_time_period == "Day": - resolution = priority.resolution_time - elif priority.resolution_time_period == "Week": - resolution = priority.resolution_time * 7 + response = priority.response_time + resolution = priority.resolution_time if response > resolution: frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) @@ -109,9 +98,7 @@ class ServiceLevelAgreement(Document): return frappe._dict({ "priority": priority.priority, "response_time": priority.response_time, - "response_time_period": priority.response_time_period, - "resolution_time": priority.resolution_time, - "resolution_time_period": priority.resolution_time_period + "resolution_time": priority.resolution_time }) def check_agreement_status(): diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index f56008eb4c..6377d1a962 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -10,10 +10,8 @@ "default_priority", "sb_00", "response_time", - "response_time_period", "cb_00", - "resolution_time", - "resolution_time_period" + "resolution_time" ], "fields": [ { @@ -31,7 +29,7 @@ { "columns": 2, "fieldname": "resolution_time", - "fieldtype": "Int", + "fieldtype": "Duration", "in_list_view": 1, "label": "Resolution Time" }, @@ -39,22 +37,6 @@ "fieldname": "cb_00", "fieldtype": "Column Break" }, - { - "columns": 2, - "fieldname": "response_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Response Time Period", - "options": "Hour\nDay\nWeek" - }, - { - "columns": 2, - "fieldname": "resolution_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Resolution Time Period", - "options": "Hour\nDay\nWeek" - }, { "fieldname": "cb_01", "fieldtype": "Column Break" @@ -70,14 +52,14 @@ { "columns": 2, "fieldname": "response_time", - "fieldtype": "Int", + "fieldtype": "Duration", "in_list_view": 1, "label": "First Response Time" } ], "istable": 1, "links": [], - "modified": "2020-04-24 14:50:13.774308", + "modified": "2020-05-04 22:08:04.503949", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From 35d853a476b689443b44b0c1fee11609a7ea86d9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 5 May 2020 18:54:50 +0530 Subject: [PATCH 05/57] fix: SLA in issues with Duration Fieldtype --- erpnext/support/doctype/issue/issue.py | 35 ++++++++------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 027225942c..1489f43138 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,7 +59,7 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed": + if self.status=="Closed" and status !="Closed": self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) @@ -230,24 +230,14 @@ def get_expected_time_for(parameter, service_level, start_date_time): start_time = None end_time = None - # lets assume response time is in days by default if parameter == 'response': - allotted_days = service_level.get("response_time") - time_period = service_level.get("response_time_period") + allotted_seconds = service_level.get("response_time") elif parameter == 'resolution': - allotted_days = service_level.get("resolution_time") - time_period = service_level.get("resolution_time_period") + allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) - allotted_hours = 0 - if time_period == 'Hour': - allotted_hours = allotted_days - allotted_days = 0 - elif time_period == 'Week': - allotted_days *= 7 - - expected_time_is_set = 1 if allotted_days == 0 and time_period in ['Day', 'Week'] else 0 + expected_time_is_set = 0 support_days = {} for service in service_level.get("support_and_resolution"): @@ -267,25 +257,22 @@ def get_expected_time_for(parameter, service_level, start_date_time): if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \ else support_days[current_weekday].start_time end_time = support_days[current_weekday].end_time - time_left_today = time_diff_in_hours(end_time, start_time) + time_left_today = time_diff_in_seconds(end_time, start_time) # no time left for support today - if time_left_today < 0: pass - elif time_period == 'Hour': - if time_left_today >= allotted_hours: + if time_left_today <= 0: pass + elif allotted_seconds: + if time_left_today >= allotted_seconds: expected_time = datetime.combine(getdate(current_date_time), get_time(start_time)) - expected_time = add_to_date(expected_time, hours=allotted_hours) + expected_time = add_to_date(expected_time, seconds=allotted_seconds) expected_time_is_set = 1 else: - allotted_hours = allotted_hours - time_left_today - else: - allotted_days -= 1 - expected_time_is_set = allotted_days <= 0 + allotted_seconds = allotted_seconds - time_left_today if not expected_time_is_set: current_date_time = add_to_date(current_date_time, days=1) - if end_time and time_period != 'Hour': + if end_time and allotted_seconds >= 86400: current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) else: current_date_time = expected_time From a2a1e257ae92fce590a208e26a970bfc6fe604ef Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:09:33 +0530 Subject: [PATCH 06/57] fix: set Issue metrics using Duration fieldtype --- erpnext/support/doctype/issue/issue.py | 38 ++++++-------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 1489f43138..df0a2f662d 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -302,6 +302,7 @@ def set_service_level_agreement_variance(issue=None): frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) def set_average_response_time(issue): + # avg response time for all the responses communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, "reference_name": issue.name @@ -319,18 +320,17 @@ def set_average_response_time(issue): response_times.append(response_time) avg_response_time = sum(response_times) / len(response_times) - avg_response_time = timedelta(seconds=avg_response_time) - duration = get_duration(avg_response_time) - issue.db_set('avg_response_time', duration) + issue.db_set('avg_response_time', avg_response_time) def set_resolution_time(issue): - resolution_time = time_diff(now_datetime(), issue.creation) - duration = get_duration(resolution_time) - issue.db_set('resolution_time', duration) + # total time taken from issue creation to closing + resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) + issue.db_set('resolution_time', resolution_time) def set_user_operational_time(issue): + # total time taken by a user to close the issue apart from wait_time communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, "reference_name": issue.name @@ -346,30 +346,10 @@ def set_user_operational_time(issue): if wait_time > 0: pending_time.append(wait_time) - total_pending_time = timedelta(seconds=sum(pending_time)) + total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) - resolution_time = timedelta(seconds=resolution_time_in_secs) - user_operational_time = resolution_time - total_pending_time - duration = get_duration(user_operational_time) - issue.db_set('user_operational_time', duration) - - -def get_duration(time): - days = time.days - seconds = time.seconds - hours = time.seconds // 3600 - mins = (time.seconds // 60) % 60 - duration = "" - if days: - duration += str(days) + " day" - duration += "s " if days > 1 else " " - if hours: - duration += str(hours) + " hour" - duration += "s " if hours > 1 else " " - if mins: - duration += str(mins) + " min" - duration += "s" if mins > 1 else "" - return duration + user_operational_time = resolution_time_in_secs - total_pending_time + issue.db_set('user_operational_time', user_operational_time) def get_list_context(context=None): From fc4c795661d17a682b4abe97fa194fd501ec196f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:42:43 +0530 Subject: [PATCH 07/57] fix: reset issue metrics on Reopen and Split --- erpnext/support/doctype/issue/issue.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index df0a2f662d..f2ee75498e 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -71,6 +71,7 @@ class Issue(Document): if self.status=="Open" and status !="Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None + self.reset_issue_metrics() def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": @@ -131,6 +132,7 @@ class Issue(Document): replicated_issue.response_by_variance = None replicated_issue.resolution_by = None replicated_issue.resolution_by_variance = None + replicated_issue.reset_issue_metrics() frappe.get_doc(replicated_issue).insert() @@ -224,6 +226,12 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" self.save() + def reset_issue_metrics(self): + self.db_set('resolution_time', 0) + self.db_set('user_operational_time', 0) + self.db_set('avg_response_time',0) + + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time From f77d7243dc6ef71fcfbf29f20464581cd7ac96b3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:47:52 +0530 Subject: [PATCH 08/57] fix: remove Service Level DocType --- .../support/doctype/service_level/__init__.py | 0 .../doctype/service_level/service_level.js | 6 - .../doctype/service_level/service_level.json | 111 ------------- .../doctype/service_level/service_level.py | 95 ----------- .../service_level/service_level_dashboard.py | 12 -- .../service_level/test_service_level.py | 149 ------------------ .../service_level_agreement.json | 11 +- 7 files changed, 7 insertions(+), 377 deletions(-) delete mode 100644 erpnext/support/doctype/service_level/__init__.py delete mode 100644 erpnext/support/doctype/service_level/service_level.js delete mode 100644 erpnext/support/doctype/service_level/service_level.json delete mode 100644 erpnext/support/doctype/service_level/service_level.py delete mode 100644 erpnext/support/doctype/service_level/service_level_dashboard.py delete mode 100644 erpnext/support/doctype/service_level/test_service_level.py diff --git a/erpnext/support/doctype/service_level/__init__.py b/erpnext/support/doctype/service_level/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/support/doctype/service_level/service_level.js b/erpnext/support/doctype/service_level/service_level.js deleted file mode 100644 index abe254bd03..0000000000 --- a/erpnext/support/doctype/service_level/service_level.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Service Level', { - -}); diff --git a/erpnext/support/doctype/service_level/service_level.json b/erpnext/support/doctype/service_level/service_level.json deleted file mode 100644 index dced3aa9e9..0000000000 --- a/erpnext/support/doctype/service_level/service_level.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "autoname": "field:service_level", - "creation": "2018-11-19 12:44:30.407502", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "service_level", - "employee_group", - "column_break_2", - "holiday_list", - "default_priority", - "response_and_resoution_time", - "priorities", - "section_break_01", - "support_and_resolution" - ], - "fields": [ - { - "fieldname": "service_level", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Level", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Holiday List (ignored during SLA calculation)", - "options": "Holiday List", - "reqd": 1 - }, - { - "fieldname": "employee_group", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee Group", - "options": "Employee Group" - }, - { - "fieldname": "response_and_resoution_time", - "fieldtype": "Section Break", - "label": "Response and Resoution Time" - }, - { - "fieldname": "section_break_01", - "fieldtype": "Section Break", - "label": "Support Hours" - }, - { - "fieldname": "support_and_resolution", - "fieldtype": "Table", - "label": "Support and Resolution", - "options": "Service Day", - "reqd": 1 - }, - { - "fieldname": "priorities", - "fieldtype": "Table", - "label": "Priorities", - "options": "Service Level Priority", - "reqd": 1 - }, - { - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 - } - ], - "modified": "2019-06-06 12:58:03.464056", - "modified_by": "Administrator", - "module": "Support", - "name": "Service Level", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/service_level.py b/erpnext/support/doctype/service_level/service_level.py deleted file mode 100644 index 89fa25c233..0000000000 --- a/erpnext/support/doctype/service_level/service_level.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.document import Document -from datetime import datetime -from frappe.utils import get_weekdays - -class ServiceLevel(Document): - - def validate(self): - self.check_priorities() - self.check_support_and_resolution() - - def check_priorities(self): - default_priority = [] - priorities = [] - - for priority in self.priorities: - # Check if response and resolution time is set for every priority - if not (priority.response_time or priority.resolution_time): - frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) - - priorities.append(priority.priority) - - if priority.default_priority: - default_priority.append(priority.default_priority) - - if priority.response_time_period == "Hour": - response = priority.response_time * 0.0416667 - elif priority.response_time_period == "Day": - response = priority.response_time - elif priority.response_time_period == "Week": - response = priority.response_time * 7 - - if priority.resolution_time_period == "Hour": - resolution = priority.resolution_time * 0.0416667 - elif priority.resolution_time_period == "Day": - resolution = priority.resolution_time - elif priority.resolution_time_period == "Week": - resolution = priority.resolution_time * 7 - - if response > resolution: - frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) - - # Check if repeated priority - if not len(set(priorities)) == len(priorities): - repeated_priority = get_repeated(priorities) - frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) - - # Check if repeated default priority - if not len(set(default_priority)) == len(default_priority): - frappe.throw(_("Select only one Priority as Default.")) - - # set default priority from priorities - try: - self.default_priority = next(d.priority for d in self.priorities if d.default_priority) - except Exception: - frappe.throw(_("Select a Default Priority.")) - - def check_support_and_resolution(self): - week = get_weekdays() - support_days = [] - - for support_and_resolution in self.support_and_resolution: - # Check if start and end time is set for every support day - if not (support_and_resolution.start_time or support_and_resolution.end_time): - frappe.throw(_("Set Start Time and End Time for \ - Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) - - support_days.append(support_and_resolution.workday) - support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 - - if support_and_resolution.start_time >= support_and_resolution.end_time: - frappe.throw(_("Start Time can't be greater than or equal to End Time \ - for {0}.".format(support_and_resolution.workday))) - - # Check for repeated workday - if not len(set(support_days)) == len(support_days): - repeated_days = get_repeated(support_days) - frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) - -def get_repeated(values): - unique_list = [] - diff = [] - for value in values: - if value not in unique_list: - unique_list.append(str(value)) - else: - if value not in diff: - diff.append(str(value)) - return " ".join(diff) diff --git a/erpnext/support/doctype/service_level/service_level_dashboard.py b/erpnext/support/doctype/service_level/service_level_dashboard.py deleted file mode 100644 index 393095e117..0000000000 --- a/erpnext/support/doctype/service_level/service_level_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'fieldname': 'service_level', - 'transactions': [ - { - 'label': _('Service Level Agreement'), - 'items': ['Service Level Agreement'] - } - ] - } \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/test_service_level.py b/erpnext/support/doctype/service_level/test_service_level.py deleted file mode 100644 index 09577df166..0000000000 --- a/erpnext/support/doctype/service_level/test_service_level.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals -from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group -from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities - -import frappe -import unittest - -class TestServiceLevel(unittest.TestCase): - - def test_service_level(self): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - test_make_service_level = create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - get_make_service_level = get_service_level("__Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - # Service Level - test_make_service_level = create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) - get_make_service_level = get_service_level("_Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - -def create_service_level(service_level, holiday_list, employee_group, response_time, resolution_time): - sl = frappe.get_doc({ - "doctype": "Service Level", - "service_level": service_level, - "holiday_list": holiday_list, - "employee_group": employee_group, - "priorities": [ - { - "priority": "Low", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "Medium", - "response_time": response_time, - "default_priority": 1, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "High", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - } - ], - "support_and_resolution": [ - { - "workday": "Monday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Tuesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Wednesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Thursday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Friday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Saturday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Sunday", - "start_time": "10:00:00", - "end_time": "18:00:00", - } - ] - }) - - sl_exists = frappe.db.exists("Service Level", {"service_level": service_level}) - - if not sl_exists: - sl.insert() - return sl - else: - return frappe.get_doc("Service Level", {"service_level": service_level}) - -def get_service_level(service_level): - return frappe.get_doc("Service Level", service_level) - -def make_holiday_list(): - holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") - if not holiday_list: - now = frappe.utils.now_datetime() - holiday_list = frappe.get_doc({ - "doctype": "Holiday List", - "holiday_list_name": "__Test Holiday List", - "from_date": "2019-01-01", - "to_date": "2019-12-31", - "holidays": [ - { - "description": "Test Holiday 1", - "holiday_date": "2019-03-05" - }, - { - "description": "Test Holiday 2", - "holiday_date": "2019-03-07" - }, - { - "description": "Test Holiday 3", - "holiday_date": "2019-02-11" - }, - ] - }).insert() - -def create_service_level_for_sla(): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - - # Service Level - create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 3725e15a54..2d33c3e033 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -40,7 +40,8 @@ "fieldname": "holiday_list", "fieldtype": "Link", "label": "Holiday List", - "options": "Holiday List" + "options": "Holiday List", + "reqd": 1 }, { "fieldname": "column_break_2", @@ -92,13 +93,15 @@ "fieldname": "support_and_resolution", "fieldtype": "Table", "label": "Support and Resolution", - "options": "Service Day" + "options": "Service Day", + "reqd": 1 }, { "fieldname": "priorities", "fieldtype": "Table", "label": "Priorities", - "options": "Service Level Priority" + "options": "Service Level Priority", + "reqd": 1 }, { "default": "1", @@ -150,7 +153,7 @@ } ], "links": [], - "modified": "2020-04-28 14:10:18.767202", + "modified": "2020-05-06 11:46:38.834810", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", From f2d36364f5ce821ca172a2c76c711ee6c5b47746 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 12:32:55 +0530 Subject: [PATCH 09/57] fix: handle issue metrics on Reopen and Close --- erpnext/support/doctype/issue/issue.json | 30 ++++++++++++++---------- erpnext/support/doctype/issue/issue.py | 18 +++++++------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index dfe0647cfa..131e1cb9bf 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -54,7 +54,7 @@ "attachment", "via_customer_portal", "resolution_time", - "user_operational_time" + "user_resolution_time" ], "fields": [ { @@ -374,27 +374,33 @@ { "bold": 1, "fieldname": "avg_response_time", - "fieldtype": "Data", + "fieldtype": "Duration", "label": "Average Response Time", - "read_only": 1 - }, - { - "fieldname": "user_operational_time", - "fieldtype": "Data", - "label": "User Operational Time", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "resolution_time", - "fieldtype": "Data", + "fieldtype": "Duration", "label": "Resolution Time", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "user_resolution_time", + "fieldtype": "Duration", + "label": "User Resolution Time", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-04-28 23:42:28.576580", + "modified": "2020-05-06 12:28:58.093654", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index f2ee75498e..4fb2d8a2b9 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -63,10 +63,10 @@ class Issue(Document): self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) - set_average_response_time(issue=self) - set_resolution_time(issue=self) - set_user_operational_time(issue=self) self.update_agreement_status() + set_average_response_time(issue=self) + set_resolution_time(issue=self) + set_user_resolution_time(issue=self) if self.status=="Open" and status !="Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config @@ -227,9 +227,9 @@ class Issue(Document): self.save() def reset_issue_metrics(self): - self.db_set('resolution_time', 0) - self.db_set('user_operational_time', 0) - self.db_set('avg_response_time',0) + self.db_set('resolution_time', None) + self.db_set('user_resolution_time', None) + self.db_set('avg_response_time', None) def get_expected_time_for(parameter, service_level, start_date_time): @@ -337,7 +337,7 @@ def set_resolution_time(issue): issue.db_set('resolution_time', resolution_time) -def set_user_operational_time(issue): +def set_user_resolution_time(issue): # total time taken by a user to close the issue apart from wait_time communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, @@ -356,8 +356,8 @@ def set_user_operational_time(issue): total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) - user_operational_time = resolution_time_in_secs - total_pending_time - issue.db_set('user_operational_time', user_operational_time) + user_resolution_time = resolution_time_in_secs - total_pending_time + issue.db_set('user_resolution_time', user_resolution_time) def get_list_context(context=None): From 3d891f8e8937e6233055ce89f176e35e9b818dcf Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 13:00:33 +0530 Subject: [PATCH 10/57] fix: set SLA as Ongoing on Issue Reopen --- erpnext/support/doctype/issue/issue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 4fb2d8a2b9..2d9392c572 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,6 +72,7 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + self.agreement_fulfilled = "Ongoing" def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": From 8993d38dafd8515eff56ed026e9cfa5ff0e80ee1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 13:37:10 +0530 Subject: [PATCH 11/57] fix: set avg response time only when there are responses --- erpnext/support/doctype/issue/issue.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 2d9392c572..a9c4897017 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -322,14 +322,14 @@ def set_average_response_time(issue): if len(communications): response_times = [] - for i in range(len(communications)-1): + for i in range(len(communications)): if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if response_time > 0: response_times.append(response_time) - - avg_response_time = sum(response_times) / len(response_times) - issue.db_set('avg_response_time', avg_response_time) + if response_times: + avg_response_time = sum(response_times) / len(response_times) + issue.db_set('avg_response_time', avg_response_time) def set_resolution_time(issue): @@ -349,7 +349,7 @@ def set_user_resolution_time(issue): ) pending_time = [] - for i in range(len(communications)-1): + for i in range(len(communications)): if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent": wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if wait_time > 0: From 372aa44b1953850fb493c2941fa45ba20ec85cf5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 21:37:58 +0530 Subject: [PATCH 12/57] feat: implement hold time on Replied status for SLA --- erpnext/support/doctype/issue/issue.js | 14 +++++++- erpnext/support/doctype/issue/issue.json | 20 +++++++++++- erpnext/support/doctype/issue/issue.py | 41 +++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index bad40cc37f..66c62d1ff2 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -41,7 +41,19 @@ frappe.ui.form.on("Issue", { if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { if (frm.doc.service_level_agreement) { - set_time_to_resolve_and_response(frm); + if (frm.doc.status == "Replied") { + frm.dashboard.clear_headline(); + let message = {"indicator": "orange", "msg": __("Replied {0}", [moment(frm.doc.on_hold_since).fromNow()])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm); + } } frm.add_custom_button(__("Close"), function () { diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 131e1cb9bf..82443a51cb 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -31,6 +31,8 @@ "resolution_by", "resolution_by_variance", "service_level_agreement_creation", + "on_hold_since", + "total_hold_time", "response", "mins_to_first_response", "first_responded_on", @@ -395,12 +397,28 @@ "read_only": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "on_hold_since", + "fieldtype": "Datetime", + "label": "On Hold Since", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "total_hold_time", + "fieldtype": "Duration", + "label": "Total Hold Time", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-06 12:28:58.093654", + "modified": "2020-05-14 23:59:01.172007", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index a9c4897017..47b9c05161 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,7 +72,36 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() - self.agreement_fulfilled = "Ongoing" + + if self.status == "Replied" and status != "Replied": + self.on_hold_since = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + self.response_by = None + self.response_by_variance = None + self.resolution_by = None + self.resolution_by_variance = None + + if self.status != "Replied" and status == "Replied": + hold_time = self.total_hold_time if self.total_hold_time else 0 + self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + + if self.status == "Open" and status == "Replied": + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) + + if not self.first_responded_on: + response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) + self.response_by = add_to_date(response_by, seconds=round(hold_time)) + response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) + self.response_by_variance = response_by_variance + (hold_time // 3600) + + resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) + self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) + self.on_hold_since = None + def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": @@ -233,6 +262,16 @@ class Issue(Document): self.db_set('avg_response_time', None) +def get_priority(issue): + service_level_agreement = frappe.get_doc("Service Level Agreement", issue.service_level_agreement) + priority = service_level_agreement.get_service_level_agreement_priority(issue.priority) + priority.update({ + "support_and_resolution": service_level_agreement.support_and_resolution, + "holiday_list": service_level_agreement.holiday_list + }) + return priority + + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time From 79ed5755be8f4d393a2d4045165d42858bede911 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 22:04:04 +0530 Subject: [PATCH 13/57] refactor: remove Hold status --- erpnext/support/doctype/issue/issue.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 82443a51cb..2b79e03f45 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", + "options": "Open\nReplied\nClosed", "search_index": 1 }, { @@ -167,6 +167,7 @@ "options": "Service Level Agreement" }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "response_by", "fieldtype": "Datetime", "label": "Response By", @@ -180,6 +181,7 @@ "read_only": 1 }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "resolution_by", "fieldtype": "Datetime", "label": "Resolution By", @@ -334,7 +336,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "response_by_variance", "fieldtype": "Float", @@ -342,7 +344,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "resolution_by_variance", "fieldtype": "Float", @@ -418,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-14 23:59:01.172007", + "modified": "2020-05-25 22:02:32.974165", "modified_by": "Administrator", "module": "Support", "name": "Issue", From 899ec3653259b4973987e7a5f7d324c944bb3ec8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:23:28 +0530 Subject: [PATCH 14/57] feat: added Resolved status with same functionality as Closed --- erpnext/support/doctype/issue/issue.json | 4 ++-- erpnext/support/doctype/issue/issue.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 2b79e03f45..8fb94013af 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nClosed", + "options": "Open\nReplied\nResolved\nClosed", "search_index": 1 }, { @@ -420,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-25 22:02:32.974165", + "modified": "2020-05-26 12:12:59.343559", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 47b9c05161..8bc00fd2f0 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,7 +59,7 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed" and status !="Closed": + if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) From ec6246306cfc6fa43cca182e6d67e4ea6ba77136 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:32:12 +0530 Subject: [PATCH 15/57] refactor: set variance and SLA as Ongoing on Issue reopen --- erpnext/support/doctype/issue/issue.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 8bc00fd2f0..02a2df1c19 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,6 +72,8 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + self.agreement_fulfilled = "Ongoing" + set_service_level_agreement_variance(issue=self.name) if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() From 5379e87880409c300a2d22a219b810e32ed4a2e9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:48:03 +0530 Subject: [PATCH 16/57] refactor: code cleanup, break functions --- erpnext/support/doctype/issue/issue.py | 62 +++++++++++++------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 02a2df1c19..b7da29649d 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -47,8 +47,8 @@ class Issue(Document): self.contact = frappe.db.get_value("Contact", {"email_id": email_id}) if self.contact: - contact = frappe.get_doc('Contact', self.contact) - self.customer = contact.get_link_for('Customer') + contact = frappe.get_doc("Contact", self.contact) + self.customer = contact.get_link_for("Customer") if not self.company: self.company = frappe.db.get_value("Lead", self.lead, "company") or \ @@ -56,7 +56,7 @@ class Issue(Document): def update_status(self): status = frappe.db.get_value("Issue", self.name, "status") - if self.status!="Open" and status =="Open" and not self.first_responded_on: + if self.status != "Open" and status == "Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: @@ -68,13 +68,18 @@ class Issue(Document): set_resolution_time(issue=self) set_user_resolution_time(issue=self) - if self.status=="Open" and status !="Open": + if self.status == "Open" and status != "Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + # enable SLA and variance on Reopen self.agreement_fulfilled = "Ongoing" set_service_level_agreement_variance(issue=self.name) + self.handle_hold_time() + + def handle_hold_time(self): + # set response and resolution variance as None as the issue is on Hold for status as Replied if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() if not self.first_responded_on: @@ -83,28 +88,30 @@ class Issue(Document): self.resolution_by = None self.resolution_by_variance = None + # calculate hold time when status is changed from Replied to any other status if self.status != "Replied" and status == "Replied": hold_time = self.total_hold_time if self.total_hold_time else 0 self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + # re-calculate SLA variables after issue changes from Replied to Open + # add hold time to SLA variables if self.status == "Open" and status == "Replied": start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) if not self.first_responded_on: - response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.response_by = add_to_date(response_by, seconds=round(hold_time)) response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) self.response_by_variance = response_by_variance + (hold_time // 3600) - resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) self.on_hold_since = None - def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": if frappe.db.get_value("Issue", self.name, "response_by_variance") < 0 or \ @@ -174,7 +181,7 @@ class Issue(Document): communications = frappe.get_all("Communication", filters={"reference_doctype": "Issue", "reference_name": comm_to_split_from.reference_name, - "creation": ('>=', comm_to_split_from.creation)}) + "creation": (">=", comm_to_split_from.creation)}) for communication in communications: doc = frappe.get_doc("Communication", communication.name) @@ -210,20 +217,15 @@ class Issue(Document): self.service_level_agreement = service_level_agreement.name self.priority = service_level_agreement.default_priority if not priority else priority - service_level_agreement = frappe.get_doc("Service Level Agreement", service_level_agreement.name) - priority = service_level_agreement.get_service_level_agreement_priority(self.priority) - priority.update({ - "support_and_resolution": service_level_agreement.support_and_resolution, - "holiday_list": service_level_agreement.holiday_list - }) + priority = get_priority(self) if not self.creation: self.creation = now_datetime() self.service_level_agreement_creation = now_datetime() start_date_time = get_datetime(self.service_level_agreement_creation) - self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) - self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) @@ -259,9 +261,9 @@ class Issue(Document): self.save() def reset_issue_metrics(self): - self.db_set('resolution_time', None) - self.db_set('user_resolution_time', None) - self.db_set('avg_response_time', None) + self.db_set("resolution_time", None) + self.db_set("user_resolution_time", None) + self.db_set("avg_response_time", None) def get_priority(issue): @@ -280,9 +282,9 @@ def get_expected_time_for(parameter, service_level, start_date_time): start_time = None end_time = None - if parameter == 'response': + if parameter == "response": allotted_seconds = service_level.get("response_time") - elif parameter == 'resolution': + elif parameter == "resolution": allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) @@ -292,8 +294,8 @@ def get_expected_time_for(parameter, service_level, start_date_time): support_days = {} for service in service_level.get("support_and_resolution"): support_days[service.workday] = frappe._dict({ - 'start_time': service.start_time, - 'end_time': service.end_time, + "start_time": service.start_time, + "end_time": service.end_time, }) holidays = get_holidays(service_level.get("holiday_list")) @@ -370,13 +372,13 @@ def set_average_response_time(issue): response_times.append(response_time) if response_times: avg_response_time = sum(response_times) / len(response_times) - issue.db_set('avg_response_time', avg_response_time) + issue.db_set("avg_response_time", avg_response_time) def set_resolution_time(issue): # total time taken from issue creation to closing resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) - issue.db_set('resolution_time', resolution_time) + issue.db_set("resolution_time", resolution_time) def set_user_resolution_time(issue): @@ -399,7 +401,7 @@ def set_user_resolution_time(issue): total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time - issue.db_set('user_resolution_time', user_resolution_time) + issue.db_set("user_resolution_time", user_resolution_time) def get_list_context(context=None): @@ -409,7 +411,7 @@ def get_list_context(context=None): "row_template": "templates/includes/issue_row.html", "show_sidebar": True, "show_search": True, - 'no_breadcrumbs': True + "no_breadcrumbs": True } @@ -417,12 +419,12 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord from frappe.www.list import get_list user = frappe.session.user - contact = frappe.db.get_value('Contact', {'user': user}, 'name') + contact = frappe.db.get_value("Contact", {"user": user}, "name") customer = None if contact: - contact_doc = frappe.get_doc('Contact', contact) - customer = contact_doc.get_link_for('Customer') + contact_doc = frappe.get_doc("Contact", contact) + customer = contact_doc.get_link_for("Customer") ignore_permissions = False if is_website_user(): From f3fc544918643502e0ec12f19fa18901c62795de Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 15:03:15 +0530 Subject: [PATCH 17/57] fix: SLA tests --- .../test_service_level_agreement.py | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 4a741ea4e1..94e0b582f9 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -5,19 +5,19 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.support.doctype.service_level.test_service_level import create_service_level_for_sla +from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group +from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities class TestServiceLevelAgreement(unittest.TestCase): def test_service_level_agreement(self): frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) - create_service_level_for_sla() - # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) + get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name) @@ -28,7 +28,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Customer", entity=customer, response_time=2, resolution_time=3) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) @@ -40,7 +40,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer Group customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) @@ -52,7 +52,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Territory territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Territory", entity=territory, response_time=2, resolution_time=3) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) @@ -71,14 +71,18 @@ def get_service_level_agreement(default_service_level_agreement=None, entity_typ service_level_agreement = frappe.get_doc("Service Level Agreement", filters) return service_level_agreement -def create_service_level_agreement(default_service_level_agreement, service_level, holiday_list, employee_group, +def create_service_level_agreement(default_service_level_agreement, holiday_list, employee_group, response_time, entity_type, entity, resolution_time): + employee_group = make_employee_group() + make_holiday_list() + make_priorities() + service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", "enable": 1, + "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, - "service_level": service_level, "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -167,6 +171,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve else: return frappe.get_doc("Service Level Agreement", service_level_agreement_exists) + def create_customer(): customer = frappe.get_doc({ "doctype": "Customer", @@ -206,23 +211,42 @@ def create_territory(): return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"}) def create_service_level_agreements_for_issues(): - create_service_level_for_sla() - - create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) create_customer() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) create_customer_group() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) create_territory() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + +def make_holiday_list(): + holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") + if not holiday_list: + now = frappe.utils.now_datetime() + holiday_list = frappe.get_doc({ + "doctype": "Holiday List", + "holiday_list_name": "__Test Holiday List", + "from_date": "2019-01-01", + "to_date": "2019-12-31", + "holidays": [ + { + "description": "Test Holiday 1", + "holiday_date": "2019-03-05" + }, + { + "description": "Test Holiday 2", + "holiday_date": "2019-03-07" + }, + { + "description": "Test Holiday 3", + "holiday_date": "2019-02-11" + }, + ] + }).insert() From f91ed4ce248490d7fe93856ab4159d4a11e1f78b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 16:03:47 +0530 Subject: [PATCH 18/57] fix: add default priority field in SLA --- .../service_level_agreement.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 2d33c3e033..ede5f98eba 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -9,6 +9,7 @@ "enable", "section_break_2", "service_level", + "default_priority", "default_service_level_agreement", "column_break_2", "employee_group", @@ -150,10 +151,19 @@ "fieldname": "default_service_level_agreement", "fieldtype": "Check", "label": "Default Service Level Agreement" + }, + { + "fieldname": "default_priority", + "fieldtype": "Link", + "label": "Default Priority", + "options": "Issue Priority", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "links": [], - "modified": "2020-05-06 11:46:38.834810", + "modified": "2020-05-26 16:02:59.859980", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", From c8e8e29083aa5d75615cbf22dd1dbe60cd47f7aa Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 13:48:09 +0530 Subject: [PATCH 19/57] test: Issue Metrics --- erpnext/support/doctype/issue/issue.py | 16 +++--- erpnext/support/doctype/issue/test_issue.py | 52 ++++++++++++++++++- .../test_service_level_agreement.py | 17 +++--- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index b7da29649d..31797dff42 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -76,9 +76,9 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" set_service_level_agreement_variance(issue=self.name) - self.handle_hold_time() + self.handle_hold_time(status) - def handle_hold_time(self): + def handle_hold_time(self, status): # set response and resolution variance as None as the issue is on Hold for status as Replied if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() @@ -91,24 +91,26 @@ class Issue(Document): # calculate hold time when status is changed from Replied to any other status if self.status != "Replied" and status == "Replied": hold_time = self.total_hold_time if self.total_hold_time else 0 - self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + now_time = frappe.flags.current_time or now_datetime() + self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) # re-calculate SLA variables after issue changes from Replied to Open # add hold time to SLA variables if self.status == "Open" and status == "Replied": start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.response_by = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) + response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) self.response_by_variance = response_by_variance + (hold_time // 3600) resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) self.on_hold_since = None @@ -377,7 +379,7 @@ def set_average_response_time(issue): def set_resolution_time(issue): # total time taken from issue creation to closing - resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time = time_diff_in_seconds(issue.resolution_date, issue.creation) issue.db_set("resolution_time", resolution_time) @@ -399,7 +401,7 @@ def set_user_resolution_time(issue): pending_time.append(wait_time) total_pending_time = sum(pending_time) - resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time issue.db_set("user_resolution_time", user_resolution_time) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 7a5e3e300d..2818b1b8eb 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -10,10 +10,13 @@ import datetime from datetime import timedelta class TestIssue(unittest.TestCase): - def test_response_time_and_resolution_time_based_on_different_sla(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") + frappe.db.sql("delete from `tabEmployee`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() + def test_response_time_and_resolution_time_based_on_different_sla(self): creation = datetime.datetime(2019, 3, 4, 12, 0) # make issue with customer specific SLA @@ -72,6 +75,33 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.agreement_fulfilled, 'Fulfilled') + def test_issue_metrics(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + # make issue with customer specific SLA + customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory") + issue = make_issue(creation, "_Test Customer", 1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + issue = frappe.get_doc('Issue', issue.name) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.status = 'Closed' + issue.save() + + self.assertEqual(issue.avg_response_time, 600) + self.assertEqual(issue.resolution_time, 3900) + self.assertEqual(issue.user_resolution_time, 1200) + + def make_issue(creation=None, customer=None, index=0): issue = frappe.get_doc({ @@ -86,6 +116,7 @@ def make_issue(creation=None, customer=None, index=0): return issue + def create_customer(name, customer_group, territory): create_customer_group(customer_group) @@ -99,6 +130,7 @@ def create_customer(name, customer_group, territory): "territory": territory }).insert(ignore_permissions=True) + def create_customer_group(customer_group): if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}): @@ -107,6 +139,7 @@ def create_customer_group(customer_group): "customer_group_name": customer_group }).insert(ignore_permissions=True) + def create_territory(territory): if not frappe.db.exists("Territory", {"territory_name": territory}): @@ -114,3 +147,20 @@ def create_territory(territory): "doctype": "Territory", "territory_name": territory, }).insert(ignore_permissions=True) + + +def create_communication(reference_name, sender, sent_or_received, creation): + issue = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "sent_or_received": sent_or_received, + "email_status": "Open", + "subject": "Test Issue", + "sender": sender, + "content": "Test", + "status": "Linked", + "reference_doctype": "Issue", + "creation": creation, + "reference_name": reference_name + }).insert(ignore_permissions=True) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 94e0b582f9..57d4747e5c 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -16,7 +16,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + entity_type=None, entity=None, response_time=14400, resolution_time=21600) get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) @@ -29,7 +29,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity=customer, response_time=2, resolution_time=3) + entity_type="Customer", entity=customer, response_time=7200, resolution_time=10800) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name) @@ -41,7 +41,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) + entity_type="Customer Group", entity=customer_group, response_time=7200, resolution_time=10800) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name) @@ -53,7 +53,7 @@ class TestServiceLevelAgreement(unittest.TestCase): territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity=territory, response_time=2, resolution_time=3) + entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name) @@ -83,6 +83,7 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "enable": 1, "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, + "default_priority": "Medium", "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -212,19 +213,19 @@ def create_territory(): def create_service_level_agreements_for_issues(): create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=14400, resolution_time=21600) create_customer() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800) create_customer_group() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800) create_territory() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800) def make_holiday_list(): holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") From 376a46f1e5d14d6fc0c2b1f9fa05af174cf2b629 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 14:33:30 +0530 Subject: [PATCH 20/57] test: hold time for Replied status --- erpnext/support/doctype/issue/issue.py | 2 +- erpnext/support/doctype/issue/test_issue.py | 45 ++++++++++++++++++--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 31797dff42..c09c729c5c 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -100,7 +100,7 @@ class Issue(Document): start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) now_time = frappe.flags.current_time or now_datetime() - hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) + hold_time = time_diff_in_seconds(now_time, self.on_hold_since) if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 2818b1b8eb..a004843270 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -78,9 +78,7 @@ class TestIssue(unittest.TestCase): def test_issue_metrics(self): creation = datetime.datetime(2020, 3, 4, 4, 0) - # make issue with customer specific SLA - customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory") - issue = make_issue(creation, "_Test Customer", 1) + issue = make_issue(creation, index=1) create_communication(issue.name, "test@example.com", "Received", creation) creation = datetime.datetime(2020, 3, 4, 4, 15) @@ -91,9 +89,9 @@ class TestIssue(unittest.TestCase): creation = datetime.datetime(2020, 3, 4, 5, 5) create_communication(issue.name, "test@admin.com", "Sent", creation) - issue = frappe.get_doc('Issue', issue.name) frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() issue.status = 'Closed' issue.save() @@ -101,9 +99,43 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.resolution_time, 3900) self.assertEqual(issue.user_resolution_time, 1200) + def test_hold_time_on_replied(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + issue = make_issue(creation, index=1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15) + issue.reload() + issue.status = 'Replied' + issue.save() + + self.assertEqual(issue.on_hold_since, frappe.flags.current_time) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + issue.reload() + self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45)) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() + issue.status = 'Closed' + issue.save() + + issue.reload() + self.assertEqual(issue.total_hold_time, 2700) + def make_issue(creation=None, customer=None, index=0): - issue = frappe.get_doc({ "doctype": "Issue", "subject": "Service Level Agreement Issue {0}".format(index), @@ -163,4 +195,5 @@ def create_communication(reference_name, sender, sent_or_received, creation): "reference_doctype": "Issue", "creation": creation, "reference_name": reference_name - }).insert(ignore_permissions=True) + }) + issue.save() From bc38289a595749824530ed5f6399b9d50153f3db Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 29 May 2020 11:43:05 +0530 Subject: [PATCH 21/57] fix: added patch for sla enhancements --- erpnext/patches.txt | 3 +- .../patches/v13_0/update_sla_enhancements.py | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v13_0/update_sla_enhancements.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0421f43c6..3ea15375d2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -694,4 +694,5 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports -erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +erpnext.patches.v13_0.update_sla_enhancements \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py new file mode 100644 index 0000000000..1d5f372b9b --- /dev/null +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -0,0 +1,111 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + # add holiday list and employee group fields in SLA + # change response and resolution time in priorities child table + if frappe.db.exists('DocType', 'Service Level Agreement'): + sla_details = frappe.db.get_all('Service Level Agreement', fields=['name', 'service_level']) + priorities = frappe.db.get_all('Service Level Priority', fields=['*'], filters={ + 'parenttype': ('in', ['Service Level Agreement', 'Service Level']) + }) + + frappe.reload_doc('support', 'doctype', 'service_level_agreement') + frappe.reload_doc('support', 'doctype', 'service_level_priority') + + for entry in sla_details: + values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group']) + if values: + holiday_list = values[0] + employee_group = values[1] + frappe.db.set_value('Service Level Agreement', entry.name, { + 'holiday_list': holiday_list, + 'employee_group': employee_group + }) + + priority_dict = {} + + for priority in priorities: + if priority.parenttype == 'Service Level Agreement': + response_time = convert_to_seconds(priority.response_time, priority.response_time_period) + resolution_time = convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + frappe.db.set_value('Service Level Priority', priority.name, { + 'response_time': response_time, + 'resolution_time': resolution_time + }) + if priority.parenttype == 'Service Level': + if not priority.parent in priority_dict: + priority_dict[priority.parent] = [] + priority_dict[priority.parent].append(priority) + + + # copy Service Levels to Service Level Agreements + sl = [entry.service_level for entry in sla_details] + service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) + for entry in service_levels: + sla = frappe.new_doc('Service Level Agreement') + sla.service_level = entry.service_level + sla.holiday_list = entry.holiday_list + sla.employee_group = entry.employee_group + sla.flags.ignore_validate = True + sla = sla.insert(ignore_mandatory=True) + + frappe.db.sql(""" + UPDATE + `tabService Day` + SET + parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' + WHERE + parent = %(old_parent)s + """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) + + priority_list = priority_dict.get(entry.name) + if priority_list: + sla = frappe.get_doc('Service Level Agreement', sla.name) + for priority in priority_list: + row = sla.append('priorities', { + 'priority': priority.priority, + 'default_priority': priority.default_priority, + 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), + 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + }) + row.db_update() + sla.db_update() + + # set issue status as Replied since Hold status is removed + if frappe.db.exists('DocType', 'Issue'): + issues_on_hold = frappe.db.sql(""" + SELECT + name + FROM + `tabIssue` + WHERE + status = 'Hold' + """, as_dict=1) + + issues = [entry.name for entry in issues_on_hold] + + frappe.reload_doc('support', 'doctype', 'issue') + frappe.db.sql(""" + UPDATE + `tabIssue` + SET + status='Replied' + WHERE + name in %(issues)s + """, {'issues': issues}, debug=1) + + +def convert_to_seconds(value, unit): + seconds = 0 + if unit == "Hour": + seconds = value * 3600 + if unit == "Day": + seconds = value * 3600 * 24 + if unit == "Week": + seconds = value * 3600 * 24 * 7 + return seconds From b3338a149bbb14c982e75a3dfce1b25425d7087e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 12:35:00 +0530 Subject: [PATCH 22/57] fix: code clean-up --- .../patches/v13_0/update_sla_enhancements.py | 26 ++++++++----------- erpnext/support/doctype/issue/issue.js | 4 +-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 1d5f372b9b..91c613fa46 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -22,10 +22,10 @@ def execute(): if values: holiday_list = values[0] employee_group = values[1] - frappe.db.set_value('Service Level Agreement', entry.name, { - 'holiday_list': holiday_list, - 'employee_group': employee_group - }) + frappe.db.set_value('Service Level Agreement', entry.name, { + 'holiday_list': holiday_list, + 'employee_group': employee_group + }) priority_dict = {} @@ -76,18 +76,14 @@ def execute(): row.db_update() sla.db_update() + frappe.delete_doc('DocType', 'Service Level') + # set issue status as Replied since Hold status is removed if frappe.db.exists('DocType', 'Issue'): - issues_on_hold = frappe.db.sql(""" - SELECT - name - FROM - `tabIssue` - WHERE - status = 'Hold' - """, as_dict=1) - + issues_on_hold = frappe.db.get_all('Issue', {'status': 'Hold'}) issues = [entry.name for entry in issues_on_hold] + if not issues: + return frappe.reload_doc('support', 'doctype', 'issue') frappe.db.sql(""" @@ -96,8 +92,8 @@ def execute(): SET status='Replied' WHERE - name in %(issues)s - """, {'issues': issues}, debug=1) + name IN %(issues)s + """, {'issues': issues}) def convert_to_seconds(value, unit): diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 66c62d1ff2..6632ab69ba 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -43,11 +43,11 @@ frappe.ui.form.on("Issue", { if (frm.doc.service_level_agreement) { if (frm.doc.status == "Replied") { frm.dashboard.clear_headline(); - let message = {"indicator": "orange", "msg": __("Replied {0}", [moment(frm.doc.on_hold_since).fromNow()])}; + let message = {"indicator": "orange", "msg": __("Replied {0}", [frappe.datetime.comment_when(frm.doc.on_hold_since)])}; frm.dashboard.set_headline_alert( '
' + '
' + - ' ' + + ''+ message.msg +' ' + '
' + '
' ); From 94d03c6100e5b6d0f14bca541a72602b3b492bbb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 12:35:36 +0530 Subject: [PATCH 23/57] fix: remove Service Level DocType from Support Desk Page --- erpnext/support/desk_page/support/support.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index a3fe72d051..b1ad7c8aa0 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-05-28 13:51:23.869954", + "modified": "2020-06-04 11:54:56.124219", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -65,8 +65,8 @@ "type": "DocType" }, { - "label": "Service Level", - "link_to": "Service Level", + "label": "Service Level Agreement", + "link_to": "Service Level Agreement", "type": "DocType" } ] From 3f083501818d5522faf76a53307b3eb585478629 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 15:56:59 +0530 Subject: [PATCH 24/57] fix: retain Hold status --- .../patches/v13_0/update_sla_enhancements.py | 17 ----------------- erpnext/support/doctype/issue/issue.json | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 91c613fa46..2356fb2679 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -78,23 +78,6 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') - # set issue status as Replied since Hold status is removed - if frappe.db.exists('DocType', 'Issue'): - issues_on_hold = frappe.db.get_all('Issue', {'status': 'Hold'}) - issues = [entry.name for entry in issues_on_hold] - if not issues: - return - - frappe.reload_doc('support', 'doctype', 'issue') - frappe.db.sql(""" - UPDATE - `tabIssue` - SET - status='Replied' - WHERE - name IN %(issues)s - """, {'issues': issues}) - def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 8fb94013af..70595e483a 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nResolved\nClosed", + "options": "Open\nReplied\nHold\nResolved\nClosed", "search_index": 1 }, { @@ -420,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-26 12:12:59.343559", + "modified": "2020-06-04 15:53:38.322514", "modified_by": "Administrator", "module": "Support", "name": "Issue", From b29cb878683f79e0d7e2dd50ec5bc3f20fe50956 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 13:35:38 +0530 Subject: [PATCH 25/57] refactor: setting avg_response_time moved to communication --- erpnext/support/doctype/issue/issue.py | 23 ------------------- .../service_level_priority.json | 22 +++++++++++++----- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index c09c729c5c..ac700c91e7 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -64,7 +64,6 @@ class Issue(Document): if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) self.update_agreement_status() - set_average_response_time(issue=self) set_resolution_time(issue=self) set_user_resolution_time(issue=self) @@ -265,7 +264,6 @@ class Issue(Document): def reset_issue_metrics(self): self.db_set("resolution_time", None) self.db_set("user_resolution_time", None) - self.db_set("avg_response_time", None) def get_priority(issue): @@ -355,27 +353,6 @@ def set_service_level_agreement_variance(issue=None): if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) -def set_average_response_time(issue): - # avg response time for all the responses - communications = frappe.get_list("Communication", filters={ - "reference_doctype": issue.doctype, - "reference_name": issue.name - }, - fields=["sent_or_received", "name", "creation"], - order_by="creation" - ) - - if len(communications): - response_times = [] - for i in range(len(communications)): - if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": - response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) - if response_time > 0: - response_times.append(response_time) - if response_times: - avg_response_time = sum(response_times) / len(response_times) - issue.db_set("avg_response_time", avg_response_time) - def set_resolution_time(issue): # total time taken from issue creation to closing diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 6377d1a962..3995d1e248 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -20,11 +20,15 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Priority", - "options": "Issue Priority" + "options": "Issue Priority", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_00", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -35,11 +39,15 @@ }, { "fieldname": "cb_00", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_01", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 1, @@ -47,7 +55,9 @@ "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, - "label": "Default Priority" + "label": "Default Priority", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -59,7 +69,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-04 22:08:04.503949", + "modified": "2020-06-05 13:08:26.428657", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From 2c9f7cf371ac0ee8d6751a87b6f44bd33edb17f7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:35:57 +0530 Subject: [PATCH 26/57] feat: pause SLA on statuses configuration --- erpnext/support/doctype/issue/issue.json | 3 +- erpnext/support/doctype/issue/issue.py | 64 ++++++++++--------- .../doctype/pause_sla_on_status/__init__.py | 0 .../pause_sla_on_status.json | 33 ++++++++++ .../pause_sla_on_status.py | 10 +++ .../support_settings/support_settings.js | 10 ++- .../support_settings/support_settings.json | 13 +++- 7 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 erpnext/support/doctype/pause_sla_on_status/__init__.py create mode 100644 erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json create mode 100644 erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 70595e483a..712b70c2dd 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -403,6 +403,7 @@ { "fieldname": "on_hold_since", "fieldtype": "Datetime", + "hidden": 1, "label": "On Hold Since", "read_only": 1, "show_days": 1, @@ -420,7 +421,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-06-04 15:53:38.322514", + "modified": "2020-06-05 15:45:24.474425", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index ac700c91e7..146eb5a249 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -78,40 +78,44 @@ class Issue(Document): self.handle_hold_time(status) def handle_hold_time(self, status): - # set response and resolution variance as None as the issue is on Hold for status as Replied - if self.status == "Replied" and status != "Replied": - self.on_hold_since = frappe.flags.current_time or now_datetime() - if not self.first_responded_on: - self.response_by = None - self.response_by_variance = None - self.resolution_by = None - self.resolution_by_variance = None + if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + # set response and resolution variance as None as the issue is on Hold for status as Replied + pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"]) + hold_statuses = [entry.status for entry in pause_sla_on] - # calculate hold time when status is changed from Replied to any other status - if self.status != "Replied" and status == "Replied": - hold_time = self.total_hold_time if self.total_hold_time else 0 - now_time = frappe.flags.current_time or now_datetime() - self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) + if self.status in hold_statuses and status not in hold_statuses: + self.on_hold_since = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + self.response_by = None + self.response_by_variance = None + self.resolution_by = None + self.resolution_by_variance = None - # re-calculate SLA variables after issue changes from Replied to Open - # add hold time to SLA variables - if self.status == "Open" and status == "Replied": - start_date_time = get_datetime(self.service_level_agreement_creation) - priority = get_priority(self) - now_time = frappe.flags.current_time or now_datetime() - hold_time = time_diff_in_seconds(now_time, self.on_hold_since) + # calculate hold time when status is changed from Replied to any other status + if self.status not in hold_statuses and status in hold_statuses: + hold_time = self.total_hold_time if self.total_hold_time else 0 + now_time = frappe.flags.current_time or now_datetime() + self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) - if not self.first_responded_on: - response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) - self.response_by = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) - self.response_by_variance = response_by_variance + (hold_time // 3600) + # re-calculate SLA variables after issue changes from Replied to Open + # add hold time to SLA variables + if self.status == "Open" and status in hold_statuses: + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() + hold_time = time_diff_in_seconds(now_time, self.on_hold_since) - resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) - self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) - self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) - self.on_hold_since = None + if not self.first_responded_on: + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + self.response_by = add_to_date(response_by, seconds=round(hold_time)) + response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) + self.response_by_variance = response_by_variance + (hold_time // 3600) + + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) + self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) + self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) + self.on_hold_since = None def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": diff --git a/erpnext/support/doctype/pause_sla_on_status/__init__.py b/erpnext/support/doctype/pause_sla_on_status/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json new file mode 100644 index 0000000000..5b03f25f48 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2020-06-05 13:59:43.265588", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status" + ], + "fields": [ + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-05 15:15:29.986608", + "modified_by": "Administrator", + "module": "Support", + "name": "Pause SLA On Status", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py new file mode 100644 index 0000000000..a3b547e480 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PauseSLAOnStatus(Document): + pass diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 1d1069d58b..33ddf0b079 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -2,7 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Support Settings', { - refresh: function(frm) { + setup: function(frm) { + let allow_statuses = []; + const exclude_statuses = ['Open', 'Closed', 'Resolved']; + frappe.model.with_doctype('Issue', () => { + let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; + statuses = statuses.split('\n'); + allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); + frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); + }); } }); diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 3f52181a46..da4b607e2c 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -10,6 +10,7 @@ "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", + "pause_sla_on_status", "portal_sb", "get_started_sections", "show_latest_forum_posts", @@ -123,14 +124,24 @@ }, { "default": "0", + "depends_on": "eval:doc.track_service_level_agreement;", "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" + }, + { + "depends_on": "eval:doc.track_service_level_agreement;", + "fieldname": "pause_sla_on_status", + "fieldtype": "Table", + "label": "Pause SLA On", + "options": "Pause SLA On Status", + "show_days": 1, + "show_seconds": 1 } ], "issingle": 1, "links": [], - "modified": "2020-04-28 14:11:15.117019", + "modified": "2020-06-05 16:35:13.905096", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 8d3992841847d09a118ae6d4efe6fa974c7fab63 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:36:31 +0530 Subject: [PATCH 27/57] refactor: dashboard indicator when SLA is on hold --- erpnext/support/doctype/issue/issue.js | 42 +++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 6632ab69ba..32a77739d0 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -38,22 +38,35 @@ frappe.ui.form.on("Issue", { }, refresh: function (frm) { - if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { if (frm.doc.service_level_agreement) { - if (frm.doc.status == "Replied") { - frm.dashboard.clear_headline(); - let message = {"indicator": "orange", "msg": __("Replied {0}", [frappe.datetime.comment_when(frm.doc.on_hold_since)])}; - frm.dashboard.set_headline_alert( - '
' + - '
' + - ''+ message.msg +' ' + - '
' + - '
' - ); - } else { - set_time_to_resolve_and_response(frm); - } + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Support Settings', + name: 'Support Settings' + }, + callback: function(data) { + let statuses = data.message.pause_sla_on_status; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ''+ message.msg +' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm); + } + } + }); } frm.add_custom_button(__("Close"), function () { @@ -67,6 +80,7 @@ frappe.ui.form.on("Issue", { frm: frm }); }, __("Make")); + } else { if (frm.doc.service_level_agreement) { frm.dashboard.clear_headline(); From 25ef6a963811210c65d6ef3778bc0923c365c5ad Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:37:20 +0530 Subject: [PATCH 28/57] fix: add default hold statuses in fixtures and patch --- erpnext/patches/v13_0/update_sla_enhancements.py | 4 ++++ erpnext/setup/install.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 2356fb2679..884d01b10c 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from erpnext.setup.install import add_sla_hold_statuses_to_support_settings def execute(): # add holiday list and employee group fields in SLA @@ -78,6 +79,9 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') + # add SLA hold statuses to Support Settings + add_sla_hold_statuses_to_support_settings() + def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index e666a41f30..90e5f5a0ae 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -25,6 +25,7 @@ def after_install(): create_default_success_action() create_default_energy_point_rules() add_company_to_session_defaults() + add_sla_hold_statuses_to_support_settings() frappe.db.commit() @@ -105,3 +106,13 @@ def add_company_to_session_defaults(): "ref_doctype": "Company" }) settings.save() + +def add_sla_hold_statuses_to_support_settings(): + settings = frappe.get_single("Support Settings") + settings.append("pause_sla_on_status", { + "status": "Replied" + }) + settings.append("pause_sla_on_status", { + "status": "Hold" + }) + settings.save() From d7d3ca4214e2be005da27342bf2d0fd6e621baa5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:57:37 +0530 Subject: [PATCH 29/57] fix: tests --- erpnext/support/doctype/issue/test_issue.py | 2 ++ .../service_level_agreement/test_service_level_agreement.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index a004843270..93a6005cc2 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues +from erpnext.setup.install import add_sla_hold_statuses_to_support_settings from frappe.utils import now_datetime, get_datetime import datetime from datetime import timedelta @@ -100,6 +101,7 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.user_resolution_time, 1200) def test_hold_time_on_replied(self): + add_sla_hold_statuses_to_support_settings() creation = datetime.datetime(2020, 3, 4, 4, 0) issue = make_issue(creation, index=1) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 57d4747e5c..26740ed899 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -9,10 +9,11 @@ from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_ from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities class TestServiceLevelAgreement(unittest.TestCase): - - def test_service_level_agreement(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) + def test_service_level_agreement(self): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", From 73326d308b610458e28dc0516ff50aee9d1c7d35 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 18:23:39 +0530 Subject: [PATCH 30/57] refactor: move pause SLA configuration to SLA DocType --- .../patches/v13_0/update_sla_enhancements.py | 4 ---- erpnext/setup/install.py | 10 ---------- erpnext/support/doctype/issue/issue.js | 6 +++--- erpnext/support/doctype/issue/issue.py | 5 +++-- erpnext/support/doctype/issue/test_issue.py | 2 -- .../service_level_agreement.js | 12 +++++++++++- .../service_level_agreement.json | 19 ++++++++++++++++++- .../test_service_level_agreement.py | 5 +++++ .../support_settings/support_settings.js | 12 ++---------- .../support_settings/support_settings.json | 12 +----------- 10 files changed, 43 insertions(+), 44 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 884d01b10c..2356fb2679 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import frappe -from erpnext.setup.install import add_sla_hold_statuses_to_support_settings def execute(): # add holiday list and employee group fields in SLA @@ -79,9 +78,6 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') - # add SLA hold statuses to Support Settings - add_sla_hold_statuses_to_support_settings() - def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 90e5f5a0ae..74ff0ecfd8 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -25,7 +25,6 @@ def after_install(): create_default_success_action() create_default_energy_point_rules() add_company_to_session_defaults() - add_sla_hold_statuses_to_support_settings() frappe.db.commit() @@ -107,12 +106,3 @@ def add_company_to_session_defaults(): }) settings.save() -def add_sla_hold_statuses_to_support_settings(): - settings = frappe.get_single("Support Settings") - settings.append("pause_sla_on_status", { - "status": "Replied" - }) - settings.append("pause_sla_on_status", { - "status": "Hold" - }) - settings.save() diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 32a77739d0..e7e5bd312b 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -43,11 +43,11 @@ frappe.ui.form.on("Issue", { frappe.call({ 'method': 'frappe.client.get', args: { - doctype: 'Support Settings', - name: 'Support Settings' + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement }, callback: function(data) { - let statuses = data.message.pause_sla_on_status; + let statuses = data.message.pause_sla_on; const hold_statuses = []; $.each(statuses, (_i, entry) => { hold_statuses.push(entry.status); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 146eb5a249..4003047e81 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -78,9 +78,10 @@ class Issue(Document): self.handle_hold_time(status) def handle_hold_time(self, status): - if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + if self.service_level_agreement: # set response and resolution variance as None as the issue is on Hold for status as Replied - pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"]) + pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], + filters={"parent": self.service_level_agreement}) hold_statuses = [entry.status for entry in pause_sla_on] if self.status in hold_statuses and status not in hold_statuses: diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 93a6005cc2..a004843270 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues -from erpnext.setup.install import add_sla_hold_statuses_to_support_settings from frappe.utils import now_datetime, get_datetime import datetime from datetime import timedelta @@ -101,7 +100,6 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.user_resolution_time, 1200) def test_hold_time_on_replied(self): - add_sla_hold_statuses_to_support_settings() creation = datetime.datetime(2020, 3, 4, 4, 0) issue = make_issue(creation, index=1) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 7aeaae48ba..5346195a39 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -2,5 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Service Level Agreement', { + setup: function(frm) { + let allow_statuses = []; + const exclude_statuses = ['Open', 'Closed', 'Resolved']; -}); + frappe.model.with_doctype('Issue', () => { + let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; + statuses = statuses.split('\n'); + allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); + frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); + }); + } +}); \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index ede5f98eba..e65169c0d4 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -23,6 +23,8 @@ "active", "column_break_7", "end_date", + "section_break_18", + "pause_sla_on", "response_and_resolution_time_section", "priorities", "support_and_resolution_section_break", @@ -160,10 +162,25 @@ "read_only": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "hide_border": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "pause_sla_on", + "fieldtype": "Table", + "label": "Pause SLA On", + "options": "Pause SLA On Status", + "show_days": 1, + "show_seconds": 1 } ], "links": [], - "modified": "2020-05-26 16:02:59.859980", + "modified": "2020-06-05 17:51:15.050785", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 26740ed899..0746a9c73e 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -115,6 +115,11 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "resolution_time_period": "Hour", } ], + "pause_sla_on": [ + { + "status": "Replied" + } + ], "support_and_resolution": [ { "workday": "Monday", diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 33ddf0b079..78adca81ca 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -2,15 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on('Support Settings', { - setup: function(frm) { - let allow_statuses = []; - const exclude_statuses = ['Open', 'Closed', 'Resolved']; - - frappe.model.with_doctype('Issue', () => { - let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; - statuses = statuses.split('\n'); - allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); - frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); - }); + refresh: function(frm) { + // } }); diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index da4b607e2c..1c1b0c3517 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -10,7 +10,6 @@ "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", - "pause_sla_on_status", "portal_sb", "get_started_sections", "show_latest_forum_posts", @@ -128,20 +127,11 @@ "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" - }, - { - "depends_on": "eval:doc.track_service_level_agreement;", - "fieldname": "pause_sla_on_status", - "fieldtype": "Table", - "label": "Pause SLA On", - "options": "Pause SLA On Status", - "show_days": 1, - "show_seconds": 1 } ], "issingle": 1, "links": [], - "modified": "2020-06-05 16:35:13.905096", + "modified": "2020-06-05 17:56:17.491684", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From bb3b616a046b07996a47e1154e59d878aff45cd1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Jun 2020 23:17:51 +0530 Subject: [PATCH 31/57] fix: set valuesin db for handle hold time --- erpnext/support/doctype/issue/issue.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 4003047e81..a23fe0564f 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -83,20 +83,21 @@ class Issue(Document): pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], filters={"parent": self.service_level_agreement}) hold_statuses = [entry.status for entry in pause_sla_on] + update_values = {} if self.status in hold_statuses and status not in hold_statuses: - self.on_hold_since = frappe.flags.current_time or now_datetime() + update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() if not self.first_responded_on: - self.response_by = None - self.response_by_variance = None - self.resolution_by = None - self.resolution_by_variance = None + update_values['response_by'] = None + update_values['response_by_variance'] = 0 + update_values['resolution_by'] = None + update_values['resolution_by_variance'] = 0 # calculate hold time when status is changed from Replied to any other status if self.status not in hold_statuses and status in hold_statuses: hold_time = self.total_hold_time if self.total_hold_time else 0 now_time = frappe.flags.current_time or now_datetime() - self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) + update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) # re-calculate SLA variables after issue changes from Replied to Open # add hold time to SLA variables @@ -108,15 +109,17 @@ class Issue(Document): if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) - self.response_by = add_to_date(response_by, seconds=round(hold_time)) + update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time)) response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) - self.response_by_variance = response_by_variance + (hold_time // 3600) + update_values['response_by_variance'] = response_by_variance + (hold_time // 3600) resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) - self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time)) resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) - self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) - self.on_hold_since = None + update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600) + update_values['on_hold_since'] = None + + self.db_set(update_values) def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": From 1e49c6b68b6794794c4113319aadf211d848d7fe Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 10 Jun 2020 12:58:16 +0530 Subject: [PATCH 32/57] fix: update duration options --- erpnext/support/doctype/issue/issue.json | 22 +++++----------- .../service_level_agreement.json | 14 +++------- .../service_level_priority.json | 26 +++++++------------ 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 712b70c2dd..6525ab27d3 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -380,48 +380,38 @@ "fieldname": "avg_response_time", "fieldtype": "Duration", "label": "Average Response Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "resolution_time", "fieldtype": "Duration", "label": "Resolution Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "user_resolution_time", "fieldtype": "Duration", "label": "User Resolution Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "on_hold_since", "fieldtype": "Datetime", "hidden": 1, "label": "On Hold Since", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_hold_time", "fieldtype": "Duration", "label": "Total Hold Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-06-05 15:45:24.474425", + "modified": "2020-06-10 12:47:37.146914", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index e65169c0d4..939c199982 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -159,28 +159,22 @@ "fieldtype": "Link", "label": "Default Priority", "options": "Issue Priority", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break_18", "fieldtype": "Section Break", - "hide_border": 1, - "show_days": 1, - "show_seconds": 1 + "hide_border": 1 }, { "fieldname": "pause_sla_on", "fieldtype": "Table", "label": "Pause SLA On", - "options": "Pause SLA On Status", - "show_days": 1, - "show_seconds": 1 + "options": "Pause SLA On Status" } ], "links": [], - "modified": "2020-06-05 17:51:15.050785", + "modified": "2020-06-10 12:30:15.050785", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 3995d1e248..0a88c53bf0 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -20,34 +20,28 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Priority", - "options": "Issue Priority", - "show_days": 1, - "show_seconds": 1 + "options": "Issue Priority" }, { "fieldname": "sb_00", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, "fieldname": "resolution_time", "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Resolution Time" }, { "fieldname": "cb_00", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "cb_01", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "columns": 1, @@ -55,21 +49,21 @@ "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, - "label": "Default Priority", - "show_days": 1, - "show_seconds": 1 + "label": "Default Priority" }, { "columns": 2, "fieldname": "response_time", "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "First Response Time" } ], "istable": 1, "links": [], - "modified": "2020-06-05 13:08:26.428657", + "modified": "2020-06-10 12:45:47.545915", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From 596560c3c219d218e23eef575e18e7d0627bcf6f Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 16:39:03 +0530 Subject: [PATCH 33/57] fix: Don't prompt for Quality Inspection on Return Documents. --- erpnext/controllers/stock_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 90d293088b..2ce41590cf 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.is_return: + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 9c27d683ba542574c07ff45d9f72a9d158c4cb67 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 11 Jun 2020 17:03:53 +0530 Subject: [PATCH 34/57] fix: priority column width Co-authored-by: Himanshu --- .../service_level_priority/service_level_priority.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 0a88c53bf0..65d51694cc 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -15,7 +15,7 @@ ], "fields": [ { - "columns": 1, + "columns": 2, "fieldname": "priority", "fieldtype": "Link", "in_list_view": 1, @@ -73,4 +73,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From d7481f59ba6dec000b0fd5b970cf970e96211369 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 11 Jun 2020 17:41:05 +0530 Subject: [PATCH 35/57] fix: patch --- .../patches/v13_0/update_sla_enhancements.py | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 2356fb2679..3eb0411f82 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -45,38 +45,39 @@ def execute(): # copy Service Levels to Service Level Agreements sl = [entry.service_level for entry in sla_details] - service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) - for entry in service_levels: - sla = frappe.new_doc('Service Level Agreement') - sla.service_level = entry.service_level - sla.holiday_list = entry.holiday_list - sla.employee_group = entry.employee_group - sla.flags.ignore_validate = True - sla = sla.insert(ignore_mandatory=True) + if frappe.db.exists('DocType', 'Service Level'): + service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) + for entry in service_levels: + sla = frappe.new_doc('Service Level Agreement') + sla.service_level = entry.service_level + sla.holiday_list = entry.holiday_list + sla.employee_group = entry.employee_group + sla.flags.ignore_validate = True + sla = sla.insert(ignore_mandatory=True) - frappe.db.sql(""" - UPDATE - `tabService Day` - SET - parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' - WHERE - parent = %(old_parent)s - """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) + frappe.db.sql(""" + UPDATE + `tabService Day` + SET + parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' + WHERE + parent = %(old_parent)s + """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) - priority_list = priority_dict.get(entry.name) - if priority_list: - sla = frappe.get_doc('Service Level Agreement', sla.name) - for priority in priority_list: - row = sla.append('priorities', { - 'priority': priority.priority, - 'default_priority': priority.default_priority, - 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), - 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) - }) - row.db_update() - sla.db_update() + priority_list = priority_dict.get(entry.name) + if priority_list: + sla = frappe.get_doc('Service Level Agreement', sla.name) + for priority in priority_list: + row = sla.append('priorities', { + 'priority': priority.priority, + 'default_priority': priority.default_priority, + 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), + 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + }) + row.db_update() + sla.db_update() - frappe.delete_doc('DocType', 'Service Level') + frappe.delete_doc_if_exists('DocType', 'Service Level') def convert_to_seconds(value, unit): From 9df4532d14ea1355c2d6538e0e5f2f5ee5a16b4b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 11 Jun 2020 21:33:43 +0530 Subject: [PATCH 36/57] fix: Travis(develop) --- erpnext/accounts/dashboard_fixtures.py | 30 +++++++++--- .../sales_invoice/test_sales_invoice.py | 47 ------------------- erpnext/assets/dashboard_fixtures.py | 38 +++++++-------- erpnext/buying/dashboard_fixtures.py | 27 ++++++----- erpnext/manufacturing/dashboard_fixtures.py | 1 - erpnext/regional/united_states/setup.py | 4 +- erpnext/stock/dashboard_fixtures.py | 37 +++++++-------- 7 files changed, 73 insertions(+), 111 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 421c86dba0..f90ac26f35 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -5,7 +5,18 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year, get_account_name +from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError + +def _get_fiscal_year(date=None): + try: + fiscal_year = get_fiscal_year(date=nowdate()) + except FiscalYearError: + #if no fiscal year for current date then get default fiscal year + try: + fiscal_year = get_fiscal_year() + except FiscalYearError: + #if still no fiscal year found then no accounting data created, return + return None def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -18,10 +29,16 @@ def get_company_for_dashboards(): return None def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards() + "charts": get_charts(fiscal_year), + "number_cards": get_number_cards(fiscal_year) }) def get_dashboards(): @@ -46,10 +63,9 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(fiscal_year): company = frappe.get_doc("Company", get_company_for_dashboards()) bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) - fiscal_year = get_fiscal_year(date=nowdate()) default_cost_center = company.cost_center return [ @@ -190,8 +206,8 @@ def get_charts(): }, ] -def get_number_cards(): - fiscal_year = get_fiscal_year(date=nowdate()) +def get_number_cards(fiscal_year): + year_start_date = get_date_str(fiscal_year[1]) year_end_date = get_date_str(fiscal_year[2]) return [ diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c82a249843..6cdf9b57d1 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1745,53 +1745,6 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, "2019-01-30") - def test_deferred_error_email(self): - deferred_account = create_account(account_name="Deferred Revenue", - parent_account="Current Liabilities - _TC", company="_Test Company") - - item = create_item("_Test Item for Deferred Accounting") - item.enable_deferred_revenue = 1 - item.deferred_revenue_account = deferred_account - item.no_of_months = 12 - item.save() - - si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True) - si.items[0].enable_deferred_revenue = 1 - si.items[0].service_start_date = "2019-01-10" - si.items[0].service_end_date = "2019-03-15" - si.items[0].deferred_revenue_account = deferred_account - si.save() - si.submit() - - from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income - - acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - acc_settings.acc_frozen_upto = '2019-01-31' - acc_settings.save() - - pda = frappe.get_doc(dict( - doctype='Process Deferred Accounting', - posting_date=nowdate(), - start_date="2019-01-01", - end_date="2019-03-31", - type="Income", - company="_Test Company" - )) - - pda.insert() - pda.submit() - - email = frappe.db.sql(""" select name from `tabEmail Queue` - where message like %(txt)s """, { - 'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name) - }) - - self.assertTrue(email) - - acc_settings.load_from_db() - acc_settings.acc_frozen_upto = None - acc_settings.save() - def test_inter_company_transaction(self): if not frappe.db.exists("Customer", "_Test Internal Customer"): diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 9af45d16b6..d1e65e11d1 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -5,14 +5,23 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year - +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + year_start_date = get_date_str(fiscal_year[1]) + year_end_date = get_date_str(fiscal_year[2]) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(fiscal_year, year_start_date, year_end_date), + "number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date), }) def get_dashboards(): @@ -31,12 +40,7 @@ def get_dashboards(): ] }] -fiscal_year = get_fiscal_year(date=nowdate()) -year_start_date = get_date_str(fiscal_year[1]) -year_end_date = get_date_str(fiscal_year[2]) - - -def get_charts(): +def get_charts(fiscal_year, year_start_date, year_end_date): company = get_company_for_dashboards() return [ { @@ -134,7 +138,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(fiscal_year, year_start_date, year_end_date): return [ { "name": "Total Assets", @@ -172,14 +176,4 @@ def get_number_cards(): "filters_json": "[]", "doctype": "Number Card" } - ] - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None \ No newline at end of file + ] \ No newline at end of file diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 186bfb23af..172c936bd2 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -5,13 +5,24 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) def get_company_for_dashboards(): @@ -24,12 +35,6 @@ def get_company_for_dashboards(): return company_list[0].name return None -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Buying", @@ -48,7 +53,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "name": "Purchase Order Analysis", @@ -139,7 +144,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Annual Purchase", diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 4a17fd07fb..64e4bc6ed0 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -4,7 +4,6 @@ import frappe, erpnext, json from frappe import _ from frappe.utils import nowdate, get_first_day, get_last_day, add_months -from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py index 6d344025d2..cae28bee8b 100644 --- a/erpnext/regional/united_states/setup.py +++ b/erpnext/regional/united_states/setup.py @@ -9,14 +9,14 @@ def setup(company=None, patch=True): make_custom_fields() add_print_formats() -def make_custom_fields(): +def make_custom_fields(update=True): custom_fields = { 'Supplier': [ dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id', label='Is IRS 1099 reporting required for supplier?') ] } - create_custom_fields(custom_fields) + create_custom_fields(custom_fields, update=update) def add_print_formats(): frappe.reload_doc("regional", "print_format", "irs_1099_form") diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index 0f1fd128f0..7625b1ad28 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -5,31 +5,26 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Stock", @@ -48,7 +43,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "doctype": "Dashboard Chart", @@ -133,7 +128,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Total Active Items", From fd3d976e067464cb5f4ec347fb79538b68c13cac Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 12 Jun 2020 12:30:59 +0530 Subject: [PATCH 37/57] Update erpnext/controllers/stock_controller.py Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ce41590cf..2888c764ef 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - if not self.is_return: + if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 7e974c9e2c77eda1aebe48bd8356c9b36937a5c0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 12 Jun 2020 15:29:40 +0530 Subject: [PATCH 38/57] fix: Test Cases --- erpnext/accounts/dashboard_fixtures.py | 20 +++++++++++-------- .../accounting_dimension.py | 6 +++++- erpnext/assets/dashboard_fixtures.py | 8 ++++---- .../test_procurement_tracker.py | 19 ++++++++++-------- .../inpatient_record/test_inpatient_record.py | 5 ++++- erpnext/projects/doctype/task/test_task.py | 2 +- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index f90ac26f35..b2abffc79d 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -9,11 +9,15 @@ from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYear def _get_fiscal_year(date=None): try: - fiscal_year = get_fiscal_year(date=nowdate()) + fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True) + return fiscal_year + except FiscalYearError: #if no fiscal year for current date then get default fiscal year try: - fiscal_year = get_fiscal_year() + fiscal_year = get_fiscal_year(as_dict=True) + return fiscal_year + except FiscalYearError: #if still no fiscal year found then no accounting data created, return return None @@ -77,8 +81,8 @@ def get_charts(fiscal_year): "filters_json": json.dumps({ "company": company.name, "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "periodicity": "Monthly", "include_default_book_entries": 1 }), @@ -174,8 +178,8 @@ def get_charts(fiscal_year): "report_name": "Budget Variance Report", "filters_json": json.dumps({ "company": company.name, - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period": "Monthly", "budget_against": "Cost Center" }), @@ -208,8 +212,8 @@ def get_charts(fiscal_year): def get_number_cards(fiscal_year): - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + year_start_date = get_date_str(fiscal_year.get("year_start_date")) + year_end_date = get_date_str(fiscal_year.get("year_end_date")) return [ { "doctype": "Number Card", diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 894ec5bdec..8834385135 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -72,7 +72,11 @@ def make_dimension_in_accounting_doctypes(doc): if doctype == "Budget": add_dimension_to_budget_doctype(df, doc) else: - create_custom_field(doctype, df) + meta = frappe.get_meta(doctype, cached=False) + fieldnames = [d.fieldname for d in meta.get("fields")] + + if df['fieldname'] not in fieldnames: + create_custom_field(doctype, df) count += 1 diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index d1e65e11d1..7f3c1de406 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -15,8 +15,8 @@ def get_data(): if not fiscal_year: return frappe._dict() - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + year_start_date = get_date_str(fiscal_year.get('year_start_date')) + year_end_date = get_date_str(fiscal_year.get('year_end_date')) return frappe._dict({ "dashboards": get_dashboards(), @@ -59,8 +59,8 @@ def get_charts(fiscal_year, year_start_date, year_end_date): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period_start_date": year_start_date, "period_end_date": year_end_date, "date_based_on": "Purchase Date", diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec5..c7204a1f34 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index 4c2d3f692a..2bef5fb5bd 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -15,6 +15,7 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() # Schedule Admission ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record")) self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status")) @@ -26,7 +27,7 @@ class TestInpatientRecord(unittest.TestCase): self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) # Discharge - schedule_discharge(patient=patient) + schedule_discharge(frappe.as_json({'patient': patient})) self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name) @@ -44,8 +45,10 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) ip_record_new = create_inpatient(patient) + ip_record_new.expected_length_of_stay = 0 self.assertRaises(frappe.ValidationError, ip_record_new.save) service_unit = get_healthcare_service_unit() diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index bd3369447b..47a28fd111 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -64,7 +64,7 @@ class TestTask(unittest.TestCase): def assign(): from frappe.desk.form import assign_to assign_to.add({ - "assign_to": "test@example.com", + "assign_to": ["test@example.com"], "doctype": task.doctype, "name": task.name, "description": "Close this task" From 67600776747c0a0079e3fa33e7f66cc75790a8d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 22:40:23 +0530 Subject: [PATCH 39/57] fix: Billing address in for Purchase documents --- .../purchase_invoice/purchase_invoice.json | 616 +++++++++++++----- .../purchase_order/purchase_order.json | 508 +++++++++++---- erpnext/public/js/controllers/buying.js | 7 + .../purchase_receipt/purchase_receipt.json | 456 ++++++++++--- 4 files changed, 1215 insertions(+), 372 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 98ba5c72ae..829c34da67 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -42,6 +42,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -168,7 +170,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -180,7 +184,9 @@ "options": "ACC-PINV-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -192,7 +198,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -204,7 +212,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -212,21 +222,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -234,19 +250,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -256,13 +278,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -274,7 +300,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -283,6 +311,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -291,7 +321,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -303,44 +335,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -348,11 +394,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -360,13 +410,17 @@ "label": "Supplier Invoice Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -376,26 +430,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -403,51 +465,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -456,7 +534,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -465,18 +545,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -484,14 +570,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -500,11 +590,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -512,7 +606,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -522,11 +618,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -534,7 +634,9 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -545,25 +647,33 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -573,42 +683,56 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", "options": "Purchase Receipt Item Supplied", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -616,7 +740,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -626,18 +752,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -647,42 +779,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -691,7 +837,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -699,13 +847,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -714,13 +866,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -730,7 +886,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -740,7 +898,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -750,11 +910,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -764,7 +928,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -774,7 +940,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -782,14 +950,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -797,7 +969,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -805,28 +979,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -836,7 +1020,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -845,7 +1031,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -855,7 +1043,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -864,13 +1054,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -881,7 +1075,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -890,7 +1086,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -900,7 +1098,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -909,7 +1109,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -920,7 +1122,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -931,14 +1135,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -946,30 +1154,40 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", "fieldtype": "Date", "hidden": 1, - "label": "Clearance Date" + "label": "Clearance Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -978,7 +1196,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -987,7 +1207,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -995,7 +1217,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1003,7 +1227,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1012,11 +1238,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1024,7 +1254,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1032,7 +1264,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1042,13 +1276,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1056,7 +1294,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1066,20 +1306,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1087,7 +1333,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1095,25 +1343,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1121,7 +1377,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1129,11 +1387,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1145,14 +1407,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1161,7 +1427,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1172,7 +1440,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1182,7 +1452,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1192,7 +1464,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1202,11 +1476,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1215,14 +1493,18 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", "label": "Inter Company Invoice Reference", "options": "Sales Invoice", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1231,14 +1513,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1247,7 +1533,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1256,11 +1544,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1269,24 +1561,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1294,7 +1594,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1302,14 +1604,32 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-06-13 22:26:30.800199", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a4f60fbba5..7145fea5c5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -38,6 +38,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -135,7 +137,9 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -145,7 +149,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -157,7 +163,9 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -170,14 +178,18 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests" + "label": "Get Items from Open Material Requests", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -186,7 +198,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -198,13 +212,17 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -216,27 +234,35 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By" + "label": "Required By", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No" + "label": "Order Confirmation No", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date" + "label": "Order Confirmation Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -248,19 +274,25 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship" + "label": "Drop Ship", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -268,31 +300,41 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_email", @@ -300,46 +342,60 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -347,32 +403,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -382,7 +448,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -392,18 +460,24 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -411,14 +485,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -427,11 +505,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -439,11 +521,15 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -452,25 +538,33 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -480,26 +574,34 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -509,17 +611,23 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -527,7 +635,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -538,18 +648,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -559,20 +675,26 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -581,22 +703,30 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_52", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -604,13 +734,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -619,13 +753,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -635,7 +773,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -645,7 +785,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -656,11 +798,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -670,7 +816,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -680,7 +828,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -688,14 +838,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -703,7 +857,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -711,28 +867,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -743,7 +909,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -752,7 +920,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -762,7 +932,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -772,12 +944,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -787,7 +963,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -796,20 +974,26 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -818,7 +1002,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advance_paid", @@ -827,19 +1013,25 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -847,7 +1039,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -856,7 +1050,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -865,21 +1061,27 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -894,7 +1096,9 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "ref_sq", @@ -905,7 +1109,9 @@ "oldfieldname": "ref_sq", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -915,18 +1121,24 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -936,7 +1148,9 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -946,7 +1160,9 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -956,6 +1172,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -966,7 +1184,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -978,11 +1198,15 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -990,19 +1214,25 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1010,7 +1240,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1018,11 +1250,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1031,44 +1267,72 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category" + "options": "Tax Category", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking" + "label": "Tracking", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_75", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:13:14.186280", + "modified": "2020-06-13 22:25:47.333850", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 9c56189476..a4cc68b3e2 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); + me.frm.set_query('billing_address', erpnext.queries.company_address_query); + if(this.frm.fields_dict.supplier) { this.frm.set_query("supplier", function() { return{ query: "erpnext.controllers.queries.supplier_query" }}); @@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ "shipping_address_display", true); }, + billing_address: function() { + erpnext.utils.get_address_display(this.frm, "billing_address", + "billing_address_display", true); + }, + tc_name: function() { this.get_terms(); }, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 467a206d18..44d5f69028 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -32,6 +32,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -130,13 +132,17 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -147,7 +153,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -159,7 +167,9 @@ "options": "MAT-PRE-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -174,6 +184,8 @@ "print_width": "150px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -184,18 +196,24 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_delivery_note", "fieldtype": "Data", - "label": "Supplier Delivery Note" + "label": "Supplier Delivery Note", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -210,6 +228,8 @@ "print_width": "100px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -223,6 +243,8 @@ "print_hide": 1, "print_width": "100px", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -231,7 +253,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -245,6 +269,8 @@ "print_width": "150px", "remember_last_selected_value": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -254,7 +280,9 @@ "label": "Is Return", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_return", @@ -264,46 +292,60 @@ "no_copy": 1, "options": "Purchase Receipt", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -311,32 +353,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -346,7 +398,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which supplier's currency is converted to company's base currency", @@ -357,13 +411,17 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -371,7 +429,9 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -380,7 +440,9 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -388,7 +450,9 @@ "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -397,11 +461,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Accepted Warehouse' in each row of the items table.", @@ -409,7 +477,9 @@ "fieldtype": "Link", "label": "Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Rejected Warehouse' in each row of the items table.", @@ -420,11 +490,15 @@ "oldfieldname": "rejected_warehouse", "oldfieldtype": "Link", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -434,7 +508,9 @@ "oldfieldname": "is_subcontracted", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -447,13 +523,17 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -463,20 +543,26 @@ "oldfieldname": "purchase_receipt_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", @@ -485,7 +571,9 @@ "label": "Get Current Stock", "oldfieldtype": "Button", "options": "get_current_stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -496,7 +584,9 @@ "oldfieldtype": "Section Break", "options": "fa fa-table", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -507,18 +597,24 @@ "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break0", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -526,7 +622,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -539,18 +637,24 @@ "print_width": "150px", "read_only": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { "fieldname": "column_break_27", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -560,42 +664,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_col", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", - "options": "Shipping Rule" + "options": "Shipping Rule", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -604,7 +722,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -612,13 +732,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -627,13 +751,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -643,7 +771,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -653,7 +783,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -663,12 +795,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break3", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -679,7 +815,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -689,7 +827,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -697,14 +837,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -712,7 +856,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -720,28 +866,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_44", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -751,7 +907,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -760,7 +918,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -769,7 +929,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -779,11 +941,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -793,7 +959,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -802,7 +970,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -812,7 +982,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -821,13 +993,17 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -836,7 +1012,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -845,14 +1023,18 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -861,7 +1043,9 @@ "label": "Bill No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -870,7 +1054,9 @@ "label": "Bill Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -878,7 +1064,9 @@ "fieldtype": "Section Break", "label": "More Information", "oldfieldtype": "Section Break", - "options": "fa fa-file-text" + "options": "fa fa-file-text", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -895,6 +1083,8 @@ "read_only": 1, "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -910,6 +1100,8 @@ "print_hide": 1, "print_width": "150px", "read_only": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -919,7 +1111,9 @@ "label": "Range", "oldfieldname": "range", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", @@ -927,6 +1121,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -935,12 +1131,16 @@ "label": "% Amount Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -949,13 +1149,17 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -963,7 +1167,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -975,13 +1181,17 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -989,11 +1199,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_details", @@ -1004,6 +1218,8 @@ "options": "
Other Details
", "print_hide": 1, "print_width": "30%", + "show_days": 1, + "show_seconds": 1, "width": "30%" }, { @@ -1011,13 +1227,17 @@ "fieldtype": "Small Text", "label": "Instructions", "oldfieldname": "instructions", - "oldfieldtype": "Text" + "oldfieldtype": "Text", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1025,19 +1245,25 @@ "fieldname": "transporter_info", "fieldtype": "Section Break", "label": "Transporter Details", - "options": "fa fa-truck" + "options": "fa fa-truck", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transporter_name", "fieldtype": "Data", "label": "Transporter Name", "oldfieldname": "transporter_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1048,6 +1274,8 @@ "oldfieldname": "lr_no", "oldfieldtype": "Data", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1058,6 +1286,8 @@ "oldfieldname": "lr_date", "oldfieldtype": "Date", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1066,26 +1296,48 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", "options": "Delivery Note", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-04-18 18:02:18.020763", + "modified": "2020-06-13 22:26:03.600092", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", @@ -1152,4 +1404,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file From 65e7a2e7a6696c5493d4731149078c2e16816145 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 13 Jun 2020 13:11:43 +0530 Subject: [PATCH 40/57] opportunity-dashboard-fix --- .../opportunity/opportunity_dashboard.py | 6 +- .../social_media_post/social_media_post.json | 76 ++++++++++++++----- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd2..68f0104fd6 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 2601c14b4d..9c74aaad5f 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -30,24 +30,32 @@ "fieldname": "text", "fieldtype": "Small Text", "label": "Tweet", - "mandatory_depends_on": "eval:doc.twitter ==1" + "mandatory_depends_on": "eval:doc.twitter ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "image", "fieldtype": "Attach Image", - "label": "Image" + "label": "Image", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "twitter", "fieldtype": "Check", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "linkedin", "fieldtype": "Check", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -56,13 +64,17 @@ "no_copy": 1, "options": "Social Media Post", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.twitter ==1", "fieldname": "content", "fieldtype": "Section Break", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -70,7 +82,9 @@ "fieldtype": "Select", "label": "Post Status", "options": "\nScheduled\nPosted\nError", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -78,7 +92,9 @@ "fieldtype": "Data", "hidden": 1, "label": "Twitter Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -86,68 +102,89 @@ "fieldtype": "Data", "hidden": 1, "label": "LinkedIn Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign_name", "fieldtype": "Link", "in_list_view": 1, "label": "Campaign", - "options": "Campaign" + "options": "Campaign", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_6", "fieldtype": "Column Break", - "label": "Share On" + "label": "Share On", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tweet_preview", - "fieldtype": "HTML" + "fieldtype": "HTML", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "depends_on": "eval:doc.linkedin==1", "fieldname": "linkedin_section", "fieldtype": "Section Break", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "attachments_section", "fieldtype": "Section Break", - "label": "Attachments" + "label": "Attachments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "linkedin_post", "fieldtype": "Text", "label": "Post", - "mandatory_depends_on": "eval:doc.linkedin ==1" + "mandatory_depends_on": "eval:doc.linkedin ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "scheduled_time", "fieldtype": "Datetime", "label": "Scheduled Time", - "read_only_depends_on": "eval:doc.post_status == \"Posted\"" + "read_only_depends_on": "eval:doc.post_status == \"Posted\"", + "show_days": 1, + "show_seconds": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 15:10:04.953713", + "modified": "2020-06-13 20:53:47.670536", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", "owner": "Administrator", "permissions": [ { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -157,6 +194,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], From 234b5f9a8c869e9fb49fbd16b099153f9972e5ff Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 14 Jun 2020 10:34:58 +0530 Subject: [PATCH 41/57] sm-post-permission-fix --- .../social_media_post/social_media_post.json | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 9c74aaad5f..0a00dca280 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -177,7 +177,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-06-13 20:53:47.670536", + "modified": "2020-06-14 10:31:33.961381", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", @@ -196,6 +196,34 @@ "share": 1, "submit": 1, "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "submit": 1, + "write": 1 } ], "sort_field": "modified", From d6f9a51cbb2724dd6a3675c8c85b5c40317255cb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jun 2020 14:06:12 +0530 Subject: [PATCH 42/57] fix: item none not found while making sales invoice using opening invoice creation tool --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8b5d4d110c..5e8279bb08 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -582,14 +582,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self): From a2cf79da0f5528e7e5ba38511b8f29075786ad1c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 15 Jun 2020 12:28:31 +0530 Subject: [PATCH 43/57] fix: allow to enter Releaving date if status = Left --- erpnext/hr/doctype/employee/employee.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index f575765f69..2c2b2f6a17 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -205,7 +205,7 @@ "label": "Status", "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nActive\nLeft", + "options": "Active\nLeft", "reqd": 1, "search_index": 1 }, @@ -667,6 +667,7 @@ "oldfieldtype": "Date" }, { + "depends_on": "eval:doc.status == \"Left\"", "fieldname": "relieving_date", "fieldtype": "Date", "label": "Relieving Date", @@ -803,7 +804,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2020-05-05 18:51:03.152503", + "modified": "2020-06-15 12:26:30.003741", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 45b6fed029a2c22f064e372a07b11f005f52ee30 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 15 Jun 2020 12:44:55 +0530 Subject: [PATCH 44/57] fix: added some standard filters in expense cliam --- erpnext/hr/doctype/expense_claim/expense_claim.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 96baaab595..fa28470af8 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -66,6 +66,7 @@ "fieldname": "employee", "fieldtype": "Link", "in_global_search": 1, + "in_standard_filter": 1, "label": "From Employee", "oldfieldname": "employee", "oldfieldtype": "Link", @@ -164,6 +165,7 @@ "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", + "in_standard_filter": 1, "label": "Posting Date", "oldfieldname": "posting_date", "oldfieldtype": "Date", @@ -236,6 +238,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -368,7 +371,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2019-12-14 23:52:05.388458", + "modified": "2020-06-15 12:43:04.099803", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", From 2957a631db9c4ed3c9d417aaabedf8e7b4722cfb Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:11:16 +0200 Subject: [PATCH 45/57] fix: Validation of Purchase Order against Material Request missing (#22192) Validation of Purchase Order and Purchase Order Item against the linked Material Request Item was missing, so it was possible to exchange items in the purcahse order against others and still fullfill the material request. Co-authored-by: Marica --- erpnext/buying/doctype/purchase_order/purchase_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f62df20ae1..c7efb8a1a1 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController): "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True + }, + "Material Request": { + "ref_dn_field": "material_request", + "compare_fields": [["company", "="]], + }, + "Material Request Item": { + "ref_dn_field": "material_request_item", + "compare_fields": [["project", "="], ["item_code", "="]], + "is_child_table": True } }) From 30487bd85430b686c957943982eddbdfd7b95a4b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 15 Jun 2020 13:49:58 +0530 Subject: [PATCH 46/57] fix: codacy issues --- erpnext/support/doctype/issue/issue.py | 2 +- .../doctype/service_level_agreement/service_level_agreement.py | 1 - .../service_level_agreement/test_service_level_agreement.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index a23fe0564f..883e603fd3 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff +from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 530230e1e8..c692315706 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.utils import getdate, get_weekdays -from datetime import datetime class ServiceLevelAgreement(Document): diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 0746a9c73e..07ef368cbe 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -236,7 +236,6 @@ def create_service_level_agreements_for_issues(): def make_holiday_list(): holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") if not holiday_list: - now = frappe.utils.now_datetime() holiday_list = frappe.get_doc({ "doctype": "Holiday List", "holiday_list_name": "__Test Holiday List", From 096792791ba459ef83ae8e1e9ecf5cec6488c9e6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 15 Jun 2020 14:07:06 +0530 Subject: [PATCH 47/57] fix(patch): reload child tables --- erpnext/patches/v13_0/update_sla_enhancements.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 3eb0411f82..c156ba9577 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -15,7 +15,9 @@ def execute(): }) frappe.reload_doc('support', 'doctype', 'service_level_agreement') + frappe.reload_doc('support', 'doctype', 'pause_sla_on_status') frappe.reload_doc('support', 'doctype', 'service_level_priority') + frappe.reload_doc('support', 'doctype', 'service_day') for entry in sla_details: values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group']) From 8a0058787ed054f2951815b2c297df9212675706 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 14:40:39 +0530 Subject: [PATCH 48/57] fix: Wrong key sent to gte_valuation_rate --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 608e537e1b..89b48f07ee 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -349,7 +349,7 @@ class BuyingController(StockController): }) if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) From c2496a36007dc4262275af5c0257fd98a9a6db7b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 15:18:49 +0530 Subject: [PATCH 49/57] fix: Asset maintenance test --- erpnext/assets/doctype/asset_maintenance/asset_maintenance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index d6adde6a37..1869a29c8d 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -41,7 +41,7 @@ def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, nex team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', - 'assign_to' : team_member, + 'assign_to' : [team_member], 'name' : asset_maintenance_name, 'description' : maintenance_task, 'date' : next_due_date From 2112834743388b08981cd14177908a9bf7d36a1e Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 16 Jun 2020 00:20:57 +0530 Subject: [PATCH 50/57] fix: Handle unavailable Variants in Website (#22195) * fix: Handle unavailable Variants in Website * fix: Fetch cart setting from argument --- erpnext/portal/product_configurator/utils.py | 3 +++ erpnext/shopping_cart/cart.py | 4 +++- erpnext/templates/generators/item/item_configure.js | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 0993e69e04..6b6b8c579b 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,4 +1,5 @@ import frappe +from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -243,6 +244,8 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) + return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 7096c17fb1..5bd30ab2e8 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -78,8 +78,10 @@ def place_order(): if is_stock_item: item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + if not cint(item_stock.in_stock): + throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: - throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) + throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) sales_order.flags.ignore_permissions = True sales_order.insert() diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 5fd901169f..163c955c56 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -193,14 +193,17 @@ class ItemConfigure { filtered_items_count === 1 ? filtered_items[0] : ''; + // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. + const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + const add_to_cart = `${__('Add to cart')}`; + const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + const item_add_to_cart = one_item ? ` `: ''; From fa6e4f62e24c9d0f1c498131e4f198531bc14cff Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 15 Jun 2020 22:21:04 +0530 Subject: [PATCH 51/57] fix: travis for develop --- ...ve_due_advance_amount_to_pending_amount.py | 4 +- .../doctype/timesheet/test_timesheet.py | 38 +++++++++++++------ erpnext/support/doctype/issue/test_issue.py | 6 +-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py index f1ffaf9d2d..6013eaa29c 100644 --- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -6,4 +6,6 @@ import frappe def execute(): ''' Move from due_advance_amount to pending_amount ''' - frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 32f0428fcd..cbc624c064 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -13,7 +13,7 @@ from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.hr.doctype.salary_structure.test_salary_structure \ import make_salary_structure, create_salary_structure_assignment - +from erpnext.hr.doctype.employee.test_employee import make_employee class TestTimesheet(unittest.TestCase): def setUp(self): @@ -25,8 +25,10 @@ class TestTimesheet(unittest.TestCase): def test_timesheet_billing_amount(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=1) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 2) @@ -35,8 +37,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 100) def test_timesheet_billing_amount_not_billable(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=0) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=0) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 0) @@ -45,8 +49,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 0) def test_salary_slip_from_timesheet(self): - salary_structure = make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate = True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + salary_structure = make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate = True, billable=1) salary_slip = make_salary_slip(timesheet.name) salary_slip.submit() @@ -65,7 +71,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.status, 'Submitted') def test_sales_invoice_from_timesheet(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1) sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer') sales_invoice.due_date = nowdate() sales_invoice.submit() @@ -80,7 +88,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(item.rate, 50.00) def test_timesheet_billing_based_on_project(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1, project = '_Test Project', company='_Test Company') + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company') sales_invoice = create_sales_invoice(do_not_save=True) sales_invoice.project = '_Test Project' sales_invoice.submit() @@ -90,6 +100,8 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name) def test_timesheet_time_overlap(self): + emp = make_employee("test_employee_6@salary.com") + settings = frappe.get_single('Projects Settings') initial_setting = settings.ignore_employee_time_overlap settings.ignore_employee_time_overlap = 0 @@ -97,7 +109,7 @@ class TestTimesheet(unittest.TestCase): update_activity_type("_Test Activity Type") timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.append( 'time_logs', { @@ -129,12 +141,14 @@ class TestTimesheet(unittest.TestCase): settings.save() def test_timesheet_std_working_hours(self): + emp = make_employee("test_employee_6@salary.com") + company = frappe.get_doc('Company', "_Test Company") company.standard_working_hours = 8 company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', @@ -156,7 +170,7 @@ class TestTimesheet(unittest.TestCase): company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index a004843270..fb8ceb53b2 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues -from frappe.utils import now_datetime, get_datetime +from frappe.utils import now_datetime, get_datetime, flt import datetime from datetime import timedelta @@ -120,7 +120,7 @@ class TestIssue(unittest.TestCase): create_communication(issue.name, "test@example.com", "Received", creation) issue.reload() - self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(flt(issue.total_hold_time, 2), 2700) self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45)) creation = datetime.datetime(2020, 3, 4, 5, 5) @@ -132,7 +132,7 @@ class TestIssue(unittest.TestCase): issue.save() issue.reload() - self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(flt(issue.total_hold_time, 2), 2700) def make_issue(creation=None, customer=None, index=0): From 58831ecc7d18f477b08e767fbc505fa11a00a9e6 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 16 Jun 2020 19:23:52 +0530 Subject: [PATCH 52/57] fix(HR): wrong shortcut in desk page (#22269) --- erpnext/hr/desk_page/hr/hr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 1c24444fdd..12548d48a7 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -93,7 +93,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-06-10 12:41:41.695669", + "modified": "2020-06-16 19:20:50.976045", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -126,7 +126,7 @@ }, { "label": "Salary Structure", - "link_to": "Payroll Entry", + "link_to": "Salary Structure", "type": "DocType" }, { From c159556c24dd90f18d2b5dafacd94abfacfff4d8 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:34:58 +0530 Subject: [PATCH 53/57] fix: typo for language in Terms description (#22270) --- .../doctype/terms_and_conditions/terms_and_conditions.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 aba6a791a4..28d1d16a05 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -49,7 +50,7 @@ "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", "label": "Terms and Conditions Help", - "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

" + "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

" }, { "fieldname": "applicable_modules_section", @@ -81,7 +82,8 @@ ], "icon": "icon-legal", "idx": 1, - "modified": "2019-07-04 13:31:30.393425", + "links": [], + "modified": "2020-06-16 22:54:38.094844", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", From 8d856659afec9bb6bfb17ad28a9697ffc93c0a39 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:37:41 +0530 Subject: [PATCH 54/57] refactor: hide company currency fields in the routing (#22267) (cherry picked from commit fd3ff6be18b0bd5accc88aea22e3adfe48e62aff) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + .../doctype/bom_operation/bom_operation.json | 8 +++++--- erpnext/manufacturing/doctype/routing/routing.js | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2543eec53e..7d31a1cd15 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -119,6 +119,7 @@ class BOM(WebsiteGenerator): "description": d.description, "time_in_mins": d.time_in_mins, "batch_size": d.batch_size, + "operating_cost": d.operating_cost, "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783..0350e2cb37 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b..d7589fa390 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } From 0a3c34de01fe4a08db9540c3de86debc8b11d3dc Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 17 Jun 2020 10:53:13 +0530 Subject: [PATCH 55/57] address label chages (#22137) Co-authored-by: Marica --- erpnext/patches.txt | 3 ++- .../v12_0/update_address_template_for_india.py | 12 ++++++++++++ .../regional/address_template/templates/india.html | 2 +- erpnext/shopping_cart/cart.py | 4 ++-- erpnext/templates/includes/cart/address_card.html | 2 +- erpnext/templates/includes/cart/cart_address.html | 2 +- 6 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 erpnext/patches/v12_0/update_address_template_for_india.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index db9610b62b..279c453e35 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -697,4 +697,5 @@ execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions -erpnext.patches.v13_0.update_sla_enhancements \ No newline at end of file +erpnext.patches.v13_0.update_sla_enhancements +erpnext.patches.v12_0.update_address_template_for_india diff --git a/erpnext/patches/v12_0/update_address_template_for_india.py b/erpnext/patches/v12_0/update_address_template_for_india.py new file mode 100644 index 0000000000..0d582da4b5 --- /dev/null +++ b/erpnext/patches/v12_0/update_address_template_for_india.py @@ -0,0 +1,12 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.address_template.setup import set_up_address_templates + +def execute(): + if frappe.db.get_value('Company', {'country': 'India'}, 'name'): + address_template = frappe.db.get_value('Address Template', 'India', 'template') + if not address_template or "gstin" not in address_template: + set_up_address_templates(default_country='India') diff --git a/erpnext/regional/address_template/templates/india.html b/erpnext/regional/address_template/templates/india.html index ffb9d0547e..5d2329efff 100644 --- a/erpnext/regional/address_template/templates/india.html +++ b/erpnext/regional/address_template/templates/india.html @@ -1,7 +1,7 @@ {{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}
{% endif -%}{{ city }}
{% if gst_state %}{{ gst_state }}{% endif -%} {% if gst_state_number %}, State Code: {{ gst_state_number }}
{% endif -%} -{% if pincode %}PIN: {{ pincode }}
{% endif -%} +{% if pincode %}Postal Code: {{ pincode }}
{% endif -%} {{ country }}
{% if phone %}Phone: {{ phone }}
{% endif -%} {% if fax %}Fax: {{ fax }}
{% endif -%} diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 5bd30ab2e8..a7e8388be9 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -42,9 +42,9 @@ def get_cart_quotation(doc=None): return { "doc": decorate_quotation_doc(doc), - "shipping_addresses": [{"name": address.name, "display": address.display} + "shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Shipping"], - "billing_addresses": [{"name": address.name, "display": address.display} + "billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Billing"], "shipping_rules": get_applicable_shipping_rules(party), "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html index c91723e91e..646210e65f 100644 --- a/erpnext/templates/includes/cart/address_card.html +++ b/erpnext/templates/includes/cart/address_card.html @@ -3,7 +3,7 @@
-
{{ address.name }}
+
{{ address.title }}

{{ address.display }}

diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index 60de3af17b..aa25c885fe 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -109,7 +109,7 @@ frappe.ready(() => { reqd: 1 }, { - label: __('Pin Code'), + label: __('Postal Code'), fieldname: 'pincode', fieldtype: 'Data' }, From c6592c880a2873adcb7bf78883e9c3f20b25e59b Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 17 Jun 2020 12:38:31 +0530 Subject: [PATCH 56/57] fix: Student Admission (#22255) * Student Admission fix * adding check for application * adding check for application * updating error message * added date_diff for date comparision --- .../student_admission/student_admission.json | 477 ++++-------------- .../templates/student_admission.html | 13 +- .../test_student_admission.js | 4 +- .../student_admission_program.json | 294 +++-------- .../student_applicant/student_applicant.py | 21 +- .../student_applicant/student_applicant.json | 406 ++++++++------- .../generators/student_admission.html | 6 +- 7 files changed, 416 insertions(+), 805 deletions(-) diff --git a/erpnext/education/doctype/student_admission/student_admission.json b/erpnext/education/doctype/student_admission/student_admission.json index b3c10d4331..1096888d4d 100644 --- a/erpnext/education/doctype/student_admission/student_admission.json +++ b/erpnext/education/doctype/student_admission/student_admission.json @@ -1,398 +1,119 @@ { - "allow_copy": 0, - "allow_guest_to_view": 1, - "allow_import": 0, - "allow_rename": 1, - "autoname": "", - "beta": 0, - "creation": "2016-09-13 03:05:27.154713", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_guest_to_view": 1, + "allow_rename": 1, + "creation": "2016-09-13 03:05:27.154713", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "route", + "column_break_3", + "academic_year", + "admission_start_date", + "admission_end_date", + "published", + "enable_admission_application", + "section_break_5", + "program_details", + "introduction" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "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": "Title", - "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": "title", + "fieldtype": "Data", + "label": "Title" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "route", - "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": "Route", - "length": 0, - "no_copy": 1, - "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, + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "no_copy": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_form_route", - "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": "Application Form Route", - "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": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "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": "academic_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Academic Year", + "no_copy": 1, + "options": "Academic Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_year", - "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": "Academic Year", - "length": 0, - "no_copy": 1, - "options": "Academic Year", - "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": "admission_start_date", + "fieldtype": "Date", + "label": "Admission Start Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_start_date", - "fieldtype": "Date", - "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": "Admission Start Date", - "length": 0, - "no_copy": 1, - "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": "admission_end_date", + "fieldtype": "Date", + "label": "Admission End Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_end_date", - "fieldtype": "Date", - "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": "Admission End Date", - "length": 0, - "no_copy": 1, - "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": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Publish on website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "published", - "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": "Publish on website", - "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": "section_break_5", + "fieldtype": "Section Break", + "label": "Eligibility and Details" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "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, - "label": "Eligibility and Details", - "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": "program_details", + "fieldtype": "Table", + "label": "Eligibility and Details", + "options": "Student Admission Program" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_details", - "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": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "options": "Student Admission Program", - "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": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "introduction", - "fieldtype": "Text Editor", - "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": "Introduction", - "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": "0", + "fieldname": "enable_admission_application", + "fieldtype": "Check", + "label": "Enable Admission Application" } - ], - "has_web_view": 1, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_published_field": "published", - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 18:57:34.570376", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 1, + "is_published_field": "published", + "links": [], + "modified": "2020-06-15 20:18:38.591626", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "route": "admissions", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 + ], + "restrict_to_domain": "Education", + "route": "admissions", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title" } \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html index 25afaca84d..e5a9ead31e 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission.html @@ -43,8 +43,8 @@ Program/Std. - Minumum Age(DOB) - Maximum Age(DOB) + Minumum Age + Maximum Age Application Fee @@ -52,8 +52,8 @@ {% for row in program_details %} {{ row.program }} - {{ row.minimum_age }} - {{ row.maximum_age }} + {{ row.min_age }} + {{ row.max_age }} {{ row.application_fee }} {% endfor %} @@ -61,12 +61,11 @@
{% endif %} - - {%- if application_form_route -%} + {%- if doc.enable_admission_application -%}

+ href='/student-applicant?new=1&student_admission={{doc.name}}'> {{ _("Apply Now") }}

{% endif %} diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js index ed794b2482..3a0bb0b2f2 100644 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ b/erpnext/education/doctype/student_admission/test_student_admission.js @@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) { {admission_start_date: '2016-04-20'}, {admission_end_date: '2016-05-31'}, {title: '2016-17 Admissions'}, - {application_form_route: 'student-applicant'}, + {enable_admission_application: 1}, {introduction: 'Test intro'}, {program_details: [ [ @@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) { assert.ok(cur_frm.doc.admission_start_date == '2016-04-20'); assert.ok(cur_frm.doc.admission_end_date == '2016-05-31'); assert.ok(cur_frm.doc.title == '2016-17 Admissions'); - assert.ok(cur_frm.doc.application_form_route == 'student-applicant'); + assert.ok(cur_frm.doc.enable_admission_application == 1); assert.ok(cur_frm.doc.introduction == 'Test intro'); assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected'); assert.ok(cur_frm.doc.program_details[0].application_fee == 1000); diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.json b/erpnext/education/doctype/student_admission_program/student_admission_program.json index 97b1bba421..e9f041e101 100644 --- a/erpnext/education/doctype/student_admission_program/student_admission_program.json +++ b/erpnext/education/doctype/student_admission_program/student_admission_program.json @@ -1,237 +1,77 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2017-09-15 12:59:43.207923", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-09-15 12:59:43.207923", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "program", + "min_age", + "max_age", + "column_break_4", + "application_fee", + "applicant_naming_series" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "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": 0, - "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "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 - }, + "fieldname": "program", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Program", + "options": "Program", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "minimum_age", - "fieldtype": "Date", - "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": "Minimum Age", - "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 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maximum_age", - "fieldtype": "Date", - "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": "Maximum Age", - "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 - }, + "fieldname": "application_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Application Fee", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "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, - "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 - }, + "fieldname": "applicant_naming_series", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Naming Series (for Student Applicant)", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_fee", - "fieldtype": "Currency", - "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": "Application Fee", - "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 - }, + "fieldname": "min_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Minimum Age", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "applicant_naming_series", - "fieldtype": "Data", - "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": "Naming Series (for Student Applicant)", - "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 + "fieldname": "max_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Age", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "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": "2018-11-04 03:37:17.408427", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission Program", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-06-10 23:06:30.037404", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission Program", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py index ab947807dd..211348201e 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate +from frappe.utils import getdate, add_years, nowdate, date_diff class StudentApplicant(Document): def autoname(self): @@ -31,6 +31,7 @@ class StudentApplicant(Document): def validate(self): self.validate_dates() self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + if self.student_admission and self.program and self.date_of_birth: self.validation_from_student_admission() @@ -48,16 +49,16 @@ class StudentApplicant(Document): frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) def validation_from_student_admission(self): + student_admission = get_student_admission_data(self.student_admission, self.program) - # different validation for minimum and maximum age so that either min/max can also work independently. - if student_admission and student_admission.minimum_age and \ - getdate(student_admission.minimum_age) < getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.min_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) - if student_admission and student_admission.maximum_age and \ - getdate(student_admission.maximum_age) > getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.max_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) def on_payment_authorized(self, *args, **kwargs): @@ -65,10 +66,12 @@ class StudentApplicant(Document): def get_student_admission_data(student_admission, program): + student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date, - sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series + sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series from `tabStudent Admission` sa, `tabStudent Admission Program` sap where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1) + if student_admission: return student_admission[0] else: diff --git a/erpnext/education/web_form/student_applicant/student_applicant.json b/erpnext/education/web_form/student_applicant/student_applicant.json index b1ad754c32..1810f07a05 100644 --- a/erpnext/education/web_form/student_applicant/student_applicant.json +++ b/erpnext/education/web_form/student_applicant/student_applicant.json @@ -1,200 +1,248 @@ { - "accept_payment": 0, - "allow_comments": 0, - "allow_delete": 0, - "allow_edit": 1, - "allow_incomplete": 0, - "allow_multiple": 1, - "allow_print": 0, - "amount": 0.0, - "amount_based_on_field": 0, - "creation": "2016-09-22 13:10:10.792735", - "doc_type": "Student Applicant", - "docstatus": 0, - "doctype": "Web Form", - "idx": 0, - "is_standard": 1, - "login_required": 1, - "max_attachment_size": 0, - "modified": "2017-02-21 05:44:46.022738", - "modified_by": "Administrator", - "module": "Education", - "name": "student-applicant", - "owner": "Administrator", - "payment_button_label": "Buy Now", - "published": 1, - "route": "student-applicant", - "show_sidebar": 1, - "sidebar_items": [], - "success_url": "/student-applicant", - "title": "Student Applicant", + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2016-09-22 13:10:10.792735", + "doc_type": "Student Applicant", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2020-06-11 22:53:45.875310", + "modified_by": "Administrator", + "module": "Education", + "name": "student-applicant", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "student-applicant", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/student-applicant", + "title": "Student Applicant", "web_form_fields": [ { - "fieldname": "first_name", - "fieldtype": "Data", - "hidden": 0, - "label": "First Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "middle_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Middle Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "last_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Last Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "image", - "fieldtype": "Data", - "hidden": 0, - "label": "Image", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "image", + "fieldtype": "Data", + "hidden": 0, + "label": "Image", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "label": "Program", - "max_length": 0, - "max_value": 0, - "options": "Program", - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "program", + "fieldtype": "Link", + "hidden": 0, + "label": "Program", + "max_length": 0, + "max_value": 0, + "options": "Program", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "label": "Academic Year", - "max_length": 0, - "max_value": 0, - "options": "Academic Year", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "academic_year", + "fieldtype": "Link", + "hidden": 0, + "label": "Academic Year", + "max_length": 0, + "max_value": 0, + "options": "Academic Year", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "date_of_birth", - "fieldtype": "Date", - "hidden": 0, - "label": "Date of Birth", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "date_of_birth", + "fieldtype": "Date", + "hidden": 0, + "label": "Date of Birth", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "blood_group", - "fieldtype": "Select", - "hidden": 0, - "label": "Blood Group", - "max_length": 0, - "max_value": 0, - "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "blood_group", + "fieldtype": "Select", + "hidden": 0, + "label": "Blood Group", + "max_length": 0, + "max_value": 0, + "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_email_id", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Email ID", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_email_id", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Email ID", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_mobile_number", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Mobile Number", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_mobile_number", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Mobile Number", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "default": "INDIAN", - "fieldname": "nationality", - "fieldtype": "Data", - "hidden": 0, - "label": "Nationality", - "max_length": 0, - "max_value": 0, - "options": "", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "default": "INDIAN", + "fieldname": "nationality", + "fieldtype": "Data", + "hidden": 0, + "label": "Nationality", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_1", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 1", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_1", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 1", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_2", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 2", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_2", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 2", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "hidden": 0, - "label": "Pincode", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "hidden": 0, + "label": "Pincode", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "guardians", - "fieldtype": "Table", - "hidden": 0, - "label": "Guardians", - "max_length": 0, - "max_value": 0, - "options": "Student Guardian", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "guardians", + "fieldtype": "Table", + "hidden": 0, + "label": "Guardians", + "max_length": 0, + "max_value": 0, + "options": "Student Guardian", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "siblings", - "fieldtype": "Table", - "hidden": 0, - "label": "Siblings", - "max_length": 0, - "max_value": 0, - "options": "Student Sibling", - "read_only": 0, - "reqd": 0 + "allow_read_on_all_link_options": 0, + "fieldname": "siblings", + "fieldtype": "Table", + "hidden": 0, + "label": "Siblings", + "max_length": 0, + "max_value": 0, + "options": "Student Sibling", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "student_admission", + "fieldtype": "Link", + "hidden": 0, + "label": "Student Admission", + "max_length": 0, + "max_value": 0, + "options": "Student Admission", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 } ] } \ No newline at end of file diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html index ae70df8b08..8b153448ee 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,12 +14,12 @@ {%- if introduction -%}
{{ introduction }}
-{% endif %} +{% endif %} -{%- if application_form_route -%} +{%- if doc.enable_admission_application -%}

+ href='/student-applicant'> {{ _("Apply Now") }}

{% endif %} From 17422a3e45fc61cf686931a89321fede0cde8141 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 13:46:21 +0530 Subject: [PATCH 57/57] fix: Message Formatting --- erpnext/controllers/stock_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2888c764ef..759c6cd73e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -227,7 +227,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ + Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account",