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,108 @@
# -*- coding: utf-8 -*-
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestNdflReport(TransactionCase):
"""6-НДФЛ (NO_NDFL6.2 5.05): раздел 1 (6 сроков), раздел 2, XML."""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company = cls.env.company
cls.company.write({
'rsbu_tax_office_code': '5050',
'rsbu_oktmo': '46788000',
'rsbu_kbk_ndfl': '18210102010011000110',
})
cls.company.partner_id.write({'inn': '5050164162', 'kpp': '505001001'})
user = cls.env['res.users'].search([], limit=1)
cls.company.rsbu_signatory_id = user.id
user.partner_id.write({'name': 'Коробков Сергей Васильевич'})
cls.emp1 = cls.env['hr.employee'].create({
'name': 'Петров Пётр', 'company_id': cls.company.id})
cls.emp2 = cls.env['hr.employee'].create({
'name': 'Сидоров Иван', 'company_id': cls.company.id})
cls.Summary = cls.env['rsbu.ndfl.summary']
cls.Report = cls.env['rsbu.ndfl.report']
def _summary(self, emp, month, accrued, withheld, wh_date, rate=13):
"""Создать помесячный итог НДФЛ с датой удержания."""
from odoo import fields
d = fields.Date.to_date(wh_date)
term = self.Summary._deadline_term(d)
return self.Summary.create({
'company_id': self.company.id, 'employee_id': emp.id,
'year': 2026, 'month': month, 'rate': rate,
'accrued': accrued, 'deduction': 0.0, 'base': accrued,
'computed_tax': withheld, 'withheld_tax': withheld,
'withholding_date': d, 'withholding_month': d.month,
'deadline_term': term,
})
def test_01_deadline_term_mapping(self):
"""Срок квартала по дате удержания: 1-22 → нечёт, 23- → чёт."""
from odoo import fields
# Апрель (2-й квартал, 1-й месяц квартала).
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-04-10')), 1) # 1-22 апр → срок 1
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-04-25')), 2) # 23-30 апр → срок 2
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-05-15')), 3) # 1-22 май → срок 3
self.assertEqual(self.Summary._deadline_term(
fields.Date.to_date('2026-06-28')), 6) # 23-30 июн → срок 6
def test_02_section1_six_terms(self):
"""Раздел 1: удержанный НДФЛ разложен по 6 срокам квартала."""
# Полугодие (квартал 2): удержания в апреле/мае/июне по полупериодам.
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10') # срок1
self._summary(self.emp1, 4, 50000.0, 6500.0, '2026-04-25') # срок2
self._summary(self.emp1, 5, 100000.0, 13000.0, '2026-05-12') # срок3
self._summary(self.emp1, 6, 100000.0, 13000.0, '2026-06-20') # срок5
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
self.assertAlmostEqual(rep.term1, 13000.0, 2)
self.assertAlmostEqual(rep.term2, 6500.0, 2)
self.assertAlmostEqual(rep.term3, 13000.0, 2)
self.assertAlmostEqual(rep.term4, 0.0, 2)
self.assertAlmostEqual(rep.term5, 13000.0, 2)
self.assertAlmostEqual(rep.term6, 0.0, 2)
self.assertAlmostEqual(rep.withheld_total, 45500.0, 2)
def test_03_section2_by_rate(self):
"""Раздел 2: строка по ставке — число лиц, доход, удержано."""
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10')
self._summary(self.emp2, 4, 80000.0, 10400.0, '2026-04-10')
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
line = rep.line_ids.filtered(lambda l: l.rate == 13)
self.assertTrue(line)
self.assertEqual(line.persons_count, 2)
self.assertAlmostEqual(line.accrued, 180000.0, 2)
self.assertAlmostEqual(line.withheld_tax, 23400.0, 2)
def test_04_xml_valid_against_xsd(self):
"""Сгенерированный 6-НДФЛ проходит XSD 5.05."""
self._summary(self.emp1, 4, 100000.0, 13000.0, '2026-04-10')
self._summary(self.emp1, 5, 100000.0, 13000.0, '2026-05-12')
self._summary(self.emp1, 6, 100000.0, 13000.0, '2026-06-20')
rep = self.Report.create({
'year': 2026, 'company_id': self.company.id, 'quarter': '2'})
rep.compute_from_summary()
fname = rep._ndfl_xml_filename()
xml_bytes = rep._ndfl_build_xml(filename=fname)
ok, errors = rep._ndfl_validate_xml(xml_bytes)
self.assertTrue(ok, '6-НДФЛ не прошёл XSD: %s' % '; '.join(errors[:8]))
self.assertIn(b'windows-1251', xml_bytes[:60])
self.assertTrue(fname.startswith('NO_NDFL6.2_'))
from lxml import etree
root = etree.fromstring(xml_bytes)
self.assertEqual(root.get('ИдФайл'), fname)
doc = root.find('Документ')
self.assertEqual(doc.get('КНД'), '1151100')
self.assertEqual(doc.get('Период'), '31')
obyaz = doc.find('НДФЛ6.2/ОбязНА')
self.assertEqual(obyaz.get('КБК'), '18210102010011000110')