[IMP] l10n_pe_hr_payroll: refactor 5th Cat. add 4th Cat.

This commit is contained in:
Jared Kipe
2022-05-06 14:57:49 +00:00
parent 24629e56e7
commit 16d4d5ca2b
12 changed files with 143 additions and 56 deletions

View File

@@ -22,6 +22,7 @@ Peru - Payroll Rules.
'data/integration_rules.xml',
'data/afp_rules.xml',
'data/onp_rules.xml',
'data/ir_4ta_cat_rules.xml',
'data/ir_5ta_cat_rules.xml',
'data/er_rules.xml',
'views/hr_contract_views.xml',

View File

@@ -24,7 +24,7 @@
<field name="sequence" eval="190"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_pe_afp"/>
<field name="name">EE: PE AFP Pensiones</field>
<field name="name">EE: PE AFP Pensions</field>
<field name="code">EE_PE_AFP_PENSIONES</field>
<field name="condition_select">python</field>
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'</field>
@@ -37,7 +37,7 @@
<field name="sequence" eval="191"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_pe_afp"/>
<field name="name">EE: PE AFP Seguro</field>
<field name="name">EE: PE AFP Insurance</field>
<field name="code">EE_PE_AFP_SEGURO</field>
<field name="condition_select">python</field>
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'</field>
@@ -60,7 +60,7 @@ result, result_rate = eligible_wage, rate
<field name="sequence" eval="192"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_pe_afp"/>
<field name="name">EE: PE AFP Comisión Mixta</field>
<field name="name">EE: PE AFP Mixed Commission</field>
<field name="code">EE_PE_AFP_COMISION_MIXTA</field>
<field name="condition_select">python</field>
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp' and contract.pe_payroll_config_value('afp_comision_type') == 'mixta'</field>
@@ -73,7 +73,7 @@ result, result_rate = eligible_wage, rate
<field name="sequence" eval="192"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_pe_afp"/>
<field name="name">EE: PE AFP Comisn (Non-Mixta)</field>
<field name="name">EE: PE AFP Comission (Non-Mixed)</field>
<field name="code">EE_PE_AFP_COMISION_NON_MIXTA</field>
<field name="condition_select">python</field>
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp' and contract.pe_payroll_config_value('afp_comision_type') == 'non_mixta'</field>

View File

