Merge branch 'mig/15.0/l10n_pe_hr_payroll_account' into 15.0

This commit is contained in:
Jared Kipe
2022-05-11 21:35:53 +00:00
35 changed files with 1488 additions and 16 deletions

View File

@@ -138,6 +138,10 @@ class Payslips(BrowsableObject):
def paid_amount(self):
return self.dict._get_paid_amount()
# Hibou helper
@property
def pay_periods_in_year(self):
return self.dict.get_pay_periods_in_year()
# Patch over Core
browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__
@@ -149,3 +153,4 @@ browsable_object.WorkedDays.sum = WorkedDays.sum
browsable_object.Payslips._compile_browsable_query = Payslips._compile_browsable_query
browsable_object.Payslips.sum = Payslips.sum
browsable_object.Payslips.sum_category = Payslips.sum_category
browsable_object.Payslips.pay_periods_in_year = Payslips.pay_periods_in_year

View File

@@ -6,6 +6,19 @@ from odoo import api, fields, models
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,
}
# normal_wage is an integer field, but that lacks precision.
normal_wage = fields.Float(compute='_compute_normal_wage', store=True)
# We need to be able to support more complexity,
@@ -35,3 +48,6 @@ class HrPayslip(models.Model):
# This would be a good place to override though with a 'work type'
# based mechanism, like a minimum rate or 'rate card' implementation
return self.contract_id._get_contract_wage(work_type=work_type)
def get_pay_periods_in_year(self):
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)

View File

@@ -0,0 +1,12 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models
def _post_install_hook(cr, registry):
"""
This method will set the default for the Payslip Sum Behavior
"""
cr.execute("SELECT id FROM ir_config_parameter WHERE key = 'hr_payroll.payslip.sum_behavior';")
existing = cr.fetchall()
if not existing:
cr.execute("INSERT INTO ir_config_parameter (key, value) VALUES ('hr_payroll.payslip.sum_behavior', 'date');")

View File

