feat(accounts): expose upgrade verification status + fix evidence→ERPNext link mapping (ENG-608) - #515
Merged
Conversation
…Next link mapping (ENG-608)
Backend half of ENG-608.
Evidence → ERPNext link mapping. `Verification Evidence.document_type` and
`issuing_country` are Link fields (Identity Document Type / Country). Phase 0
wrote the raw GraphQL input ("passport", "JM"), which Frappe rejects on
insert. `evidenceRowToErpnext` now maps (kind, ISO-2) to the seeded registry
code (JM_PASSPORT, TT_DRIVERS_PERMIT, SV_DUI, ...) and ISO-2 to the Frappe
country name; unmapped values omit the Link field and log a warning without
PII. Codes and country names read back from ERPNext pass through, so the
retention job's rewrite round-trips.
Customer-facing status. `AccountUpgradeRequest.verification` returns
`{ status, reasonCode, reasonMessage, reviewedAt }` with
`AccountUpgradeVerificationStatus` = SUBMITTED | UNDER_REVIEW |
MORE_INFO_NEEDED | APPROVED | REJECTED, derived from the AUR decision plus
the latest ID Verification's review state. reasonMessage is the Decision
Reason's user_facing_message; reviewer_note is never exposed. Lookups never
fail the query.
New ErpNext reads: getIdVerificationByUpgradeRequest, getDecisionReason.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- evidenceRowToErpnext: pass read-back rows (rowName set) through verbatim instead of re-translating them through the local registry mirror; the retention job PUTs the full evidence table and would silently drop any operator-added document type / country. Spec added. - Stop requesting reviewer_note from ERPNext and drop it from IdVerificationSummary; it was never read and only leak surface on the object feeding the public resolver. - Remove the unused per-call Decision Reason cache parameter. - Collapse dead `case undefined/null` labels into `default`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
toFrappeCountry / toIdentityDocumentTypeCode still claimed the verbatim acceptance existed so read-back rows could round-trip, but read-back rows now bypass both mappers in evidenceRowToErpnext. State the real reason: a fresh GraphQL row may already carry a registry code / Frappe country name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backend half (PR 1) of ENG-608. Mobile (PR 2) consumes the new
verificationfield.What
1. Evidence → ERPNext link mapping fix (
src/services/frappe/models/IdVerification.ts)Verification Evidence.document_typeis a Link → Identity Document Type andissuing_countrya Link → Country (seeverification_evidence.jsonin frappe-flash-admin). Phase 0 (#500) wrote the raw GraphQL input ("passport","JM") into those fields. Frappe validates Links on insert, so any row carrying either value fails the wholePOST /api/resource/ID Verification.It has not bitten in prod because nothing sends typed evidence yet: today's only row is the legacy
idDocumentsynthesized asID_FRONT, which carries neither field. It would have bitten with the first mobile build that fillsdocumentType/issuingCountry, and because the IDV insert is non-fatal by design, the symptom would have been "upgrade request created, no ID Verification, warn log only".evidenceRowToErpnextnow maps(kind, ISO-2)→ the seeded registry code and ISO-2 → Frappe country name, built fromadmin_panel/setup.pyIDENTITY_DOCUMENT_TYPES(JM, KY, TT, BB, BS, SV; 17 codes). Kinds are matched case-insensitively with punctuation collapsed (drivers_license,Driver's Licence,national id card,dui,nids, ...). Unmapped values omit the Link field (rest of the row still lands) and log a warning with the kind and country only. Codes/country names read back from ERPNext pass through, so the retention job's full-table rewrite round-trips unchanged. Full table indocs/id-verification.md§3.2. New ERPNext reads (
ErpNext.ts):getIdVerificationByUpgradeRequest(name)(list filtered byupgrade_request,modified desc,limit_page_length: 1,nullwhen none) andgetDecisionReason(code)(detail GET,nullon 404). NewDecisionReasonQueryError; error-map entries for it andIdVerificationQueryError.3.
AccountUpgradeRequestmodel gainsdecisionReason/reviewedAt(both exist on the AUR doctype).4. GraphQL —
AccountUpgradeRequest.verification: AccountUpgradeVerification!:Derivation (pure,
src/app/accounts/upgrade-verification-status.ts):Reason prefers IDV
decision_reason, then AUR's. The field never fails the query: an IDV read error derives from the AUR alone; a Decision Reason error or unknown code returns the code with a null message (both warn).SDL and supergraph regenerated (
yarn write-sdl), both carry the new type and enum.Tests
IdVerification.spec.ts: registry table pinned to setup.py, 18 alias/country mappings, unmapped cases, PII-free warning, round-trip from ERPNext (+22)ErpNext.idVerification.spec.ts: URL/filters/fields/order asserted for both new reads, 404→null, error wrapping (+9)AccountUpgradeRequest.spec.ts: decision field hydration (+3)upgrade-verification-status.spec.ts(new): every AUR×IDV cell + exhaustiveness sweepget-upgrade-verification.spec.ts(new): reason preference, note never leaks, lookup failure paths, every returned status serializes through the real enumyarn tsc-check,yarn eslint-check,yarn check:sdl,typos, prettier on touched files: green. Touched specs +test/flash/unit/graphql: 48 suites / 597 tests green.Not in this PR
Mobile rendering of
verification(PR 2).schema.graphql/supergraph.graphqlwere already non-prettier on main (generated), left as generated.🤖 Generated with Claude Code