Skip to content

Freeze a billing period as an immutable month-end snapshot - #82

Merged
JetSquirrel merged 3 commits into
mainfrom
feat/billing-snapshots
Nov 3, 2026
Merged

JetSquirrel merged 3 commits into
mainfrom
feat/billing-snapshots

Conversation

@JetSquirrel

Copy link
Copy Markdown
Owner

Closes #77

Adds a Month-end snapshots card to Account detail. A user can freeze a month's bill and later compare that frozen copy with whatever the ledger holds now.

What it does

  • Freeze: billing_snapshot and billing_snapshot_charge (schema v10) hold a full copy of the month's charges, not a pointer to a batch. Whole-period replacement never touches them. Nothing updates a snapshot.
  • Revisions: freezing a changed bill adds a new revision and keeps the earlier ones. Freezing an unchanged bill adds nothing.
  • Provenance: each revision records its snapshot ID, timestamp, batch, source, row count and checksum, plus totals per billing currency and category and the FX rates used. Totals read back without the FX table.
  • Comparison: frozen against current by currency, service and category, with sortable columns. A row opens the charges behind it: new, removed or changed, with amounts and the FOCUS columns that differ.
  • Explicit states: not frozen, month not ended, rows with no amount (shown as "No amount", not 0), missing FX rate, current differs, and a failed integrity check.

The web demo has the same surface, held in memory. The arithmetic is shared in src/analytics/snapshot.rs.

Worth a look in review

  • Deleting an account's billing history also deletes its snapshots. That keeps "delete" meaning delete, and the confirmation text now says so. The issue asks for immutability against refresh and import, not against an explicit delete. Tell me if you would rather keep them.
  • charge_ids moved from src/ledger/mod.rs to src/analytics/snapshot.rs so both targets share it. The key and format are unchanged, so existing ids stay stable.
  • sha2 and hex moved to the dependencies shared by both targets, because the web demo computes checksums too. Cargo.lock is unchanged.
  • Not in this PR: the tables are sortable but have no column visibility or filter controls, and a row shows at most its 50 largest charges (it says so when there are more). "Unsupported/unknown" from the issue's state list has no state of its own; a row with no cost basis shows as "No amount".

Testing

Run locally on Windows against this branch before the changelog-only commit:

  • cargo test: 581 passed, 2 ignored. New tests cover refresh, re-import, credits and adjustments, multiple currencies, absent-cost rows, repeat freezes, a corrected AWS FOCUS export and account deletion.
  • cargo clippy --all-targets -- -D warnings and cargo fmt --check: clean.
  • cargo +nightly-2026-07-28 check --lib --target wasm32-unknown-unknown: passes. This is a type-check, not the full build-web.sh.

Not tested in a real window. Nothing here has been clicked. Please check on desktop, using demo data:

  • Freeze a month: the confirmation names the month and totals, and Cancel and Escape do nothing.
  • After a re-import, the status reads as differing, and a row opens its charges.
  • Sort headers cycle through descending, ascending and default, with an open row staying open.
  • Keyboard: Tab reaches a row, and Enter or Space opens it.
  • Layout holds at minimum window width and in 简体中文.

🤖 Generated with Claude Code

JetSquirrel and others added 3 commits November 3, 2026 10:39
Closes #77

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Confirm before freezing, keep Freeze beside the month switcher, make the
frozen-against-current tables sortable, and let a row open the charges
behind it. Show the snapshot id and how fresh the ledger's batch is, and
keep months older than the latest six reachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JetSquirrel
JetSquirrel merged commit b59dcc4 into main Nov 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(billing): immutable month-end billing snapshots [P0]

1 participant