Files
tradon/modules/account_payment_sepa/tests/test_module.py
2026-03-14 09:42:12 +00:00

505 lines
18 KiB
Python

# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime as dt
import os
from decimal import Decimal
from io import BytesIO, open
from unittest.mock import Mock, patch
from lxml import etree
from trytond.exceptions import UserError
from trytond.modules.account.tests import create_chart
from trytond.modules.account_payment_sepa.payment import CAMT054
from trytond.modules.company.tests import (
CompanyTestMixin, create_company, set_company)
from trytond.modules.currency.tests import create_currency
from trytond.modules.party.tests import PartyCheckReplaceMixin
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.transaction import Transaction
def setup_environment():
pool = Pool()
Address = pool.get('party.address')
Party = pool.get('party.party')
Bank = pool.get('bank')
Identifier = pool.get('party.identifier')
currency = create_currency('EUR')
company = create_company(currency=currency)
sepa = Identifier(party=company.party, code='ES23ZZZ47690558N',
type='eu_at_02')
sepa.save()
bank_party = Party(name='European Bank')
bank_party.save()
bank = Bank(party=bank_party, bic='BICODEBBXXX')
bank.save()
customer = Party(name='Customer')
address = Address(street='street', postal_code='1234', city='City')
customer.addresses = [address]
customer.save()
return {
'company': company,
'bank': bank,
'customer': customer,
}
def setup_accounts(bank, company, customer):
pool = Pool()
Account = pool.get('bank.account')
return Account.create([{
'bank': bank,
'owners': [('add', [company.party])],
'currency': company.currency.id,
'numbers': [('create', [{
'type': 'iban',
'number': 'ES8200000000000000000000',
}])]}, {
'bank': bank,
'owners': [('add', [customer])],
'currency': company.currency.id,
'numbers': [('create', [{
'type': 'iban',
'number': 'ES3600000000050000000001',
}])]}])
def setup_mandate(company, customer, account):
pool = Pool()
Mandate = pool.get('account.payment.sepa.mandate')
Date = pool.get('ir.date')
return Mandate.create([{
'company': company,
'party': customer,
'address': customer.addresses[0],
'account_number': account.numbers[0],
'identification': 'MANDATE',
'type': 'recurrent',
'sequence_type_rcur': False,
'signature_date': Date.today(),
'state': 'validated',
}])[0]
def setup_journal(flavor, kind, company, account):
pool = Pool()
Journal = pool.get('account.payment.journal')
journal = Journal()
journal.name = flavor
journal.company = company
journal.currency = company.currency
journal.process_method = 'sepa'
journal.sepa_bank_account_number = account.numbers[0]
journal.sepa_payable_flavor = 'pain.001.001.03'
journal.sepa_receivable_flavor = 'pain.008.001.02'
setattr(journal, 'sepa_%s_flavor' % kind, flavor)
journal.save()
return journal
def validate_file(flavor, kind, xsd=None):
'Test generated files are valid'
pool = Pool()
Payment = pool.get('account.payment')
PaymentGroup = pool.get('account.payment.group')
Date = pool.get('ir.date')
ProcessPayment = pool.get('account.payment.process', type='wizard')
if xsd is None:
xsd = flavor
environment = setup_environment()
company = environment['company']
bank = environment['bank']
customer = environment['customer']
with set_company(company):
company_account, customer_account = setup_accounts(
bank, company, customer)
setup_mandate(company, customer, customer_account)
journal = setup_journal(flavor, kind, company, company_account)
payment, = Payment.create([{
'company': company,
'party': customer,
'journal': journal,
'kind': kind,
'amount': Decimal('1000.0'),
'state': 'approved' if kind == 'payable' else 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
}])
session_id, _, _ = ProcessPayment.create()
process_payment = ProcessPayment(session_id)
with Transaction().set_context(
active_model=Payment.__name__, active_ids=[payment.id]):
_, data = process_payment.do_process(None)
group, = PaymentGroup.browse(data['res_id'])
message, = group.sepa_messages
assert message.type == 'out', message.type
assert message.state == 'waiting', message.state
sepa_string = bytes(message.message)
sepa_xml = etree.fromstring(sepa_string)
schema_file = os.path.join(os.path.dirname(__file__),
'%s.xsd' % xsd)
schema = etree.XMLSchema(etree.parse(schema_file))
schema.assertValid(sepa_xml)
class AccountPaymentSepaTestCase(
PartyCheckReplaceMixin, CompanyTestMixin, ModuleTestCase):
'Test Account Payment SEPA module'
module = 'account_payment_sepa'
@with_transaction()
def test_pain001_001_03(self):
'Test pain001.001.03 xsd validation'
validate_file('pain.001.001.03', 'payable')
@with_transaction()
def test_pain001_001_05(self):
'Test pain001.001.05 xsd validation'
validate_file('pain.001.001.05', 'payable')
@with_transaction()
def test_pain001_003_03(self):
'Test pain001.003.03 xsd validation'
validate_file('pain.001.003.03', 'payable')
@with_transaction()
def test_pain008_001_02(self):
'Test pain008.001.02 xsd validation'
validate_file('pain.008.001.02', 'receivable')
@with_transaction()
def test_pain008_001_04(self):
'Test pain008.001.04 xsd validation'
validate_file('pain.008.001.04', 'receivable')
@with_transaction()
def test_pain008_003_02(self):
'Test pain008.003.02 xsd validation'
validate_file('pain.008.003.02', 'receivable')
@with_transaction()
def test_sepa_mandate_sequence(self):
'Test SEPA mandate sequence'
pool = Pool()
Configuration = pool.get('account.configuration')
Sequence = pool.get('ir.sequence')
SequenceType = pool.get('ir.sequence.type')
Party = pool.get('party.party')
Mandate = pool.get('account.payment.sepa.mandate')
party = Party(name='Test')
party.save()
company = create_company()
with set_company(company):
mandate = Mandate(party=party)
mandate.save()
self.assertFalse(mandate.identification)
sequence_type, = SequenceType.search([
('name', '=', "SEPA Mandate"),
], limit=1)
sequence = Sequence(name="Test", sequence_type=sequence_type)
sequence.save()
config = Configuration(1)
config.sepa_mandate_sequence = sequence
config.save()
mandate = Mandate(party=party)
mandate.save()
self.assertTrue(mandate.identification)
@with_transaction()
def test_identification_unique(self):
'Test unique identification constraint'
pool = Pool()
Party = pool.get('party.party')
Mandate = pool.get('account.payment.sepa.mandate')
same_id = '1'
party = Party(name='Test')
party.save()
company = create_company()
with set_company(company):
mandate = Mandate(party=party, identification=same_id)
mandate.save()
for i in range(2):
mandate = Mandate(party=party)
mandate.save()
mandate = Mandate(party=party, identification='')
mandate.save()
self.assertEqual(mandate.identification, None)
Mandate.write([mandate], {
'identification': '',
})
self.assertEqual(mandate.identification, None)
self.assertRaises(UserError, Mandate.create, [{
'party': party.id,
'identification': same_id,
}])
@with_transaction()
def test_payment_sepa_bank_account_number(self):
'Test Payment.sepa_bank_account_number'
pool = Pool()
Payment = pool.get('account.payment')
Mandate = pool.get('account.payment.sepa.mandate')
AccountNumber = pool.get('bank.account.number')
Party = pool.get('party.party')
BankAccount = pool.get('bank.account')
company = create_company()
with set_company(company):
create_chart(company)
account_number = AccountNumber()
mandate = Mandate(account_number=account_number)
payment = Payment(kind='receivable', sepa_mandate=mandate)
self.assertEqual(id(payment.sepa_bank_account_number),
id(account_number))
other_account_number = AccountNumber(type='other')
iban_account_number = AccountNumber(type='iban')
bank_account = BankAccount(
numbers=[other_account_number, iban_account_number])
party = Party(
bank_accounts_used=[bank_account])
payment = Payment(
kind='payable', party=party,
sepa_payable_bank_account_number=None)
self.assertEqual(id(payment.sepa_bank_account_number),
id(iban_account_number))
payment.sepa_payable_bank_account_number = iban_account_number
party.bank_accounts_used = []
self.assertEqual(
payment.sepa_bank_account_number,
iban_account_number)
@with_transaction()
def test_payment_sequence_type(self):
'Test payment sequence type'
pool = Pool()
Date = pool.get('ir.date')
Payment = pool.get('account.payment')
ProcessPayment = pool.get('account.payment.process', type='wizard')
environment = setup_environment()
company = environment['company']
bank = environment['bank']
customer = environment['customer']
with set_company(company):
company_account, customer_account = setup_accounts(
bank, company, customer)
mandate = setup_mandate(company, customer, customer_account)
journal = setup_journal('pain.008.001.02', 'receivable',
company, company_account)
self.assertFalse(mandate.has_payments)
self.assertEqual(mandate.sequence_type, 'FRST')
mandate.sequence_type_rcur = True
self.assertEqual(mandate.sequence_type, 'RCUR')
mandate.sequence_type_rcur = False
payment, = Payment.create([{
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('1000.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
}])
session_id, _, _ = ProcessPayment.create()
process_payment = ProcessPayment(session_id)
with Transaction().set_context(
active_model=Payment.__name__, active_ids=[payment.id]):
_, data = process_payment.do_process(None)
self.assertEqual(payment.sepa_mandate_sequence_type, 'FRST')
self.assertTrue(payment.sepa_mandate.has_payments)
payments = Payment.create([{
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('2000.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
}, {
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('3000.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
},
])
session_id, _, _ = ProcessPayment.create()
process_payment = ProcessPayment(session_id)
payment_ids = [p.id for p in payments]
with Transaction().set_context(
active_model=Payment.__name__, active_ids=payment_ids):
_, data = process_payment.do_process(None)
for payment in payments:
self.assertEqual(payment.sepa_mandate_sequence_type, 'RCUR')
self.assertTrue(payment.sepa_info_id)
self.assertIsNotNone(payment.group.sepa_id)
@with_transaction()
def test_payment_sequence_type_ordered_date(self):
"Test sequence type by ordered date"
pool = Pool()
Date = pool.get('ir.date')
Payment = pool.get('account.payment')
ProcessPayment = pool.get('account.payment.process', type='wizard')
environment = setup_environment()
company = environment['company']
bank = environment['bank']
customer = environment['customer']
with set_company(company):
company_account, customer_account = setup_accounts(
bank, company, customer)
mandate = setup_mandate(company, customer, customer_account)
journal = setup_journal('pain.008.001.02', 'receivable',
company, company_account)
self.assertEqual(mandate.sequence_type, 'FRST')
payments = Payment.create([{
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('1000.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today() + dt.timedelta(days=1),
}, {
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('1500.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
}, {
'company': company,
'party': customer,
'journal': journal,
'kind': 'receivable',
'amount': Decimal('2000.0'),
'state': 'submitted',
'reference': 'PAYMENT',
'date': Date.today(),
}])
session_id, _, _ = ProcessPayment.create()
process_payment = ProcessPayment(session_id)
payment_ids = [p.id for p in payments]
with Transaction().set_context(
active_model=Payment.__name__, active_ids=payment_ids):
_, data = process_payment.do_process(None)
self.assertEqual(
[p.sepa_mandate_sequence_type for p in payments],
['RCUR', 'FRST', 'RCUR'])
@with_transaction()
def handle_camt054(self, flavor):
'Handle camt.054'
pool = Pool()
Message = pool.get('account.payment.sepa.message')
message_file = os.path.join(os.path.dirname(__file__),
'%s.xml' % flavor)
with open(message_file, 'rb') as fp:
message = fp.read()
namespace = Message.get_namespace(message)
self.assertEqual(namespace,
'urn:iso:std:iso:20022:tech:xsd:%s' % flavor)
payment = Mock()
Payment = Mock()
Payment.search.return_value = [payment]
handler = CAMT054(BytesIO(message), Payment)
self.assertEqual(handler.msg_id, 'AAAASESS-FP-00001')
Payment.search.assert_called_with([
('sepa_end_to_end_id', '=', 'MUELL/FINP/RA12345'),
('kind', '=', 'payable'),
])
Payment.succeed.assert_called_with([payment])
payment.reset_mock()
Payment.reset_mock()
with patch.object(CAMT054, 'is_returned') as is_returned:
is_returned.return_value = True
handler = CAMT054(BytesIO(message), Payment)
Payment.save.assert_called_with([payment])
Payment.fail.assert_called_with([payment])
def test_camt054_001_01(self):
'Test camt.054.001.01 handling'
self.handle_camt054('camt.054.001.01')
def test_camt054_001_02(self):
'Test camt.054.001.02 handling'
self.handle_camt054('camt.054.001.02')
def test_camt054_001_03(self):
'Test camt.054.001.03 handling'
self.handle_camt054('camt.054.001.03')
def test_camt054_001_04(self):
'Test camt.054.001.04 handling'
self.handle_camt054('camt.054.001.04')
@with_transaction()
def test_sepa_mandate_report(self):
'Test sepa mandate report'
pool = Pool()
Report = pool.get('account.payment.sepa.mandate', type='report')
environment = setup_environment()
company = environment['company']
bank = environment['bank']
customer = environment['customer']
with set_company(company):
company_account, customer_account = setup_accounts(
bank, company, customer)
mandate = setup_mandate(company, customer, customer_account)
oext, content, _, _ = Report.execute([mandate.id], {})
self.assertEqual(oext, 'odt')
self.assertTrue(content)
del ModuleTestCase