diff --git a/erpnext/projects/doctype/ticket/ticket.py b/erpnext/projects/doctype/ticket/ticket.py
index 4d3e988f11..6d53d6a841 100644
--- a/erpnext/projects/doctype/ticket/ticket.py
+++ b/erpnext/projects/doctype/ticket/ticket.py
@@ -1,12 +1,14 @@
# Please edit this list and import only required elements
import webnotes
-from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, sendmail, set_default, str_esc_quote, user_format, validate_email_add
+from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, set_default, str_esc_quote, user_format, validate_email_add
+from webnotes.util.email_lib import sendmail
from webnotes.model import db_exists
from webnotes.model.doc import Document, addchild, removechild, getchildren, make_autoname, SuperDocType
from webnotes.model.doclist import getlist, copy_doclist
from webnotes.model.code import get_obj, get_server_obj, run_server_obj, updatedb, check_syntax
from webnotes import session, form, is_testing, msgprint, errprint
+
sql = webnotes.conn.sql
set = webnotes.conn.set
get_value = webnotes.conn.get_value
@@ -15,161 +17,183 @@ get_value = webnotes.conn.get_value
class DocType:
- def __init__(self,doc,doclist=[]):
- self.doc = doc
- self.doclist = doclist
-
- def get_project_details(self):
- cust = sql("select customer, customer_name from `tabProject` where name = %s", self.doc.project)
- if cust:
- ret = {'customer': cust and cust[0][0] or '', 'customer_name': cust and cust[0][1] or ''}
- return ret
-
- def get_customer_details(self):
- cust = sql("select customer_name from `tabCustomer` where name=%s", self.doc.customer)
- if cust:
- ret = {'customer_name': cust and cust[0][0] or ''}
- return ret
-
- def get_allocated_to_name(self):
- as_em = sql("select first_name, last_name from `tabProfile` where name=%s",str(self.doc.allocated_to))
- ret = { 'allocated_to_name' : as_em and (as_em[0][0] + ' ' + as_em[0][1]) or ''}
- return ret
+ def __init__(self,doc,doclist=[]):
+ self.doc = doc
+ self.doclist = doclist
+
+ def get_project_details(self):
+ cust = sql("select customer, customer_name from `tabProject` where name = %s", self.doc.project)
+ if cust:
+ ret = {'customer': cust and cust[0][0] or '', 'customer_name': cust and cust[0][1] or ''}
+ return ret
+
+ def get_customer_details(self):
+ cust = sql("select customer_name from `tabCustomer` where name=%s", self.doc.customer)
+ if cust:
+ ret = {'customer_name': cust and cust[0][0] or ''}
+ return ret
+
+ def get_allocated_to_name(self):
+ as_em = sql("select first_name, last_name from `tabProfile` where name=%s",str(self.doc.allocated_to))
+ ret = { 'allocated_to_name' : as_em and (as_em[0][0] + ' ' + as_em[0][1]) or ''}
+ return ret
- # validate
- #--------------------------------------------
+ # validate
+ #--------------------------------------------
- def validate(self):
- if not self.doc.opening_date:
- msgprint("Please enter Opening Date.")
- raise Exception
- elif getdate(self.doc.opening_date) > getdate(nowdate()):
- msgprint("Opening date can not be future date")
- raise Exception
-
- 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'")
- raise Exception
-
- 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
-
- if self.doc.opening_date and self.doc.review_date and getdate(self.doc.opening_date) > getdate(self.doc.review_date):
- msgprint("Review Date should be greater than or equal to Opening Date ")
- raise Exception
-
- if self.doc.closing_date and self.doc.review_date and getdate(self.doc.closing_date) < getdate(self.doc.review_date):
- msgprint("Closing Date should be greater than or equal to Review Date ")
- raise Exception
+ def validate(self):
+ if not self.doc.opening_date:
+ msgprint("Please enter Opening Date.")
+ raise Exception
+ elif getdate(self.doc.opening_date) > getdate(nowdate()):
+ msgprint("Opening date can not be future date")
+ raise Exception
+
+ 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'")
+ raise Exception
+
+ 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
+
+ if self.doc.opening_date and self.doc.review_date and getdate(self.doc.opening_date) > getdate(self.doc.review_date):
+ msgprint("Review Date should be greater than or equal to Opening Date ")
+ raise Exception
+
+ if self.doc.closing_date and self.doc.review_date and getdate(self.doc.closing_date) < getdate(self.doc.review_date):
+ msgprint("Closing Date should be greater than or equal to Review Date ")
+ raise Exception
- # on update
- #--------------------------------------------
-
- def on_update(self):
- if (self.doc.status =='Open') and (self.doc.task_email_notify==1):
- msg2= 'A task %s has been assigned to you by %s on %s
\
- Project:%s
Review Date:%s
Closing Date:%s
Details %s' \
- %(self.doc.name,self.doc.senders_name,self.doc.opening_date,self.doc.project, \
- self.doc.review_date,self.doc.closing_date,self.doc.description)
- sendmail(self.doc.allocated_to, sender='automail@webnotestech.com', \
- subject='A task has been assigned', parts=[['text/plain',msg2]])
- pass
-
- #validate before sending for approval
- def validate_for_pending_review(self):
- if not self.doc.allocated_to:
- msgprint("Please enter allocated_to.")
- raise Exception
- self.validate_with_timesheet_dates()
-
- #validate before closing task
- def validate_for_closed(self):
- self.check_non_submitted_timesheets()
- self.get_actual_total_hrs()
-
- def check_non_submitted_timesheets(self):
- chk = sql("select t1.name from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent=t1.name and t2.task_id=%s and t1.status='Draft'", self.doc.name)
- if chk:
- chk_lst = [x[0] for x in chk]
- msgprint("Please submit timesheet(s) : "+','.join(chk_lst)+" before declaring this task as completed. As details of this task present in timesheet(s)")
- raise Exception
-
- #calculate actual total hours taken to complete task from timesheets
- def get_actual_total_hrs(self):
- import datetime
- import time
- chk = sql("select t2.act_total_hrs from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status = 'Submitted' and ifnull(t2.act_total_hrs, '')!='' order by t1.timesheet_date asc", self.doc.name)
- if chk:
- chk_lst = [x[0] for x in chk]
- actual_total = total =0
-
- for m in chk_lst:
- m1, m2=[], 0
- m1 = m.split(":")
- m2 = (datetime.timedelta(minutes=cint(m1[1]), hours=cint(m1[0]))).seconds
- total = total + m2
-
- actual_total = time.strftime("%H:%M", time.gmtime(total))
- set(self.doc, 'act_total_hrs', actual_total)
-
- # validate and fetch actual start and end date
- def validate_with_timesheet_dates(self):
- chk = sql("select t1.name, t1.timesheet_date from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status = 'Submitted' order by t1.timesheet_date asc", self.doc.name, as_dict=1)
- if chk:
- if self.doc.act_start_date:
- if chk[0]['timesheet_date'] > getdate(self.doc.act_start_date) or chk[0]['timesheet_date'] < getdate(self.doc.act_start_date):
- msgprint("Actual start date of this task is "+cstr(chk[0]['timesheet_date'])+" as per timesheet "+cstr(chk[0]['name']))
- raise Exception
- else:
- self.doc.act_start_date = chk[0]['timesheet_date']
-
- if self.doc.act_end_date:
- if chk[len(chk)-1]['timesheet_date'] < getdate(self.doc.act_end_date) or chk[len(chk)-1]['timesheet_date'] > getdate(self.doc.act_end_date):
- msgprint("Actual end date of this task is "+cstr(chk[len(chk)-1]['timesheet_date'])+" as per timesheet "+cstr(chk[len(chk)-1]['name']))
- raise Exception
- else:
- self.doc.act_end_date = chk[len(chk)-1]['timesheet_date']
-
- def set_for_review(self):
- self.check_non_submitted_timesheets()
- self.validate_for_pending_review()
- self.get_actual_total_hrs()
- self.doc.review_date = nowdate()
- set(self.doc, 'status', 'Pending Review')
- self.doc.save()
- return cstr('true')
-
- def reopen_task(self):
- set(self.doc, 'status', 'Open')
- self.doc.save()
- return cstr('true')
-
- def declare_completed(self):
- if self.doc.status == 'Open':
- self.validate_for_pending_review()
- self.doc.review_date = nowdate()
- else:
- self.validate_with_timesheet_dates()
- self.validate_for_closed()
- self.doc.closing_date = nowdate()
- set(self.doc, 'status', 'Closed')
- set(self.doc, 'docstatus', 1)
- self.doc.save()
- return cstr('true')
-
- def cancel_task(self):
- chk = sql("select distinct t1.name from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status!='Cancelled'", self.doc.name)
- if chk:
- chk_lst = [x[0] for x in chk]
- msgprint("Timesheet(s) "+','.join(chk_lst)+" created against this task. Thus can not be cancelled")
- raise Exception
- else:
- set(self.doc, 'status', 'Cancelled')
- set(self.doc, 'docstatus', 2)
- self.doc.save()
- return cstr('true')
-
- def on_cancel(self):
- self.cancel_task()
+ # on update
+ #--------------------------------------------
+
+ def on_update(self):
+ if (self.doc.status =='Open') and (self.doc.task_email_notify==1):
+ if (self.doc.allocated_to == self.doc.allocated_to_old):
+ return
+ else:
+ self.doc.allocated_to_old = self.doc.allocated_to
+ msg2="""A task %s has been assigned to you by %s on %s
Project: %s
\
+ Review Date: %s
Closing Date: %s
Details: %s""" \
+ %(self.doc.name, self.doc.senders_name, self.doc.opening_date, \
+ self.doc.project, self.doc.review_date, self.doc.closing_date, self.doc.description)
+ self.add_calendar_event()
+ sendmail(self.doc.allocated_to, sender='automail@webnotestech.com',subject='A task has been assigned',\
+ parts=[['text/plain',msg2]])
+ self.doc.sent_reminder=0
+ pass
+ #Function to be called from server inside scheduler ... set reminder/events
+ #validate before sending for approval
+ def validate_for_pending_review(self):
+ if not self.doc.allocated_to:
+ msgprint("Please enter allocated_to.")
+ raise Exception
+ self.validate_with_timesheet_dates()
+
+ #validate before closing task
+ def validate_for_closed(self):
+ self.check_non_submitted_timesheets()
+ self.get_actual_total_hrs()
+
+ def check_non_submitted_timesheets(self):
+ chk = sql("select t1.name from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent=t1.name and t2.task_id=%s and t1.status='Draft'", self.doc.name)
+ if chk:
+ chk_lst = [x[0] for x in chk]
+ msgprint("Please submit timesheet(s) : "+','.join(chk_lst)+" before declaring this task as completed. As details of this task present in timesheet(s)")
+ raise Exception
+
+ #calculate actual total hours taken to complete task from timesheets
+ def get_actual_total_hrs(self):
+ import datetime
+ import time
+ chk = sql("select t2.act_total_hrs from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status = 'Submitted' and ifnull(t2.act_total_hrs, '')!='' order by t1.timesheet_date asc", self.doc.name)
+ if chk:
+ chk_lst = [x[0] for x in chk]
+ actual_total = total =0
+
+ for m in chk_lst:
+ m1, m2=[], 0
+ m1 = m.split(":")
+ m2 = (datetime.timedelta(minutes=cint(m1[1]), hours=cint(m1[0]))).seconds
+ total = total + m2
+
+ actual_total = time.strftime("%H:%M", time.gmtime(total))
+ set(self.doc, 'act_total_hrs', actual_total)
+
+ # validate and fetch actual start and end date
+ def validate_with_timesheet_dates(self):
+ chk = sql("select t1.name, t1.timesheet_date from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status = 'Submitted' order by t1.timesheet_date asc", self.doc.name, as_dict=1)
+ if chk:
+ if self.doc.act_start_date:
+ if chk[0]['timesheet_date'] > getdate(self.doc.act_start_date) or chk[0]['timesheet_date'] < getdate(self.doc.act_start_date):
+ msgprint("Actual start date of this task is "+cstr(chk[0]['timesheet_date'])+" as per timesheet "+cstr(chk[0]['name']))
+ raise Exception
+ else:
+ self.doc.act_start_date = chk[0]['timesheet_date']
+
+ if self.doc.act_end_date:
+ if chk[len(chk)-1]['timesheet_date'] < getdate(self.doc.act_end_date) or chk[len(chk)-1]['timesheet_date'] > getdate(self.doc.act_end_date):
+ msgprint("Actual end date of this task is "+cstr(chk[len(chk)-1]['timesheet_date'])+" as per timesheet "+cstr(chk[len(chk)-1]['name']))
+ raise Exception
+ else:
+ self.doc.act_end_date = chk[len(chk)-1]['timesheet_date']
+
+ def set_for_review(self):
+ self.check_non_submitted_timesheets()
+ self.validate_for_pending_review()
+ self.get_actual_total_hrs()
+ self.doc.review_date = nowdate()
+ set(self.doc, 'status', 'Pending Review')
+ self.doc.save()
+ return cstr('true')
+
+ def reopen_task(self):
+ set(self.doc, 'status', 'Open')
+ self.doc.save()
+ return cstr('true')
+
+ def declare_completed(self):
+ if self.doc.status == 'Open':
+ self.validate_for_pending_review()
+ self.doc.review_date = nowdate()
+ else:
+ self.validate_with_timesheet_dates()
+ self.validate_for_closed()
+ self.doc.closing_date = nowdate()
+ set(self.doc, 'status', 'Closed')
+ set(self.doc, 'docstatus', 1)
+ self.doc.save()
+ return cstr('true')
+
+ def cancel_task(self):
+ chk = sql("select distinct t1.name from `tabTimesheet` t1, `tabTimesheet Detail` t2 where t2.parent = t1.name and t2.task_id = %s and t1.status!='Cancelled'", self.doc.name)
+ if chk:
+ chk_lst = [x[0] for x in chk]
+ msgprint("Timesheet(s) "+','.join(chk_lst)+" created against this task. Thus can not be cancelled")
+ raise Exception
+ else:
+ set(self.doc, 'status', 'Cancelled')
+ set(self.doc, 'docstatus', 2)
+ self.doc.save()
+ return cstr('true')
+ # def delete_event_from_calender(self): Add later
+
+ def add_calendar_event(self):
+ in_calendar_of = self.doc.allocated_to
+ event = Document('Event')
+ event.owner = in_calendar_of
+ event.description =''
+ #'Task:%s
By:%s
Project:%s
Details:%s' \
+ #%(self.doc.name,self.doc.senders_name,self.doc.project,self.doc.details)
+ event.event_date = self.doc.exp_start_date
+ event.event_hour = self.doc.exp_total_hrs and exp_total_hrs or ''
+ event.event_type = 'Private'
+ event.ref_type = 'Task'
+ event.ref_name = self.doc.name
+ event.save(1)
+
+
+ def on_cancel(self):
+ self.cancel_task()
diff --git a/erpnext/projects/doctype/ticket/ticket.txt b/erpnext/projects/doctype/ticket/ticket.txt
index cd984aca94..17c0f2d5b9 100644
--- a/erpnext/projects/doctype/ticket/ticket.txt
+++ b/erpnext/projects/doctype/ticket/ticket.txt
@@ -5,14 +5,14 @@
{
'creation': '2011-01-28 17:52:35',
'docstatus': 0,
- 'modified': '2011-12-21 16:32:30',
+ 'modified': '2011-12-21 17:22:20',
'modified_by': 'Administrator',
'owner': 'Administrator'
},
# These values are common for all DocType
{
- '_last_update': '1324394580',
+ '_last_update': '1324465350',
'allow_trash': 1,
'autoname': 'TIC/.####',
'colour': 'White:FFF',
@@ -26,7 +26,7 @@
'show_in_menu': 0,
'subject': '%(subject)s',
'tag_fields': 'status',
- 'version': 247
+ 'version': 248
},
# These values are common for all DocField
@@ -249,6 +249,16 @@
'width': '50%'
},
+ # DocField
+ {
+ 'doctype': 'DocField',
+ 'fieldname': 'allocated_to_old',
+ 'fieldtype': 'Link',
+ 'hidden': 1,
+ 'label': 'Allocated To Old',
+ 'permlevel': 0
+ },
+
# DocField
{
'colour': 'White:FFF',