commit bf3b89b911cac72fec9efa48c9e909a7f57f51bf
parent 49816cfa28712b91857519eeddb7724201e3ab16
Author: Sebastian <sebasjm@gmail.com>
Date: Mon, 3 Feb 2025 16:23:08 -0300
calculating pep based on vqf 902 4
Diffstat:
11 files changed, 665 insertions(+), 327 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/forms/index.ts b/packages/aml-backoffice-ui/src/forms/index.ts
@@ -13,9 +13,18 @@
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import type {
- FormMetadata,
- InternationalizationAPI,
+import {
+ VQF_902_1,
+ VQF_902_11,
+ VQF_902_12,
+ VQF_902_13,
+ VQF_902_14,
+ VQF_902_15,
+ VQF_902_4,
+ VQF_902_5,
+ VQF_902_9,
+ type FormMetadata,
+ type InternationalizationAPI,
} from "@gnu-taler/web-util/browser";
import { v1 as simplest } from "./simplest.js";
@@ -138,47 +147,65 @@ export const preloadedForms: (
id: "__simple_comment",
version: 1,
config: simplest(i18n),
- // },
- // {
- // label: i18n.str`Identification form`,
- // id: "vqf-902-1",
- // version: 1,
- // config: VQF_902_1(i18n),
- // }, {
- // label: i18n.str`Operational legal entity or partnership`,
- // id: "902.11e",
- // version: 1,
- // config: form_902_11e_v1(i18n),
- // }, {
- // label: i18n.str`Foundations`,
- // id: "902.12e",
- // version: 1,
- // config: form_902_12e_v1(i18n),
- // }, {
- // label: i18n.str`Declaration for trusts`,
- // id: "902.13e",
- // version: 1,
- // config: form_902_13e_v1(i18n),
- // }, {
- // label: i18n.str`Information on life insurance policies`,
- // id: "902.15e",
- // version: 1,
- // config: form_902_15e_v1(i18n),
- // }, {
- // label: i18n.str`Declaration of beneficial owner`,
- // id: "902.9e",
- // version: 1,
- // config: form_902_9e_v1(i18n),
- // }, {
- // label: i18n.str`Customer profile`,
- // id: "902.5e",
- // version: 1,
- // config: form_902_5e_v1(i18n),
- // }, {
- // label: i18n.str`Risk profile`,
- // id: "902.4e",
- // version: 1,
- // config: form_902_4e_v1(i18n),
+ },
+ {
+ label: i18n.str`Identification Form`,
+ description: i18n.str`The customer has to be identified on entering into a permanent business relationship or on concluding a cash transaction, which meets the according threshold.`,
+ id: "vqf-902-1",
+ version: 1,
+ config: VQF_902_1(i18n),
+ },
+ {
+ label: i18n.str`Risk Profile AMLA`,
+ id: "vqf-902-4",
+ version: 1,
+ config: VQF_902_4(i18n),
+ },
+ {
+ label: i18n.str`Customer Profile`,
+ description: i18n.str`The information below has to refer to the persons from whom the assets originate ultimately (e.g. beneficial owner of the assets, founder/creator of a trust or foundation). Is the customer an operational legal entity or partnership the information may refer to the entity itself (not to the controlling person), unless the entity holds the assets in trust for a third party.`,
+ id: "vqf-902-5",
+ version: 1,
+ config: VQF_902_5(i18n),
+ },
+ {
+ label: i18n.str`Declaration of identity of the beneficial owner (A)`,
+ id: "vqf-902-9",
+ version: 1,
+ config: VQF_902_9(i18n),
+ },
+ {
+ label: i18n.str`Establishing of the controlling person of operating legal entities and partnerships both not quoted on the stock exchange (K)`,
+ description: i18n.str`for operating legal entities and partnerships that are contracting partner as well as analogously for operating legal entities and partnership that are beneficial owners`,
+ id: "vqf-902-11",
+ version: 1,
+ config: VQF_902_11(i18n),
+ },
+ {
+ label: i18n.str`Foundations (as well as similar constructs) (S)`,
+ id: "vqf-902-12",
+ version: 1,
+ config: VQF_902_12(i18n),
+ },
+ {
+ label: i18n.str`Declaration for trusts (T)`,
+ id: "vqf-902-13",
+ version: 1,
+ config: VQF_902_13(i18n),
+ },
+ {
+ label: i18n.str`Special Clarifications`,
+ description: i18n.str`When a business relationship or transaction is associated with increased risk, appears unusual or evidence exists that the assets are the proceeds of a felony or a qualified tax offence, the member has to perform additional clarifications.`,
+ id: "vqf-902-14",
+ version: 1,
+ config: VQF_902_14(i18n),
+ },
+ {
+ label: i18n.str`Insurance policies (I)`,
+ description: i18n.str`Information of life insurance policies with separately managed accounts/securities accounts (so-called insurance wrappers)`,
+ id: "vqf-902-15",
+ version: 1,
+ config: VQF_902_15(i18n),
},
];
diff --git a/packages/aml-backoffice-ui/src/hooks/decision-request.ts b/packages/aml-backoffice-ui/src/hooks/decision-request.ts
@@ -38,7 +38,7 @@ export interface DecisionRequest {
new_measures: string[] | undefined;
deadline: AbsoluteTime | undefined;
onExpire_measures: string[] | undefined;
- properties: Record<string, any> | undefined;
+ properties: Record<string, boolean> | undefined;
custom_properties: Record<string, any> | undefined;
custom_events: string[] | undefined;
inhibit_events: string[] | undefined;
@@ -50,7 +50,7 @@ export const codecForDecisionRequest = (): Codec<DecisionRequest> =>
buildCodecForObject<DecisionRequest>()
.property("rules", codecOptional(codecForList(codecForKycRules())))
.property("deadline", codecOptional(codecForAbsoluteTime))
- .property("properties", codecForAny())
+ .property("properties", codecOptional(codecForMap(codecForBoolean())))
.property("custom_properties", codecForAny())
.property("justification", codecOptional(codecForString()))
.property("custom_events", codecOptional(codecForList(codecForString())))
diff --git a/packages/aml-backoffice-ui/src/pages/Dashboard.tsx b/packages/aml-backoffice-ui/src/pages/Dashboard.tsx
@@ -151,10 +151,34 @@ function EventMetrics({
function labelForEvent(name: AmlEventsName, i18n: InternationalizationAPI) {
switch (name) {
- case AmlEventsName.ACCOUNT_FROZEN:
- return i18n.str`Frozen accounts`;
- case AmlEventsName.ACCOUNT_PEP:
- return i18n.str`PEP persons`;
+ case AmlEventsName.ACCOUNT_OPENED:
+ return i18n.str`Account opened`;
+ case AmlEventsName.ACCOUNT_CLOSED:
+ return i18n.str`Account closed`;
+ case AmlEventsName.ACCOUNT_OPENED_HIGH_RISK:
+ return i18n.str`High risk account incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HIGH_RISK:
+ return i18n.str`High risk account removed`;
+ case AmlEventsName.ACCOUNT_OPENED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country removed`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART9:
+ return i18n.str`MROS reported by obligation`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART305:
+ return i18n.str`MROS reported by right`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_COMPLETED:
+ return i18n.str`Investigations after Art 6 Gwg completed`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_FAILED:
+ return i18n.str`Investigations after Art 6 Gwg failed`;
case AmlEventsName.TEST_EVENT_KEY_1:
return i18n.str`TEST_EVENT_KEY_1`;
case AmlEventsName.TEST_EVENT_KEY_2:
@@ -165,24 +189,6 @@ function labelForEvent(name: AmlEventsName, i18n: InternationalizationAPI) {
return i18n.str`TEST_EVENT_KEY_4`;
case AmlEventsName.TEST_EVENT_KEY_5:
return i18n.str`TEST_EVENT_KEY_5`;
- case AmlEventsName.TEST_EVENT_KEY_6:
- return i18n.str`TEST_EVENT_KEY_6`;
- case AmlEventsName.TEST_EVENT_KEY_7:
- return i18n.str`TEST_EVENT_KEY_7`;
- case AmlEventsName.TEST_EVENT_KEY_8:
- return i18n.str`TEST_EVENT_KEY_8`;
- case AmlEventsName.TEST_EVENT_KEY_9:
- return i18n.str`TEST_EVENT_KEY_9`;
- case AmlEventsName.TEST_EVENT_KEY_10:
- return i18n.str`TEST_EVENT_KEY_10`;
- case AmlEventsName.TEST_EVENT_KEY_11:
- return i18n.str`TEST_EVENT_KEY_11`;
- case AmlEventsName.TEST_EVENT_KEY_12:
- return i18n.str`TEST_EVENT_KEY_12`;
- case AmlEventsName.TEST_EVENT_KEY_13:
- return i18n.str`TEST_EVENT_KEY_13`;
- case AmlEventsName.TEST_EVENT_KEY_14:
- return i18n.str`TEST_EVENT_KEY_14`;
default: {
assertUnreachable(name);
}
@@ -194,10 +200,34 @@ function descriptionForEvent(
i18n: InternationalizationAPI,
): TranslatedString | undefined {
switch (name) {
- case AmlEventsName.ACCOUNT_FROZEN:
- return i18n.str`Accounts that can move funds`;
- case AmlEventsName.ACCOUNT_PEP:
- return i18n.str`Public exposed persons`;
+ case AmlEventsName.ACCOUNT_OPENED:
+ return i18n.str`Account opened`;
+ case AmlEventsName.ACCOUNT_CLOSED:
+ return i18n.str`Account closed`;
+ case AmlEventsName.ACCOUNT_OPENED_HIGH_RISK:
+ return i18n.str`High risk account incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HIGH_RISK:
+ return i18n.str`High risk account removed`;
+ case AmlEventsName.ACCOUNT_OPENED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country removed`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART9:
+ return i18n.str`MROS reported by obligation`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART305:
+ return i18n.str`MROS reported by right`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_COMPLETED:
+ return i18n.str`Investigations after Art 6 Gwg completed`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_FAILED:
+ return i18n.str`Investigations after Art 6 Gwg failed`;
case AmlEventsName.TEST_EVENT_KEY_1:
return i18n.str`TEST_EVENT_KEY_1`;
case AmlEventsName.TEST_EVENT_KEY_2:
@@ -208,24 +238,6 @@ function descriptionForEvent(
return i18n.str`TEST_EVENT_KEY_4`;
case AmlEventsName.TEST_EVENT_KEY_5:
return i18n.str`TEST_EVENT_KEY_5`;
- case AmlEventsName.TEST_EVENT_KEY_6:
- return i18n.str`TEST_EVENT_KEY_6`;
- case AmlEventsName.TEST_EVENT_KEY_7:
- return i18n.str`TEST_EVENT_KEY_7`;
- case AmlEventsName.TEST_EVENT_KEY_8:
- return i18n.str`TEST_EVENT_KEY_8`;
- case AmlEventsName.TEST_EVENT_KEY_9:
- return i18n.str`TEST_EVENT_KEY_9`;
- case AmlEventsName.TEST_EVENT_KEY_10:
- return i18n.str`TEST_EVENT_KEY_10`;
- case AmlEventsName.TEST_EVENT_KEY_11:
- return i18n.str`TEST_EVENT_KEY_11`;
- case AmlEventsName.TEST_EVENT_KEY_12:
- return i18n.str`TEST_EVENT_KEY_12`;
- case AmlEventsName.TEST_EVENT_KEY_13:
- return i18n.str`TEST_EVENT_KEY_13`;
- case AmlEventsName.TEST_EVENT_KEY_14:
- return i18n.str`TEST_EVENT_KEY_14`;
default: {
assertUnreachable(name);
}
diff --git a/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx b/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
@@ -30,6 +30,7 @@ import {
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { AmlEvent } from "./CaseDetails.js";
+import { useEffect } from "preact/hooks";
/**
* the exchange doesn't have a consistent api
@@ -97,17 +98,21 @@ export function ShowConsolidated({
: [],
};
- const { handler } = useForm(design, fixed);
+ const { handler, update } = useForm(design, fixed);
+
+ useEffect(() => {
+ update(fixed);
+ }, [until.t_ms]);
return <FormUI design={design} handler={handler} />;
}
interface Consolidated {
- aml: {
- state: TalerExchangeApi.AmlState;
- threshold: AmountJson;
- since: AbsoluteTime;
- };
+ // aml: {
+ // state: TalerExchangeApi.AmlState;
+ // threshold: AmountJson;
+ // since: AbsoluteTime;
+ // };
kyc: {
[field: string]: {
value: unknown;
@@ -117,39 +122,39 @@ interface Consolidated {
};
}
-function getConsolidated(
+export function getConsolidated(
history: AmlEvent[],
when: AbsoluteTime,
): Consolidated {
const initial: Consolidated = {
- aml: {
- state: TalerExchangeApi.AmlState.normal,
- threshold: {
- currency: "ARS",
- value: 1000,
- fraction: 0,
- },
- since: AbsoluteTime.never(),
- },
+ // aml: {
+ // state: TalerExchangeApi.AmlState.normal,
+ // threshold: {
+ // currency: "ARS",
+ // value: 1000,
+ // fraction: 0,
+ // },
+ // since: AbsoluteTime.never(),
+ // },
kyc: {},
};
return history.reduce((prev, cur) => {
- if (AbsoluteTime.cmp(when, cur.when) < 0) {
+ if (AbsoluteTime.cmp(when, cur.when) <= 0) {
return prev;
}
switch (cur.type) {
case "kyc-expiration": {
- cur.fields.forEach((field) => {
- delete prev.kyc[field];
- });
+ // cur.fields.forEach((field) => {
+ // delete prev.kyc[field];
+ // });
break;
}
case "aml-form": {
- prev.aml = {
- since: cur.when,
- state: cur.state,
- threshold: cur.threshold,
- };
+ // prev.aml = {
+ // since: cur.when,
+ // state: cur.state,
+ // threshold: cur.threshold,
+ // };
break;
}
case "kyc-collection": {
diff --git a/packages/aml-backoffice-ui/src/pages/decision/AmlDecisionRequestWizard.tsx b/packages/aml-backoffice-ui/src/pages/decision/AmlDecisionRequestWizard.tsx
@@ -90,7 +90,7 @@ export function AmlDecisionRequestWizard({
step,
onMove,
}: {
- account?: string;
+ account: string;
step?: WizardSteps;
onMove: (n: WizardSteps | undefined) => void;
}): VNode {
@@ -101,9 +101,9 @@ export function AmlDecisionRequestWizard({
case "rules":
return <Rules account={account} />;
case "properties":
- return <Properties />;
+ return <Properties account={account} />;
case "events":
- return <Events />;
+ return <Events account={account} />;
case "measures":
return <Measures />;
case "justification":
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Events.tsx b/packages/aml-backoffice-ui/src/pages/decision/Events.tsx
@@ -1,7 +1,9 @@
import {
+ AmlDecision,
AmlSpaDialect,
assertUnreachable,
MeasureInformation,
+ TalerError,
} from "@gnu-taler/taler-util";
import {
FormDesign,
@@ -15,16 +17,21 @@ import {
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
+import {
+ DecisionRequest,
+ useCurrentDecisionRequest,
+} from "../../hooks/decision-request.js";
import { usePreferences } from "../../hooks/preferences.js";
import { AML_EVENTS_INFO, AmlEventsName } from "./aml-events.js";
+import { useAccountActiveDecision } from "../../hooks/decisions.js";
/**
* Trigger additional events
* @param param0
* @returns
*/
-export function Events({}: {}): VNode {
+export function Events({ account }: { account: string }): VNode {
+ const activeDecision = useAccountActiveDecision(account);
const { i18n } = useTranslationContext();
const [request, _, updateRequest] = useCurrentDecisionRequest();
const [pref] = usePreferences();
@@ -34,26 +41,18 @@ export function Events({}: {}): VNode {
(pref.testingDialect ? undefined : config.config.aml_spa_dialect) ??
AmlSpaDialect.TESTING;
- const calculatedEvents = Object.entries(AML_EVENTS_INFO).reduce(
- (prev, [name, info]) => {
- const field = {
- id: name as UIHandlerId,
- type: "toggle",
- required: true,
- label: labelForEvent(name as AmlEventsName, i18n),
- } satisfies UIFormElementConfig;
+ const lastDecision =
+ !activeDecision ||
+ activeDecision instanceof TalerError ||
+ activeDecision.type === "fail"
+ ? undefined
+ : activeDecision.body;
- if (info.shouldBeTriggered(request, dialect)) {
- prev.on.push(field);
- } else {
- prev.off.push(field);
- }
- return prev;
- },
- { on: [], off: [] } as {
- on: UIFormElementConfig[];
- off: UIFormElementConfig[];
- },
+ const calculatedEvents = calculateEventsBasedOnState(
+ lastDecision,
+ request,
+ i18n,
+ dialect,
);
const design = formDesign(i18n, calculatedEvents.on);
@@ -143,10 +142,34 @@ const formDesign = (
function labelForEvent(event: AmlEventsName, i18n: InternationalizationAPI) {
switch (event) {
- case AmlEventsName.ACCOUNT_FROZEN:
- return i18n.str`new account frozen`;
- case AmlEventsName.ACCOUNT_PEP:
- return i18n.str`new exposed person`;
+ case AmlEventsName.ACCOUNT_OPENED:
+ return i18n.str`Account opened`;
+ case AmlEventsName.ACCOUNT_CLOSED:
+ return i18n.str`Account closed`;
+ case AmlEventsName.ACCOUNT_OPENED_HIGH_RISK:
+ return i18n.str`High risk account incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HIGH_RISK:
+ return i18n.str`High risk account removed`;
+ case AmlEventsName.ACCOUNT_OPENED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_DOMESTIC_PEP:
+ return i18n.str`Account from dometic PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_FOREIGN_PEP:
+ return i18n.str`Account from foreign PEP removed`;
+ case AmlEventsName.ACCOUNT_OPENED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country incorporated`;
+ case AmlEventsName.ACCOUNT_CLOSED_HR_COUNTRY:
+ return i18n.str`Account from high-risk country removed`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART9:
+ return i18n.str`MROS reported by obligation`;
+ case AmlEventsName.ACCOUNT_MROS_REPORTED_ART305:
+ return i18n.str`MROS reported by right`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_COMPLETED:
+ return i18n.str`Investigations after Art 6 Gwg completed`;
+ case AmlEventsName.ACCOUNT_INVESTIGATION_ART6_FAILED:
+ return i18n.str`Investigations after Art 6 Gwg failed`;
case AmlEventsName.TEST_EVENT_KEY_1:
return i18n.str`TEST_EVENT_KEY_1`;
case AmlEventsName.TEST_EVENT_KEY_2:
@@ -157,26 +180,38 @@ function labelForEvent(event: AmlEventsName, i18n: InternationalizationAPI) {
return i18n.str`TEST_EVENT_KEY_4`;
case AmlEventsName.TEST_EVENT_KEY_5:
return i18n.str`TEST_EVENT_KEY_5`;
- case AmlEventsName.TEST_EVENT_KEY_6:
- return i18n.str`TEST_EVENT_KEY_6`;
- case AmlEventsName.TEST_EVENT_KEY_7:
- return i18n.str`TEST_EVENT_KEY_7`;
- case AmlEventsName.TEST_EVENT_KEY_8:
- return i18n.str`TEST_EVENT_KEY_8`;
- case AmlEventsName.TEST_EVENT_KEY_9:
- return i18n.str`TEST_EVENT_KEY_9`;
- case AmlEventsName.TEST_EVENT_KEY_10:
- return i18n.str`TEST_EVENT_KEY_10`;
- case AmlEventsName.TEST_EVENT_KEY_11:
- return i18n.str`TEST_EVENT_KEY_11`;
- case AmlEventsName.TEST_EVENT_KEY_12:
- return i18n.str`TEST_EVENT_KEY_12`;
- case AmlEventsName.TEST_EVENT_KEY_13:
- return i18n.str`TEST_EVENT_KEY_13`;
- case AmlEventsName.TEST_EVENT_KEY_14:
- return i18n.str`TEST_EVENT_KEY_14`;
default: {
assertUnreachable(event);
}
}
}
+
+function calculateEventsBasedOnState(
+ currentState: AmlDecision | undefined,
+ request: DecisionRequest,
+ i18n: InternationalizationAPI,
+ dialect: AmlSpaDialect,
+) {
+ const init: {
+ on: UIFormElementConfig[];
+ off: UIFormElementConfig[];
+ } = { on: [], off: [] };
+ return Object.entries(AML_EVENTS_INFO).reduce((prev, [name, info]) => {
+ const field = {
+ id: name as UIHandlerId,
+ type: "toggle",
+ required: true,
+ label: labelForEvent(name as AmlEventsName, i18n),
+ } satisfies UIFormElementConfig;
+
+ if (
+ currentState &&
+ info.shouldBeTriggered(request, currentState, dialect)
+ ) {
+ prev.on.push(field);
+ } else {
+ prev.off.push(field);
+ }
+ return prev;
+ }, init);
+}
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx b/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx
@@ -1,24 +1,22 @@
import { MeasureInformation, TalerError } from "@gnu-taler/taler-util";
import {
- useTranslationContext,
- useForm,
- onComponentUnload,
+ FormDesign,
FormUI,
InternationalizationAPI,
- FormDesign,
+ onComponentUnload,
UIHandlerId,
- UIFormElementConfig,
- RecursivePartial,
+ useForm,
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
-import { ShowMeasuresToSelect } from "../CaseDetails.js";
-import { useServerMeasures } from "../../hooks/server-info.js";
import { useMemo } from "preact/hooks";
import {
CustomMeasures,
useCustomMeasures,
} from "../../hooks/custom-measures.js";
+import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
+import { useServerMeasures } from "../../hooks/server-info.js";
+import { ShowMeasuresToSelect } from "../CaseDetails.js";
/**
* Ask for more information, define new paths to proceed
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx b/packages/aml-backoffice-ui/src/pages/decision/Properties.tsx
@@ -1,40 +1,98 @@
import {
- useTranslationContext,
- useExchangeApiContext,
- useForm,
- onComponentUnload,
+ AbsoluteTime,
+ AmlDecision,
+ AmlSpaDialect,
+ Amounts,
+ KycAttributes,
+ LimitOperationType,
+ TalerError,
+} from "@gnu-taler/taler-util";
+import {
+ FormDesign,
FormUI,
InternationalizationAPI,
+ onComponentUnload,
+ TalerFormAttributes,
UIFormElementConfig,
UIHandlerId,
- TalerFormAttributes,
- FormDesign,
+ useExchangeApiContext,
+ useForm,
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
+import {
+ DecisionRequest,
+ useCurrentDecisionRequest,
+} from "../../hooks/decision-request.js";
+import { useAccountActiveDecision } from "../../hooks/decisions.js";
import { usePreferences } from "../../hooks/preferences.js";
-import { AmlSpaDialect } from "@gnu-taler/taler-util";
+import { useAccountInformation } from "../../hooks/account.js";
+import { getConsolidated } from "../ShowConsolidated.js";
+import { getEventsFromAmlHistory } from "../CaseDetails.js";
/**
* Update account properites
* @param param0
* @returns
*/
-export function Properties({}: {}): VNode {
- const { i18n } = useTranslationContext();
- const [request, _, updateRequest] = useCurrentDecisionRequest();
+export function Properties({ account }: { account: string }): VNode {
+ const activeDecision = useAccountActiveDecision(account);
+ const accountDetails = useAccountInformation(account);
+ const [request] = useCurrentDecisionRequest();
const { config } = useExchangeApiContext();
const [pref] = usePreferences();
- const design = propertiesForm(
- i18n,
- propertiesByDialect(
- i18n,
- pref.testingDialect ? "testing" : config.config.aml_spa_dialect,
- ),
+
+ const lastDecision =
+ !activeDecision ||
+ activeDecision instanceof TalerError ||
+ activeDecision.type === "fail"
+ ? undefined
+ : activeDecision.body;
+
+ const details =
+ !accountDetails ||
+ accountDetails instanceof TalerError ||
+ accountDetails.type === "fail"
+ ? undefined
+ : accountDetails.body;
+
+ const dialect =
+ (pref.testingDialect ? undefined : config.config.aml_spa_dialect) ??
+ AmlSpaDialect.TESTING;
+
+ const calculatedProps = calculatePropertiesBasedOnState(
+ lastDecision,
+ details,
+ request,
+ dialect,
);
+ const merged = Object.entries(calculatedProps).reduce(
+ (prev, [key, value]) => {
+ if (prev[key] === undefined) {
+ prev[key] = value;
+ }
+ return prev;
+ },
+ request.properties ?? {},
+ );
+ if (!details) {
+ return <div>loading...</div>;
+ }
+
+ return (
+ <div>
+ <ReloadForm merged={merged} />
+ </div>
+ );
+}
+
+function ReloadForm({ merged }: { merged: any }): VNode {
+ const { i18n } = useTranslationContext();
+ const [request, _, updateRequest] = useCurrentDecisionRequest();
+ const design = propertiesForm(i18n, propertiesByDialect(i18n));
const form = useForm<PropertiesForm>(design, {
- defined: request.properties,
+ defined: merged,
custom: Object.entries(request.custom_properties ?? {}).map(
([name, value]) => {
return { name, value };
@@ -45,7 +103,7 @@ export function Properties({}: {}): VNode {
onComponentUnload(() => {
updateRequest({
...request,
- properties: form.status.result.defined ?? {},
+ properties: form.status.result.defined as Record<string, boolean>,
custom_properties: (form.status.result.custom ?? []).reduce(
(prev, cur) => {
if (!cur || !cur.name || !cur.value) return prev;
@@ -56,7 +114,6 @@ export function Properties({}: {}): VNode {
),
});
});
-
return (
<div>
<FormUI design={design} handler={form.handler} />
@@ -65,7 +122,7 @@ export function Properties({}: {}): VNode {
}
export type PropertiesForm = {
- defined: { [name: string]: string };
+ defined: { [name: string]: boolean };
custom: { name: string; value: string }[];
};
@@ -77,7 +134,6 @@ export const propertiesForm = (
sections: [
{
title: i18n.str`Properties`,
- description: i18n.str`Default properties are defined by the server dialect`,
fields: props.map((f) =>
"id" in f ? { ...f, id: ("defined." + f.id) as UIHandlerId } : f,
),
@@ -111,86 +167,135 @@ export const propertiesForm = (
export function propertiesByDialect(
i18n: InternationalizationAPI,
- dialect: string | undefined,
+ // dialect: AmlSpaDialect,
): UIFormElementConfig[] {
- if (!dialect) return [];
- switch (dialect) {
- case AmlSpaDialect.TESTING: {
- return [
- {
- id: "ACCOUNT_PEP" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Public exposed person?`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_BUSINESS_DOMAIN" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Business domain`,
- // gana_type: "Text",
- type: "text",
- required: true,
- },
- ];
- }
- case AmlSpaDialect.GLS: {
- return [
- {
- id: "ACCOUNT_REPORTED" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Is PEP`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- ];
- }
- case AmlSpaDialect.TOPS: {
- return [
- {
- id: "ACCOUNT_FROZEN" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Frozen?`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_HIGH_RISK" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`High risk?`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_PEP" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Public exposed person?`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_REPORTED" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Is reported to authorities?`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_SANCTIONED" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Is PEP`,
- // gana_type: "Boolean",
- type: "toggle",
- required: true,
- },
- {
- id: "ACCOUNT_BUSINESS_DOMAIN" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
- label: i18n.str`Business domain`,
- // gana_type: "Boolean",
- type: "text",
- },
- ];
- }
- default: {
- return [];
- }
- }
+ // if (!dialect) return [];
+ return [
+ {
+ id: "AML_ACCOUNT_ACTIVE_DEPOSIT" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Is account active for deposit and payments?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_DOMESTIC_PEP" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Does account belong to a domestic PEP?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_FOREIGN_PEP" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Does account belong to a foreign PEP?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_HIGH_RISK_BUSINESS" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Does account belong to a high risk business?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_HIGH_RISK_COUNTRY" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Does account belong to a person from a high risk country?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_INVESTIGATION_ART6_COMPLETED" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Was succesfully investigated under Art 6 Gwg?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_INVESTIGATION_ART6_FAILED" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Was investigated under Art 6 Gwg and failed?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_MROS_REPORTED_ART305" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Was reported to MROS by obligation?`,
+ type: "toggle",
+ threeState: true,
+ },
+ {
+ id: "AML_MROS_REPORTED_ART9" satisfies keyof TalerFormAttributes.AccountProperties as UIHandlerId,
+ label: i18n.str`Was reported to MROS by right?`,
+ type: "toggle",
+ threeState: true,
+ },
+ ];
+}
+
+type PropName = keyof TalerFormAttributes.AccountProperties;
+
+type PartialRecord<K extends keyof any, T> = {
+ [P in K]?: T;
+};
+// 7N6GSTA1RA8XC6QKGJHS4WS7ZDBE5B01ZD8MZ6QXSTH9D2T73HTG
+function calculatePropertiesBasedOnState(
+ currentState: AmlDecision | undefined,
+ details: KycAttributes | undefined,
+ request: DecisionRequest,
+ dialect: AmlSpaDialect,
+): PartialRecord<PropName, boolean> {
+ const result: Record<PropName, boolean | undefined> = {
+ AML_ACCOUNT_ACTIVE_DEPOSIT: undefined,
+ AML_DOMESTIC_PEP: undefined,
+ AML_FOREIGN_PEP: undefined,
+ AML_HIGH_RISK_BUSINESS: undefined,
+ AML_HIGH_RISK_COUNTRY: undefined,
+ AML_INVESTIGATION_ART6_COMPLETED: undefined,
+ AML_INVESTIGATION_ART6_FAILED: undefined,
+ AML_MROS_REPORTED_ART305: undefined,
+ AML_MROS_REPORTED_ART9: undefined,
+ AML_NO_OPERATION_DURING_PERIOD: undefined,
+ };
+ if (!details) return result;
+
+ const hasRuleThatFreezeDeposit =
+ (request.rules ?? []).findIndex(
+ (r) =>
+ r.operation_type === LimitOperationType.deposit &&
+ Amounts.isZero(r.threshold),
+ ) === -1;
+
+ result.AML_ACCOUNT_ACTIVE_DEPOSIT = !hasRuleThatFreezeDeposit;
+
+ const events = [...details.details].sort((a, b) => {
+ return AbsoluteTime.cmp(
+ AbsoluteTime.fromProtocolTimestamp(a.collection_time),
+ AbsoluteTime.fromProtocolTimestamp(b.collection_time),
+ );
+ });
+ const consolidated = events.reduce(
+ (prev, cur) => {
+ const since = AbsoluteTime.fromProtocolTimestamp(cur.collection_time);
+ Object.entries(cur.attributes ?? {}).forEach(([key, value]) => {
+ prev[key] = { value: value.text, since };
+ });
+ return prev;
+ },
+ {} as {
+ [attrName: string]:
+ | {
+ value: unknown;
+ since: AbsoluteTime;
+ }
+ | undefined;
+ },
+ );
+
+ result.AML_HIGH_RISK_COUNTRY =
+ consolidated["HIGH_RISK_COUNTRY"]?.value === "true";
+ result.AML_DOMESTIC_PEP = consolidated["PEP_DOMESTIC"]?.value === "true";
+ console.log("AML_DOMESTIC_PEP", result.AML_DOMESTIC_PEP);
+ result.AML_FOREIGN_PEP = consolidated["PEP_FOREIGN"]?.value === "true";
+ result.AML_HIGH_RISK_BUSINESS =
+ consolidated["COUNTRY_RISK_BUSINESS_LEVEL"]?.value === "true";
+
+ // event.attributes[""];
+
+ return result;
}
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx b/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx
@@ -26,19 +26,21 @@ import { useServerMeasures } from "../../hooks/server-info.js";
import { ShowDecisionLimitInfo } from "../CaseDetails.js";
import { RulesInfo } from "../RulesInfo.js";
+const DEFAULT_MEASURE_IF_NONE = ["VERBOTEN"];
+
/**
* Defined new limits for the account
* @param param0
* @returns
*/
-export function Rules({ account }: { account?: string }): VNode {
+export function Rules({ account }: { account: string }): VNode {
const activeDecision = useAccountActiveDecision(account);
const { i18n } = useTranslationContext();
const { config } = useExchangeApiContext();
const [request, updateRequest] = useCurrentDecisionRequest();
const measures = useServerMeasures();
- // const [rules, setRules] = useState<KycRule[]>([]);
+
const measureList =
!measures || measures instanceof TalerError || measures.type === "fail"
? []
@@ -63,7 +65,7 @@ export function Rules({ account }: { account?: string }): VNode {
function addNewRule(nr: FormType) {
const result = !request.rules ? [] : [...request.rules];
const clean = (nr.measures ?? []).filter((m) => !!m);
- const measures = !clean.length ? ["VERBOTEN"] : clean;
+ const measures = !clean.length ? DEFAULT_MEASURE_IF_NONE : clean;
result.push({
timeframe: !nr.timeframe
? Duration.toTalerProtocolDuration(Duration.getForever())
diff --git a/packages/aml-backoffice-ui/src/pages/decision/aml-events.ts b/packages/aml-backoffice-ui/src/pages/decision/aml-events.ts
@@ -1,130 +1,182 @@
-import { AmlSpaDialect } from "@gnu-taler/taler-util";
+import { AmlDecision, AmlSpaDialect } from "@gnu-taler/taler-util";
import { TalerFormAttributes } from "@gnu-taler/web-util/browser";
import { DecisionRequest } from "../../hooks/decision-request.js";
export enum AmlEventsName {
- ACCOUNT_FROZEN = "ACCOUNT_FROZEN",
- ACCOUNT_PEP = "ACCOUNT_PEP",
+ ACCOUNT_OPENED = "ACCOUNT_OPENED",
+ ACCOUNT_CLOSED = "ACCOUNT_CLOSED",
+
+ ACCOUNT_OPENED_HIGH_RISK = "ACCOUNT_OPENED_HIGH_RISK",
+ ACCOUNT_CLOSED_HIGH_RISK = "ACCOUNT_CLOSED_HIGH_RISK",
+ ACCOUNT_OPENED_DOMESTIC_PEP = "ACCOUNT_OPENED_DOMESTIC_PEP",
+ ACCOUNT_CLOSED_DOMESTIC_PEP = "ACCOUNT_CLOSED_DOMESTIC_PEP",
+ ACCOUNT_OPENED_FOREIGN_PEP = "ACCOUNT_OPENED_FOREIGN_PEP",
+ ACCOUNT_CLOSED_FOREIGN_PEP = "ACCOUNT_CLOSED_FOREIGN_PEP",
+ ACCOUNT_OPENED_HR_COUNTRY = "ACCOUNT_OPENED_HR_COUNTRY",
+ ACCOUNT_CLOSED_HR_COUNTRY = "ACCOUNT_CLOSED_HR_COUNTRY",
+
+ ACCOUNT_MROS_REPORTED_ART9 = "ACCOUNT_MROS_REPORTED_ART9",
+ ACCOUNT_MROS_REPORTED_ART305 = "ACCOUNT_MROS_REPORTED_ART305",
+ ACCOUNT_INVESTIGATION_ART6_COMPLETED = "ACCOUNT_INVESTIGATION_ART6_COMPLETED",
+ ACCOUNT_INVESTIGATION_ART6_FAILED = "ACCOUNT_INVESTIGATION_ART6_FAILED",
+
TEST_EVENT_KEY_1 = "TEST_EVENT_KEY_1",
TEST_EVENT_KEY_2 = "TEST_EVENT_KEY_2",
TEST_EVENT_KEY_3 = "TEST_EVENT_KEY_3",
TEST_EVENT_KEY_4 = "TEST_EVENT_KEY_4",
TEST_EVENT_KEY_5 = "TEST_EVENT_KEY_5",
- TEST_EVENT_KEY_6 = "TEST_EVENT_KEY_6",
- TEST_EVENT_KEY_7 = "TEST_EVENT_KEY_7",
- TEST_EVENT_KEY_8 = "TEST_EVENT_KEY_8",
- TEST_EVENT_KEY_9 = "TEST_EVENT_KEY_9",
- TEST_EVENT_KEY_10 = "TEST_EVENT_KEY_10",
- TEST_EVENT_KEY_11 = "TEST_EVENT_KEY_11",
- TEST_EVENT_KEY_12 = "TEST_EVENT_KEY_12",
- TEST_EVENT_KEY_13 = "TEST_EVENT_KEY_13",
- TEST_EVENT_KEY_14 = "TEST_EVENT_KEY_14",
}
export type EventMapInfo = {
[name in AmlEventsName]: {
// fieldLabel: TranslatedString;
dialect: AmlSpaDialect[];
- shouldBeTriggered: (req: DecisionRequest, d: AmlSpaDialect) => boolean;
+ shouldBeTriggered: (
+ req: DecisionRequest,
+ status: AmlDecision,
+ d: AmlSpaDialect,
+ ) => boolean;
};
};
export const AML_EVENTS_INFO: EventMapInfo = {
- ACCOUNT_FROZEN: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_OPENED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
if (!req.properties) return false;
return !!(req.properties as TalerFormAttributes.AccountProperties)
- .ACCOUNT_FROZEN;
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- ACCOUNT_PEP: {
- dialect: [AmlSpaDialect.GLS, AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ ACCOUNT_CLOSED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
if (!req.properties) return false;
return !!(req.properties as TalerFormAttributes.AccountProperties)
- .ACCOUNT_PEP;
+ .AML_DOMESTIC_PEP;
},
},
- TEST_EVENT_KEY_1: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_OPENED_HIGH_RISK: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- TEST_EVENT_KEY_2: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_CLOSED_HIGH_RISK: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
},
},
- TEST_EVENT_KEY_3: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_OPENED_DOMESTIC_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- TEST_EVENT_KEY_4: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_CLOSED_DOMESTIC_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
},
},
- TEST_EVENT_KEY_5: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_OPENED_FOREIGN_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- TEST_EVENT_KEY_6: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_CLOSED_FOREIGN_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
},
},
- TEST_EVENT_KEY_7: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_OPENED_HR_COUNTRY: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- TEST_EVENT_KEY_8: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_CLOSED_HR_COUNTRY: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
},
},
- TEST_EVENT_KEY_9: {
- dialect: [AmlSpaDialect.TESTING],
+ ACCOUNT_MROS_REPORTED_ART305: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
- return true;
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ ACCOUNT_MROS_REPORTED_ART9: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeTriggered(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ ACCOUNT_INVESTIGATION_ART6_COMPLETED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeTriggered(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ ACCOUNT_INVESTIGATION_ART6_FAILED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeTriggered(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
},
},
- TEST_EVENT_KEY_10: {
+ TEST_EVENT_KEY_1: {
dialect: [AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
return true;
},
},
- TEST_EVENT_KEY_11: {
+ TEST_EVENT_KEY_2: {
dialect: [AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
return true;
},
},
- TEST_EVENT_KEY_12: {
+ TEST_EVENT_KEY_3: {
dialect: [AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
return true;
},
},
- TEST_EVENT_KEY_13: {
+ TEST_EVENT_KEY_4: {
dialect: [AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
return true;
},
},
- TEST_EVENT_KEY_14: {
+ TEST_EVENT_KEY_5: {
dialect: [AmlSpaDialect.TESTING],
shouldBeTriggered(req, dialect) {
return true;
diff --git a/packages/aml-backoffice-ui/src/pages/decision/aml-properties.ts b/packages/aml-backoffice-ui/src/pages/decision/aml-properties.ts
@@ -0,0 +1,102 @@
+import {
+ AccountProperties,
+ AmlDecision,
+ AmlSpaDialect,
+} from "@gnu-taler/taler-util";
+import { TalerFormAttributes } from "@gnu-taler/web-util/browser";
+import { DecisionRequest } from "../../hooks/decision-request.js";
+
+export type PropertiesMapInfo = {
+ [name in keyof TalerFormAttributes.AccountProperties]: {
+ // fieldLabel: TranslatedString;
+ dialect: AmlSpaDialect[];
+ shouldBeOn: (
+ req: DecisionRequest,
+ status: AmlDecision,
+ d: AmlSpaDialect,
+ ) => boolean;
+ };
+};
+
+export const AML_PROPERTIES_INFO: PropertiesMapInfo = {
+ AML_ACCOUNT_ACTIVE_DEPOSIT: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ AML_DOMESTIC_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ AML_FOREIGN_PEP: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ AML_HIGH_RISK_BUSINESS: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ AML_HIGH_RISK_COUNTRY: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ AML_INVESTIGATION_ART6_COMPLETED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ AML_INVESTIGATION_ART6_FAILED: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ AML_MROS_REPORTED_ART305: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+ AML_MROS_REPORTED_ART9: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_ACCOUNT_ACTIVE_DEPOSIT;
+ },
+ },
+ AML_NO_OPERATION_DURING_PERIOD: {
+ dialect: [AmlSpaDialect.TOPS, AmlSpaDialect.TESTING],
+ shouldBeOn(req, dialect) {
+ if (!req.properties) return false;
+ return !!(req.properties as TalerFormAttributes.AccountProperties)
+ .AML_DOMESTIC_PEP;
+ },
+ },
+};