# -*- coding: utf-8 -*- from dateutil.relativedelta import relativedelta from odoo import models, fields, api, _ from odoo.exceptions import ValidationError class HrPayrollRaise(models.Model): _name = 'hr.payroll.raise' _inherit = ['mail.thread', 'mail.activity.mixin'] _description = "Employee annual raises" _order = "employee_id asc, id desc" employee_id = fields.Many2one('hr.employee', 'Employee', required=True, domain=[('state', '=', 'open')]) application_date = fields.Date('Application Date', required=True) raise_type = fields.Selection(selection=[('annual', 'Annual'), ('discrimination', 'Discrimination'), ('exceptional', 'Exceptional') ], string='Raise Type', required=True, default='exceptional') margin = fields.Float('Time Margin') deviation = fields.Float('Deviation in Days', compute='_compute_deviation', store=True, default=0) scale_id = fields.Many2one('hr.payroll.structure', 'Scale', domain=[('type', '=', 'scale')]) level_id = fields.Many2one('hr.payroll.structure', 'Level', domain=[('type', '=', 'level')]) group_id = fields.Many2one('hr.payroll.structure', 'Group', domain=[('type', '=', 'group')]) degree_id = fields.Many2one('hr.payroll.structure', 'Degrees', domain=[('type', '=', 'degree')]) nominated_degree_id = fields.Many2one('hr.payroll.structure', 'Nominated Degree', domain=[('type', '=', 'degree')]) last_raise_date = fields.Date('Last Raise Date') next_raise_date = fields.Date('Next Raise Date') note = fields.Html('Notes') state = fields.Selection([('draft', 'Draft'), ('hr_officer', 'HR Officer'), ('confirm', 'HR Manager'), ('approve', 'Approved'), ('refuse', 'Refused')], 'State', default='draft', tracking=True) last_raises = fields.Boolean(string='Last Raise', default=True, readonly=True) current_salary = fields.Float(string='Current Salary', store=True) current_salary_insurance = fields.Float(string='Salary Insurance', store=True) percentage_bonus = fields.Boolean(string='Percentage Bonus') percentage_raises = fields.Float(string='Percentage Raises') new_salary = fields.Float(string='New Basic Salary', store=True) re_contract_id = fields.Many2one('hr.re.contract', 'Re-Contract', readonly=True) employee_appraisal = fields.Many2one('hr.employee.appraisal', string="Employee Appraisal", readonly=True, store=True) is_required = fields.Boolean(string='Is Required', compute='_compute_is_required') @api.depends('employee_id', 'employee_id.employee_type_id', 'employee_id.employee_type_id.salary_type') def _compute_is_required(self): for rec in self: rec.is_required = True if rec.employee_id and rec.employee_id.employee_type_id and rec.employee_id.employee_type_id.salary_type == 'amount': rec.is_required = False @api.onchange('employee_id', 'percentage_bonus', 'percentage_raises') def onchange_employee(self): self.nominated_degree_id = False if self.employee_id: self.employee_appraisal = self.employee_id.contract_id.appraisal_result.id self.scale_id = self.employee_id.salary_scale.id self.level_id = self.employee_id.salary_level.id self.group_id = self.employee_id.salary_group.id self.degree_id = self.employee_id.salary_degree.id self.last_raise_date = self.employee_id.degree_date self.current_salary = self.employee_id.contract_id.salary self.current_salary_insurance = self.employee_id.contract_id.salary_insurnce rs_dt = self.employee_id.degree_date and self.employee_id.degree_date or self.employee_id.first_hiring_date if rs_dt and self.employee_id.degree_date: ndate = fields.Date.from_string(rs_dt) + relativedelta(days=self.employee_id.salary_degree.time_margin ) or False self.next_raise_date = ndate if self.percentage_bonus == True: self.new_salary = round((self.current_salary * self.percentage_raises) / 100 + self.current_salary, 2) if self.employee_appraisal: self.percentage_raises = self.employee_appraisal.level_achieved_percentage * self.scale_id.Percentage_increase @api.onchange('nominated_degree_id') def onchange_degree(self): self.new_salary = self.nominated_degree_id.base_salary if self.employee_id and self.nominated_degree_id and self.degree_id and \ self.nominated_degree_id.sequence < self.degree_id.sequence: raise ValidationError(_('Sorry nominated degree %s is less than the current employee degree') % self.nominated_degree_id.name) @api.depends('nominated_degree_id', 'application_date') def _compute_deviation(self): for rec in self: if not rec.application_date: rec.deviation = 0 continue if rec.nominated_degree_id and rec.degree_id: days_to_raise = 0 degress_between = self.env['hr.payroll.structure'].search( [('salary_scale_level_id', '=', rec.level_id.id), ('salary_scale_group_id', '=', rec.group_id.id), ('salary_scale_id', '=', rec.scale_id.id), ('sequence', '>=', rec.degree_id.sequence), ('sequence', '<', rec.nominated_degree_id.sequence)]) for dg in degress_between: days_to_raise += dg.time_margin degree_date = rec.employee_id.degree_date and rec.employee_id.degree_date or \ rec.employee_id.first_hiring_date if not degree_date: rec.deviation = 0 continue passed_days = rec.employee_id and \ (fields.Date.from_string(rec.application_date) - fields.Date.from_string( degree_date)).days or 0 rec.deviation = passed_days - days_to_raise rs_dt = rec.employee_id.degree_date and rec.employee_id.degree_date or rec.employee_id.first_hiring_date if rs_dt and rec.employee_id.salary_degree: ndate = fields.Date.from_string(rs_dt) + relativedelta( days=rec.employee_id.salary_degree.time_margin) or False rec.next_raise_date = ndate else: rec.deviation = 0 def hr_officer(self): if self.percentage_bonus == True and self.percentage_raises <= 0: raise ValidationError(_('Sorry, The Percentage Of Bonus Increase Must Be Greater Than Zero')) self.state = 'hr_officer' def act_confirm(self): self.state = 'confirm' def act_approve(self): for rec in self: if not rec.percentage_bonus: rec.employee_id.degree_date = rec.application_date rec.employee_id.contract_id.salary_degree = rec.nominated_degree_id.id rec.employee_id.contract_id.salary = rec.nominated_degree_id.base_salary rec.employee_id.contract_id.salary_insurnce = rec.nominated_degree_id.base_salary last_raise = self.search([('employee_id', '=', rec.employee_id.id), ('id', '!=', rec.id), ('level_id', '=', rec.level_id.id), ('group_id', '=', rec.group_id.id), ('scale_id', '=', rec.scale_id.id), ('state', '=', 'approve')], order='application_date desc', limit=1) if last_raise: last_raise.last_raises = False else: if rec.percentage_bonus == True and rec.percentage_raises > 0: rec.employee_id.contract_id.salary = rec.new_salary rec.employee_id.contract_id.salary_insurnce = rec.employee_id.contract_id.salary rec.state = 'approve' def act_refuse(self): self.state = 'refuse' def act_reset(self): for rec in self: if not rec.last_raises: raise ValidationError(_('Sorry you can not set to Draft this Not last Raise')) rec.employee_id.contract_id.salary_degree = rec.degree_id.id last_raise = rec.search([('employee_id', '=', rec.employee_id.id), ('id', '!=', rec.id), ('level_id', '=', rec.level_id.id), ('group_id', '=', rec.group_id.id), ('scale_id', '=', rec.scale_id.id), ('state', '=', 'approve')], order='application_date desc', limit=1) rec.employee_id.degree_date = last_raise and last_raise.application_date or \ rec.employee_id.first_hiring_date rec.employee_id.contract_id.salary = rec.current_salary rec.employee_id.contract_id.salary_insurnce = rec.current_salary_insurance if last_raise: last_raise.last_raises = True rec.state = 'draft' def unlink(self): for rec in self: if rec.state != 'draft': raise ValidationError(_('Sorry you can not delete a record that is not in draft state')) return super(HrPayrollRaise, self).unlink() def write(self, vals): res = super(HrPayrollRaise, self).write(vals) if 'state' in vals: for rec in self: if rec.nomination_id: rec.nomination_id.check_raise_nominee() return res class hr_extend(models.Model): _inherit = 'hr.re.contract' raise_ids = fields.One2many('hr.payroll.raise', 're_contract_id', string="Employee Raise") @api.onchange('employee_id') def onchange_emp(self): for rec in self: if rec.raise_ids: for line in rec.raise_ids: if line.state != 'draft': raise ValidationError(_('You can not Change Employee Name Has Annual raise in state not in Draft')) else: rec.raise_ids = False def unlink(self): for i in self: if i.state != 'draft': raise ValidationError(_('You can not delete record in state not in draft')) if i.raise_ids: for rec in i.raise_ids: if rec.state != 'draft': raise ValidationError(_('You can not delete record Has Employee Annual raise in state not in Draft')) rec.unlink() return super(hr_extend, i).unlink()