Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC

This commit is contained in:
CI Publish Bot
2026-11-01 22:54:30 +00:00
commit 26250aa580
2492 changed files with 664277 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestNdsDeclaration(TransactionCase):
"""Декларация НДС (комплект 3 файла): расчёт из книг, XML по XSD 5.12."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.company
cls.company.write({
'rsbu_vat_mode': 'vat_5',
'rsbu_tax_office_code': '7704',
'rsbu_oktmo': '45301000',
})
cls.company.partner_id.write({'inn': '7704567896', 'kpp': '770401001'})
cls.user = cls.env['res.users'].search([], limit=1)
cls.company.rsbu_signatory_id = cls.user.id
cls.Decl = cls.env['rsbu.nds.declaration']
cls.BookLine = cls.env['rsbu.vat.book.line']
cls.buyer = cls.env['res.partner'].create({
'name': 'ООО Покупатель', 'inn': '7730179829',
'kpp': '773001001'})
def _sales_line(self, base, vat):
return self.BookLine.create({
'company_id': self.company.id, 'book': 'sales',
'date': '2026-04-15', 'base_amount': base, 'vat_amount': vat,
'partner_id': self.buyer.id})
def _purchase_line(self, vat):
return self.BookLine.create({
'company_id': self.company.id, 'book': 'purchases',
'date': '2026-05-10', 'vat_amount': vat,
'partner_id': self.company.partner_id.id})
def _declaration(self):
return self.Decl.create({
'company_id': self.company.id,
'date_from': '2026-04-01', 'date_to': '2026-06-30'})
def _compute_no_recompute(self, dec):
"""Расчёт без пересбора книг (иначе тестовые строки затрутся).
recompute_period пересобирает книги из проводок; в тесте их нет,
поэтому вызываем внутренний расчёт по существующим строкам напрямую.
"""
# Разложение и суммы считаем по уже созданным строкам книги.
real_vat, adv_vat = dec._split_sales_vat()
fraction = 5.0 / 105.0
inv = 1.0 / fraction - 1.0
dec.vat_tax_realization = real_vat
dec.vat_base_realization = round(real_vat * inv, 2)
dec.vat_tax_advance = adv_vat
dec.vat_base_advance = round(adv_vat / fraction, 2) if adv_vat else 0.0
dec.vat_charged_total = round(real_vat + adv_vat, 2)
ded = sum(self.BookLine.search([
('company_id', '=', self.company.id), ('book', '=', 'purchases'),
('date', '>=', dec.date_from), ('date', '<=', dec.date_to),
]).mapped('vat_amount'))
dec.vat_deduction = round(ded, 2)
dec.vat_payable = round(dec.vat_charged_total - ded, 2)
dec.oktmo = self.company.rsbu_oktmo
dec.kbk = self.company.rsbu_kbk_nds
dec.state = 'computed'
def test_01_totals(self):
"""Начислено = книга продаж, вычет = книга покупок, к уплате = разница."""
self._sales_line(100000.0, 5000.0) # реализация 5%
self._purchase_line(2000.0) # вычет
dec = self._declaration()
self._compute_no_recompute(dec)
self.assertAlmostEqual(dec.vat_tax_realization, 5000.0, 2)
self.assertAlmostEqual(dec.vat_base_realization, 100000.0, 2)
self.assertAlmostEqual(dec.vat_charged_total, 5000.0, 2)
self.assertAlmostEqual(dec.vat_deduction, 2000.0, 2)
self.assertAlmostEqual(dec.vat_payable, 3000.0, 2)
def test_02_three_files_valid_against_xsd(self):
"""Комплект из 3 файлов валиден против XSD 5.12."""
self._sales_line(100000.0, 5000.0)
self._purchase_line(2000.0)
dec = self._declaration()
self._compute_no_recompute(dec)
files = dec._nds_build_all()
self.assertEqual(len(files), 3)
for (fname, xml), kind in zip(files, ['root', 'purchases', 'sales']):
ok, errors = dec._nds_validate_xml(xml, kind)
self.assertTrue(ok, '%s не прошёл XSD: %s'
% (kind, '; '.join(errors[:4])))
self.assertIn(b'windows-1251', xml[:60])
def test_03_root_references_sections(self):
"""Корневая декларация ссылается на файлы разделов 8 и 9."""
self._sales_line(100000.0, 5000.0)
self._purchase_line(2000.0)
dec = self._declaration()
self._compute_no_recompute(dec)
files = dec._nds_build_all()
name_root, name_pokup, name_prod = (f[0] for f in files)
root_xml = files[0][1].decode('cp1251')
self.assertIn(name_pokup, root_xml) # КнигаПокуп/НаимКнПок
self.assertIn(name_prod, root_xml) # КнигаПрод/НаимКнПрод
self.assertTrue(name_pokup.startswith('NO_NDS.8_'))
self.assertTrue(name_prod.startswith('NO_NDS.9_'))
def test_04_advance_split(self):
"""Начисленный НДС делится на реализацию и авансы (по 76.АВ)."""
# Реализация (без 76.АВ) и аванс (эмулируем через отдельную строку).
self._sales_line(100000.0, 5000.0)
dec = self._declaration()
real, adv = dec._split_sales_vat()
# Без авансовых проводок весь НДС — реализация.
self.assertAlmostEqual(real, 5000.0, 2)
self.assertAlmostEqual(adv, 0.0, 2)