Public release from ruodoo-project: 19.0 - 2026-05-10 21:19:01 UTC
This commit is contained in:
293
account_bank_statement_1c_import/tests/test_parser.py
Normal file
293
account_bank_statement_1c_import/tests/test_parser.py
Normal file
@ -0,0 +1,293 @@
|
||||
"""
|
||||
Tests for Parser_1C (account_bank_statement_1c_import).
|
||||
|
||||
Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.9
|
||||
"""
|
||||
import base64
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
COMPANY_INN = "7700000001"
|
||||
COMPANY_KPP = "770001001"
|
||||
|
||||
PARTNER_INN = "7700000002"
|
||||
PARTNER_KPP = "770001002"
|
||||
|
||||
VALID_TXT_TEMPLATE = """\
|
||||
1CClientBankExchange
|
||||
ВерсияФормата=1.03
|
||||
Кодировка=Windows
|
||||
Отправитель=Бухгалтерия предприятия
|
||||
ДатаСоздания=01.01.2024
|
||||
ВремяСоздания=12:00:00
|
||||
ДатаНачала=01.01.2024
|
||||
ДатаКонца=31.01.2024
|
||||
РасчСчет=40702810000000000001
|
||||
СекцияРасчСчет
|
||||
ДатаНачала=01.01.2024
|
||||
ДатаКонца=31.01.2024
|
||||
РасчСчет=40702810000000000001
|
||||
НачальныйОстаток=10000.00
|
||||
ВсегоПоступило=5000.00
|
||||
ВсегоСписано=3000.00
|
||||
КонечныйОстаток=12000.00
|
||||
КонецРасчСчет
|
||||
{transactions}КонецФайла
|
||||
"""
|
||||
|
||||
TRANSACTION_PAYER = """\
|
||||
СекцияДокумент=Платежное поручение
|
||||
Номер=1
|
||||
Дата=15.01.2024
|
||||
Сумма=1000.00
|
||||
ПлательщикСчет=40702810000000000001
|
||||
ДатаСписано=15.01.2024
|
||||
ПлательщикИНН={company_inn}
|
||||
ПлательщикКПП={company_kpp}
|
||||
Плательщик=ООО Наша Компания
|
||||
Плательщик1=ООО Наша Компания
|
||||
ПлательщикРасчСчет=40702810000000000001
|
||||
ПлательщикБанк1=Банк Плательщика
|
||||
ПлательщикБИК=044525001
|
||||
ПлательщикКорсчет=30101810400000000001
|
||||
ПолучательСчет=40702810000000000002
|
||||
ПолучательИНН={partner_inn}
|
||||
ПолучательКПП={partner_kpp}
|
||||
Получатель=ООО Контрагент
|
||||
Получатель1=ООО Контрагент
|
||||
ПолучательРасчСчет=40702810000000000002
|
||||
ПолучательБанк1=Банк Получателя
|
||||
ПолучательБИК=044525002
|
||||
ПолучательКорсчет=30101810400000000002
|
||||
НазначениеПлатежа=Оплата по договору №1
|
||||
КонецДокумента
|
||||
"""
|
||||
|
||||
TRANSACTION_RECIPIENT = """\
|
||||
СекцияДокумент=Платежное поручение
|
||||
Номер=2
|
||||
Дата=16.01.2024
|
||||
Сумма=2000.00
|
||||
ПлательщикСчет=40702810000000000002
|
||||
ДатаПоступило=16.01.2024
|
||||
ПлательщикИНН={partner_inn}
|
||||
ПлательщикКПП={partner_kpp}
|
||||
Плательщик=ООО Контрагент
|
||||
Плательщик1=ООО Контрагент
|
||||
ПлательщикРасчСчет=40702810000000000002
|
||||
ПлательщикБанк1=Банк Плательщика
|
||||
ПлательщикБИК=044525002
|
||||
ПлательщикКорсчет=30101810400000000002
|
||||
ПолучательСчет=40702810000000000001
|
||||
ПолучательИНН={company_inn}
|
||||
ПолучательКПП={company_kpp}
|
||||
Получатель=ООО Наша Компания
|
||||
Получатель1=ООО Наша Компания
|
||||
ПолучательРасчСчет=40702810000000000001
|
||||
ПолучательБанк1=Банк Получателя
|
||||
ПолучательБИК=044525001
|
||||
ПолучательКорсчет=30101810400000000001
|
||||
НазначениеПлатежа=Поступление по договору №2
|
||||
КонецДокумента
|
||||
"""
|
||||
|
||||
TRANSACTION_UNKNOWN_INN = """\
|
||||
СекцияДокумент=Платежное поручение
|
||||
Номер=3
|
||||
Дата=17.01.2024
|
||||
Сумма=500.00
|
||||
ПлательщикСчет=40702810000000000003
|
||||
ДатаСписано=17.01.2024
|
||||
ПлательщикИНН=9999999999
|
||||
ПлательщикКПП=999999999
|
||||
Плательщик=ООО Чужая Компания
|
||||
Плательщик1=ООО Чужая Компания
|
||||
ПлательщикРасчСчет=40702810000000000003
|
||||
ПлательщикБанк1=Чужой Банк
|
||||
ПлательщикБИК=044525003
|
||||
ПлательщикКорсчет=30101810400000000003
|
||||
ПолучательСчет=40702810000000000004
|
||||
ПолучательИНН=8888888888
|
||||
ПолучательКПП=888888888
|
||||
Получатель=ООО Другая Компания
|
||||
Получатель1=ООО Другая Компания
|
||||
ПолучательРасчСчет=40702810000000000004
|
||||
ПолучательБанк1=Другой Банк
|
||||
ПолучательБИК=044525004
|
||||
ПолучательКорсчет=30101810400000000004
|
||||
НазначениеПлатежа=Транзакция без нашей компании
|
||||
КонецДокумента
|
||||
"""
|
||||
|
||||
|
||||
def _encode(text):
|
||||
"""Encode text to base64 as cp1251 (as the wizard expects)."""
|
||||
return base64.b64encode(text.encode('cp1251'))
|
||||
|
||||
|
||||
def _make_txt(transactions=""):
|
||||
return VALID_TXT_TEMPLATE.format(transactions=transactions)
|
||||
|
||||
|
||||
def _payer_tx():
|
||||
return TRANSACTION_PAYER.format(
|
||||
company_inn=COMPANY_INN, company_kpp=COMPANY_KPP,
|
||||
partner_inn=PARTNER_INN, partner_kpp=PARTNER_KPP,
|
||||
)
|
||||
|
||||
|
||||
def _recipient_tx():
|
||||
return TRANSACTION_RECIPIENT.format(
|
||||
company_inn=COMPANY_INN, company_kpp=COMPANY_KPP,
|
||||
partner_inn=PARTNER_INN, partner_kpp=PARTNER_KPP,
|
||||
)
|
||||
|
||||
|
||||
def _unknown_inn_tx():
|
||||
return TRANSACTION_UNKNOWN_INN
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures / setUp helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class _Base(TransactionCase):
|
||||
"""Common setUp: company VAT, bank journal with use_in_bank_statement."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Set company VAT so the parser can match INN
|
||||
self.env.company.write({'vat': COMPANY_INN})
|
||||
|
||||
# Create (or find) a bank journal with use_in_bank_statement=True
|
||||
self.journal = self.env['account.journal'].search(
|
||||
[('type', '=', 'bank'), ('use_in_bank_statement', '=', True)],
|
||||
limit=1,
|
||||
)
|
||||
if not self.journal:
|
||||
self.journal = self.env['account.journal'].create({
|
||||
'name': 'Test Bank Journal',
|
||||
'type': 'bank',
|
||||
'code': 'TBNK',
|
||||
'use_in_bank_statement': True,
|
||||
})
|
||||
|
||||
def _wizard(self, file_content_bytes, file_name='statement.txt'):
|
||||
"""Create an InvoiceImportWizard record."""
|
||||
return self.env['invoice.import.wizard'].create({
|
||||
'file': base64.b64encode(file_content_bytes),
|
||||
'file_name': file_name,
|
||||
})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestParser1CBasic
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestParser1CBasic(_Base):
|
||||
"""Validates: Requirements 2.1, 2.2, 2.3"""
|
||||
|
||||
def test_valid_txt_parses_without_exception(self):
|
||||
"""Req 2.1 — correct TXT file is parsed without exceptions."""
|
||||
content = _make_txt().encode('cp1251')
|
||||
wizard = self._wizard(content, 'statement.txt')
|
||||
# action_import_invoice should not raise
|
||||
wizard.action_import_invoice()
|
||||
|
||||
def test_wrong_extension_raises_user_error(self):
|
||||
"""Req 2.2 — non-.txt extension raises UserError."""
|
||||
content = _make_txt().encode('cp1251')
|
||||
wizard = self._wizard(content, 'statement.csv')
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_import_invoice()
|
||||
|
||||
def test_no_file_raises_user_error(self):
|
||||
"""Req 2.3 — missing file raises UserError."""
|
||||
wizard = self.env['invoice.import.wizard'].create({
|
||||
'file': False,
|
||||
'file_name': 'statement.txt',
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_import_invoice()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestParser1CLines
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestParser1CLines(_Base):
|
||||
"""Validates: Requirements 2.4, 2.5, 2.6"""
|
||||
|
||||
def _import_and_get_lines(self, transactions_text):
|
||||
content = _make_txt(transactions_text).encode('cp1251')
|
||||
wizard = self._wizard(content, 'statement.txt')
|
||||
wizard.action_import_invoice()
|
||||
return self.env['account.bank.statement.line'].search([], order='id desc')
|
||||
|
||||
def test_company_as_payer_creates_negative_amount(self):
|
||||
"""Req 2.4 — company INN as payer → negative amount line."""
|
||||
lines = self._import_and_get_lines(_payer_tx())
|
||||
payer_lines = lines.filtered(lambda l: l.payment_ref == '1')
|
||||
self.assertTrue(payer_lines, "Expected a statement line with payment_ref='1'")
|
||||
self.assertLess(payer_lines[0].amount, 0, "Amount should be negative when company is payer")
|
||||
|
||||
def test_company_as_recipient_creates_positive_amount(self):
|
||||
"""Req 2.5 — company INN as recipient → positive amount line."""
|
||||
lines = self._import_and_get_lines(_recipient_tx())
|
||||
recipient_lines = lines.filtered(lambda l: l.payment_ref == '2')
|
||||
self.assertTrue(recipient_lines, "Expected a statement line with payment_ref='2'")
|
||||
self.assertGreater(recipient_lines[0].amount, 0, "Amount should be positive when company is recipient")
|
||||
|
||||
def test_duplicate_line_is_skipped(self):
|
||||
"""Req 2.6 — importing the same transaction twice does not create a duplicate."""
|
||||
tx = _payer_tx()
|
||||
content = _make_txt(tx).encode('cp1251')
|
||||
|
||||
# First import
|
||||
self._wizard(content, 'statement.txt').action_import_invoice()
|
||||
count_after_first = self.env['account.bank.statement.line'].search_count(
|
||||
[('payment_ref', '=', '1')]
|
||||
)
|
||||
|
||||
# Second import of the same file
|
||||
self._wizard(content, 'statement.txt').action_import_invoice()
|
||||
count_after_second = self.env['account.bank.statement.line'].search_count(
|
||||
[('payment_ref', '=', '1')]
|
||||
)
|
||||
|
||||
self.assertEqual(count_after_first, count_after_second,
|
||||
"Duplicate transaction should be skipped on second import")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestParser1CPartner
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestParser1CPartner(_Base):
|
||||
"""Validates: Requirements 2.7, 2.9"""
|
||||
|
||||
def test_unknown_inn_kpp_creates_new_partner(self):
|
||||
"""Req 2.7 — unknown INN/KPP → new partner of type 'company' is created."""
|
||||
# Ensure partner does not exist
|
||||
self.env['res.partner'].search([('vat', '=', PARTNER_INN)]).unlink()
|
||||
|
||||
content = _make_txt(_payer_tx()).encode('cp1251')
|
||||
self._wizard(content, 'statement.txt').action_import_invoice()
|
||||
|
||||
partner = self.env['res.partner'].search([('vat', '=', PARTNER_INN)], limit=1)
|
||||
self.assertTrue(partner, "A new partner should have been created")
|
||||
self.assertEqual(partner.company_type, 'company',
|
||||
"New partner should have company_type='company'")
|
||||
|
||||
def test_unknown_company_inn_in_transaction_raises_user_error(self):
|
||||
"""Req 2.9 — company INN not found as payer or recipient → UserError."""
|
||||
content = _make_txt(_unknown_inn_tx()).encode('cp1251')
|
||||
wizard = self._wizard(content, 'statement.txt')
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_import_invoice()
|
||||
Reference in New Issue
Block a user