From 9bda9ae8619a75fdb694beb80da35c43a8c6d97c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 9 Nov 2011 15:07:26 +0100 Subject: [PATCH] [FIX] revert change 5542, which makes tests fail bzr revid: rco@openerp.com-20111109140726-far79s7wwa1w9uty --- .../hr_timesheet_sheet/hr_timesheet_sheet.py | 404 +++++++++--------- .../hr_timesheet_sheet_view.xml | 43 +- 2 files changed, 203 insertions(+), 244 deletions(-) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index 07e0d024c24..089ade7c618 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -20,7 +20,7 @@ ############################################################################## import time -from datetime import datetime, timedelta +from datetime import datetime from dateutil.relativedelta import relativedelta from osv import fields, osv @@ -35,10 +35,16 @@ class one2many_mod2(fields.one2many): if values is None: values = {} - # res6: {id: date_current, ...} - res6 = dict([(rec['id'], rec['date_current']) - for rec in obj.read(cr, user, ids, ['date_current'], context=context)]) + # dict: + # {idn: (date_current, user_id), ... + # 1: ('2010-08-15', 1)} + res6 = dict([(rec['id'], (rec['date_current'], rec['user_id'][0])) + for rec + in obj.read(cr, user, ids, ['date_current', 'user_id'], context=context)]) + # eg: ['|', '|', + # '&', '&', ('name', '>=', '2011-03-01'), ('name', '<=', '2011-03-01'), ('employee_id.user_id', '=', 1), + # '&', '&', ('name', '>=', '2011-02-01'), ('name', '<=', '2011-02-01'), ('employee_id.user_id', '=', 1)] dom = [] for c, id in enumerate(ids): if id in res6: @@ -46,9 +52,9 @@ class one2many_mod2(fields.one2many): dom.insert(0 ,'|') dom.append('&') dom.append('&') - dom.append(('name', '>=', res6[id])) - dom.append(('name', '<=', res6[id])) - dom.append(('sheet_id', '=', id)) + dom.append(('name', '>=', res6[id][0])) + dom.append(('name', '<=', res6[id][0])) + dom.append(('employee_id.user_id', '=', res6[id][1])) ids2 = obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit) @@ -56,9 +62,10 @@ class one2many_mod2(fields.one2many): for i in ids: res[i] = [] - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_read'): + for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id]: res[r[self._fields_id][0]].append(r['id']) + return res def set(self, cr, obj, id, field, values, user=None, context=None): @@ -78,155 +85,62 @@ class one2many_mod(fields.one2many): if values is None: values = {} - res6 = dict([(rec['id'], rec['date_current']) - for rec in obj.read(cr, user, ids, ['date_current'], context=context)]) + + res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context=context) + res6 = {} + for r in res5: + res6[r['id']] = (r['date_current'], r['user_id'][0]) ids2 = [] for id in ids: dom = [] if id in res6: - dom = [('date', '=', res6[id]), ('sheet_id', '=', id)] + dom = [('date', '=', res6[id][0]), ('user_id', '=', res6[id][1])] ids2.extend(obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit)) res = {} for i in ids: res[i] = [] for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_read'): + [self._fields_id], context=context, load='_classic_write'): if r[self._fields_id]: res[r[self._fields_id][0]].append(r['id']) return res - class hr_timesheet_sheet(osv.osv): _name = "hr_timesheet_sheet.sheet" _table = 'hr_timesheet_sheet_sheet' _order = "id desc" _description="Timesheet" - def _total_attendances(self, cr, uid, ids, name, args, context=None): - """ - Get the total attendance for the timesheets - Returns a dict like : - {id: {'date_current': '2011-06-17', - 'totals_per_day': { - day: timedelta, - day: timedelta} - } - } - """ - context = context or {} - attendance_obj = self.pool.get('hr.attendance') + def _total_day(self, cr, uid, ids, name, args, context=None): res = {} - for sheet_id in ids: - sheet = self.browse(cr, uid, sheet_id, context) - date_current = sheet.date_current - # field attendances_ids of hr_timesheet_sheet.sheet only - # returns attendances of timesheet's current date - attendance_ids = attendance_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context) - attendances = attendance_obj.browse(cr, uid, attendance_ids, context=context) - total_attendance = {} - for attendance in [att for att in attendances - if att.action in ('sign_in', 'sign_out')]: - day = attendance.name[:10] - if not total_attendance.get(day, False): - total_attendance[day] = timedelta(seconds=0) - - attendance_in_time = datetime.strptime(attendance.name, '%Y-%m-%d %H:%M:%S') - attendance_interval = timedelta(hours=attendance_in_time.hour, - minutes=attendance_in_time.minute, - seconds=attendance_in_time.second) - if attendance.action == 'sign_in': - total_attendance[day] -= attendance_interval - else: - total_attendance[day] += attendance_interval - - # if the delta is negative, it means that a sign out is missing - # in a such case, we want to have the time to the end of the day - # for a past date, and the time to now for the current date - if total_attendance[day] < timedelta(0): - if day == date_current: - now = datetime.now() - total_attendance[day] += timedelta(hours=now.hour, - minutes=now.minute, - seconds=now.second) - else: - total_attendance[day] += timedelta(days=1) - - res[sheet_id] = {'date_current': date_current, - 'totals_per_day': total_attendance} - return res - - def _total_timesheet(self, cr, uid, ids, name, args, context=None): - """ - Get the total of analytic lines for the timesheets - Returns a dict like : - {id: {day: timedelta, - day: timedelta,}} - """ - context = context or {} - sheet_line_obj = self.pool.get('hr.analytic.timesheet') - - res = {} - for sheet_id in ids: - # field timesheet_ids of hr_timesheet_sheet.sheet only - # returns lines of timesheet's current date - sheet_lines_ids = sheet_line_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context) - sheet_lines = sheet_line_obj.browse(cr, uid, sheet_lines_ids, context=context) - total_timesheet = {} - for line in sheet_lines: - day = line.date - if not total_timesheet.get(day, False): - total_timesheet[day] = timedelta(seconds=0) - total_timesheet[day] += timedelta(hours=line.unit_amount) - res[sheet_id] = total_timesheet + cr.execute('SELECT sheet.id, day.total_attendance, day.total_timesheet, day.total_difference\ + FROM hr_timesheet_sheet_sheet AS sheet \ + LEFT JOIN hr_timesheet_sheet_sheet_day AS day \ + ON (sheet.id = day.sheet_id \ + AND day.name = sheet.date_current) \ + WHERE sheet.id IN %s',(tuple(ids),)) + for record in cr.fetchall(): + res[record[0]] = {} + res[record[0]]['total_attendance_day'] = record[1] + res[record[0]]['total_timesheet_day'] = record[2] + res[record[0]]['total_difference_day'] = record[3] return res def _total(self, cr, uid, ids, name, args, context=None): - """ - Compute the attendances, analytic lines timesheets and differences between them - for all the days of a timesheet and the current day - """ - def sum_all_days(sheet_amounts): - if not sheet_amounts: - return timedelta(seconds=0) - total = reduce(lambda memo, value: memo + value, sheet_amounts.values()) - return total - - def timedelta_to_hours(delta): - hours = delta.days * 24 + float(delta.seconds) / 3600 - if delta.microseconds: - hours += float(delta.microseconds) / 3600000000 - return hours - res = {} - all_timesheet_attendances = self._total_attendances(cr, uid, ids, name, args, context) - all_timesheet_lines = self._total_timesheet(cr, uid, ids, name, args, context) - for id in ids: - res[id] = {} - - all_attendances_sheet = all_timesheet_attendances[id] - - date_current = all_attendances_sheet['date_current'] - total_attendances_sheet = all_attendances_sheet['totals_per_day'] - total_attendances_all_days = sum_all_days(total_attendances_sheet) - total_attendances_day = total_attendances_sheet.get(date_current, timedelta(seconds=0)) - - total_timesheets_sheet = all_timesheet_lines[id] - total_timesheets_all_days = sum_all_days(total_timesheets_sheet) - total_timesheets_day = total_timesheets_sheet.get(date_current, timedelta(seconds=0)) - - total_difference_all_days = total_attendances_all_days - total_timesheets_all_days - total_difference_day = total_attendances_day - total_timesheets_day - - res[id]['total_attendance'] = timedelta_to_hours(total_attendances_all_days) - res[id]['total_timesheet'] = timedelta_to_hours(total_timesheets_all_days) - res[id]['total_difference'] = timedelta_to_hours(total_difference_all_days) - - res[id]['total_attendance_day'] = timedelta_to_hours(total_attendances_day) - res[id]['total_timesheet_day'] = timedelta_to_hours(total_timesheets_day) - res[id]['total_difference_day'] = timedelta_to_hours(total_difference_day) + cr.execute('SELECT s.id, COALESCE(SUM(d.total_attendance),0), COALESCE(SUM(d.total_timesheet),0), COALESCE(SUM(d.total_difference),0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN hr_timesheet_sheet_sheet_day d \ + ON (s.id = d.sheet_id) \ + WHERE s.id IN %s GROUP BY s.id',(tuple(ids),)) + for record in cr.fetchall(): + res[record[0]] = {} + res[record[0]]['total_attendance'] = record[1] + res[record[0]]['total_timesheet'] = record[2] + res[record[0]]['total_difference'] = record[3] return res def check_employee_attendance_state(self, cr, uid, sheet_id, context=None): @@ -240,7 +154,6 @@ class hr_timesheet_sheet(osv.osv): def copy(self, cr, uid, ids, *args, **argv): raise osv.except_osv(_('Error !'), _('You cannot duplicate a timesheet!')) - def create(self, cr, uid, vals, *args, **argv): if 'employee_id' in vals: if not self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).user_id: @@ -363,12 +276,12 @@ class hr_timesheet_sheet(osv.osv): \n* The \'Confirmed\' state is used for to confirm the timesheet by user. \ \n* The \'Done\' state is used when users timesheet is accepted by his/her senior.'), 'state_attendance' : fields.related('employee_id', 'state', type='selection', selection=[('absent', 'Absent'), ('present', 'Present')], string='Current Status', readonly=True), - 'total_attendance_day': fields.function(_total, method=True, string='Total Attendance', multi="_total"), - 'total_timesheet_day': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), - 'total_difference_day': fields.function(_total, method=True, string='Difference', multi="_total"), - 'total_attendance': fields.function(_total, method=True, string='Total Attendance', multi="_total"), - 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), - 'total_difference': fields.function(_total, method=True, string='Difference', multi="_total"), + 'total_attendance_day': fields.function(_total_day, string='Total Attendance', multi="_total_day"), + 'total_timesheet_day': fields.function(_total_day, string='Total Timesheet', multi="_total_day"), + 'total_difference_day': fields.function(_total_day, string='Difference', multi="_total_day"), + 'total_attendance': fields.function(_total, string='Total Attendance', multi="_total_sheet"), + 'total_timesheet': fields.function(_total, string='Total Timesheet', multi="_total_sheet"), + 'total_difference': fields.function(_total, string='Difference', multi="_total_sheet"), 'period_ids': fields.one2many('hr_timesheet_sheet.sheet.day', 'sheet_id', 'Period', readonly=True), 'account_ids': fields.one2many('hr_timesheet_sheet.sheet.account', 'sheet_id', 'Analytic accounts', readonly=True), 'company_id': fields.many2one('res.company', 'Company'), @@ -479,47 +392,85 @@ class hr_timesheet_line(osv.osv): def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - res = dict.fromkeys(ids, False) - for ts_line in self.browse(cursor, user, ids, context=context): - sheet_ids = sheet_obj.search(cursor, user, - [('date_to', '>=', ts_line.date), - ('date_from', '<=', ts_line.date), - ('employee_id.user_id', '=', ts_line.user_id.id)], context=context) - if sheet_ids: - # [0] because only one sheet possible for an employee between 2 dates - res[ts_line.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0] + cursor.execute('SELECT l.id, COALESCE(MAX(s.id), 0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_analytic_timesheet l \ + LEFT JOIN account_analytic_line al \ + ON (l.line_id = al.id)) \ + ON (s.date_to >= al.date \ + AND s.date_from <= al.date \ + AND s.user_id = al.user_id) \ + WHERE l.id IN %s GROUP BY l.id',(tuple(ids),)) + res = dict(cursor.fetchall()) + sheet_names = {} + for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), + context=context): + sheet_names[sheet_id] = name + + for line_id in {}.fromkeys(ids): + sheet_id = res.get(line_id, False) + if sheet_id: + res[line_id] = (sheet_id, sheet_names[sheet_id]) + else: + res[line_id] = False return res - def _get_hr_timesheet_sheet(self, cr, uid, ids, context=None): - ts_line_ids = [] - for ts in self.browse(cr, uid, ids, context=context): - cr.execute(""" - SELECT l.id - FROM hr_analytic_timesheet l - INNER JOIN account_analytic_line al - ON (l.line_id = al.id) - WHERE %(date_to)s >= al.date - AND %(date_from)s <= al.date - AND %(user_id)s = al.user_id - GROUP BY l.id""", {'date_from': ts.date_from, - 'date_to': ts.date_to, - 'user_id': ts.employee_id.user_id.id,}) - ts_line_ids.extend([row[0] for row in cr.fetchall()]) - return ts_line_ids + def _sheet_search(self, cursor, user, obj, name, args, context=None): + if not len(args): + return [] + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - def _get_account_analytic_line(self, cr, uid, ids, context=None): - ts_line_ids = self.pool.get('hr.analytic.timesheet').search(cr, uid, [('line_id', 'in', ids)]) - return ts_line_ids + i = 0 + while i < len(args): + fargs = args[i][0].split('.', 1) + if len(fargs) > 1: + args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, + [(fargs[1], args[i][1], args[i][2])], context=context)) + i += 1 + continue + if isinstance(args[i][2], basestring): + res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], + args[i][1]) + args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) + i += 1 + qu1, qu2 = [], [] + for x in args: + if x[1] != 'in': + if (x[2] is False) and (x[1] == '='): + qu1.append('(s.id IS NULL)') + elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): + qu1.append('(s.id IS NOT NULL)') + else: + qu1.append('(s.id %s %s)' % (x[1], '%s')) + qu2.append(x[2]) + elif x[1] == 'in': + if len(x[2]) > 0: + qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) + qu2 += x[2] + else: + qu1.append('(False)') + if len(qu1): + qu1 = ' WHERE ' + ' AND '.join(qu1) + else: + qu1 = '' + cursor.execute('SELECT l.id \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_analytic_timesheet l \ + LEFT JOIN account_analytic_line al \ + ON (l.line_id = al.id)) \ + ON (s.date_to >= al.date \ + AND s.date_from <= al.date \ + AND s.user_id = al.user_id)' + \ + qu1, qu2) + res = cursor.fetchall() + if not len(res): + return [('id', '=', '0')] + return [('id', 'in', [x[0] for x in res])] _columns = { 'sheet_id': fields.function(_sheet, string='Sheet', type='many2one', relation='hr_timesheet_sheet.sheet', - store={ - 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10), - 'account.analytic.line': (_get_account_analytic_line, ['user_id', 'date'], 10), - 'hr.analytic.timesheet': (lambda self,cr,uid,ids,c={}: ids, ['line_id'], 10), - }, - ), + fnct_search=_sheet_search), } _defaults = { 'date': _get_default_date, @@ -561,47 +512,90 @@ class hr_attendance(osv.osv): return context['name'] + time.strftime(' %H:%M:%S') return time.strftime('%Y-%m-%d %H:%M:%S') - def _get_hr_timesheet_sheet(self, cr, uid, ids, context=None): - attendance_ids = [] - for ts in self.browse(cr, uid, ids, context=context): - cr.execute(""" - SELECT a.id - FROM hr_attendance a - INNER JOIN hr_employee e - INNER JOIN resource_resource r - ON (e.resource_id = r.id) - ON (a.employee_id = e.id) - WHERE %(date_to)s >= date_trunc('day', a.name) - AND %(date_from)s <= a.name - AND %(user_id)s = r.user_id - GROUP BY a.id""", {'date_from': ts.date_from, - 'date_to': ts.date_to, - 'user_id': ts.employee_id.user_id.id,}) - attendance_ids.extend([row[0] for row in cr.fetchall()]) - return attendance_ids - def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - res = dict.fromkeys(ids, False) - for attendance in self.browse(cursor, user, ids, context=context): - date_to = datetime.strftime(datetime.strptime(attendance.name[0:10], '%Y-%m-%d'), '%Y-%m-%d %H:%M:%S') - sheet_ids = sheet_obj.search(cursor, user, - [('date_to', '>=', date_to), - ('date_from', '<=', attendance.name), - ('employee_id', '=', attendance.employee_id.id)], context=context) - if sheet_ids: - # [0] because only one sheet possible for an employee between 2 dates - res[attendance.id] = sheet_obj.name_get(cursor, user, sheet_ids, context=context)[0] + cursor.execute("SELECT a.id, COALESCE(MAX(s.id), 0) \ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_attendance a \ + LEFT JOIN hr_employee e \ + LEFT JOIN resource_resource r \ + ON (e.resource_id = r.id) \ + ON (a.employee_id = e.id)) \ + ON (s.date_to >= date_trunc('day',a.name) \ + AND s.date_from <= a.name \ + AND s.user_id = r.user_id) \ + WHERE a.id IN %s GROUP BY a.id",(tuple(ids),)) + res = dict(cursor.fetchall()) + sheet_names = {} + for sheet_id, name in sheet_obj.name_get(cursor, user, res.values(), + context=context): + sheet_names[sheet_id] = name + for line_id in {}.fromkeys(ids): + sheet_id = res.get(line_id, False) + if sheet_id: + res[line_id] = (sheet_id, sheet_names[sheet_id]) + else: + res[line_id] = False return res + def _sheet_search(self, cursor, user, obj, name, args, context=None): + if not len(args): + return [] + + sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') + i = 0 + while i < len(args): + fargs = args[i][0].split('.', 1) + if len(fargs) > 1: + args[i] = (fargs[0], 'in', sheet_obj.search(cursor, user, + [(fargs[1], args[i][1], args[i][2])], context=context)) + i += 1 + continue + if isinstance(args[i][2], basestring): + res_ids = sheet_obj.name_search(cursor, user, args[i][2], [], + args[i][1]) + args[i] = (args[i][0], 'in', [x[0] for x in res_ids]) + i += 1 + qu1, qu2 = [], [] + for x in args: + if x[1] != 'in': + if (x[2] is False) and (x[1] == '='): + qu1.append('(s.id IS NULL)') + elif (x[2] is False) and (x[1] == '<>' or x[1] == '!='): + qu1.append('(s.id IS NOT NULL)') + else: + qu1.append('(s.id %s %s)' % (x[1], '%s')) + qu2.append(x[2]) + elif x[1] == 'in': + if len(x[2]) > 0: + qu1.append('(s.id in (%s))' % (','.join(['%d'] * len(x[2])))) + qu2 += x[2] + else: + qu1.append('(False)') + if len(qu1): + qu1 = ' WHERE ' + ' AND '.join(qu1) + else: + qu1 = '' + cursor.execute('SELECT a.id\ + FROM hr_timesheet_sheet_sheet s \ + LEFT JOIN (hr_attendance a \ + LEFT JOIN hr_employee e \ + ON (a.employee_id = e.id)) \ + LEFT JOIN resource_resource r \ + ON (e.resource_id = r.id) \ + ON (s.date_to >= date_trunc(\'day\',a.name) \ + AND s.date_from <= a.name \ + AND s.user_id = r.user_id) ' + \ + qu1, qu2) + res = cursor.fetchall() + if not len(res): + return [('id', '=', '0')] + return [('id', 'in', [x[0] for x in res])] + _columns = { 'sheet_id': fields.function(_sheet, string='Sheet', type='many2one', relation='hr_timesheet_sheet.sheet', - store={ - 'hr_timesheet_sheet.sheet': (_get_hr_timesheet_sheet, ['employee_id', 'date_from', 'date_to'], 10), - 'hr.attendance': (lambda self,cr,uid,ids,c={}: ids, ['employee_id', 'name', 'day'], 10), - }, - ) + fnct_search=_sheet_search), } _defaults = { 'name': _get_default_date, diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 53ecb81ef4f..11e1789aa41 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -228,19 +228,6 @@ - - - hr.analytic.timesheet.search - hr.analytic.timesheet - form - - - - - - - - @@ -292,21 +279,14 @@ - - - - - hr.timesheet.day.tree - hr_timesheet_sheet.sheet.day - tree - - - - - - - - - -