Merge branch 'new/13.0/l10n_pe_hr_payroll' into '13.0-test'

new/13.0/l10n_pe_hr_payroll into 13.0-test

See merge request hibou-io/hibou-odoo/suite!1391
This commit is contained in:
Hibou Bot
2022-04-28 19:32:58 +00:00
17 changed files with 304 additions and 345 deletions

View File

@@ -3,7 +3,7 @@
{ {
'name': 'Hibou Payroll', 'name': 'Hibou Payroll',
'author': 'Hibou Corp. <hello@hibou.io>', 'author': 'Hibou Corp. <hello@hibou.io>',
'version': '13.0.2.0.0', 'version': '13.0.2.1.0',
'category': 'Payroll Localization', 'category': 'Payroll Localization',
'depends': [ 'depends': [
'hr_payroll', 'hr_payroll',

View File

@@ -138,6 +138,11 @@ class Payslips(BrowsableObject):
def paid_amount(self): def paid_amount(self):
return self.dict._get_paid_amount() 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 # Patch over Core
browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__ browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__

View File

@@ -7,6 +7,22 @@ from .browsable_object import BrowsableObject, InputLine, WorkedDays, Payslips
class HrPayslip(models.Model): class HrPayslip(models.Model):
_inherit = 'hr.payslip' _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_pay_periods_in_year(self):
return self.PAY_PERIODS_IN_YEAR.get(self.contract_id.schedule_pay, 0)
# We need to be able to support more complexity, # We need to be able to support more complexity,
# namely, that different employees will be paid by different wage types as 'salary' vs 'hourly' # namely, that different employees will be paid by different wage types as 'salary' vs 'hourly'
wage_type = fields.Selection(related='contract_id.wage_type') wage_type = fields.Selection(related='contract_id.wage_type')

View File

@@ -53,4 +53,34 @@
<field name="parent_id" ref="hr_payroll.COMP"/> <field name="parent_id" ref="hr_payroll.COMP"/>
</record> </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">Bono</field>
<field name="code">BONO</field>
</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">Bono</field>
<field name="sequence" eval="25"/>
<field name="struct_id" ref="l10n_pe_hr_payroll.hr_payroll_structure"/>
</record>
</odoo> </odoo>

View File

@@ -22,7 +22,7 @@
<record id="hr_payroll_rule_er_essalud" model="hr.salary.rule"> <record id="hr_payroll_rule_er_essalud" model="hr.salary.rule">
<field name="sequence" eval="220"/> <field name="sequence" eval="220"/>
<field name="struct_id" ref="hr_payroll_structure"/> <field name="struct_id" ref="hr_payroll_structure"/>
<field name="category_id" ref="hr_payroll_category_ee_pe_afp"/> <field name="category_id" ref="hr_payroll_category_er_essalud"/>
<field name="name">ER: PE Essalud</field> <field name="name">ER: PE Essalud</field>
<field name="code">ER_PE_ESSALUD</field> <field name="code">ER_PE_ESSALUD</field>
<field name="condition_select">python</field> <field name="condition_select">python</field>

View File

@@ -47,12 +47,17 @@
<field name="condition_python">result = categories.GROSS</field> <field name="condition_python">result = categories.GROSS</field>
<field name="amount_select">code</field> <field name="amount_select">code</field>
<field name="amount_python_compute"> <field name="amount_python_compute">
# TODO normalize anual wage based on pay period pay_periods_in_year = payslip.pay_periods_in_year
uit = payslip.rule_parameter('pe_uit') uit = payslip.rule_parameter('pe_uit')
wage = categories.GROSS
wage_year = wage * 12.0 basic_wage = BASIC
# additional 2 months wage_period = categories.GROSS
wage_year += wage * 2.0 period_additional_wage = max(wage_period - basic_wage, 0.0)
wage_year = basic_wage * pay_periods_in_year
# additional 2 months (July and December)
wage_year += wage_year * (1/6) # 2 months 2/12
wage_year += period_additional_wage
over_7uit = wage_year - (7.0 * uit) over_7uit = wage_year - (7.0 * uit)
if over_7uit &lt;= 0.0: if over_7uit &lt;= 0.0:
result = 0.0 result = 0.0
@@ -72,7 +77,7 @@ else:
break break
last_uit = _uit last_uit = _uit
tax = -total_tax / 12.0 tax = -total_tax / 12.0
result, result_rate = wage, (tax / wage * 100.0)</field> result, result_rate = wage_period, (tax / wage_period * 100.0)</field>
<field name="partner_id" ref="res_partner_ir_5ta_cat"/> <field name="partner_id" ref="res_partner_ir_5ta_cat"/>
<field name="appears_on_payslip" eval="True"/> <field name="appears_on_payslip" eval="True"/>
</record> </record>

View File

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

View File

@@ -1,148 +0,0 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import fields
from odoo.addons.hr_payroll.models import browsable_object
class BrowsableObject(object):
def __init__(self, employee_id, dict, env):
self.employee_id = employee_id
self.dict = dict
self.env = env
# Customization to allow changing the behavior of the discrete browsable objects.
# you can think of this as 'compiling' the query based on the configuration.
sum_field = env['ir.config_parameter'].sudo().get_param('hr_payroll.payslip.sum_behavior', 'date_from')
if sum_field == 'date' and 'date' not in env['hr.payslip']:
# missing attribute, closest by definition
sum_field = 'date_to'
if not sum_field:
sum_field = 'date_from'
self._compile_browsable_query(sum_field)
def __getattr__(self, attr):
return attr in self.dict and self.dict.__getitem__(attr) or 0.0
def _compile_browsable_query(self, sum_field):
pass
class InputLine(BrowsableObject):
"""a class that will be used into the python code, mainly for usability purposes"""
def _compile_browsable_query(self, sum_field):
self.__browsable_query = """
SELECT sum(amount) as sum
FROM hr_payslip as hp, hr_payslip_input as pi
WHERE hp.employee_id = %s AND hp.state = 'done'
AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
def sum(self, code, from_date, to_date=None):
if to_date is None:
to_date = fields.Date.today()
self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
return self.env.cr.fetchone()[0] or 0.0
class WorkedDays(BrowsableObject):
"""a class that will be used into the python code, mainly for usability purposes"""
def _compile_browsable_query(self, sum_field):
self.__browsable_query = """
SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours
FROM hr_payslip as hp, hr_payslip_worked_days as pi
WHERE hp.employee_id = %s AND hp.state = 'done'
AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s""".format(sum_field=sum_field)
def _sum(self, code, from_date, to_date=None):
if to_date is None:
to_date = fields.Date.today()
self.env.cr.execute(self.__browsable_query, (self.employee_id, from_date, to_date, code))
return self.env.cr.fetchone()
def sum(self, code, from_date, to_date=None):
res = self._sum(code, from_date, to_date)
return res and res[0] or 0.0
def sum_hours(self, code, from_date, to_date=None):
res = self._sum(code, from_date, to_date)
return res and res[1] or 0.0
class Payslips(BrowsableObject):
"""a class that will be used into the python code, mainly for usability purposes"""
def _compile_browsable_query(self, sum_field):
# Note that the core odoo has this as `hp.credit_note = False` but what if it is NULL?
# reverse of the desired behavior.
self.__browsable_query_rule = """
SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
FROM hr_payslip as hp, hr_payslip_line as pl
WHERE hp.employee_id = %s AND hp.state = 'done'
AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""".format(sum_field=sum_field)
# Original (non-recursive)
# self.__browsable_query_category = """
# SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
# FROM hr_payslip as hp, hr_payslip_line as pl, hr_salary_rule_category as rc
# WHERE hp.employee_id = %s AND hp.state = 'done'
# AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
# AND rc.id = pl.category_id AND rc.code = %s""".format(sum_field=sum_field)
# Hibou Recursive version
self.__browsable_query_category = """
WITH RECURSIVE
category_by_code as (
SELECT id
FROM hr_salary_rule_category
WHERE code = %s
),
category_ids as (
SELECT COALESCE((SELECT id FROM category_by_code), -1) AS id
UNION ALL
SELECT rc.id
FROM hr_salary_rule_category AS rc
JOIN category_ids AS rcs ON rcs.id = rc.parent_id
)
SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end)
FROM hr_payslip as hp, hr_payslip_line as pl
WHERE hp.employee_id = %s AND hp.state = 'done'
AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id
AND pl.category_id in (SELECT id from category_ids)""".format(sum_field=sum_field)
def sum(self, code, from_date, to_date=None):
if to_date is None:
to_date = fields.Date.today()
self.env.cr.execute(self.__browsable_query_rule, (self.employee_id, from_date, to_date, code))
res = self.env.cr.fetchone()
return res and res[0] or 0.0
def rule_parameter(self, code):
return self.env['hr.rule.parameter']._get_parameter_from_code(code, self.dict.date_to)
def sum_category(self, code, from_date, to_date=None):
if to_date is None:
to_date = fields.Date.today()
self.env['hr.payslip'].flush(['credit_note', 'employee_id', 'state', 'date_from', 'date_to'])
self.env['hr.payslip.line'].flush(['total', 'slip_id', 'category_id'])
self.env['hr.salary.rule.category'].flush(['code'])
# standard version
# self.env.cr.execute(self.__browsable_query_category, (self.employee_id, from_date, to_date, code))
# recursive category version
self.env.cr.execute(self.__browsable_query_category, (code, self.employee_id, from_date, to_date))
res = self.env.cr.fetchone()
return res and res[0] or 0.0
@property
def paid_amount(self):
return self.dict._get_paid_amount()
# Patch over Core
browsable_object.BrowsableObject.__init__ = BrowsableObject.__init__
browsable_object.BrowsableObject._compile_browsable_query = BrowsableObject._compile_browsable_query
browsable_object.InputLine._compile_browsable_query = InputLine._compile_browsable_query
browsable_object.InputLine.sum = InputLine.sum
browsable_object.WorkedDays._compile_browsable_query = WorkedDays._compile_browsable_query
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

View File

@@ -1,9 +1,6 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. # Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
# TODO make/move to l10n_pe_hr_payroll_params # Tests moved to `l10n_pe_hr_payroll_params`
# Tests moved to `l10n_us_hr_payroll_params`
# common remains for site specific tests # common remains for site specific tests
from . import common from . import common
from . import test_2020
# from . import test_2022

View File

@@ -1,142 +0,0 @@
# 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)

View File

@@ -1,42 +0,0 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from .common import TestPePayslip, process_payslip
class Test2022(TestPePayslip):
# AFP Constants
AFP_PENSIONES = 0.1 # 10%
AFP_SEGURO = 0.0174 # 1.74%
AFP_COMISION = 0.0018 # 0.18%
# ER ESSALUD
ER_ESSALUD = 0.0675 # 6.75%
###
# 2022 Taxes and Rates
###
def test_2022_taxes(self):
salary = 3290.0
employee = self._createEmployee()
contract = self._createContract(employee, wage=salary)
self._log(contract.read())
self._log('2022 tax first payslip:')
payslip = self._createPayslip(employee, '2022-01-01', '2022-01-31')
payslip.compute_sheet()
cats = self._getCategories(payslip)
rules = self._getRules(payslip)
# Employee
self.assertPayrollEqual(cats['BASIC'], salary)
self.assertPayrollEqual(rules['EE_PE_AFP_PENSIONES'], -cats['BASIC'] * self.AFP_PENSIONES)
self.assertPayrollEqual(rules['EE_PE_AFP_SEGURO'], -cats['BASIC'] * self.AFP_SEGURO)
self.assertPayrollEqual(rules['EE_PE_AFP_COMISION'], -cats['BASIC'] * self.AFP_COMISION)
# Employer
self.assertPayrollEqual(rules['ER_PE_ESSALUD'], -cats['BASIC'] * self.ER_ESSALUD)
process_payslip(payslip)

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': '13.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,48 @@
<?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_eps='1237',
# pay_ir_5ta_cat='1238',
# pay_essalud='1239',
# exp_salary='1240',
# 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_eps='1237',
# pay_ir_5ta_cat='1238',
# pay_essalud='1239',
# exp_salary='1240',
# 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,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,136 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from odoo import models
EXP_SALARY = '6211000'
EXP_ESSALUD = '6271000'
PAY_EE = '4111000'
PAY_AFP = '407' # TODO
PAY_AFP = '4032000' # TODO
PAY_ONP = '4032000'
PAY_EPS = '4075' # TODO EPS isn't implemented
PAY_IR_5TA_CAT = '4017' # TODO
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_eps=PAY_EPS,
pay_ir_5ta_cat=PAY_IR_5TA_CAT,
pay_essalud=PAY_ESSALUD,
exp_salary=EXP_SALARY,
exp_essalud=EXP_ESSALUD,
salary_rules=None, full_reset=False):
account_codes = (
pay_ee,
pay_afp,
pay_onp,
pay_eps,
pay_ir_5ta_cat,
pay_essalud,
exp_salary,
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_credit_account_id:
journal.default_credit_account_id = accounts[exp_salary].id
if not journal.default_debit_account_id:
journal.default_debit_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_credit_account_id': accounts[exp_salary].id,
'default_debit_account_id': accounts[exp_salary].id,
})
if hasattr(journal, 'payroll_entry_type'):
journal.payroll_entry_type = 'grouped'
self.env['ir.property'].create([{
'name': 'structure_journal_id',
'company_id': company.id,
'fields_id': journal_field_id.id,
'value_reference': 'account.journal,%s' % journal.id,
'res_id': 'hr.payroll.structure,%s' % structure.id,
} 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'])
# 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_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])