diff --git a/l10n_pe_hr_payroll/__manifest__.py b/l10n_pe_hr_payroll/__manifest__.py
index 65927b42..0ae186ac 100644
--- a/l10n_pe_hr_payroll/__manifest__.py
+++ b/l10n_pe_hr_payroll/__manifest__.py
@@ -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': [
diff --git a/l10n_pe_hr_payroll/data/afp_rules.xml b/l10n_pe_hr_payroll/data/afp_rules.xml
index aa991d9f..9b5fc102 100644
--- a/l10n_pe_hr_payroll/data/afp_rules.xml
+++ b/l10n_pe_hr_payroll/data/afp_rules.xml
@@ -2,36 +2,20 @@
-
- EE: AFP Pensiones
- ee_afp_pensiones
+
+ EE: AFP
+ ee_afp
-
- 10.0
-
-
-
-
-
- EE: AFP Seguro
- ee_afp_seguro
-
-
-
- 1.74
-
-
-
-
-
- EE: AFP Comisión
- ee_afp_comision
-
-
-
- 0.18
-
+
+ {
+ # 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),
+ }
+
@@ -48,9 +32,9 @@
EE: PE AFP Pensiones
EE_PE_AFP_PENSIONES
python
- result = categories.BASIC
+ result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'
code
- result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_pensiones')
+ result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][4]
@@ -62,23 +46,44 @@
EE: PE AFP Seguro
EE_PE_AFP_SEGURO
python
- result = categories.BASIC
+ result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp'
code
- result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_seguro')
+
+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)
+
-
+
- EE: PE AFP Comisión
- EE_PE_AFP_COMISION
+ EE: PE AFP Comisión Mixta
+ EE_PE_AFP_COMISION_MIXTA
python
- result = categories.BASIC
+ result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp' and contract.pe_payroll_config_value('afp_comision_type') == 'mixta'
code
- result, result_rate = categories.BASIC, -payslip.rule_parameter('ee_afp_comision')
+ result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][1]
+
+
+
+
+
+
+
+
+ EE: PE AFP Comisión (Non-Mixta)
+ EE_PE_AFP_COMISION_NON_MIXTA
+ python
+ result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'afp' and contract.pe_payroll_config_value('afp_comision_type') == 'non_mixta'
+ code
+ result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_afp')[contract.pe_payroll_config_value('afp_type')][0]
diff --git a/l10n_pe_hr_payroll/data/base.xml b/l10n_pe_hr_payroll/data/base.xml
index 1a4d2236..fffaf5bf 100644
--- a/l10n_pe_hr_payroll/data/base.xml
+++ b/l10n_pe_hr_payroll/data/base.xml
@@ -29,4 +29,28 @@
+
+
+ EE: ONP
+ EE_PE_ONP
+
+
+
+ ER: ONP
+ ER_PE_ONP
+
+
+
+
+
+ EE: IR 5ta Cat.
+ EE_PE_IR_5TA_CAT
+
+
+
+ ER: IR 5ta Cat.
+ ER_PE_IR_5TA_CAT
+
+
+
diff --git a/l10n_pe_hr_payroll/data/ir_5ta_cat_rules.xml b/l10n_pe_hr_payroll/data/ir_5ta_cat_rules.xml
new file mode 100644
index 00000000..8d293ad7
--- /dev/null
+++ b/l10n_pe_hr_payroll/data/ir_5ta_cat_rules.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+ Peru UIT
+ pe_uit
+
+
+
+ 4400.0
+
+
+
+
+
+ EE: IR 5ta Cat.
+ ee_ir_5ta_cat
+
+
+
+
+ [
+ ( 5.0, 8.0),
+ ( 20.0, 14.0),
+ ( 35.0, 17.0),
+ ( 45.0, 20.0),
+ ('inf', 30.0),
+ ]
+
+
+
+
+
+
+ IR 5ta Cat.
+
+
+
+
+
+
+
+ EE: PE IR 5TA Cat.
+ EE_PE_IR_5TA_CAT
+ python
+ result = categories.GROSS
+ code
+
+# 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)
+
+
+
+
+
diff --git a/l10n_pe_hr_payroll/data/onp_rules.xml b/l10n_pe_hr_payroll/data/onp_rules.xml
new file mode 100644
index 00000000..b1d9df0c
--- /dev/null
+++ b/l10n_pe_hr_payroll/data/onp_rules.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ EE: ONP
+ ee_onp
+
+
+
+ 13.0
+
+
+
+
+
+
+ ONP
+
+
+
+
+
+
+
+ EE: PE ONP/SNP
+ EE_PE_ONP
+ python
+ result = categories.GROSS and contract.pe_payroll_config_value('retirement_type') == 'onp' and rule == contract.pe_payroll_config_value('onp_rule_id')
+ code
+ result, result_rate = categories.GROSS, -payslip.rule_parameter('ee_onp')
+
+
+
+
+
diff --git a/l10n_pe_hr_payroll/models/__init__.py b/l10n_pe_hr_payroll/models/__init__.py
index 34b51524..e45c0ad8 100644
--- a/l10n_pe_hr_payroll/models/__init__.py
+++ b/l10n_pe_hr_payroll/models/__init__.py
@@ -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
diff --git a/l10n_pe_hr_payroll/models/hr_contract.py b/l10n_pe_hr_payroll/models/hr_contract.py
index 316411f1..b7980e38 100644
--- a/l10n_pe_hr_payroll/models/hr_contract.py
+++ b/l10n_pe_hr_payroll/models/hr_contract.py
@@ -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
-
- # 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]
+
+ pe_payroll_config_id = fields.Many2one('hr.contract.pe_payroll_config', 'Payroll Forms')
+
+ def pe_payroll_config_value(self, name):
+ return self.pe_payroll_config_id[name]
diff --git a/l10n_pe_hr_payroll/models/hr_payslip.py b/l10n_pe_hr_payroll/models/hr_payslip.py
index 90a27025..432337e2 100644
--- a/l10n_pe_hr_payroll/models/hr_payslip.py
+++ b/l10n_pe_hr_payroll/models/hr_payslip.py
@@ -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)
diff --git a/l10n_pe_hr_payroll/models/pe_payroll_config.py b/l10n_pe_hr_payroll/models/pe_payroll_config.py
new file mode 100644
index 00000000..ec7da289
--- /dev/null
+++ b/l10n_pe_hr_payroll/models/pe_payroll_config.py
@@ -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')
diff --git a/l10n_pe_hr_payroll/security/ir.model.access.csv b/l10n_pe_hr_payroll/security/ir.model.access.csv
new file mode 100644
index 00000000..2ccba490
--- /dev/null
+++ b/l10n_pe_hr_payroll/security/ir.model.access.csv
@@ -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
diff --git a/l10n_pe_hr_payroll/tests/__init__.py b/l10n_pe_hr_payroll/tests/__init__.py
index 761fda6c..05472054 100644
--- a/l10n_pe_hr_payroll/tests/__init__.py
+++ b/l10n_pe_hr_payroll/tests/__init__.py
@@ -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
diff --git a/l10n_pe_hr_payroll/tests/common.py b/l10n_pe_hr_payroll/tests/common.py
index a47a2cfd..2eb46fa7 100755
--- a/l10n_pe_hr_payroll/tests/common.py
+++ b/l10n_pe_hr_payroll/tests/common.py
@@ -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'):
diff --git a/l10n_pe_hr_payroll/tests/test_2020.py b/l10n_pe_hr_payroll/tests/test_2020.py
new file mode 100644
index 00000000..6e72d03e
--- /dev/null
+++ b/l10n_pe_hr_payroll/tests/test_2020.py
@@ -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)
diff --git a/l10n_pe_hr_payroll/views/hr_contract_views.xml b/l10n_pe_hr_payroll/views/hr_contract_views.xml
index b7532699..ece8dc38 100644
--- a/l10n_pe_hr_payroll/views/hr_contract_views.xml
+++ b/l10n_pe_hr_payroll/views/hr_contract_views.xml
@@ -1,17 +1,17 @@
-
+
hr.contract.form.inherit
hr.contract
-
+ 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}"/>
diff --git a/l10n_pe_hr_payroll/views/pe_payroll_config_views.xml b/l10n_pe_hr_payroll/views/pe_payroll_config_views.xml
new file mode 100644
index 00000000..c2e86f30
--- /dev/null
+++ b/l10n_pe_hr_payroll/views/pe_payroll_config_views.xml
@@ -0,0 +1,71 @@
+
+
+
+ hr.contract.pe_payroll_config.tree
+ hr.contract.pe_payroll_config
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.contract.pe_payroll_config.form
+ hr.contract.pe_payroll_config
+
+
+
+
+
+
+ hr.contract.pe_payroll_config.search
+ hr.contract.pe_payroll_config
+
+
+
+
+
+
+
+
+
+ Peru Employee Payroll Forms
+ hr.contract.pe_payroll_config
+ tree,form
+
+
+ No Forms
+
+
+
+
+
+