taler-typescript-core

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

commit d4ded9f3ca6ec266d1a5a4f13043112c3946b4b0
parent 8a6c2a75208d5fc6d7f994d88299cee5e2c2a6c5
Author: Sebastian <sebasjm@gmail.com>
Date:   Tue,  3 Jun 2025 17:29:31 -0300

fix #9677

Diffstat:
Mpackages/aml-backoffice-ui/src/hooks/decision-request.ts | 6++++++
Mpackages/aml-backoffice-ui/src/pages/decision/AmlDecisionRequestWizard.tsx | 2+-
Mpackages/aml-backoffice-ui/src/pages/decision/Properties.tsx | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mpackages/taler-util/src/aml/properties.ts | 2+-
Mpackages/web-util/src/forms/gana/VQF_902_14.ts | 3+++
5 files changed, 65 insertions(+), 16 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/hooks/decision-request.ts b/packages/aml-backoffice-ui/src/hooks/decision-request.ts @@ -78,6 +78,10 @@ export interface DecisionRequest { */ properties: Record<string, boolean | string> | undefined; /** + * Errors on the properties + */ + properties_errors : object | undefined; + /** * If given all the information, this account need to be investigated */ keep_investigating: boolean | undefined; @@ -130,6 +134,7 @@ export const codecForDecisionRequest = (): Codec<DecisionRequest> => .property("rules", codecOptional(codecForList(codecForKycRules()))) .property("deadline", codecOptional(codecForAbsoluteTime)) .property("properties", codecOptional(codecForMap(codecForAny()))) + .property("properties_errors", codecForAny()) .property("attributes", codecOptional(codecForAccountAttributes())) .property("custom_properties", codecForAny()) .property("justification", codecOptional(codecForString())) @@ -153,6 +158,7 @@ const DECISION_REQUEST_EMPTY: DecisionRequest = { custom_events: undefined, attributes: undefined, accountName: undefined, + properties_errors: undefined, triggering_events: undefined, justification: undefined, keep_investigating: undefined, diff --git a/packages/aml-backoffice-ui/src/pages/decision/AmlDecisionRequestWizard.tsx b/packages/aml-backoffice-ui/src/pages/decision/AmlDecisionRequestWizard.tsx @@ -81,7 +81,7 @@ export function isAttributesCompleted(request: DecisionRequest): boolean { ); } export function isPropertiesCompleted(request: DecisionRequest): boolean { - return request.properties !== undefined; + return request.properties !== undefined && request.properties_errors === undefined; } export function isEventsCompleted(request: DecisionRequest): boolean { return request.custom_events !== undefined; diff --git a/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx b/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx @@ -7,20 +7,24 @@ import { PropertiesDerivation_TOPS, PropertiesDerivationFunctionByPropertyName, TalerAmlProperties, - TOPS_AccountProperties + TalerFormAttributes, + TOPS_AccountProperties, } from "@gnu-taler/taler-util"; import { + ErrorsSummary, FormDesign, FormUI, InternationalizationAPI, onComponentUnload, UIFormElementConfig, UIHandlerId, + undefinedIfEmpty, useExchangeApiContext, useForm, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { DecisionRequest, useCurrentDecisionRequest, @@ -34,7 +38,7 @@ import { DEFAULT_LIMITS_WHEN_NEW_ACCOUNT } from "./Rules.js"; * @returns */ export function Properties({}: {}): VNode { - const [request] = useCurrentDecisionRequest(); + const [request, updateRequest] = useCurrentDecisionRequest(); const { config } = useExchangeApiContext(); const [pref] = usePreferences(); @@ -51,8 +55,8 @@ export function Properties({}: {}): VNode { const merged = Object.entries(calculatedProps).reduce( (prev, [key, value]) => { - if (prev[key] === undefined) { - prev[key] = !!value; + if (prev[key] === undefined && value !== undefined) { + prev[key] = value; } return prev; }, @@ -65,18 +69,37 @@ export function Properties({}: {}): VNode { ); } +function officerMustCheckInvestigationState( + data: Record<keyof typeof TalerFormAttributes, string>, +): boolean { + if (data[TalerFormAttributes.FORM_ID] !== "vqf_902_14") return false; + if (data[TalerFormAttributes.INCRISK_RESULT] !== "OTHER") return false; + return true; +} + function ReloadForm({ merged }: { merged: any }): VNode { const { i18n } = useTranslationContext(); const [request, updateRequest] = useCurrentDecisionRequest(); - const design = propertiesForm(i18n, propertiesByDialect(i18n)); + const design = propertiesForm( + i18n, + propertiesByDialect(i18n, { + MANDATORY_INVESTIGATION_STATE: officerMustCheckInvestigationState( + (request.attributes?.data ?? {}) as any, + ), + }), + ); + // const [id, setId] = useState(new Date().getTime()); + + const customProps = Object.entries(request.custom_properties ?? {}).map( + ([name, value]) => { + return { name, value }; + }, + ); const form = useForm<PropertiesForm>(design, { defined: merged, - custom: Object.entries(request.custom_properties ?? {}).map( - ([name, value]) => { - return { name, value }; - }, - ), + custom: customProps, }); + const errors = form.status.errors; onComponentUnload(() => { updateRequest("unload properties", { @@ -89,10 +112,25 @@ function ReloadForm({ merged }: { merged: any }): VNode { }, {} as Record<string, string>, ), + properties_errors: errors, }); }); + return ( <div> + <button + onClick={() => { + form.update({ + custom: customProps, + defined: merged, + }); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm disabled:bg-gray-500 bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" + > + <i18n.Translate>Reset to default</i18n.Translate> + </button> + + {!errors ? undefined : <ErrorsSummary errors={errors.defined as any} />} <FormUI design={design} model={form.model} /> </div> ); @@ -144,9 +182,10 @@ export const propertiesForm = ( function propertiesByDialect( i18n: InternationalizationAPI, - // dialect: AmlSpaDialect, + options: { + MANDATORY_INVESTIGATION_STATE?: boolean; + } = {}, ): UIFormElementConfig[] { - // if (!dialect) return []; return [ { id: TalerAmlProperties.FILE_NOTE, @@ -198,6 +237,7 @@ function propertiesByDialect( 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`, @@ -230,7 +270,7 @@ function propertiesByDialect( 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`, + help: i18n.str`Best match, does not mean it was a good match.`, }, { id: TalerAmlProperties.SANCTION_LIST_RATING, diff --git a/packages/taler-util/src/aml/properties.ts b/packages/taler-util/src/aml/properties.ts @@ -180,7 +180,7 @@ export const PropertiesDerivation_TOPS: PropertiesDerivationFunctionByPropertyNa if ( attributes[TalerFormAttributes.INCRISK_RESULT] === "OTHER" ) { - return "INVESTIGATION_PENDING"; + return null as any; } } diff --git a/packages/web-util/src/forms/gana/VQF_902_14.ts b/packages/web-util/src/forms/gana/VQF_902_14.ts @@ -31,6 +31,9 @@ export function VQF_902_14( help: i18n.str`Description of the circumstances/transactions, which triggered the special clarifications`, type: "textArea", required: true, + validator(text, form) { + return !text ? i18n.str`can't be empty` : undefined + }, }, ], },