testsAndMisc/billsplit
Krzysztof kuhy Rudnicki fc185b835f
Some checks failed
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
billsplit CI / quality (push) Has been cancelled
billsplit: drop the four unused platform runner directories
windows/ (18 files), macos/ (28), ios/ (40) and linux/ (10) were flutter
create scaffolding that nothing in this repo builds: billsplit-ci.yml runs
pub get, dart format, analyze, test and the Python coverage gate, with no
flutter build step for any platform, and no script or doc invokes one
either. 96 tracked files, 4188 lines.

This also removes win32_window.cpp (288 lines), the last over-cap C++
file. It could not be split honestly - flutter regenerates it - so
deleting the target it belongs to is the honest way off the list.
Over-cap: 41 -> 40, and the cpp category is now empty.

Unpicked all three references: the .binary-allowlist icon entries and the
.gitignore un-ignore rules that existed only to let those icons past the
binary block, plus the README claim that the repo ships without platform
runner folders (false while 96 were tracked, true again now).

The four dirs are now gitignored so `flutter create` regenerates them
locally without re-tracking them. Verified by actually running
`flutter create . --platforms=linux,windows,macos,ios`: git add -An then
refuses all four as ignored. analyze/test/format stay green, 60 tests.

analysis_options.yaml is deliberately untouched - flutter analyze rewrites
the platform excludes back on every run, so removing them is not durable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 11:14:32 +02:00
..
android Format last three markdown files; cap billsplit's Android build memory 2026-08-03 13:25:48 +02:00
assets/icon Move billsplit Flutter app into the monorepo 2026-08-03 12:22:22 +02:00
lib billsplit: fix the two CI gates that were already red on main 2026-08-17 11:11:04 +02:00
test billsplit: fix the two CI gates that were already red on main 2026-08-17 11:11:04 +02:00
.gitignore Move billsplit Flutter app into the monorepo 2026-08-03 12:22:22 +02:00
.metadata Move billsplit Flutter app into the monorepo 2026-08-03 12:22:22 +02:00
analysis_options.yaml Split every over-cap billsplit Dart file 2026-08-16 20:57:56 +02:00
pubspec.lock billsplit: adopt the shared palette, retire the teal seed 2026-08-14 23:35:08 +02:00
pubspec.yaml billsplit: adopt the shared palette, retire the teal seed 2026-08-14 23:35:08 +02:00
README.md billsplit: drop the four unused platform runner directories 2026-08-17 11:14:32 +02:00

BillSplit

Flutter app (mobile + desktop) for splitting shop receipts between people — including the drinkers vs non-drinkers case — with Biedronka e-paragon import and .xlsx export.

Features

  • People roster with a per-person drinks alcohol toggle, plus arbitrary groups (tags) like "meat eaters".
  • Receipts: import a Polish fiscal e-receipt JSON (e-paragon, JPK_KASA_PARAGON_v2-0 — the file the Biedronka app exports; the embedded JWS payload is decoded, signature not verified) or build one manually. Multiple receipts persist locally (state.json in app documents).
  • Per-item assignment: each item is split among Everyone / Drinkers / Non-drinkers / a group / hand-picked people. Alcohol is auto-detected on import (wódka/piwo/wino/… keywords) and defaults to drinkers only; deposits (kaucja) become their own line split among everyone. Category and assignment are always editable — the keyword detection is a default, not a decision.
  • Grosz-exact math: every item's shares sum to exactly the item total (largest-remainder distribution), so per-person totals reconcile with the receipt to the grosz — no floating point drift.
  • Totals view: per person, with alcohol / rest breakdown, unassigned-item warnings, and a reconciliation check against the paid amount.
  • Export .xlsx: one row per item with a 1/0 person matrix plus a summary — same layout as the hand-made spreadsheet this app grew out of. Matrix and summary are exported values (the app is the calculator); column totals are live SUM formulas.

Running

android/ is the only platform runner folder the repo tracks. Generate the others locally for whatever you want to run on:

cd billsplit
flutter create . --platforms=linux,windows,macos,ios
flutter pub get
flutter run          # picks a connected device / desktop

Those four directories are gitignored, so a regenerated runner stays local instead of being committed back. CI never builds them: billsplit-ci.yml runs flutter analyze and flutter test only.

Tested with Flutter 3.44.8 / Dart 3.10.

Tests

flutter test

Covers: parsing the real sample receipt in test/fixtures/ (49 items + deposit, totals 590.55 + 7.00 = 597.55 zł), split invariants incl. a 500-case fuzz of the remainder distribution, assignment-mode resolution, and xlsx generation (the test writes build/test_export.xlsx for external inspection).

Design notes / edge cases

  • Money is int grosze end to end; formatting happens only at the UI edge.
  • An item resolving to zero people (e.g. "non-drinkers" on an all-drinker receipt) is excluded from totals and loudly flagged, never silently dropped into someone's bill.
  • Deleting a person prunes them from groups, receipt participants and custom assignments; deleting a group falls back affected items to Everyone.
  • Assignment modes are stored symbolically (not as materialized ID sets), so adding a person later automatically includes them in Everyone items. Touching a person checkbox materializes the current set and switches the item to custom.
  • paidTotalGr (what the card was charged) is kept separately from the item sum; a mismatch shows red in Totals instead of being "fixed" silently.

Quality gates (enforced in CI)

  • dart format --set-exit-if-changed . — formatting is canonical
  • flutter analyze --fatal-infos — very_good_analysis + strict-casts, strict-inference, strict-raw-types; any info fails the build
  • flutter test --coverage + tool/coverage_check.py — 100% line coverage of lib/ is mandatory; the gate also fails if a lib file is never executed by any test