# -*- coding: utf-8 -*- ############################################################################## # # Clear Groups for Odoo # Copyright (C) 2016 Bytebrand GmbH (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser 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 Affero General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # ############################################################################## import datetime as dtime from datetime import datetime, timedelta from openerp import api, fields, models, _ from dateutil import rrule, parser from openerp.tools.translate import _ class HrTimesheetDh(models.Model): """ Addition plugin for HR timesheet for work with duty hours """ _inherit = 'hr_timesheet_sheet.sheet' @api.multi def _duty_hours(self): for sheet in self: sheet['total_duty_hours'] = 0.0 if sheet.state == 'done': sheet['total_duty_hours'] = sheet.total_duty_hours_done else: dates = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse(sheet.date_from), until=parser.parse(sheet.date_to))) period = {'date_from': sheet.date_from, 'date_to': sheet.date_to} for date_line in dates: duty_hours = sheet.calculate_duty_hours(date_from=date_line, period=period, ) sheet['total_duty_hours'] += duty_hours sheet['total_duty_hours'] = (sheet.total_duty_hours - sheet.total_attendance) @api.multi def count_leaves(self, date_from, employee_id, period): holiday_obj = self.env['hr.holidays'] start_leave_period = end_leave_period = False if period.get('date_from') and period.get('date_to'): start_leave_period = period.get('date_from') end_leave_period = period.get('date_to') holiday_ids = holiday_obj.search( ['|', '&', ('date_from', '>=', start_leave_period), ('date_from', '<=', end_leave_period), '&', ('date_to', '<=', end_leave_period), ('date_to', '>=', start_leave_period), ('employee_id', '=', employee_id), ('state', '=', 'validate'), ('type', '=', 'remove')]) leaves = [] for leave in holiday_ids: leave_date_from = datetime.strptime(leave.date_from, '%Y-%m-%d %H:%M:%S') leave_date_to = datetime.strptime(leave.date_to, '%Y-%m-%d %H:%M:%S') leave_dates = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse( leave.date_from), until=parser.parse(leave.date_to))) for date in leave_dates: if date.strftime('%Y-%m-%d') == date_from.strftime('%Y-%m-%d'): leaves.append( (leave_date_from, leave_date_to, leave.number_of_days)) break return leaves @api.multi def get_overtime(self, start_date): for sheet in self: if sheet.state == 'done': return sheet.total_duty_hours_done * -1 return self.calculate_diff(start_date) @api.multi def _overtime_diff(self): for sheet in self: old_timesheet_start_from = parser.parse( sheet.date_from) - timedelta(days=1) prev_timesheet_diff = \ self.get_previous_month_diff( sheet.employee_id.id, old_timesheet_start_from.strftime('%Y-%m-%d') ) sheet['calculate_diff_hours'] = ( self.get_overtime(datetime.today().strftime('%Y-%m-%d'), ) + prev_timesheet_diff) sheet['prev_timesheet_diff'] = prev_timesheet_diff @api.multi def _get_analysis(self): res = {} for sheet in self: function_call = True data = self.attendance_analysis(sheet.id, function_call) values = [] output = [ ''] for val in data.values(): if isinstance(val, (int, float)): output.append('') prev_ts = _('Previous Timesheet:') output.append('') output.append('') output.append('') for k, v in data.items(): if isinstance(v, list): output.append('') for th in v[0].keys(): output.append('') output.append('') for res in v: values.append(res.values()) for tr in values: output.append('') for td in tr: output.append('') output.append('') if isinstance(v, dict): output.append('') total_ts = _('Total:') output.append('') for td in v.values(): output.append('') output.append('') output.append('
' + prev_ts + ' ' + str(val) + '
' + th + '
' + td + '
' + total_ts + ' ' + '%s' % round(td, 4) + '
') sheet['analysis'] = '\n'.join(output) total_duty_hours = fields.Float(compute='_duty_hours', string='Total Duty Hours', multi="_duty_hours") total_duty_hours_done = fields.Float(string='Total Duty Hours', readonly=True, default=0.0) total_diff_hours = fields.Float(string='Total Diff Hours', readonly=True, default=0.0) calculate_diff_hours = fields.Char(compute='_overtime_diff', string="Diff (worked-duty)", multi="_diff") prev_timesheet_diff = fields.Char(compute='_overtime_diff', method=True, string="Diff from old", multi="_diff") analysis = fields.Text(compute='_get_analysis', type="text", string="Attendance Analysis") @api.multi def calculate_duty_hours(self, date_from, period): contract_obj = self.env['hr.contract'] calendar_obj = self.env['resource.calendar'] duty_hours = 0.0 contract_ids = contract_obj.search( [ ('employee_id', '=', self.employee_id.id), ('date_start', '<=', date_from), '|', ('date_end', '>=', date_from), ('date_end', '=', None) ] ) for contract in contract_ids: ctx = dict(self.env.context).copy() ctx.update(period) dh = calendar_obj.get_working_hours_of_date( cr=self._cr, uid=self.env.user.id, ids=contract.working_hours.id, start_dt=date_from, resource_id=self.employee_id.id, context=ctx) leaves = self.count_leaves(date_from, self.employee_id.id, period) if not leaves: if not dh: dh = 0.00 duty_hours += dh else: if leaves[-1] and leaves[-1][-1]: if float(leaves[-1][-1]) == (-0.5): duty_hours += dh / 2 return duty_hours @api.multi def get_previous_month_diff(self, employee_id, prev_timesheet_date_from): total_diff = 0.0 timesheet_ids = self.search( [('employee_id', '=', employee_id), ('date_from', '<', prev_timesheet_date_from) ]) for timesheet in timesheet_ids: total_diff += timesheet.get_overtime( start_date=prev_timesheet_date_from) return total_diff @api.multi def _get_user_datetime_format(self): """ Get user's language & fetch date/time formats of that language """ lang_obj = self.env['res.lang'] language = self.env.user.lang lang_ids = lang_obj.search([('code', '=', language)]) date_format = _('%Y-%m-%d') time_format = _('%H:%M:%S') for lang in lang_ids: date_format = lang.date_format time_format = lang.time_format return date_format, time_format @api.multi def attendance_analysis(self, timesheet_id=None, function_call=False): attendance_obj = self.env['hr.attendance'] date_format, time_format = self._get_user_datetime_format() for sheet in self: if sheet.id == timesheet_id: employee_id = sheet.employee_id.id start_date = sheet.date_from end_date = sheet.date_to previous_month_diff = self.get_previous_month_diff( employee_id, start_date) current_month_diff = previous_month_diff res = { 'previous_month_diff': previous_month_diff, 'hours': [] } period = {'date_from': start_date, 'date_to': end_date } dates = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse(start_date), until=parser.parse( end_date))) work_current_month_diff = 0.0 total = {'worked_hours': 0.0, 'duty_hours': 0.0, 'diff': current_month_diff, 'work_current_month_diff': ''} for date_line in dates: dh = sheet.calculate_duty_hours(date_from=date_line, period=period, ) worked_hours = 0.0 for att in sheet.period_ids: if att.name == date_line.strftime('%Y-%m-%d'): worked_hours = att.total_attendance diff = worked_hours - dh current_month_diff += diff work_current_month_diff += diff if function_call: res['hours'].append({ _('Date'): date_line.strftime(date_format), _('Duty Hours'): attendance_obj.float_time_convert(dh), _('Worked Hours'): attendance_obj.float_time_convert(worked_hours), _('Difference'): self.sign_float_time_convert(diff), _('Running'): self.sign_float_time_convert( current_month_diff)}) else: res['hours'].append({ 'name': date_line.strftime(date_format), 'dh': attendance_obj.float_time_convert(dh), 'worked_hours': attendance_obj.float_time_convert( worked_hours), 'diff': self.sign_float_time_convert(diff), 'running': self.sign_float_time_convert( current_month_diff) }) total['duty_hours'] += dh total['worked_hours'] += worked_hours total['diff'] += diff total['work_current_month_diff'] = work_current_month_diff res['total'] = total return res @api.multi def sign_float_time_convert(self, float_time): sign = '-' if float_time < 0 else '' attendance_obj = self.pool.get('hr.attendance') return sign + attendance_obj.float_time_convert(float_time) @api.multi def write(self, vals): if 'state' in vals and vals['state'] == 'done': vals['total_diff_hours'] = self.calculate_diff(None) for sheet in self: vals['total_duty_hours_done'] = sheet.total_duty_hours elif 'state' in vals and vals['state'] == 'draft': vals['total_diff_hours'] = 0.0 res = super(HrTimesheetDh, self).write(vals) return res @api.multi def calculate_diff(self, end_date=None): for sheet in self: return sheet.total_duty_hours * (-1)