oca-ocb-accounting/odoo-bringout-oca-ocb-account/account/tests/test_account_payment_register.py
Ernad Husremovic 768b70e05e 19.0 vanilla
2026-03-09 09:30:07 +01:00

2151 lines
89 KiB
Python

# -*- coding: utf-8 -*-
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.exceptions import UserError
from odoo.tests import tagged, users
from odoo import fields, Command
from dateutil.relativedelta import relativedelta
from itertools import product
from unittest.mock import patch
from odoo import fields, Command
from odoo.exceptions import UserError
from odoo.tests import tagged, Form
from odoo.tests.common import Like
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.payment.tests.common import PaymentCommon
@tagged('post_install', '-at_install')
class TestAccountPaymentRegister(AccountTestInvoicingCommon, PaymentCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.current_year = fields.Date.today().year
cls.other_currency = cls.setup_other_currency('EUR')
cls.other_currency_2 = cls.setup_other_currency('CAD', rates=[('2016-01-01', 3.0), ('2017-01-01', 0.01)])
cls.payment_debit_account_id = cls.copy_account(cls.inbound_payment_method_line.payment_account_id)
cls.payment_credit_account_id = cls.copy_account(cls.outbound_payment_method_line.payment_account_id)
cls.bank_journal_1 = cls.company_data['default_journal_bank']
cls.bank_journal_2 = cls.company_data['default_journal_bank'].copy()
cls.invoice_payment_term_1 = cls.env['account.payment.term'].create({
'name': '2% 10 Net 30',
'early_discount': True,
'discount_days': 10,
'discount_percentage': 2,
'line_ids': [
Command.create({
'value': 'percent',
'value_amount': 100,
'delay_type': 'days_after',
'nb_days': 30,
}),
],
})
cls.term_advance_60days = cls.env.ref('account.account_payment_term_advance_60days')
cls.term_0_5_10_days = cls.env['account.payment.term'].create({
'name': 'term_0_5_10_days',
'line_ids': [
Command.create({'value': 'percent', 'value_amount': 10.0, 'nb_days': 0}),
Command.create({'value': 'percent', 'value_amount': 30.0, 'nb_days': 5}),
Command.create({'value': 'percent', 'value_amount': 60.0, 'nb_days': 10}),
],
})
cls.partner_bank_account1 = cls.env['res.partner.bank'].create({
'acc_number': "0123456789",
'partner_id': cls.partner_a.id,
'acc_type': 'bank',
})
cls.partner_bank_account2 = cls.env['res.partner.bank'].create({
'acc_number': "9876543210",
'partner_id': cls.partner_a.id,
'acc_type': 'bank',
})
cls.comp_bank_account1 = cls.env['res.partner.bank'].create({
'acc_number': "985632147",
'partner_id': cls.env.company.partner_id.id,
'acc_type': 'bank',
'allow_out_payment': True,
})
cls.comp_bank_account2 = cls.env['res.partner.bank'].create({
'acc_number': "741258963",
'partner_id': cls.env.company.partner_id.id,
'acc_type': 'bank',
'allow_out_payment': True,
})
# Customer invoices sharing the same batch.
cls.out_invoice_1 = cls.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'currency_id': cls.other_currency.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
})
cls.out_invoice_2 = cls.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'currency_id': cls.other_currency.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 2000.0, 'tax_ids': []})],
})
cls.out_invoice_3 = cls.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'currency_id': cls.other_currency.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 24.02, 'tax_ids': []})],
})
cls.out_invoice_4 = cls.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'currency_id': cls.other_currency.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 23.98, 'tax_ids': []})],
})
(cls.out_invoice_1 + cls.out_invoice_2 + cls.out_invoice_3 + cls.out_invoice_4).action_post()
# Vendor bills, in_invoice_1 + in_invoice_2 are sharing the same batch but not in_invoice_3.
cls.in_invoice_1 = cls.env['account.move'].create({
'move_type': 'in_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
})
cls.in_invoice_2 = cls.env['account.move'].create({
'move_type': 'in_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 2000.0, 'tax_ids': []})],
})
cls.in_invoice_3 = cls.env['account.move'].create({
'move_type': 'in_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_b.id,
'invoice_payment_term_id': False,
'currency_id': cls.other_currency.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 3000.0, 'tax_ids': []})],
})
cls.in_invoice_epd_applied = cls.env['account.move'].create({
'move_type': 'in_invoice',
'date': fields.Date.today(),
'invoice_date': fields.Date.today(),
'partner_id': cls.partner_b.id,
'invoice_payment_term_id': cls.invoice_payment_term_1.id,
'invoice_line_ids': [Command.create({'product_id': cls.product_a.id, 'price_unit': 25.0, 'tax_ids': []})],
})
cls.in_invoice_epd_not_applied = cls.env['account.move'].create({
'move_type': 'in_invoice',
'date': fields.Date.today() - relativedelta(days=11),
'invoice_date': fields.Date.today() - relativedelta(days=11),
'partner_id': cls.partner_b.id,
'invoice_payment_term_id': cls.invoice_payment_term_1.id,
'invoice_line_ids': [Command.create({'product_id': cls.product_a.id, 'price_unit': 25.0, 'tax_ids': []})],
})
(cls.in_invoice_1 + cls.in_invoice_2 + cls.in_invoice_3 + cls.in_invoice_epd_applied + cls.in_invoice_epd_not_applied).action_post()
# Credit note
cls.in_refund_1 = cls.env['account.move'].create({
'move_type': 'in_refund',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': cls.partner_a.id,
'invoice_line_ids': [(0, 0, {'product_id': cls.product_a.id, 'price_unit': 1600.0, 'tax_ids': []})],
})
cls.in_refund_2 = cls.env['account.move'].create({
'move_type': 'in_refund',
'date': fields.Date.today(),
'invoice_date': fields.Date.today(),
'partner_id': cls.partner_b.id,
'invoice_line_ids': [Command.create({'product_id': cls.product_a.id, 'price_unit': 10.0, 'tax_ids': []})],
})
(cls.in_refund_1 + cls.in_refund_2).action_post()
cls.branch = cls._create_company(name="Branch", parent_id=cls.env.company.id)
cls.user_branch = cls.env['res.users'].create({
'name': 'Branch User',
'login': 'user_branch',
'group_ids': [
Command.link(cls.env.ref('base.group_user').id),
Command.link(cls.env.ref('account.group_account_user').id),
Command.link(cls.env.ref('account.group_account_manager').id),
],
'company_id': cls.branch.id,
'company_ids': [Command.set(cls.branch.ids)],
})
@classmethod
def get_wizard_available_journals(cls, wizard):
return wizard.available_journal_ids.filtered_domain([
*cls.env['account.journal']._check_company_domain(wizard.company_id),
])
def test_register_payment_single_batch_grouped_keep_open_lower_amount(self):
''' Pay 800.0 with 'open' as payment difference handling on two customer invoices (1000 + 2000). '''
active_ids = (self.out_invoice_1 + self.out_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 800.0,
'group_payment': True,
'payment_difference_handling': 'open',
'currency_id': self.other_currency.id,
'payment_method_line_id': self.inbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': Like(f'GROUP/{self.current_year}/...'),
'payment_method_line_id': self.inbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 400.0,
'currency_id': self.other_currency.id,
'amount_currency': -800.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 400.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 800.0,
'reconciled': False,
},
])
def test_register_payment_single_batch_grouped_keep_open_higher_amount(self):
''' Pay 3100.0 with 'open' as payment difference handling on two customer invoices (1000 + 2000). '''
active_ids = (self.out_invoice_1 + self.out_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 3100.0,
'group_payment': True,
'currency_id': self.other_currency.id,
'payment_method_line_id': self.inbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': Like(f'GROUP/{self.current_year}/...'),
'payment_method_line_id': self.inbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 1550.0,
'currency_id': self.other_currency.id,
'amount_currency': -3100.0,
'reconciled': False,
},
# Liquidity line:
{
'debit': 1550.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 3100.0,
'reconciled': False,
},
])
def test_register_payment_single_batch_grouped_writeoff_lower_amount_debit(self):
''' Pay 800.0 with 'reconcile' as payment difference handling on two customer invoices (1000 + 2000). '''
active_ids = (self.out_invoice_1 + self.out_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 800.0,
'group_payment': True,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
'payment_method_line_id': self.inbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': Like(f'GROUP/{self.current_year}/...'),
'payment_method_line_id': self.inbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 1500.0,
'currency_id': self.other_currency.id,
'amount_currency': -3000.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 400.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 800.0,
'reconciled': False,
},
# Writeoff line:
{
'debit': 1100.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 2200.0,
'reconciled': False,
},
])
def test_register_payment_single_batch_grouped_writeoff_higher_amount_debit(self):
''' Pay 3100.0 with 'reconcile' as payment difference handling on two customer invoices (1000 + 2000). '''
active_ids = (self.out_invoice_1 + self.out_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 3100.0,
'group_payment': True,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
'payment_method_line_id': self.inbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': Like(f'GROUP/{self.current_year}/...'),
'payment_method_line_id': self.inbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 1500.0,
'currency_id': self.other_currency.id,
'amount_currency': -3000.0,
'reconciled': True,
},
# Writeoff line:
{
'debit': 0.0,
'credit': 50.0,
'currency_id': self.other_currency.id,
'amount_currency': -100.0,
'reconciled': False,
},
# Liquidity line:
{
'debit': 1550.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 3100.0,
'reconciled': False,
},
])
def test_register_payment_single_batch_grouped_writeoff_lower_amount_credit(self):
''' Pay 800.0 with 'reconcile' as payment difference handling on two vendor billes (1000 + 2000). '''
active_ids = (self.in_invoice_1 + self.in_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 800.0,
'group_payment': True,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
'payment_method_line_id': self.outbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': 'BILL/2017/01/0001, BILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Writeoff line:
{
'debit': 0.0,
'credit': 2200.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -2200.0,
'reconciled': False,
},
# Liquidity line:
{
'debit': 0.0,
'credit': 800.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -800.0,
'reconciled': False,
},
# Payable line:
{
'debit': 3000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 3000.0,
'reconciled': True,
},
])
def test_register_payment_single_batch_grouped_writeoff_higher_amount_credit(self):
''' Pay 3100.0 with 'reconcile' as payment difference handling on two vendor billes (1000 + 2000). '''
active_ids = (self.in_invoice_1 + self.in_invoice_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'amount': 3100.0,
'group_payment': True,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
'payment_method_line_id': self.outbound_payment_method_line.id,
})._create_payments()
self.assertRecordValues(payments, [{
'memo': 'BILL/2017/01/0001, BILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
}])
self.assertRecordValues(payments.move_id.line_ids.sorted('balance'), [
# Liquidity line:
{
'debit': 0.0,
'credit': 3100.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -3100.0,
'reconciled': False,
},
# Writeoff line:
{
'debit': 100.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 100.0,
'reconciled': False,
},
# Payable line:
{
'debit': 3000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 3000.0,
'reconciled': True,
},
])
def test_register_payment_single_batch_not_grouped(self):
''' Choose to pay two customer invoices with separated payments (1000 + 2000). '''
active_ids = (self.out_invoice_1 + self.out_invoice_2).ids
payment_register = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=active_ids)\
.create({
'group_payment': False,
'amount': 1200.0
})
payments = payment_register._create_payments()
self.assertRecordValues(payments, [
{
'memo': 'INV/2017/00001',
'payment_method_line_id': self.inbound_payment_method_line.id,
},
{
'memo': 'INV/2017/00002',
'payment_method_line_id': self.inbound_payment_method_line.id,
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance') + payments[1].move_id.line_ids.sorted('balance'), [
# == Payment 1: to pay out_invoice_1 ==
# Receivable line:
{
'debit': 0.0,
'credit': 500.0,
'currency_id': self.other_currency.id,
'amount_currency': -1000.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 500.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 1000.0,
'reconciled': False,
},
# == Payment 2: to pay out_invoice_2 ==
# Receivable line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.other_currency.id,
'amount_currency': -2000.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 2000.0,
'reconciled': False,
},
])
def test_register_payment_different_type_single_batch_not_grouped(self):
""" Choose to pay a bill and a refund with separated payments (1000 + -2000)."""
active_ids = (self.in_invoice_1 + self.in_refund_1).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': False,
})._create_payments()
self.assertRecordValues(payments[0], [
{
'memo': 'BILL/2017/01/0001',
'payment_type': 'outbound',
}
])
self.assertRecordValues(payments[1], [
{
'memo': 'RBILL/2017/01/0001',
'payment_type': 'inbound',
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance'), [
# == Payment 1: to pay in_invoice_1 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1000.0,
'reconciled': True,
},
])
self.assertRecordValues(payments[1].move_id.line_ids.sorted('balance'), [
# == Payment 2: to pay in_refund_1 ==
# Payable line:
{
'debit': 0.0,
'credit': 1600.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1600.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 1600.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1600.0,
'reconciled': False,
},
])
def test_register_payment_single_batch_grouped_with_credit_note(self):
''' Pay 1400.0 on two vendor bills (1000.0 + 2000.0) and one credit note (1600.0). '''
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_refund_1).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})._create_payments()
self.assertRecordValues(payments, [
{
'memo': 'BILL/2017/01/0001, BILL/2017/01/0002, RBILL/2017/01/0001',
'payment_method_line_id': self.outbound_payment_method_line.id,
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance'), [
# Liquidity line:
{
'debit': 0.0,
'credit': 1400.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1400.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1400.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1400.0,
'reconciled': True,
},
])
def test_register_payment_multiple_batch_grouped_with_credit_note(self):
''' Do not batch payments if multiple partner_bank_id '''
bank1 = self.env['res.partner.bank'].create({
'acc_number': 'BE43798822936101',
'partner_id': self.partner_a.id,
'allow_out_payment': True,
})
bank2 = self.env['res.partner.bank'].create({
'acc_number': 'BE85812541345906',
'partner_id': self.partner_a.id,
'allow_out_payment': True,
})
self.in_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = bank1
self.in_invoice_2.with_context(skip_readonly_check=True).partner_bank_id = bank2
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_refund_1).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})._create_payments()
self.assertRecordValues(payments, [
{
'memo': 'BILL/2017/01/0001',
'payment_method_line_id': self.outbound_payment_method_line.id,
},
{
'memo': 'BILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
},
{
'memo': 'RBILL/2017/01/0001',
'payment_method_line_id': self.inbound_payment_method_line.id,
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance') + payments[1].move_id.line_ids.sorted('balance') + payments[2].move_id.line_ids.sorted('balance'), [
# Liquidity line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1000.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 0.0,
'credit': 2000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -2000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 2000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 2000.0,
'reconciled': True,
},
# Receivable line:
{
'debit': 0.0,
'credit': 1600.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1600.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 1600.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1600.0,
'reconciled': False,
},
])
def test_register_payment_multi_batches_grouped(self):
''' Choose to pay multiple batches, one with two customer invoices (1000 + 2000)
and one with a vendor bill of 600, by grouping payments.
'''
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_invoice_3).ids
payment_register = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=active_ids)\
.create({
'group_payment': True,
})
payments = payment_register._create_payments()
self.assertRecordValues(payments, [
{
'memo': 'BILL/2017/01/0001, BILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
},
{
'memo': 'BILL/2017/01/0003',
'payment_method_line_id': self.outbound_payment_method_line.id,
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance') + payments[1].move_id.line_ids.sorted('balance'), [
# == Payment 1: to pay in_invoice_1 & in_invoice_2 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 3000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -3000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 3000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 3000.0,
'reconciled': True,
},
# == Payment 2: to pay in_invoice_3 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1500.0,
'currency_id': self.other_currency.id,
'amount_currency': -3000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1500.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 3000.0,
'reconciled': True,
},
])
def test_register_payment_multi_batches_grouped_with_credit_note(self):
''' Choose to pay multiple batches, one with customer A bill + refund (1000 - 500)
and one with customer B bill (1000).
'''
partner_b = self.partner_b.copy({'property_account_position_id': False})
partner_b_bank_account = self.env['res.partner.bank'].create({
'acc_number': "123454321",
'partner_id': partner_b.id,
'acc_type': 'bank',
})
invoice_1 = self.in_invoice_1
invoice_2 = invoice_1.copy({
'invoice_date': invoice_1.invoice_date,
'partner_id': partner_b.id,
'partner_bank_id': partner_b_bank_account.id
})
refund_1 = self.env['account.move'].create(
{
'move_type': 'in_refund',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id, 'price_unit': 500.0, 'tax_ids': False})],
},
)
(invoice_2 + refund_1).action_post()
active_ids = (refund_1 + invoice_1 + invoice_2).ids
payment_register = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=active_ids)\
.create({'group_payment': True})
payments = payment_register._create_payments()
self.assertRecordValues(payments, [
{
'memo': 'BILL/2017/01/0001, RBILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': self.partner_bank_account1.id,
},
{
'memo': 'BILL/2017/01/0004',
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': partner_b_bank_account.id,
},
])
def test_register_payment_multi_batches_not_grouped(self):
''' Choose to pay multiple batches, one with two customer invoices (1000 + 2000)
and one with a vendor bill of 600, by splitting payments.
'''
self.in_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = self.partner_bank_account1
self.in_invoice_2.with_context(skip_readonly_check=True).partner_bank_id = self.partner_bank_account2
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_invoice_3).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': False,
})._create_payments()
self.assertRecordValues(payments, [
{
'journal_id': self.bank_journal_1.id,
'memo': 'BILL/2017/01/0001',
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': self.partner_bank_account1.id,
},
{
'journal_id': self.bank_journal_1.id,
'memo': 'BILL/2017/01/0002',
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': self.partner_bank_account2.id,
},
{
'journal_id': self.bank_journal_1.id,
'memo': 'BILL/2017/01/0003',
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': False,
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance') + payments[1].move_id.line_ids.sorted('balance') + payments[2].move_id.line_ids.sorted('balance'), [
# == Payment 1: to pay in_invoice_1 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1000.0,
'reconciled': True,
},
# == Payment 2: to pay in_invoice_2 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 2000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -2000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 2000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 2000.0,
'reconciled': True,
},
# == Payment 3: to pay in_invoice_3 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1500.0,
'currency_id': self.other_currency.id,
'amount_currency': -3000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1500.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 3000.0,
'reconciled': True,
},
])
def test_register_payment_constraints(self):
# Test to register a payment for an already fully reconciled journal entry.
self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_2.ids)\
.create({})\
._create_payments()
with self.assertRaises(UserError):
self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_2.ids)\
.create({})
def test_register_payment_doesnt_send_email(self):
''' When registering a payment manually with a payment register,
we shouldn't sent email notification automatically.
'''
self.env['ir.config_parameter'].set_param('sale.automatic_invoice', True)
if self.env['ir.module.module']._get('payment_demo').state == 'installed':
payment_token = self._create_token(provider_id=self._prepare_provider(code='demo').id,
demo_simulated_state='done')
else:
payment_token = self._create_token()
payment_register = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_4.ids)\
.create({'payment_token_id': payment_token.id})
with patch(
'odoo.addons.sale.models.payment_transaction.PaymentTransaction'
'._send_invoice'
) as patched:
payment_register._create_payments()
patched.assert_not_called()
def test_register_payment_multi_currency_rounding_issue_positive_delta(self):
''' When registering a payment using a different currency than the invoice one, the invoice must be fully paid
at the end whatever the currency rate.
'''
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_3.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.12,
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 12.01,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Liquidity line:
{
'debit': 12.01,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.12,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_negative_delta(self):
''' When registering a payment using a different currency than the invoice one, the invoice must be fully paid
at the end whatever the currency rate.
'''
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_4.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.12,
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 11.99,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Liquidity line:
{
'debit': 11.99,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.12,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_writeoff_lower_amount_keep_open(self):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_3.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.08,
'payment_difference_handling': 'open',
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 8.0,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.08,
'reconciled': True,
},
# Liquidity line:
{
'debit': 8.0,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.08,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_writeoff_lower_amount_reconcile_positive_delta(self):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_3.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.08,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 12.01,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Write-off line:
{
'debit': 4.0,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.04,
'reconciled': False,
},
# Liquidity line:
{
'debit': 8.01,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.08,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_writeoff_lower_amount_reconcile_negative_delta(self):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_4.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.08,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 11.99,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Write-off line:
{
'debit': 4.0,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.04,
'reconciled': False,
},
# Liquidity line:
{
'debit': 7.99,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.08,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_writeoff_higher_amount_reconcile_positive_delta(self):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_3.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.16,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 12.01,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Write-off line:
{
'debit': 0.0,
'credit': 4.0,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.04,
'reconciled': False,
},
# Liquidity line:
{
'debit': 16.01,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.16,
'reconciled': False,
},
])
def test_register_payment_multi_currency_rounding_issue_writeoff_higher_amount_reconcile_negative_delta(self):
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=self.out_invoice_4.ids)\
.create({
'currency_id': self.other_currency_2.id,
'amount': 0.16,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.company_data['default_account_revenue'].id,
'writeoff_label': 'writeoff',
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 11.99,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.12,
'reconciled': True,
},
# Write-off line:
{
'debit': 0.0,
'credit': 4.0,
'currency_id': self.other_currency_2.id,
'amount_currency': -0.04,
'reconciled': False,
},
# Liquidity line:
{
'debit': 15.99,
'credit': 0.0,
'currency_id': self.other_currency_2.id,
'amount_currency': 0.16,
'reconciled': False,
},
])
def test_register_foreign_currency_on_payment_exchange_writeoff_account(self):
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
})
invoice.action_post()
# 1998 GOL = 999 USD
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'currency_id': self.other_currency.id,
'amount': 1998,
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.env.company.expense_currency_exchange_account_id.id,
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.other_currency.id,
'amount_currency': -1998.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.other_currency.id,
'amount_currency': 1998.0,
'reconciled': False,
},
])
def test_register_foreign_currency_on_invoice_exchange_writeoff_account(self):
self.env.company.tax_exigibility = True
self.env.company.account_cash_basis_base_account_id = self.env['account.account'].create({
'code': 'cash.basis.base.account',
'name': 'cash_basis_base_account',
'account_type': 'income',
})
default_tax = self.company_data['default_tax_sale']
default_tax.cash_basis_transition_account_id = self.env['account.account'].create({
'code': 'cash.basis.transfer.account',
'name': 'cash_basis_transfer_account',
'account_type': 'income',
'reconcile': True,
})
default_tax.tax_exigibility = 'on_payment'
# 1150 GOL = 575 USD
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': self.partner_a.id,
'currency_id': self.other_currency.id,
'invoice_line_ids': [Command.create({
'product_id': self.product_a.id,
'price_unit': 1000.0,
'tax_ids': [Command.set(default_tax.ids)],
})],
})
invoice.action_post()
# 1110 GOL = 370 USD
payment = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'currency_id': self.env.company.currency_id.id,
'amount': 370.0,
'payment_date': '2016-01-01',
'payment_difference_handling': 'reconcile',
'writeoff_account_id': self.env.company.expense_currency_exchange_account_id.id,
})\
._create_payments()
self.assertRecordValues(payment.move_id.line_ids.sorted('balance'), [
# Receivable line:
{
'balance': -370.0,
'currency_id': self.env.company.currency_id.id,
'amount_currency': -370.0,
'reconciled': True,
},
# Liquidity line:
{
'balance': 370.0,
'currency_id': self.env.company.currency_id.id,
'amount_currency': 370.0,
'reconciled': False,
},
])
self.assertRecordValues(invoice.line_ids.matched_credit_ids, [
{
'amount': 370.0,
'debit_amount_currency': 1150.0,
'credit_amount_currency': 370.0,
},
{
'amount': 205.00,
'debit_amount_currency': 0.0,
'credit_amount_currency': 0.0,
},
])
# Cash basis.
caba_move = self.env['account.move'].search([('tax_cash_basis_origin_move_id', '=', invoice.id)])
self.assertRecordValues(caba_move.line_ids.sorted('balance'), [
{
'balance': -321.74,
'currency_id': self.other_currency.id,
'amount_currency': -1000.0,
},
{
'balance': -48.26,
'currency_id': self.other_currency.id,
'amount_currency': -150.0,
},
{
'balance': 48.26,
'currency_id': self.other_currency.id,
'amount_currency': 150.0,
},
{
'balance': 321.74,
'currency_id': self.other_currency.id,
'amount_currency': 1000.0,
},
])
def test_suggested_default_partner_bank_inbound_payment(self):
""" Test the suggested bank account on the wizard for inbound payment. """
self.out_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = False
ctx = {'active_model': 'account.move', 'active_ids': self.out_invoice_1.ids}
wizard = self.env['account.payment.register'].with_context(**ctx).create({})
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_1.id,
'available_partner_bank_ids': [],
'partner_bank_id': False,
}])
self.out_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = self.comp_bank_account2
self.bank_journal_2.bank_account_id = self.comp_bank_account2
wizard = self.env['account.payment.register'].with_context(**ctx).create({})
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_2.id,
'available_partner_bank_ids': self.comp_bank_account2.ids,
'partner_bank_id': self.comp_bank_account2.id,
}])
wizard.journal_id = self.bank_journal_1
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_1.id,
'available_partner_bank_ids': [],
'partner_bank_id': False,
}])
def test_suggested_default_partner_bank_outbound_payment(self):
""" Test the suggested bank account on the wizard for outbound payment. """
self.in_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = False
ctx = {'active_model': 'account.move', 'active_ids': self.in_invoice_1.ids}
wizard = self.env['account.payment.register'].with_context(**ctx).create({})
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_1.id,
'available_partner_bank_ids': self.partner_a.bank_ids.ids,
'partner_bank_id': self.partner_bank_account1.id,
}])
self.in_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = self.partner_bank_account2
wizard = self.env['account.payment.register'].with_context(**ctx).create({})
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_1.id,
'available_partner_bank_ids': self.partner_a.bank_ids.ids,
'partner_bank_id': self.partner_bank_account2.id,
}])
wizard.journal_id = self.bank_journal_2
self.assertRecordValues(wizard, [{
'journal_id': self.bank_journal_2.id,
'available_partner_bank_ids': self.partner_a.bank_ids.ids,
'partner_bank_id': self.partner_bank_account2.id,
}])
def test_register_payment_inbound_multiple_bank_account(self):
""" Pay customer invoices with different bank accounts. """
self.out_invoice_1.with_context(skip_readonly_check=True).partner_bank_id = self.comp_bank_account1
self.out_invoice_2.with_context(skip_readonly_check=True).partner_bank_id = self.comp_bank_account2
self.bank_journal_2.bank_account_id = self.comp_bank_account2
ctx = {'active_model': 'account.move', 'active_ids': (self.out_invoice_1 + self.out_invoice_2).ids}
wizard = self.env['account.payment.register'].with_context(**ctx).create({'journal_id': self.bank_journal_2.id})
payments = wizard._create_payments()
self.assertRecordValues(payments, [
{
'journal_id': self.bank_journal_2.id,
'memo': 'INV/2017/00001',
'partner_bank_id': self.comp_bank_account2.id,
},
{
'journal_id': self.bank_journal_2.id,
'memo': 'INV/2017/00002',
'partner_bank_id': self.comp_bank_account2.id,
},
])
def test_register_payment_invoice_foreign_curr_payment_comp_curr(self):
# Invoice 1200 Gol = 400 USD
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2016-01-01',
'invoice_date': '2016-01-01',
'partner_id': self.partner_a.id,
'currency_id': self.other_currency.id,
'invoice_line_ids': [Command.create(
{'product_id': self.product_a.id,
'price_unit': 1200.0,
'tax_ids': [],
})],
})
invoice.action_post()
# Payment of 600 USD (equivalent to 1200 Gol in 2017).
# 600.0 USD should be computed correctly to fully paid the invoices.
wizard = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'currency_id': self.company_data['currency'].id,
'payment_date': '2017-01-01',
})
self.assertRecordValues(wizard, [{
'amount': 600.0,
'currency_id': self.company_data['currency'].id,
}])
payment = wizard._create_payments()
lines = (invoice + payment.move_id).line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
self.assertRecordValues(lines, [
{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.other_currency.id, 'reconciled': True},
{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.company_data['currency'].id, 'reconciled': True},
])
def test_register_payment_invoice_comp_curr_payment_foreign_curr(self):
# Invoice of 600 USD (equivalent to 1200 Gol in 2017).
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'date': '2016-01-01',
'invoice_date': '2016-01-01',
'partner_id': self.partner_a.id,
'currency_id': self.company_data['currency'].id,
'invoice_line_ids': [Command.create({
'product_id': self.product_a.id,
'price_unit': 600.0,
'tax_ids': [],
})],
})
invoice.action_post()
# Payment of 600 USD = 1200 Gol.
# 1200.0 Gol should be computed correctly to fully paid the invoices.
wizard = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({
'currency_id': self.other_currency.id,
'payment_date': '2017-01-01',
})
self.assertRecordValues(wizard, [{
'amount': 1200.0,
'currency_id': self.other_currency.id,
}])
payment = wizard._create_payments()
lines = (invoice + payment.move_id).line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
self.assertRecordValues(lines, [
{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.company_data['currency'].id, 'reconciled': True},
{'amount_residual': 0.0, 'amount_residual_currency': 0.0, 'currency_id': self.other_currency.id, 'reconciled': True},
])
def test_payment_method_different_type_single_batch_not_grouped(self):
""" Test payment methods when paying a bill and a refund with separated payments (1000 + -2000)."""
invoice_1 = self.in_invoice_1
invoice_2 = invoice_1.copy({'invoice_date': invoice_1.invoice_date, 'partner_id': self.partner_b.id})
refund_1, refund_2 = self.env['account.move'].create([
{
'move_type': 'in_refund',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': self.partner_a.id,
'invoice_line_ids': [(0, 0, {'product_id': self.product_a.id, 'price_unit': 1600.0, 'tax_ids': False})],
},
{
'move_type': 'in_refund',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'partner_id': self.partner_b.copy({'property_account_position_id': False}).id,
'invoice_line_ids': [(0, 0, {'product_id': self.product_a.id, 'price_unit': 1600.0, 'tax_ids': False})],
},
])
(invoice_2 + refund_1 + refund_2).action_post()
for moves in ((invoice_1 + invoice_2), (refund_1 + refund_2)):
wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=moves.ids).create({
'group_payment': False,
})
expected_available_payment_method_lines = wizard.journal_id.inbound_payment_method_line_ids if moves[0].move_type == 'in_refund' else wizard.journal_id.outbound_payment_method_line_ids
self.assertRecordValues(wizard, [
{
'available_payment_method_line_ids': expected_available_payment_method_lines.ids,
'payment_method_line_id': expected_available_payment_method_lines[:1].id,
}
])
active_ids = (invoice_1 + invoice_2 + refund_1 + refund_2).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': False,
})._create_payments()
self.assertRecordValues(payments[0], [
{
'memo': 'BILL/2017/01/0001',
'payment_method_line_id': self.bank_journal_1.outbound_payment_method_line_ids[0].id,
'payment_type': 'outbound',
}
])
self.assertRecordValues(payments[2], [
{
'memo': 'BILL/2017/01/0004',
'payment_method_line_id': self.bank_journal_1.outbound_payment_method_line_ids[0].id,
'payment_type': 'outbound',
}
])
self.assertRecordValues(payments[1], [
{
'memo': 'RBILL/2017/01/0002',
'payment_method_line_id': self.bank_journal_1.inbound_payment_method_line_ids[0].id,
'payment_type': 'inbound',
},
])
self.assertRecordValues(payments[3], [
{
'memo': 'RBILL/2017/01/0003',
'payment_method_line_id': self.bank_journal_1.inbound_payment_method_line_ids[0].id,
'payment_type': 'inbound',
},
])
self.assertRecordValues(payments[0].move_id.line_ids.sorted('balance'), [
# == Payment 1: to pay invoice_1 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1000.0,
'reconciled': False,
},
# Payable line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1000.0,
'reconciled': True,
},
])
self.assertRecordValues(payments[2].move_id.line_ids.sorted('balance'), [
# == Payment 3: to pay invoice_2 ==
# Payable line:
{
'debit': 0.0,
'credit': 1000.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1000.0,
'reconciled': False,
},
# Liquidity line:
{
'debit': 1000.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1000.0,
'reconciled': True,
},
])
self.assertRecordValues(payments[1].move_id.line_ids.sorted('balance'), [
# == Payment 2: to pay refund_1 ==
# Liquidity line:
{
'debit': 0.0,
'credit': 1600.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1600.0,
'reconciled': True,
},
# Payable line:
{
'debit': 1600.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1600.0,
'reconciled': False,
},
])
self.assertRecordValues(payments[3].move_id.line_ids.sorted('balance'), [
# == Payment 4: to pay refund_2 ==
# Payable line:
{
'debit': 0.0,
'credit': 1600.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': -1600.0,
'reconciled': True,
},
# Liquidity line:
{
'debit': 1600.0,
'credit': 0.0,
'currency_id': self.company_data['currency'].id,
'amount_currency': 1600.0,
'reconciled': False,
},
])
def test_group_payment_method_with_and_without_discount(self):
""" Test payment methods when creating group payment for discounted and non-discounted bills"""
active_ids = (self.in_invoice_epd_applied + self.in_invoice_epd_not_applied).ids
wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})
self.assertEqual(wizard.amount, 49.50)
def test_group_payment_method_with_and_without_discount_and_refund(self):
""" Test payment methods when creating group payment for discounted and non-discounted bills with a refund"""
active_ids = (self.in_invoice_epd_applied + self.in_invoice_epd_not_applied + self.in_refund_2).ids
wizard = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})
self.assertEqual(
wizard.amount, 46.50,
"Due to the payment term on partner_b, only 3 is removed for in_refund_2, as it is the first installment"
)
self.assertEqual(wizard.installments_switch_amount, 39.50, "With the full refund and the epd")
def test_keep_user_amount(self):
comp_curr = self.env.company.currency_id
foreign_curr = self.other_currency
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2016-01-01',
'invoice_payment_term_id': self.term_advance_60days.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id})],
})
invoice.action_post()
self.assertRecordValues(invoice.line_ids.sorted().filtered('date_maturity'), [
{'date_maturity': fields.Date.from_string('2016-01-01'), 'amount_currency': 345.0},
{'date_maturity': fields.Date.from_string('2016-03-01'), 'amount_currency': 805.0},
])
wizard = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({'payment_date': '2016-01-01'})
self.assertRecordValues(wizard, [{
'amount': 345.0,
'currency_id': comp_curr.id,
'custom_user_amount': 0.0,
'custom_user_currency_id': False,
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 1150.0,
}])
# Custom amount.
with Form(wizard) as wizard_form:
wizard_form.amount = 300.0
self.assertRecordValues(wizard, [{
'amount': 300.0,
'currency_id': comp_curr.id,
'custom_user_amount': 300.0,
'custom_user_currency_id': comp_curr.id,
'payment_difference': 850.0,
'installments_mode': 'full',
'installments_switch_amount': 345.0,
}])
# Custom currency.
with Form(wizard) as wizard_form:
wizard_form.currency_id = foreign_curr
self.assertRecordValues(wizard, [{
'amount': 900.0,
'currency_id': foreign_curr.id,
'custom_user_amount': 900.0,
'custom_user_currency_id': foreign_curr.id,
'payment_difference': 2550.0,
'installments_mode': 'full',
'installments_switch_amount': 1035.0,
}])
# Switch to full installment amount.
with Form(wizard) as wizard_form:
wizard_form.amount = wizard.installments_switch_amount
self.assertRecordValues(wizard, [{
'amount': 1035.0,
'currency_id': foreign_curr.id,
'custom_user_amount': 0.0,
'custom_user_currency_id': False,
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 3450.0,
}])
# Custom amount.
with Form(wizard) as wizard_form:
wizard_form.amount = 3000.0
self.assertRecordValues(wizard, [{
'amount': 3000.0,
'currency_id': foreign_curr.id,
'custom_user_amount': 3000.0,
'custom_user_currency_id': foreign_curr.id,
'payment_difference': 450.0,
'installments_mode': 'full',
'installments_switch_amount': 1035.0,
}])
# Change the date (rate changed from 1:3 to 1:2).
with Form(wizard) as wizard_form:
wizard_form.payment_date = fields.Date.from_string('2017-01-01')
self.assertRecordValues(wizard, [{
'amount': 3000.0,
'currency_id': foreign_curr.id,
'custom_user_amount': 3000.0,
'custom_user_currency_id': foreign_curr.id,
'payment_difference': -700.0,
'installments_mode': 'full',
'installments_switch_amount': 2300.0,
}])
def test_installment_mode_single_batch(self):
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2016-01-01',
'invoice_payment_term_id': self.term_0_5_10_days.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id})],
})
invoice.action_post()
self.assertRecordValues(invoice.line_ids.sorted().filtered('date_maturity'), [
{'date_maturity': fields.Date.from_string('2016-01-01'), 'amount_currency': 115.0},
{'date_maturity': fields.Date.from_string('2016-01-06'), 'amount_currency': 345.0},
{'date_maturity': fields.Date.from_string('2016-01-11'), 'amount_currency': 690.0},
])
term_lines = invoice.line_ids.filtered('date_maturity').sorted('date_maturity')
wizard = self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({'payment_date': '2015-12-31'})
self.assertRecordValues(wizard, [{
'amount': 115.0,
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 1150.0,
'communication': wizard._get_communication(term_lines[0]),
}])
# Paying less switch the wizard in full mode.
wizard.amount = 114.0
self.assertRecordValues(wizard, [{
'amount': 114.0,
'payment_difference': 1036.0,
'installments_mode': 'full',
'installments_switch_amount': 115.0,
'communication': invoice.name,
}])
# Case when at date of the first installment.
wizard.payment_date = '2016-01-01'
self.assertRecordValues(wizard, [{
'amount': 115.0,
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 1150.0,
'communication': wizard._get_communication(term_lines[0]),
}])
# Paying more switch the wizard in full mode.
wizard.amount = 116.0
self.assertRecordValues(wizard, [{
'amount': 116.0,
'payment_difference': 1034.0,
'installments_mode': 'full',
'installments_switch_amount': 115.0,
'communication': invoice.name,
}])
# First installment is overdue.
wizard.payment_date = '2016-01-02'
self.assertRecordValues(wizard, [{
'amount': 115.0,
'payment_difference': 0.0,
'installments_mode': 'overdue',
'installments_switch_amount': 1150.0,
'communication': wizard._get_communication(term_lines[0]),
}])
# Second installment is overdue.
wizard.payment_date = '2016-01-07'
self.assertRecordValues(wizard, [{
'amount': 460.0,
'payment_difference': 0.0,
'installments_mode': 'overdue',
'installments_switch_amount': 1150.0,
'communication': invoice.name,
}])
# Third installment is overdue.
wizard.payment_date = '2016-01-12'
self.assertRecordValues(wizard, [{
'amount': 1150.0,
'payment_difference': 0.0,
'installments_mode': 'full',
'installments_switch_amount': 0.0,
'communication': invoice.name,
}])
def test_installment_mode_multiple_batches(self):
""" Tests the wizard values if you select several invoices that produce several batches, with installments """
in_invoice_cad_with_payment_term, in_invoice_cad_copy = self.env['account.move'].create([
{
'move_type': 'in_invoice',
'invoice_date': '2016-01-01',
'invoice_payment_term_id': self.term_0_5_10_days.id,
'currency_id': self.other_currency_2.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
},
{
'move_type': 'in_invoice',
'invoice_date': '2016-01-01',
'invoice_payment_term_id': self.term_0_5_10_days.id,
'currency_id': self.other_currency_2.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({'product_id': self.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
},
])
(in_invoice_cad_with_payment_term + in_invoice_cad_copy).action_post()
wizard = self.env['account.payment.register'].with_context(
active_model='account.move', active_ids=(self.in_invoice_1 + in_invoice_cad_with_payment_term).ids
).create({'payment_date': '2016-01-01'})
self.assertRecordValues(wizard, [{
'amount': 1033.33, # 1000 from in_invoice_1 + 1000 * 0.1 (payment_term) / 3 (rate of CAD) for second one
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 1333.33,
'currency_id': self.company.currency_id.id, # Different currencies, so we get the company's one
}])
wizard = self.env['account.payment.register'].with_context(
active_model='account.move', active_ids=(self.in_invoice_1 + in_invoice_cad_with_payment_term).ids
).create({'payment_date': '2016-01-07'})
# Some installments are overdue for the second bill, but not the first one, so it is in next
self.assertRecordValues(wizard, [{
'amount': 1133.33, # 1000 from in_invoice_1 + 1000 * 0.4 (payment_term) / 3 (rate of CAD) for second one
'payment_difference': 0.0,
'installments_mode': 'next',
'installments_switch_amount': 1333.33,
}])
wizard = self.env['account.payment.register'].with_context(
active_model='account.move', active_ids=(in_invoice_cad_with_payment_term + in_invoice_cad_copy).ids
).create({'payment_date': '2016-01-07'})
# Some installments are overdue for the second bill, but not the first one, so it is in next
self.assertRecordValues(wizard, [{
'amount': 800, # both invoices have the payment term at 40%
'payment_difference': 0.0,
'installments_mode': 'overdue',
'currency_id': self.other_currency_2.id, # Same currency, so we can provide the right one
'installments_switch_amount': 2000,
}])
wizard = self.env['account.payment.register'].with_context(
active_model='account.move', active_ids=(self.in_invoice_epd_applied + in_invoice_cad_with_payment_term).ids
).create({'payment_date': '2016-01-01'})
self.assertRecordValues(wizard, [{
'amount': 57.83, # 24.5 for in_invoice_epd_applied + 1000 * 0.1 (payment_term) / 3 (rate) for the second
'payment_difference': 0.5,
'installments_mode': 'next',
'installments_switch_amount': 357.83, # 24.5 for in_invoice_epd_applied + 1000 / 3 (rate) for the second
}])
# Clicking on the button to full gets the amount from js, so we need to put it by hand here
wizard.write({
'installments_mode': 'full',
'amount': 357.83,
})
self.assertRecordValues(wizard, [{
'amount': 357.83, # The switch amount computed above
'payment_difference': 0.5,
'installments_mode': 'full',
'installments_switch_amount': 57.83, # The previous 'next' amount
}])
def test_payment_register_with_next_payment_date(self):
invoice_2 = self.env['account.move'].create({
'move_type': 'in_invoice',
'date': '2017-01-01',
'invoice_date': '2017-01-01',
'invoice_payment_term_id': self.term_0_5_10_days.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [(0, 0, {'product_id': self.product_a.id, 'price_unit': 1000.0, 'tax_ids': []})],
})
wizard = self.env['account.payment.register'].with_context(
active_domain=[['next_payment_date', '=', '2017-01-07']], # To mimick next_payment date in the search
active_model='account.move', active_ids=invoice_2.ids
).create({'payment_date': '2017-01-01'})
# as we have next_payment_date in the search domain, we go in before_date and pay all installments before this
# date (and not taken the payment_date into account there)
self.assertRecordValues(wizard, [{
'amount': 400, # The switch amount computed above
'installments_mode': 'before_date',
'installments_switch_amount': 1000, # The full amount
}])
def test_payment_with_branch(self):
"""
Test register payment with branches when different receivable accounts are used in each branch with combinations of invoices and selected companies
"""
def test_register_payment_flow(cases):
for group_payment, case in product((False, True), cases):
invoices, selected_companies, expected_companies, expected_pmnt_comp, should_raise = case.values()
with self.subTest(invoices=invoices.mapped('name'), selected_companies=selected_companies.mapped('name')):
wizard = self.env['account.payment.register'].with_context(allowed_company_ids=selected_companies.ids, active_model='account.move', active_ids=invoices.ids).create({'group_payment': group_payment})
available_journals = self.get_wizard_available_journals(wizard)
self.assertEqual(available_journals.company_id, expected_companies)
if should_raise:
with self.assertRaisesRegex(UserError, 'got some company inconsistencies here:'):
wizard._create_payments()
else:
payments = wizard._create_payments()
self.assertEqual(payments.company_id, expected_pmnt_comp)
invoices.line_ids.filtered(lambda l: l.display_type == 'payment_term').remove_move_reconcile()
# create a new branch and other company
self._create_company(name='New Branch', parent_id=self.env.company.id)
branches = self.env.company.child_ids
self.user_branch.company_ids = branches
company_2 = self._create_company(name='New Company')
# PART 1: Basic cases
# create invoices on branches
branch_invoices = self.env['account.move']
for branch in branches:
self.env["account.journal"].create({
'code': 'TEST',
'company_id': branch.id,
'name': f'{branch.name} journal',
'type': 'bank',
})
branch_invoices |= self.init_invoice('out_invoice', products=self.product_a, company=branch)
parent_invoice = self.init_invoice('out_invoice', products=self.product_a)
other_company_invoice = self.init_invoice('out_invoice', products=self.product_a, company=company_2)
(branch_invoices | parent_invoice | other_company_invoice).action_post()
# test first branches invoices with branch user
with self.with_user('user_branch'):
with self.assertRaisesRegex(UserError, 'branches without access to parent company.'):
self.env['account.payment.register'].with_context(allowed_company_ids=branches.ids, active_model='account.move', active_ids=branch_invoices.ids).create({})
# test also with two differents companies
with (self.assertRaisesRegex(UserError, 'for entries belonging to different companies.')):
for group_payment in [False, True]:
self.env['account.payment.register'].with_context(
allowed_company_ids=(self.env.company + company_2).ids,
active_model='account.move',
active_ids=(parent_invoice + other_company_invoice).ids
).create({'group_payment': group_payment})
cases = [
{
'invoices': branch_invoices[0] + parent_invoice,
'selected_companies': (self.branch + self.env.company),
'expected_companies': self.env.company,
'expected_pmnt_comp': self.env.company,
'should_raise': False,
},
{
'invoices': branch_invoices,
'selected_companies': branches,
'expected_companies': self.env.company,
'expected_pmnt_comp': self.env.company,
'should_raise': False,
},
{
'invoices': branch_invoices + parent_invoice,
'selected_companies': self.env.company._accessible_branches(),
'expected_companies': self.env.company,
'expected_pmnt_comp': self.env.company,
'should_raise': False,
},
{
'invoices': branch_invoices[0],
'selected_companies': self.branch,
'expected_companies': (self.env.company + self.branch),
'expected_pmnt_comp': self.branch,
'should_raise': False,
},
{
'invoices': branch_invoices,
'selected_companies': self.env.company._accessible_branches(),
'expected_companies': self.env.company,
'expected_pmnt_comp': self.env.company,
'should_raise': False,
},
]
test_register_payment_flow(cases)
# PART 2: Test the same cases with different receivable accounts for each branch
# An error should be raised as the receivable account doesn't belong to the wizard's company, except for the case where we register payment only for one branch
branch_invoices.button_draft()
for branch in branches:
receivable_account = self.company_data['default_account_receivable'].with_company(branch).copy({'company_ids': branch.ids})
branch_invoice = branch_invoices.filtered(lambda inv: inv.company_id == branch)
# To mock the situation where the partner has his own receivable account depending on the branch
branch_invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term').account_id = receivable_account
branch_invoices.action_post()
new_cases = [
{
'invoices': branch_invoices[0] + parent_invoice,
'expected_pmnt_comp': False,
'should_raise': True,
},
{
'invoices': branch_invoices,
'expected_pmnt_comp': False,
'should_raise': True,
},
{
'invoices': branch_invoices + parent_invoice,
'expected_pmnt_comp': False,
'should_raise': True,
},
{
'invoices': branch_invoices[0],
'expected_pmnt_comp': self.branch,
'should_raise': False,
},
{
'invoices': branch_invoices,
'expected_pmnt_comp': False,
'should_raise': True,
},
]
cases = [{**case, **new_case} for case, new_case in zip(cases, new_cases)]
test_register_payment_flow(cases)
def test_epd_and_cash_rounding(self):
cash_rounding = self.env['account.cash.rounding'].create({
'name': 'add_invoice_line',
'rounding': 0.05,
'strategy': 'add_invoice_line',
'profit_account_id': self.company_data['default_account_revenue'].copy().id,
'loss_account_id': self.company_data['default_account_expense'].copy().id,
'rounding_method': 'UP',
})
payment_term = self.env.ref('account.account_payment_term_30days_early_discount')
tax = self.env['account.tax'].create({
'name': "21",
'amount_type': 'percent',
'amount': 21.0,
})
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2024-01-01',
'invoice_payment_term_id': payment_term.id,
'invoice_cash_rounding_id': cash_rounding.id,
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({
'product_id': self.product_a.id,
'price_unit': 11,
'tax_ids': [Command.set(tax.ids)],
})]
})
invoice.action_post()
self.assertRecordValues(invoice, [{'amount_total': 13.35}])
self.env['account.payment.register']\
.with_context(active_model='account.move', active_ids=invoice.ids)\
.create({'payment_date': '2024-01-01'})\
._create_payments()
self.assertRecordValues(invoice, [{'amount_residual': 0.0}])
@users('user_branch')
def test_branch_user_register_payment(self):
bill = self.env['account.move'].create({
'move_type': 'in_invoice',
'invoice_date': '2024-05-01',
'partner_id': self.partner_a.id,
'invoice_line_ids': [Command.create({
'name': 'line',
'price_unit': 1000,
'quantity': 1,
})]
})
bill.action_post()
wizard = self.env['account.payment.register'].with_context(allowed_company_ids=self.env.company.ids, active_model='account.move', active_ids=bill.ids).create({
'amount': bill.amount_total,
'currency_id': bill.currency_id.id,
'payment_method_line_id': self.inbound_payment_method_line.id,
})
self.env.company.parent_ids.invalidate_recordset()
payment = wizard._create_payments()
self.assertTrue(payment)
def test_payment_register_wizard_without_receivable_line_due_date(self):
"""Test creating the payment register wizard when a receivable line has no due date."""
invoice = self.out_invoice_1
invoice.button_draft()
invoice.invoice_payment_term_id = self.term_0_5_10_days
receivable_lines = invoice.line_ids.filtered(lambda x: x.account_type == 'asset_receivable')
self.assertEqual(len(receivable_lines), 3)
receivable_lines[0].date_maturity = False
invoice.action_post()
wizard = Form(self.env['account.payment.register'].with_context(
active_model='account.move', active_ids=invoice.ids))
self.assertEqual(wizard.amount, invoice.amount_residual)
self.assertRecordValues(receivable_lines, [
{'amount_currency': 100, 'date_maturity': False},
{'amount_currency': 300, 'date_maturity': fields.Date.from_string('2017-01-06')},
{'amount_currency': 600, 'date_maturity': fields.Date.from_string('2017-01-11')},
])
def test_payment_register_misc_different_batches(self):
""" Tests that payments that should be in different batches stay in different ones """
move1 = self.env['account.move'].create({
'move_type': 'entry',
'journal_id': self.company_data['default_journal_misc'].id,
'date': '2025-01-01',
'line_ids': [
Command.create({
'account_id': self.company_data['default_account_receivable'].id,
'partner_id': self.partner_a.id,
'balance': 100.0,
}),
Command.create({
'account_id': self.company_data['default_account_receivable'].id,
'partner_id': self.partner_b.id,
'balance': 100.0,
}),
Command.create({
'account_id': self.company_data['default_account_revenue'].id,
'balance': -200.0,
}),
],
})
move2 = self.env['account.move'].create({
'move_type': 'entry',
'journal_id': self.company_data['default_journal_misc'].id,
'date': '2025-01-01',
'line_ids': [
Command.create({
'account_id': self.company_data['default_account_receivable'].id,
'partner_id': self.partner_a.id,
'balance': 100.0,
}),
Command.create({
'account_id': self.company_data['default_account_revenue'].id,
'balance': -100.0,
}),
],
})
payments = self._register_payment(move1 + move2, group_payment=False)
self.assertEqual(len(payments), 3, "We should get 2 payments from the first move and 1 for the second one")