From be9ef4ac891a6ded9e748227e016a39bf775282b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 14 Jan 2013 15:48:00 +0530 Subject: [PATCH] added code to update timestamps in Task, Customer Issue and Support Ticket --- patches/january_2013/update_closed_on.py | 32 ++++++++++++++++ projects/doctype/task/task.py | 14 ++++++- projects/doctype/task/task.txt | 15 ++------ public/js/utils.js | 1 + startup/report_data_map.py | 2 +- .../stock_ledger_entry/stock_ledger_entry.py | 4 +- .../doctype/customer_issue/customer_issue.js | 16 ++++---- .../doctype/customer_issue/customer_issue.py | 6 +++ .../doctype/customer_issue/customer_issue.txt | 29 ++++----------- .../doctype/support_ticket/support_ticket.js | 31 ++++++++-------- .../doctype/support_ticket/support_ticket.py | 30 ++++++++++----- .../doctype/support_ticket/support_ticket.txt | 36 +++++++----------- .../support_analytics/support_analytics.js | 37 ++++++++++++++----- 13 files changed, 152 insertions(+), 101 deletions(-) create mode 100644 patches/january_2013/update_closed_on.py diff --git a/patches/january_2013/update_closed_on.py b/patches/january_2013/update_closed_on.py new file mode 100644 index 0000000000..138549a1fe --- /dev/null +++ b/patches/january_2013/update_closed_on.py @@ -0,0 +1,32 @@ +import webnotes + +def execute(): + webnotes.reload_doc("core", "doctype", "docfield") + webnotes.reload_doc("support", "doctype", "support_ticket") + + # customer issue resolved_by should be Profile + if webnotes.conn.sql("""select count(*) from `tabCustomer Issue` + where ifnull(resolved_by,"")!="" """)[0][0]: + webnotes.make_property_setter({ + "doctype":"Customer Issue", + "fieldname": "resolved_by", + "property": "options", + "value": "Sales Person" + }) + + def get_communication_time(support_ticket, sort_order = 'asc'): + tmp = webnotes.conn.sql("""select creation from tabCommunication where + support_ticket=%s order by creation %s limit 1""" % ("%s", sort_order), + support_ticket) + return tmp and tmp[0][0] or None + + # update in support ticket + webnotes.conn.auto_commit_on_many_writes = True + for st in webnotes.conn.sql("""select name, modified, status from + `tabSupport Ticket`""", as_dict=1): + + webnotes.conn.sql("""update `tabSupport Ticket` set first_responded_on=%s where + name=%s""", (get_communication_time(st.name) or st.modified, st.name)) + if st.status=="Closed": + webnotes.conn.sql("""update `tabSupport Ticket` set resolution_date=%s where + name=%s""", (get_communication_time(st.name, 'desc') or st.modified, st.name)) diff --git a/projects/doctype/task/task.py b/projects/doctype/task/task.py index 83f899586a..79fca9aa9c 100644 --- a/projects/doctype/task/task.py +++ b/projects/doctype/task/task.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import getdate +from webnotes.utils import getdate, today from webnotes.model import db_exists from webnotes.model.wrapper import copy_doclist from webnotes import msgprint @@ -40,7 +40,7 @@ class DocType: if cust: ret = {'customer_name': cust and cust[0][0] or ''} return ret - + def validate(self): if self.doc.exp_start_date and self.doc.exp_end_date and getdate(self.doc.exp_start_date) > getdate(self.doc.exp_end_date): msgprint("'Expected Start Date' can not be greater than 'Expected End Date'") @@ -49,4 +49,14 @@ class DocType: if self.doc.act_start_date and self.doc.act_end_date and getdate(self.doc.act_start_date) > getdate(self.doc.act_end_date): msgprint("'Actual Start Date' can not be greater than 'Actual End Date'") raise Exception + + self.update_status() + def update_status(self): + status = webnotes.conn.get_value("Task", self.doc.name, "status") + if self.doc.status=="Working" and status !="Working" and not self.doc.act_start_date: + self.doc.act_start_date = today() + + if self.doc.status=="Closed" and status != "Closed" and not self.doc.act_end_date: + self.doc.act_end_date = today() + \ No newline at end of file diff --git a/projects/doctype/task/task.txt b/projects/doctype/task/task.txt index 9b8c2eb2ca..e95c456418 100644 --- a/projects/doctype/task/task.txt +++ b/projects/doctype/task/task.txt @@ -2,9 +2,9 @@ { "owner": "Administrator", "docstatus": 0, - "creation": "2012-10-29 14:30:00", + "creation": "2013-01-10 16:34:17", "modified_by": "Administrator", - "modified": "2013-01-02 12:40:26" + "modified": "2013-01-14 14:03:52" }, { "autoname": "TASK.#####", @@ -28,6 +28,8 @@ "parent": "Task", "read": 1, "doctype": "DocPerm", + "submit": 0, + "report": 1, "parenttype": "DocType", "role": "Projects User", "parentfield": "permissions" @@ -191,14 +193,6 @@ "fieldname": "act_end_date", "fieldtype": "Date" }, - { - "oldfieldtype": "Data", - "doctype": "DocField", - "label": "Total Hours (Actual)", - "oldfieldname": "act_total_hrs", - "fieldname": "act_total_hrs", - "fieldtype": "Data" - }, { "oldfieldtype": "Currency", "doctype": "DocField", @@ -253,7 +247,6 @@ "amend": 0, "create": 0, "doctype": "DocPerm", - "submit": 0, "cancel": 0, "permlevel": 1 } diff --git a/public/js/utils.js b/public/js/utils.js index fe79248c59..271dab14d4 100644 --- a/public/js/utils.js +++ b/public/js/utils.js @@ -32,6 +32,7 @@ erpnext.utils.Controller = Class.extend({ var defaults = { posting_date: wn.datetime.get_today(), + posting_time: wn.datetime.now_time() } $.each(defaults, function(k, v) { diff --git a/startup/report_data_map.py b/startup/report_data_map.py index 41d6cb868a..228a8ae2a1 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -219,7 +219,7 @@ data_map = { # Support "Support Ticket": { - "columns": ["name","status","creation","modified"], + "columns": ["name","status","creation","resolution_date","first_responded_on"], "conditions": ["docstatus < 2"], "order_by": "creation" } diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index fae2fcb9f6..3e013b6495 100644 --- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -100,7 +100,7 @@ class DocType: def validate_posting_time(self): """ Validate posting time format""" - if self.doc.posting_time and len(self.doc.posting_time.split(':')) > 2: + if self.doc.posting_time and len(self.doc.posting_time.split(':')) > 3: msgprint("Wrong format of posting time, can not complete the transaction. If you think \ you entered posting time correctly, please contact ERPNext support team.") raise Exception @@ -108,6 +108,4 @@ class DocType: def scrub_posting_time(self): if not self.doc.posting_time or self.doc.posting_time == '00:0': self.doc.posting_time = '00:00' - if len(self.doc.posting_time.split(':')) > 2: - self.doc.posting_time = '00:00' \ No newline at end of file diff --git a/support/doctype/customer_issue/customer_issue.js b/support/doctype/customer_issue/customer_issue.js index 835509fd5f..523b240172 100644 --- a/support/doctype/customer_issue/customer_issue.js +++ b/support/doctype/customer_issue/customer_issue.js @@ -15,15 +15,17 @@ // along with this program. If not, see . cur_frm.cscript.onload = function(doc,cdt,cdn){ - if(!doc.status) set_multiple(dt,dn,{status:'Open'}); + if(!doc.status) + set_multiple(dt,dn,{status:'Open'}); if(doc.__islocal){ hide_field(['customer_address','contact_person']); - } + } } cur_frm.cscript.refresh = function(doc,ct,cdn){ if(doc.docstatus == 1 && (doc.status == 'Open' || doc.status == 'Work In Progress')) - cur_frm.add_custom_button('Make Maintenance Visit', cur_frm.cscript['Make Maintenance Visit']); + cur_frm.add_custom_button('Make Maintenance Visit', + cur_frm.cscript['Make Maintenance Visit']); } @@ -109,11 +111,9 @@ cur_frm.add_fetch('serial_no', 'description', 'description'); cur_frm.add_fetch('serial_no', 'maintenance_status', 'warranty_amc_status'); cur_frm.add_fetch('serial_no', 'warranty_expiry_date', 'warranty_expiry_date'); cur_frm.add_fetch('serial_no', 'amc_expiry_date', 'amc_expiry_date'); -if (cstr(doc.customer) == '') { - cur_frm.add_fetch('serial_no', 'customer', 'customer'); - cur_frm.add_fetch('serial_no', 'customer_name', 'customer_name'); - cur_frm.add_fetch('serial_no', 'delivery_address', 'customer_address'); -} +cur_frm.add_fetch('serial_no', 'customer', 'customer'); +cur_frm.add_fetch('serial_no', 'customer_name', 'customer_name'); +cur_frm.add_fetch('serial_no', 'delivery_address', 'customer_address'); // ---------- // item code diff --git a/support/doctype/customer_issue/customer_issue.py b/support/doctype/customer_issue/customer_issue.py index 0a08d82f38..af93f33eda 100644 --- a/support/doctype/customer_issue/customer_issue.py +++ b/support/doctype/customer_issue/customer_issue.py @@ -21,6 +21,7 @@ import webnotes from webnotes.model import db_exists from webnotes.model.wrapper import copy_doclist from webnotes import session, msgprint +from webnotes.utils import today sql = webnotes.conn.sql @@ -43,6 +44,11 @@ class DocType(TransactionBase): if session['user'] != 'Guest' and not self.doc.customer: msgprint("Please select Customer from whom issue is raised", raise_exception=True) + + if self.doc.status=="Closed" and \ + webnotes.conn.get_value("Customer Issue", self.doc.name, "status")!="Closed": + self.doc.resolution_date = today() + self.doc.resolved_by = webnotes.session.user def on_cancel(self): lst = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t2.prevdoc_docname = '%s' and t1.docstatus!=2"%(self.doc.name)) diff --git a/support/doctype/customer_issue/customer_issue.txt b/support/doctype/customer_issue/customer_issue.txt index 9225eb37b2..1b01803740 100644 --- a/support/doctype/customer_issue/customer_issue.txt +++ b/support/doctype/customer_issue/customer_issue.txt @@ -2,9 +2,9 @@ { "owner": "harshada@webnotestech.com", "docstatus": 0, - "creation": "2012-11-28 11:26:23", + "creation": "2013-01-10 16:34:30", "modified_by": "Administrator", - "modified": "2012-12-03 17:10:41" + "modified": "2013-01-14 12:41:48" }, { "is_submittable": 1, @@ -27,6 +27,7 @@ "read": 1, "doctype": "DocPerm", "parenttype": "DocType", + "report": 1, "parentfield": "permissions" }, { @@ -51,7 +52,6 @@ "permlevel": 0, "no_copy": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Status", "oldfieldname": "status", @@ -87,12 +87,10 @@ { "print_hide": 1, "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Customer", "oldfieldname": "customer", "permlevel": 0, - "trigger": "Client", "fieldname": "customer", "fieldtype": "Link", "search_index": 1, @@ -140,28 +138,24 @@ }, { "description": "Item, Warranty, AMC (Annual Maintenance Contract) details will be automatically fetched when Serial Number is selected.", - "colour": "White:FFF", "doctype": "DocField", "label": "Serial No", - "trigger": "Client", + "options": "Serial No", "fieldname": "serial_no", "fieldtype": "Link", - "options": "Serial No", "permlevel": 0 }, { "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Item Code", "oldfieldname": "item_code", - "permlevel": 0, - "trigger": "Client", + "options": "Item", "fieldname": "item_code", "fieldtype": "Link", "search_index": 1, "reqd": 0, - "options": "Item", + "permlevel": 0, "in_filter": 1 }, { @@ -174,7 +168,6 @@ }, { "oldfieldtype": "Data", - "colour": "White:FFF", "doctype": "DocField", "label": "Item Name", "oldfieldname": "item_name", @@ -185,7 +178,6 @@ }, { "oldfieldtype": "Small Text", - "colour": "White:FFF", "doctype": "DocField", "label": "Description", "oldfieldname": "description", @@ -196,7 +188,6 @@ "permlevel": 1 }, { - "colour": "White:FFF", "doctype": "DocField", "label": "Warranty / AMC Status", "options": "\nUnder Warranty\nOut of Warranty\nUnder AMC\nOut of AMC", @@ -223,7 +214,6 @@ { "description": "To assign this issue, use the \"Assign\" button in the sidebar.", "oldfieldtype": "Section Break", - "colour": "White:FFF", "doctype": "DocField", "label": "Resolution", "options": "Simple", @@ -238,7 +228,7 @@ "label": "Resolution Date", "oldfieldname": "resolution_date", "fieldname": "resolution_date", - "fieldtype": "Date", + "fieldtype": "Datetime", "search_index": 1, "permlevel": 0, "in_filter": 1 @@ -249,7 +239,7 @@ "doctype": "DocField", "label": "Resolved By", "oldfieldname": "resolved_by", - "options": "Sales Person", + "options": "Profile", "fieldname": "resolved_by", "fieldtype": "Link", "search_index": 1, @@ -300,7 +290,6 @@ { "print_hide": 1, "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Territory", "oldfieldname": "territory", @@ -373,12 +362,10 @@ { "print_hide": 1, "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Company", "oldfieldname": "company", "permlevel": 0, - "trigger": "Client", "fieldname": "company", "fieldtype": "Link", "search_index": 1, diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js index 3484bf346e..159dddd62a 100644 --- a/support/doctype/support_ticket/support_ticket.js +++ b/support/doctype/support_ticket/support_ticket.js @@ -85,25 +85,26 @@ $.extend(cur_frm.cscript, { }, 'Close Ticket': function() { - var doc = cur_frm.doc - if(doc.name) - $c_obj(make_doclist(doc.doctype, doc.name),'close_ticket','',function(r,rt) { - if(!r.exc) { - cur_frm.refresh(); - } - }); + cur_frm.cscript.set_status("Closed"); }, 'Re-Open Ticket': function() { - var doc = cur_frm.doc - if(doc.name) - $c_obj(make_doclist(doc.doctype, doc.name),'reopen_ticket','',function(r,rt) { - if(!r.exc) { - cur_frm.refresh(); - } - }); - } + cur_frm.cscript.set_status("Open"); + }, + set_status: function(status) { + wn.call({ + method:"support.doctype.support_ticket.support_ticket.set_status", + args: { + name: cur_frm.doc.name, + status: status + }, + callback: function(r) { + if(!r.exc) cur_frm.reload_doc(); + } + }) + + } }) diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py index 0f4a25eeb7..43b8283b54 100644 --- a/support/doctype/support_ticket/support_ticket.py +++ b/support/doctype/support_ticket/support_ticket.py @@ -19,6 +19,7 @@ import webnotes from utilities.transaction_base import TransactionBase from home import update_feed +from webnotes.utils import now class DocType(TransactionBase): def __init__(self, doc, doclist=[]): @@ -40,6 +41,9 @@ class DocType(TransactionBase): if signature: content += '

