Files
public/rsbu_fns_reporting/tests/test_nds_declaration.py

117 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)