taler-typescript-core

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

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

GLS-specific sample rules

Diffstat:
Mpackages/aml-backoffice-ui/src/pages/decision/Rules.tsx | 170++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 133 insertions(+), 37 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx b/packages/aml-backoffice-ui/src/pages/decision/Rules.tsx @@ -15,6 +15,7 @@ */ import { AbsoluteTime, + AmlSpaDialect, AmountJson, Amounts, assertUnreachable, @@ -46,10 +47,9 @@ import { RulesInfo } from "../../components/RulesInfo.js"; import { ShowDecisionLimitInfo } from "../../components/ShowDecisionLimitInfo.js"; import { ShowDefaultRules } from "../../components/ShowDefaultRules.js"; import { useCurrentDecisionRequest } from "../../hooks/decision-request.js"; +import { usePreferences } from "../../hooks/preferences.js"; import { useServerMeasures } from "../../hooks/server-info.js"; -const TALER_SCREEN_ID = 103; - const DEFAULT_MEASURE_IF_NONE = ["VERBOTEN"]; export const DEFAULT_LIMITS_WHEN_NEW_ACCOUNT: LegitimizationRuleSet = { custom_measures: {}, @@ -276,6 +276,10 @@ function UpdateRulesForm({ defaultRules: KycRule[]; }): VNode { const { i18n } = useTranslationContext(); + const [pref] = usePreferences(); + const dialect = + (pref.testingDialect ? undefined : config.aml_spa_dialect) ?? + AmlSpaDialect.TESTING; const [request, updateRequest] = useCurrentDecisionRequest(); const [showAddRuleForm, setShowAddRuleForm] = useState(false); const measureList = !rootMeasures ? [] : Object.keys(rootMeasures); @@ -343,6 +347,61 @@ function UpdateRulesForm({ ? undefined : findRuleInconsistency(isWallet, currentRules); + const ButtonRulesBasicPlan = () => ( + <button + onClick={() => { + setRules(BASIC_PLAN(config.currency, isWallet)); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" + > + <i18n.Translate>Basic plan</i18n.Translate> + </button> + ); + + const ButtonRulesPremiumPlan = () => ( + <button + onClick={() => { + setRules(PREMIUM_PLAN(config.currency, isWallet)); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" + > + <i18n.Translate>Premium</i18n.Translate> + </button> + ); + + const ButtonRulesEcommerce = () => ( + <button + onClick={() => { + setRules(E_COMMERCE(config.currency, isWallet)); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" + > + <i18n.Translate>E-commerce</i18n.Translate> + </button> + ); + + const ButtonRulesPoS = () => ( + <button + onClick={() => { + setRules(POINT_OF_SALE(config.currency, isWallet)); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" + > + <i18n.Translate>Point-of-sale</i18n.Translate> + </button> + ); + + const ButtonRulesGlsMerchantRegisteredEcommerce = () => ( + <button + onClick={() => { + setRules(GLS_MERCHANT_REGISTERED_ECOMMERCE(config.currency, isWallet)); + }} + class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" + > + <i18n.Translate>GLS Registered E-Commerce Merchant</i18n.Translate> + </button> + ); + return ( <div> {!ruleInconsistency ? undefined : ( @@ -394,8 +453,8 @@ function UpdateRulesForm({ case "missing-bank-rules": { return ( <i18n.Translate> - The account is bank account and there are bank operation's - limits which are not limited: {BANK_RULES.join(",")} + The AML account is bank account, but not all account-related + operations are limited ({BANK_RULES.join(",")}). </i18n.Translate> ); } @@ -459,38 +518,19 @@ function UpdateRulesForm({ > <i18n.Translate>Freeze account</i18n.Translate> </button> - <button - onClick={() => { - setRules(BASIC_PLAN(config.currency, isWallet)); - }} - class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" - > - <i18n.Translate>Basic plan</i18n.Translate> - </button> - <button - onClick={() => { - setRules(PREMIUM_PLAN(config.currency, isWallet)); - }} - class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" - > - <i18n.Translate>Premium</i18n.Translate> - </button> - <button - onClick={() => { - setRules(E_COMMERCE(config.currency, isWallet)); - }} - class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" - > - <i18n.Translate>E-commerce</i18n.Translate> - </button> - <button - onClick={() => { - setRules(POINT_OF_SALE(config.currency, isWallet)); - }} - class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" - > - <i18n.Translate>Point-of-sale</i18n.Translate> - </button> + {dialect == AmlSpaDialect.TESTING ? ( + <> + <ButtonRulesBasicPlan /> + <ButtonRulesPremiumPlan /> + <ButtonRulesEcommerce /> + <ButtonRulesPoS /> + </> + ) : null} + {dialect == AmlSpaDialect.GLS ? ( + <> + <ButtonRulesGlsMerchantRegisteredEcommerce /> + </> + ) : null} <h2 class="mt-4 mb-2"> <i18n.Translate>Behavior on rule expiration</i18n.Translate> </h2> @@ -499,7 +539,7 @@ function UpdateRulesForm({ onClick={() => { expirationForm.model .getHandlerForAttributeKey("measure") - .onChange(limits.successor_measure ?? ""); + .onChange(defaultSuccessorMeasure); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600" > @@ -985,6 +1025,62 @@ const POINT_OF_SALE: TemplateRulesFunction = (currency, isWallet) => : BANK_RULES.includes(r.operation_type); }); +const GLS_MERCHANT_REGISTERED_ECOMMERCE: TemplateRulesFunction = ( + currency, + isWallet, +) => + [ + { + display_priority: 1, + measures: ["VERBOTEN"], + operation_type: LimitOperationType.withdraw, + threshold: Amounts.stringify(`${currency}:0`), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["VERBOTEN"], + operation_type: LimitOperationType.close, + threshold: Amounts.stringify(`${currency}:0`), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["VERBOTEN"], + operation_type: LimitOperationType.merge, + threshold: Amounts.stringify(`${currency}:0`), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["preserve-investigate"], + operation_type: LimitOperationType.deposit, + threshold: Amounts.stringify(`${currency}:100000`), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + { + display_priority: 1, + measures: ["preserve-investigate"], + operation_type: LimitOperationType.aggregate, + threshold: Amounts.stringify(`${currency}:100000`), + timeframe: Duration.toTalerProtocolDuration( + Duration.fromSpec({ months: 1 }), + ), + }, + ].filter((r) => { + return isWallet + ? WALLET_RULES.includes(r.operation_type) + : BANK_RULES.includes(r.operation_type); + }); + const E_COMMERCE: TemplateRulesFunction = (currency, isWallet) => [ {