fix: treat VS as a string on read and write

Go stringified sheet cells with fmt.Sprint, so a numeric VS read with
UNFORMATTED_VALUE was rendered as 2.609767878e+09. Text columns of the
payments sheet now go through sheets.CellString, which never uses
exponent form; bank inference uses the same helper.

Both sync implementations now append VS as a text cell so Sheets keeps
it a string and leading zeros survive. Existing rows are not rewritten.

Python already read VS as a digit string; a regression test pins that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 11:34:20 +02:00
co-authored by Claude Opus 5.5
parent 022facc957
commit 97f94afa02
11 changed files with 161 additions and 9 deletions
+22 -1
View File
@@ -1,6 +1,7 @@
import unittest
from unittest.mock import MagicMock, patch
from scripts.match_payments import canonical_member_key, reconcile
from scripts.match_payments import canonical_member_key, fetch_sheet_data, reconcile
class TestCanonicalMemberKey(unittest.TestCase):
@@ -67,3 +68,23 @@ class TestReconcileTolerantPersonMatching(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
class TestFetchSheetDataVS(unittest.TestCase):
"""Numeric VS cells arrive as floats (UNFORMATTED_VALUE) and must stay plain digit strings."""
def _fetch(self, vs_cell):
service = MagicMock()
service.spreadsheets().values().get().execute.return_value = {"values": [
["Date", "Amount", "manual fix", "Person", "Purpose", "Inferred Amount",
"Sender", "VS", "Message", "Bank ID", "Sync ID"],
["2026-04-15", 750, "", "", "", "", "Test Sender", vs_cell, "fee", "", "abc"],
]}
with patch("scripts.match_payments.get_sheets_service", return_value=service):
return fetch_sheet_data("sheet-id", "creds.json")
def test_numeric_vs_is_plain_digit_string(self):
self.assertEqual(self._fetch(2609767878.0)[0]["vs"], "2609767878")
def test_string_vs_is_kept(self):
self.assertEqual(self._fetch("0308")[0]["vs"], "0308")
+33
View File
@@ -0,0 +1,33 @@
import contextlib
import io
import unittest
from unittest.mock import MagicMock, patch
from scripts.sync_fio_to_sheets import COLUMN_LABELS, as_text_cell, sync_to_sheets
class TestAsTextCell(unittest.TestCase):
def test_digits_get_text_marker(self):
self.assertEqual(as_text_cell("0308"), "'0308")
self.assertEqual(as_text_cell(123), "'123")
def test_empty_stays_empty(self):
self.assertEqual(as_text_cell(""), "")
self.assertEqual(as_text_cell(None), "")
class TestSyncWritesVSAsText(unittest.TestCase):
def test_appended_row_has_text_vs(self):
service = MagicMock()
sheet = service.spreadsheets.return_value
sheet.values.return_value.get.return_value.execute.return_value = {"values": [COLUMN_LABELS]}
tx = {"date": "2026-04-10", "amount": 750.0, "sender": "Test Sender",
"vs": "0308", "message": "fee", "bank_id": "111"}
with patch("scripts.sync_fio_to_sheets.get_sheets_service", return_value=service), \
patch("scripts.sync_fio_to_sheets.fetch_transactions_all", return_value=[tx]), \
contextlib.redirect_stdout(io.StringIO()):
sync_to_sheets("sheet-id", "creds.json", days=30)
body = sheet.values.return_value.append.call_args.kwargs["body"]
self.assertEqual(len(body["values"]), 1)
self.assertEqual(body["values"][0][7], "'0308")