diff --git a/synthgen/osv.py b/synthgen/osv.py index cc180f2..e1827ea 100644 --- a/synthgen/osv.py +++ b/synthgen/osv.py @@ -32,6 +32,7 @@ from synthgen.periods import Period ZERO = Decimal(0) STYLE_PATH = Path(__file__).with_name("templates") / "osv_style.json" SHEET = "Лист_1" +AMOUNT_FORMAT = "#,##0.00" 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(): ws.cell(i, col(int(c)), placeholders.get(value, value)) 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, "ИНН") 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)) @@ -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: for (a, b), value in zip(spans, six, strict=True): if value != 0: - ws.cell(r, a, float(value)) + # формат задаём явно: в строке-образце сальдо бывает пустым, и его ячейка несёт «Общий» формат + ws.cell(r, a, float(value)).number_format = AMOUNT_FORMAT if b > a: ws.merge_cells(start_row=r, start_column=a, end_row=r, end_column=b) diff --git a/synthgen/tests/test_osv.py b/synthgen/tests/test_osv.py index 222fbf9..7a15eef 100644 --- a/synthgen/tests/test_osv.py +++ b/synthgen/tests/test_osv.py @@ -63,6 +63,25 @@ def test_style_matches_sample(tmp_path, osv_world): 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): 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)}