mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[IMP] l10n_pe_hr_payroll: retirement type and other contract fields
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
'version': '13.0.2022.0.0',
|
||||
'category': 'Payroll Localization',
|
||||
'depends': [
|
||||
'hr_payroll',
|
||||
'hr_payroll_hibou',
|
||||
'hr_contract_reports',
|
||||
'hibou_professional',
|
||||
],
|
||||
@@ -17,11 +17,15 @@ Peru - Payroll Rules.
|
||||
""",
|
||||
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/base.xml',
|
||||
'data/integration_rules.xml',
|
||||
'data/afp_rules.xml',
|
||||
'data/onp_rules.xml',
|
||||
'data/ir_5ta_cat_rules.xml',
|
||||
'data/er_rules.xml',
|
||||
# 'views/hr_contract_views.xml',
|
||||
'views/hr_contract_views.xml',
|
||||
'views/pe_payroll_config_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
],
|
||||
'demo': [
|
||||
|
||||
@@ -2,36 +2,20 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Parameters -->
|
||||
<record id="rule_parameter_ee_afp_pensiones" model="hr.rule.parameter">
|
||||
<field name="name">EE: AFP Pensiones</field>
|
||||
<field name="code">ee_afp_pensiones</field>
|
||||
<record id="rule_parameter_ee_afp" model="hr.rule.parameter">
|
||||
<field name="name">EE: AFP</field>
|
||||
<field name="code">ee_afp</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<record id="rule_parameter_ee_afp_pensiones_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">10.0</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_afp_pensiones"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ee_afp_seguro" model="hr.rule.parameter">
|
||||
<field name="name">EE: AFP Seguro</field>
|
||||
<field name="code">ee_afp_seguro</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<record id="rule_parameter_ee_afp_seguro_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">1.74</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_afp_seguro"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ee_afp_comision" model="hr.rule.parameter">
|
||||
<field name="name">EE: AFP Comisión</field>
|
||||
<field name="code">ee_afp_comision</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<record id="rule_parameter_ee_afp_comision_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">0.18</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_afp_comision"/>
|
||||
<record id="rule_parameter_ee_afp_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">{
|
||||
# non-mixta, mixta_monthly, mixta_annually, iss, ley, maximum (iss wage base)
|
||||
'habitat': (1.47, 0.38, 1.25, 1.35, 10.0, 9707.03),
|
||||
'integra': (1.55, 0.00, 1.82, 1.35, 10.0, 9707.03),
|
||||
'prima': (1.60, 0.18, 1.25, 1.35, 10.0, 9707.03),
|
||||
'profuturo': (1.69, 0.67, 1.20, 1.35, 10.0, 9707.03),
|
||||
}</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_afp"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
@@ -48,9 +32,9 @@
|
||||
<field name="name">EE: PE AFP Pensiones</field>
|
||||
<field name="code">EE_PE_AFP_PENSIONES</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.BASIC</field>
|
||||
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_pensiones')</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][4]</field>
|
||||
<field name="partner_id" ref="res_partner_afp"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
@@ -62,23 +46,44 @@
|
||||
<field name="name">EE: PE AFP Seguro</field>
|
||||
<field name="code">EE_PE_AFP_SEGURO</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.BASIC</field>
|
||||
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_seguro')</field>
|
||||
<field name="amount_python_compute">
|
||||
year = payslip.dict.get_year()
|
||||
ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01')
|
||||
wage = categories.GROSS
|
||||
wage_base = payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][5]
|
||||
rate = payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][3]
|
||||
result, result_rate = general_rate(payslip, wage, ytd_wage, wage_base=wage_base, rate=rate)
|
||||
</field>
|
||||
<field name="partner_id" ref="res_partner_afp"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_afp_comision" model="hr.salary.rule">
|
||||
<record id="hr_payroll_rule_ee_afp_comision_mixta" model="hr.salary.rule">
|
||||
<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</field>
|
||||
<field name="code">EE_PE_AFP_COMISION</field>
|
||||
<field name="name">EE: PE AFP Comisión Mixta</field>
|
||||
<field name="code">EE_PE_AFP_COMISION_MIXTA</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.BASIC</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>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_comision')</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][1]</field>
|
||||
<field name="partner_id" ref="res_partner_afp"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_payroll_rule_ee_afp_comision_non_mixta" model="hr.salary.rule">
|
||||
<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 (Non-Mixta)</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>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][0]</field>
|
||||
<field name="partner_id" ref="res_partner_afp"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
@@ -29,4 +29,28 @@
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<!-- ONP -->
|
||||
<record id="hr_payroll_category_ee_pe_onp" model="hr.salary.rule.category">
|
||||
<field name="name">EE: ONP</field>
|
||||
<field name="code">EE_PE_ONP</field>
|
||||
<field name="parent_id" ref="hr_payroll.DED"/>
|
||||
</record>
|
||||
<record id="hr_payroll_category_er_pe_onp" model="hr.salary.rule.category">
|
||||
<field name="name">ER: ONP</field>
|
||||
<field name="code">ER_PE_ONP</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
<!-- 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="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="code">ER_PE_IR_5TA_CAT</field>
|
||||
<field name="parent_id" ref="hr_payroll.COMP"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
80
l10n_pe_hr_payroll/data/ir_5ta_cat_rules.xml
Normal file
80
l10n_pe_hr_payroll/data/ir_5ta_cat_rules.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<!-- Parameters -->
|
||||
<record id="rule_parameter_pe_uit" model="hr.rule.parameter">
|
||||
<field name="name">Peru UIT</field>
|
||||
<field name="code">pe_uit</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<record id="rule_parameter_pe_uit_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">4400.0</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_pe_uit"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<record id="rule_parameter_ee_ir_5ta_cat" model="hr.rule.parameter">
|
||||
<field name="name">EE: IR 5ta Cat.</field>
|
||||
<field name="code">ee_ir_5ta_cat</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<!-- 2015 chart -->
|
||||
<record id="rule_parameter_ee_ir_5ta_cat_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">[
|
||||
( 5.0, 8.0),
|
||||
( 20.0, 14.0),
|
||||
( 35.0, 17.0),
|
||||
( 45.0, 20.0),
|
||||
('inf', 30.0),
|
||||
]</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_ir_5ta_cat"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Partner -->
|
||||
<record id="res_partner_ir_5ta_cat" model="res.partner">
|
||||
<field name="name">IR 5ta Cat.</field>
|
||||
</record>
|
||||
|
||||
<!-- EE Rules -->
|
||||
<record id="hr_payroll_rule_ee_ir_5ta_cat" model="hr.salary.rule">
|
||||
<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="code">EE_PE_IR_5TA_CAT</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.GROSS</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">
|
||||
# TODO normalize anual wage based on pay period
|
||||
uit = payslip.rule_parameter('pe_uit')
|
||||
wage = categories.GROSS
|
||||
wage_year = wage * 12.0
|
||||
# additional 2 months
|
||||
wage_year += wage * 2.0
|
||||
over_7uit = wage_year - (7.0 * uit)
|
||||
if over_7uit <= 0.0:
|
||||
result = 0.0
|
||||
else:
|
||||
total_tax = 0.0
|
||||
last_uit = 0.0
|
||||
for _uit, rate in payslip.rule_parameter('ee_ir_5ta_cat'):
|
||||
# marginal brackets
|
||||
_uit = float(_uit)
|
||||
if over_7uit > (last_uit * uit):
|
||||
eligible_wage = min(over_7uit, _uit * uit) - (last_uit * uit)
|
||||
if eligible_wage > 0.0:
|
||||
total_tax += eligible_wage * (rate / 100.0)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
last_uit = _uit
|
||||
tax = -total_tax / 12.0
|
||||
result, result_rate = wage, (tax / wage * 100.0)</field>
|
||||
<field name="partner_id" ref="res_partner_ir_5ta_cat"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
36
l10n_pe_hr_payroll/data/onp_rules.xml
Normal file
36
l10n_pe_hr_payroll/data/onp_rules.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<!-- Parameters -->
|
||||
<record id="rule_parameter_ee_onp" model="hr.rule.parameter">
|
||||
<field name="name">EE: ONP</field>
|
||||
<field name="code">ee_onp</field>
|
||||
<field name="country_id" ref="base.pe"/>
|
||||
</record>
|
||||
<record id="rule_parameter_ee_onp_2020" model="hr.rule.parameter.value">
|
||||
<field name="parameter_value">13.0</field>
|
||||
<field name="rule_parameter_id" ref="rule_parameter_ee_onp"/>
|
||||
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
|
||||
</record>
|
||||
|
||||
<!-- Partner -->
|
||||
<record id="res_partner_onp" model="res.partner">
|
||||
<field name="name">ONP</field>
|
||||
</record>
|
||||
|
||||
<!-- EE Rules -->
|
||||
<record id="hr_payroll_rule_ee_onp" model="hr.salary.rule">
|
||||
<field name="sequence" eval="190"/>
|
||||
<field name="struct_id" ref="hr_payroll_structure"/>
|
||||
<field name="category_id" ref="hr_payroll_category_ee_pe_onp"/>
|
||||
<field name="name">EE: PE ONP/SNP</field>
|
||||
<field name="code">EE_PE_ONP</field>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'onp' and rule == contract.pe_payroll_config_value('onp_rule_id')</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="amount_python_compute">result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_onp')</field>
|
||||
<field name="partner_id" ref="res_partner_onp"/>
|
||||
<field name="appears_on_payslip" eval="True"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -3,5 +3,6 @@
|
||||
from . import browsable_object
|
||||
from . import hr_contract
|
||||
from . import hr_payslip
|
||||
from . import pe_payroll_config
|
||||
from . import res_config_settings
|
||||
from . import update
|
||||
|
||||
@@ -1,33 +1,12 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
# from .us_payroll_config import FUTA_TYPE_NORMAL, \
|
||||
# FUTA_TYPE_BASIC, \
|
||||
# FUTA_TYPE_EXEMPT
|
||||
|
||||
|
||||
# class HrPayrollStructureType(models.Model):
|
||||
# _inherit = 'hr.payroll.structure.type'
|
||||
# default_schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
|
||||
|
||||
|
||||
# class HrPayrollStructure(models.Model):
|
||||
# _inherit = 'hr.payroll.structure'
|
||||
# schedule_pay = fields.Selection(selection_add=[('semi-monthly', 'Semi-monthly')])
|
||||
|
||||
|
||||
class PEHRContract(models.Model):
|
||||
_inherit = 'hr.contract'
|
||||
|
||||
# FUTA_TYPE_NORMAL = FUTA_TYPE_NORMAL
|
||||
# FUTA_TYPE_BASIC = FUTA_TYPE_BASIC
|
||||
# FUTA_TYPE_EXEMPT = FUTA_TYPE_EXEMPT
|
||||
pe_payroll_config_id = fields.Many2one('hr.contract.pe_payroll_config', 'Payroll Forms')
|
||||
|
||||
# us_payroll_config_id = fields.Many2one('hr.contract.us_payroll_config', 'Payroll Forms')
|
||||
# external_wages = fields.Float(string='External Existing Wages')
|
||||
|
||||
# Simplified fields for easier rules, state code will exempt based on contract's futa_type
|
||||
# futa_type = fields.Selection(related='us_payroll_config_id.fed_940_type')
|
||||
|
||||
# def us_payroll_config_value(self, name):
|
||||
# return self.us_payroll_config_id[name]
|
||||
def pe_payroll_config_value(self, name):
|
||||
return self.pe_payroll_config_id[name]
|
||||
|
||||
@@ -2,133 +2,71 @@
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
# from .federal.fed_940 import er_us_940_futa
|
||||
# from .federal.fed_941 import ee_us_941_fica_ss, \
|
||||
# ee_us_941_fica_m, \
|
||||
# ee_us_941_fica_m_add,\
|
||||
# er_us_941_fica_ss, \
|
||||
# er_us_941_fica_m, \
|
||||
# ee_us_941_fit
|
||||
# from .state.general import general_state_unemployment, \
|
||||
# general_state_income_withholding, \
|
||||
# is_us_state
|
||||
# from .state.al_alabama import al_alabama_state_income_withholding
|
||||
# from .state.ar_arkansas import ar_arkansas_state_income_withholding
|
||||
# from .state.az_arizona import az_arizona_state_income_withholding
|
||||
# from .state.ca_california import ca_california_state_income_withholding
|
||||
# from .state.co_colorado import co_colorado_state_income_withholding
|
||||
# from .state.ct_connecticut import ct_connecticut_state_income_withholding
|
||||
# from .state.de_delaware import de_delaware_state_income_withholding
|
||||
# from .state.ga_georgia import ga_georgia_state_income_withholding
|
||||
# from .state.hi_hawaii import hi_hawaii_state_income_withholding
|
||||
# from .state.ia_iowa import ia_iowa_state_income_withholding
|
||||
# from .state.id_idaho import id_idaho_state_income_withholding
|
||||
# from .state.il_illinois import il_illinois_state_income_withholding
|
||||
# from .state.in_indiana import in_indiana_state_income_withholding
|
||||
# from .state.ks_kansas import ks_kansas_state_income_withholding
|
||||
# from .state.ky_kentucky import ky_kentucky_state_income_withholding
|
||||
# from .state.la_louisiana import la_louisiana_state_income_withholding
|
||||
# from .state.me_maine import me_maine_state_income_withholding
|
||||
# from .state.mi_michigan import mi_michigan_state_income_withholding
|
||||
# from .state.mn_minnesota import mn_minnesota_state_income_withholding
|
||||
# from .state.mo_missouri import mo_missouri_state_income_withholding
|
||||
# from .state.ms_mississippi import ms_mississippi_state_income_withholding
|
||||
# from .state.mt_montana import mt_montana_state_income_withholding
|
||||
# from .state.nc_northcarolina import nc_northcarolina_state_income_withholding
|
||||
# from .state.nd_north_dakota import nd_north_dakota_state_income_withholding
|
||||
# from .state.ne_nebraska import ne_nebraska_state_income_withholding
|
||||
# from .state.nj_newjersey import nj_newjersey_state_income_withholding
|
||||
# from .state.nm_new_mexico import nm_new_mexico_state_income_withholding
|
||||
# from .state.ny_new_york import ny_new_york_state_income_withholding
|
||||
# from .state.oh_ohio import oh_ohio_state_income_withholding
|
||||
# from .state.ok_oklahoma import ok_oklahoma_state_income_withholding
|
||||
# from .state.ri_rhode_island import ri_rhode_island_state_income_withholding
|
||||
# from .state.sc_south_carolina import sc_south_carolina_state_income_withholding
|
||||
# from .state.ut_utah import ut_utah_state_income_withholding
|
||||
# from .state.vt_vermont import vt_vermont_state_income_withholding
|
||||
# from .state.va_virginia import va_virginia_state_income_withholding
|
||||
# from .state.wa_washington import wa_washington_fml_er, \
|
||||
# wa_washington_fml_ee, \
|
||||
# wa_washington_cares_ee
|
||||
# from .state.wi_wisconsin import wi_wisconsin_state_income_withholding
|
||||
# from .state.wv_west_virginia import wv_west_virginia_state_income_withholding
|
||||
|
||||
def _general_rate(payslip, wage, ytd_wage, wage_base=None, wage_start=None, rate=None):
|
||||
"""
|
||||
Function parameters:
|
||||
wage_base, wage_start, rate can either be strings (rule_parameters) or floats
|
||||
:return: result, result_rate(wage, percent)
|
||||
"""
|
||||
|
||||
# Resolve parameters. On exception, return (probably missing a year, would rather not have exception)
|
||||
if wage_base and isinstance(wage_base, str):
|
||||
try:
|
||||
wage_base = payslip.rule_parameter(wage_base)
|
||||
except (KeyError, UserError):
|
||||
return 0.0, 0.0
|
||||
|
||||
if wage_start and isinstance(wage_start, str):
|
||||
try:
|
||||
wage_start = payslip.rule_parameter(wage_start)
|
||||
except (KeyError, UserError):
|
||||
return 0.0, 0.0
|
||||
|
||||
if rate and isinstance(rate, str):
|
||||
try:
|
||||
rate = payslip.rule_parameter(rate)
|
||||
except (KeyError, UserError):
|
||||
return 0.0, 0.0
|
||||
|
||||
if not rate:
|
||||
return 0.0, 0.0
|
||||
else:
|
||||
# Rate assumed positive percentage!
|
||||
rate = -rate
|
||||
|
||||
if wage_base:
|
||||
remaining = wage_base - ytd_wage
|
||||
if remaining < 0.0:
|
||||
result = 0.0
|
||||
elif remaining < wage:
|
||||
result = remaining
|
||||
else:
|
||||
result = wage
|
||||
|
||||
# _logger.warn(' wage_base method result: ' + str(result) + ' rate: ' + str(rate))
|
||||
return result, rate
|
||||
if wage_start:
|
||||
if ytd_wage >= wage_start:
|
||||
# _logger.warn(' wage_start 1 method result: ' + str(wage) + ' rate: ' + str(rate))
|
||||
return wage, rate
|
||||
if ytd_wage + wage <= wage_start:
|
||||
# _logger.warn(' wage_start 2 method result: ' + str(0.0) + ' rate: ' + str(0.0))
|
||||
return 0.0, 0.0
|
||||
# _logger.warn(' wage_start 3 method result: ' + str((wage - (wage_start - ytd_wage))) + ' rate: ' + str(rate))
|
||||
return (wage - (wage_start - ytd_wage)), rate
|
||||
|
||||
# If the wage doesn't have a start or a base
|
||||
# _logger.warn(' basic result: ' + str(wage) + ' rate: ' + str(rate))
|
||||
return wage, rate
|
||||
|
||||
|
||||
class HRPayslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
# From IRS Publication 15-T or logically (annually, bi-monthly)
|
||||
PAY_PERIODS_IN_YEAR = {
|
||||
'annually': 1,
|
||||
'semi-annually': 2,
|
||||
'quarterly': 4,
|
||||
'bi-monthly': 6,
|
||||
'monthly': 12,
|
||||
'semi-monthly': 24,
|
||||
'bi-weekly': 26,
|
||||
'weekly': 52,
|
||||
'daily': 260,
|
||||
}
|
||||
|
||||
def _get_base_local_dict(self):
|
||||
res = super()._get_base_local_dict()
|
||||
res.update({
|
||||
# 'er_us_940_futa': er_us_940_futa,
|
||||
# 'ee_us_941_fica_ss': ee_us_941_fica_ss,
|
||||
# 'ee_us_941_fica_m': ee_us_941_fica_m,
|
||||
# 'ee_us_941_fica_m_add': ee_us_941_fica_m_add,
|
||||
# 'er_us_941_fica_ss': er_us_941_fica_ss,
|
||||
# 'er_us_941_fica_m': er_us_941_fica_m,
|
||||
# 'ee_us_941_fit': ee_us_941_fit,
|
||||
# 'general_state_unemployment': general_state_unemployment,
|
||||
# 'general_state_income_withholding': general_state_income_withholding,
|
||||
# 'is_us_state': is_us_state,
|
||||
# 'al_alabama_state_income_withholding': al_alabama_state_income_withholding,
|
||||
# 'ar_arkansas_state_income_withholding': ar_arkansas_state_income_withholding,
|
||||
# 'az_arizona_state_income_withholding': az_arizona_state_income_withholding,
|
||||
# 'ca_california_state_income_withholding': ca_california_state_income_withholding,
|
||||
# 'co_colorado_state_income_withholding': co_colorado_state_income_withholding,
|
||||
# 'ct_connecticut_state_income_withholding': ct_connecticut_state_income_withholding,
|
||||
# 'de_delaware_state_income_withholding': de_delaware_state_income_withholding,
|
||||
# 'ga_georgia_state_income_withholding': ga_georgia_state_income_withholding,
|
||||
# 'hi_hawaii_state_income_withholding': hi_hawaii_state_income_withholding,
|
||||
# 'ia_iowa_state_income_withholding': ia_iowa_state_income_withholding,
|
||||
# 'id_idaho_state_income_withholding': id_idaho_state_income_withholding,
|
||||
# 'il_illinois_state_income_withholding': il_illinois_state_income_withholding,
|
||||
# 'in_indiana_state_income_withholding': in_indiana_state_income_withholding,
|
||||
# 'ks_kansas_state_income_withholding': ks_kansas_state_income_withholding,
|
||||
# 'ky_kentucky_state_income_withholding':ky_kentucky_state_income_withholding,
|
||||
# 'la_louisiana_state_income_withholding': la_louisiana_state_income_withholding,
|
||||
# 'me_maine_state_income_withholding': me_maine_state_income_withholding,
|
||||
# 'mi_michigan_state_income_withholding': mi_michigan_state_income_withholding,
|
||||
# 'mn_minnesota_state_income_withholding': mn_minnesota_state_income_withholding,
|
||||
# 'mo_missouri_state_income_withholding': mo_missouri_state_income_withholding,
|
||||
# 'ms_mississippi_state_income_withholding': ms_mississippi_state_income_withholding,
|
||||
# 'mt_montana_state_income_withholding': mt_montana_state_income_withholding,
|
||||
# 'nc_northcarolina_state_income_withholding': nc_northcarolina_state_income_withholding,
|
||||
# 'nd_north_dakota_state_income_withholding': nd_north_dakota_state_income_withholding,
|
||||
# 'ne_nebraska_state_income_withholding': ne_nebraska_state_income_withholding,
|
||||
# 'nj_newjersey_state_income_withholding': nj_newjersey_state_income_withholding,
|
||||
# 'nm_new_mexico_state_income_withholding': nm_new_mexico_state_income_withholding,
|
||||
# 'ny_new_york_state_income_withholding': ny_new_york_state_income_withholding,
|
||||
# 'oh_ohio_state_income_withholding': oh_ohio_state_income_withholding,
|
||||
# 'ok_oklahoma_state_income_withholding': ok_oklahoma_state_income_withholding,
|
||||
# 'ri_rhode_island_state_income_withholding': ri_rhode_island_state_income_withholding,
|
||||
# 'sc_south_carolina_state_income_withholding': sc_south_carolina_state_income_withholding,
|
||||
# 'ut_utah_state_income_withholding': ut_utah_state_income_withholding,
|
||||
# 'vt_vermont_state_income_withholding': vt_vermont_state_income_withholding,
|
||||
# 'va_virginia_state_income_withholding': va_virginia_state_income_withholding,
|
||||
# 'wa_washington_fml_er': wa_washington_fml_er,
|
||||
# 'wa_washington_fml_ee': wa_washington_fml_ee,
|
||||
# 'wa_washington_cares_ee': wa_washington_cares_ee,
|
||||
# 'wi_wisconsin_state_income_withholding': wi_wisconsin_state_income_withholding,
|
||||
# 'wv_west_virginia_state_income_withholding': wv_west_virginia_state_income_withholding,
|
||||
'general_rate': _general_rate,
|
||||
})
|
||||
return res
|
||||
|
||||
def get_year(self):
|
||||
# Helper method to get the year (normalized between Odoo Versions)
|
||||
return self.date_to.year
|
||||
|
||||
def get_pay_periods_in_year(self):
|
||||
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)
|
||||
|
||||
39
l10n_pe_hr_payroll/models/pe_payroll_config.py
Normal file
39
l10n_pe_hr_payroll/models/pe_payroll_config.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class HRContractPEPayrollConfig(models.Model):
|
||||
_name = 'hr.contract.pe_payroll_config'
|
||||
_description = 'Contract PE Payroll Forms'
|
||||
|
||||
name = fields.Char(string="Description")
|
||||
employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
|
||||
|
||||
retirement_type = fields.Selection([
|
||||
('afp', 'AFP'),
|
||||
('onp', 'ONP'),
|
||||
('retired', 'Retired'),
|
||||
], string='Retirement Type', required=True, default='afp')
|
||||
|
||||
onp_rule_id = fields.Many2one('hr.salary.rule', string='ONP Rule', domain=[('code', '=like', 'EE_PE_ONP%')])
|
||||
|
||||
# AFP Type may actually be company specific....
|
||||
afp_type = fields.Selection([
|
||||
('habitat', 'Habitat'),
|
||||
('integra', 'Integra'),
|
||||
('prima', 'Prima'),
|
||||
('profuturo', 'Profuturo'),
|
||||
], string='AFP Type', default='profuturo')
|
||||
afp_comision_type = fields.Selection([
|
||||
('mixta', 'Mixta'),
|
||||
('non_mixta', 'Non-Mixta'),
|
||||
], string='AFP Commission Type', default='mixta')
|
||||
|
||||
comp_ss_type = fields.Selection([
|
||||
('essalud', 'Essalud'),
|
||||
('eps', 'EPS'),
|
||||
], string='Company Social Services', default='essalud')
|
||||
comp_ss_eps_rule_id = fields.Many2one('hr.salary.rule', string='Company Social Security EPS Rule')
|
||||
comp_life_insurance_rule_id = fields.Many2one('hr.salary.rule', string='Company Life Insurance Rule')
|
||||
comp_risk_insurance_rule_id = fields.Many2one('hr.salary.rule', string='Company Risk Insurance Rule')
|
||||
2
l10n_pe_hr_payroll/security/ir.model.access.csv
Normal file
2
l10n_pe_hr_payroll/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_hr_contract_pe_payroll_config,hr.contract.pe_payroll_config,model_hr_contract_pe_payroll_config,hr_payroll.group_hr_payroll_manager,1,1,1,1
|
||||
|
@@ -5,4 +5,5 @@
|
||||
# common remains for site specific tests
|
||||
|
||||
from . import common
|
||||
from . import test_2022
|
||||
from . import test_2020
|
||||
# from . import test_2022
|
||||
|
||||
@@ -58,7 +58,12 @@ class TestPePayslip(common.TransactionCase):
|
||||
if not 'schedule_pay' in kwargs:
|
||||
kwargs['schedule_pay'] = 'monthly'
|
||||
schedule_pay = kwargs['schedule_pay']
|
||||
config_model = self.env['hr.contract.pe_payroll_config']
|
||||
contract_model = self.env['hr.contract']
|
||||
config_values = {
|
||||
'name': 'Test Config Values',
|
||||
'employee_id': employee.id,
|
||||
}
|
||||
contract_values = {
|
||||
'name': 'Test Contract',
|
||||
'employee_id': employee.id,
|
||||
@@ -69,12 +74,18 @@ class TestPePayslip(common.TransactionCase):
|
||||
if hasattr(val, 'id'):
|
||||
val = val.id
|
||||
found = False
|
||||
if hasattr(config_model, key):
|
||||
config_values[key] = val
|
||||
found = True
|
||||
if hasattr(contract_model, key):
|
||||
contract_values[key] = val
|
||||
found = True
|
||||
if not found:
|
||||
self._logger.warn('cannot locate attribute names "%s" on contract' % (key, ))
|
||||
|
||||
# PE Payroll Config Defaults Should be set on the Model
|
||||
config = config_model.create(config_values)
|
||||
contract_values['pe_payroll_config_id'] = config.id
|
||||
|
||||
# Some Basic Defaults
|
||||
if not contract_values.get('state'):
|
||||
|
||||
142
l10n_pe_hr_payroll/tests/test_2020.py
Normal file
142
l10n_pe_hr_payroll/tests/test_2020.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||
|
||||
from .common import TestPePayslip, process_payslip
|
||||
|
||||
|
||||
class Test2020(TestPePayslip):
|
||||
|
||||
###
|
||||
# 2020 Taxes and Rates
|
||||
###
|
||||
|
||||
def test_2020_taxes(self):
|
||||
# High salary to hit the maximum for AFP_SEGURO
|
||||
salary = 8000.00
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
retirement_type='afp',
|
||||
afp_type='profuturo',
|
||||
afp_comision_type='mixta',
|
||||
comp_ss_type='essalud',
|
||||
)
|
||||
self._log(contract.read())
|
||||
|
||||
self._log('2020 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
# Employee
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_PENSIONES'], -cats['GROSS'] * (10.0 / 100.0))
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_SEGURO'], -cats['GROSS'] * (1.35 / 100.0))
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_COMISION_MIXTA'], -cats['GROSS'] * (0.67 / 100.0))
|
||||
# Employer
|
||||
self.assertPayrollEqual(rules['ER_PE_ESSALUD'], -cats['GROSS'] * (6.75 / 100.0))
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2020 tax second payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-02-01', '2020-02-28')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
# Employee
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_PENSIONES'], -cats['GROSS'] * (10.0 / 100.0))
|
||||
|
||||
self.assertTrue(cats['GROSS'] < 9707.03)
|
||||
# Seguro has a wage base.
|
||||
second_seguro = -(9707.03 - cats['GROSS']) * (1.35 / 100.0)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_SEGURO'], second_seguro)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_COMISION_MIXTA'], -cats['GROSS'] * (0.67 / 100.0))
|
||||
# Employer
|
||||
self.assertPayrollEqual(rules['ER_PE_ESSALUD'], -cats['GROSS'] * (6.75 / 100.0))
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
self._log('2020 tax third payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-03-01', '2020-03-31')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
# Employee
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_PENSIONES'], -cats['GROSS'] * (10.0 / 100.0))
|
||||
|
||||
self.assertTrue(cats['GROSS'] < 9707.03)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_SEGURO'], 0.0)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_COMISION_MIXTA'], -cats['GROSS'] * (0.67 / 100.0))
|
||||
# Employer
|
||||
self.assertPayrollEqual(rules['ER_PE_ESSALUD'], -cats['GROSS'] * (6.75 / 100.0))
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_2020_onp(self):
|
||||
salary = 3500.00
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
retirement_type='onp',
|
||||
onp_rule_id=self.env.ref('l10n_pe_hr_payroll.hr_payroll_rule_ee_onp').id,
|
||||
comp_ss_type='essalud',
|
||||
)
|
||||
self._log(contract.read())
|
||||
|
||||
self._log('2020 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
# Employee
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_PENSIONES'], 0.0)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_SEGURO'], 0.0)
|
||||
self.assertPayrollEqual(rules['EE_PE_AFP_COMISION_MIXTA'], 0.0)
|
||||
self.assertPayrollEqual(cats['EE_PE_ONP'], -cats['GROSS'] * (13.0 / 100.0))
|
||||
# Employer
|
||||
self.assertPayrollEqual(rules['ER_PE_ESSALUD'], -cats['GROSS'] * (6.75 / 100.0))
|
||||
|
||||
process_payslip(payslip)
|
||||
|
||||
def test_2020_ir_5ta_cat(self):
|
||||
salary = 1500.00
|
||||
|
||||
employee = self._createEmployee()
|
||||
|
||||
contract = self._createContract(employee,
|
||||
wage=salary,
|
||||
retirement_type='onp',
|
||||
onp_rule_id=self.env.ref('l10n_pe_hr_payroll.hr_payroll_rule_ee_onp').id,
|
||||
comp_ss_type='essalud',
|
||||
)
|
||||
|
||||
self._log('2020 tax first payslip:')
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_IR_5TA_CAT'], 0.0)
|
||||
payslip.state = 'cancel'
|
||||
payslip.unlink()
|
||||
|
||||
# larger salary to trigger calculation
|
||||
salary = 3000.0
|
||||
contract.wage = salary
|
||||
payslip = self._createPayslip(employee, '2020-01-01', '2020-01-31')
|
||||
payslip.compute_sheet()
|
||||
cats = self._getCategories(payslip)
|
||||
rules = self._getRules(payslip)
|
||||
self.assertPayrollEqual(cats['GROSS'], salary)
|
||||
self.assertPayrollEqual(rules['EE_PE_IR_5TA_CAT'], -74.67)
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="hr_contract_view_form_inherit_l10n_us" model="ir.ui.view">
|
||||
<record id="hr_contract_view_form_inherit_l10n_pe" model="ir.ui.view">
|
||||
<field name="name">hr.contract.form.inherit</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='structure_type_id']" position="after">
|
||||
<!-- <field name="us_payroll_config_id"
|
||||
<field name="pe_payroll_config_id"
|
||||
domain="[('employee_id', '=', employee_id)]"
|
||||
attrs="{'invisible': [('structure_type_id', '!=', %(l10n_us_hr_payroll.structure_type_employee)s)],
|
||||
'required': [('structure_type_id', '=', %(l10n_us_hr_payroll.structure_type_employee)s)]}"
|
||||
context="{'default_employee_id': employee_id}"/> -->
|
||||
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}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
71
l10n_pe_hr_payroll/views/pe_payroll_config_views.xml
Normal file
71
l10n_pe_hr_payroll/views/pe_payroll_config_views.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="pe_payroll_config_tree" model="ir.ui.view">
|
||||
<field name="name">hr.contract.pe_payroll_config.tree</field>
|
||||
<field name="model">hr.contract.pe_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Peru Employee Payroll Forms">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<field name="retirement_type"/>
|
||||
<field name="create_date"/>
|
||||
<field name="write_date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pe_payroll_config_form" model="ir.ui.view">
|
||||
<field name="name">hr.contract.pe_payroll_config.form</field>
|
||||
<field name="model">hr.contract.pe_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Employee Payroll Forms">
|
||||
<sheet>
|
||||
<group name="General">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
</group>
|
||||
<group>
|
||||
<group name="employee" string="Employee">
|
||||
<field name="retirement_type"/>
|
||||
<field name="onp_rule_id" attrs="{'invisible': [('retirement_type', '!=', 'onp')]}"/>
|
||||
<field name="afp_type" attrs="{'invisible': [('retirement_type', '!=', 'afp')]}"/>
|
||||
<field name="afp_comision_type" attrs="{'invisible': [('retirement_type', '!=', 'afp')]}"/>
|
||||
</group>
|
||||
<group name="employer" string="Employer">
|
||||
<field name="comp_ss_type" />
|
||||
<field name="comp_ss_eps_rule_id" attrs="{'invisible': [('comp_ss_type', '!=', 'eps')]}"/>
|
||||
<field name="comp_life_insurance_rule_id" />
|
||||
<field name="comp_risk_insurance_rule_id" />
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pe_payroll_config_search" model="ir.ui.view">
|
||||
<field name="name">hr.contract.pe_payroll_config.search</field>
|
||||
<field name="model">hr.contract.pe_payroll_config</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Peru Employee Payroll Forms Search">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pe_payroll_config_action_main" model="ir.actions.act_window">
|
||||
<field name="name">Peru Employee Payroll Forms</field>
|
||||
<field name="res_model">hr.contract.pe_payroll_config</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
No Forms
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="pe_payroll_config_menu_main" name="Peru Payroll Forms"
|
||||
action="pe_payroll_config_action_main"
|
||||
sequence="50" parent="hr_payroll.menu_hr_payroll_employees_root"/>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user