@@ -2,13 +2,13 @@
<odoo>
<record id="structure_type_employee" model="hr.payroll.structure.type">
<field name="name">Peru Employee</field>
<field name="name">Peru Employee (5ta Cat.)</field>
<field name="default_resource_calendar_id" ref="resource.resource_calendar_std"/>
<field name="country_id" ref="base.pe"/>
</record>
<record id="hr_payroll_structure" model="hr.payroll.structure">
<field name="name">Peru Employee Standard</field>
<field name="name">Peru Employee (5ta Cat.)</field>
<field name="country_id" ref="base.pe"/>
<field name="type_id" ref="l10n_pe_hr_payroll.structure_type_employee"/>
<field name="regular_pay" eval="True"/>
@@ -17,6 +17,19 @@
]"/>
</record>
<record id="structure_type_employee_4ta_cat" model="hr.payroll.structure.type">
<field name="name">Peru Employee (4ta Cat.)</field>
<field name="default_resource_calendar_id" ref="resource.resource_calendar_std"/>
<field name="country_id" ref="base.pe"/>
</record>
<record id="hr_payroll_structure_4ta_cat" model="hr.payroll.structure">
<field name="name">Peru Employee (4ta Cat.)</field>
<field name="country_id" ref="base.pe"/>
<field name="type_id" ref="l10n_pe_hr_payroll.structure_type_employee_4ta_cat"/>
<field name="regular_pay" eval="True"/>
</record>
<!-- AFP -->
<record id="hr_payroll_category_ee_pe_afp" model="hr.salary.rule.category">
<field name="name">EE: AFP</field>
@@ -43,16 +56,23 @@
<!-- IR 5TA CAT -->
<record id="hr_payroll_category_ee_ir_5ta_cat" model="hr.salary.rule.category">
<field name="name">EE: IR 5ta Cat.</field>
<field name="name">EE: IR 5th Cat.</field>
<field name="code">EE_PE_IR_5TA_CAT</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_er_ir_5ta_cat" model="hr.salary.rule.category">
<field name="name">ER: IR 5ta Cat.</field>
<field name="name">ER: IR 5th Cat.</field>
<field name="code">ER_PE_IR_5TA_CAT</field>
<field name="parent_id" ref="hr_payroll.COMP"/>
</record>
<!-- IR 4TA CAT -->
<record id="hr_payroll_category_ee_ir_4ta_cat" model="hr.salary.rule.category">
<field name="name">EE: IR 4th Cat.</field>
<field name="code">EE_PE_IR_4TA_CAT</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<!-- ESSALUD-->
<record id="hr_payroll_category_ee_essalud" model="hr.salary.rule.category">
<field name="name">EE: Essalud (rem)</field>
@@ -67,7 +87,7 @@
<!-- Bonus input & rule -->
<record id="input_type_bono" model="hr.payslip.input.type">
<field name="name">Bono</field>
<field name="name">Bonus</field>
<field name="code">BONO</field>
<field name="country_id" ref="base.pe" />
<field name="struct_ids" eval="[
@@ -82,7 +102,7 @@
<field name="amount_python_compute">result = inputs.BONO.amount if inputs.BONO else 0</field>
<field name="code">BASIC_BONO</field>
<field name="category_id" ref="hr_payroll.BASIC"/>
<field name="name">Bono</field>
<field name="name">Bonus</field>
<field name="sequence" eval="25"/>
<field name="struct_id" ref="l10n_pe_hr_payroll.hr_payroll_structure"/>
</record>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Parameters -->
<record id="rule_parameter_ee_ir_4ta_cat" model="hr.rule.parameter">
<field name="name">EE: IR 4ta Cat.</field>
<field name="code">ee_ir_4ta_cat</field>
<field name="country_id" ref="base.pe"/>
</record>
<record id="rule_parameter_ee_ir_4ta_cat_2020" model="hr.rule.parameter.value">
<field name="parameter_value">8.0</field>
<field name="rule_parameter_id" ref="rule_parameter_ee_ir_4ta_cat"/>
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
</record>
<!-- EE Rules -->
<record id="hr_payroll_rule_ee_ir_4ta_cat" model="hr.salary.rule">
<field name="sequence" eval="190"/>
<field name="struct_id" ref="hr_payroll_structure_4ta_cat"/>
<field name="category_id" ref="hr_payroll_category_ee_ir_4ta_cat"/>
<field name="name">EE: PE IR 4th Cat.</field>
<field name="code">EE_PE_IR_4TA_CAT</field>
<field name="condition_select">python</field>
<field name="condition_python">result, _ = ir_4ta_cat(payslip, categories, worked_days, inputs)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result, result_rate = ir_4ta_cat(payslip, categories, worked_days, inputs)</field>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -48,7 +48,7 @@
<field name="sequence" eval="196"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_ir_5ta_cat"/>
<field name="name">EE: PE IR 5TA Cat.</field>
<field name="name">EE: PE IR 5th Cat.</field>
<field name="code">EE_PE_IR_5TA_CAT</field>
<field name="condition_select">python</field>
<field name="condition_python">result, _ = ir_5ta_cat(payslip, categories, worked_days, inputs, BASIC)</field>

View File

@@ -7,6 +7,7 @@ class PEHRContract(models.Model):
_inherit = 'hr.contract'
pe_payroll_config_id = fields.Many2one('hr.contract.pe_payroll_config', 'Payroll Forms')
pe_payroll_ee_4ta_cat_exempt = fields.Boolean(string='Exempt from 4th Cat. withholding.')
def pe_payroll_config_value(self, name):
return self.pe_payroll_config_id[name]

