taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit a796e980e096e5be49a105325151fb7f2d076056
parent 9c0ebc8928de096f0a7ed05c75414050d1688688
Author: Florian Dold <florian@dold.me>
Date:   Tue, 22 Jul 2025 00:54:38 +0200

aml: rudimentary property rendering, default to existing props in decision

Diffstat:
Mpackages/aml-backoffice-ui/src/pages/AccountDetails.tsx | 27+++++++++++++++++++++++++++
Mpackages/aml-backoffice-ui/src/pages/decision/Properties.tsx | 248++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
2 files changed, 165 insertions(+), 110 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/AccountDetails.tsx b/packages/aml-backoffice-ui/src/pages/AccountDetails.tsx @@ -15,6 +15,7 @@ */ import { AbsoluteTime, + AccountProperties, assertUnreachable, HttpStatusCode, TalerError, @@ -44,6 +45,24 @@ import { BANK_RULES, WALLET_RULES } from "./decision/Rules.js"; const TALER_SCREEN_ID = 116; +export function ShowProperties(props: { properties?: AccountProperties }) { + const { properties } = props; + const keys = Object.keys(properties ?? {}); + if (!properties || keys.length == 0) { + return <span>No properties defined for this account.</span>; + } + return keys.map((x) => { + if (properties[x] == null) { + return null; + } + return ( + <p> + {x}: {properties[x]} + </p> + ); + }); +} + export function AccountDetails({ account, routeToShowCollectedInfo, @@ -158,6 +177,8 @@ export function AccountDetails({ : BANK_RULES.includes(r.operation_type); }); + console.log(activeDecision?.properties); + return ( <div class="min-w-60"> <header class="flex flex-col justify-between border-b border-white/5 px-4 py-4 sm:px-6 sm:py-6 lg:px-8 gap-2"> @@ -215,6 +236,12 @@ export function AccountDetails({ /> )} + <h1 class="mb-4 text-base font-semibold leading-6 text-black"> + <i18n.Translate>Current account properties</i18n.Translate> + </h1> + + <ShowProperties properties={activeDecision?.properties} /> + {!activeDecision ? ( <Fragment> <Attention title={i18n.str`No active decision found`} type="warning"> diff --git a/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx b/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx @@ -35,7 +35,7 @@ import { UIHandlerId, useExchangeApiContext, useForm, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { @@ -45,8 +45,6 @@ import { import { usePreferences } from "../../hooks/preferences.js"; import { DEFAULT_LIMITS_WHEN_NEW_ACCOUNT } from "./Rules.js"; -const TALER_SCREEN_ID = 104; - /** * Update account properties * @param param0 @@ -68,6 +66,8 @@ export function Properties(): VNode { dialect, ); + console.log(`calculated props`, calculatedProps); + const merged = Object.entries(calculatedProps).reduce( (prev, [key, value]) => { if (prev[key] === undefined && value !== undefined) { @@ -92,18 +92,28 @@ function officerMustCheckInvestigationState( return true; } -function ReloadForm({ merged }: { merged: Record<string, string | boolean> }): VNode { +function ReloadForm({ + merged, +}: { + merged: Record<string, string | boolean>; +}): VNode { const { i18n } = useTranslationContext(); const [request, updateRequest] = useCurrentDecisionRequest(); + const [pref] = usePreferences(); + const { config } = useExchangeApiContext(); + const dialect = + (pref.testingDialect ? undefined : config.config.aml_spa_dialect) ?? + AmlSpaDialect.TESTING; const design = propertiesForm( i18n, - propertiesByDialect(i18n, { + propertiesByDialect(i18n, dialect, { MANDATORY_INVESTIGATION_STATE: officerMustCheckInvestigationState( // @ts-expect-error data is the form - (request.attributes?.data ?? {}), + request.attributes?.data ?? {}, ), }), ); + // const [id, setId] = useState(new Date().getTime()); const customProps = Object.entries(request.custom_properties ?? {}).map( @@ -198,113 +208,128 @@ const propertiesForm = ( function propertiesByDialect( i18n: InternationalizationAPI, + dialect: AmlSpaDialect, options: { MANDATORY_INVESTIGATION_STATE?: boolean; } = {}, ): UIFormElementConfig[] { - return [ - { - id: TalerAmlProperties.FILE_NOTE, - label: i18n.str`Current note on the GWG File`, - type: "text", - }, - { - id: TalerAmlProperties.CUSTOMER_LABEL, - label: i18n.str`Customer name or internal alias.`, - type: "text", - }, - { - id: TalerAmlProperties.ACCOUNT_OPEN, - label: i18n.str`Is the account active for deposit and payments?`, - type: "toggle", - }, - { - id: TalerAmlProperties.ACCOUNT_IDLE, - label: i18n.str`Is the account idle?`, - help: i18n.str`No operation for a period of time`, - type: "toggle", - }, - { - id: TalerAmlProperties.PEP_DOMESTIC, - label: i18n.str`Does account belong to a domestic PEP?`, - type: "toggle", - }, - { - id: TalerAmlProperties.PEP_FOREIGN, - label: i18n.str`Does account belong to a foreign PEP?`, - type: "toggle", - }, - { - id: TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION, - label: i18n.str`Does account belong to a international organization PEP?`, - type: "toggle", - }, - { - id: TalerAmlProperties.HIGH_RISK_CUSTOMER, - label: i18n.str`Does account belong to a high risk customer?`, - type: "toggle", - }, - { - id: TalerAmlProperties.HIGH_RISK_COUNTRY, - label: i18n.str`Does account belong to a person from a high risk country?`, - type: "toggle", - }, - { - id: TalerAmlProperties.INVESTIGATION_STATE, - label: i18n.str`The MROS reporting state for the account.`, - type: "choiceStacked", - required: options.MANDATORY_INVESTIGATION_STATE, - choices: [ - { - label: i18n.str`None`, - value: "NONE", - }, - { - label: i18n.str`Pending investigation`, - value: "INVESTIGATION_PENDING", - }, - { - label: i18n.str`Investigated without suspicion`, - value: "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION", - }, - { - label: i18n.str`Reported simple suspicion`, - value: "REPORTED_SUSPICION_SIMPLE", - }, - { - label: i18n.str`Reported substantaited suspicion`, - value: "REPORTED_SUSPICION_SUBSTANTIATED", - }, - ], - }, - { - id: TalerAmlProperties.INVESTIGATION_TRIGGER, - label: i18n.str`Informal reason why the AML investigation was triggered`, - type: "text", - }, - { - id: TalerAmlProperties.SANCTION_LIST_BEST_MATCH, - label: i18n.str`Identifies the sanction list entry that the account matched against`, - type: "text", - help: i18n.str`Best match, does not mean it was a good match.`, - }, - { - id: TalerAmlProperties.SANCTION_LIST_RATING, - label: i18n.str`Score for how good the sanction list match was`, - type: "integer", - }, - { - id: TalerAmlProperties.SANCTION_LIST_CONFIDENCE, - label: i18n.str`Score for how much supporting data we had for the sanction list match`, - type: "integer", - }, - { - id: TalerAmlProperties.SANCTION_LIST_SUPPRESS, - label: i18n.str`Suppress flagging this account when it creates a hit on a sanctions list, this is a false-positive`, - type: "toggle", - threeState: true, - }, - ]; + if (dialect === AmlSpaDialect.TOPS) { + return [ + { + id: TalerAmlProperties.FILE_NOTE, + label: i18n.str`Current note on the GWG File`, + type: "text", + }, + { + id: TalerAmlProperties.CUSTOMER_LABEL, + label: i18n.str`Customer name or internal alias.`, + type: "text", + }, + { + id: TalerAmlProperties.ACCOUNT_OPEN, + label: i18n.str`Is the account active for deposit and payments?`, + type: "toggle", + }, + { + id: TalerAmlProperties.ACCOUNT_IDLE, + label: i18n.str`Is the account idle?`, + help: i18n.str`No operation for a period of time`, + type: "toggle", + }, + { + id: TalerAmlProperties.PEP_DOMESTIC, + label: i18n.str`Does account belong to a domestic PEP?`, + type: "toggle", + }, + { + id: TalerAmlProperties.PEP_FOREIGN, + label: i18n.str`Does account belong to a foreign PEP?`, + type: "toggle", + }, + { + id: TalerAmlProperties.PEP_INTERNATIONAL_ORGANIZATION, + label: i18n.str`Does account belong to a international organization PEP?`, + type: "toggle", + }, + { + id: TalerAmlProperties.HIGH_RISK_CUSTOMER, + label: i18n.str`Does account belong to a high risk customer?`, + type: "toggle", + }, + { + id: TalerAmlProperties.HIGH_RISK_COUNTRY, + label: i18n.str`Does account belong to a person from a high risk country?`, + type: "toggle", + }, + { + id: TalerAmlProperties.INVESTIGATION_STATE, + label: i18n.str`The MROS reporting state for the account.`, + type: "choiceStacked", + required: options.MANDATORY_INVESTIGATION_STATE, + choices: [ + { + label: i18n.str`None`, + value: "NONE", + }, + { + label: i18n.str`Pending investigation`, + value: "INVESTIGATION_PENDING", + }, + { + label: i18n.str`Investigated without suspicion`, + value: "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION", + }, + { + label: i18n.str`Reported simple suspicion`, + value: "REPORTED_SUSPICION_SIMPLE", + }, + { + label: i18n.str`Reported substantaited suspicion`, + value: "REPORTED_SUSPICION_SUBSTANTIATED", + }, + ], + }, + { + id: TalerAmlProperties.INVESTIGATION_TRIGGER, + label: i18n.str`Informal reason why the AML investigation was triggered`, + type: "text", + }, + { + id: TalerAmlProperties.SANCTION_LIST_BEST_MATCH, + label: i18n.str`Identifies the sanction list entry that the account matched against`, + type: "text", + help: i18n.str`Best match, does not mean it was a good match.`, + }, + { + id: TalerAmlProperties.SANCTION_LIST_RATING, + label: i18n.str`Score for how good the sanction list match was`, + type: "integer", + }, + { + id: TalerAmlProperties.SANCTION_LIST_CONFIDENCE, + label: i18n.str`Score for how much supporting data we had for the sanction list match`, + type: "integer", + }, + { + id: TalerAmlProperties.SANCTION_LIST_SUPPRESS, + label: i18n.str`Suppress flagging this account when it creates a hit on a sanctions list, this is a false-positive`, + type: "toggle", + threeState: true, + }, + ]; + } + + if (dialect === AmlSpaDialect.GLS) { + return [ + { + id: "GLS_CUSTOMER_ID", + label: i18n.str`GLS Customer ID`, + type: "text", + }, + ]; + } + + return []; } function calculatePropertiesBasedOnState( @@ -321,7 +346,10 @@ function calculatePropertiesBasedOnState( const formId = request.attributes?.formId; - if (!formId) return initial; + if (!formId) { + return state; + } + const FORM_ID = formId; function mergeProperties<T extends string>(