[IMP] hr_payroll_hibou: Control the 'wage_type' at the HR Contract Level

This will make it possible to be more abstract with 'work_type' or 'worked days lines' and overtime.
This commit is contained in:
Jared Kipe
2020-11-27 14:22:16 -08:00
parent 487daf1201
commit 30f1414df5
10 changed files with 118 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ Base module for fixing specific qwerks or assumptions in the way Payroll Odoo En
""", """,
'data': [ 'data': [
'views/hr_contract_views.xml',
'views/res_config_settings_views.xml', 'views/res_config_settings_views.xml',
], ],
'demo': [ 'demo': [

View File

@@ -1,4 +1,5 @@
from . import browsable_object from . import browsable_object
from . import hr_contract
from . import hr_payslip from . import hr_payslip
from . import hr_salary_rule from . import hr_salary_rule
from . import res_config_settings from . import res_config_settings

View File

@@ -0,0 +1,20 @@
from odoo import fields, models
class HrContract(models.Model):
_inherit = 'hr.contract'
wage_type = fields.Selection([('monthly', 'Period Fixed Wage'), ('hourly', 'Hourly Wage')],
default='monthly', required=True, related=False)
def _get_contract_wage(self, work_type=None):
# Override if you pay differently for different work types
# In 14.0, this utilizes new computed field mechanism,
# but will still get the 'wage' field by default.
self.ensure_one()
return self[self._get_contract_wage_field(work_type=work_type)]
def _get_contract_wage_field(self, work_type=None):
if self.wage_type == 'hourly':
return 'hourly_wage'
return super()._get_contract_wage_field()

View File

@@ -6,9 +6,22 @@ from odoo import fields, models
class HrPayslip(models.Model): class HrPayslip(models.Model):
_inherit = 'hr.payslip' _inherit = 'hr.payslip'
# We need to be able to support more complexity,
# namely, that different employees will be paid by different wage types as 'salary' vs 'hourly'
wage_type = fields.Selection(related='contract_id.wage_type')
def get_year(self): def get_year(self):
""" """
# Helper method to get the year (normalized between Odoo Versions) # Helper method to get the year (normalized between Odoo Versions)
:return: int year of payslip :return: int year of payslip
""" """
return self.date_to.year return self.date_to.year
def _get_contract_wage(self, work_type=None):
# Override if you pay differently for different work types
# In 14.0, this utilizes new computed field mechanism,
# but will still get the 'wage' field by default.
# 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)

View File

@@ -7,6 +7,8 @@ class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings' _inherit = 'res.config.settings'
# TODO We need MORE here... # TODO We need MORE here...
module_hr_payroll_attendance = fields.Boolean(string='Attendance Entries & Overtime')
module_hr_payroll_timesheet = fields.Boolean(string='Timesheet Entries & Overtime')
module_l10n_us_hr_payroll = fields.Boolean(string='USA Payroll') module_l10n_us_hr_payroll = fields.Boolean(string='USA Payroll')
module_l10n_us_hr_payroll_401k = fields.Boolean(string='USA Payroll 401k') module_l10n_us_hr_payroll_401k = fields.Boolean(string='USA Payroll 401k')

View File

@@ -2,4 +2,5 @@
from . import common from . import common
from . import test_contract_wage_type
from . import test_special from . import test_special

View File

@@ -10,16 +10,21 @@ from odoo.tools.float_utils import float_round as odoo_float_round
def process_payslip(payslip): def process_payslip(payslip):
try: try:
payslip.action_payslip_done() return payslip.action_payslip_done()
except AttributeError: except AttributeError:
# v9 # v9
payslip.process_sheet() return payslip.process_sheet()
class TestPayslip(common.TransactionCase): class TestPayslip(common.TransactionCase):
debug = False debug = False
_logger = getLogger(__name__) _logger = getLogger(__name__)
def process_payslip(self, payslip=None):
if not payslip:
return process_payslip(self.payslip)
return process_payslip(payslip)
def setUp(self): def setUp(self):
super(TestPayslip, self).setUp() super(TestPayslip, self).setUp()
self.contract_model = self.env['hr.contract'] self.contract_model = self.env['hr.contract']

View File

@@ -0,0 +1,26 @@
from .common import TestPayslip, process_payslip
class TestContractWageType(TestPayslip):
def test_per_contract_wage_type_salary(self):
self.debug = True
salary = 80000.0
employee = self._createEmployee()
contract = self._createContract(employee, wage=salary, hourly_wage=salary/100.0, wage_type='monthly', schedule_pay='bi-weekly')
payslip = self._createPayslip(employee, '2019-12-30', '2020-01-12')
self.assertEqual(contract.wage_type, 'monthly')
self.assertEqual(payslip.wage_type, 'monthly')
cats = self._getCategories(payslip)
self.assertEqual(cats['BASIC'], salary)
def test_per_contract_wage_type_hourly(self):
self.debug = True
hourly_wage = 21.50
employee = self._createEmployee()
contract = self._createContract(employee, wage=hourly_wage*100.0, hourly_wage=hourly_wage, wage_type='hourly', schedule_pay='bi-weekly')
payslip = self._createPayslip(employee, '2019-12-30', '2020-01-12')
self.assertEqual(contract.wage_type, 'hourly')
self.assertEqual(payslip.wage_type, 'hourly')
cats = self._getCategories(payslip)
self.assertEqual(cats['BASIC'], hourly_wage * 80.0)

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_contract_form_inherit" model="ir.ui.view">
<field name="name">hr.contract.form.inherit</field>
<field name="model">hr.contract</field>
<field name="priority">20</field>
<field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/>
<field name="arch" type="xml">
<data>
<!-- existing field `wage_type` is now per-contract -->
<xpath expr="//group[@name='main_info_hourly']" position="attributes">
<attribute name="attrs">{}</attribute>
</xpath>
<xpath expr="//group[@name='salary_and_advantages']" position="attributes">
<attribute name="string">Period Advantages in Cash</attribute>
</xpath>
</data>
</field>
</record>
</odoo>

View File

@@ -32,6 +32,7 @@
<label for="module_l10n_us_hr_payroll_401k" string="USA Payroll 401k"/> <label for="module_l10n_us_hr_payroll_401k" string="USA Payroll 401k"/>
<div class="text-muted"> <div class="text-muted">
Provide retirement plans with optional company matching. Provide retirement plans with optional company matching.
<strong>Hibou Professional</strong>
</div> </div>
<div class="mt8" id="l10n_us_hr_payroll_401k_match"> <div class="mt8" id="l10n_us_hr_payroll_401k_match">
<button name="%(hr_payroll.hr_rule_parameter_action)d" icon="fa-arrow-right" type="action" <button name="%(hr_payroll.hr_rule_parameter_action)d" icon="fa-arrow-right" type="action"
@@ -41,6 +42,30 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-6 col-12 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_hr_payroll_attendance"/>
</div>
<div class="o_setting_right_pane">
<label for="module_hr_payroll_attendance"/>
<div class="text-muted">
Extend Attendance into Payroll with Work Types, Overtime!
<strong>Hibou Professional</strong>
</div>
</div>
</div>
<div class="col-lg-6 col-12 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_hr_payroll_timesheet"/>
</div>
<div class="o_setting_right_pane">
<label for="module_hr_payroll_timesheet"/>
<div class="text-muted">
Extend Timesheets into Payroll with Work Types, Overtime!
<strong>Hibou Professional</strong>
</div>
</div>
</div>
</div> </div>
</xpath> </xpath>
</field> </field>