gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-typescript-core] branch master updated (e63dfd6bb -> 23b999c4e)


From: Admin
Subject: [taler-typescript-core] branch master updated (e63dfd6bb -> 23b999c4e)
Date: Mon, 17 Feb 2025 21:49:49 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository taler-typescript-core.

    from e63dfd6bb wallet cli: implement wait flag for testing withdrawal
     new e7055cac4 new api
     new 23b999c4e working on transfers view #9548

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../aml-backoffice-ui/src/ExchangeAmlFrame.tsx     |   6 +
 packages/aml-backoffice-ui/src/Routing.tsx         |   6 +
 .../src/hooks/{decisions.ts => transfers.ts}       |  82 ++------
 packages/aml-backoffice-ui/src/pages/Cases.tsx     |  17 ++
 .../src/pages/Transfers.tsx}                       | 219 +++++++++++++--------
 packages/taler-util/src/http-client/exchange.ts    |  86 +++++++-
 packages/taler-util/src/types-taler-exchange.ts    |  34 ++++
 7 files changed, 301 insertions(+), 149 deletions(-)
 copy packages/aml-backoffice-ui/src/hooks/{decisions.ts => transfers.ts} (72%)
 copy packages/{bank-ui/src/components/Transactions/views.tsx => 
aml-backoffice-ui/src/pages/Transfers.tsx} (59%)

diff --git a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx 
b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
index 2c08f7346..fa75980a8 100644
--- a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
+++ b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
@@ -39,6 +39,7 @@ import {
   PeopleIcon,
   SearchIcon,
   ToInvestigateIcon,
+  TransfersIcon,
 } from "./pages/Cases.js";
 
 /**
@@ -251,6 +252,11 @@ function Navigation(): VNode {
       Icon: ToInvestigateIcon,
       label: i18n.str`Investigation`,
     },
+    {
+      route: privatePages.transfers,
+      Icon: TransfersIcon,
+      label: i18n.str`Transfers`,
+    },
     { route: privatePages.active, Icon: HomeIcon, label: i18n.str`Active` },
     {
       route: privatePages.search,
diff --git a/packages/aml-backoffice-ui/src/Routing.tsx 
b/packages/aml-backoffice-ui/src/Routing.tsx
index c27ee9aef..2820b7bbe 100644
--- a/packages/aml-backoffice-ui/src/Routing.tsx
+++ b/packages/aml-backoffice-ui/src/Routing.tsx
@@ -45,6 +45,7 @@ import {
 import { useCurrentDecisionRequest } from "./hooks/decision-request.js";
 import { Dashboard } from "./pages/Dashboard.js";
 import { NewMeasure } from "./pages/NewMeasure.js";
+import { Transfers } from "./pages/Transfers.js";
 
 export function Routing(): VNode {
   const session = useOfficer();
@@ -127,6 +128,7 @@ export const privatePages = {
   measures: urlPattern(/\/measures/, () => "#/measures"),
   search: urlPattern(/\/search/, () => "#/search"),
   investigation: urlPattern(/\/investigation/, () => "#/investigation"),
+  transfers: urlPattern(/\/transfers/, () => "#/transfers"),
   active: urlPattern(/\/active/, () => "#/active"),
   caseUpdate: urlPattern<{ cid: string; type: string }>(
     /\/case\/(?<cid>[a-zA-Z0-9]+)\/new\/(?<type>[a-zA-Z0-9_.]+)/,
@@ -293,6 +295,10 @@ function PrivateRouting(): VNode {
     case "dashboard": {
       return <Dashboard routeToDownloadStats={privatePages.statsDownload} />;
     }
+    case "transfers": {
+      return <Transfers />;
+    }
+
     default:
       assertUnreachable(location);
   }
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts 
b/packages/aml-backoffice-ui/src/hooks/transfers.ts
similarity index 72%
copy from packages/aml-backoffice-ui/src/hooks/decisions.ts
copy to packages/aml-backoffice-ui/src/hooks/transfers.ts
index a6c95098d..bbb50f414 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/transfers.ts
@@ -34,54 +34,19 @@ export const PAGINATED_LIST_SIZE = 10;
 // and use it to know if there are more to request
 export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1;
 
-/**
- * @param account
- * @param args
- * @returns
- */
-export function useCurrentDecisionsUnderInvestigation() {
-  const officer = useOfficer();
-  const session = officer.state === "ready" ? officer.account : undefined;
-  const {
-    lib: { exchange: api },
-  } = useExchangeApiContext();
-
-  const [offset, setOffset] = useState<string>();
-
-  async function fetcher([officer, offset, investigation]: [
-    OfficerAccount,
-    string | undefined,
-    boolean | undefined,
-  ]) {
-    return await api.getAmlDecisions(officer, {
-      order: "dec",
-      offset,
-      investigation: true,
-      active: true,
-      limit: PAGINATED_LIST_REQUEST,
-    });
-  }
-
-  const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
-    TalerHttpError
-  >(!session ? undefined : [session, offset, "getAmlDecisions"], fetcher);
-
-  if (error) return error;
-  if (data === undefined) return undefined;
-  if (data.type !== "ok") return data;
-
-  return buildPaginatedResult(data.body.records, offset, setOffset, (d) =>
-    String(d.rowid),
+export function revalidateAccountDecisions() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getAmlDecisions",
+    undefined,
+    { revalidate: true },
   );
 }
 
 /**
- * @param account
  * @param args
  * @returns
  */
