X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fhr_timesheet_sheet%2Fhr_timesheet_sheet.py;h=1f948a557201fc7d0c0836423dee7a91ce03f8c9;hb=3e62ee3fa621893ff961a765cebfde4540f06b58;hp=6965274dca1c39cbf3d2cc3dcdad2ca9cb21b3fd;hpb=eaa876bb450b8c61d4f5eadc243b413914a61b43;p=odoo%2Fodoo.git diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index 6965274..1f948a5 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -1,78 +1,92 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2008 Tiny SPRL (). All Rights Reserved -# $Id$ +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2010 Tiny SPRL (). # # This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# GNU Affero General Public License for more details. # -# You should have received a copy of the GNU General Public License +# You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## import time -from osv import fields -from osv import osv -import netsvc +from datetime import datetime +from dateutil.relativedelta import relativedelta, MO -from mx import DateTime +from osv import fields, osv from tools.translate import _ - +import netsvc class one2many_mod2(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): - res = {} - for id in ids: - res[id] = [] - - res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], context) - res6 = {} - for r in res5: - res6[r['id']] = (r['date_current'], r['user_id'][0]) + def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + if context is None: + context = {} - ids2 = [] - for id in ids: - dom = [] + if values is None: + values = {} + + # 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: - dom = [('name', '>=', res6[id][0] + ' 00:00:00'), - ('name', '<=', res6[id][0] + ' 23:59:59'), - ('employee_id.user_id', '=', res6[id][1])] - ids2.extend(obj.pool.get(self._obj).search(cr, user, - dom, limit=self._limit)) + if c: # skip first + dom.insert(0 ,'|') + dom.append('&') + dom.append('&') + dom.append(('name', '>=', res6[id][0])) + dom.append(('name', '<=', res6[id][0])) + dom.append(('employee_id.user_id', '=', res6[id][1])) - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_write'): + ids2 = 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_write'): if r[self._fields_id]: - res.setdefault(r[self._fields_id][0], []).append(r['id']) + res[r[self._fields_id][0]].append(r['id']) return res def set(self, cr, obj, id, field, values, user=None, context=None): if context is None: context = {} + context = context.copy() context['sheet_id'] = id - return super(one2many_mod2, self).set(cr, obj, id, field, values, user=user, - context=context) + return super(one2many_mod2, self).set(cr, obj, id, field, values, user=user, context=context) class one2many_mod(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context={}, values={}): - res = {} - for id in ids: - res[id] = [] + def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + if context is None: + context = {} + + if values is None: + values = {} - res5 = obj.read(cr, user, ids, ['date_current', 'user_id'], 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]) @@ -84,11 +98,13 @@ class one2many_mod(fields.one2many): 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_write'): if r[self._fields_id]: - res.setdefault(r[self._fields_id][0], []).append(r['id']) + res[r[self._fields_id][0]].append(r['id']) return res @@ -96,36 +112,47 @@ class hr_timesheet_sheet(osv.osv): _name = "hr_timesheet_sheet.sheet" _table = 'hr_timesheet_sheet_sheet' _order = "id desc" + _description="Timesheet" - def _total_day(self, cr, uid, ids, name, args, context): - field_name = name.strip('_day') - cr.execute('SELECT sheet.id, day.' + field_name +' \ + def _total_day(self, cr, uid, ids, name, args, context=None): + res = {} + 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 (' + ','.join([str(x) for x in ids]) + ')') - return dict(cr.fetchall()) + 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): - cr.execute('SELECT s.id, COALESCE(SUM(d.' + name + '),0) \ + def _total(self, cr, uid, ids, name, args, context=None): + res = {} + 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 ('+ ','.join(map(str, ids)) + ') \ - GROUP BY s.id') - return dict(cr.fetchall()) + 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 _state_attendance(self, cr, uid, ids, name, args, context): + def _state_attendance(self, cr, uid, ids, name, args, context=None): + emp_obj = self.pool.get('hr.employee') result = {} link_emp = {} emp_ids = [] - emp_obj = self.pool.get('hr.employee') - for sheet in self.browse(cr, uid, ids, context): + for sheet in self.browse(cr, uid, ids, context=context): result[sheet.id] = 'none' emp_ids2 = emp_obj.search(cr, uid, - [('user_id', '=', sheet.user_id.id)]) + [('user_id', '=', sheet.user_id.id)], context=context) if emp_ids2: link_emp[emp_ids2[0]] = sheet.id emp_ids.append(emp_ids2[0]) @@ -135,8 +162,35 @@ class hr_timesheet_sheet(osv.osv): result[sheet_id] = emp.state return result - def button_confirm(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): + def check_employee_attendance_state(self, cr, uid, sheet_id, context=None): + ids_signin = self.pool.get('hr.attendance').search(cr,uid,[('sheet_id', '=', sheet_id),('action','=','sign_in')]) + ids_signout = self.pool.get('hr.attendance').search(cr,uid,[('sheet_id', '=', sheet_id),('action','=','sign_out')]) + + if len(ids_signin) != len(ids_signout): + raise osv.except_osv(('Warning !'),_('The timesheet cannot be validated as it does not contain equal no. of sign ins and sign outs!')) + return True + + 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: + raise osv.except_osv(_('Error !'), _('You cannot create a timesheet for an employee that does not have any user defined !')) + return super(hr_timesheet_sheet, self).create(cr, uid, vals, *args, **argv) + + def write(self, cr, uid, ids, vals, *args, **argv): + if 'employee_id' in vals: + new_user_id = self.pool.get('hr.employee').browse(cr, uid, vals['employee_id']).user_id.id or False + if not new_user_id: + raise osv.except_osv(_('Error !'), _('You cannot create a timesheet for an employee that does not have any user defined !')) + if not self._sheet_date(cr, uid, ids, forced_user_id=new_user_id): + raise osv.except_osv(_('Error !'), _('You can not have 2 timesheets that overlaps !\nPlease use the menu \'My Current Timesheet\' to avoid this problem.')) + return super(hr_timesheet_sheet, self).write(cr, uid, ids, vals, *args, **argv) + + def button_confirm(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + self.check_employee_attendance_state(cr, uid, sheet.id, context) di = sheet.user_id.company_id.timesheet_max_difference if (abs(sheet.total_difference) < di) or not di: wf_service = netsvc.LocalService("workflow") @@ -145,131 +199,148 @@ class hr_timesheet_sheet(osv.osv): raise osv.except_osv(_('Warning !'), _('Please verify that the total difference of the sheet is lower than %.2f !') %(di,)) return True - def date_today(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.now() <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) - elif DateTime.now() >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + def date_today(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + if datetime.today() <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) + elif datetime.now() >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) else: - self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}) + self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}, context=context) return True - def date_previous(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) + def date_previous(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + if datetime.strptime(sheet.date_current, '%Y-%m-%d') <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) else: self.write(cr, uid, [sheet.id], { - 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=-1)).strftime('%Y-%m-%d'), - }) + 'date_current': (datetime.strptime(sheet.date_current, '%Y-%m-%d') + relativedelta(days=-1)).strftime('%Y-%m-%d'), + }, context=context) return True - def date_next(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + def date_next(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + if datetime.strptime(sheet.date_current, '%Y-%m-%d') >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) else: self.write(cr, uid, [sheet.id], { - 'date_current': (DateTime.strptime(sheet.date_current, '%Y-%m-%d') + DateTime.RelativeDateTime(days=1)).strftime('%Y-%m-%d'), - }) + 'date_current': (datetime.strptime(sheet.date_current, '%Y-%m-%d') + relativedelta(days=1)).strftime('%Y-%m-%d'), + }, context=context) return True - def button_dummy(self, cr, uid, ids, context): - for sheet in self.browse(cr, uid, ids, context): - if DateTime.strptime(sheet.date_current, '%Y-%m-%d') <= DateTime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}) - elif DateTime.strptime(sheet.date_current, '%Y-%m-%d') >= DateTime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}) + def button_dummy(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + if datetime.strptime(sheet.date_current, '%Y-%m-%d') <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) + elif datetime.strptime(sheet.date_current, '%Y-%m-%d') >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): + self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) return True - def sign_in(self, cr, uid, ids, context): - if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): - raise osv.except_osv(_('Error !'), _('You can not sign in from an other date than today')) + def sign(self, cr, uid, ids, typ, context=None): emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) + sheet = self.browse(cr, uid, ids, context=context)[0] + if context is None: + context = {} + if not sheet.date_current == time.strftime('%Y-%m-%d'): + raise osv.except_osv(_('Error !'), _('You can not sign in from an other date than today')) + emp_id = sheet.employee_id.id context['sheet_id']=ids[0] - success = emp_obj.sign_in(cr, uid, emp_id, context=context) + emp_obj.attendance_action_change(cr, uid, [emp_id], type=typ, context=context,) return True - def sign_out(self, cr, uid, ids, context): - if not self.browse(cr, uid, ids, context)[0].date_current == time.strftime('%Y-%m-%d'): - raise osv.except_osv(_('Error !'), _('You can not sign out from an other date than today')) - emp_obj = self.pool.get('hr.employee') - emp_id = emp_obj.search(cr, uid, [('user_id', '=', uid)]) - context['sheet_id']=ids[0] - success = emp_obj.sign_out(cr, uid, emp_id, context=context) - return True + def sign_in(self, cr, uid, ids, context=None): + return self.sign(cr,uid,ids,'sign_in',context=None) + + def sign_out(self, cr, uid, ids, context=None): + return self.sign(cr,uid,ids,'sign_out',context=None) _columns = { - 'name': fields.char('Description', size=64, select=1), - 'user_id': fields.many2one('res.users', 'User', required=True, select=1), + 'name': fields.char('Description', size=64, select=1, + states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}), + 'employee_id': fields.many2one('hr.employee', 'Employee', required=True), + 'user_id': fields.related('employee_id', 'user_id', type="many2one", relation="res.users", store=True, string="User", required=False, readonly=True),#fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}), 'date_from': fields.date('Date from', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), 'date_to': fields.date('Date to', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), - 'date_current': fields.date('Current date', required=True), + 'date_current': fields.date('Current date', required=True, select=1), 'timesheet_ids' : one2many_mod('hr.analytic.timesheet', 'sheet_id', 'Timesheet lines', domain=[('date', '=', time.strftime('%Y-%m-%d'))], readonly=True, states={ 'draft': [('readonly', False)], 'new': [('readonly', False)]} ), - 'attendances_ids' : one2many_mod2('hr.attendance', 'sheet_id', 'Attendances', readonly=True, states={'draft':[('readonly',False)],'new':[('readonly',False)]}), - 'state' : fields.selection([('new', 'New'),('draft','Draft'),('confirm','Confirmed'),('done','Done')], 'Status', select=True, required=True, readonly=True), + 'attendances_ids' : one2many_mod2('hr.attendance', 'sheet_id', 'Attendances', readonly=True,), + 'state' : fields.selection([ + ('new', 'New'), + ('draft','Draft'), + ('confirm','Confirmed'), + ('done','Done')], 'State', select=True, required=True, readonly=True, + help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed timesheet. \ + \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.function(_state_attendance, method=True, type='selection', selection=[('absent', 'Absent'), ('present', 'Present'),('none','No employee defined')], string='Current Status'), - 'total_attendance_day': fields.function(_total_day, method=True, string='Total Attendance'), - 'total_timesheet_day': fields.function(_total_day, method=True, string='Total Timesheet'), - 'total_difference_day': fields.function(_total_day, method=True, string='Difference'), - 'total_attendance': fields.function(_total, method=True, string='Total Attendance'), - 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet'), - 'total_difference': fields.function(_total, method=True, string='Difference'), + 'total_attendance_day': fields.function(_total_day, method=True, string='Total Attendance', multi="_total_day"), + 'total_timesheet_day': fields.function(_total_day, method=True, string='Total Timesheet', multi="_total_day"), + 'total_difference_day': fields.function(_total_day, method=True, string='Difference', multi="_total_day"), + 'total_attendance': fields.function(_total, method=True, string='Total Attendance', multi="_total_sheet"), + 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet', multi="_total_sheet"), + 'total_difference': fields.function(_total, method=True, 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'), + 'department_id':fields.many2one('hr.department','Department'), } - def _default_date_from(self,cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context) + def _default_date_from(self,cr, uid, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) r = user.company_id and user.company_id.timesheet_range or 'month' if r=='month': return time.strftime('%Y-%m-01') elif r=='week': - return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Monday,0))).strftime('%Y-%m-%d') + return (datetime.today() + relativedelta(weekday=MO(-1))).strftime('%Y-%m-%d') elif r=='year': return time.strftime('%Y-01-01') return time.strftime('%Y-%m-%d') - def _default_date_to(self,cr, uid, context={}): - user = self.pool.get('res.users').browse(cr, uid, uid, context) + def _default_date_to(self,cr, uid, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) r = user.company_id and user.company_id.timesheet_range or 'month' if r=='month': - return (DateTime.now() + DateTime.RelativeDateTime(months=+1,day=1,days=-1)).strftime('%Y-%m-%d') + return (datetime.today() + relativedelta(months=+1,day=1,days=-1)).strftime('%Y-%m-%d') elif r=='week': - return (DateTime.now() + DateTime.RelativeDateTime(weekday=(DateTime.Sunday,0))).strftime('%Y-%m-%d') + return (datetime.today() + relativedelta(weekday=6)).strftime('%Y-%m-%d') elif r=='year': return time.strftime('%Y-12-31') return time.strftime('%Y-%m-%d') + def _default_employee(self,cr, uid, context=None): + emp_ids = self.pool.get('hr.employee').search(cr, uid, [('user_id','=',uid)], context=context) + return emp_ids and emp_ids[0] or False + _defaults = { - 'user_id': lambda self,cr,uid,c: uid, 'date_from' : _default_date_from, 'date_current' : lambda *a: time.strftime('%Y-%m-%d'), 'date_to' : _default_date_to, - 'state': lambda *a: 'new', + 'state': 'new', + 'employee_id': _default_employee, + 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'hr_timesheet_sheet.sheet', context=c) } - def _sheet_date(self, cr, uid, ids): - for sheet in self.browse(cr, uid, ids): - cr.execute('SELECT id \ + def _sheet_date(self, cr, uid, ids, forced_user_id=False, context=None): + for sheet in self.browse(cr, uid, ids, context=context): + new_user_id = forced_user_id or sheet.user_id and sheet.user_id.id + if new_user_id: + cr.execute('SELECT id \ FROM hr_timesheet_sheet_sheet \ WHERE (date_from < %s and %s < date_to) \ AND user_id=%s \ - AND id <> %s', (sheet.date_to, sheet.date_from, - sheet.user_id.id, sheet.id)) - if cr.fetchall(): - return False + AND id <> %s',(sheet.date_to, sheet.date_from, new_user_id, sheet.id)) + if cr.fetchall(): + return False return True - def _date_current_check(self, cr, uid, ids): - for sheet in self.browse(cr, uid, ids): + def _date_current_check(self, cr, uid, ids, context=None): + for sheet in self.browse(cr, uid, ids, context=context): if sheet.date_current < sheet.date_from or sheet.date_current > sheet.date_to: return False return True @@ -277,7 +348,7 @@ class hr_timesheet_sheet(osv.osv): _constraints = [ (_sheet_date, 'You can not have 2 timesheets that overlaps !\nPlease use the menu \'My Current Timesheet\' to avoid this problem.', ['date_from','date_to']), - (_date_current_check, 'You must select a Current date wich is in the timesheet dates !', ['date_current']), + (_date_current_check, 'You must select a Current date which is in the timesheet dates !', ['date_current']), ] def action_set_to_draft(self, cr, uid, ids, *args): @@ -287,12 +358,21 @@ class hr_timesheet_sheet(osv.osv): wf_service.trg_create(uid, self._name, id, cr) return True - def name_get(self, cr, uid, ids, context={}): + def name_get(self, cr, uid, ids, context=None): if not len(ids): return [] return [(r['id'], r['date_from'] + ' - ' + r['date_to']) \ for r in self.read(cr, uid, ids, ['date_from', 'date_to'], - context, load='_classic_write')] + context=context, load='_classic_write')] + + def unlink(self, cr, uid, ids, context=None): + sheets = self.read(cr, uid, ids, ['state','total_attendance'], context=context) + for sheet in sheets: + if sheet['state'] in ('confirm', 'done'): + raise osv.except_osv(_('Invalid action !'), _('Cannot delete Sheet(s) which are already confirmed !')) + elif sheet['total_attendance'] <> 0.00: + raise osv.except_osv(_('Invalid action !'), _('Cannot delete Sheet(s) which have attendance entries encoded !')) + return super(hr_timesheet_sheet, self).unlink(cr, uid, ids, context=context) hr_timesheet_sheet() @@ -300,14 +380,15 @@ hr_timesheet_sheet() class hr_timesheet_line(osv.osv): _inherit = "hr.analytic.timesheet" - def _get_default_date(self, cr, uid, context={}): + def _get_default_date(self, cr, uid, context=None): + if context is None: + context = {} if 'date' in context: return context['date'] return time.strftime('%Y-%m-%d') - def _sheet(self, cursor, user, ids, name, args, context): + def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - cursor.execute('SELECT l.id, COALESCE(MAX(s.id), 0) \ FROM hr_timesheet_sheet_sheet s \ LEFT JOIN (hr_analytic_timesheet l \ @@ -316,8 +397,7 @@ class hr_timesheet_line(osv.osv): ON (s.date_to >= al.date \ AND s.date_from <= al.date \ AND s.user_id = al.user_id) \ - WHERE l.id in (' + ','.join([str(x) for x in ids]) + ') \ - GROUP BY l.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(), @@ -332,7 +412,7 @@ class hr_timesheet_line(osv.osv): res[line_id] = False return res - def _sheet_search(self, cursor, user, obj, name, args): + 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') @@ -342,7 +422,7 @@ class hr_timesheet_line(osv.osv): 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])])) + [(fargs[1], args[i][1], args[i][2])], context=context)) i += 1 continue if isinstance(args[i][2], basestring): @@ -358,7 +438,7 @@ class hr_timesheet_line(osv.osv): 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], '%d')) + qu1.append('(s.id %s %s)' % (x[1], '%s')) qu2.append(x[2]) elif x[1] == 'in': if len(x[2]) > 0: @@ -394,7 +474,7 @@ class hr_timesheet_line(osv.osv): } def create(self, cr, uid, vals, *args, **kwargs): - if 'sheet_id' in vals: + if vals.get('sheet_id', False): ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, vals['sheet_id']) if not ts.state in ('draft', 'new'): raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) @@ -419,30 +499,31 @@ hr_timesheet_line() class hr_attendance(osv.osv): _inherit = "hr.attendance" - def _get_default_date(self, cr, uid, context={}): + def _get_default_date(self, cr, uid, context=None): + if context is None: + context = {} if 'name' in context: return context['name'] + time.strftime(' %H:%M:%S') return time.strftime('%Y-%m-%d %H:%M:%S') - def _sheet(self, cursor, user, ids, name, args, context): + def _sheet(self, cursor, user, ids, name, args, context=None): sheet_obj = self.pool.get('hr_timesheet_sheet.sheet') - - cursor.execute('SELECT a.id, COALESCE(MAX(s.id), 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 >= a.name \ + ON (s.date_to >= date_trunc('day',a.name) \ AND s.date_from <= a.name \ - AND s.user_id = e.user_id) \ - WHERE a.id in (' + ','.join([str(x) for x in ids]) + ') \ - GROUP BY a.id') + 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: @@ -451,17 +532,17 @@ class hr_attendance(osv.osv): res[line_id] = False return res - def _sheet_search(self, cursor, user, obj, name, args): + 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') + 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])])) + [(fargs[1], args[i][1], args[i][2])], context=context)) i += 1 continue if isinstance(args[i][2], basestring): @@ -477,7 +558,7 @@ class hr_attendance(osv.osv): 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], '%d')) + qu1.append('(s.id %s %s)' % (x[1], '%s')) qu2.append(x[2]) elif x[1] == 'in': if len(x[2]) > 0: @@ -494,9 +575,11 @@ class hr_attendance(osv.osv): LEFT JOIN (hr_attendance a \ LEFT JOIN hr_employee e \ ON (a.employee_id = e.id)) \ - ON (s.date_to >= a.name \ + 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 = e.user_id) ' + \ + AND s.user_id = r.user_id) ' + \ qu1, qu2) res = cursor.fetchall() if not len(res): @@ -512,15 +595,17 @@ class hr_attendance(osv.osv): 'name': _get_default_date, } - def create(self, cr, uid, vals, context={}): + def create(self, cr, uid, vals, context=None): + if context is None: + context = {} if 'sheet_id' in context: - ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, context['sheet_id']) + ts = self.pool.get('hr_timesheet_sheet.sheet').browse(cr, uid, context['sheet_id'], context=context) if ts.state not in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + raise osv.except_osv(_('Error !'), _('You cannot modify an entry in a confirmed timesheet !')) res = super(hr_attendance,self).create(cr, uid, vals, context=context) if 'sheet_id' in context: if context['sheet_id'] != self.browse(cr, uid, res, context=context).sheet_id.id: - raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ + raise osv.except_osv(_('UserError'), _('You cannot enter an attendance ' \ 'date outside the current timesheet dates!')) return res @@ -528,33 +613,35 @@ class hr_attendance(osv.osv): self._check(cr, uid, ids) return super(hr_attendance,self).unlink(cr, uid, ids,*args, **kwargs) - def write(self, cr, uid, ids, vals, context={}): + def write(self, cr, uid, ids, vals, context=None): + if context is None: + context = {} self._check(cr, uid, ids) res = super(hr_attendance,self).write(cr, uid, ids, vals, context=context) if 'sheet_id' in context: for attendance in self.browse(cr, uid, ids, context=context): if context['sheet_id'] != attendance.sheet_id.id: - raise osv.except_osv(_('UserError'), _('You can not enter an attendance ' \ + raise osv.except_osv(_('UserError'), _('You cannot enter an attendance ' \ 'date outside the current timesheet dates!')) return res def _check(self, cr, uid, ids): for att in self.browse(cr, uid, ids): if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): - raise osv.except_osv(_('Error !'), _('You can not modify an entry in a confirmed timesheet !')) + raise osv.except_osv(_('Error !'), _('You cannot modify an entry in a confirmed timesheet !')) return True hr_attendance() class hr_timesheet_sheet_sheet_day(osv.osv): _name = "hr_timesheet_sheet.sheet.day" - _description = "Timesheets by period" + _description = "Timesheets by Period" _auto = False _order='name' _columns = { 'name': fields.date('Date', readonly=True), 'sheet_id': fields.many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True, select="1"), - 'total_timesheet': fields.float('Project Timesheet', readonly=True), + 'total_timesheet': fields.float('Total Timesheet', readonly=True), 'total_attendance': fields.float('Attendance', readonly=True), 'total_difference': fields.float('Difference', readonly=True), } @@ -567,7 +654,7 @@ class hr_timesheet_sheet_sheet_day(osv.osv): sheet_id, total_timesheet, total_attendance, - (total_attendance - total_timesheet) AS total_difference + cast(round(cast(total_attendance - total_timesheet as Numeric),2) as Double Precision) AS total_difference FROM (( SELECT @@ -611,10 +698,12 @@ class hr_timesheet_sheet_sheet_day(osv.osv): from hr_attendance a LEFT JOIN (hr_timesheet_sheet_sheet s - LEFT JOIN hr_employee e - ON (s.user_id = e.user_id)) + LEFT JOIN resource_resource r + LEFT JOIN hr_employee e + ON (e.resource_id = r.id) + ON (s.user_id = r.user_id)) ON (a.employee_id = e.id - AND s.date_to >= a.name + AND s.date_to >= date_trunc('day',a.name) AND s.date_from <= a.name) WHERE action in ('sign_in', 'sign_out') group by a.name::date, s.id @@ -627,7 +716,7 @@ hr_timesheet_sheet_sheet_day() class hr_timesheet_sheet_sheet_account(osv.osv): _name = "hr_timesheet_sheet.sheet.account" - _description = "Timesheets by period" + _description = "Timesheets by Period" _auto = False _order='name' _columns = { @@ -664,13 +753,13 @@ class res_company(osv.osv): _inherit = 'res.company' _columns = { 'timesheet_range': fields.selection( - [('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timeshet range'), - 'timesheet_max_difference': fields.float('Timesheet allowed difference', - help="Allowed difference between the sign in/out and the timesheet " \ + [('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timesheet range'), + 'timesheet_max_difference': fields.float('Timesheet allowed difference(Hours)', + help="Allowed difference in hours between the sign in/out and the timesheet " \ "computation for one sheet. Set this to 0 if you do not want any control."), } _defaults = { - 'timesheet_range': lambda *args: 'month', + 'timesheet_range': lambda *args: 'week', 'timesheet_max_difference': lambda *args: 0.0 }