Adds --write to the matcher. Wrote 1,242 split rows across 657 transactions. Imported settled, and that is the whole design. These obligations were discharged years ago on a platform we no longer run, and their residual is already carried by transaction 2348. Writing them unsettled would re-open roughly $40k of debts that were paid. ACTIVE_OBLIGATION keeps settled splits out of every owed figure while myShare/mySplitOf still count them, which is exactly the asymmetry this needs: the import exists to correct historical SPEND, not to move a balance. Effect: $35,259 leaves my historical spend -- $13,088 in 2024, $22,117 in 2025 -- because a $200 grocery shop that was always half hers no longer reads as $200 of mine. Balances are byte-identical before and after (Molina -1226.72/145, Sonu 5428.08/419), which is the assertion that matters. Shares are written as the CSV computed them, so a 50/50 row can land as 50.01/49.99. That is faithful rather than tidy; no transaction exceeds 100%. Rehearsed on the 37-row Rome file first (24 rows) and verified before the full run -- both the balances and one split read back through the API.
This commit is contained in:
@@ -221,6 +221,10 @@ def main() -> int:
|
||||
ap.add_argument("--verbose", action="store_true")
|
||||
ap.add_argument("--file", help="only this CSV (substring match)")
|
||||
ap.add_argument("--window", type=int, default=5, help="date tolerance in days")
|
||||
ap.add_argument(
|
||||
"--write", action="store_true",
|
||||
help="actually insert the splits (settled=true). Without this, nothing is written.",
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
paths = sorted(glob.glob(os.path.join(DUMP_DIR, "*SplitMyExpenses*.csv")))
|
||||
@@ -324,6 +328,36 @@ def main() -> int:
|
||||
print(f"Of the matched, {already} already carry a split and would be left alone;")
|
||||
print(f"{len(rep.matched) - already} would gain one.")
|
||||
|
||||
if args.write:
|
||||
# Imported as settled: these obligations were discharged on a platform
|
||||
# we no longer run, and the residual is already carried by transaction
|
||||
# 2348. Writing them unsettled would re-open ~$40k of debts that were
|
||||
# paid years ago. See ACTIVE_OBLIGATION in analytics-sql.ts -- settled
|
||||
# rows stay in spend and leave every owed figure, which is exactly the
|
||||
# point: this import exists to correct historical SPEND.
|
||||
SETTLED_ON = "2026-01-09" # the carryover's date
|
||||
written = 0
|
||||
for row, tx in rep.matched:
|
||||
if tx["has_split"]:
|
||||
continue
|
||||
payer_share = round(100 - row.ower_share, 2)
|
||||
pairs = [(row.ower, row.ower_share)]
|
||||
if payer_share > 0:
|
||||
pairs.append((row.payer, payer_share))
|
||||
for pid, share in pairs:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO transaction_splits
|
||||
(transaction_id, participant_id, share_percent, settled, settled_at)
|
||||
VALUES (%s, %s, %s, true, %s)
|
||||
ON CONFLICT (transaction_id, participant_id) DO NOTHING
|
||||
""",
|
||||
(tx["id"], pid, share, SETTLED_ON),
|
||||
)
|
||||
written += cur.rowcount
|
||||
conn.commit()
|
||||
print(f"\nWROTE {written} split rows (settled=true, settled_at={SETTLED_ON}).")
|
||||
|
||||
if args.verbose:
|
||||
print("\n--- ambiguous ---")
|
||||
for row, cands in rep.ambiguous[:40]:
|
||||
|
||||
Reference in New Issue
Block a user