# -*- coding: utf-8 -*- ############################################################################## # # TicTac allows several HR functionalities. This program bases on Odoo v. 8. Copyright # (C) 2018 ITIS www.itis.de commissioned by Wikimedia Deutschland e.V. # # This program is free software: you can redistribute it and/or modify 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License along with # this program. If not, see . # ############################################################################## from openerp import models, api, fields, _ from openerp.tools import float_round from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT from openerp.exceptions import Warning from datetime import datetime, timedelta from calendar import monthrange from dateutil.relativedelta import relativedelta from openerp import http from openerp.addons.web.controllers import main as parent_controller from openerp.http import request, serialize_exception as _serialize_exception import logging import re logger = logging.getLogger(__name__) import pdb import logging logger = logging.getLogger(__name__) number_select = [(x,str('x')) for x in range(1,7)] class employee_overtime_count(models.Model): _name = 'employee.overtime.count' _rec_name = "emp_overtime_count" emp_overtime_count = fields.Float(string='Overtime Count') employee_id = fields.Many2one('hr.employee',string="Employee") class resource_calendar(models.Model): _inherit = 'resource.calendar' hourly_basis = fields.Boolean(string="Hourly Basis") class itis_contract_type(models.Model): _name = 'itis.contract.type' name = fields.Char(string="Art") class planned_job(models.Model): _name = 'planned.job' name = fields.Char(string="Planstellen") class itis_confession(models.Model): _name = 'itis_confession' name = fields.Char(string="Bezeichnung") class leave_time(models.Model): _name = 'leave.time' active = fields.Boolean(string="Active") fullday_start_time = fields.Integer(string="Start Time") fullday_end_time = fields.Integer(string="End Time") halfday_morning_start_time = fields.Integer(string="Start Time") halfday_morning_end_time = fields.Integer(string="End Time") halfday_afternoon_start_time = fields.Integer(string="Start Time") halfday_afternoon_end_time = fields.Integer(string="End Time") class itis_employee_children(models.Model): _name = 'itis_employee_children' name = fields.Char(string="Name") birth_date = fields.Date(string="Geburtsdatum") parent_id = fields.Many2one('hr.employee',string="Mitarbeiter") class itis_limitation_reason(models.Model): _name = 'itis_limitation_reason' name = fields.Char(string="Bezeichnung") class itis_hr_contact(models.Model): _name = 'itis.hr.contact' name = fields.Char(index=True, required=1) street = fields.Char() street2 = fields.Char() zip = fields.Char(change_default=True) city = fields.Char() state_id = fields.Many2one("res.country.state", string='State', ondelete='restrict') country_id = fields.Many2one('res.country', string='Country', ondelete='restrict') email = fields.Char() phone = fields.Char() fax = fields.Char() mobile = fields.Char() class itis_hr_employee_fte(models.Model): _name = 'hr.employee.fte' emp_id = fields.Many2one('hr.employee', string='Employee') department_id = fields.Many2one('hr.department', string='Department') fte = fields.Float(string="FTE", digits=(5, 3)) class health_insurance(models.Model): _name = 'hr.health.insurance' name = fields.Char(string='Name') class falimy_status(models.Model): _name = 'hr.family.status' name = fields.Char(string='Status') class hr_payroll_structure(models.Model): _inherit = 'hr.payroll.structure' base_on_hours = fields.Boolean(string="Base on Hours") class itis_hr_employee(models.Model): _inherit = 'hr.employee' surname = fields.Char(string="Vorname") second_name = fields.Char(string="Nachname") birth_name = fields.Char(string="Geburtsname") taxclass = fields.Selection(selection=[(1,'Steuerklasse 1'),(2,'Steuerklasse 2'),(3,'Steuerklasse 3'),(4,'Steuerklasse 4'),(5,'Steuerklasse 5'),(6,'Steuerklasse 6')], string="Steuerklasse") confession = fields.Many2one('itis_confession', string="Konfession") health_insurance = fields.Many2one('hr.health.insurance', string="Krankenkasse") disability = fields.Selection([('no','nein'),('yes','Ja')], string="Schwerbehinderung") disability_limited_until = fields.Date(string="Befristet bis") fte = fields.Float(string="FTE", compute="_compute_fte", digits=(5, 3)) sign_permission = fields.Char(string="Zeichnungsbefugnis") active_employee = fields.Boolean(string="Aktiv") emergency_contact = fields.Many2one('itis.hr.contact', string='Notfallkontakt') emergency_contact2 = fields.Many2one('itis.hr.contact', string='zweiter Notfallkontakt') children_ids = fields.One2many('itis_employee_children', 'parent_id', string='Kinder') five_years = fields.Date(string="5-Jahres-Jubiläum") bereich = fields.Many2one('hr.department', string="Bereich") overtime_count = fields.Float(string='Overtime Count') fte_ids = fields.One2many('hr.employee.fte', 'emp_id', string="FTEs") family_status = fields.Many2one('hr.family.status',string='Familienstand') initial_date = fields.Date(string='Ersteintrittsdatum') temp_contract_end_date = fields.Date(string='Enddatum befristeter Vertrag',compute="_compute_contract_end_date",multi="contract_end_date") wage_info = fields.Text(string='Wage Information') br_member = fields.Selection([('no','nein'),('yes','Ja')], string="BR Mitglied") position = fields.Char(string="Position") planned_job_id = fields.Many2one('planned.job',string='Planstellen') employee_meeting_id = fields.Many2one('employee.meeting',compute="_compute_next_emp_meeting",string='Next Meeting') employee_instruction_id = fields.Many2one('employee.instruction',compute="_compute_next_emp_instruction_meeting",string='Next Instruction Meeting') employee_overtime_id = fields.Many2one('employee.overtime.count',string="Overtime Count New") computed_overtime_count = fields.Float(string='Overtime Count',compute="_compute_overtime_count") executive_employee = fields.Boolean(string="Executive Employee") contract_type = fields.Many2one('hr.contract.type',string="Type", compute="get_active_contract_data", multi="contract_data") contract_wage = fields.Float(string="Wage", compute="get_active_contract_data", multi="contract_data") contract_leaves = fields.Float(string="Leaves", compute="get_active_contract_data", multi="contract_data") contract_notes = fields.Text(string="Notes", compute="get_active_contract_data", multi="contract_data") contract_limitation_reason = fields.Many2one('itis_limitation_reason',string="Reason of Limitation", compute="_compute_contract_end_date", multi="contract_end_date") contract_working_hours = fields.Many2one('resource.calendar',string="Working Hours", compute="get_active_contract_data", multi="contract_data") contract_trial_end_date = fields.Date(string="Trial End Date", compute="get_active_contract_data", multi="contract_data") last_ma_conversation_date = fields.Date(string='Last Meeting', compute="_compute_last_emp_meeting") # last_ma_conversation= fields.Many2one('employee.meeting',compute="_compute_last_emp_meeting",string='Last Meeting',help='Use to give most recent meeting in the past') compensation_at_vz = fields.Float(string="Compensation At VZ", compute="get_active_contract_data", multi="contract_data") remuneration_incl_ag_costs = fields.Float(string="Vergütung inkl. AG Kosten (25%)", compute="get_active_contract_data", multi="contract_data") last_contract_changed_wage = fields.Float(string="Last Contract Changed Wage", compute="get_active_contract_data", multi="contract_data") last_contract_changed_date = fields.Date(string="Last Contract Change Date", compute="get_active_contract_data", multi="contract_data") # the below field is use to calculate the employee wage base on the hour as well as normal wage.It called on the scheduler once in the day. emp_wage_cal = fields.Float(string="Wage") @api.model def get_monthly_employee_data(self): """Call from Scheduler, Use to generate monthly employee data report and send it via email to work council""" logger.info("---In the get_monthly_employee_data cron--") today_date = datetime.today().date() last_month_date = today_date - relativedelta(months= 1) hr_employee_brw = self.search([('executive_employee','=',False),('active','=',True)],order='id') if hr_employee_brw: logger.info("---Records to get %s" %hr_employee_brw) # call the subfunction to generate the .csv report context = self.env['employee.data.export'].with_context(for_work_council=True).export_csv_subfunction(hr_employee_brw) if context: # create attachement record of the related csv file ir_attachment = self.env['ir.attachment'] file_name ='employee_data_report_'+str(last_month_date)+'_'+str(today_date)+'.csv' attachment_data = { 'name': file_name, 'datas_fname':file_name, 'datas':context.get('default_name'), 'res_model': 'email.template', } ir_attachment_brw = ir_attachment.create(attachment_data) #template = self.env.ref('itis_hr_extend.email_template_monthly_employee_data', False) template = False #Added by IT IS to disable email sending. if ir_attachment_brw and template: logger.info("---Template found and send a mail") template.write({'attachment_ids': [(6, 0, [ir_attachment_brw.id])]}) # link a attachment to email template template.send_mail(hr_employee_brw[0].id)# send a mail with attachment template.write({'attachment_ids': [(3, ir_attachment_brw.id)]})# to unlink the attachment from template after sending a email @api.model def open_emp_data_tree(self): """Add view for the employee data report,called from the server action""" filter_employee_ids = [] today_date = datetime.today().date() hr_contract = self.env['hr.contract'] hr_employee_recs = self.env['hr.employee'].search([('user_id','!=',False),('parent_id','!=',False)]) if hr_employee_recs: for hr_employee_rec in hr_employee_recs: active_contract_brw = hr_contract.search([('employee_id','=',hr_employee_rec.id), ('date_start','<=',today_date),'|',('date_end','>=',today_date),('date_end','=',False)],limit=1) if active_contract_brw: filter_employee_ids.append(hr_employee_rec.id) if active_contract_brw.struct_id.base_on_hours: self.calculate_emp_wage(hr_employee_rec,active_contract_brw) else: hr_employee_rec.write({'emp_wage_cal':active_contract_brw.wage}) # print"filter_employee_ids----",filter_employee_ids domain = "[('id', 'in', " + str(filter_employee_ids) + ")]" tree_view_id = self.env.ref('itis_hr_extend.hr_emp_data_tree').id value = { 'domain': domain, 'name': 'Personalübersicht', 'view_type': 'form', 'view_mode': 'tree', 'res_model': 'hr.employee', 'view_id': tree_view_id, 'type': 'ir.actions.act_window' } return value def calculate_emp_wage(self,hr_employee_rec,active_contract_brw): """to calculate the monthly wage base on the hours salary structure """ logger.info("---hr_employee_rec %s" %hr_employee_rec) gross_salary = 0.0 hr_payslip = self.env['hr.payslip'] year = datetime.today().year month = datetime.today().month selected_month_range,num_day = monthrange(year,month) period_start_date = datetime.today().date().replace(day=1) period_end_date = datetime.today().date().replace(day=num_day) contract_date_start = datetime.strptime(active_contract_brw.date_start,'%Y-%m-%d').date() if active_contract_brw.date_end: contract_date_end = datetime.strptime(active_contract_brw.date_end,'%Y-%m-%d').date() else: contract_date_end = datetime.today().date() if contract_date_start <= period_start_date: period_contract_start_date =period_start_date else: period_contract_start_date =contract_date_start if contract_date_end >= period_end_date: period_contract_end_date =period_end_date else: period_contract_end_date =contract_date_end logger.info("---period_contract_start_date %s" %period_contract_start_date) logger.info("---period_contract_end_date %s" %period_contract_end_date) # print"period_contract_start_date---------",period_contract_start_date # print"period_contract_end_date---------",period_contract_end_date # #To check for the date and values if period_contract_start_date > period_contract_end_date: period_contract_end_date = period_contract_start_date #period_contract_end_date = period_contract_start_date period_contract_start_date = str(period_contract_start_date) period_contract_end_date = str(period_contract_end_date) payslip_values = {'employee_id':active_contract_brw.employee_id.id,'date_from':period_contract_start_date,'date_to':period_contract_end_date, 'contract_id':active_contract_brw.id,'struct_id':active_contract_brw.struct_id.id,'journal_id':1 } hr_payslip_brw=hr_payslip.sudo().create(payslip_values) res = self.pool.get('hr.payslip').onchange_employee_id(self._cr, self._uid, [], period_contract_start_date, period_contract_end_date, active_contract_brw.employee_id.id, active_contract_brw.id, None) if res and res.get('value'): value = res.get('value') worked_days_line_ids = value.get('worked_days_line_ids') for line_rec in worked_days_line_ids: line_rec.update({'payslip_id':hr_payslip_brw.id}) self.env['hr.payslip.worked_days'].create(line_rec) hr_payslip_brw.compute_sheet() for data in hr_payslip_brw.details_by_salary_rule_category: if data.code == "NET": gross_salary = data.total print"gross_salary-------",gross_salary if gross_salary: hr_employee_rec.write({'emp_wage_cal':gross_salary}) hr_payslip_brw.unlink() @api.one def get_active_contract_data(self): """ Functional fields to get the data from the currently active contract """ today_date = datetime.today().date() hr_contract = self.env['hr.contract'] # active_contract_brw = self.env['hr.contract'].search([('id','=',1)],limit=1) active_contract_brw = hr_contract.search([('employee_id','=',self.id),('date_start','<=',today_date),'|',('date_end','>=',today_date),('date_end','=',False)],limit=1) if active_contract_brw: self.contract_type = active_contract_brw.type_id.id self.contract_wage = active_contract_brw.wage self.contract_working_hours = active_contract_brw.working_hours self.contract_leaves = active_contract_brw.base_leaves # self.remuneration_incl_ag_costs = round(active_contract_brw.wage*1.25,2) self.remuneration_incl_ag_costs = round(self.emp_wage_cal*1.25,2) self.contract_notes = active_contract_brw.notes contract_trial_end_date_condition = False if not active_contract_brw.trial_date_end: contract_trial_end_date_condition = True if active_contract_brw.trial_date_end: trial_date_end = datetime.strptime(active_contract_brw.trial_date_end, DEFAULT_SERVER_DATE_FORMAT).date() if trial_date_end < datetime.today().date(): contract_trial_end_date_condition = True if contract_trial_end_date_condition: #search for the new contract new_contracts = hr_contract.search([('employee_id','=',self.id),('date_start','>',active_contract_brw.date_start)],order='date_start') for new_contract in new_contracts: if new_contract.trial_date_end: new_trial_date_end = datetime.strptime(new_contract.trial_date_end, DEFAULT_SERVER_DATE_FORMAT).date() if new_trial_date_end >= datetime.today().date(): self.contract_trial_end_date = new_contract.trial_date_end break else: self.contract_trial_end_date = active_contract_brw.trial_date_end if self.fte: # self.compensation_at_vz = round(active_contract_brw.wage/self.fte,2) self.compensation_at_vz = round(self.emp_wage_cal/self.fte,2) #Add a condition to search for the old contract and check for the change in the contract wage old_contracts = hr_contract.search([('employee_id','=',self.id),('date_start','<',active_contract_brw.date_start)],order='date_start desc') # print"old_contract-------",old_contracts,self.id middel_contracts =[] for old_contract in old_contracts: if old_contract.wage!= active_contract_brw.wage: self.last_contract_changed_wage =old_contract.wage # print"middel------",middel_contracts if middel_contracts: for middel_contract in middel_contracts: if middel_contract.wage != old_contract.wage: self.last_contract_changed_date = middel_contract.date_start # break else: self.last_contract_changed_date = active_contract_brw.date_start break else: middel_contracts.append(old_contract) @api.one def _compute_overtime_count(self): """computed field, to calculate overtime from the new field""" emp_overtime_count = 0.0 if self.employee_overtime_id: emp_overtime_count = self.employee_overtime_id.emp_overtime_count self.computed_overtime_count = emp_overtime_count @api.model def create(self,values): employee_overtime_count = self.env['employee.overtime.count'] res = super(itis_hr_employee, self).create(values) for hr_employee in res: emp_count_rec = employee_overtime_count.create({'employee_id':hr_employee.id,'emp_overtime_count':0.0}) hr_employee.write({'employee_overtime_id':emp_count_rec.id}) return res @api.model def update_new_overtime_field(self): """ Scheduler, Use to update new overtime field. """ employee_overtime_count = self.env['employee.overtime.count'] hr_employee = self.env['hr.employee'] emp_over_count_recs = employee_overtime_count.search([]) if emp_over_count_recs:emp_over_count_recs.unlink() for employee_rec in hr_employee.search([('user_id','!=',False)]): emp_count_rec = employee_overtime_count.create({'employee_id':employee_rec.id,'emp_overtime_count':employee_rec.overtime_count}) employee_rec.write({'employee_overtime_id':emp_count_rec.id}) @api.one def _compute_contract_end_date(self): """this function is to calculate the end date of all the contract when there is end date and no gap in contracts""" contract_end_date = False limitation_reason = False if self: hr_contract_record = self.env['hr.contract'].search([('employee_id','=',self.id)],order='date_end') if hr_contract_record: if len(hr_contract_record) ==1: if hr_contract_record[0].date_end: contract_end_date = hr_contract_record[0].date_end else: end_date=[] for hr_contract_brw in hr_contract_record: if hr_contract_brw.date_end: # end_date.append(hr_contract_brw.date_end) ###---new code ###----old code commented next_date_start = datetime.strptime(hr_contract_brw.date_end,DEFAULT_SERVER_DATE_FORMAT).date() + timedelta(days=1) hr_contract_record_new = self.env['hr.contract'].search([('employee_id','=',self.id),('date_start','=',next_date_start)],limit=1) if hr_contract_record_new: if hr_contract_record_new.date_end: end_date.append(hr_contract_record_new.date_end) else: end_date=[] else: end_date.append(hr_contract_brw.date_end) break ###---old code is commented END----## if end_date: end_date.sort(reverse=True) if datetime.strptime(end_date[0],DEFAULT_SERVER_DATE_FORMAT).date() >= datetime.today().date(): contract_end_date = end_date[0] if contract_end_date: hr_contract_record_new = self.env['hr.contract'].search([('employee_id','=',self.id),('date_end','=',contract_end_date)],limit=1) if hr_contract_record_new: limitation_reason = hr_contract_record_new.limitation_reason # print"contract_end_date------",contract_end_date self.contract_limitation_reason = limitation_reason self.temp_contract_end_date = contract_end_date @api.onchange("initial_date") def onchange_initial_date(self): """use to calculate 5 year date for the anniversary when initial date is set""" five_year_date =False if self.initial_date: date_start = datetime.strptime(self.initial_date,DEFAULT_SERVER_DATE_FORMAT).date() five_year_date = date_start + relativedelta(years=+5) self.five_years = five_year_date @api.one def _compute_last_emp_meeting(self): """ This computed function is use to find out the mosta recent employee meeting in the past base on the date :return:meeting id """ employee_meeting_env ,employee_meeting_brw= self.env['employee.meeting'],False employee_meeting_brw = employee_meeting_env.search([('employee_id','=',self.id),('meeting_date', '<=', datetime.today().date())],order='meeting_date desc') if employee_meeting_brw:employee_meeting_brw=employee_meeting_brw[0] self.last_ma_conversation_date=employee_meeting_brw.meeting_date @api.one def _compute_next_emp_meeting(self): """ This computed function is use to find out the next employee meeting base on the date :return:meeting id """ employee_meeting_env ,employee_meeting_brw= self.env['employee.meeting'],False employee_meeting_brw = employee_meeting_env.search([('employee_id','=',self.id),('meeting_date', '>=', datetime.today().date())],order='meeting_date') if employee_meeting_brw:employee_meeting_brw=employee_meeting_brw[0] self.employee_meeting_id=employee_meeting_brw @api.one def _compute_next_emp_instruction_meeting(self): """ This computed function is use to find out the next employee instruction meeting base on the date :return:instruction meeting id """ employee_instruction_env ,employee_instruction_brw= self.env['employee.instruction'],False employee_instruction_brw = employee_instruction_env.search([('employee_id','=',self.id),('meeting_date', '>=', datetime.today().date())],order='meeting_date') if employee_instruction_brw:employee_instruction_brw=employee_instruction_brw[0] self.employee_instruction_id=employee_instruction_brw @api.multi def open_ld_change(self): return { 'name': "Last Day Update", 'res_model': 'ot.change', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': {'from_ld_change': True} } @api.multi def open_ot_change(self): return { 'name': "Overtime Count Update", 'res_model': 'ot.change', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': {'from_ot_change': True} } @api.multi def open_leaved_day_change(self): return { 'name': "Leave Days Update", 'res_model': 'ld.change', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': {'current_year':True} } #for SOW17 @api.multi def open_nextyear_leaved_day_change(self): """Add a logic to give ability to change leave day for the next year. User can only change it after May month """ if datetime.today().month <6: raise Warning(_("You can only change next year leave after May month")) return { 'name': "Next Year Leave Days Update", 'res_model': 'ld.change', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': {'next_year':True} } #for SOW17 @api.multi def open_nextyear_add_day_change(self): """Add a logic to give ability to change additional leave day for the next year. User can only change it after May month """ if datetime.today().month <6: raise Warning(_("You can only change additional leave days for next year after May month")) return { 'name': "Last Day Update", 'res_model': 'ot.change', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'view_type': 'form', 'target': 'new', 'context': {'from_ld_change': True,'next_year':True} } @api.multi def update_overtime_count(self, ot_time, reason): # old_ot = self.overtime_count # self.write({'overtime_count': ot_time}) old_ot = self.employee_overtime_id.emp_overtime_count self.employee_overtime_id.write({'emp_overtime_count': ot_time}) leave_journal_obj = self.env['hr.leave.journal'] # ot_change = ot_time - self.overtime_count ot_change = ot_time - self.employee_overtime_id.emp_overtime_count values = { 'employee_id':self.id, 'year': datetime.today().year, 'year_type': 'actual', 'type': 'manual', 'leave_type':'hours', 'leave_days': ot_change, 'name': reason + ' ' + self.name + ' ' + datetime.now().strftime('%d%m%Y %H:%M:%S'), } leave_journal_obj.create(values) msg = _("Overtime has been chagned from %s to %s.
Reason : %s") % \ (str(old_ot), str(ot_time), reason) self.message_post(body=msg) return True @api.multi def update_leave_day(self, leave_day, reason): """Use to update additional leaves""" leave_journal_obj = self.env['hr.leave.journal'] old_additional_day = self.additional_leave_days # Added for SOW17 if self.env.context.get("next_year", False): year_type = 'next' year = datetime.today().year+1 old_additional_day = self.additional_leave_days_ny leave_days = leave_day - self.additional_leave_days_ny else: year_type = 'actual' year = datetime.today().year self.write({'additional_leave_days': leave_day}) leave_days = leave_day - self.additional_leave_days values = { 'employee_id':self.id, 'year': year, 'year_type': year_type, 'type': 'additional', 'leave_type':'days', 'leave_days': leave_days, 'name': reason + ' ' + self.name + ' ' + datetime.now().strftime('%d%m%Y %H:%M:%S'), } leave_journal_obj.create(values) msg = _("Additional leave has been changed from %s to %s.
Reason : %s") % \ (str(old_additional_day), str(leave_day), reason) self.message_post(body=msg) return True def onchange_department_id(self, cr, uid, ids, department_id, context=None): value = super(itis_hr_employee,self).onchange_department_id(cr, uid, ids, department_id, context=context) value['value']['bereich'] = False if department_id: department = self.pool.get('hr.department').browse(cr, uid, department_id) value['value']['bereich'] = department.parent_id.id return value @api.one def _compute_fte(self): contract_obj = self.pool.get('hr.contract') contract_ids = contract_obj.search(self._cr, self._uid, [('employee_id', '=', self.id), ('date_start', '<=', datetime.today().date()), '|', ('date_end', '>=', datetime.today().date()),('date_end','=',False)]) if len(contract_ids) < 1: self.fte = 0 elif len(contract_ids) > 1: self.fte = 0 raise Warning(_("There are 2 contracts assigned to this employee.")) else: work_plan = contract_obj.browse(self._cr, self._uid, contract_ids[0]).working_hours weekly_hours = 0.0 if not work_plan: self.fte = 0 for attendance_id in work_plan.attendance_ids: weekly_hours += attendance_id.hour_to - attendance_id.hour_from icp_obj = self.env['ir.config_parameter'].sudo() fte_base = icp_obj.get_param("fte.base") self.fte = float_round(weekly_hours / float(fte_base), precision_digits = 4) #add a logic for the 1stjune scheduler for leave calculation request on SOW17 @api.model def june_year_change_calc_days(self): """Use for calculation of the leave day for the next year base on the valid contract""" leave_journal_obj = self.env['hr.leave.journal'] contract_obj = self.env['hr.contract'] error_obj = self.env['itis.leave.days.calc.error'] logger.info("---------In the Jun calculation vacation cron--------") year = datetime.today().year #current year #2018 next_year = int(year) + 1 #next year #2019 for employee in self.search([('active','=',True)]): # print('employee--name----',employee.name) contract_ids_unlimited = contract_obj.search([('employee_id','=',employee.id),('date_start','<=',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), ('date_end','=',False)]) contract_ids_limited = contract_obj.search([('employee_id','=',employee.id),('date_start','<=',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), ('date_end', '>=', datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y'))]) contract_ids_future = contract_obj.search([('employee_id','=',employee.id),('date_start','>',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), '|', ('date_end', '>=', datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')),('date_end','=',False)]) error = False #contract_used = False #contract_id_used = False if len(contract_ids_limited) > 1 or len(contract_ids_unlimited) > 1: error = _('Mehr als ein Vertrag zum 01.Januar') elif not contract_ids_future: if not contract_ids_unlimited and len(contract_ids_limited)==1: leave_days_next_year = contract_ids_limited[0].calculate_leave_days(None,next_year)[0] #contract_used = contract_ids_limited[0] elif len(contract_ids_unlimited)==1 and not contract_ids_limited: leave_days_next_year = contract_ids_unlimited[0].calculate_leave_days(None,next_year)[0] #contract_used = contract_ids_unlimited[0] elif len(contract_ids_unlimited) > 0 and len(contract_ids_limited) > 0: error = _('Mehr als ein Vertrag zum 01.Januar') else: error = _('Kein Vertrag am 01.Januar vorhanden') else: if len(contract_ids_unlimited) > 0: error = _('Überschneidende Verträge') elif not contract_ids_limited and len(contract_ids_future) == 1: leave_days_next_year = contract_ids_future[0].calculate_leave_days(None,next_year)[0] #contract_used = contract_ids_future[0] else: answer = self.get_leave_days_from_multiple_contracts(contract_ids_limited.ids or [],contract_ids_future.ids or [],next_year) # print('answer-------',answer) if 'leave_days' in answer: leave_days_next_year = answer['leave_days'] elif 'error' in answer: error = answer['error'] else: error = _('Fehler in Berechnung') #if contract_used: # contract_id_used = contract_used.id if not error: values_next_year_actual = { 'employee_id': employee.id, 'year': next_year, 'year_type': 'next', 'type': 'calculate', 'leave_type':'days', 'leave_days': leave_days_next_year, 'name': _('Berechnung Urlaub zu Jahresbeginn') + str(datetime.today().date()), #'contract_id': contract_id_used, } leave_journal_obj.create(values_next_year_actual) else: values_error = { 'name': employee.id, 'year': next_year, 'error': error, } error_obj.create(values_error) pass @api.model # @api.multi def year_change_calc_days(self): """ Imp Note :: This scheduler should run in Dec month.Else remaining leave will have 0 value in it. """ leave_journal_obj = self.env['hr.leave.journal'] contract_obj = self.env['hr.contract'] error_obj = self.env['itis.leave.days.calc.error'] logger.info("---------In the year_change_calc_days cron--------") if datetime.today().month == 12 or datetime.today().month == 11: year = datetime.today().year else: year = int(datetime.today().year)-1 #2018 # year = datetime.today().year next_year = int(year) + 1 #2019 for employee in self.search([('active','=',True)]): # print('Employee Name-------',employee.name) remaining_leaves = employee.sum_leaves addtional_leave = employee.additional_leave_days_ny contract_leave = employee.leave_days_ny remaining_part_leave = 0.0 # remaining_part_leave2 = 0.0 #calculation for the last year leave and total vacation if employee.approved_leaves_ny >0.0: #check for the approved leave is there or not. #check there is approve leave till march, if it is there reduce it from remaining leave if employee.approved_leaves_till_march_ny >0.0: if remaining_leaves > employee.approved_leaves_till_march_ny: remaining_leaves -=employee.approved_leaves_till_march_ny else: #else get a part of it remaining_part_leave = employee.approved_leaves_till_march_ny-remaining_leaves remaining_leaves = 0.0 # # if employee.additional_leave_days_ny >0.0: #check for the additional leave # if remaining_part_leave <=employee.additional_leave_days_ny: # addtional_leave -=remaining_part_leave # remaining_part_leave =0.0 # else: # remaining_part_leave = remaining_part_leave - employee.additional_leave_days_ny # addtional_leave =0.0 # # contract_leave = employee.leave_days_ny - remaining_part_leave #check there is approve leave after march, if it is there add it in the remaining_part_leave if employee.approved_leaves_after_march_ny >0.0: remaining_part_leave += employee.approved_leaves_after_march_ny # if employee.approved_leaves_after_march_ny <=addtional_leave: # addtional_leave -=employee.approved_leaves_after_march_ny # remaining_part_leave2 =0.0 # else: # remaining_part_leave2 = employee.approved_leaves_after_march_ny - addtional_leave # addtional_leave =0.0 # # contract_leave = contract_leave - remaining_part_leave2 # print"Additional leave-------",addtional_leave # print"remaining_leaves-------",remaining_leaves # print"contract_leave-------",contract_leave # print"Approve leave-------",remaining_part_leave # print"sum leave----",contract_leave+remaining_leaves+addtional_leave-remaining_part_leave #for leave that is remaining in last year carry forward to next year #maintain for current year for info purpose #differentiate by last_year_carry_fwd values = { 'employee_id': employee.id, 'year': year, 'year_type': 'actual', 'type': 'leave', 'leave_type':'days', 'leave_days': employee.sum_leaves, 'last_year_carry_fwd':True, 'name': _('Resturlaub zum Jahreswechsel') + str(datetime.today().date()), } leave_journal_obj.create(values) #for leave that is remaining in last year carry forward to next year values_next_year_last = { 'employee_id': employee.id, 'year': next_year, 'year_type': 'last', 'type': 'calculate', 'leave_type':'days', 'leave_days': remaining_leaves, 'name': _('Umwandlung Resturlaub') + str(datetime.today().date()), } leave_journal_obj.create(values_next_year_last) #for leave base on contract calculated base on june scheduler values values_next_year_actual = { 'employee_id': employee.id, 'year': next_year, 'year_type': 'actual', 'type': 'calculate', 'leave_type':'days', 'leave_days': contract_leave, 'name': _('Berechnung Urlaub zu Jahresbeginn') + str(datetime.today().date()), } leave_journal_obj.create(values_next_year_actual) #to add following year additional leave to current year SOW17 if addtional_leave > 0.0: values_next_year_additional = { 'employee_id': employee.id, 'year': next_year, 'year_type': 'actual', 'type': 'additional', 'leave_type':'days', 'leave_days': addtional_leave, 'name': _('Berechnung Urlaub zu Jahresbeginn') + str(datetime.today().date()), } leave_journal_obj.create(values_next_year_additional) #for leave approve leave in following year which approve in preceding year carry forward values_next_year_last = { 'employee_id': employee.id, 'year': next_year, 'year_type': 'actual', 'type': 'leave', 'leave_type':'days', 'leave_days': remaining_part_leave, 'name': _('Abgegoltene Urlaubstage') + str(datetime.today().date()), } leave_journal_obj.create(values_next_year_last) #To send an email to employee for the remaining leave if remaining_leaves>0.0: template = self.env.ref('itis_hr_extend.email_template_remaining_leave_notification', False) if template: email_cc_list = self.get_cc_emails(employee) #to get a cc email address from hr manager, template.with_context(remaining_leaves=remaining_leaves,email_cc = email_cc_list).send_mail(employee.id) # Commented as requested on SOW17 # values_next_year_additional = { # 'employee_id': employee.id, # 'year': next_year, # 'year_type': 'actual', # 'type': 'additional', # 'leave_type':'days', # 'leave_days': employee.additional_leave_days, # 'name': _('Berechnung Urlaub zu Jahresbeginn') + str(datetime.today().date()), # } # leave_journal_obj.create(values_next_year_additional) #Below logic is commented as it is transfer to the 1st june scheduler SOW17 # leave_days_next_year = 0 # contract_ids_unlimited = contract_obj.search([('employee_id','=',employee.id),('date_start','<=',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), ('date_end','=',False)]) # contract_ids_limited = contract_obj.search([('employee_id','=',employee.id),('date_start','<=',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), ('date_end', '>=', datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y'))]) # contract_ids_future = contract_obj.search([('employee_id','=',employee.id),('date_start','>',datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')), '|', ('date_end', '>=', datetime.strptime('01-01-'+str(next_year),'%d-%m-%Y')),('date_end','=',False)]) # error = False # if len(contract_ids_limited) > 1 or len(contract_ids_unlimited) > 1: # error = _('Mehr als ein Vertrag zum 01.Januar') # elif not contract_ids_future: # if not contract_ids_unlimited and len(contract_ids_limited)==1: # leave_days_next_year = contract_ids_limited[0].calculate_leave_days(None,next_year)[0] # elif len(contract_ids_unlimited)==1 and not contract_ids_limited: # leave_days_next_year = contract_ids_unlimited[0].calculate_leave_days(None,next_year)[0] # elif len(contract_ids_unlimited) > 0 and len(contract_ids_limited) > 0: # error = _('Mehr als ein Vertrag zum 01.Januar') # else: # error = _('Kein Vertrag am 01.Januar vorhanden') # else: # if len(contract_ids_unlimited) > 0: # error = _('Überschneidende Verträge') # elif not contract_ids_limited and len(contract_ids_future) == 1: # leave_days_next_year = contract_ids_future[0].calculate_leave_days(None,next_year)[0] # else: # answer = self.get_leave_days_from_multiple_contracts(contract_ids_limited.ids or [],contract_ids_future.ids or [],next_year) # if 'leave_days' in answer: # leave_days_next_year = answer['leave_days'] # elif 'error' in answer: # error = answer['error'] # else: # error = _('Fehler in Berechnung') # if not error: # values_next_year_actual = { # 'employee_id': employee.id, # 'year': next_year, # 'year_type': 'actual', # 'type': 'calculate', # 'leave_type':'days', # 'leave_days': leave_days_next_year, # 'name': _('Berechnung Urlaub zu Jahresbeginn') + str(datetime.today().date()), # } # leave_journal_obj.create(values_next_year_actual) # else: # values_error = { # 'name': employee.id, # 'year': next_year, # 'error': error, # } # error_obj.create(values_error) return @api.model def get_leave_days_from_multiple_contracts(self,contracts1,contracts2,year): contract_obj = self.env['hr.contract'] values = {} error = False contracts1.extend(contracts2) contracts = contract_obj.browse(contracts1) if len(contracts) == 1: values['start_date'] = contracts[0].start_date values['end_date'] = contracts[0].start_date leave_days = contracts[0].calculate_leave_days(values,year)[0] elif len(contracts) > 1: contractvalues = {} for contract in contracts: workplan_days = 0 weekdays = {} for attendance in contract.working_hours.attendance_ids: if attendance.dayofweek not in weekdays: weekdays[attendance.dayofweek] = True workplan_days = len(weekdays) contractvalues[contract] = { 'date_start' : contract.date_start, 'date_end': contract.date_end, 'workplan_days': workplan_days, 'base_leaves': contract.base_leaves } contractvalues_sorted = sorted(contractvalues.items(), key=lambda x: x[1]['date_start']) date_start = False date_end = False workplan_days = False base_leaves = False for contract in contractvalues_sorted: if not workplan_days: workplan_days = contract[1]['workplan_days'] if not base_leaves: base_leaves = contract[1]['base_leaves'] if not date_start: date_start = contract[1]['date_start'] if not date_end: date_end = contract[1]['date_end'] else: if workplan_days == contract[1]['workplan_days']: if base_leaves == contract[1]['base_leaves']: if (datetime.strptime(contract[1]['date_start'], DEFAULT_SERVER_DATE_FORMAT)-datetime.strptime(date_end, DEFAULT_SERVER_DATE_FORMAT)).days ==1: date_end = contract[1]['date_end'] else: error = _('Fehler automatische Kalkulation durch keine durchgängigen Arbeitsverträge') break else: error = _('Fehler automatische Kalkulation durch Änderung Urlaubsanspruch') break else: error = _('Fehler automatische Kalkulation durch Änderung Wochenarbeitstage') break if not error: values = { 'date_start':date_start, } leave_days = contract[0].calculate_leave_days(values,year)[0] if error: return{'error':error} else: return {'leave_days':leave_days} @api.model def leave_request_state_change(self,employee): year = datetime.today().year leave_to_date = datetime.strptime('31-12-'+str(year),'%d-%m-%Y').date() leave_from_date = datetime.strptime('01-01-'+str(year),'%d-%m-%Y').date() leaves_ids =self.env['hr.holidays'].search([('employee_id','=',employee.id),('state','in',['draft','confirm'])]).ids logger.info("-Employee --------" +str(employee.id)) # print'employee----',employee.name # print'leaves_ids----',leaves_ids for leave_id in leaves_ids: leave = self.env['hr.holidays'].browse(leave_id) from_dt = datetime.strptime(leave.date_from, DEFAULT_SERVER_DATETIME_FORMAT).date() to_dt = datetime.strptime(leave.date_to, DEFAULT_SERVER_DATETIME_FORMAT).date() if leave_to_date >= to_dt and leave_from_date <= from_dt: if leave.state=='draft': logger.info("-draft------" +str(leave.id)) self._cr.execute("update hr_holidays set state = 'cancel' where id = %s",(leave.id,)) template = self.env.ref('itis_hr_extend.email_template_draft_leave_cancel', False) if template: email_cc_list = self.get_cc_emails(employee) #to get a cc email address from hr manager, template.with_context(date_from=from_dt,date_to =to_dt,email_cc = email_cc_list).send_mail(leave.id) if leave.state =='confirm': logger.info("-confirm------" +str(leave.id)) try: if leave.employee_id.parent_id: if leave.employee_id.parent_id.user_id: leave.sudo(user=leave.employee_id.parent_id.user_id.id).holidays_validate() else: leave.holidays_validate() except: pass if leave.state =='validate': template = self.env.ref('itis_hr_extend.email_template_toapprove_leave_approve', False) if template: email_cc_list = self.get_cc_emails(employee) #to get a cc email address from hr manager, template.with_context(date_from=from_dt,date_to =to_dt,email_cc = email_cc_list).send_mail(leave.id) @api.model def get_cc_emails(self,employee): groups = [self.env.ref('base.group_hr_manager').id] cc_list = [] groups_env = self.env['res.groups'] cc_emails='' for group in groups_env.browse(groups): for user in group.users: if user.login and user.email and re.match('[^@]+@[^@]+\.[^@]+', user.login): if employee and employee.user_id.id != user.id: cc_list.append(user) if employee.parent_id and employee.parent_id.user_id and employee.parent_id.user_id.email: if employee.parent_id.user_id not in cc_list: cc_list.append(employee.parent_id.user_id) # print"CC list--------",cc_list for i in cc_list: cc_emails += i.email+',' # print('cc_emails-----',cc_emails) return cc_emails @api.model def delete_last_year_leave_days(self): """This scheduler is of no use. Not sure why it is added. I have added a logic for the make draft leave of this year to cancel and to approve leave with approve. Imp Note :: This scheduler should run in Dec month. """ leave_journal_obj = self.env['hr.leave.journal'] contract_obj = self.env['hr.contract'] logger.info("---------In the delete last year leave cron--------") for employee in self.search([]): if not employee.active: continue to_delete_days = employee.leave_days_last_year - employee.approved_leaves if to_delete_days > 0: to_delete_days * -1 values = { 'employee_id': employee.id, 'year': datetime.today().year, 'year_type': 'actual', 'type': 'calculate', 'leave_type':'days', 'leave_days': to_delete_days, 'name': _('Verfall Resturlaub') + str(datetime.today().date()), } leave_journal_obj.create(values) #function use to make draft leave of last year rejected and to approve with approve state self.leave_request_state_change(employee) return class itis_hr_contract(models.Model): _inherit = 'hr.contract' itis_contract_type = fields.Many2one('itis.contract.type', string="Art") limitation_reason = fields.Many2one('itis_limitation_reason', string="Befristungsgrund") base_leaves = fields.Float(string="Urlaubsanspruch", default="20") class itis_hr_department(models.Model): _inherit = 'hr.department' planned_fte = fields.Float('Planned FTE') account_id = fields.Many2one('account.analytic.account',string="Analytic Account") def name_get(self, cr, uid, ids, context=None): if context is None: context = {} if not ids: return [] reads = self.read(cr, uid, ids, ['name','parent_id'], context=context) res = [] for record in reads: name = record['name'] # if record['parent_id']: # name = record['parent_id'][1]+' / '+name res.append((record['id'], name)) return res class itis_leave_days_calc_error(models.Model): _name = 'itis.leave.days.calc.error' name = fields.Many2one('hr.employee', string='Mitarbeiter') year = fields.Integer(string='Jahr') error = fields.Char(string='Fehlermeldung') class MyFilter(parent_controller.DataSet): @http.route(['/web/dataset/call_kw', '/web/dataset/call_kw/'], type='json', auth="user") def call_kw(self, model, method, args, kwargs, path=None): return_value = self._call_kw(model, method, args, kwargs) if model == "hr.employee": cr, uid, context = request.cr, request.uid, request.context if isinstance(return_value,list): for data in return_value: if isinstance(data,dict): if data.get("message_ids",False): res_users_obj = request.registry["res.users"] ir_model_obj = request.registry["ir.model.data"] res_user_data = res_users_obj.browse(cr,uid,[uid],context) xml_list = [] group_ids = [] for group in res_user_data.groups_id: group_ids.append(group.id) model_ids = ir_model_obj.search(cr,uid,[['model','=','res.groups'],['res_id','in',group_ids]],0,False,False,context) model_data = ir_model_obj.browse(cr,uid,model_ids,context) for value in model_data: xml_list.append(value.name) if "group_hr_manager" in xml_list or "group_hr_payroll_manager" in xml_list or "group_hr_user" in xml_list or uid == 1: continue else: data["message_ids"] = [] return return_value