mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[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:
@@ -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': [
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
20
hr_payroll_hibou/models/hr_contract.py
Normal file
20
hr_payroll_hibou/models/hr_contract.py
Normal 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()
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
from . import common
|
from . import common
|
||||||
|
|
||||||
|
from . import test_contract_wage_type
|
||||||
from . import test_special
|
from . import test_special
|
||||||
|
|||||||
@@ -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']
|
||||||
|
|||||||
26
hr_payroll_hibou/tests/test_contract_wage_type.py
Normal file
26
hr_payroll_hibou/tests/test_contract_wage_type.py
Normal 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)
|
||||||
22
hr_payroll_hibou/views/hr_contract_views.xml
Executable file
22
hr_payroll_hibou/views/hr_contract_views.xml
Executable 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>
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user