' + signature + '

' return content + + def validate(self): + self.update_status() def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Waiting for Customer') @@ -47,15 +51,23 @@ class DocType(TransactionBase): webnotes.conn.set(self.doc, 'lead', comm.lead) if comm.contact and not self.doc.contact: webnotes.conn.set(self.doc, 'contact', comm.contact) - - def close_ticket(self): - webnotes.conn.set(self.doc,'status','Closed') - update_feed(self) - - def reopen_ticket(self): - webnotes.conn.set(self.doc,'status','Open') - update_feed(self) - + def on_trash(self): webnotes.conn.sql("""update `tabCommunication` set support_ticket=NULL where support_ticket=%s""", (self.doc.name,)) + + def update_status(self): + status = webnotes.conn.get_value("Support Ticket", self.doc.name, "status") + if self.doc.status!="Open" and status =="Open": + self.doc.first_responded_on = now() + if self.doc.status=="Closed" and status !="Closed": + self.doc.resolution_date = now() + if self.doc.status=="Open" and status !="Open": + self.doc.resolution_date = "" + +@webnotes.whitelist() +def set_status(name, status): + st = webnotes.model_wrapper("Support Ticket", name) + st.doc.status = status + st.save() + diff --git a/support/doctype/support_ticket/support_ticket.txt b/support/doctype/support_ticket/support_ticket.txt index b2bdadb735..b475db56b4 100644 --- a/support/doctype/support_ticket/support_ticket.txt +++ b/support/doctype/support_ticket/support_ticket.txt @@ -2,9 +2,9 @@ { "owner": "Administrator", "docstatus": 0, - "creation": "2012-11-02 17:17:05", + "creation": "2013-01-10 16:34:31", "modified_by": "Administrator", - "modified": "2012-11-28 10:45:19" + "modified": "2013-01-14 14:15:37" }, { "autoname": "naming_series:", @@ -25,10 +25,11 @@ "name": "__common__", "parent": "Support Ticket", "amend": 0, - "submit": 0, "doctype": "DocPerm", + "submit": 0, "read": 1, "parenttype": "DocType", + "report": 1, "parentfield": "permissions" }, { @@ -52,7 +53,6 @@ "permlevel": 1, "no_copy": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Status", "oldfieldname": "status", @@ -117,7 +117,6 @@ "permlevel": 1 }, { - "colour": "White:FFF", "doctype": "DocField", "label": "Additional Info", "fieldname": "additional_info", @@ -152,12 +151,10 @@ { "print_hide": 1, "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Customer", "oldfieldname": "customer", "permlevel": 1, - "trigger": "Client", "fieldname": "customer", "fieldtype": "Link", "search_index": 1, @@ -218,31 +215,26 @@ "permlevel": 1 }, { - "depends_on": "eval:!doc.__islocal", + "doctype": "DocField", + "label": "First Responded On", + "fieldname": "first_responded_on", + "fieldtype": "Datetime", + "permlevel": 0 + }, + { "no_copy": 1, - "search_index": 0, - "colour": "White:FFF", + "depends_on": "eval:!doc.__islocal", "doctype": "DocField", "label": "Resolution Date", "oldfieldname": "resolution_date", "fieldname": "resolution_date", - "fieldtype": "Date", + "fieldtype": "Datetime", + "search_index": 0, "oldfieldtype": "Date", "permlevel": 1, "in_filter": 0 }, { - "oldfieldtype": "Time", - "doctype": "DocField", - "label": "Resolution Time", - "oldfieldname": "resolution_time", - "fieldname": "resolution_time", - "fieldtype": "Time", - "depends_on": "eval:!doc.__islocal", - "permlevel": 1 - }, - { - "colour": "White:FFF", "doctype": "DocField", "label": "Content Type", "fieldname": "content_type", diff --git a/support/page/support_analytics/support_analytics.js b/support/page/support_analytics/support_analytics.js index e310df30a3..60384bb0a8 100644 --- a/support/page/support_analytics/support_analytics.js +++ b/support/page/support_analytics/support_analytics.js @@ -53,8 +53,12 @@ erpnext.SupportAnalytics = wn.views.GridReportWithPlot.extend({ checked:true}; var days_to_close = {status:"Days to Close", "id":"days-to-close", checked:false}; + var total_closed = {}; var hours_to_close = {status:"Hours to Close", "id":"hours-to-close", checked:false}; + var hours_to_respond = {status:"Hours to Respond", "id":"hours-to-respond", + checked:false}; + var total_responded = {}; $.each(wn.report_dump.data["Support Ticket"], function(i, d) { @@ -62,25 +66,40 @@ erpnext.SupportAnalytics = wn.views.GridReportWithPlot.extend({ var date = d.creation.split(" ")[0]; var col = me.column_map[date]; if(col) { - // just count - var day_diff = dateutil.get_diff(d.modified, d.creation); - var hour_diff = dateutil.get_hour_diff(d.modified, d.creation); - total_tickets[col.field] = flt(total_tickets[col.field]) + 1; - days_to_close[col.field] = flt(days_to_close[col.field]) + day_diff; - hours_to_close[col.field] = flt(hours_to_close[col.field]) + hour_diff; + if(d.status=="Closed") { + // just count + total_closed[col.field] = flt(total_closed[col.field]) + 1; + + days_to_close[col.field] = flt(days_to_close[col.field]) + + dateutil.get_diff(d.resolution_date, d.creation); + + hours_to_close[col.field] = flt(hours_to_close[col.field]) + + dateutil.get_hour_diff(d.resolution_date, d.creation); + + } + if (d.first_responded_on) { + total_responded[col.field] = flt(total_responded[col.field]) + 1; + + hours_to_respond[col.field] = flt(hours_to_respond[col.field]) + + dateutil.get_hour_diff(d.first_responded_on, d.creation); + } } }); // make averages $.each(this.columns, function(i, col) { if(col.formatter==me.currency_formatter && total_tickets[col.field]) { - days_to_close[col.field] = flt(days_to_close[col.field]) / flt(total_tickets[col.field]); - hours_to_close[col.field] = flt(hours_to_close[col.field]) / flt(total_tickets[col.field]); + days_to_close[col.field] = flt(days_to_close[col.field]) / + flt(total_closed[col.field]); + hours_to_close[col.field] = flt(hours_to_close[col.field]) / + flt(total_closed[col.field]); + hours_to_respond[col.field] = flt(hours_to_respond[col.field]) / + flt(total_responded[col.field]); } }) - this.data = [total_tickets, days_to_close, hours_to_close]; + this.data = [total_tickets, days_to_close, hours_to_close, hours_to_respond]; }, get_plot_points: function(item, col, idx) {