mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[MIG] hr_payroll_payment: for Odoo 14.0
Added actual payment to tests.
This commit is contained in:
@@ -19,6 +19,8 @@ When paying on a batch, a "Batch Payment" will be generated and linked to the wh
|
|||||||
|
|
||||||
Adds Accounting Date field on Batch to use when creating slips with the batch's date.
|
Adds Accounting Date field on Batch to use when creating slips with the batch's date.
|
||||||
|
|
||||||
|
Adds fiscal position mappings to set a fiscal position on the contract and have payslips map their accounts.
|
||||||
|
|
||||||
Tested
|
Tested
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
'name': 'Payroll Payments',
|
'name': 'Payroll Payments',
|
||||||
'author': 'Hibou Corp. <hello@hibou.io>',
|
'author': 'Hibou Corp. <hello@hibou.io>',
|
||||||
'version': '13.0.1.1.0',
|
'version': '14.0.1.0.0',
|
||||||
'category': 'Human Resources',
|
'category': 'Human Resources',
|
||||||
'sequence': 95,
|
'sequence': 95,
|
||||||
'summary': 'Register payments for Payroll Payslips',
|
'summary': 'Register payments for Payroll Payslips',
|
||||||
@@ -13,7 +13,7 @@ Pay your Payroll
|
|||||||
|
|
||||||
Hibou's Payroll Payments modifies, and abstracts, the way that the accounting for payslips is generated.
|
Hibou's Payroll Payments modifies, and abstracts, the way that the accounting for payslips is generated.
|
||||||
|
|
||||||
In stock Odoo 13, journal entries are grouped by account and name, but has no linking to partners.
|
In stock Odoo 14, journal entries are grouped by account and name, but has no linking to partners.
|
||||||
|
|
||||||
On the Payroll Journal, you can now select optional journal entry creation with the options:
|
On the Payroll Journal, you can now select optional journal entry creation with the options:
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@ When paying on a batch, a "Batch Payment" will be generated and linked to the wh
|
|||||||
|
|
||||||
Adds Accounting Date field on Batch to use when creating slips with the batch's date.
|
Adds Accounting Date field on Batch to use when creating slips with the batch's date.
|
||||||
|
|
||||||
|
Adds fiscal position mappings to set a fiscal position on the contract and have payslips map their accounts.
|
||||||
|
|
||||||
Tested
|
Tested
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -41,7 +43,6 @@ Passes original Payroll Accounting tests and additional ones for gouping behavio
|
|||||||
'hibou_professional',
|
'hibou_professional',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
#'wizard/hr_payroll_register_payment_views.xml',
|
|
||||||
'views/account_views.xml',
|
'views/account_views.xml',
|
||||||
'views/hr_payslip_views.xml',
|
'views/hr_payslip_views.xml',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class HrPayslip(models.Model):
|
|||||||
def _payment_values(self, amount):
|
def _payment_values(self, amount):
|
||||||
values = {
|
values = {
|
||||||
'payment_reference': self.number,
|
'payment_reference': self.number,
|
||||||
'communication': self.number + ' - ' + self.name,
|
'ref': self.number + ' - ' + self.name,
|
||||||
'journal_id': self.move_id.journal_id.payroll_payment_journal_id.id,
|
'journal_id': self.move_id.journal_id.payroll_payment_journal_id.id,
|
||||||
'payment_method_id': self.move_id.journal_id.payroll_payment_method_id.id,
|
'payment_method_id': self.move_id.journal_id.payroll_payment_method_id.id,
|
||||||
'partner_type': 'supplier',
|
'partner_type': 'supplier',
|
||||||
@@ -82,20 +82,29 @@ class HrPayslip(models.Model):
|
|||||||
return values
|
return values
|
||||||
|
|
||||||
def action_register_payment(self):
|
def action_register_payment(self):
|
||||||
if not all(slip.move_id.journal_id.payroll_payment_journal_id for slip in self):
|
slips = self.filtered(lambda s: s.move_id.state in ('draft', 'posted') and not s.is_paid)
|
||||||
|
if not all(slip.move_id.journal_id.payroll_payment_journal_id for slip in slips):
|
||||||
raise UserError(_('Payroll Payment journal not configured on the existing entry\'s journal.'))
|
raise UserError(_('Payroll Payment journal not configured on the existing entry\'s journal.'))
|
||||||
if not all(slip.move_id.journal_id.payroll_payment_method_id for slip in self):
|
if not all(slip.move_id.journal_id.payroll_payment_method_id for slip in slips):
|
||||||
raise UserError(_('Payroll Payment method not configured on the existing entry\'s journal.'))
|
raise UserError(_('Payroll Payment method not configured on the existing entry\'s journal.'))
|
||||||
|
|
||||||
|
# as of 14, you cannot reconcile to un-posted moves
|
||||||
|
# so if you are paying it, we must assume you want to post any draft entries
|
||||||
|
slip_moves = slips.mapped('move_id')
|
||||||
|
unposted_moves = slip_moves.filtered(lambda m: m.state == 'draft')
|
||||||
|
unposted_moves._post(soft=False)
|
||||||
|
|
||||||
payments = self.env['account.payment']
|
payments = self.env['account.payment']
|
||||||
for slip in self.filtered(lambda s: s.move_id and not s.is_paid):
|
for slip in slips:
|
||||||
lines_to_pay = slip.move_id.line_ids.filtered(lambda l: l.partner_id == slip.employee_id.address_home_id
|
lines_to_pay = slip.move_id.line_ids.filtered(lambda l: l.partner_id == slip.employee_id.address_home_id
|
||||||
and l.account_id == slip.employee_id.address_home_id.property_account_payable_id)
|
and l.account_id == slip.employee_id.address_home_id.property_account_payable_id)
|
||||||
amount = sum(lines_to_pay.mapped('amount_residual'))
|
amount = sum(lines_to_pay.mapped('amount_residual'))
|
||||||
|
if not amount:
|
||||||
|
continue
|
||||||
payment_values = slip._payment_values(amount)
|
payment_values = slip._payment_values(amount)
|
||||||
payment = payments.create(payment_values)
|
payment = payments.create(payment_values)
|
||||||
payment.post()
|
payment.action_post()
|
||||||
lines_paid = payment.move_line_ids.filtered(lambda l: l.account_id == slip.employee_id.address_home_id.property_account_payable_id)
|
lines_paid = payment.line_ids.filtered(lambda l: l.account_id == slip.employee_id.address_home_id.property_account_payable_id)
|
||||||
lines_to_reconcile = lines_to_pay + lines_paid
|
lines_to_reconcile = lines_to_pay + lines_paid
|
||||||
lines_to_reconcile.reconcile()
|
lines_to_reconcile.reconcile()
|
||||||
payments += payment
|
payments += payment
|
||||||
@@ -296,10 +305,10 @@ class HrPayslip(models.Model):
|
|||||||
|
|
||||||
# The code below is called if there is an error in the balance between credit and debit sum.
|
# The code below is called if there is an error in the balance between credit and debit sum.
|
||||||
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_credit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_('The Expense Journal "%s" has not properly configured the Credit Account!') % (
|
_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
@@ -321,9 +330,9 @@ class HrPayslip(models.Model):
|
|||||||
adjust_credit['credit'] = debit_sum - credit_sum
|
adjust_credit['credit'] = debit_sum - credit_sum
|
||||||
|
|
||||||
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_debit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(_('The Expense Journal "%s" has not properly configured the Debit Account!') % (
|
raise UserError(_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
@@ -378,10 +387,10 @@ class HrPayslip(models.Model):
|
|||||||
|
|
||||||
# The code below is called if there is an error in the balance between credit and debit sum.
|
# The code below is called if there is an error in the balance between credit and debit sum.
|
||||||
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_credit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_('The Expense Journal "%s" has not properly configured the Credit Account!') % (
|
_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
@@ -403,9 +412,9 @@ class HrPayslip(models.Model):
|
|||||||
adjust_credit['credit'] = debit_sum - credit_sum
|
adjust_credit['credit'] = debit_sum - credit_sum
|
||||||
|
|
||||||
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_debit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(_('The Expense Journal "%s" has not properly configured the Debit Account!') % (
|
raise UserError(_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
@@ -529,10 +538,10 @@ class HrPayslip(models.Model):
|
|||||||
|
|
||||||
# The code below is called if there is an error in the balance between credit and debit sum.
|
# The code below is called if there is an error in the balance between credit and debit sum.
|
||||||
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_credit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_('The Expense Journal "%s" has not properly configured the Credit Account!') % (
|
_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
@@ -554,9 +563,9 @@ class HrPayslip(models.Model):
|
|||||||
adjust_credit['credit'] = debit_sum - credit_sum
|
adjust_credit['credit'] = debit_sum - credit_sum
|
||||||
|
|
||||||
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1:
|
||||||
acc_id = slip.journal_id.default_debit_account_id.id
|
acc_id = slip.journal_id.default_account_id.id
|
||||||
if not acc_id:
|
if not acc_id:
|
||||||
raise UserError(_('The Expense Journal "%s" has not properly configured the Debit Account!') % (
|
raise UserError(_('The Expense Journal "%s" has not properly configured the Default Account!') % (
|
||||||
slip.journal_id.name))
|
slip.journal_id.name))
|
||||||
existing_adjustment_line = (
|
existing_adjustment_line = (
|
||||||
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
line_id for line_id in line_ids if line_id['name'] == _('Adjustment Entry')
|
||||||
|
|||||||
@@ -4,20 +4,41 @@
|
|||||||
import odoo.tests
|
import odoo.tests
|
||||||
from odoo.addons.hr_payroll_account.tests.test_hr_payroll_account import TestHrPayrollAccount as TestBase
|
from odoo.addons.hr_payroll_account.tests.test_hr_payroll_account import TestHrPayrollAccount as TestBase
|
||||||
|
|
||||||
|
|
||||||
@odoo.tests.tagged('post_install', '-at_install')
|
@odoo.tests.tagged('post_install', '-at_install')
|
||||||
class TestHrPayrollAccount(TestBase):
|
class TestHrPayrollAccount(TestBase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
# upstream code no-longer sets the journal, though it does create it....
|
||||||
|
self.hr_structure_softwaredeveloper.journal_id = self.account_journal
|
||||||
|
# upstream code no-longer has any accounts (just makes journal entries without any lines)
|
||||||
|
demo_account = self.env.ref('hr_payroll_account.demo_account')
|
||||||
|
self.hr_structure_softwaredeveloper.rule_ids.filtered(lambda r: r.code == 'HRA').account_debit = demo_account
|
||||||
|
# Need a default account as there will be adjustment lines equal and opposite to the above PT rule...
|
||||||
|
self.account_journal.default_account_id = demo_account
|
||||||
|
|
||||||
# Two employees, but in stock tests they share the same partner...
|
# Two employees, but in stock tests they share the same partner...
|
||||||
self.hr_employee_mark.address_home_id = self.env['res.partner'].create({
|
self.hr_employee_mark.address_home_id = self.env['res.partner'].create({
|
||||||
'name': 'employee_mark',
|
'name': 'employee_mark',
|
||||||
})
|
})
|
||||||
|
|
||||||
# This rule has a partner, and is the only one with any accounting side effects.
|
# This rule has a partner, and is the only one with any accounting side effects.
|
||||||
# Remove partner to use the home address...
|
# Remove partner to use the home address...
|
||||||
self.rule = self.env.ref('hr_payroll.hr_salary_rule_houserentallowance1')
|
self.rule = self.hr_structure_softwaredeveloper.rule_ids.filtered(lambda r: r.code == 'HRA')
|
||||||
self.rule.partner_id = False
|
self.rule.partner_id = False
|
||||||
|
|
||||||
|
# configure journal to be able to make payments
|
||||||
|
ap = self.hr_employee_mark.address_home_id.property_account_payable_id
|
||||||
|
self.assertTrue(ap)
|
||||||
|
# note there is no NET rule, so I just use a random allowance with fixed 800.0 amount
|
||||||
|
net_rule = self.hr_structure_softwaredeveloper.rule_ids.filtered(lambda r: r.code == 'CA')
|
||||||
|
self.assertTrue(net_rule)
|
||||||
|
net_rule.account_credit = ap
|
||||||
|
bank_journal = self.env['account.journal'].search([('type', '=', 'bank')], limit=1)
|
||||||
|
self.account_journal.payroll_payment_journal_id = bank_journal
|
||||||
|
self.account_journal.payroll_payment_method_id = bank_journal.outbound_payment_method_ids[0]
|
||||||
|
|
||||||
def _setup_fiscal_position(self):
|
def _setup_fiscal_position(self):
|
||||||
account_rule_debit = self.rule.account_debit
|
account_rule_debit = self.rule.account_debit
|
||||||
self.assertTrue(account_rule_debit)
|
self.assertTrue(account_rule_debit)
|
||||||
@@ -47,12 +68,12 @@ class TestHrPayrollAccount(TestBase):
|
|||||||
def test_00_fiscal_position(self):
|
def test_00_fiscal_position(self):
|
||||||
self._setup_fiscal_position()
|
self._setup_fiscal_position()
|
||||||
self.test_00_hr_payslip_run()
|
self.test_00_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 3)
|
||||||
|
|
||||||
def test_00_fiscal_position_empty(self):
|
def test_00_fiscal_position_empty(self):
|
||||||
self._setup_fiscal_position_empty()
|
self._setup_fiscal_position_empty()
|
||||||
self.test_00_hr_payslip_run()
|
self.test_00_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 1)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
||||||
|
|
||||||
def test_01_hr_payslip_run(self):
|
def test_01_hr_payslip_run(self):
|
||||||
# Grouped method groups but has partners.
|
# Grouped method groups but has partners.
|
||||||
@@ -61,16 +82,21 @@ class TestHrPayrollAccount(TestBase):
|
|||||||
self.assertEqual(len(self.payslip_run.slip_ids), 3)
|
self.assertEqual(len(self.payslip_run.slip_ids), 3)
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id')), 1)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id')), 1)
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.partner_id')), 2)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.partner_id')), 2)
|
||||||
|
# what is going on with the 3rd one?!
|
||||||
|
slips_to_pay = self.payslip_run.slip_ids
|
||||||
|
action = slips_to_pay.action_register_payment()
|
||||||
|
payment_ids = action['res_ids']
|
||||||
|
self.assertEqual(len(payment_ids), 2)
|
||||||
|
|
||||||
def test_01_fiscal_position(self):
|
def test_01_fiscal_position(self):
|
||||||
self._setup_fiscal_position()
|
self._setup_fiscal_position()
|
||||||
self.test_01_hr_payslip_run()
|
self.test_01_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 3)
|
||||||
|
|
||||||
def test_01_fiscal_position_empty(self):
|
def test_01_fiscal_position_empty(self):
|
||||||
self._setup_fiscal_position_empty()
|
self._setup_fiscal_position_empty()
|
||||||
self.test_01_hr_payslip_run()
|
self.test_01_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 1)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
||||||
|
|
||||||
def test_01_2_hr_payslip_run(self):
|
def test_01_2_hr_payslip_run(self):
|
||||||
# Payslip method makes an entry per payslip
|
# Payslip method makes an entry per payslip
|
||||||
@@ -80,13 +106,20 @@ class TestHrPayrollAccount(TestBase):
|
|||||||
self.assertEqual(len(self.payslip_run.slip_ids), 3)
|
self.assertEqual(len(self.payslip_run.slip_ids), 3)
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id')), 3)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id')), 3)
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.partner_id')), 2)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.partner_id')), 2)
|
||||||
|
slips_to_pay = self.payslip_run.slip_ids
|
||||||
|
# what is going on with the 3rd one?!
|
||||||
|
# it is possible to filter it out, but it doesn't change it
|
||||||
|
self.assertEqual(len(slips_to_pay), 3)
|
||||||
|
action = slips_to_pay.action_register_payment()
|
||||||
|
payment_ids = action['res_ids']
|
||||||
|
self.assertEqual(len(payment_ids), 2)
|
||||||
|
|
||||||
def test_01_2_fiscal_position(self):
|
def test_01_2_fiscal_position(self):
|
||||||
self._setup_fiscal_position()
|
self._setup_fiscal_position()
|
||||||
self.test_01_2_hr_payslip_run()
|
self.test_01_2_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 3)
|
||||||
|
|
||||||
def test_01_2_fiscal_position_empty(self):
|
def test_01_2_fiscal_position_empty(self):
|
||||||
self._setup_fiscal_position_empty()
|
self._setup_fiscal_position_empty()
|
||||||
self.test_01_2_hr_payslip_run()
|
self.test_01_2_hr_payslip_run()
|
||||||
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 1)
|
self.assertEqual(len(self.payslip_run.slip_ids.mapped('move_id.line_ids.account_id')), 2)
|
||||||
|
|||||||
@@ -13,6 +13,9 @@
|
|||||||
<field name="payroll_payment_method_refund_id" domain="[('payment_type', '=', 'inbound')]"/>
|
<field name="payroll_payment_method_refund_id" domain="[('payment_type', '=', 'inbound')]"/>
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='bank_account']//field[@name='code']" position="before">
|
||||||
|
<field name="default_account_id" string="Default Account" attrs="{'invisible': [('type', '!=', 'general')]}" />
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
Reference in New Issue
Block a user