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:
@@ -20,8 +20,9 @@ data class SettleRequest(val editing: SettlementId?, val payer: UserId, val amou
|
||||
data class SettlementDetailState(val id: SettlementId, val from: User, val to: User, val amountOre: Long, val date: String, val note: String, val allocations: List<Pair<Account, Long>>, val deleted: Boolean, val pending: Boolean, val timeline: List<TimelineLine>)
|
||||
|
||||
interface SettlementRepository {
|
||||
suspend fun start(editing: SettlementId?, fixedAccount: AccountId?): SettleState // defaults per AC-11.3 / AC-11.4
|
||||
suspend fun recompute(request: SettleRequest): SettleState // allocations == null: propose (§9.4); else validate and warn
|
||||
// v0.18, D-111: null when there is no paired session or the partner has not joined (S-9 is not offered then, AC-5.1).
|
||||
suspend fun start(editing: SettlementId?, fixedAccount: AccountId?): SettleState? // defaults per AC-11.3 / AC-11.4
|
||||
suspend fun recompute(request: SettleRequest): SettleState? // allocations == null: propose (§9.4); else validate and warn
|
||||
suspend fun save(request: SettleRequest): SaveResult<SettlementId>
|
||||
suspend fun delete(id: SettlementId)
|
||||
fun detail(id: SettlementId): Flow<SettlementDetailState?>
|
||||
|
||||
@@ -6,9 +6,19 @@ import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* [actor]: who made the server's current version (its `updated_by_user_id`, the "<actor>" of S-16's stale copy, §14.4.6);
|
||||
* null when the server had no row or the user is unknown.
|
||||
* null when the server had no row or the user is unknown. [lost]: the phone's version that was refused, read from
|
||||
* `sync_rejections.rejected_payload` and shown read-only on S-16 (v0.18, D-109); null when it cannot be shown (its
|
||||
* category or a user is not on the phone, or the payload is unreadable).
|
||||
*/
|
||||
data class Rejection(val mutationId: String, val entityType: EntityType, val entityId: String, val reason: SyncRejectionReason, val summary: String, val rejectedAt: String, val actor: User?)
|
||||
data class Rejection(val mutationId: String, val entityType: EntityType, val entityId: String, val reason: SyncRejectionReason, val summary: String, val rejectedAt: String, val actor: User?, val lost: RejectedVersion?)
|
||||
|
||||
/**
|
||||
* The lost local version of a rejected change, as pushed (v0.18, D-109). [row] is the ledger row it would have been
|
||||
* (`pending` false). For an expense: [accountName] its account, [payer], [mode] and [shares] its split; for a settlement
|
||||
* those are null or empty and the row carries the people and the amount. [notes] is the expense's notes or the
|
||||
* settlement's note; [deleted] is true when the refused change was a deletion.
|
||||
*/
|
||||
data class RejectedVersion(val row: LedgerRow, val accountName: String?, val payer: User?, val mode: SplitMode?, val shares: List<Share>, val notes: String, val deleted: Boolean)
|
||||
|
||||
data class SyncStatusState(val summary: SyncSummary, val pending: List<LedgerRow>, val rejections: List<Rejection>)
|
||||
|
||||
@@ -45,7 +55,12 @@ interface UpdateRepository {
|
||||
suspend fun install()
|
||||
}
|
||||
|
||||
data class AboutState(val versionName: String, val versionCode: Int, val serverUrl: String, val serverVersion: String?)
|
||||
/**
|
||||
* [serverVersion] and [schemaVersion] come from E-1 `/health`, fetched when S-22 opens and online (v0.18, D-110);
|
||||
* offline or when it fails, [serverVersion] falls back to the `X-Kvitt-Server-Version` header of the last response and
|
||||
* [schemaVersion] is null.
|
||||
*/
|
||||
data class AboutState(val versionName: String, val versionCode: Int, val serverUrl: String, val serverVersion: String?, val schemaVersion: Int?)
|
||||
|
||||
interface AboutRepository {
|
||||
fun about(): Flow<AboutState>
|
||||
|
||||
Reference in New Issue
Block a user