View File

@@ -2,6 +2,7 @@
from odoo import api, fields, models
from .rules.general import _general_rate
from .rules.ir_4ta_cat import ir_4ta_cat
from .rules.ir_5ta_cat import ir_5ta_cat
@@ -12,6 +13,7 @@ class HRPayslip(models.Model):
res = super()._get_base_local_dict()
res.update({
'general_rate': _general_rate,
'ir_4ta_cat': ir_4ta_cat,
'ir_5ta_cat': ir_5ta_cat,
})
return res

View File

@@ -1,6 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import api, fields, models, _
class HRContractPEPayrollConfig(models.Model):
@@ -10,7 +10,7 @@ class HRContractPEPayrollConfig(models.Model):
name = fields.Char(string="Description")
employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
date_hired = fields.Date(string='Date Hired', required=True, default=fields.Date.today,
help='For calculations like IR 5TA CAT.')
help='For calculations like IR 5TH CAT.')
retirement_type = fields.Selection([
('afp', 'AFP'),
@@ -26,8 +26,8 @@ class HRContractPEPayrollConfig(models.Model):
('profuturo', 'Profuturo'),
], string='AFP Type', default='profuturo')
afp_comision_type = fields.Selection([
('mixta', 'Mixta'),
('non_mixta', 'Non-Mixta'),
('mixta', 'Mixed'),
('non_mixta', 'Non-Mixed'),
], string='AFP Commission Type', default='mixta')
comp_ss_type = fields.Selection([

View File

@@ -1,4 +1,5 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import general
from . import ir_4ta_cat
from . import ir_5ta_cat

View File

@@ -0,0 +1,10 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
def ir_4ta_cat(payslip, categories, worked_days, inputs):
if payslip.contract_id.pe_payroll_ee_4ta_cat_exempt:
return 0.0, 0.0
wage = categories.GROSS
assert wage == 11000.00
rate = payslip.rule_parameter('ee_ir_4ta_cat')
assert rate == 8.0
return wage, -rate

View File

@@ -1,42 +1,58 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from datetime import date
def ir_5ta_cat(payslip, categories, worked_days, inputs, basic_wage):
pay_periods_in_year = payslip.pay_periods_in_year
uit = payslip.rule_parameter('pe_uit')
payslip_date_end = payslip.dict.date_to
# there are two special scenarios
# 1. IF employee's `date_hired` is in current year
# IF this is the last payroll in June or December
# THEN we need to 'true up' the last two quarters of withholding (e.g. give a refund)
last_payslip_june = payslip_date_end.month == 6 and payslip_date_end.day == 30
# NOTE we do NOT currently support 'catch up' in June. Our formula genearlly already catches up.
last_payslip_june = False
last_payslip_december = payslip_date_end.month == 12 and payslip_date_end.day == 31
wage_period = categories.GROSS
if not any((basic_wage, wage_period, last_payslip_june, last_payslip_december)):
return 0.0, 0.0
period_additional_wage = max(wage_period - basic_wage, 0.0) # 0.0 or positive
year = payslip_date_end.year
next_year = date(year+1, 1, 1)
prior_wage_year = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
pay_periods_at_current = round(((next_year - payslip_date_end).days / 365) * pay_periods_in_year) + 1.0
wage_year = (basic_wage * pay_periods_at_current) + prior_wage_year
# IF employee's `date_hired` is in current year
# THEN we can pro-rate the period (reduce withholding)
date_hired = payslip.dict.contract_id.pe_payroll_config_value('date_hired')
payslip_date_end = payslip.dict.date_to
hired_in_year = date_hired.year == payslip_date_end.year
# 2. IF this is the last payroll in June or December
# THEN we need to 'true up' the last two quarters of withholding (e.g. give a refund)
last_payslip_june = payslip_date_end.month == 6 and payslip_date_end.day == 30
last_payslip_december = payslip_date_end.month == 12 and payslip_date_end.day == 31
# basic_wage = BASIC # must be paramatarized as we will not have locals
wage_period = categories.GROSS
if not all((basic_wage, wage_period)) and not any((last_payslip_june, last_payslip_december)):
return 0.0, 0.0
period_additional_wage = max(wage_period - basic_wage, 0.0) # 0.0 or positive
wage_year = basic_wage * pay_periods_in_year
# this can be reduced
remaining_months = 12
periods_in_year_eligible = pay_periods_in_year
if hired_in_year:
# e.g. hired in March (3) gives us 12 - 3 + 1 = 10 (Jan, Feb are the 2 missing from 12)
remaining_months = 12 - date_hired.month + 1
periods_in_year_eligible = round(((next_year - date_hired).days / 365) * pay_periods_in_year)
# additional 2 months (July and December) (note 2/12 == 1/6)
wage_2 = wage_year * (1/6)
if remaining_months < 6:
wage_2 = wage_year * (1/12)
wage_3 = wage_2 * (payslip.rule_parameter('ee_ir_5ta_cat_ley_29351') / 100.0)
wage_year += wage_2 + wage_3
# now that we have wage_year, we may need to adjust it by remaining months
wage_year = wage_year * remaining_months / 12 # could be 12/12
# normalize 1era Gratification
if hired_in_year and date_hired.month > 6:
wage_gratif_1 = 0.0
elif hired_in_year:
wage_gratif_1 = basic_wage / 6 * (6 - date_hired.month + 1)
else:
wage_gratif_1 = basic_wage
# normalize 2da Gratification
if hired_in_year and date_hired.month > 6:
wage_gratif_2 = basic_wage / 6 * (12 - date_hired.month + 1)
else:
wage_gratif_2 = basic_wage
wage_year += wage_gratif_1 + wage_gratif_2
cat_ley = (wage_gratif_1 + wage_gratif_2) * (payslip.rule_parameter('ee_ir_5ta_cat_ley_29351') / 100.0)
wage_year += cat_ley
wage_year += period_additional_wage
over_7uit = wage_year - (7.0 * uit)
@@ -57,15 +73,19 @@ def ir_5ta_cat(payslip, categories, worked_days, inputs, basic_wage):
break
last_uit = _uit
if total_tax:
if last_payslip_june or last_payslip_december:
year = payslip_date_end.year
# if total_tax:
ytd_tax = -payslip.sum_category('EE_PE_IR_5TA_CAT', str(year) + '-01-01', str(year+1) + '-01-01')
if last_payslip_june or last_payslip_december:
if last_payslip_june:
# does not work right because the gratif_2 is already there
total_tax /= 2
# remaining_tax may flip signs
remaining_tax = -(total_tax - ytd_tax)
if not wage_period:
# to give refund, cannot normalize to wage
return remaining_tax, 100.0
return wage_period, (remaining_tax / wage_period * 100.0)
tax = -total_tax / remaining_months # TODO needs to be normalized to periods in year if not monthly...
tax = -(total_tax - ytd_tax) / pay_periods_at_current
return wage_period, (tax / wage_period * 100.0)
return 0.0, 0.0

View File

@@ -12,6 +12,8 @@
attrs="{'invisible': [('structure_type_id', '!=', %(l10n_pe_hr_payroll.structure_type_employee)s)],
'required': [('structure_type_id', '=', %(l10n_pe_hr_payroll.structure_type_employee)s)]}"
context="{'default_employee_id': employee_id}"/>
<field name="pe_payroll_ee_4ta_cat_exempt"
attrs="{'invisible': [('structure_type_id', '!=', %(l10n_pe_hr_payroll.structure_type_employee_4ta_cat)s)]}" />
</xpath>
</field>
</record>