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 |
||
|---|---|---|
| .. | ||
| android | ||
| assets/icon | ||
| lib | ||
| test | ||
| .gitignore | ||
| .metadata | ||
| analysis_options.yaml | ||
| pubspec.lock | ||
| pubspec.yaml | ||
| README.md | ||
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.jsonin 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
intgrosze 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 canonicalflutter analyze --fatal-infos—very_good_analysis+strict-casts,strict-inference,strict-raw-types; any info fails the buildflutter test --coverage+tool/coverage_check.py— 100% line coverage oflib/is mandatory; the gate also fails if a lib file is never executed by any test