Files

432 lines
29 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from datetime import date
from dateutil.relativedelta import relativedelta
from functools import reduce
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError, UserError
from odoo.tools import float_round
class HrEmployee(models.Model):
_inherit = 'hr.employee'
niss = fields.Char(
'NISS Number', compute="_compute_niss", store=True, readonly=False,
groups="hr.group_hr_user", tracking=True, index=True)
spouse_fiscal_status_explanation = fields.Char(compute='_compute_spouse_fiscal_status_explanation', groups="hr.group_hr_user")
start_notice_period = fields.Date("Start notice period", groups="hr.group_hr_user", copy=False, tracking=True)
end_notice_period = fields.Date("End notice period", groups="hr.group_hr_user", copy=False, tracking=True)
first_contract_in_company = fields.Date("First contract in company", groups="hr.group_hr_user", copy=False, store=True, compute='_compute_first_contract_in_company')
l10n_be_scale_seniority = fields.Integer(string="Seniority at Hiring", groups="hr.group_hr_user", tracking=True)
# The attestation for the year of the first contract date
first_contract_year_n = fields.Char(compute='_compute_first_contract_year', groups="hr_payroll.group_hr_payroll_user")
first_contract_year_n_plus_1 = fields.Char(compute='_compute_first_contract_year', groups="hr_payroll.group_hr_payroll_user")
l10n_be_holiday_pay_to_recover_n = fields.Float(
string="Simple Holiday Pay to Recover (N)", tracking=True, groups="hr_payroll.group_hr_payroll_user",
help="Amount of the holiday pay paid by the previous employer to recover.")
l10n_be_holiday_pay_number_of_days_n = fields.Float(
string="Number of days to recover (N)", tracking=True, groups="hr_payroll.group_hr_payroll_user",
help="Number of days on which you should recover the holiday pay.")
l10n_be_holiday_pay_recovered_n = fields.Float(
string="Recovered Simple Holiday Pay (N)",
compute='_compute_l10n_be_holiday_pay_recovered', groups="hr_payroll.group_hr_payroll_user",
help="Amount of the holiday pay paid by the previous employer already recovered.")
double_pay_line_n_ids = fields.Many2many(
'l10n.be.double.pay.recovery.line', 'double_pay_n_rel' 'employee_id', 'double_pay_line_n_ids',
compute='_compute_from_double_pay_line_ids', readonly=False,
inverse='_inverse_double_pay_line_n_ids',
string='Previous Occupations (N)', groups="hr_payroll.group_hr_payroll_user")
# The attestation for the previous year of the first contract date
first_contract_year_n1 = fields.Char(compute='_compute_first_contract_year', groups="hr_payroll.group_hr_payroll_user")
l10n_be_holiday_pay_to_recover_n1 = fields.Float(
string="Simple Holiday Pay to Recover (N-1)", tracking=True, groups="hr_payroll.group_hr_payroll_user",
help="Amount of the holiday pay paid by the previous employer to recover.")
l10n_be_holiday_pay_number_of_days_n1 = fields.Float(
string="Number of days to recover (N-1)", tracking=True, groups="hr_payroll.group_hr_payroll_user",
help="Number of days on which you should recover the holiday pay.")
l10n_be_holiday_pay_recovered_n1 = fields.Float(
string="Recovered Simple Holiday Pay (N-1)",
compute='_compute_l10n_be_holiday_pay_recovered', groups="hr_payroll.group_hr_payroll_user",
help="Amount of the holiday pay paid by the previous employer already recovered.")
double_pay_line_n1_ids = fields.Many2many(
'l10n.be.double.pay.recovery.line', 'double_pay_n1_rel' 'employee_id', 'double_pay_line_n1_ids',
compute='_compute_from_double_pay_line_ids', readonly=False,
inverse='_inverse_double_pay_line_n1_ids',
string='Previous Occupations (N-1)', groups="hr_payroll.group_hr_payroll_user")
first_contract_year = fields.Integer(compute='_compute_first_contract_year', groups="hr_payroll.group_hr_payroll_user")
double_pay_line_ids = fields.One2many(
'l10n.be.double.pay.recovery.line', 'employee_id',
string='Previous Occupations', groups="hr_payroll.group_hr_payroll_user")
fiscal_voluntarism = fields.Monetary(readonly=False, related="version_id.fiscal_voluntarism", inherited=True, groups="hr_payroll.group_hr_payroll_user")
transport_mode_car = fields.Boolean(readonly=False, related="version_id.transport_mode_car", inherited=True, groups="hr_payroll.group_hr_payroll_user")
transport_mode_private_car = fields.Boolean(readonly=False, related="version_id.transport_mode_private_car", inherited=True, groups="hr_payroll.group_hr_payroll_user")
transport_mode_train = fields.Boolean(readonly=False, related="version_id.transport_mode_train", inherited=True, groups="hr_payroll.group_hr_payroll_user")
transport_mode_public = fields.Boolean(readonly=False, related="version_id.transport_mode_public", inherited=True, groups="hr_payroll.group_hr_payroll_user")
car_atn = fields.Monetary(readonly=False, related="version_id.car_atn", inherited=True, groups="hr_payroll.group_hr_payroll_user")
train_transport_employee_amount = fields.Monetary(readonly=False, related="version_id.train_transport_employee_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
public_transport_employee_amount = fields.Monetary(readonly=False, related="version_id.public_transport_employee_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
warrant_value_employee = fields.Monetary(related="version_id.warrant_value_employee", inherited=True, groups="hr_payroll.group_hr_payroll_user")
meal_voucher_paid_by_employer = fields.Monetary(related="version_id.meal_voucher_paid_by_employer", inherited=True, groups="hr_payroll.group_hr_payroll_user")
meal_voucher_paid_monthly_by_employer = fields.Monetary(related="version_id.meal_voucher_paid_monthly_by_employer", inherited=True, groups="hr_payroll.group_hr_payroll_user")
company_car_total_depreciated_cost = fields.Monetary(readonly=False, related="version_id.company_car_total_depreciated_cost", inherited=True, groups="hr_payroll.group_hr_payroll_user")
private_car_reimbursed_amount = fields.Monetary(related="version_id.private_car_reimbursed_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
train_transport_reimbursed_amount = fields.Monetary(readonly=False, related="version_id.train_transport_reimbursed_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
public_transport_reimbursed_amount = fields.Monetary(readonly=False, related="version_id.public_transport_reimbursed_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
warrants_cost = fields.Monetary(related="version_id.warrants_cost", inherited=True, groups="hr_payroll.group_hr_payroll_user")
yearly_commission = fields.Monetary(related="version_id.yearly_commission", inherited=True, groups="hr_payroll.group_hr_payroll_user")
yearly_commission_cost = fields.Monetary(related="version_id.yearly_commission_cost", inherited=True, groups="hr_payroll.group_hr_payroll_user")
commission_on_target = fields.Monetary(readonly=False, related="version_id.commission_on_target", inherited=True, groups="hr_payroll.group_hr_payroll_user")
fuel_card = fields.Monetary(readonly=False, related="version_id.fuel_card", inherited=True, groups="hr_payroll.group_hr_payroll_user")
internet = fields.Monetary(readonly=False, related="version_id.internet", inherited=True, groups="hr_payroll.group_hr_payroll_user")
representation_fees = fields.Monetary(readonly=False, related="version_id.representation_fees", inherited=True, groups="hr_payroll.group_hr_payroll_user")
mobile = fields.Monetary(readonly=False, related="version_id.mobile", inherited=True, groups="hr_payroll.group_hr_payroll_user")
has_laptop = fields.Boolean(readonly=False, related="version_id.has_laptop", inherited=True, groups="hr_payroll.group_hr_payroll_user")
meal_voucher_amount = fields.Monetary(readonly=False, related="version_id.meal_voucher_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
meal_voucher_average_monthly_amount = fields.Monetary(related="version_id.meal_voucher_average_monthly_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
eco_checks = fields.Monetary(readonly=False, related="version_id.eco_checks", inherited=True, groups="hr_payroll.group_hr_payroll_user")
ip = fields.Boolean(readonly=False, related="version_id.ip", inherited=True, groups="hr_payroll.group_hr_payroll_user")
ip_wage_rate = fields.Float(readonly=False, related="version_id.ip_wage_rate", inherited=True, groups="hr_payroll.group_hr_payroll_user")
ip_value = fields.Float(related="version_id.ip_value", inherited=True, groups="hr_payroll.group_hr_payroll_user")
no_onss = fields.Boolean(readonly=False, related="version_id.no_onss", inherited=True, groups="hr_payroll.group_hr_payroll_user")
no_withholding_taxes = fields.Boolean(readonly=False, related="version_id.no_withholding_taxes", inherited=True, groups="hr_payroll.group_hr_payroll_user")
rd_percentage = fields.Integer(readonly=False, related="version_id.rd_percentage", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_impulsion_plan = fields.Selection(readonly=False, related="version_id.l10n_be_impulsion_plan", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_onss_restructuring = fields.Boolean(readonly=False, related="version_id.l10n_be_onss_restructuring", inherited=True, groups="hr_payroll.group_hr_payroll_user")
has_hospital_insurance = fields.Boolean(readonly=False, related="version_id.has_hospital_insurance", inherited=True, groups="hr_payroll.group_hr_payroll_user")
insured_relative_children = fields.Integer(readonly=False, related="version_id.insured_relative_children", inherited=True, groups="hr_payroll.group_hr_payroll_user")
insured_relative_adults = fields.Integer(readonly=False, related="version_id.insured_relative_adults", inherited=True, groups="hr_payroll.group_hr_payroll_user")
insured_relative_spouse = fields.Boolean(readonly=False, related="version_id.insured_relative_spouse", inherited=True, groups="hr_payroll.group_hr_payroll_user")
hospital_insurance_amount_per_child = fields.Float(readonly=False, related="version_id.hospital_insurance_amount_per_child", inherited=True, groups="hr_payroll.group_hr_payroll_user")
hospital_insurance_amount_per_adult = fields.Float(readonly=False, related="version_id.hospital_insurance_amount_per_adult", inherited=True, groups="hr_payroll.group_hr_payroll_user")
insurance_amount = fields.Float(related="version_id.insurance_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
insured_relative_adults_total = fields.Integer(related="version_id.insured_relative_adults_total", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_hospital_insurance_notes = fields.Text(readonly=False, related="version_id.l10n_be_hospital_insurance_notes", inherited=True, groups="hr_payroll.group_hr_payroll_user")
wage_with_holidays = fields.Monetary(readonly=False, related="version_id.wage_with_holidays", inherited=True, groups="hr_payroll.group_hr_payroll_user")
# Group Insurance
l10n_be_group_insurance_rate = fields.Float(readonly=False, related="version_id.l10n_be_group_insurance_rate", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_group_insurance_amount = fields.Monetary(related="version_id.l10n_be_group_insurance_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_group_insurance_cost = fields.Monetary(related="version_id.l10n_be_group_insurance_cost", inherited=True, groups="hr_payroll.group_hr_payroll_user")
# Ambulatory Insurance
l10n_be_has_ambulatory_insurance = fields.Boolean(readonly=False, related="version_id.l10n_be_has_ambulatory_insurance", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insured_children = fields.Integer(readonly=False, related="version_id.l10n_be_ambulatory_insured_children", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insured_adults = fields.Integer(readonly=False, related="version_id.l10n_be_ambulatory_insured_adults", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insured_spouse = fields.Boolean(readonly=False, related="version_id.l10n_be_ambulatory_insured_spouse", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_amount_per_child = fields.Float(readonly=False, related="version_id.l10n_be_ambulatory_amount_per_child", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_amount_per_adult = fields.Float(readonly=False, related="version_id.l10n_be_ambulatory_amount_per_adult", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insurance_amount = fields.Float(related="version_id.l10n_be_ambulatory_insurance_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insured_adults_total = fields.Integer(related="version_id.l10n_be_ambulatory_insured_adults_total", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_ambulatory_insurance_notes = fields.Text(readonly=False, related="version_id.l10n_be_ambulatory_insurance_notes", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_mobility_budget = fields.Boolean(readonly=False, related="version_id.l10n_be_mobility_budget", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_mobility_budget_amount = fields.Monetary(readonly=False, related="version_id.l10n_be_mobility_budget_amount", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_mobility_budget_amount_monthly = fields.Monetary(related="version_id.l10n_be_mobility_budget_amount_monthly", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_wage_with_mobility_budget = fields.Monetary(readonly=False, related="version_id.l10n_be_wage_with_mobility_budget", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_is_below_scale_warning = fields.Char(readonly=False, related="version_id.l10n_be_is_below_scale_warning", inherited=True, groups="hr_payroll.group_hr_payroll_user")
l10n_be_canteen_cost = fields.Monetary(readonly=False, related="version_id.l10n_be_canteen_cost", inherited=True, groups="hr_payroll.group_hr_payroll_user")
def _get_certificate_selection(self):
if self.env.company.country_id.code != "BE":
return super()._get_certificate_selection()
certificate_selection = [
('primary', self.env._('Primary School')),
('lower_secondary', self.env._('Lower Secondary')),
('higher_secondary', self.env._('Higher Secondary'))
]
civil_engineer_added = False
for selection in super()._get_certificate_selection():
certificate_selection += [selection]
if selection[0] == 'master':
certificate_selection += [('civil_engineer', self.env._('Master: Civil Engineering'))]
civil_engineer_added = True
if not civil_engineer_added:
certificate_selection += [('civil_engineer', self.env._('Master: Civil Engineering'))]
return certificate_selection
@api.depends('version_ids.date_version', 'version_ids.contract_date_start', 'version_ids.contract_date_end')
def _compute_first_contract_year(self):
for employee in self:
version_date = employee._get_first_version_date()
year = (version_date or fields.Date.today()).year
employee.first_contract_year = year
employee.first_contract_year_n = year
employee.first_contract_year_n1 = year - 1
employee.first_contract_year_n_plus_1 = year + 1
def _compute_from_double_pay_line_ids(self):
for employee in self:
year = employee.first_contract_year
employee.double_pay_line_n_ids = employee.double_pay_line_ids.filtered(lambda d: d.year == year)
employee.double_pay_line_n1_ids = employee.double_pay_line_ids.filtered(lambda d: d.year == year - 1)
def _inverse_double_pay_line_n_ids(self):
for employee in self:
year = employee.first_contract_year
to_be_deleted = employee.double_pay_line_ids.filtered(lambda d: d.year == year) - employee.double_pay_line_n_ids
employee.double_pay_line_ids.filtered(lambda d: d.id in to_be_deleted.ids).unlink()
employee.double_pay_line_ids |= employee.double_pay_line_n_ids
def _inverse_double_pay_line_n1_ids(self):
for employee in self:
year = employee.first_contract_year
to_be_deleted = employee.double_pay_line_ids.filtered(lambda d: d.year == year - 1) - employee.double_pay_line_n1_ids
employee.double_pay_line_ids.filtered(lambda d: d.id in to_be_deleted.ids).unlink()
employee.double_pay_line_ids |= employee.double_pay_line_n1_ids
@api.constrains('start_notice_period', 'end_notice_period')
def _check_notice_period(self):
for employee in self:
if employee.start_notice_period and employee.end_notice_period and employee.start_notice_period > employee.end_notice_period:
raise ValidationError(_('The employee start notice period should be set before the end notice period'))
def _compute_l10n_be_holiday_pay_recovered(self):
payslips = self.env['hr.payslip'].search([
('employee_id', 'in', self.ids),
('struct_id', '=', self.env.ref('l10n_be_hr_payroll.hr_payroll_structure_cp200_employee_salary').id),
('company_id', '=', self.env.company.id),
('state', 'in', ['validated', 'paid']),
])
line_values = payslips._get_line_values(['HolPayRecN', 'HolPayRecN1'])
payslips_by_employee = defaultdict(lambda: self.env['hr.payslip'])
for payslip in payslips:
payslips_by_employee[payslip.employee_id] |= payslip
for employee in self:
employee_payslips = payslips_by_employee[employee]
employee.l10n_be_holiday_pay_recovered_n = - sum(line_values['HolPayRecN'][p.id]['total'] for p in employee_payslips)
employee.l10n_be_holiday_pay_recovered_n1 = - sum(line_values['HolPayRecN1'][p.id]['total'] for p in employee_payslips)
def _compute_spouse_fiscal_status_explanation(self):
no_income_threshold = self.env['hr.rule.parameter'].sudo()._get_parameter_from_code('spouse_no_income_threshold')
low_income_threshold = self.env['hr.rule.parameter'].sudo()._get_parameter_from_code('spouse_low_income_threshold')
other_income_threshold = self.env['hr.rule.parameter'].sudo()._get_parameter_from_code('spouse_other_income_threshold')
for employee in self:
employee.spouse_fiscal_status_explanation = _("""- High Income: Spouse earns more than %(low_income_threshold)s€ net/month.\n
- Low Income: Spouse earns between %(no_income_threshold)s€ and %(low_income_threshold)s€ net/month.\n
- Without Income: Spouse earns less than %(no_income_threshold)s€ net/month.\n
- High Pensions : Spouse is eligible to a pension higher than %(other_income_threshold)s€ net/month.\n
- Low Pensions : Spouse is eligible to a pension lower than %(other_income_threshold)s€ net/month.\n
Earnings are made of professional income, remuneration, unemployment allocations, annuities or similar income.""",
no_income_threshold=no_income_threshold,
low_income_threshold=low_income_threshold,
other_income_threshold=other_income_threshold)
@api.depends('identification_id')
def _compute_niss(self):
characters = dict.fromkeys([',', '.', '-', ' '], '')
for employee in self:
if employee.identification_id and not employee.niss and employee.company_country_code == 'BE':
employee.niss = reduce(lambda a, kv: a.replace(*kv), characters.items(), employee.identification_id)
@api.model
def _validate_niss(self, niss):
try:
test = niss[:-2]
if test[0] in ['0', '1', '2', '3', '4', '5']: # Should be good for several years
test = '2%s' % test
checksum = int(niss[-2:])
if checksum != (97 - int(test) % 97):
raise Exception()
return True
except Exception:
return False
def _is_niss_valid(self):
# The last 2 positions constitute the check digit. This check digit is
# a sequence of 2 digits forming a number between 01 and 97. This number is equal to 97
# minus the remainder of the division by 97 of the number formed:
# - either by the first 9 digits of the national number for people born before the 1st
# January 2000.
# - either by the number 2 followed by the first 9 digits of the national number for people
# born after December 31, 1999.
# (https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_registre_national)
self.ensure_one()
niss = self.niss
if not niss or len(niss) != 11:
return False
return self._validate_niss(niss)
@api.onchange('disabled_children_bool')
def _onchange_disabled_children_bool(self):
self.disabled_children_number = 0
@api.onchange('other_dependent_people')
def _onchange_other_dependent_people(self):
self.other_senior_dependent = 0.0
self.other_disabled_senior_dependent = 0.0
self.other_juniors_dependent = 0.0
self.other_disabled_juniors_dependent = 0.0
@api.onchange('has_hospital_insurance')
def _onchange_has_hospital_insurance(self):
self.version_id._onchange_has_hospital_insurance()
@api.onchange('l10n_be_has_ambulatory_insurance')
def _onchange_l10n_be_has_ambulatory_insurance(self):
self.version_id._onchange_l10n_be_has_ambulatory_insurance()
@api.onchange('transport_mode_car', 'transport_mode_train', 'transport_mode_public')
def _onchange_transport_mode(self):
self.version_id._onchange_transport_mode()
@api.onchange('transport_mode_private_car')
def _onchange_transport_mode_private_car(self):
self.version_id._onchange_transport_mode_private_car()
@api.model
def _get_invalid_niss_employee_ids(self):
res = self.search_read([
('company_id', 'in', self.env.companies.filtered(lambda c: c.country_id.code == 'BE').ids),
('employee_type', 'in', ('employee', 'student')),
], ['id', 'niss'])
return [row['id'] for row in res if not row['niss'] or not self._validate_niss(row['niss'])]
def _get_first_versions(self):
self.ensure_one()
versions = super()._get_first_versions()
pfi = self.env.ref('l10n_be_hr_payroll.l10n_be_contract_type_pfi', raise_if_not_found=False)
if not pfi:
return versions
return versions.filtered(
lambda c: c.company_id.country_id.code != 'BE' or (c.company_id.country_id.code == 'BE' and c.contract_type_id != pfi))
@api.model_create_multi
def create(self, vals_list):
employees = super(HrEmployee, self.with_context(pending_employee_creation=True)).create(vals_list)
updated_context = dict(employees.env.context)
updated_context.pop('pending_employee_creation')
created_employees = employees.with_context(updated_context)
created_employees.current_version_id.sudo().filtered(
lambda v: v._is_struct_from_country('BE') and v.contract_date_start
)._trigger_l10n_be_next_activities()
return created_employees
def _compute_current_version_id(self):
prev_current_version_id_by_employee = self.current_version_id.grouped('employee_id')
super()._compute_current_version_id()
to_trigger_next_activities = self.current_version_id.filtered(
lambda v: not prev_current_version_id_by_employee.get(v.employee_id) or
prev_current_version_id_by_employee.get(v.employee_id) != v
)
if to_trigger_next_activities:
to_trigger_next_activities.sudo().filtered(
lambda v: v._is_struct_from_country('BE') and v.contract_date_start
)._trigger_l10n_be_next_activities()
def action_employee_work_schedule_change_wizard(self):
if len(self) != 1:
raise UserError(self.env._("This feature can only be used on a single employee."))
if not self.contract_date_start:
raise UserError(self.env._('This feature can only be used on versions that have a contract start date'))
action = self.env['ir.actions.actions']._for_xml_id('l10n_be_hr_payroll.schedule_change_wizard_action')
action['context'] = {'default_version_id': self.current_version_id.id}
return action
def action_open_attest_wizard(self):
self.ensure_one()
default_year = self.env.context.get('default_year')
default_employee_id = self.id
return {
'type': 'ir.actions.act_window',
'name': 'Create Attestation Line',
'res_model': 'l10n.be.holiday.attest.wizard',
'view_mode': 'form',
'target': 'new',
'context': {
'default_year': default_year,
'default_employee_id': default_employee_id,
}
}
# --- Holiday Attest Helper Methods --- #
@api.model
def _get_l10n_be_holiday_attest_non_equivalent_codes(self):
# In french non-equivalent means "Non-assimilés"
return [
'LEAVE90', # Unpaid
'LEAVE700', # Unjustified reason
'LEAVE250', # Unpredictable reason
'LEAVE300', # Credit Time
]
def _get_l10n_be_holiday_attest_worked_days(self, date_from, date_to):
payslips = self.env['hr.payslip'].search([
('employee_id', '=', self.id),
('date_from', '>=', date_from),
('date_to', '<=', date_to),
('state', 'in', ['validated', 'paid'])
])
all_days = sum(payslips.worked_days_line_ids.mapped('number_of_days'))
non_equivalent_days = sum(
payslips.worked_days_line_ids.filtered(
lambda wd: wd.code in self._get_l10n_be_holiday_attest_non_equivalent_codes()
).mapped('number_of_days')
)
equivalent_days = all_days - non_equivalent_days
return equivalent_days, non_equivalent_days
def get_l10n_be_holiday_attest_occupations(self, year):
first_of_year = date(year, 1, 1)
last_of_year = min(date(year, 12, 31), self.end_notice_period or self.departure_date or date.today())
versions = self.version_ids.filtered(
lambda v: v._is_overlapping_period(first_of_year, last_of_year)
)
if not versions:
return []
occupations = []
for idx, version in enumerate(versions):
previous_occupation_work_time_rate = (
float_round(versions[idx - 1].hours_per_week, 2), versions[idx - 1].resource_calendar_id._get_days_per_week()
) if idx != 0 else None
occupation_work_time_rate = (
float_round(version.hours_per_week, 2), version.resource_calendar_id._get_days_per_week()
)
if previous_occupation_work_time_rate != occupation_work_time_rate:
if occupations:
equivalent_days, non_equivalent_days = self._get_l10n_be_holiday_attest_worked_days(
occupations[-1]['date_start'], version.date_version - relativedelta(days=1)
)
occupations[-1].update({
'date_end': version.date_version - relativedelta(days=1),
'equivalent_days': equivalent_days,
'non_equivalent_days': non_equivalent_days
})
occupations.append({
'date_start': max(first_of_year, version.date_version),
'hours_per_week': float_round(version.hours_per_week, 2),
'days_per_week': version.resource_calendar_id._get_days_per_week(),
})
equivalent_days, non_equivalent_days = self._get_l10n_be_holiday_attest_worked_days(
occupations[-1]['date_start'], last_of_year
)
occupations[-1].update({
'date_end': last_of_year,
'equivalent_days': equivalent_days,
'non_equivalent_days': non_equivalent_days
})
return occupations
@api.depends('version_ids', 'version_ids.contract_date_start', 'version_ids.active')
def _compute_first_contract_in_company(self):
employee_earliest_contract = self.env['hr.version']._read_group(
domain=[
('employee_id', 'in', self.ids),
('active', '!=', False),
],
groupby=['employee_id'],
aggregates=['contract_date_start:min'],
)
earliest_by_employee = {employee.id: earliest for employee, earliest in employee_earliest_contract}
for employee in self:
employee.first_contract_in_company = earliest_by_employee.get(employee.id)