[FIX] This fixes the project_issue issue analysis report. Values on the Extra Info tab on issues are now correct.

bzr revid: cbi@openerp.com-20121029125417-2zql6d3xa8p7zke3
This commit is contained in:
Chris Biersbach
2012-10-29 13:54:17 +01:00
parent 5f67d9c3b4
commit c46f1f9fc0
4 changed files with 118 additions and 64 deletions
+17 -23
View File
@@ -1,4 +1,4 @@
#-*- coding: utf-8 -*-
#-*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
@@ -65,7 +65,9 @@ class project_issue(crm.crm_case, osv.osv):
"""
res = super(project_issue, self).case_open(cr, uid, ids, *args)
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' : uid})
for issue in self.browse(cr, uid, ids):
if not issue.date_open:
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' : uid})
for (id, name) in self.name_get(cr, uid, ids):
message = _("Issue '%s' has been opened.") % name
self.log(cr, uid, id, message)
@@ -96,9 +98,16 @@ class project_issue(crm.crm_case, osv.osv):
"""
cal_obj = self.pool.get('resource.calendar')
res_obj = self.pool.get('resource.resource')
res = {}
for issue in self.browse(cr, uid, ids, context=context):
# if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None
if not issue.project_id or not issue.project_id.resource_calendar_id:
working_hours = None
else:
working_hours = issue.project_id.resource_calendar_id.id
res[issue.id] = {}
for field in fields:
duration = 0
@@ -112,7 +121,7 @@ class project_issue(crm.crm_case, osv.osv):
ans = date_open - date_create
date_until = issue.date_open
#Calculating no. of working hours to open the issue
hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
hours = cal_obj.interval_hours_get(cr, issue.user_id.id or uid, working_hours,
date_create,
date_open)
elif field in ['working_hours_close','day_close']:
@@ -121,7 +130,7 @@ class project_issue(crm.crm_case, osv.osv):
date_until = issue.date_closed
ans = date_close - date_create
#Calculating no. of working hours to close the issue
hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
hours = cal_obj.interval_hours_get(cr, issue.user_id.id or uid, working_hours,
date_create,
date_close)
elif field in ['days_since_creation']:
@@ -142,27 +151,12 @@ class project_issue(crm.crm_case, osv.osv):
resource_ids = res_obj.search(cr, uid, [('user_id','=',issue.user_id.id)])
if resource_ids and len(resource_ids):
resource_id = resource_ids[0]
duration = float(ans.days)
if issue.project_id and issue.project_id.resource_calendar_id:
duration = float(ans.days) * 24
new_dates = cal_obj.interval_min_get(cr, uid,
issue.project_id.resource_calendar_id.id,
date_create,
duration, resource=resource_id)
no_days = []
date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S')
for in_time, out_time in new_dates:
if in_time.date not in no_days:
no_days.append(in_time.date)
if out_time > date_until:
break
duration = len(no_days)
duration = float(ans.days) + float(ans.seconds)/(24*3600)
if field in ['working_hours_open','working_hours_close']:
res[issue.id][field] = hours
else:
res[issue.id][field] = abs(float(duration))
elif field in ['day_open','day_close']:
res[issue.id][field] = duration
return res
@@ -50,13 +50,13 @@ class project_issue_report(osv.osv):
'opening_date': fields.date('Date of Opening', readonly=True),
'creation_date': fields.date('Creation Date', readonly=True),
'date_closed': fields.date('Date of Closing', readonly=True),
'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',section_id),('object_id.model', '=', 'project.issue')]"),
'categ_id': fields.many2one('crm.case.categ', 'Category', domain="['&',('section_id','=',section_id),('object_id.model', '=', 'project.issue')]"),
'type_id': fields.many2one('project.task.type', 'Stage'),
'nbr': fields.integer('# of Issues', readonly=True),
'working_hours_open': fields.float('Avg. Working Hours to Open', readonly=True, group_operator="avg"),
'working_hours_close': fields.float('Avg. Working Hours to Close', readonly=True, group_operator="avg"),
'delay_open': fields.float('Avg. Delay to Open', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to close the project issue"),
help="Number of Days to open the project issue"),
'delay_close': fields.float('Avg. Delay to Close', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to close the project issue"),
'company_id' : fields.many2one('res.company', 'Company'),
@@ -64,9 +64,9 @@ class project_issue_report(osv.osv):
'project_id':fields.many2one('project.project', 'Project',readonly=True),
'version_id': fields.many2one('project.issue.version', 'Version'),
'user_id' : fields.many2one('res.users', 'Assigned to',readonly=True),
'partner_id': fields.many2one('res.partner','Partner',domain="[('object_id.model', '=', 'project.issue')]"),
'partner_id': fields.many2one('res.partner','Partner'),
'channel_id': fields.many2one('crm.case.channel', 'Channel',readonly=True),
'task_id': fields.many2one('project.task', 'Task',domain="[('object_id.model', '=', 'project.issue')]" ),
'task_id': fields.many2one('project.task', 'Task'),
'email': fields.integer('# Emails', size=128, readonly=True),
}
@@ -98,8 +98,8 @@ class project_issue_report(osv.osv):
c.channel_id,
c.task_id,
date_trunc('day',c.create_date) as create_date,
extract('epoch' from (c.date_open-c.create_date))/(3600*24) as delay_open,
extract('epoch' from (c.date_closed-c.date_open))/(3600*24) as delay_close,
c.day_open as delay_open,
c.day_close as delay_close,
(SELECT count(id) FROM mail_message WHERE model='project.issue' AND res_id=c.id) AS email
FROM
@@ -81,7 +81,7 @@
<separator orientation="vertical" />
<field name="project_id"/>
<field name="user_id" select="1" widget="selection" filter_domain="[('user_id','ilike',self)]"/>
<field name="user_id" select="1" filter_domain="[('user_id','ilike',self)]"/>
<field name="partner_id"/>
<field name="version_id" widget="selection" />
</group>
+94 -34
View File
@@ -28,6 +28,7 @@ from tools.translate import _
from itertools import groupby
from operator import itemgetter
import pytz
class resource_calendar(osv.osv):
_name = "resource.calendar"
@@ -199,7 +200,7 @@ class resource_calendar(osv.osv):
res = self.interval_get_multi(cr, uid, [(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)], resource, byday)[(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)]
return res
def interval_hours_get(self, cr, uid, id, dt_from, dt_to, resource=False):
def interval_hours_get(self, cr, uid, id, dt_from, dt_to, resource=False, context=None):
""" Calculates the Total Working hours based on given start_date to
end_date, If resource id is supplied that it will consider the source
leaves also in calculating the hours.
@@ -212,45 +213,104 @@ class resource_calendar(osv.osv):
@return : Total number of working hours based dt_from and dt_end and
resource if supplied.
"""
if not id:
return 0.0
dt_leave = self._get_leaves(cr, uid, id, resource)
hours = 0.0
dt_to_hours = dt_to.hour + dt_to.minute / 60.0 + dt_to.second / 3600.0
current_hour = dt_from.hour
users_obj = self.pool.get('res.users')
user = users_obj.browse(cr, uid, uid, context=context)
utc = pytz.timezone('UTC')
if user.context_tz:
context_tz = pytz.timezone(user.context_tz)
else:
context_tz = utc
# gap between UTC and user timezone
tz_gap = (utc.localize(dt_from) - context_tz.localize(dt_from)).seconds / 3600
while (dt_from <= dt_to):
cr.execute("select hour_from,hour_to from resource_calendar_attendance where dayofweek='%s' and calendar_id=%s order by hour_from", (dt_from.weekday(),id))
der = cr.fetchall()
for (hour_from,hour_to) in der:
if hours != 0.0:#For first time of the loop only,hours will be 0
current_hour = hour_from
leave_flag = False
if (hour_to>=current_hour):
dt_check = dt_from.strftime('%Y-%m-%d')
for leave in dt_leave:
if dt_check == leave:
dt_check = datetime.strptime(dt_check, "%Y-%m-%d") + timedelta(days=1)
leave_flag = True
orig_dt_from = dt_from
if leave_flag:
break
while dt_from <= dt_to:
# if the working hours are None, use the default ones
if id:
cr.execute("select hour_from,hour_to from resource_calendar_attendance where dayofweek='%s' and calendar_id=%s order by hour_from", (dt_from.weekday(),id))
slots = cr.fetchall()
elif dt_from.weekday() not in [5, 6]:
slots = [(8, 12), (13, 17)]
else:
slots = []
for (hour_from, hour_to) in slots:
hour_from = hour_from - tz_gap
if hour_from < 0:
hour_from = 0
if hour_from > 24:
hour_from = 24
hour_to = hour_to - tz_gap
if hour_to < 0:
hour_to = 0
if hour_to > 24:
hour_to = 24
dt_from_hours = dt_from.hour + dt_from.minute / 60.0 + dt_from.second / 3600.0
first_day = dt_from.year == orig_dt_from.year and dt_from.month == orig_dt_from.month and dt_from.day == orig_dt_from.day
last_day = dt_from.year == dt_to.year and dt_from.month == dt_to.month and dt_from.day == dt_to.day
# special case: the ticket is closed the same day it is opened:
if first_day and last_day:
# no overlap (beginning time after end of current slot or ending time before beginning of current slot)
if dt_from_hours >= hour_to or dt_to_hours <= hour_from:
continue
# beginning of working hours is after beginning time and ending time is after end of working hours: add the entire slot
elif dt_from_hours <= hour_from and dt_to_hours >= hour_to:
hours += hour_to - hour_from
# add only the overlap (left)
elif dt_from_hours >= hour_from and dt_to_hours >= hour_to:
hours += hour_to - dt_from_hours
# add only the overlap (right)
elif dt_from_hours <= hour_from and dt_to_hours <= hour_to:
hours += dt_to_hours - hour_from
# add only the overlap (both sides)
else:
d1 = dt_from
d2 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_to)), int((hour_to%1) * 60))
hours += dt_to_hours - dt_from_hours
# first day: only add from beginning time to end of working hours
elif first_day:
# no overlap (beginning time after end of current slot)
if dt_from_hours >= hour_to:
continue
# beginning of working hours is after beginning time: add the entire slot
elif dt_from_hours <= hour_from:
hours += hour_to - hour_from
# add only the overlap
else:
hours += hour_to - dt_from_hours
if hours != 0.0:#For first time of the loop only,hours will be 0
d1 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(current_hour)), int((current_hour%1) * 60))
if dt_from.day == dt_to.day:
if hour_from <= dt_to.hour <= hour_to:
d2 = dt_to
dt_from = d2
hours += (d2-d1).seconds
dt_from = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(current_hour)), int((current_hour%1) * 60)) + timedelta(days=1)
current_hour = 0.0
return (hours/3600)
# last day: only add from beginning of working hours to ending time
elif last_day:
# no overlap (ending time before beginning of current slot)
if dt_to_hours <= hour_from:
continue
# ending time is after end of working hours: add the entire slot
elif dt_to_hours >= hour_to:
hours += hour_to - hour_from
# add only the overlap
else:
hours += dt_to_hours - hour_from
# complete day in the middle: add complete working hours
else:
hours += hour_to - hour_from
dt_from += timedelta(days=1)
dt_from = datetime(dt_from.year, dt_from.month, dt_from.day)
return hours
resource_calendar()