Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC

This commit is contained in:
CI Publish Bot
2026-05-31 21:19:21 +00:00
commit aa4214c195
1213 changed files with 183945 additions and 0 deletions

View File

@ -0,0 +1 @@
from . import test_parser

View 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()