Чистый модуль statements/number_format.py (display_amount) и публичная numbers.quantize_money. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
"""Отображаемое значение числа по числовому формату ячейки (#42, R-2…R-6): примеры из реальных выгрузок 1С."""
|
||
|
||
from decimal import Decimal
|
||
|
||
import pytest
|
||
|
||
from src.parser.statements.number_format import display_amount
|
||
|
||
|
||
def shown(raw: str, fmt: str) -> tuple[str, Decimal | None]:
|
||
result = display_amount(Decimal(raw), fmt)
|
||
return result.kind, result.value
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "fmt", "expected"),
|
||
[
|
||
("10741981390", "#,##0,", "10741981.00"),
|
||
("586260.26", "0,", "586.00"),
|
||
("898852.21795", "#,##0", "898852.00"),
|
||
("898852.5", "#,##0", "898853.00"),
|
||
("1234.5", "#,##0.00", "1234.50"),
|
||
("1234.567", "#,##0.00", "1234.57"),
|
||
("1500", "#,##0,", "2.00"),
|
||
("-1500", "#,##0,", "-2.00"),
|
||
("1499.99", "#,##0,", "1.00"),
|
||
("-1499.99", "#,##0,", "-1.00"),
|
||
("586260.26", "0.00,", "586.26"),
|
||
("5000000", "#,##0,,", "5.00"),
|
||
("1500", '#,##0,;(#,##0,);"-"', "2.00"),
|
||
("-1500", '#,##0,;(#,##0,);"-"', "-2.00"),
|
||
("1500", "#,##0;(#,##0)", "1500.00"),
|
||
("-1500", "#,##0;(#,##0)", "-1500.00"),
|
||
("-1500", "#,##0;#,##0", "-1500.00"),
|
||
("1500", "[Red]#,##0", "1500.00"),
|
||
("1500", "[$-419]#,##0", "1500.00"),
|
||
("1500", "#,##0_);[Red](#,##0)", "1500.00"),
|
||
("-1500", "#,##0_);[Red](#,##0)", "-1500.00"),
|
||
("1500.126", "0.000", "1500.13"),
|
||
("1500", '0 "тыс."', "1500.00"),
|
||
("-872206880.07", '[=-872206880.07]"(872 207)";#,##0,', "-872207.00"),
|
||
("-872206881", '[=-872206880.07]"(872 207)";#,##0,', "-872207.00"),
|
||
("-646532", '[=-646532]"(647)"', "-647.00"),
|
||
("12345678", '[=-872206880.07]"(872 207)";#,##0,', "12346.00"),
|
||
],
|
||
)
|
||
def test_value(raw: str, fmt: str, expected: str) -> None:
|
||
kind, value = shown(raw, fmt)
|
||
assert kind == "value"
|
||
assert value is not None
|
||
assert str(value) == expected
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "fmt"),
|
||
[
|
||
("0", '[=0]"-"'),
|
||
("0", '[=0]""'),
|
||
("0", '[=0]"-";General'),
|
||
("-0.79", '[=-0.79]"-"'),
|
||
("0", '#,##0,;(#,##0,);"-"'),
|
||
],
|
||
)
|
||
def test_no_value(raw: str, fmt: str) -> None:
|
||
assert shown(raw, fmt) == ("value", None)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "fmt"),
|
||
[
|
||
("5", '[=0]"-"'),
|
||
("5", "0%"),
|
||
("5", "0.00E+00"),
|
||
("5", "# ?/?"),
|
||
("5", "@"),
|
||
("5", "dd.mm.yyyy"),
|
||
("5", "h:mm AM/PM"),
|
||
("0", '[=0]"н/д"'),
|
||
("5", '"н/д"'),
|
||
("5", "0 руб"),
|
||
],
|
||
)
|
||
def test_unparsed(raw: str, fmt: str) -> None:
|
||
assert shown(raw, fmt) == ("unparsed", None)
|
||
|
||
|
||
@pytest.mark.parametrize("fmt", ["General", "general", "", " "])
|
||
def test_raw(fmt: str) -> None:
|
||
assert shown("5.5", fmt) == ("raw", None)
|
||
|
||
|
||
def test_general_section_after_condition() -> None:
|
||
assert shown("5.5", '[=0]"-";General') == ("raw", None)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "fmt", "expected"),
|
||
[
|
||
("5", "[<10]0;[>=10]0,", "5.00"),
|
||
("20000", "[<10]0;[>=10]0,", "20.00"),
|
||
("5", "[<>5]0;0.0", "5.0"),
|
||
],
|
||
)
|
||
def test_comparison_conditions(raw: str, fmt: str, expected: str) -> None:
|
||
kind, value = shown(raw, fmt)
|
||
assert kind == "value"
|
||
assert value is not None
|
||
assert Decimal(expected) == value
|
||
|
||
|
||
def test_fourth_section_ignored() -> None:
|
||
assert shown("1500", "#,##0,;(#,##0,);0;@") == ("value", Decimal("2.00"))
|
||
|
||
|
||
def test_semicolon_in_quotes_does_not_split() -> None:
|
||
assert shown("0", '[=0]"a;b"') == ("unparsed", None)
|
||
|
||
|
||
def test_escaped_char_is_decoration() -> None:
|
||
assert shown("1500", "#,##0\\ \\р") == ("value", Decimal("1500.00"))
|
||
|
||
|
||
def test_huge_value_out_of_range_is_unparsed() -> None:
|
||
assert shown("1" + "0" * 40, "#,##0") == ("unparsed", None)
|
||
|
||
|
||
def test_max_numeric_18_2_range() -> None:
|
||
assert shown("9999999999999999", "0") == ("value", Decimal("9999999999999999.00"))
|
||
assert shown("99999999999999999", "0") == ("unparsed", None)
|
||
|
||
|
||
def test_scaled_huge_value_fits() -> None:
|
||
assert shown("99999999999999999000", "0,") == ("unparsed", None)
|
||
assert shown("9999999999999999000", "0,") == ("value", Decimal("9999999999999999.00"))
|