diff --git a/addons/l10n_be_hr_payroll/__manifest__.py b/addons/l10n_be_hr_payroll/__manifest__.py
index dbdc45db691..12f4c72db74 100644
--- a/addons/l10n_be_hr_payroll/__manifest__.py
+++ b/addons/l10n_be_hr_payroll/__manifest__.py
@@ -20,9 +20,8 @@ Belgian Payroll Rules.
* Salary Maj, ONSS, Withholding Tax, Child Allowance, ...
""",
- 'demo': ['data/l10n_be_hr_payroll_demo.xml'],
'website': 'https://www.odoo.com/page/accounting',
- 'data':[
+ 'data': [
'views/l10n_be_hr_payroll_view.xml',
'data/l10n_be_hr_payroll_data.xml',
'data/hr.salary.rule.csv',
diff --git a/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_demo.xml b/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_demo.xml
deleted file mode 100644
index f45f732dc99..00000000000
--- a/addons/l10n_be_hr_payroll/data/l10n_be_hr_payroll_demo.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
- Developer's Contract (Belgium payroll C.P. 218)
-
-
-
-
-
-
-
-
-
-
- single
-
-
-
- 54 Rue Jean Baptiste Colette BP
- Attiches
- 59551
-
- onss@yourcompany.example.com
-
-
- Shanghai Branch
- Anshun Road 83, Room 1603 -
- Shanghai
- 200052
-
- fiscal@yourcompany.example.com
-
-
diff --git a/addons/l10n_be_hr_payroll/models/l10n_be_hr_payroll.py b/addons/l10n_be_hr_payroll/models/l10n_be_hr_payroll.py
index c8ca237988b..17436cd10c1 100644
--- a/addons/l10n_be_hr_payroll/models/l10n_be_hr_payroll.py
+++ b/addons/l10n_be_hr_payroll/models/l10n_be_hr_payroll.py
@@ -1,30 +1,246 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import fields, models
-from odoo.addons import decimal_precision as dp
+from odoo import api, fields, models, _
+
+from odoo.exceptions import ValidationError
class HrContract(models.Model):
_inherit = 'hr.contract'
- travel_reimbursement_amount = fields.Float(string='Reimbursement of travel expenses', digits=dp.get_precision('Payroll'))
- car_company_amount = fields.Float(string='Company car employer', digits=dp.get_precision('Payroll'))
- car_employee_deduction = fields.Float(string='Company Car Deduction for Worker', digits=dp.get_precision('Payroll'))
- misc_onss_deduction = fields.Float(string='Miscellaneous exempt ONSS', digits=dp.get_precision('Payroll'))
- meal_voucher_amount = fields.Float(string='Check Value Meal', digits=dp.get_precision('Payroll'))
- meal_voucher_employee_deduction = fields.Float(string='Check Value Meal - by worker', digits=dp.get_precision('Payroll'))
- insurance_employee_deduction = fields.Float(string='Insurance Group - by worker', digits=dp.get_precision('Payroll'))
- misc_advantage_amount = fields.Float(string='Benefits of various nature', digits=dp.get_precision('Payroll'))
- additional_net_amount = fields.Float(string='Net supplements', digits=dp.get_precision('Payroll'))
- retained_net_amount = fields.Float('Net retained ', digits=dp.get_precision('Payroll'))
+ transport_mode = fields.Selection([
+ ('company_car', 'Company car'),
+ ('public_transport', 'Public Transport'),
+ ('others', 'Other'),
+ ], string="Transport", default='company_car')
+ car_atn = fields.Monetary(string='ATN Company Car')
+ public_transport_employee_amount = fields.Monetary('Paid by the employee (Monthly)')
+ thirteen_month = fields.Monetary(compute='_compute_holidays_advantages', string='13th Month')
+ double_holidays = fields.Monetary(compute='_compute_holidays_advantages', string='Double holidays')
+ warrant_value_employee = fields.Monetary(compute='_compute_warrants_cost', string="Warrant value for the employee")
+
+ # Employer costs fields
+ final_yearly_costs = fields.Monetary(compute='_compute_final_yearly_costs', readonly=False,
+ string='Total Employee Cost', groups="hr.group_hr_manager")
+ monthly_yearly_costs = fields.Monetary(compute='_compute_monthly_yearly_costs', string='Monthly Equivalent Cost', readonly=True)
+ ucm_insurance = fields.Monetary(compute='_compute_ucm_insurance', string="Social Secretary Costs")
+ social_security_contributions = fields.Monetary(compute='_compute_social_security_contributions', string="Social Security Contributions")
+ yearly_cost_before_charges = fields.Monetary(compute='_compute_yearly_cost_before_charges', string="Yearly Costs Before Charges")
+ meal_voucher_paid_by_employer = fields.Monetary(compute='_compute_meal_voucher_paid_by_employer', string="Meal Voucher Paid by Employer")
+ company_car_total_depreciated_cost = fields.Monetary()
+ public_transport_reimbursed_amount = fields.Monetary(string='Reimbursed amount',
+ compute='_compute_public_transport_reimbursed_amount', readonly=False, store=True)
+ others_reimbursed_amount = fields.Monetary(string='Reimbursed amount')
+ transport_employer_cost = fields.Monetary(compute='_compute_transport_employer_cost', string="Employer cost from employee transports")
+ warrants_cost = fields.Monetary(compute='_compute_warrants_cost')
+
+ # Advantages
+ commission_on_target = fields.Monetary(string="Commission on Target", default=1000)
+ fuel_card = fields.Monetary(string="Fuel Card", default=150)
+ internet = fields.Monetary(string="Internet", default=38, help="Your internet subscription will be paid up to this amount.")
+ representation_fees = fields.Monetary(string="Representation Fees", default=150.0)
+ mobile = fields.Monetary(string="Mobile", default=30, help="Your mobile subscription will be paid up to this amount.")
+ mobile_plus = fields.Monetary(string="International Communication", default=50, help="Your international mobile subscription will be paid up to this amount.")
+ meal_voucher_amount = fields.Monetary(string="Meal Vouchers", default=7.45)
+ holidays = fields.Float(string="Holidays", default=20)
+ holidays_compensation = fields.Monetary(compute='_compute_holidays_compensation', string="Holidays Compensation")
+ wage_with_holidays = fields.Monetary(compute='_compute_wage_with_holidays', sting="Wage update with holidays retenues")
+ additional_net_amount = fields.Monetary(string="Net Supplements")
+ retained_net_amount = fields.Monetary(sting="Net Retained")
+ eco_checks = fields.Monetary("Eco Vouchers", default=250)
+
+ @api.depends('holidays', 'wage')
+ def _compute_wage_with_holidays(self):
+ for contract in self:
+ if contract.holidays > 20.0:
+ yearly_cost = contract.final_yearly_costs * (1.0 - (contract.holidays - 20.0) / 220.0)
+ contract.wage_with_holidays = contract._get_gross_from_employer_costs(yearly_cost)
+ else:
+ contract.wage_with_holidays = contract.wage
+
+ @api.depends('transport_mode', 'company_car_total_depreciated_cost',
+ 'public_transport_reimbursed_amount', 'others_reimbursed_amount')
+ def _compute_transport_employer_cost(self):
+ for contract in self:
+ if contract.transport_mode == 'company_car':
+ contract.transport_employer_cost = contract.company_car_total_depreciated_cost
+ elif contract.transport_mode == 'public_transport':
+ contract.transport_employer_cost = contract.public_transport_reimbursed_amount
+ elif contract.transport_mode == 'others':
+ contract.transport_employer_cost == contract.others_reimbursed_amount
+
+ @api.depends('commission_on_target')
+ def _compute_warrants_cost(self):
+ for contract in self:
+ contract.warrants_cost = contract.commission_on_target * 1.326 / 1.05 * 12.0
+ contract.warrant_value_employee = contract.warrants_cost * 0.54
+
+ @api.depends('wage', 'fuel_card', 'representation_fees', 'transport_employer_cost',
+ 'internet', 'mobile', 'mobile_plus')
+ def _compute_yearly_cost_before_charges(self):
+ for contract in self:
+ contract.yearly_cost_before_charges = 12.0 * (
+ contract.wage * (1.0 + 1.0 / 12.0) +
+ contract.fuel_card +
+ contract.representation_fees +
+ contract.internet +
+ contract.mobile +
+ contract.mobile_plus +
+ contract.transport_employer_cost
+ )
+
+ @api.depends('yearly_cost_before_charges', 'ucm_insurance', 'social_security_contributions',
+ 'social_security_contributions', 'double_holidays', 'warrants_cost', 'meal_voucher_paid_by_employer')
+ def _compute_final_yearly_costs(self):
+ for contract in self:
+ contract.final_yearly_costs = (
+ contract.yearly_cost_before_charges +
+ contract.ucm_insurance +
+ contract.social_security_contributions +
+ contract.double_holidays +
+ contract.warrants_cost +
+ (220.0 * contract.meal_voucher_paid_by_employer)
+ )
+
+ @api.depends('holidays', 'final_yearly_costs')
+ def _compute_holidays_compensation(self):
+ for contract in self:
+ if contract.holidays < 20:
+ decrease_amount = contract.final_yearly_costs * (20.0 - contract.holidays) / 220.0
+ contract.holidays_compensation = decrease_amount
+ else:
+ contract.holidays_compensation = 0.0
+
+ @api.onchange('final_yearly_costs')
+ def _onchange_final_yearly_costs(self):
+ self.wage = self._get_gross_from_employer_costs(self.final_yearly_costs)
+
+ @api.depends('meal_voucher_amount')
+ def _compute_meal_voucher_paid_by_employer(self):
+ for contract in self:
+ contract.meal_voucher_paid_by_employer = contract.meal_voucher_amount * (1 - 0.1463)
+
+ @api.depends('wage')
+ def _compute_social_security_contributions(self):
+ for contract in self:
+ total_wage = contract.wage * 13.0
+ contract.social_security_contributions = (total_wage) * 0.3507
+
+ @api.depends('wage')
+ def _compute_ucm_insurance(self):
+ for contract in self:
+ contract.ucm_insurance = (contract.wage * 12.0) * 0.05
+
+ @api.depends('public_transport_employee_amount')
+ def _compute_public_transport_reimbursed_amount(self):
+ for contract in self:
+ contract.public_transport_reimbursed_amount = contract._get_public_transport_reimbursed_amount(contract.public_transport_employee_amount)
+
+ def _get_public_transport_reimbursed_amount(self, amount):
+ return amount * 0.68
+
+ @api.depends('final_yearly_costs')
+ def _compute_monthly_yearly_costs(self):
+ for contract in self:
+ contract.monthly_yearly_costs = contract.final_yearly_costs / 12.0
+
+ @api.depends('wage')
+ def _compute_holidays_advantages(self):
+ for contract in self:
+ contract.double_holidays = contract.wage * 0.92
+ contract.thirteen_month = contract.wage
+
+ @api.onchange('mobile', 'mobile_plus')
+ def _onchange_mobile(self):
+ if self.mobile_plus and not self.mobile:
+ raise ValidationError(_('You should have a mobile subscription to select an international communication amount!'))
+
+ def _get_internet_amount(self, has_internet):
+ if has_internet:
+ return self.get_attribute('internet', 'default_value')
+ else:
+ return 0.0
+
+ def _get_mobile_amount(self, has_mobile, international_communication):
+ if has_mobile and international_communication:
+ return self.get_attribute('mobile', 'default_value') + self.get_attribute('mobile_plus', 'default_value')
+ elif has_mobile:
+ return self.get_attribute('mobile', 'default_value')
+ else:
+ return 0.0
+
+ def _get_gross_from_employer_costs(self, yearly_cost):
+ contract = self
+ remaining_for_gross = yearly_cost \
+ - 12.0 * contract.representation_fees \
+ - 12.0 * contract.fuel_card \
+ - 12.0 * contract.internet \
+ - 12.0 * (contract.mobile + contract.mobile_plus) \
+ - 12.0 * contract.transport_employer_cost \
+ - (1.326 / 1.05 * 12.0) * contract.commission_on_target \
+ - 220.0 * contract.meal_voucher_paid_by_employer
+ gross = remaining_for_gross / (12.0 * 0.05 + 13.0 + 13.0 * 0.3507 + 0.92)
+ return gross
class HrEmployee(models.Model):
_inherit = 'hr.employee'
- spouse_fiscal_status = fields.Selection([('without income','Without Income'),('with income','With Income')], string='Tax status for spouse')
+ spouse_fiscal_status = fields.Selection([
+ ('without income', 'Without Income'),
+ ('with income', 'With Income')
+ ], string='Tax status for spouse')
+ disabled = fields.Boolean(string="Disabled", help="If the employee is declared disabled by law")
disabled_spouse_bool = fields.Boolean(string='Disabled Spouse', help='if recipient spouse is declared disabled by law')
disabled_children_bool = fields.Boolean(string='Disabled Children', help='if recipient children is/are declared disabled by law')
resident_bool = fields.Boolean(string='Nonresident', help='if recipient lives in a foreign country')
disabled_children_number = fields.Integer('Number of disabled children')
+ dependent_children = fields.Integer(compute='_compute_dependent_children', string='Considered number of dependent children')
+ other_dependent_people = fields.Boolean(string="Other Dependent People", help="If other people are dependent on the employee")
+ other_senior_dependent = fields.Integer('# seniors (>=65)', help="Number of seniors dependent on the employee, including the disabled ones")
+ other_disabled_senior_dependent = fields.Integer('# disabled seniors (>=65)')
+ other_juniors_dependent = fields.Integer('# people (<65)', help="Number of juniors dependent on the employee, including the disabled ones")
+ other_disabled_juniors_dependent = fields.Integer('# disabled people (<65)')
+ dependent_seniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent seniors")
+ dependent_juniors = fields.Integer(compute='_compute_dependent_people', string="Considered number of dependent juniors")
+ spouse_net_revenue = fields.Float(string="Spouse Net Revenue", help="Own professional income, other than pensions, annuities or similar income")
+ spouse_other_net_revenue = fields.Float(string="Spouse Other Net Revenue",
+ help='Own professional income which is exclusively composed of pensions, annuities or similar income')
+
+ @api.constrains('spouse_fiscal_status', 'spouse_net_revenue', 'spouse_other_net_revenue')
+ def _check_spouse_revenue(self):
+ for employee in self:
+ if employee.spouse_fiscal_status == 'with income' and not employee.spouse_net_revenue and not employee.spouse_other_net_revenue:
+ raise ValidationError(_("The revenue for the spouse can't be equal to zero is the fiscal status is 'With Income'."))
+
+ @api.onchange('spouse_fiscal_status')
+ def _onchange_spouse_fiscal_status(self):
+ self.spouse_net_revenue = 0.0
+ self.spouse_other_net_revenue = 0.0
+
+ @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.depends('disabled_children_bool', 'disabled_children_number', 'children')
+ def _compute_dependent_children(self):
+ for employee in self:
+ if employee.disabled_children_bool:
+ employee.dependent_children = employee.children + employee.disabled_children_number
+ else:
+ employee.dependent_children = employee.children
+
+ @api.depends('other_dependent_people', 'other_senior_dependent',
+ 'other_disabled_senior_dependent', 'other_juniors_dependent', 'other_disabled_juniors_dependent')
+ def _compute_dependent_people(self):
+ for employee in self:
+ employee.dependent_seniors = employee.other_senior_dependent + employee.other_disabled_senior_dependent
+ employee.dependent_juniors = employee.other_juniors_dependent + employee.other_disabled_juniors_dependent
diff --git a/addons/l10n_be_hr_payroll/views/l10n_be_hr_payroll_view.xml b/addons/l10n_be_hr_payroll/views/l10n_be_hr_payroll_view.xml
index 7cf651f8019..b07a075956e 100644
--- a/addons/l10n_be_hr_payroll/views/l10n_be_hr_payroll_view.xml
+++ b/addons/l10n_be_hr_payroll/views/l10n_be_hr_payroll_view.xml
@@ -1,27 +1,124 @@
-
- hr.contract.form.inherit
- hr.contract
- 20
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ hr.contract.form
+ hr.contract
+
+
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+ / worked day
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+
+
+
+
+
+
+
+
+
+ / year
+
+
+
+
+
+
+
+
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+ / month
+
+
+
+
+
+
+ / year
+
+
+
+
+ / year
+
+
+
+
+ / year
+
+
+
+
+ / year
+
+
+
+
+
+
hr.employee.inherit.form
@@ -29,7 +126,6 @@
30
-
@@ -37,11 +133,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-