M12f: S-16 lost version read-only, S-22 versions from /health, settle start without a partner (D-109 to D-111)

- Rejection.lost: RejectedVersion (the pushed entity from sync_rejections.rejected_payload as a ledger row, account,
  payer, split, notes, deleted); S-16 "Öppna" shows it read-only in a dialog with "Visa nuvarande version" (S-8/S-10);
  without one it opens the current version as before. Ledger rows take a null onClick (read-only).
- AboutState.schemaVersion; AboutRepositoryImpl calls E-1 /health once per collection when online; header fallback.
  S-22 shows "1.1.0 (schema 3)".
- SettlementRepository.start/recompute return null without a session or partner; save is Invalid(payer); no
  checkNotNull/requireNotNull in :data.
- :feature:home screenshot harness: one more frame and an idle looper before each capture; the committed sv_dark,
  200 % and dialog goldens were one variant stale (light instead of dark, en instead of sv_200).
- Tests: lost version (stale, refused deletion, unreadable payload), /health online and offline, settle without a
  partner, category reorder PATCHes, recurring toWire splits, §9.2 effect cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 22:32:24 +02:00
parent 7fb86f5524
commit dc7a296d70
90 changed files with 550 additions and 70 deletions
@@ -153,7 +153,8 @@ class SettleViewModel @Inject constructor(
home.home().collect { h -> mutable.update { it.copy(me = h.me, partner = h.partner) } }
}
viewModelScope.launch {
val start = settlements.start(editing, if (editing == null) fixedAccountId else null)
// Null only without a partner, where S-9 is not offered (AC-5.1, D-111): the screen stays empty.
val start = settlements.start(editing, if (editing == null) fixedAccountId else null) ?: return@launch
mutable.update { s -> s.withResult(start, propose = true).copy(loading = false, date = start.date, note = start.note, rowsEdited = editing != null) }
}
}
@@ -234,7 +235,7 @@ class SettleViewModel @Inject constructor(
val request = request(current, fresh) ?: return
recomputing?.cancel()
recomputing = viewModelScope.launch {
val result = settlements.recompute(request)
val result = settlements.recompute(request) ?: return@launch
mutable.update { it.withResult(result, fresh) }
}
}
@@ -9,7 +9,6 @@
<string name="settle_swap">Byt riktning</string>
<string name="settle_direction">Riktning</string>
<string name="settle_amount">Belopp</string>
<string name="settle_empty_settled">Ni är kvitt just nu.</string>
<string name="settle_empty_hint">Skriv ett belopp så föreslår Kvitt en fördelning.</string>
<string name="settle_allocation">Fördelning</string>
<string name="settle_row_debt">Skuld %1$s</string>
@@ -9,7 +9,6 @@
<string name="settle_swap">Swap direction</string>
<string name="settle_direction">Direction</string>
<string name="settle_amount">Amount</string>
<string name="settle_empty_settled">You are all settled up right now.</string>
<string name="settle_empty_hint">Enter an amount and Kvitt suggests an allocation.</string>
<string name="settle_allocation">Allocation</string>
<string name="settle_row_debt">Debt %1$s</string>