tictac/itis_hr_extend/models/hr_employee.py

1158 lines
56 KiB
Python

# -*- 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 <https://www.gnu.org/licenses/>.
#
##############################################################################
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 <b>%s</b> to <b>%s</b>.<br/>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 <b>%s</b> to <b>%s</b>.<br/>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/<path:path>'], 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