@@ -0,0 +1,36 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{
'name': 'Peru - Payroll',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '15.0.2022.0.0',
'category': 'Payroll Localization',
'depends': [
'hr_payroll_hibou',
'hr_contract_reports',
'hibou_professional',
],
'description': """
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_4ta_cat_rules.xml',
'data/ir_5ta_cat_rules.xml',
'data/er_rules.xml',
'views/hr_contract_views.xml',
'views/pe_payroll_config_views.xml',
],
'demo': [
],
'auto_install': False,
'post_init_hook': '_post_install_hook',
'license': 'OPL-1',
}

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Parameters -->
<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_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>
<!-- EE Rules -->
<record id="hr_payroll_rule_ee_afp_pensiones" 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_afp"/>
<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>
<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')][4]</field>
<field name="appears_on_payslip" eval="True"/>
</record>
<record id="hr_payroll_rule_ee_afp_seguro" model="hr.salary.rule">
<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 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>
<field name="amount_select">code</field>
<field name="amount_python_compute">
wage = categories.GROSS
# wage_max is monthly
wage_max = payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][5]
# normalize wage_max to pay period
pay_periods_in_year = payslip.pay_periods_in_year
wage_max = (wage_max * 12.0) / pay_periods_in_year
eligible_wage = min(wage, wage_max)
rate = -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][3]
result, result_rate = eligible_wage, rate
</field>
<field name="appears_on_payslip" eval="True"/>
</record>
<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 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>
<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')][1]</field>
<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 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>
<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="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="structure_type_employee" model="hr.payroll.structure.type">
<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 (5ta Cat.)</field>
<field name="country_id" ref="base.pe"/>
<field name="type_id" ref="l10n_pe_hr_payroll.structure_type_employee"/>
<field name="unpaid_work_entry_type_ids" eval="[
(4, ref('hr_work_entry_contract.work_entry_type_unpaid_leave')),
]"/>
</record>
<record id="l10n_pe_hr_payroll.structure_type_employee" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_payroll_structure"/>
</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"/>
</record>
<record id="l10n_pe_hr_payroll.structure_type_employee_4ta_cat" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_payroll_structure_4ta_cat"/>
</record>
<!-- AFP -->
<record id="hr_payroll_category_ee_pe_afp" model="hr.salary.rule.category">
<field name="name">EE: AFP</field>
<field name="code">EE_PE_AFP</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_er_pe_afp" model="hr.salary.rule.category">
<field name="name">ER: AFP</field>
<field name="code">ER_PE_AFP</field>
<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 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 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>
<field name="code">EE_PE_ESSALUD</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
<record id="hr_payroll_category_er_essalud" model="hr.salary.rule.category">
<field name="name">EE: Essalud</field>
<field name="code">ER_PE_ESSALUD</field>
<field name="parent_id" ref="hr_payroll.COMP"/>
</record>
<!-- Bonus input & rule -->
<record id="input_type_bono" model="hr.payslip.input.type">
<field name="name">Bonus</field>
<field name="code">BONO</field>
<field name="country_id" ref="base.pe" />
<field name="struct_ids" eval="[
(4, ref('l10n_pe_hr_payroll.hr_payroll_structure')),
]" />
</record>
<record id="hr_salary_rule_bonus" model="hr.salary.rule">
<field name="condition_select">python</field>
<field name="condition_python">result = inputs.BONO.amount > 0.0 if inputs.BONO else False</field>
<field name="amount_select">code</field>
<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">Bonus</field>
<field name="sequence" eval="25"/>
<field name="struct_id" ref="l10n_pe_hr_payroll.hr_payroll_structure"/>
</record>
</odoo>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Parameters -->
<record id="rule_parameter_er_essalud" model="hr.rule.parameter">
<field name="name">ER: Essalud</field>
<field name="code">er_essalud</field>
<field name="country_id" ref="base.pe"/>
</record>
<record id="rule_parameter_er_essalud_2020" model="hr.rule.parameter.value">
<field name="parameter_value">6.75</field>
<field name="rule_parameter_id" ref="rule_parameter_er_essalud"/>
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
</record>
<!-- ER Rules -->
<record id="hr_payroll_rule_er_essalud" model="hr.salary.rule">
<field name="sequence" eval="220"/>
<field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_er_essalud"/>
<field name="name">ER: PE Essalud</field>
<field name="code">ER_PE_ESSALUD</field>
<field name="condition_select">python</field>
<field name="condition_python">result = categories.BASIC</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result, result_rate = categories.BASIC, -payslip.rule_parameter('er_essalud')</field>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Commissions from hr_payroll_commission -->
<record id="hr_salary_rule_commission" model="hr.salary.rule">
<field name="condition_select">python</field>
<field name="condition_python">result = inputs.COMMISSION.amount > 0.0 if inputs.COMMISSION else False</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.COMMISSION.amount if inputs.COMMISSION else 0</field>
<field name="code">BASIC_COM</field>
<field name="category_id" ref="hr_payroll.BASIC"/>
<field name="name">Commissions</field>
<field name="sequence" eval="20"/>
<field name="struct_id" ref="l10n_pe_hr_payroll.hr_payroll_structure"/>
</record>
<!-- Badges from hr_payroll_gamification -->
<record id="hr_salary_rule_gamification" model="hr.salary.rule">
<field name="condition_select">python</field>
<field name="condition_python">result = inputs.BADGES.amount > 0.0 if inputs.BADGES else False</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = inputs.BADGES.amount if inputs.BADGES else 0</field>
<field name="code">BASIC_BADGES</field>
<field name="category_id" ref="hr_payroll.BASIC"/>
<field name="name">Badges</field>
<field name="sequence" eval="20"/>
<field name="struct_id" ref="l10n_pe_hr_payroll.hr_payroll_structure"/>
</record>
</odoo>

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

@@ -0,0 +1,60 @@
<?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>
<record id="rule_parameter_ee_ir_5ta_cat_ley_29351" model="hr.rule.parameter">
<field name="name">EE: IR 5ta Cat. Bonificación Extraordinaria (Ley 29351)</field>
<field name="code">ee_ir_5ta_cat_ley_29351</field>
<field name="country_id" ref="base.pe"/>
</record>
<record id="rule_parameter_ee_ir_5ta_cat_ley_29351_2020" model="hr.rule.parameter.value">
<field name="parameter_value">9.0</field>
<field name="rule_parameter_id" ref="rule_parameter_ee_ir_5ta_cat_ley_29351"/>
<field name="date_from" eval="datetime(2020, 1, 1).date()"/>
</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 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>
<field name="amount_select">code</field>
<field name="amount_python_compute">result, result_rate = ir_5ta_cat(payslip, categories, worked_days, inputs, BASIC)</field>
<field name="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,30 @@
<?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>
<!-- 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'</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="appears_on_payslip" eval="True"/>
</record>
</odoo>

View File

@@ -0,0 +1,323 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_pe_hr_payroll
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-05-06 01:45+0000\n"
"PO-Revision-Date: 2022-05-06 01:45+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__retirement_type__afp
msgid "AFP"
msgstr "AFP"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__afp_comision_type
msgid "AFP Commission Type"
msgstr "Tipo de Comisión de AFP"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__afp_type
msgid "AFP Type"
msgstr "Tipo de AFP"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_salary_rule_gamification
msgid "Badges"
msgstr "Insignias"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_salary_rule_bonus
msgid "Bonus"
msgstr "Bono"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_salary_rule_commission
msgid "Commissions"
msgstr "Comisiones"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__comp_ss_eps_rule_id
msgid "Company Social Security EPS Rule"
msgstr "Regla EPS de la Seguridad Social de la Empresa"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__comp_ss_type
msgid "Company Social Services"
msgstr "Servicios Social de la Empresa"
#. module: l10n_pe_hr_payroll
#: model:ir.model,name:l10n_pe_hr_payroll.model_hr_contract_pe_payroll_config
msgid "Contract PE Payroll Forms"
msgstr "Formularios de Nómina del Contrato PE"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__create_uid
msgid "Created by"
msgstr "Creado Por"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__create_date
msgid "Created on"
msgstr "Creado En"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__date_hired
msgid "Date Hired"
msgstr "Fecha de Contratación"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__name
msgid "Description"
msgstr "Descripción"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__display_name
msgid "Display Name"
msgstr "Nombre para Mostrar"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_ee_pe_afp
msgid "EE: AFP"
msgstr "EE: AFP"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_er_essalud
msgid "EE: Essalud"
msgstr "EE: Essalud"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_ee_essalud
msgid "EE: Essalud (rem)"
msgstr "EE: Essalud (rem)"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_ee_ir_4ta_cat
msgid "EE: IR 4th Cat."
msgstr "EE: IR 4ta Cat."
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_ee_ir_5ta_cat
msgid "EE: IR 5th Cat."
msgstr "EE: IR 5ta Cat."
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_ee_pe_onp
msgid "EE: ONP"
msgstr "EE: ONP"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_afp_comision_non_mixta
msgid "EE: PE AFP Comission (Non-Mixed)"
msgstr "EE: PE AFP Comisión (No-Mixta)"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_afp_seguro
msgid "EE: PE AFP Insurance"
msgstr "EE: PE AFP Seguro"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_afp_comision_mixta
msgid "EE: PE AFP Mixed Commission"
msgstr "EE: PE AFP Comisión Mixta"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_afp_pensiones
msgid "EE: PE AFP Pensions"
msgstr "EE: PE AFP Pensiones"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_ir_4ta_cat
msgid "EE: PE IR 4th Cat."
msgstr "EE: PE IR 4ta Cat."
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_ir_5ta_cat
msgid "EE: PE IR 5th Cat."
msgstr "EE: PE IR 5ta Cat."
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_ee_onp
msgid "EE: PE ONP/SNP"
msgstr "EE: PE ONP/SNP"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__comp_ss_type__eps
msgid "EPS"
msgstr "EPS"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_er_pe_afp
msgid "ER: AFP"
msgstr "ER: AFP"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_er_ir_5ta_cat
msgid "ER: IR 5th Cat."
msgstr "ER: IR 5ta Cat."
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule.category,name:l10n_pe_hr_payroll.hr_payroll_category_er_pe_onp
msgid "ER: ONP"
msgstr "ER: ONP"
#. module: l10n_pe_hr_payroll
#: model:hr.salary.rule,name:l10n_pe_hr_payroll.hr_payroll_rule_er_essalud
msgid "ER: PE Essalud"
msgstr "ER: PE Essalud"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__employee_id
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_form
msgid "Employee"
msgstr "Empleado"
#. module: l10n_pe_hr_payroll
#: model:ir.model,name:l10n_pe_hr_payroll.model_hr_contract
msgid "Employee Contract"
msgstr "Contrato de Empleado"
#. module: l10n_pe_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_form
msgid "Employee Payroll Forms"
msgstr "Formularios de Nómina del Empleado"
#. module: l10n_pe_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_form
msgid "Employer"
msgstr "Empleador"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__comp_ss_type__essalud
msgid "Essalud"
msgstr "Essalud"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract__pe_payroll_ee_4ta_cat_exempt
msgid "Exempt from 4th Cat. withholding."
msgstr "Exento de Retención de la 4ta Cat."
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,help:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__date_hired
msgid "For calculations like IR 5TH CAT."
msgstr "Para Cálculos como IR 5TA CAT."
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_type__habitat
msgid "Habitat"
msgstr "Habitat"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__id
msgid "ID"
msgstr "ID"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_type__integra
msgid "Integra"
msgstr "Integra"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config____last_update
msgid "Last Modified on"
msgstr "Última Modificación En"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__write_uid
msgid "Last Updated by"
msgstr "Última Actualización Por"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__write_date
msgid "Last Updated on"
msgstr "Ultima Actualización En"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_comision_type__mixta
msgid "Mixed"
msgstr "Mixta"
#. module: l10n_pe_hr_payroll
#: model_terms:ir.actions.act_window,help:l10n_pe_hr_payroll.pe_payroll_config_action_main
msgid "No Forms"
msgstr "Sin Formularios"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_comision_type__non_mixta
msgid "Non-Mixed"
msgstr "No-Mixta"
#. module: l10n_pe_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_form
msgid "Not supported. Specify rule."
msgstr "No Soportado. Indique una Regla"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__retirement_type__onp
msgid "ONP"
msgstr "ONP"
#. module: l10n_pe_hr_payroll
#: model:ir.model,name:l10n_pe_hr_payroll.model_hr_payslip
msgid "Pay Slip"
msgstr "Recibo de Nómina"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract__pe_payroll_config_id
msgid "Payroll Forms"
msgstr "Formularios de Nómina"
#. module: l10n_pe_hr_payroll
#: model:ir.actions.act_window,name:l10n_pe_hr_payroll.pe_payroll_config_action_main
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_tree
msgid "Peru Employee Payroll Forms"
msgstr "Formularios de Nómina del Empleado de Perú"
#. module: l10n_pe_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_pe_hr_payroll.pe_payroll_config_search
msgid "Peru Employee Payroll Forms Search"
msgstr "Búsqueda de Formularios de Nómina del Empleado de Perú"
#. module: l10n_pe_hr_payroll
#: model:ir.ui.menu,name:l10n_pe_hr_payroll.pe_payroll_config_menu_main
msgid "Peru Payroll Forms"
msgstr "Formularios de Nómina de Perú"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_type__prima
msgid "Prima"
msgstr "Prima"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__afp_type__profuturo
msgid "Profuturo"
msgstr "Profuturo"
#. module: l10n_pe_hr_payroll
#: model:ir.model,name:l10n_pe_hr_payroll.model_publisher_warranty_contract
msgid "Publisher Warranty Contract"
msgstr "Contrato de Garantía del Editorial"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields.selection,name:l10n_pe_hr_payroll.selection__hr_contract_pe_payroll_config__retirement_type__retired
msgid "Retired"
msgstr "Jubilado"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,field_description:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__retirement_type
msgid "Retirement Type"
msgstr "Tipo de Jubilación"
#. module: l10n_pe_hr_payroll
#: model:ir.model.fields,help:l10n_pe_hr_payroll.field_hr_contract_pe_payroll_config__comp_ss_eps_rule_id
msgid "Rule code prefix 'ER_PE_EPS' to select here."
msgstr "Prefijo del Código de la Regla 'ER_PE_EPS' a Seleccionar Aquí."

View File

@@ -0,0 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import hr_contract
from . import hr_payslip
from . import pe_payroll_config
from . import update

View File

@@ -0,0 +1,13 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, fields, models
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

@@ -0,0 +1,19 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
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
class HRPayslip(models.Model):
_inherit = 'hr.payslip'
def _get_base_local_dict(self):
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

@@ -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)
date_hired = fields.Date(string='Date Hired', required=True, default=fields.Date.today,
help='For calculations like IR 5TH CAT.')
retirement_type = fields.Selection([
('afp', 'AFP'),
('onp', 'ONP'),
('retired', 'Retired'),
], string='Retirement Type', required=True, default='afp')
# 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', 'Mixed'),
('non_mixta', 'Non-Mixed'),
], 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',
domain=[('code', '=like', 'ER_PE_EPS%')],
help="Rule code prefix 'ER_PE_EPS' to select here.")

View File

@@ -0,0 +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,58 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
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

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

@@ -0,0 +1,91 @@
# 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
# 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
periods_in_year_eligible = pay_periods_in_year
if hired_in_year:
periods_in_year_eligible = round(((next_year - date_hired).days / 365) * pay_periods_in_year)
# 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)
total_tax = 0.0
if over_7uit > 0.0:
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
# 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 - ytd_tax) / pay_periods_at_current
return wage_period, (tax / wage_period * 100.0)

View File

@@ -0,0 +1,32 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import datetime
from odoo import api, fields, models
class PublisherWarrantyContract(models.AbstractModel):
_inherit = 'publisher_warranty.contract'
def _get_hibou_modules(self):
modules = super(PublisherWarrantyContract, self)._get_hibou_modules()
try:
today_date = fields.Date.today()
last_thirty_date = today_date - datetime.timedelta(days=30)
today = fields.Date.to_string(today_date + datetime.timedelta(days=1)) # Dates vs Datetimes, pad out a day
last_thirty = fields.Date.to_string(last_thirty_date)
self.env.cr.execute(
'SELECT COUNT(DISTINCT(employee_id)) FROM hr_payslip WHERE create_date BETWEEN %s AND %s',
(last_thirty, today))
employee_count = self.env.cr.fetchone()[0] or 0
modules.update({
'l10n_pe_hr_payroll': employee_count,
})
except:
pass
return modules
@api.model
def hibou_payroll_modules_to_update(self):
res = super().hibou_payroll_modules_to_update()
res.append('l10n_pe_hr_payroll')
return res

View 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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
# Tests moved to `l10n_pe_hr_payroll_params`
# common remains for site specific tests
from . import common

View File

@@ -0,0 +1,65 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo.addons.hr_payroll_hibou.tests import common
process_payslip = common.process_payslip
class TestPePayslip(common.TestPayslip):
def setUp(self):
super().setUp()
self.structure_type = self.env.ref('l10n_pe_hr_payroll.structure_type_employee')
self.structure = self.env.ref('l10n_pe_hr_payroll.hr_payroll_structure')
self.structure_type.default_struct_id = self.structure
# self.debug = True
self._log('PE structue_type %s %s and structure %s %s' % (self.structure_type, self.structure_type.name, self.structure, self.structure.name))
self.country_pe = self.env.ref('base.pe')
def _createEmployee(self):
employee = super()._createEmployee()
employee.country_id = self.country_pe
return employee
def _createContract(self, employee, **kwargs):
if not 'schedule_pay' in kwargs:
kwargs['schedule_pay'] = 'monthly'
config_model = self.env['hr.contract.pe_payroll_config']
schedule_pay = kwargs['schedule_pay']
config_values = {
'name': 'Test Config Values',
'employee_id': employee.id,
}
contract_values = {
'name': 'Test Contract',
'employee_id': employee.id,
}
for key, val in kwargs.items():
# Assume any Odoo object is in a Many2one
if hasattr(val, 'id'):
val = val.id
found = False
if hasattr(config_model, key):
config_values[key] = val
found = True
if hasattr(self.contract_model, key):
contract_values[key] = val
found = True
if not found:
self._logger.warning('cannot locate attribute names "%s" on hr.contract().' % (key, ))
# PE Payroll Config Defaults Should be set on the Model
if 'date_hired' not in config_values:
config_values['date_hired'] = '2016-01-01'
config = config_model.create(config_values)
contract_values['pe_payroll_config_id'] = config.id
self._get_contract_defaults(contract_values)
contract = self.contract_model.create(contract_values)
# Compatibility with Odoo 14
contract.structure_type_id.default_struct_id.schedule_pay = schedule_pay
return contract

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<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="pe_payroll_config_id"
domain="[('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}"/>
<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>
</odoo>

View 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="date_hired"/>
<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"/>
<field name="date_hired"/>
</group>
<group>
<group name="employee" string="Employee">
<field name="retirement_type"/>
<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" />
<p attrs="{'invisible': [('comp_ss_type', '!=', 'eps')]}">Not supported. Specify rule.</p>
<field name="comp_ss_eps_rule_id" attrs="{'invisible': [('comp_ss_type', '!=', 'eps')]}" />
</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>

View File

@@ -0,0 +1,13 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import api, SUPERUSER_ID
from . import models
def _post_install_hook_configure_journals(cr, registry):
"""
This method will create a salary journal for each company and allocate it to each Peru structure.
"""
env = api.Environment(cr, SUPERUSER_ID, {})
companies = env['res.company'].search([('partner_id.country_id', '=', env.ref('base.pe').id)])
for company in companies:
env['account.chart.template']._pe_configure_payroll_account_data(company)

View File

@@ -0,0 +1,25 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{
'name': 'Peru - Payroll with Accounting',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '15.0.2022.0.0',
'category': 'Human Resources',
'depends': [
'l10n_pe_hr_payroll',
'hr_payroll_account',
],
'description': """
Accounting Data for Peru Payroll Rules.
=======================================
""",
'auto_install': True,
'data': [
'data/l10n_pe_hr_payroll_account_data.xml',
],
'demo': [
'data/l10n_pe_hr_payroll_account_demo.xml',
],
'post_init_hook': '_post_install_hook_configure_journals',
'license': 'OPL-1',
}

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record model="ir.actions.server" id="action_pe_salary_rule_account_set">
<field name="name">Peru Default Accounting</field>
<field name="model_id" ref="hr_payroll.model_hr_salary_rule"/>
<field name="binding_model_id" ref="hr_payroll.model_hr_salary_rule" />
<field name="state">code</field>
<field name="code">
if records:
companies = env.user.company_ids
# You can override the account codes with kwargs like:
# pay_ee='1234',
# pay_afp='1235',
# pay_onp='1236',
# pay_ir_4ta_cat='2222',
# pay_ir_5ta_cat='1238',
# pay_essalud='1239',
# exp_salary='1240',
# exp_com='1222',
# exp_bono='1223',
# exp_essalud='1241',
env['account.chart.template']._pe_configure_payroll_account_data(companies, salary_rules=records, full_reset=False)
</field>
</record>
<record model="ir.actions.server" id="action_pe_salary_rule_account_reset">
<field name="name">Peru Default Accounting (Reset)</field>
<field name="model_id" ref="hr_payroll.model_hr_salary_rule"/>
<field name="binding_model_id" ref="hr_payroll.model_hr_salary_rule" />
<field name="state">code</field>
<field name="code">
if records:
companies = env.user.company_ids
# You can override the account codes with kwargs like:
# pay_ee='1234',
# pay_afp='1235',
# pay_onp='1236',
# pay_ir_4ta_cat='2222',
# pay_ir_5ta_cat='1238',
# pay_essalud='1239',
# exp_salary='1240',
# exp_com='1222',
# exp_bono='1223',
# exp_essalud='1241',
env['account.chart.template']._pe_configure_payroll_account_data(companies, salary_rules=records, full_reset=True)
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Employee Contract -->
<record id="hr_contract.hr_contract_admin" model="hr.contract">
<field name="structure_type_id" ref="l10n_pe_hr_payroll.structure_type_employee"/>
</record>
<record id="hr_contract.hr_contract_qdp" model="hr.contract">
<field name="structure_type_id" ref="l10n_pe_hr_payroll.structure_type_employee"/>
</record>
</odoo>

View File

@@ -0,0 +1,31 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_pe_hr_payroll_account
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-29 22:13+0000\n"
"PO-Revision-Date: 2022-04-29 22:13+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_pe_hr_payroll_account
#: model:ir.model,name:l10n_pe_hr_payroll_account.model_account_chart_template
msgid "Account Chart Template"
msgstr "Plantila de Plan Contable"
#. module: l10n_pe_hr_payroll_account
#: model:ir.actions.server,name:l10n_pe_hr_payroll_account.action_pe_salary_rule_account_set
msgid "Peru Default Accounting"
msgstr "Contabilidad para Perú por Defecto"
#. module: l10n_pe_hr_payroll_account
#: model:ir.actions.server,name:l10n_pe_hr_payroll_account.action_pe_salary_rule_account_reset
msgid "Peru Default Accounting (Reset)"
msgstr "Contabilidad de Perú por Defecto (Resetear)"

View File

@@ -0,0 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import account_chart_template

View File

@@ -0,0 +1,140 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import models
EXP_SALARY = '6211000'
EXP_COM = '6212000'
EXP_BONO = '6213000'
EXP_ESSALUD = '6271000'
PAY_EE = '4111000'
PAY_AFP = '4170000'
PAY_ONP = '4032000'
PAY_IR_4TA_CAT = '4017200'
PAY_IR_5TA_CAT = '4017300'
PAY_ESSALUD = '4031000'
class AccountChartTemplate(models.Model):
_inherit = 'account.chart.template'
def _load(self, sale_tax_rate, purchase_tax_rate, company):
"""
Override to configure payroll accounting data as well as accounting data.
"""
res = super()._load(sale_tax_rate, purchase_tax_rate, company)
self._pe_configure_payroll_account_data(company)
return res
def _pe_configure_payroll_account_data(self, companies,
pay_ee=PAY_EE,
pay_afp=PAY_AFP,
pay_onp=PAY_ONP,
pay_ir_4ta_cat=PAY_IR_4TA_CAT,
pay_ir_5ta_cat=PAY_IR_5TA_CAT,
pay_essalud=PAY_ESSALUD,
exp_salary=EXP_SALARY,
exp_com=EXP_COM,
exp_bono=EXP_BONO,
exp_essalud=EXP_ESSALUD,
salary_rules=None, full_reset=False):
account_codes = (
pay_ee,
pay_afp,
pay_onp,
pay_ir_4ta_cat,
pay_ir_5ta_cat,
pay_essalud,
exp_salary,
exp_com,
exp_bono,
exp_essalud,
)
pe_structures = self.env['hr.payroll.structure'].search([('country_id', '=', self.env.ref('base.pe').id)])
journal_field_id = self.env['ir.model.fields'].search([
('model', '=', 'hr.payroll.structure'),
('name', '=', 'journal_id')], limit=1)
for company in companies:
self = self.with_context({'allowed_company_ids': company.ids})
accounts = {
code: self.env['account.account'].search(
[('company_id', '=', company.id), ('code', '=like', '%s%%' % code)], limit=1)
for code in account_codes
}
accounts['none'] = self.env['account.account'].browse()
def set_rule_accounts(code, account_debit, account_credit):
rule_domain = [
('struct_id', 'in', pe_structures.ids),
('code', '=like', code),
]
if salary_rules:
rule_domain.append(('id', 'in', salary_rules.ids))
rules = self.env['hr.salary.rule'].search(rule_domain)
if full_reset:
values = {
'account_debit': account_debit.id,
'account_credit': account_credit.id,
}
rules.write(values)
else:
# we need to ensure we do not update an account that is already set
for rule in rules:
values = {}
if account_debit and not rule.account_debit:
values['account_debit'] = account_debit.id
if account_credit and not rule.account_credit:
values['account_credit'] = account_credit.id
if values:
# save a write if no values to write
rule.write(values)
journal = self.env['account.journal'].search([
('code', '=', 'PAYR'),
('name', '=', 'Payroll'),
('company_id', '=', company.id),
])
if journal:
if not journal.default_account_id:
journal.default_account_id = accounts[exp_salary].id
if hasattr(journal, 'payroll_entry_type'):
journal.payroll_entry_type = 'grouped'
else:
journal = self.env['account.journal'].create({
'name': 'Payroll',
'code': 'PAYR',
'type': 'general',
'company_id': company.id,
'default_account_id': accounts[exp_salary].id,
})
if hasattr(journal, 'payroll_entry_type'):
journal.payroll_entry_type = 'grouped'
self.env['ir.property']._set_multi(
"journal_id",
"hr.payroll.structure",
{structure.id: journal for structure in pe_structures},
)
# Find rules and set accounts on them.
# Find all rules that are ...
# BASIC* -> SALARY_EXPENSE debit account
set_rule_accounts('BASIC%', accounts[exp_salary], accounts['none'])
set_rule_accounts('BASIC_COM%', accounts[exp_com], accounts['none'])
set_rule_accounts('BASIC_BONO%', accounts[exp_bono], accounts['none'])
set_rule_accounts('BASIC_BADGES%', accounts[exp_bono], accounts['none'])
# ALW* -> SALARY_EXPENSE debit account
set_rule_accounts('ALW%', accounts[exp_salary], accounts['none'])
# EE_* -> AP debit
set_rule_accounts('EE_%', accounts[pay_ee], accounts['none']) # initialize
set_rule_accounts('EE_PE_AFP%', accounts[pay_afp], accounts['none'])
set_rule_accounts('EE_PE_ONP%', accounts[pay_onp], accounts['none'])
set_rule_accounts('EE_PE_IR_4TA_CAT%', accounts[pay_ir_4ta_cat], accounts['none'])
set_rule_accounts('EE_PE_IR_5TA_CAT%', accounts[pay_ir_5ta_cat], accounts['none'])
# ER_* -> AP debit, SE credit
set_rule_accounts('ER_%', accounts[pay_ee], accounts[exp_salary]) # initialize
set_rule_accounts('ER_PE_ESSALUD%', accounts[pay_essalud], accounts[exp_essalud])
# NET* -> AP credit
set_rule_accounts('NET%', accounts['none'], accounts[pay_ee])

View File

@@ -57,19 +57,6 @@ from .state.wv_west_virginia import wv_west_virginia_state_income_withholding
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({
@@ -125,6 +112,3 @@ class HRPayslip(models.Model):
'wv_west_virginia_state_income_withholding': wv_west_virginia_state_income_withholding,
})
return res
def get_pay_periods_in_year(self):
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)