-export function useCurrentDecisions() {
+export function useTransferDebit() {
   const officer = useOfficer();
   const session = officer.state === "ready" ? officer.account : undefined;
   const {
@@ -92,44 +57,35 @@ export function useCurrentDecisions() {
 
   async function fetcher([officer, offset]: [
     OfficerAccount,
+    string,
     string | undefined,
-    boolean | undefined,
   ]) {
-    return await api.getAmlDecisions(officer, {
+    return await api.getTransfersDebit(officer, {
       order: "dec",
       offset,
-      active: true,
       limit: PAGINATED_LIST_REQUEST,
     });
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerExchangeResultByMethod<"getTransfersDebit">,
     TalerHttpError
-  >(!session ? undefined : [session, offset, "getAmlDecisions"], fetcher);
+  >(!session ? undefined : [session, offset, "getTransfersDebit"], fetcher);
 
   if (error) return error;
   if (data === undefined) return undefined;
   if (data.type !== "ok") return data;
 
-  return buildPaginatedResult(data.body.records, offset, setOffset, (d) =>
+  return buildPaginatedResult(data.body.transfers, offset, setOffset, (d) =>
     String(d.rowid),
   );
 }
 
-export function revalidateAccountDecisions() {
-  return mutate(
-    (key) => Array.isArray(key) && key[key.length - 1] === "getAmlDecisions",
-    undefined,
-    { revalidate: true },
-  );
-}
 /**
- * @param account
  * @param args
  * @returns
  */
-export function useAccountDecisions(accountStr: string) {
+export function useTransferCredit() {
   const officer = useOfficer();
   const session = officer.state === "ready" ? officer.account : undefined;
   const {
@@ -138,32 +94,28 @@ export function useAccountDecisions(accountStr: string) {
 
   const [offset, setOffset] = useState<string>();
 
-  async function fetcher([officer, account, offset]: [
+  async function fetcher([officer, offset]: [
     OfficerAccount,
     string,
     string | undefined,
   ]) {
-    return await api.getAmlDecisions(officer, {
+    return await api.getTransfersCredit(officer, {
       order: "dec",
       offset,
-      account,
       limit: PAGINATED_LIST_REQUEST,
     });
   }
 
   const { data, error } = useSWR<
-    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerExchangeResultByMethod<"getTransfersCredit">,
     TalerHttpError
-  >(
-    !session ? undefined : [session, accountStr, offset, "getAmlDecisions"],
-    fetcher,
-  );
+  >(!session ? undefined : [session, offset, "getTransfersCredit"], fetcher);
 
   if (error) return error;
   if (data === undefined) return undefined;
   if (data.type !== "ok") return data;
 
-  return buildPaginatedResult(data.body.records, offset, setOffset, (d) =>
+  return buildPaginatedResult(data.body.transfers, offset, setOffset, (d) =>
     String(d.rowid),
   );
 }
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx 
b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index 69632d1b2..96175dc53 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -358,6 +358,23 @@ export const ToInvestigateIcon = () => (
   </svg>
 );
 
+export const TransfersIcon = () => (
+  <svg
+    xmlns="http://www.w3.org/2000/svg";
+    fill="none"
+    viewBox="0 0 24 24"
+    stroke-width="1.5"
+    stroke="currentColor"
+    class="size-6"
+  >
+    <path
+      stroke-linecap="round"
+      stroke-linejoin="round"
+      d="M7.5 21 3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 
7.5H7.5"
+    />
+  </svg>
+);
+
 export const PeopleIcon = () => (
   <svg
     xmlns="http://www.w3.org/2000/svg";
diff --git a/packages/bank-ui/src/components/Transactions/views.tsx 
b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
similarity index 59%
copy from packages/bank-ui/src/components/Transactions/views.tsx
copy to packages/aml-backoffice-ui/src/pages/Transfers.tsx
index 8e3be72f8..2c35d1e5f 100644
--- a/packages/bank-ui/src/components/Transactions/views.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
@@ -1,40 +1,80 @@
-/*
- This file is part of GNU Taler
- (C) 2022-2024 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- 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 {
   Attention,
+  Loading,
   Time,
-  useBankCoreApiContext,
+  useExchangeApiContext,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useTransferCredit } from "../hooks/transfers.js";
+import {
+  AbsoluteTime,
+  AmountJson,
+  Amounts,
+  assertUnreachable,
+  CurrencySpecification,
+  HttpStatusCode,
+  TalerError,
+} from "@gnu-taler/taler-util";
+import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
+import { Officer } from "./Officer.js";
 import { format } from "date-fns";
-import { Fragment, VNode, h } from "preact";
-import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
-import { State } from "./index.js";
 
-const TALER_SCREEN_ID = 5;
-
-export function ReadyView({
-  transactions,
-  routeCreateWireTransfer,
-  onGoNext,
-  onGoStart,
-}: State.Ready): VNode {
+export function Transfers(): VNode {
   const { i18n, dateLocale } = useTranslationContext();
-  const { config } = useBankCoreApiContext();
+  const { config } = useExchangeApiContext();
+  const resp = useTransferCredit();
+  const isDebit = true; //FIXME: shoud be an option debit/credit
+
+  if (!resp) {
+    return <Loading />;
+  }
+  if (resp instanceof TalerError) {
+    return <ErrorLoadingWithDebug error={resp} />;
+  }
+  if (resp.type === "fail") {
+    switch (resp.case) {
+      case HttpStatusCode.Forbidden:
+        return (
+          <Fragment>
+            <Attention type="danger" title={i18n.str`Operation denied`}>
+              <i18n.Translate>
+                This account signature is invalid, contact administrator or
+                create a new one.
+              </i18n.Translate>
+            </Attention>
+            <Officer />
+          </Fragment>
+        );
+      case HttpStatusCode.NotFound:
+        return (
+          <Fragment>
+            <Attention type="danger" title={i18n.str`Operation denied`}>
+              <i18n.Translate>
+                The designated AML account is not known, contact administrator
+                or create a new one.
+              </i18n.Translate>
+            </Attention>
+            <Officer />
+          </Fragment>
+        );
+      case HttpStatusCode.Conflict:
+        return (
+          <Fragment>
+            <Attention type="danger" title={i18n.str`Operation denied`}>
+              <i18n.Translate>
+                The designated AML account is not enabled, contact 
administrator
+                or create a new one.
+              </i18n.Translate>
+            </Attention>
+            <Officer />
+          </Fragment>
+        );
+      default:
+        assertUnreachable(resp);
+    }
+  }
+  const transactions = resp.body;
 
   if (!transactions.length) {
     return (
@@ -42,14 +82,15 @@ export function ReadyView({
         <div class="sm:flex sm:items-center">
           <div class="sm:flex-auto">
             <h1 class="text-base font-semibold leading-6 text-gray-900">
-              <i18n.Translate>Transactions history</i18n.Translate>
+              <i18n.Translate>Transfers history</i18n.Translate>
             </h1>
           </div>
         </div>
 
-        <Attention type="low" title={i18n.str`No transactions yet.`}>
+        <Attention type="low" title={i18n.str`No transfers yet.`}>
           <i18n.Translate>
-            You can make a transfer or a withdrawal to your wallet.
+            There are no transfer reported by the exchange with the current
+            threshold.
           </i18n.Translate>
         </Attention>
       </div>
@@ -59,9 +100,11 @@ export function ReadyView({
   const txByDate = transactions.reduce(
     (prev, cur) => {
       const d =
-        cur.when.t_ms === "never"
+        cur.execution_time.t_s === "never"
           ? ""
-          : format(cur.when.t_ms, "dd/MM/yyyy", { locale: dateLocale });
+          : format(cur.execution_time.t_s * 1000, "dd/MM/yyyy", {
+              locale: dateLocale,
+            });
       if (!prev[d]) {
         prev[d] = [];
       }
@@ -70,12 +113,15 @@ export function ReadyView({
     },
     {} as Record<string, typeof transactions>,
   );
+
+  const onGoNext = resp.isLastPage ? undefined : resp.loadNext;
+  const onGoStart = resp.isFirstPage ? undefined : resp.loadFirst;
   return (
     <div class="px-4 mt-8">
       <div class="sm:flex sm:items-center">
         <div class="sm:flex-auto">
           <h1 class="text-base font-semibold leading-6 text-gray-900">
-            <i18n.Translate>Transactions history</i18n.Translate>
+            <i18n.Translate>Transfers history</i18n.Translate>
           </h1>
         </div>
       </div>
@@ -95,10 +141,10 @@ export function ReadyView({
                 scope="col"
                 class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm 
font-semibold text-gray-900 "
               >{i18n.str`Counterpart`}</th>
-              <th
+              {/* <th
                 scope="col"
                 class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm 
font-semibold text-gray-900 "
-              >{i18n.str`Subject`}</th>
+              >{i18n.str`Subject`}</th> */}
             </tr>
           </thead>
           <tbody>
@@ -124,7 +170,9 @@ export function ReadyView({
                           <div class="font-medium text-gray-900">
                             <Time
                               format="HH:mm:ss"
-                              timestamp={item.when}
+                              timestamp={AbsoluteTime.fromProtocolTimestamp(
+                                item.execution_time,
+                              )}
                               // relative={Duration.fromSpec({ days: 1 })}
                             />
                           </div>
@@ -133,19 +181,15 @@ export function ReadyView({
                               <i18n.Translate>Amount</i18n.Translate>
                             </dt>
                             <dd class="mt-1 truncate text-gray-700">
-                              {item.negative
-                                ? i18n.str`sent`
-                                : i18n.str`received`}{" "}
+                              {isDebit ? i18n.str`sent` : 
i18n.str`received`}{" "}
                               {item.amount ? (
                                 <span
-                                  data-negative={
-                                    item.negative ? "true" : "false"
-                                  }
+                                  data-negative={isDebit ? "true" : "false"}
                                   class="data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600"
                                 >
                                   <RenderAmount
-                                    value={item.amount}
-                                    spec={config.currency_specification}
+                                    value={Amounts.parseOrThrow(item.amount)}
+                                    spec={config.config.currency_specification}
                                   />
                                 </span>
                               ) : (
@@ -159,38 +203,26 @@ export function ReadyView({
                               <i18n.Translate>Counterpart</i18n.Translate>
                             </dt>
                             <dd class="mt-1 truncate text-gray-500 sm:hidden">
-                              {item.negative ? i18n.str`to` : 
i18n.str`from`}{" "}
-                              {!routeCreateWireTransfer ? (
-                                item.counterpart
-                              ) : (
-                                <a
-                                  name={`transfer to ${item.counterpart}`}
-                                  href={routeCreateWireTransfer.url({
-                                    account: item.counterpart,
-                                  })}
-                                  class="text-indigo-600 hover:text-indigo-900"
-                                >
-                                  {item.counterpart}
-                                </a>
-                              )}
+                              {isDebit ? i18n.str`to` : i18n.str`from`}{" "}
+                              {item.payto_uri}
                             </dd>
-                            <dd class="mt-1 text-gray-500 sm:hidden">
+                            {/* <dd class="mt-1 text-gray-500 sm:hidden">
                               <pre class="break-words w-56 
whitespace-break-spaces p-2 rounded-md mx-auto my-2 bg-gray-100">
                                 {item.subject}
                               </pre>
-                            </dd>
+                            </dd> */}
                           </dl>
                         </td>
                         <td
-                          data-negative={item.negative ? "true" : "false"}
+                          data-negative={isDebit ? "true" : "false"}
                           class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500 "
                         >
                           {item.amount ? (
                             <RenderAmount
-                              value={item.amount}
-                              negative={item.negative}
+                              value={Amounts.parseOrThrow(item.amount)}
+                              negative={isDebit}
                               withColor
-                              spec={config.currency_specification}
+                              spec={config.config.currency_specification}
                             />
                           ) : (
                             <span style={{ color: "grey" }}>
@@ -200,23 +232,11 @@ export function ReadyView({
                           )}
                         </td>
                         <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">
-                          {!routeCreateWireTransfer ? (
-                            item.counterpart
-                          ) : (
-                            <a
-                              name={`wire transfer to ${item.counterpart}`}
-                              href={routeCreateWireTransfer.url({
-                                account: item.counterpart,
-                              })}
-                              class="text-indigo-600 hover:text-indigo-900"
-                            >
-                              {item.counterpart}
-                            </a>
-                          )}
+                          {item.payto_uri}
                         </td>
-                        <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500 break-all min-w-md">
+                        {/* <td class="hidden sm:table-cell px-3 py-3.5 
text-sm text-gray-500 break-all min-w-md">
                           {item.subject}
-                        </td>
+                        </td> */}
                       </tr>
                     );
                   })}
@@ -253,3 +273,40 @@ export function ReadyView({
     </div>
   );
 }
+
+/**
+ * send to web-utils
+ * @param param0
+ * @returns
+ */
+export function RenderAmount({
+  value,
+  spec,
+  negative,
+  withColor,
+  hideSmall,
+}: {
+  spec: CurrencySpecification;
+  value: AmountJson;
+  hideSmall?: boolean;
+  negative?: boolean;
+  withColor?: boolean;
+}): VNode {
+  const neg = !!negative; // convert to true or false
+
+  const { currency, normal, small } = Amounts.stringifyValueWithSpec(
+    value,
+    spec,
+  );
+
+  return (
+    <span
+      data-negative={withColor ? neg : undefined}
+      class="whitespace-nowrap data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600"
+    >
+      {negative ? "- " : undefined}
+      {currency} {normal}{" "}
+      {!hideSmall && small && <sup class="-ml-1">{small}</sup>}
+    </span>
+  );
+}
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
index 8c928aac5..38c9d9c35 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Codec, codecForAny } from "../codec.js";
 import {
   HttpRequestLibrary,
   readSuccessResponseJsonOrThrow,
@@ -35,7 +36,6 @@ import {
   opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
-import { Codec, codecForAny } from "../codec.js";
 import {
   TalerSignaturePurpose,
   amountToBuffer,
@@ -71,6 +71,7 @@ import {
   codecForEventCounter,
   codecForExchangeConfig,
   codecForExchangeKeysResponse,
+  codecForExchangeTransferList,
   codecForKycProcessClientInformation,
   codecForKycProcessStartInformation,
   codecForLegitimizationNeededResponse,
@@ -78,10 +79,11 @@ import {
 import { CacheEvictor, addPaginationParams, nullEvictor } from "./utils.js";
 
 import { TalerError } from "../errors.js";
-import { TalerErrorCode } from "../taler-error-codes.js";
-import { codecForEmptyObject } from "../types-taler-wallet.js";
 import { canonicalJson } from "../helpers.js";
+import { AmountJson, Amounts } from "../index.js";
+import { TalerErrorCode } from "../taler-error-codes.js";
 import { AbsoluteTime } from "../time.js";
+import { codecForEmptyObject } from "../types-taler-wallet.js";
 
 export type TalerExchangeResultByMethod<
   prop extends keyof TalerExchangeHttpClient,
@@ -1020,6 +1022,84 @@ export class TalerExchangeHttpClient {
     }
   }
 
+  /**
+   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-credit
+   *
+   */
+  async getTransfersCredit(
+    auth: OfficerAccount,
+    params: PaginationParams & { threshold?: AmountJson } = {},
+  ) {
+    const url = new URL(`aml/${auth.id}/transfers-credit`, this.baseUrl);
+
+    addPaginationParams(url, params);
+
+    if (params.threshold) {
+      url.searchParams.set("threshold", Amounts.stringify(params.threshold));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+      headers: {
+        "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey),
+      },
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForExchangeTransferList());
+      case HttpStatusCode.NoContent:
+        return opFixedSuccess({ transfers: [] });
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+    }
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-exchange.html#get--aml-$OFFICER_PUB-transfers-debit
+   *
+   */
+  async getTransfersDebit(
+    auth: OfficerAccount,
+    params: PaginationParams & { threshold?: AmountString } = {},
+  ) {
+    const url = new URL(`aml/${auth.id}/transfers-debit`, this.baseUrl);
+
+    addPaginationParams(url, params);
+
+    if (params.threshold) {
+      url.searchParams.set("threshold", Amounts.stringify(params.threshold));
+    }
+
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "GET",
+      headers: {
+        "Taler-AML-Officer-Signature": buildAMLQuerySignature(auth.signingKey),
+      },
+    });
+
+    switch (resp.status) {
+      case HttpStatusCode.Ok:
+        return opSuccessFromHttp(resp, codecForExchangeTransferList());
+      case HttpStatusCode.NoContent:
+        return opFixedSuccess({ transfers: [] });
+      case HttpStatusCode.Forbidden:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.NotFound:
+        return opKnownHttpFailure(resp.status, resp);
+      case HttpStatusCode.Conflict:
+        return opKnownHttpFailure(resp.status, resp);
+      default:
+        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
+    }
+  }
+
   // RESERVE control
 
   /**
diff --git a/packages/taler-util/src/types-taler-exchange.ts 
b/packages/taler-util/src/types-taler-exchange.ts
index 0f874a4ef..09d5a65cc 100644
--- a/packages/taler-util/src/types-taler-exchange.ts
+++ b/packages/taler-util/src/types-taler-exchange.ts
@@ -34,6 +34,7 @@ import {
   codecForCurrencySpecificiation,
   codecForEither,
   codecForMap,
+  codecForPaytoString,
   codecForURN,
   codecOptionalDefault,
   strcmp,
@@ -2551,6 +2552,39 @@ export const codecForKycProcessClientInformation =
       )
       .build("TalerExchangeApi.KycProcessClientInformation");
 
+export const codecForExchangeTransferList = (): Codec<ExchangeTransferList> =>
+  buildCodecForObject<ExchangeTransferList>()
+    .property("transfers", codecForList(codecForExchangeTransferListEntry()))
+    .build("TalerExchangeApi.ExchangeTransferList");
+
+export const codecForExchangeTransferListEntry =
+  (): Codec<ExchangeTransferListEntry> =>
+    buildCodecForObject<ExchangeTransferListEntry>()
+      .property("rowid", codecForNumber())
+      .property("payto_uri", codecForPaytoString())
+      .property("amount", codecForAmountString())
+      .property("execution_time", codecForTimestamp)
+      .build("TalerExchangeApi.ExchangeTransferListEntry");
+
+export interface ExchangeTransferList {
+  // Matching transaction of the exchange
+  transfers: ExchangeTransferListEntry[];
+}
+
+export interface ExchangeTransferListEntry {
+  // Row ID of the record.  Used to filter by offset.
+  rowid: Integer;
+
+  // payto://-URI of the other account.
+  payto_uri: string;
+
+  // The amount involved.
+  amount: Amount;
+
+  // Time when the transfer was made
+  execution_time: Timestamp;
+}
+
 export interface KycProcessStartInformation {
   // URL to open.
   redirect_url: string;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]