synthgen: формат #,##0.00 у всех сумм ОСВ, оформление шапки колонки ИНН (#38)

Сальдо брало формат ячейки строки-образца, где сумма пустая, — «Общий»; шапка ИНН была без заливки и рамок.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-11-13 01:19:02 +04:00
parent 0abe99bf77
commit 25b1e9fe14
2 changed files with 26 additions and 1 deletions

View File

@ -32,6 +32,7 @@ from synthgen.periods import Period
ZERO = Decimal(0) ZERO = Decimal(0)
STYLE_PATH = Path(__file__).with_name("templates") / "osv_style.json" STYLE_PATH = Path(__file__).with_name("templates") / "osv_style.json"
SHEET = "Лист_1" SHEET = "Лист_1"
AMOUNT_FORMAT = "#,##0.00"
Six = tuple[Decimal, Decimal, Decimal, Decimal, Decimal, Decimal] Six = tuple[Decimal, Decimal, Decimal, Decimal, Decimal, Decimal]
@ -304,6 +305,10 @@ def write(path: Path, member: Member, account: str, period: Period, data: list[O
for c, value in row["values"].items(): for c, value in row["values"].items():
ws.cell(i, col(int(c)), placeholders.get(value, value)) ws.cell(i, col(int(c)), placeholders.get(value, value))
if with_inn: if with_inn:
# шапка колонки ИНН оформлена как соседняя шапка «Сальдо на начало периода» (заливка, рамки, Arial 9)
first_sum = tpl["sum_spans"][0][0]
for i in (9, 10, 11):
_apply(ws.cell(i, inn_col), st.get(f"header{i}", first_sum))
ws.cell(10, inn_col, "ИНН") ws.cell(10, inn_col, "ИНН")
for r1, c1, r2, c2 in tpl["header_merges"]: for r1, c1, r2, c2 in tpl["header_merges"]:
ws.merge_cells(start_row=r1, start_column=col(c1), end_row=r2, end_column=col(c2)) ws.merge_cells(start_row=r1, start_column=col(c1), end_row=r2, end_column=col(c2))
@ -315,7 +320,8 @@ def write(path: Path, member: Member, account: str, period: Period, data: list[O
def put_sums(r: int, six: Six) -> None: def put_sums(r: int, six: Six) -> None:
for (a, b), value in zip(spans, six, strict=True): for (a, b), value in zip(spans, six, strict=True):
if value != 0: if value != 0:
ws.cell(r, a, float(value)) # формат задаём явно: в строке-образце сальдо бывает пустым, и его ячейка несёт «Общий» формат
ws.cell(r, a, float(value)).number_format = AMOUNT_FORMAT
if b > a: if b > a:
ws.merge_cells(start_row=r, start_column=a, end_row=r, end_column=b) ws.merge_cells(start_row=r, start_column=a, end_row=r, end_column=b)

View File

@ -63,6 +63,25 @@ def test_style_matches_sample(tmp_path, osv_world):
assert number.number_format == "#,##0.00" assert number.number_format == "#,##0.00"
@pytest.mark.parametrize("inn", [False, True])
def test_every_amount_has_number_format(tmp_path, osv_world, inn):
"""Все суммы (сальдо и обороты, строки счёта, субсчёта, контрагента, «Итого») — `#,##0.00`, как в 1С."""
for account in ("60", "62", "76"):
(tmp_path / account).mkdir()
ws, *_ = _write(tmp_path / account, osv_world, account, inn=inn)
bad = [c.coordinate for row in ws.iter_rows(min_row=12) for c in row
if isinstance(c.value, (int, float)) and c.number_format != "#,##0.00"]
assert not bad, (account, bad[:10])
def test_inn_header_styled_like_header(tmp_path, osv_world):
ws, *_ = _write(tmp_path, osv_world, "60", inn=True)
for r in (9, 10): # E10:E11 объединена — вид задаёт верхняя ячейка
assert ws.cell(r, 5).fill.fgColor.rgb == ws.cell(r, 1).fill.fgColor.rgb == "FFF4ECC5", r
assert ws.cell(r, 5).border.left.style and ws.cell(r, 5).border.top.style, r
assert ws["E10"].font.name == "Arial" and ws["E10"].font.sz == 9
def test_merges_follow_sample(tmp_path, osv_world): def test_merges_follow_sample(tmp_path, osv_world):
ws, *_ = _write(tmp_path, osv_world, "62", inn=False) ws, *_ = _write(tmp_path, osv_world, "62", inn=False)
got = {str(m) for m in ws.merged_cells.ranges if m.min_row in (9, 10, 11)} got = {str(m) for m in ws.merged_cells.ranges if m.min_row in (9, 10, 11)}