1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/account_bacs/tests/test_bacs.py
2024-12-10 09:04:09 +07:00

306 lines
17 KiB
Python

# -*- coding: utf-8 -*-
from odoo import fields
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.account_bacs.models.account_journal import format_communication
from odoo.tests import tagged
import itertools
import datetime
import base64
@tagged('post_install', '-at_install')
class TestBACS(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref)
cls.env.ref('base.GBP').active = True
cls.bank_barclays = cls.env['res.bank'].create({
'name': 'BARCLAYS BANK PLC',
'bic': 'BARCGB22XXX',
})
cls.bank_hsbc = cls.env['res.bank'].create({
'name': 'HSBC',
'bic': 'HBUKGB4BXXX',
})
cls.company_data['company'].write({
'country_id': cls.env.ref('base.be').id,
'bacs_sun': '123456',
})
cls.bank_journal = cls.company_data['default_journal_bank']
cls.bank_journal.write({
'bank_id': cls.bank_barclays.id,
'bank_acc_number': 'GB11BARC20039525689371',
'currency_id': cls.env.ref('base.GBP').id,
})
cls.bacs_dc = cls.bank_journal.outbound_payment_method_line_ids.filtered(lambda l: l.code == 'bacs_dc')
cls.bacs_dc_method = cls.env.ref('account_bacs.payment_method_bacs_dc')
cls.bacs_dd = cls.bank_journal.inbound_payment_method_line_ids.filtered(lambda l: l.code == 'bacs_dd')
cls.bacs_dd_method = cls.env.ref('account_bacs.payment_method_bacs_dd')
def create_account(self, number, partner, bank):
return self.env['res.partner.bank'].create({
'acc_number': number,
'partner_id': partner.id,
'bank_id': bank.id,
'allow_out_payment': True,
})
def create_ddi(self, partner, partner_bank, company, payment_journal):
return self.env['bacs.ddi'].create({
'partner_bank_id': partner_bank.id,
'start_date': fields.Date.today(),
'partner_id': partner.id,
'company_id': company.id,
'payment_journal_id': payment_journal.id
})
def create_invoice(self, partner):
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': partner.id,
'currency_id': self.env.ref('base.GBP').id,
'payment_reference': 'invoice to client',
'invoice_line_ids': [(0, 0, {
'product_id': self.env['product.product'].create({'name': 'A Test Product'}).id,
'quantity': 1,
'price_unit': 42,
'name': 'something',
})],
})
invoice.action_post()
return invoice
def pay_with_mandate(self, invoice, mandate):
self.env['account.payment.register'].with_context(active_model='account.move', active_ids=invoice.ids).create({
'payment_date': invoice.invoice_date_due or invoice.invoice_date,
'journal_id': mandate.payment_journal_id.id,
'payment_method_line_id': self.bacs_dd.id,
})._create_payments()
def create_payment(self, partner, amount, payment_method, journal, date, payment_type, partner_bank=None):
return self.env['account.payment'].create({
'journal_id': journal.id,
'payment_method_line_id': payment_method.id,
'payment_type': payment_type,
'date': date,
'amount': amount,
'partner_id': partner.id,
'partner_bank_id': partner_bank.id if partner_bank else False,
})
def verify_bacs_file_headers(self, company, batch_payment, header_lines):
# test vol1
self.assertEqual(len(header_lines[0]), 80)
self.assertEqual(header_lines[0][:4], 'VOL1')
self.assertEqual(header_lines[0][4:10], batch_payment.bacs_submission_serial)
self.assertEqual(header_lines[0][10:41], ' ' * 31)
self.assertEqual(header_lines[0][41:47], company.bacs_sun)
self.assertEqual(header_lines[0][47:79], ' ' * 32)
self.assertEqual(header_lines[0][79:], '1')
# test HDR1
self.assertEqual(len(header_lines[1]), 80)
self.assertEqual(header_lines[1][:5], 'HDR1A')
self.assertEqual(header_lines[1][5:11], company.bacs_sun)
self.assertEqual(header_lines[1][11:15], 'S 1')
self.assertEqual(header_lines[1][15:21], company.bacs_sun)
self.assertEqual(header_lines[1][21:27], batch_payment.bacs_submission_serial)
self.assertEqual(header_lines[1][27:35], '00010001')
self.assertEqual(header_lines[1][35:41], ' ' * 6)
self.assertEqual(header_lines[1][41:47], batch_payment.date.strftime(' %y%j'))
self.assertEqual(header_lines[1][47:53], batch_payment.bacs_expiry_date.strftime(' %y%j'))
self.assertEqual(header_lines[1][53:], ' ' * 27)
# test HDR2
self.assertEqual(len(header_lines[2]), 80)
self.assertEqual(header_lines[2][:5], 'HDR2F')
self.assertEqual(header_lines[2][5:10], '02000')
self.assertEqual(header_lines[2][10:15], '00100')
self.assertEqual(header_lines[2][15:], ' ' * 65)
# test UHL1
self.assertEqual(len(header_lines[3]), 80)
self.assertEqual(header_lines[3][:4], 'UHL1')
self.assertEqual(header_lines[3][4:10], ' ' * 6 if batch_payment.bacs_multi_mode else batch_payment.bacs_processing_date.strftime(' %y%j'))
self.assertEqual(header_lines[3][10:20], company.bacs_sun.ljust(10))
self.assertEqual(header_lines[3][20:28], '0' * 8)
self.assertEqual(header_lines[3][28:37], '4 MULTI ' if batch_payment.bacs_multi_mode else '1 DAILY ')
self.assertEqual(header_lines[3][37:], ' ' * 43)
def verify_contra_line(self, company, payments, contra_line, is_multi, payment_method_code):
self.assertEqual(len(contra_line), 106 if is_multi else 100)
self.assertEqual(contra_line[:14], self.bank_journal.bank_account_id.sanitized_acc_number[8:])
self.assertEqual(contra_line[14:15], '0')
self.assertEqual(contra_line[15:17], '99' if payment_method_code == 'bacs_dd' else '17')
self.assertEqual(contra_line[17:31], self.bank_journal.bank_account_id.sanitized_acc_number[8:])
self.assertEqual(contra_line[31:35], ' ' * 4)
contra_amount = sum([int(payment.amount * 100) for payment in payments])
self.assertEqual(contra_line[35:46], str(contra_amount).rjust(11, '0'))
self.assertEqual(contra_line[64:82], 'CONTRA'.ljust(18))
self.assertEqual(contra_line[82:100], format_communication(company.name).ljust(18))
if is_multi:
self.assertEqual(contra_line[100:], payments[0].date.strftime(' %y%j'))
def verify_payment_line(self, company, payment, line, is_multi, payment_method_code):
self.assertEqual(len(line), 106 if is_multi else 100)
self.assertEqual(line[:14], payment.partner_bank_id.sanitized_acc_number[8:] if payment_method_code == 'bacs_dc' else payment.bacs_ddi_id.partner_bank_id.sanitized_acc_number[8:])
self.assertEqual(line[14:15], '0')
self.assertIn(line[15:17], ['01', '17', '18', '19'] if payment_method_code == 'bacs_dd' else ['99'])
self.assertEqual(line[17:31], self.bank_journal.bank_account_id.sanitized_acc_number[8:])
self.assertEqual(line[31:35], ' ' * 4)
self.assertEqual(line[35:46], str(int(payment.amount * 100)).rjust(11, '0'))
self.assertEqual(line[46:64], format_communication(company.name[:18]).ljust(18))
self.assertEqual(line[82:100], format_communication(payment.partner_id.name[:18]).ljust(18))
if is_multi:
self.assertEqual(line[100:], payment.date.strftime(' %y%j'))
def verify_bacs_file_payments(self, company, batch_payment, payment_lines):
if not batch_payment.bacs_multi_mode:
self.assertEqual(len(payment_lines), len(batch_payment.payment_ids) + 1)
for i, payment_line in enumerate(payment_lines):
if i == len(payment_lines) - 1:
self.verify_contra_line(company, batch_payment.payment_ids, payment_line, False, batch_payment.payment_method_id.code)
else:
self.verify_payment_line(company, batch_payment.payment_ids[i], payment_line, False, batch_payment.payment_method_id.code)
else:
# group payments by date and each date should have tranaction reccords of the date followed by a contra line
payments_by_date = itertools.groupby(batch_payment.payment_ids.sorted(key=lambda p: p.date), key=lambda p: p.date)
self.assertEqual(len(payment_lines), len(batch_payment.payment_ids) + len(list(payments_by_date)))
start = 0
for _, payments in payments_by_date:
payments = list(payments)
for i, payment in enumerate(payments):
self.verify_payment_line(company, payment, payment_lines[i + start], True, batch_payment.payment_method_id.code)
self.verify_contra_line(company, payments, payment_lines[len(payments) + start], True, batch_payment.payment_method_id.code)
start += len(payments) + 1
def verify_bacs_file_footers(self, company, batch_payment, footer_lines):
# test EOF1
self.assertEqual(len(footer_lines[0]), 80)
self.assertEqual(footer_lines[0][:5], 'EOF1A')
self.assertEqual(footer_lines[0][5:11], company.bacs_sun)
self.assertEqual(footer_lines[0][11:15], 'S 1')
self.assertEqual(footer_lines[0][15:21], company.bacs_sun)
self.assertEqual(footer_lines[0][21:27], batch_payment.bacs_submission_serial)
self.assertEqual(footer_lines[0][27:35], '00010001')
self.assertEqual(footer_lines[0][35:41], ' ' * 6)
self.assertEqual(footer_lines[0][41:47], batch_payment.date.strftime(' %y%j'))
self.assertEqual(footer_lines[0][47:53], batch_payment.bacs_expiry_date.strftime(' %y%j'))
self.assertEqual(footer_lines[0][53:], ' ' * 27)
# test EOF2
self.assertEqual(len(footer_lines[1]), 80)
self.assertEqual(footer_lines[1][:5], 'EOF2F')
self.assertEqual(footer_lines[1][5:10], '02000')
self.assertEqual(footer_lines[1][10:15], '00100')
self.assertEqual(footer_lines[1][15:], ' ' * 65)
# test UTL1
self.assertEqual(len(footer_lines[2]), 80)
self.assertEqual(footer_lines[2][:4], 'UTL1')
total = sum([int(payment.amount * 100) for payment in batch_payment.payment_ids])
self.assertEqual(footer_lines[2][4:17], str(total).rjust(13, '0'))
self.assertEqual(footer_lines[2][17:30], footer_lines[2][4:17])
transaction_record_count = str(len(batch_payment.payment_ids)).rjust(7, '0')
contra_record_count = str(len(set(payment.date for payment in batch_payment.payment_ids))).rjust(7, '0') if batch_payment.bacs_multi_mode else '0000001'
code = batch_payment.payment_method_id.code
self.assertEqual(footer_lines[2][30:37], transaction_record_count if code == 'bacs_dd' else contra_record_count)
self.assertEqual(footer_lines[2][37:44], contra_record_count if code == 'bacs_dd' else transaction_record_count)
self.assertEqual(footer_lines[2][44:], ' ' * 36)
def verify_bacs_file(self, company, batch):
binary_data = base64.b64decode(batch.export_file)
file_string = binary_data.decode('utf-8')
split_file = file_string.rstrip('\n').split('\n')
header_lines = split_file[:4]
payment_lines = split_file[4:-3]
footer_lines = split_file[-3:]
self.assertEqual(len(header_lines), 4, "There should be 4 header lines in the file")
self.assertEqual(len(footer_lines), 3, "There should be 3 footer lines in the file")
self.verify_bacs_file_headers(company, batch, header_lines)
self.verify_bacs_file_payments(company, batch, payment_lines)
self.verify_bacs_file_footers(company, batch, footer_lines)
def verify_multi_payments(self, company, partner, multi_mode, payment_method, payment_method_line, payment_type, partner_bank=None):
payment_1 = self.create_payment(partner, 42, payment_method_line, self.bank_journal, datetime.date.today() + datetime.timedelta(days=10), payment_type, partner_bank)
payment_1.action_post()
payment_2 = self.create_payment(partner, 1337, payment_method_line, self.bank_journal, datetime.date.today() + datetime.timedelta(days=15), payment_type, partner_bank)
payment_2.action_post()
payment_3 = self.create_payment(partner, 21, payment_method_line, self.bank_journal, datetime.date.today() + datetime.timedelta(days=20), payment_type, partner_bank)
payment_3.action_post()
payment_4 = self.create_payment(partner, 1916, payment_method_line, self.bank_journal, datetime.date.today() + datetime.timedelta(days=20), payment_type, partner_bank)
payment_4.action_post()
batch = self.env['account.batch.payment'].create({
'batch_type': payment_type,
'bacs_processing_date': fields.Date.today(),
'bacs_multi_mode': multi_mode,
'payment_ids': [(4, payment.id, None) for payment in [payment_1, payment_2, payment_3, payment_4]],
'journal_id': self.bank_journal.id,
'payment_method_id': payment_method.id,
})
wizard_action = batch.validate_batch()
self.assertFalse(wizard_action, "Validation wizard should not have returned an action")
self.verify_bacs_file(company, batch)
def testBacsDirectDebit(self):
company = self.env.company
partner_boundagani = self.env['res.partner'].create({'name': 'Boundagani'})
partner_bank_boundagani = self.create_account('GB03BARC20031819726663', partner_boundagani, self.bank_hsbc)
ddi_boundagani = self.create_ddi(partner_boundagani, partner_bank_boundagani, self.env.company, self.bank_journal)
ddi_boundagani.action_validate_ddi()
invoice_boundagani = self.create_invoice(partner_boundagani)
self.pay_with_mandate(invoice_boundagani, ddi_boundagani)
payment_boundagani = invoice_boundagani.line_ids.mapped('matched_credit_ids.credit_move_id.payment_id')
self.assertEqual(invoice_boundagani.payment_state, self.env['account.move']._get_invoice_in_payment_state(), 'This invoice should have been paid thanks to the mandate')
self.assertEqual(invoice_boundagani.bacs_ddi_id, ddi_boundagani, 'The invoice should have the right mandate')
# test single payment
batch = self.env['account.batch.payment'].create({
'bacs_processing_date': fields.Date.today(),
'bacs_multi_mode': False,
'payment_ids': [(4, payment_boundagani.id, None)],
'journal_id': self.bank_journal.id,
'payment_method_id': self.bacs_dd_method.id,
'batch_type': 'inbound',
})
wizard_action = batch.validate_batch()
self.assertFalse(wizard_action, "Validation wizard should not have returned an action")
self.verify_bacs_file(company, batch)
# test multi payment with single mode
self.verify_multi_payments(company, partner_boundagani, False, self.bacs_dd_method, self.bacs_dd, 'inbound')
# test multi payment with multi mode
self.verify_multi_payments(company, partner_boundagani, True, self.bacs_dd_method, self.bacs_dd, 'inbound')
def testBacsDirectCredit(self):
company = self.env.company
vendor_superlux = self.env['res.partner'].create({'name': 'Superlux'})
vendor_bank_superlux = self.create_account('GB70BARC20038066716256', vendor_superlux, self.bank_hsbc)
payment_superlux = self.create_payment(vendor_superlux, 42, self.bacs_dc, self.bank_journal, datetime.date.today() + datetime.timedelta(days=10), 'outbound', vendor_bank_superlux)
payment_superlux.action_post()
# test single payment
batch = self.env['account.batch.payment'].create({
'bacs_processing_date': fields.Date.today(),
'bacs_multi_mode': False,
'payment_ids': [(4, payment_superlux.id, None)],
'journal_id': self.bank_journal.id,
'payment_method_id': self.bacs_dc_method.id,
'batch_type': 'outbound',
})
wizard_action = batch.validate_batch()
self.assertFalse(wizard_action, "Validation wizard should not have returned an action")
self.verify_bacs_file(company, batch)
# test multi payment with single mode
self.verify_multi_payments(company, vendor_superlux, False, self.bacs_dc_method, self.bacs_dc, 'outbound', vendor_bank_superlux)
# test multi payment with multi mode
self.verify_multi_payments(company, vendor_superlux, True, self.bacs_dc_method, self.bacs_dc, 'outbound', vendor_bank_superlux)