Skip to content

Commit 277b4e5

Browse files
authored
fix(excel): keep exact Decimal in CSV/spreadsheet export (#216)
The query export coerced Decimal cells with float() before writing, so CSV — a text format that can carry full precision — lost it on high-precision values (e.g. a 28-digit conversion result rounded to 88.57142857142857). Keep the Decimal: csv.writer str()s it losslessly, and pyexcel accepts it for xlsx/ods (those cells are float64 by the format regardless, so no change there). test_to_csv_preserves_exact_decimal fails without the fix.
1 parent 9ffb67d commit 277b4e5

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

src/rustfava/util/excel.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ def _row_to_pyexcel(row: ResultRow, header: list[Column]) -> list[str]:
9696
continue
9797
type_ = column.datatype
9898
if type_ is Decimal:
99-
result.append(float(value))
99+
# Keep the exact Decimal: CSV writes str(Decimal) losslessly, and
100+
# pyexcel accepts it for xlsx/ods (those cells are float64 by the
101+
# format anyway). float() here would corrupt CSV precision.
102+
result.append(value)
100103
elif type_ is int:
101104
result.append(value)
102105
elif type_ in (set, frozenset) or isinstance(value, (set, frozenset)):

tests/test_util_excel.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import annotations
22

3+
from decimal import Decimal
34
from typing import Any
45
from typing import TYPE_CHECKING
56

67
import pytest
78

9+
from rustfava.rustledger.query import ColumnDescription
810
from rustfava.rustledger.query import connect
911
from rustfava.util import excel
1012

@@ -42,6 +44,18 @@ def test_to_csv(example_ledger: RustfavaLedger) -> None:
4244
assert excel.to_csv(types, rows)
4345

4446

47+
def test_to_csv_preserves_exact_decimal() -> None:
48+
"""CSV export must keep a Decimal exact — no lossy float() (R8-adjacent).
49+
50+
CSV is text, so it can carry full precision; the old float() rounded
51+
high-precision values on the way out.
52+
"""
53+
exact = Decimal("88.571428571428571428571428571")
54+
types = [ColumnDescription("amount", Decimal)]
55+
out = excel.to_csv(types, [(exact,)]).getvalue().decode("utf-8")
56+
assert "88.571428571428571428571428571" in out
57+
58+
4559
@pytest.mark.skipif(not excel.HAVE_EXCEL, reason="pyexcel not installed")
4660
def test_to_excel(example_ledger: RustfavaLedger) -> None:
4761
types, rows = _run_query(example_ledger, "balances")

0 commit comments

Comments
 (0)