117 lines
5.6 KiB
Python
117 lines
5.6 KiB
Python
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)
|