Skip to content

refactor(kyc): drive KycCubit routing from API processStatus, drop local sets#494

Merged
TaprootFreak merged 5 commits into
RealUnitCH:developfrom
Blume1977:refactor/kyc-cubit-api-driven
May 21, 2026
Merged

refactor(kyc): drive KycCubit routing from API processStatus, drop local sets#494
TaprootFreak merged 5 commits into
RealUnitCH:developfrom
Blume1977:refactor/kyc-cubit-api-driven

Conversation

@Blume1977
Copy link
Copy Markdown
Contributor

@Blume1977 Blume1977 commented May 21, 2026

Summary

Closes the 2026-05-21 ident-misroute report. Wave 2.2 of the API-as-Decision-Authority audit (docs/api-authority-plan.md, foundation rule in #491). Companion to API PR DFXswiss/api#3732 (feat/kyc-decision-fields).

The cubit used to re-implement the backend's own routing rule locally:

  • _requiredStepNames set — duplicate of requiredKycSteps(userData) on the API
  • _minLevelForActions = 30 threshold — duplicate of the level check
  • actionableStatuses / pendingStatuses sets — duplicate of how the backend tags step status
  • A 30-line iteration over kycSteps that derived KycCompleted / KycPending / _continueKyc from filter results
  • _continueKyc repeated the same manual filter over kycSteps after a realunit registration completed (parallel code path with the same anti-pattern)

That setup is exactly what misroutes a high-level user when checkDfxApproval re-issues their Ident step on the backend (user_data 338759: kycLevel 53 + an Outdated Ident step + a sequence-1 Ident step in InProgress → the app sends them back to KycIdentPage even though they didn't ask to re-verify).

After API PR #3732 the backend returns those decisions directly. The cubit now renders them.

Changes

Cubit logic

  • KycLevelDto.processStatus (new field, mirrors KycProcessStatus on the API) drives the top-level switch:
    • Completed → emit KycCompleted
    • PendingReview → emit KycPending(<the required step in review>)
    • InProgress → call _continueKyc (unchanged routing semantics, new implementation)
    • Failed → emit KycFailure
  • KycStepDto.isRequired (new field) selects which step to surface in the pending case.
  • UserKycDto.canTrade is parsed from /v2/user for downstream callers (Wave 3 will let buy/sell flows render it directly instead of guessing from level >= 30).
  • _continueKyc now reads KycSessionDto.currentStep directly instead of iterating over kycSteps to find isCurrent. A missing currentStep surfaces KycUnsupportedStepFailure(null) instead of leaking a bare StateError stack-trace into the user-facing i18n message.
  • KycPageManager drops the requiredLevel parameter and its router plumbing — the cubit no longer needs a threshold. canTrade / processStatus speak directly to the buy/sell question.

The cubit body shrinks from ~95 lines of iteration logic to a five-arm switch on processStatus. The diff removes 6 of the 10 audit-flagged violations on this file in one go.

What stays local

_legalDisclaimerAccepted / _registrationSignProduced remain per-cubit-instance security gates. They do not encode business routing — they enforce a per-session ceremony on this device before any signed call. Position in the flow is unchanged from before.

DTOs

  • KycLevelDto + KycSessionDto carry processStatus, default to inProgress.
  • KycStepDto carries isRequired, default false.
  • UserKycDto carries canTrade, default false.
  • New KycProcessStatus enum mirrors the API enum 1:1.

Backwards compatibility

All three new fields default to safe values when the API response omits them:

  • processStatus = inProgress → falls through to _continueKyc, identical to the legacy behaviour on the unhappy path
  • isRequired = falseKycPending falls back to KycCompleted (no required step found)
  • canTrade = false → downstream callers are conservative

A pre-#3732 backend keeps the app functional; an app build that consumes the new fields and a backend that hasn't shipped them yet doesn't crash.

Tests

kyc_cubit_test.dart rewritten to drive the cubit via API fixtures (processStatus + isRequired) instead of the old level-based setup. The previous level-based + step-iteration cases collapse to five fixtures:

  • processStatus: Completed → emit KycCompleted
  • processStatus: PendingReview + required ident in InReview → emit KycPending(ident)
  • processStatus: PendingReview + required dfxApproval in InReview → emit KycPending(dfxApproval)
  • processStatus: InProgress_continueKyc → emit KycSuccess(currentStep)
  • processStatus: Failed → emit KycFailure

The 403/TFA_REQUIRED matrix, generation-token regression, and sign-gate sequencing are preserved.

Verification

  • flutter analyze — clean
  • flutter test1435 / 1435 passing

Manual test plan (post-merge of #3732)

  • Re-run the 2026-05-21 reproduction on user_data 338759 (or a fresh test wallet on the same merged account): open the app → expect Dashboard if canTrade: true, or KycPending(ident) only if processStatus: PendingReview (no more spurious KycIdentPage from a re-issued Ident step).
  • New customer flow still works end-to-end: register email → disclaimer → registration → progresses through _continueKyc for each required step.
  • Existing DFX customer merge flow (PR fix(kyc): existing DFX customer merge — close misroute, surface failures #466 path): merge confirm → disclaimer → registration sign → lands on dashboard if the merged user is canTrade: true.

Closes (from audit, P0)

  • V1 — _requiredStepNames set
  • V2 — _minLevelForActions = 30 + level < _requiredLevel checks
  • V3 — actionableStatuses / pendingStatuses sets and the iteration over kycSteps
  • V5 — manual filter + routing chain over kycSteps (process-status-driven routing replaces the entire next-step selection algorithm)
  • V45 — _continueKyc's parallel iteration over kycSteps; the cubit now reads KycSessionDto.currentStep directly
  • Routing dependency on iteration logic

Tracked in docs/api-authority-audit.md.

Follow-ups

  • The auto-email-registration branch when level < 10 is still in the cubit; it's a Wave-3 candidate — the API could perform this server-side once the email is set.
  • KycStep → KycStepName map (_mapStepName) is still local; if the API ever exposes a uiHint field it can go too.

Blume1977 added a commit to Blume1977/realunit-app that referenced this pull request May 21, 2026
…g queue

Companion to the seven PRs that landed today (RealUnitCH#491, RealUnitCH#492, RealUnitCH#493, RealUnitCH#494,
RealUnitCH#495, RealUnitCH#496, RealUnitCH#497 + DFXswiss/api#3732, #3733). Adds:

- A 'Shipped (2026-05-21)' table mapping each pair-PR to the V-IDs
  it closes, with the W2 pair flagged as the closure of the
  2026-05-21 ident-misroute incident.
- An 'Outstanding — next phase' section grouping the remainders:
  P0 follow-ups (V6c/V6d/V16/V20), Wave 4 (V17/V18/V14/V19 — new
  backend modules), and the P1/P2 long tail (V21–V27, V29–V33).
  Each item carries the same V-ID anchor used elsewhere so PR
  reviewers can cross-reference cleanly.

Does not touch V<N> classifications or counts — those stay as the
reviewer set them. Pure append at the end of the file.
@Blume1977 Blume1977 force-pushed the refactor/kyc-cubit-api-driven branch from e5e4f51 to 5536c4f Compare May 21, 2026 15:01
@Blume1977 Blume1977 marked this pull request as ready for review May 21, 2026 15:18
@TaprootFreak
Copy link
Copy Markdown
Contributor

Review-Findings (Wave 2 — KycCubit API-driven Routing)

Blocker

1. processStatus: PendingReview ohne mappbaren isRequired Step → silent KycCompleted
lib/screens/kyc/cubits/kyc/kyc_cubit.dart:131-145
Im pendingReview-Zweig:

final pending = kycStatus.kycSteps.firstWhereOrNull(
  (s) => s.isRequired && _mapStepName(s.name) != null,
);
final step = pending != null ? _mapStepName(pending.name) : null;
if (step != null) {
  emit(KycPending(step));
} else if (pending != null) {        // toter Zweig
  emit(KycUnsupportedStepFailure(pending.name));
} else {
  emit(const KycCompleted());        // misroute
}

Wenn der Backend processStatus: PendingReview setzt aber kein gemappter Step als isRequired: true markiert ist (z.B. additionalDocuments, personalData, residencePermit, statutes — alle in _mapStepName nicht abgedeckt), fällt die App in KycCompleted und schickt den User zum Dashboard. Das ist exakt die gleiche Klasse Misroute wie der 2026-05-21-Incident, nur in die andere Richtung. Threat-Model-Verletzung: API sagt "Review pending", App sagt "Completed". Zudem ist der else if (pending != null)-Zweig durch den _mapStepName != null Filter in der ersten Zeile toter Code.
Vorschlag: Filter _mapStepName(s.name) != null aus dem firstWhereOrNull entfernen, dann strikt routen: gefundenes isRequired ohne Mapping → KycUnsupportedStepFailure, kein isRequired bei PendingReview → ebenfalls KycUnsupportedStepFailure (oder dedizierter KycPending ohne Step-Hint), aber nie KycCompleted in diesem Zweig.

2. Unbekannter processStatus-Wert → silent inProgress
lib/packages/service/dfx/models/kyc/kyc_level.dart:33-34

default:
  return KycProcessStatus.inProgress;

Wenn der API später einen neuen Wert einführt (z.B. OnHold, Suspended) oder fehlerhaft serialisiert, mappt der Client das stillschweigend auf inProgress und feuert _continueKyc. Das ist genau die "lokale Default-Entscheidung", die Wave-1/#491 eliminieren wollte. Plus: kollidiert mit dem Keine Fallbacks-Pattern.
Vorschlag: Unbekannter String → KycFailure('Unsupported KYC process status: $value') werfen (oder via separates KycProcessStatus.unknown durch den Switch in _runCheckKyc fallen lassen, dort dann zu KycFailure). Der Backwards-Compat-Default für fehlendes Feld (null) kann in den DTO-Parsern explizit auf inProgress bleiben — das ist die dokumentierte Pre-#3732-Migration.

3. V45 (_continueKyc parallele Iteration) nicht entfernt, obwohl Plan/Audit das in derselben PR fordern
lib/screens/kyc/cubits/kyc/kyc_cubit.dart:183

final currentStep = kycStatus.kycSteps.firstWhere((step) => step.isCurrent);

Sowohl docs/api-authority-plan.md:168 ("Delete the same kycSteps.firstWhere(step.isCurrent) loop in _continueKyc — that's V45 … same currentStep-driven rewrite applies") als auch docs/api-authority-audit.md:45-47 ("must also be deleted in the same PR") binden V45 explizit an Wave 2.2. Der PR-Body listet aber unter "Closes" nur V1/V2/V3. Sekundärer Bug: firstWhere ohne orElse wirft StateError wenn kein Step isCurrent: true ist — der wird zwar vom outer catch zu KycFailure(e.toString()) umgeleitet, aber die User-sichtbare Message ist dann ein nackter Stack-Trace.
Vorschlag: KycSessionDto.currentStep (existiert bereits, siehe Zeile 194 kycStatus.currentStep?.session.url) ist die Authoritative-Quelle — direkt daraus den Step-Namen ziehen statt erneut über kycSteps zu iterieren. Falls Scope-Trennung gewünscht: PR-Body & docs/api-authority-audit.md korrigieren und V45 als Wave-2.3-Follow-up explizit aufführen.

Empfehlungen

4. Dead-Data: Routen passen extra: state.requiredLevel weiter
lib/screens/sell/widgets/sell_button.dart:29, lib/screens/buy/widgets/payment_additional_action_needed_button.dart:77
Beide passen extra: paymentState.requiredLevel an AppRoutes.kyc, aber KycPageManager ignoriert state.extra jetzt komplett. Funktional kein Bug, aber irreführend — Reviewer der den Buy/Sell-Code liest, denkt die KYC-Page wertet das aus. Vermutung: das wird mit Wave 3 (canTrade für Buy/Sell) eh entfernt — dann hier kurzer TODO-Kommentar mit Issue-Ref.

5. canTrade wird geparsed aber nirgends gelesen
lib/packages/service/dfx/models/user/dto/user_dto.dart:27,33,41
Bewusst Wave-3, im PR-Body so deklariert. Solider Move für die Migration, kein Befund — nur Hinweis dass das Feld bis Wave 3 toter Wire-Traffic ist (akzeptabel).

6. KycFailure('KYC terminated') — Hardcoded englischer String in i18n-Pipeline
lib/screens/kyc/cubits/kyc/kyc_cubit.dart:117
KycFailurePage interpoliert die Message via S.of(context).kycFailureDescription(message). 'KYC terminated' landet roh in der UI. Vermutung: i18n-Konvention im Repo ist String-aus-Locale-File — kurze Bestätigung dass das so gewollt ist, sonst dedizierten i18n-Key für processStatus.failed.

Was solide ist

  • DTOs (KycLevelDto, KycSessionDto, KycStepDto, UserKycDto) defaulten alle drei neuen Felder auf safe Werte — Pre-#3732-Backend kompatibel.
  • Sign-Gate-Kommentare präzisiert (Zeile 96-100) — klarstellt, dass _legalDisclaimerAccepted / _registrationSignProduced keine Routing-Decisions sind sondern Session-Security-Gates.
  • Tests durchgängig auf processStatus-Fixtures migriert, alte Level-basierte step-status routing (regression for #332)-Gruppe sauber entfernt statt parallel gepinnt zu lassen.
  • Generation-Token-Regression & TFA_REQUIRED-Branch erhalten.
  • Account-Merge-Detection via step.reason == accountMergeRequested korrekt im pre-processStatus-Block belassen (der Reason ist Step-getragen, nicht im processStatus).
  • KycPageManager + router_config.dart konsequent vom requiredLevel-Parameter befreit; keine Reste in der DI.

Geprüft und negativ: keine versteckten level >= 30 / _minLevelForActions / _requiredStepNames mehr in lib/ (Buy/Sell requiredLevel kommt aus Backend-Exception, nicht aus lokalem Routing — Wave 3).

TaprootFreak added a commit to Blume1977/realunit-app that referenced this pull request May 21, 2026
…le erweitern

V45 ist mit der `_continueKyc`-Anpassung im selben PR geschlossen wie
V1/V2/V3/V5 (`docs/api-authority-plan.md` §W2.2). Audit-Eintrag
aktualisiert: Datei-Anker auf `_continueKyc` (statt der inzwischen
ueberholten Zeilennummer 208) und Closed-by-Hinweis ergaenzt, dass der
`StateError`-Pfad aus dem `firstWhere`-on-empty mit weg ist.

W2-Zeile in der `Shipped (2026-05-21)`-Tabelle erweitert: V45 in die
"Closes V-IDs"-Spalte aufgenommen, damit der Audit-Diff zur PR-RealUnitCH#494
deckungsgleich ist.
@TaprootFreak
Copy link
Copy Markdown
Contributor

Habe die drei Blocker uebernommen (Commits 3237c6d, d2328de, 897ec71). Author Blume1977 ist nicht mehr aktiv — gepusht via maintainer_can_modify auf Blume1977:refactor/kyc-cubit-api-driven.

Was gefixt ist

B1 — PendingReviewKycCompleted-Misroute
lib/screens/kyc/cubits/kyc/kyc_cubit.dart:133-152 — der firstWhereOrNull-Filter steht jetzt strikt auf s.isRequired (Mapping-Filter raus). Routing:

  • kein isRequired-Step → KycUnsupportedStepFailure(null)
  • isRequired-Step ohne UI-Mapping → KycUnsupportedStepFailure(stepName)
  • gemappt → KycPending(step)
  • nie KycCompleted in diesem Zweig

KycUnsupportedStepFailure.stepName ist nullable; KycPageManager:46-48 faellt im Null-Fall auf '-' als i18n-Platzhalter.

B2 — Unbekannter processStatus silent-default
lib/packages/service/dfx/models/kyc/kyc_level.dart:28-49KycProcessStatusExtension.fromValue wirft jetzt ArgumentError auf unbekannte Strings. Default-on-null fuer Pre-#3732 BC bleibt explizit in KycLevelDto.fromJson / KycSessionDto.fromJson (dokumentiert im neuen Kommentar).

B3 — V45 (_continueKyc parallele Iteration)
lib/screens/kyc/cubits/kyc/kyc_cubit.dart:191-220kycSteps.firstWhere((step) => step.isCurrent) ersetzt durch direkten Read von KycSessionDto.currentStep. Fehlt das Feld → KycUnsupportedStepFailure(null) statt StateError-Stack-Trace im i18n-Template.

Docs-Update: docs/api-authority-audit.md:V45 als geschlossen markiert, W2-Zeile in der Shipped-Tabelle um V45 erweitert.

Neue Tests

test/screens/kyc/cubits/kyc/kyc_cubit_test.dart:

  • PendingReview ohne required Step → KycUnsupportedStepFailure(null)
  • PendingReview + required unmapped Step (additionalDocuments) → KycUnsupportedStepFailure(additionalDocuments)
  • _continueKyc konsumiert KycSessionDto.currentStep (bestehender Test umgeschrieben + Negativ-Fall fuer fehlendes currentStep)
  • urlOrToken durchgereicht aus currentStep.session.url

test/packages/service/dfx/models/kyc/kyc_level_test.dart:

  • Round-Trip ueber alle KycProcessStatus-Werte
  • Drei Negativ-Faelle (OnHold, Suspended, Leerstring) werfen ArgumentError

Verifikation

  • flutter analyze — clean
  • flutter test1438 / 1438 passing (zuvor 1412 — die +26 sind die neuen Cases plus die Reshape der _continueKyc-Tests)

PR-Body sollte unter Closes jetzt V1, V2, V3, V5, V45 auflisten (V45 fehlt aktuell noch im Body). Mache ich nicht selber, weil das gh pr edit Draft-Status springen lassen kann.

Blume1977 and others added 5 commits May 21, 2026 20:20
…cal sets

Closes the 2026-05-21 ident-misroute report and Wave 2.2 of the
API-as-Decision-Authority audit. Companion to API PR
DFXswiss/api#3732 (`feat/kyc-decision-fields`).

The cubit used to re-implement the backend's own routing rule locally:
- `_requiredStepNames` set (duplicate of `requiredKycSteps(userData)`)
- `_minLevelForActions = 30` threshold (duplicate of the level check)
- `actionableStatuses` / `pendingStatuses` sets (duplicate of how the
  backend tags step status)
- A 30-line iteration over `kycSteps` that derived `KycCompleted` /
  `KycPending` / `_continueKyc` from filter results.

After PR #3732 the API returns those decisions directly. The cubit now
renders them:

- `KycLevelDto.processStatus` (new field, mirrors `KycProcessStatus`)
  drives the top-level switch — `Completed` → `KycCompleted`,
  `PendingReview` → `KycPending(currentStep)`, `InProgress` →
  `_continueKyc`, `Failed` → `KycFailure`.
- `KycStepDto.isRequired` (new field) selects which step to surface in
  the pending case.
- `UserKycDto.canTrade` is parsed from `/v2/user` for downstream
  callers (buy/sell flows can render it instead of guessing from
  `level >= 30`).
- `KycPageManager` drops the `requiredLevel` parameter (and its router
  plumbing) — the cubit no longer needs a threshold; `canTrade` /
  `processStatus` speak directly to the buy/sell question.

Backwards-compat: all three new fields default to safe values
(`processStatus = inProgress`, `isRequired = false`, `canTrade = false`)
when the API response omits them, so a pre-#3732 backend keeps the app
functional — it just falls through to `_continueKyc` for every check,
which matches the old behaviour on the unhappy path.

Local session gates stay local:
`_legalDisclaimerAccepted` / `_registrationSignProduced` remain
per-cubit-instance security gates. They do not encode business routing —
they enforce a per-session ceremony on this device before any signed
call. Position in the flow unchanged.

Tests:
- `kyc_cubit_test.dart` rewritten to drive the cubit via API fixtures
  (`processStatus` + `isRequired`) instead of the old level-based
  setup. The previous level-based + step-iteration cases collapse to
  five fixtures: completed, pendingReview, inProgress (→ _continueKyc),
  failed, and the session-gate path. The 403/TFA_REQUIRED matrix,
  generation-token regression, sign-gate sequencing are preserved.

Verification:
- `flutter analyze` — clean
- `flutter test` — **1412 / 1412 passing**
… mappen

Wenn der Backend einen neuen `processStatus`-Wert (z.B. `OnHold`,
`Suspended`) einfuehrt oder einen Wert fehlerhaft serialisiert, hat der
Client das bisher stillschweigend auf `inProgress` gemappt und im
Anschluss `_continueKyc` gefeuert. Das verletzt die Foundation-Regel aus
RealUnitCH#491 ("API as Decision Authority" — niemals lokale Default-Entscheidung
ueber Routing) und kollidiert mit dem `Keine Fallbacks`-Pattern: jeder
unbekannte Wert haette latent eine Misroute produziert ohne dass es
irgendwo aufgefallen waere.

`KycProcessStatusExtension.fromValue` wirft jetzt `ArgumentError`, sobald
ein unbekannter String auf der Leitung ankommt. Die Pre-#3732
Backwards-Compat fuer ein **fehlendes** Feld (`null` auf der Leitung)
bleibt explizit in den DTO-Parsern erhalten — das ist die dokumentierte
Migration, kein lokaler Routing-Default.

Tests in `kyc_level_test.dart` erweitert: Round-Trip ueber alle
Enum-Werte plus drei Negativ-Faelle (`OnHold`, `Suspended`, Leerstring).
Zwei zusammenhaengende Fixes im KycCubit-Routing, beide an Wave 2.2
(`docs/api-authority-plan.md` §W2.2, `docs/api-authority-audit.md` V45)
gebunden.

**B1 — `PendingReview` ohne mappbaren `isRequired`-Step.**
Bisher hat der `pendingReview`-Zweig die Liste mit
`s.isRequired && _mapStepName(s.name) != null` gefiltert. Wenn der
Backend `processStatus: PendingReview` setzte, aber kein gemappter Step
als `isRequired: true` markiert war (z.B. `additionalDocuments`,
`personalData`, `residencePermit`, `statutes` — alle in `_mapStepName`
nicht abgedeckt), fiel die App in den `else`-Arm und emittierte
`KycCompleted` → Dashboard. Das ist exakt dieselbe Klasse Misroute wie
der 2026-05-21-Incident, nur in die Gegenrichtung (API: Review pending →
App: Completed). Zusaetzlich war der `else if (pending != null)`-Zweig
durch den Mapping-Filter in der ersten Zeile toter Code.

Der Filter ist jetzt strikt auf `s.isRequired` reduziert. Fehlt ein
required-Step → `KycUnsupportedStepFailure(null)`; ist der required-Step
nicht gemappt → `KycUnsupportedStepFailure(pending.name)`; gemapped →
`KycPending(step)`. **Nie** `KycCompleted` in diesem Zweig.
`KycUnsupportedStepFailure.stepName` wurde dafuer nullable gemacht, der
`KycPageManager` faellt in dem Fall auf `'-'` als Platzhalter im
i18n-Template zurueck.

**B3 — V45: `_continueKyc` iterierte parallel ueber `kycSteps`.**
`kycStatus.kycSteps.firstWhere((step) => step.isCurrent)` war die
parallele Anti-Pattern-Stelle zu V5 — die `_runCheckKyc`-Schleife. Plan
und Audit binden V45 explizit an Wave 2.2 (`docs/api-authority-plan.md`
§W2.2 "Delete the same `kycSteps.firstWhere(step.isCurrent)` loop in
`_continueKyc` — that's V45"; `docs/api-authority-audit.md:V45` "must
also be deleted in the same PR"), in der ersten Iteration des PR war
das aber nicht erledigt. Sekundaerer Bug: `firstWhere` ohne `orElse`
wirft `StateError`, wenn kein Step `isCurrent: true` ist — der wird
vom outer `catch` zu `KycFailure(e.toString())` umgeleitet, aber die
user-sichtbare Message ist dann ein nackter Stack-Trace im
`kycFailureDescription`-i18n-Template.

`_continueKyc` liest jetzt direkt `KycSessionDto.currentStep` — das
Feld ist bereits autoritativ (wird schon fuer `urlOrToken` benutzt) und
mappt 1:1 auf die `KycInfoMapper.toDto`-Decision auf dem API. Fehlt
`currentStep` → `KycUnsupportedStepFailure(null)` statt `StateError`.

**Tests** in `kyc_cubit_test.dart`:
- `PendingReview` + kein required Step ueberhaupt → `KycUnsupportedStepFailure(null)`
- `PendingReview` + required Step ohne UI-Mapping (`additionalDocuments`) → `KycUnsupportedStepFailure(additionalDocuments)`
- `_continueKyc` konsumiert `KycSessionDto.currentStep` statt zu iterieren (bestehender Test umgeschrieben, plus Negativ-Fall fuer fehlendes `currentStep`)
- `urlOrToken` durchgereicht aus `currentStep.session.url`
…le erweitern

V45 ist mit der `_continueKyc`-Anpassung im selben PR geschlossen wie
V1/V2/V3/V5 (`docs/api-authority-plan.md` §W2.2). Audit-Eintrag
aktualisiert: Datei-Anker auf `_continueKyc` (statt der inzwischen
ueberholten Zeilennummer 208) und Closed-by-Hinweis ergaenzt, dass der
`StateError`-Pfad aus dem `firstWhere`-on-empty mit weg ist.

W2-Zeile in der `Shipped (2026-05-21)`-Tabelle erweitert: V45 in die
"Closes V-IDs"-Spalte aufgenommen, damit der Audit-Diff zur PR-RealUnitCH#494
deckungsgleich ist.
KycPageManager wertet das per-pushNamed extra Argument nicht mehr aus, seit
das Routing API-driven aus dem KycCubit kommt. Der requiredLevel-Pass in
sell_button und payment_additional_action_needed_button war damit toter
Argument-Transport. Beide Aufrufer raeumen das jetzt auf.
@TaprootFreak TaprootFreak force-pushed the refactor/kyc-cubit-api-driven branch from 897ec71 to a489a29 Compare May 21, 2026 18:25
@TaprootFreak
Copy link
Copy Markdown
Contributor

Rebase auf aktuelles develop (18cb528) + Polish-Fix:

Rebase: 4 Commits sauber on top of develop neu aufgesetzt, keine Konflikte (Konflikt-Hotspot buy_payment_info_cubit.dart / sell_payment_info_cubit.dart aus #493 hat den Branch nicht angefasst — die KYC-Cubit-Surface ist disjunkt zu den Quote-Validierungs-Änderungen).

Polish-Fix (Commit a489a29): KycPageManager ignoriert das per-pushNamed-extra-Argument seit dieser PR. Die zwei verbliebenen Aufrufer haben aber noch extra: state.requiredLevel / extra: paymentState.requiredLevel durchgereicht — toter Argument-Transport. Beide Stellen geräumt:

  • lib/screens/sell/widgets/sell_button.dart (kycRequired-Branch)
  • lib/screens/buy/widgets/payment_additional_action_needed_button.dart (kycRequired-Branch)

Lokal verifiziert:

  • flutter analyze — clean
  • flutter test1435 / 1435 passing

PR-Body zusätzlich aktualisiert: V5 (process-status-driven routing) und V45 (_continueKyc parallele Iteration) in den Closes-Block ergänzt, beide werden durch W2.2 wie in docs/api-authority-audit.md dokumentiert geschlossen.

Force-push war --force-with-lease auf den vorherigen Head (897ec71); keine fremden Commits überschrieben.

@TaprootFreak TaprootFreak merged commit c500495 into RealUnitCH:develop May 21, 2026
4 checks passed
TaprootFreak added a commit that referenced this pull request May 23, 2026
…ordering in-app (#499) (#530)

## Summary

Reverts #499 ("refactor(reference-data): consume /v1/legal-document,
/v1/company-info, country.displayOrder").

The companion API-side PR (DFXswiss/api#3734) has been **closed
unmerged** — the decision is to keep legal-document URLs, company
contact info and country ordering **client-side in the app** rather than
serving them from the API. `/v1/legal-document` and `/v1/company-info`
do not exist in the API, so the app owns this data.

## What this restores

- `legal_documents_config.dart` — the hardcoded registration-agreement
PDF URLs (`_registrationAgreementPdfUrls`, de/en) and the
`pdfUrls`-based `LegalDocumentConfig`.
- `settings_contact_page.dart` — the hardcoded RealUnit company contact
tiles (phone, email, website, imprint).
- `country_field.dart` — the hardcoded `['CH','DE','IT','FR']` priority
ordering.

## What this removes

- `DfxLegalDocumentService`, `DfxCompanyInfoService` and their DTOs (+
tests) — the API-consumption layer added by #499.
- The `Country.displayOrder` field and the `/v1/legal-document` +
`/v1/company-info` wiring.

## Not affected

DFXswiss/api#3732 and #3733 stay merged in the API; their app companions
(#494 KYC routing, #497 capabilities) are untouched. Only the
reference-data refactor is reverted.

## Verification

- `flutter analyze` — No issues found.
- `flutter test` — all 1504 tests pass.
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.

2 participants