BUH_bot/tests/discount_export_test.py
Игорь Надеин 2c866e2ada
Some checks failed
Build and Publish Docker Image / build-and-push (push) Has been cancelled
Fix discounted positions in 1C exports
2026-09-16 14:43:12 +03:00

135 lines
5.3 KiB
Python

"""Regression checks for discounted MoySklad positions in 1C exports."""
import sys
import types
import unittest
from decimal import Decimal
from unittest.mock import patch
from lxml import etree
with patch.dict(
sys.modules,
{"config": types.SimpleNamespace(
settings=types.SimpleNamespace(include_accounting_accounts=False)
)},
):
from modules.xml_generator import XML1CGenerator
class DiscountExportTests(unittest.TestCase):
def setUp(self):
self.generator = XML1CGenerator()
@staticmethod
def order_data(positions, sum_kopecks):
return {
"order": {
"id": "example",
"name": "example",
"moment": "2026-09-15 09:00:00",
"sum": sum_kopecks,
},
"organization": {},
"counterparty": {},
"positions": positions,
}
@staticmethod
def values(xml):
root = etree.fromstring(xml.encode("utf-8"))
document = root.xpath('//*[local-name()="Документ.РеализацияТоваровУслуг"]')[0]
header = document.xpath('./*[local-name()="Сумма"]/text()')[0]
rows = document.xpath(
'./*[local-name()="Товары" or local-name()="Услуги"]'
'/*[local-name()="Строка"]'
)
return header, [
{
name: (row.xpath('./*[local-name()=$name]/text()', name=name) or [None])[0]
for name in (
"Цена", "Сумма", "СуммаНДС", "ВидСкидки", "ПроцентСкидки",
"СуммаСкидки", "СуммаСУчетомСкидки",
)
}
for row in rows
]
def test_discount_applies_to_goods_and_services_in_both_export_modes(self):
data = self.order_data([
{
"quantity": 2, "price": 10000, "discount": 10, "vat": 20,
"assortment": {"name": "Product", "meta": {"type": "product"}},
},
{
"quantity": 1, "price": 10000, "discount": 10, "vat": 20,
"assortment": {"name": "Service", "meta": {"type": "service"}},
},
], 27000)
for xml in (
self.generator.generate_realization(data),
self.generator.generate_batch_realization([data]),
):
with self.subTest(export=xml[:50]):
header, rows = self.values(xml)
self.assertEqual(header, "270")
self.assertEqual(
[(r["Цена"], r["Сумма"], r["СуммаНДС"]) for r in rows],
[("100.00", "200.00", "30.00"),
("90.00", "90.00", "15.00")],
)
self.assertEqual(rows[0]["ВидСкидки"], "НаТовар")
self.assertEqual(rows[0]["ПроцентСкидки"], "10.00")
self.assertEqual(rows[0]["СуммаСкидки"], "20.00")
self.assertEqual(rows[0]["СуммаСУчетомСкидки"], "180.00")
self.assertEqual(
Decimal(rows[0]["СуммаСУчетомСкидки"]) + Decimal(rows[1]["Сумма"]),
Decimal(header),
)
def test_fractional_discount_preserves_kopecks(self):
data = self.order_data([{
"quantity": 2, "price": 10001, "discount": 12.5, "vat": 20,
"assortment": {"name": "Product", "meta": {"type": "product"}},
}], 17502)
header, rows = self.values(self.generator.generate_realization(data))
self.assertEqual(header, "175.02")
self.assertEqual(rows[0]["Цена"], "100.01")
self.assertEqual(rows[0]["Сумма"], "200.02")
self.assertEqual(rows[0]["ПроцентСкидки"], "12.50")
self.assertEqual(rows[0]["СуммаСкидки"], "25.00")
self.assertEqual(rows[0]["СуммаСУчетомСкидки"], "175.02")
self.assertEqual(rows[0]["СуммаНДС"], "29.17")
def test_position_without_discount_keeps_original_price(self):
data = self.order_data([{
"quantity": 1, "price": 10001, "vat": 20,
"assortment": {"name": "Product", "meta": {"type": "product"}},
}], 10001)
header, rows = self.values(self.generator.generate_realization(data))
self.assertEqual(header, "100.01")
self.assertEqual(rows[0]["Цена"], "100.01")
self.assertEqual(rows[0]["Сумма"], "100.01")
self.assertIsNone(rows[0]["ПроцентСкидки"])
def test_service_discount_uses_net_amount_with_two_decimal_price(self):
data = self.order_data([{
"quantity": 2, "price": 10001, "discount": 12.5, "vat": 20,
"assortment": {"name": "Service", "meta": {"type": "service"}},
}], 17502)
header, rows = self.values(self.generator.generate_realization(data))
self.assertEqual(header, "175.02")
self.assertEqual(rows[0]["Цена"], "87.51")
self.assertEqual(rows[0]["Сумма"], "175.02")
self.assertEqual(rows[0]["СуммаНДС"], "29.17")
self.assertIsNone(rows[0]["ПроцентСкидки"])
if __name__ == "__main__":
unittest.main()