commit f94b20d2c401fc6b0793b0aeb76815306b9aee0f parent a27d9ad049bb5d4cb70675fbbbe27bfb4c0d47be Author: Sebastian <sebasjm@taler-systems.com> Date: Mon, 12 Jan 2026 12:17:26 -0300 fix #10839 Diffstat:
12 files changed, 331 insertions(+), 76 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx b/packages/merchant-backoffice-ui/src/components/form/InputTaxes.tsx @@ -23,7 +23,7 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { h, VNode } from "preact"; import { useCallback, useState } from "preact/hooks"; import { undefinedIfEmpty } from "../../utils/table.js"; -import { FormProvider } from "./FormProvider.js"; +import { FormErrors, FormProvider } from "./FormProvider.js"; import { Input } from "./Input.js"; import { InputGroup } from "./InputGroup.js"; import { InputProps, useField } from "./useField.js"; @@ -41,7 +41,7 @@ export function InputTaxes<T>({ name, label }: Props<keyof T>): VNode { const [value, valueHandler] = useState<Partial<Entity>>({}); - const errors = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<Entity>>({ name: !value.name ? i18n.str`Required` : undefined, tax: !value.tax ? i18n.str`Required` diff --git a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx @@ -22,6 +22,8 @@ import { AmountString, Amounts, + GroupEntry, + PotEntry, TalerError, TalerMerchantApi, } from "@gnu-taler/taler-util"; @@ -32,7 +34,7 @@ import { useSessionContext } from "../../context/session.js"; import { useInstanceCategories } from "../../hooks/category.js"; import { undefinedIfEmpty } from "../../utils/table.js"; import { ErrorLoadingMerchant } from "../ErrorLoadingMerchant.js"; -import { FormProvider } from "../form/FormProvider.js"; +import { FormErrors, FormProvider } from "../form/FormProvider.js"; import { Input } from "../form/Input.js"; import { InputArray } from "../form/InputArray.js"; import { InputCurrency } from "../form/InputCurrency.js"; @@ -43,6 +45,7 @@ import { InputTaxes } from "../form/InputTaxes.js"; import { InputWithAddon } from "../form/InputWithAddon.js"; import { FragmentPersonaFlag } from "../menu/SideBar.js"; import { UIElement } from "../../hooks/preference.js"; +import { InputSelector } from "../form/InputSelector.js"; const TALER_SCREEN_ID = 23; @@ -55,6 +58,8 @@ interface Props { initial?: Partial<Entity>; alreadyExist?: boolean; categories: TalerMerchantApi.CategoryListEntry[]; + groups: TalerMerchantApi.GroupEntry[]; + pots: TalerMerchantApi.PotEntry[]; } export function ProductForm({ @@ -62,6 +67,8 @@ export function ProductForm({ initial, alreadyExist, categories, + groups = [], + pots = [], }: Props) { const { i18n } = useTranslationContext(); const { state } = useSessionContext(); @@ -72,6 +79,14 @@ export function ProductForm({ : categories .filter((c) => initial.categories?.indexOf(c.category_id) !== -1) .map((c) => ({ id: String(c.category_id), description: c.name })); + const groupMap = new Map<number, string>(); + for (const c of groups) { + groupMap.set(c.group_serial, c.group_name); + } + const potMap = new Map<number, string>(); + for (const c of pots) { + potMap.set(c.pot_serial, c.pot_name); + } const [value, valueHandler] = useState< Partial< @@ -102,7 +117,7 @@ export function ProductForm({ }, }); - const errors = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<Entity>>({ product_id: !value.product_id ? i18n.str`Required` : undefined, product_name: !value.product_name ? i18n.str`Required` : undefined, description: !value.description ? i18n.str`Required` : undefined, @@ -250,10 +265,31 @@ export function ProductForm({ return { description: cat.name, id: String(cat.category_id) }; }); }} + readonly={categories.length === 0} help={i18n.str`Search by category description or id`} tooltip={i18n.str`Categories where this product will be listed on.`} unique /> + <InputSelector<Entity> + name="money_pot_id" + label={i18n.str`Money pot`} + help={i18n.str`Search by money pot description or id`} + tooltip={i18n.str`Money pots where this product will be listed on.`} + values={[0, ...potMap.keys()]} + toStr={(p: number) => (!p ? i18n.str`Select one` : potMap.get(p)!)} + fromStr={(d) => Number.parseInt(d, 10)} + readonly={pots.length === 0} + /> + <InputSelector<Entity> + name="product_group_id" + label={i18n.str`Group`} + help={i18n.str`Search by group name or id`} + tooltip={i18n.str`Categories where this product will be listed on.`} + values={[0, ...groupMap.keys()]} + toStr={(g: number) => (!g ? i18n.str`Select one` : groupMap.get(g)!)} + fromStr={(d) => Number.parseInt(d, 10)} + readonly={groups.length === 0} + /> </FormProvider> </div> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx @@ -274,7 +274,7 @@ export function CreatePage({ onCreated, onBack }: Props): VNode { toStr={(str) => { if (str === "none") return i18n.str`Without authentication`; if (str === "bearer") return i18n.str`With token`; - return "With username and password"; + return i18n.str`With username and password`; }} /> {state.credit_facade_credentials?.type === "basic" ? ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/password/DetailPage.tsx @@ -26,7 +26,7 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormProvider } from "../../../components/form/FormProvider.js"; +import { FormErrors, FormProvider } from "../../../components/form/FormProvider.js"; import { Input } from "../../../components/form/Input.js"; import { undefinedIfEmpty } from "../../../utils/table.js"; @@ -58,7 +58,7 @@ export function DetailPage({ const { i18n } = useTranslationContext(); - const errors = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<State>>({ current: withoutCurrentPassword ? undefined : !form.current diff --git a/packages/merchant-backoffice-ui/src/paths/instance/pots/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/pots/update/UpdatePage.tsx @@ -20,31 +20,42 @@ */ import { + AmountParseError, + Amounts, + AmountString, assertUnreachable, HttpStatusCode, - TalerMerchantApi + InternationalizationAPI, + PotDetailResponse, + TalerMerchantApi, + TranslatedString, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, useLocalNotificationBetter, - useTranslationContext + useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormProvider } from "../../../../components/form/FormProvider.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { useSessionContext } from "../../../../context/session.js"; import { WithId } from "../../../../declaration.js"; +import { InputArray } from "../../../../components/form/InputArray.js"; +import { undefinedIfEmpty } from "../../../../utils/table.js"; const TALER_SCREEN_ID = 39; -type Entity = TalerMerchantApi.PotDetailResponse & WithId; +type Entity = TalerMerchantApi.PotModifyRequest; interface Props { onUpdated: () => void; onBack?: () => void; - moneyPot: Entity; + moneyPot: PotDetailResponse & WithId; } export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); @@ -52,44 +63,55 @@ export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { state: { token }, lib, } = useSessionContext(); - // FIXME: if the product list is big the will bring a lot of info - // const inventoryResult = useInstanceProducts(); - - // const inventory = - // !inventoryResult || - // inventoryResult instanceof TalerError || - // inventoryResult.type === "fail" - // ? [] - // : inventoryResult.body; - const [state, setState] = useState<Partial<Entity>>({ - ...moneyPot, + description: moneyPot.description, + expected_pot_totals: moneyPot.pot_totals, + new_pot_totals: moneyPot.pot_totals, + pot_name: moneyPot.pot_name, }); - - // useEffect(() => { - // if (!category || !category?.products || !token) return; - // const ps = category.products.map((prod) => { - // return lib.instance - // .getProductDetails(token, String(prod.product_id)) - // .then((res) => { - // return res.type === "fail" - // ? undefined - // : { - // id: String(prod.product_id), - // description: res.body.description, - // }; - // }); - // }); - // Promise.all(ps).then((all) => { - // const product_map = all.filter(notEmpty); - // setState({ ...state, product_map }); - // }); - // }, []); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const data = state as TalerMerchantApi.PotModifyRequest; + + function isAmountValid( + a: string, + i18n: InternationalizationAPI, + ): TranslatedString | undefined { + const s = Amounts.parseWithError(a); + if (s.type === "ok") return undefined; + switch (s.case) { + case AmountParseError.MISSING_CURRENCY: + return i18n.str`Missing currency name`; + case AmountParseError.BAD_CURRENCY: + return i18n.str`Currency name must be only letters`; + case AmountParseError.BAD_NUMBER: + return i18n.str`Value can only by number`; + case AmountParseError.TOO_HIGH: + return i18n.str`The value is too high`; + case AmountParseError.TOO_PRECISE: + return i18n.str`The value is too precise`; + } + } + + + let invalidAmount: TranslatedString | undefined = undefined; + const errors = undefinedIfEmpty<FormErrors<Entity>>({ + pot_name: !state.pot_name ? i18n.str`Required` : undefined, + new_pot_totals: + !state.new_pot_totals || !state.new_pot_totals.length + ? undefined + : state.new_pot_totals.find((a) => { + const res = isAmountValid(a, i18n); + if (!res) return undefined; + invalidAmount = i18n.str`Invalid amount "${a}": ${res}`; + return res; + }) !== undefined + ? invalidAmount + : undefined, + }); + const update = safeFunctionHandler( lib.instance.updateMoneyPot.bind(lib.instance), - !token ? undefined : [token, moneyPot.id, data], + !token || errors ? undefined : [token, moneyPot.id, data], ); update.onSuccess = onUpdated; update.onFail = (fail) => { @@ -130,7 +152,11 @@ export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { <section class="section is-main-section"> <div class="columns"> <div class="column is-four-fifths"> - <FormProvider object={state} valueHandler={setState}> + <FormProvider + object={state} + errors={errors} + valueHandler={setState} + > <Input<Entity> name="pot_name" label={i18n.str`Name`} @@ -141,10 +167,18 @@ export function UpdatePage({ moneyPot, onUpdated, onBack }: Props): VNode { label={i18n.str`Descripton`} // tooltip={i18n.str`Name of the category`} /> - {/* <Input<Entity> - name="pot_totals" + <InputArray<Entity> + name="new_pot_totals" label={i18n.str`Totals`} - /> */} + // placeholder={placeholder} + // help={help} + // tooltip={tooltip} + // toStr={(v?: string) => + // !v ? "" : v.replace(PAYTO_START_REGEX, "") + // } + // fromStr={(v: string) => `payto://${v}`} + unique + /> </FormProvider> <div class="buttons is-right mt-5"> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/create/CreatePage.tsx @@ -19,7 +19,13 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { assertUnreachable, HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + HttpStatusCode, + TalerError, + TalerErrorCode, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -33,6 +39,11 @@ import { useSessionContext } from "../../../../context/session.js"; import { useInstanceCategories } from "../../../../hooks/category.js"; import { Loading } from "../../../../components/exception/loading.js"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; +import { + useInstanceMoneyPots, + useMoneyPotDetails, +} from "../../../../hooks/pots.js"; +import { useInstanceProductGroups } from "../../../../hooks/groups.js"; export interface Props { onCreate: () => void; @@ -53,8 +64,14 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { switch (fail.case) { case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized.`; - case HttpStatusCode.NotFound: - return i18n.str`Not found.`; + case TalerErrorCode.MERCHANT_GENERIC_INSTANCE_UNKNOWN: + return i18n.str`The instance doesn't exist. Maybe it was remove while adding the product.`; + case TalerErrorCode.MERCHANT_GENERIC_CATEGORY_UNKNOWN: + return i18n.str`The category doesn't exist. Maybe it was remove while adding the product.`; + case TalerErrorCode.MERCHANT_GENERIC_PRODUCT_GROUP_UNKNOWN: + return i18n.str`The product group doesn't exist. Maybe it was remove while adding the product.`; + case TalerErrorCode.MERCHANT_GENERIC_MONEY_POT_UNKNOWN: + return i18n.str`The money pot doesn't exist. Maybe it was remove while adding the product.`; case HttpStatusCode.Conflict: return i18n.str`Conflict.`; default: @@ -63,17 +80,31 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { }; const { i18n } = useTranslationContext(); + const potsResult = useInstanceMoneyPots(); + const groupsResults = useInstanceProductGroups(); // FIXME: if the category list is big the will bring a lot of info // we could find a lazy way to add up on searches const categoriesResult = useInstanceCategories(); - if (!categoriesResult) return <Loading />; + if (!categoriesResult || !groupsResults || !potsResult) return <Loading />; if (categoriesResult instanceof TalerError) { return <ErrorLoadingMerchant error={categoriesResult} />; } const categories = categoriesResult.type === "fail" ? [] : categoriesResult.body.categories; + const groups = + !groupsResults || + groupsResults instanceof TalerError || + groupsResults.type === "fail" + ? [] + : groupsResults.body.groups; + const pots = + !potsResult || + potsResult instanceof TalerError || + potsResult.type === "fail" + ? [] + : potsResult.body.pots; return ( <div> <LocalNotificationBannerBulma notification={notification} /> @@ -81,7 +112,12 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <div class="columns"> <div class="column" /> <div class="column is-four-fifths"> - <ProductForm onSubscribe={setForm} categories={categories} /> + <ProductForm + onSubscribe={setForm} + categories={categories} + groups={groups} + pots={pots} + /> <div class="buttons is-right mt-5"> {onBack && ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/update/UpdatePage.tsx @@ -19,7 +19,12 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { assertUnreachable, HttpStatusCode, TalerError, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { + assertUnreachable, + HttpStatusCode, + TalerError, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, @@ -33,6 +38,8 @@ import { useSessionContext } from "../../../../context/session.js"; import { useInstanceCategories } from "../../../../hooks/category.js"; import { Loading } from "../../../../components/exception/loading.js"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; +import { useInstanceProductGroups } from "../../../../hooks/groups.js"; +import { useInstanceMoneyPots } from "../../../../hooks/pots.js"; const TALER_SCREEN_ID = 57; @@ -71,16 +78,30 @@ export function UpdatePage({ product, onBack, onConfirm }: Props): VNode { const { i18n } = useTranslationContext(); - // FIXME: if the category list is big the will bring a lot of info - // we could find a lazy way to add up on searches - const categoriesResult = useInstanceCategories(); - if (!categoriesResult) return <Loading />; - if (categoriesResult instanceof TalerError) { - return <ErrorLoadingMerchant error={categoriesResult} />; - } - const categories = - categoriesResult.type === "fail" ? [] : categoriesResult.body.categories; - + // FIXME: if the category list is big the will bring a lot of info + // we could find a lazy way to add up on searches + const categoriesResult = useInstanceCategories(); + const potsResult = useInstanceMoneyPots(); + const groupsResults = useInstanceProductGroups(); + const groups = + !groupsResults || + groupsResults instanceof TalerError || + groupsResults.type === "fail" + ? [] + : groupsResults.body.groups; + const pots = + !potsResult || + potsResult instanceof TalerError || + potsResult.type === "fail" + ? [] + : potsResult.body.pots; + + if (!categoriesResult || !groupsResults || !potsResult) return <Loading />; + if (categoriesResult instanceof TalerError) { + return <ErrorLoadingMerchant error={categoriesResult} />; + } + const categories = + categoriesResult.type === "fail" ? [] : categoriesResult.body.categories; return ( <div> @@ -105,7 +126,14 @@ export function UpdatePage({ product, onBack, onConfirm }: Props): VNode { <div class="columns"> <div class="column" /> <div class="column is-four-fifths"> - <ProductForm initial={product} onSubscribe={setForm} alreadyExist categories={categories}/> + <ProductForm + initial={product} + onSubscribe={setForm} + alreadyExist + groups={groups} + pots={pots} + categories={categories} + /> <div class="buttons is-right mt-5"> {onBack && ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -164,7 +164,7 @@ export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { const parsedPrice = !state.amount ? undefined : Amounts.parse(state.amount); - const errors: FormErrors<Entity> | undefined = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<Entity>>({ description: !state.description ? i18n.str`Required` : undefined, summary: !state.summary ? state.summary_editable diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx @@ -75,7 +75,7 @@ export function UsePage({ template.editable_defaults?.summary ?? template.template_contract.summary, }); - const errors: FormErrors<Entity> | undefined = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<Entity>>({ amount: !state.amount ? i18n.str`An amount is required` : undefined, summary: !state.summary ? i18n.str`An order summary is required` diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/DeletePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/DeletePage.tsx @@ -29,7 +29,7 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { FormProvider } from "../../../components/form/FormProvider.js"; +import { FormErrors, FormProvider } from "../../../components/form/FormProvider.js"; import { Input } from "../../../components/form/Input.js"; import { InputToggle } from "../../../components/form/InputToggle.js"; import { SolveMFAChallenges } from "../../../components/SolveMFA.js"; @@ -57,7 +57,7 @@ export function DeletePage({ instanceId, onBack, onDeleted }: Props): VNode { const { i18n } = useTranslationContext(); const { state: session, lib, logOut } = useSessionContext(); - const errors = undefinedIfEmpty({ + const errors = undefinedIfEmpty<FormErrors<State>>({ name: !form.name ? i18n.str`Required` : form.name !== instanceId diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -1206,10 +1206,23 @@ export class TalerMerchantInstanceHttpClient { ); return opEmptySuccess(); } - case HttpStatusCode.Unauthorized: // FIXME: missing in docs - return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.NotFound: // FIXME: missing in docs + case HttpStatusCode.Unauthorized: return opKnownHttpFailure(resp.status, resp); + case HttpStatusCode.NotFound: { + const details = await readTalerErrorResponse(resp); + switch (details.code) { + case TalerErrorCode.MERCHANT_GENERIC_PRODUCT_GROUP_UNKNOWN: + return opKnownTalerFailure(details.code, details); + case TalerErrorCode.MERCHANT_GENERIC_CATEGORY_UNKNOWN: + return opKnownTalerFailure(details.code, details); + case TalerErrorCode.MERCHANT_GENERIC_MONEY_POT_UNKNOWN: + return opKnownTalerFailure(details.code, details); + case TalerErrorCode.MERCHANT_GENERIC_INSTANCE_UNKNOWN: + return opKnownTalerFailure(details.code, details); + default: + return opUnknownHttpFailure(resp, details); + } + } case HttpStatusCode.Conflict: return opKnownHttpFailure(resp.status, resp); default: @@ -1264,6 +1277,7 @@ export class TalerMerchantInstanceHttpClient { category?: string; name?: string; description?: string; + groupId?: number; } = {}, ) { const url = new URL(`private/products`, this.baseUrl); @@ -1278,6 +1292,9 @@ export class TalerMerchantInstanceHttpClient { if (params.description) { url.searchParams.set("description_filter", params.description); } + if (params.groupId !== undefined) { + url.searchParams.set("product_group_serial", String(params.groupId)); + } const headers: Record<string, string> = {}; if (token) { diff --git a/packages/taler-util/src/taler-error-codes.ts b/packages/taler-util/src/taler-error-codes.ts @@ -393,6 +393,14 @@ export enum TalerErrorCode { /** + * The operating system failed to allocate required resources. Restarting services periodically can help, especially if Postgres is using excessive amounts of memory. Check with the system administrator to investigate. + * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500). + * (A value of 0 indicates that the error is generated client-side). + */ + GENERIC_OS_RESOURCE_ALLOCATION_FAILURE = 77, + + + /** * Exchange is badly configured and thus cannot operate. * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500). * (A value of 0 indicates that the error is generated client-side). @@ -785,6 +793,14 @@ export enum TalerErrorCode { /** + * The exchange is not aware of the given target account. The specified account is not a customer of this service. + * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). + * (A value of 0 indicates that the error is generated client-side). + */ + EXCHANGE_GENERIC_TARGET_ACCOUNT_UNKNOWN = 1049, + + + /** * The exchange did not find information about the specified transaction in the database. * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). * (A value of 0 indicates that the error is generated client-side). @@ -2641,6 +2657,46 @@ export enum TalerErrorCode { /** + * The report ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_GENERIC_REPORT_UNKNOWN = 2034, + + + /** + * The report ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_IMPLEMENTED (501). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_GENERIC_REPORT_GENERATOR_UNCONFIGURED = 2035, + + + /** + * The product group ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_GENERIC_PRODUCT_GROUP_UNKNOWN = 2036, + + + /** + * The money pod ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_GENERIC_MONEY_POT_UNKNOWN = 2037, + + + /** + * The session ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_GENERIC_SESSION_UNKNOWN = 2038, + + + /** * The exchange failed to provide a valid answer to the tracking request, thus those details are not in the response. * Returned with an HTTP status code of #MHD_HTTP_OK (200). * (A value of 0 indicates that the error is generated client-side). @@ -2889,11 +2945,11 @@ export enum TalerErrorCode { /** - * Legacy stuff. Remove me with protocol v1. - * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0). + * The refund request is too late because it is past the wire transfer deadline of the order. The merchant must find a different way to pay back the money to the customer. + * Returned with an HTTP status code of #MHD_HTTP_GONE (410). * (A value of 0 indicates that the error is generated client-side). */ - DEAD_QQQ_PAY_MERCHANT_POST_ORDERS_ID_ABORT_REFUND_REFUSED_PAYMENT_COMPLETE = 2169, + MERCHANT_PRIVATE_POST_REFUND_AFTER_WIRE_DEADLINE = 2169, /** @@ -3025,6 +3081,14 @@ export enum TalerErrorCode { /** + * Some of the exchanges involved refused the request for reasons related to legitimization. The wallet should try with coins of different exchanges. The merchant should check if they have some legitimization process pending at the exchange. + * Returned with an HTTP status code of #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS (451). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED = 2186, + + + /** * The contract hash does not match the given order ID. * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400). * (A value of 0 indicates that the error is generated client-side). @@ -3497,6 +3561,22 @@ export enum TalerErrorCode { /** + * The report ID provided to the backend is not known to the backend. + * Returned with an HTTP status code of #MHD_HTTP_NOT_IMPLEMENTED (501). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_REPORT_GENERATOR_FAILED = 2570, + + + /** + * Failed to fetch the data for the report from the backend. + * Returned with an HTTP status code of #MHD_HTTP_BAD_GATEWAY (502). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_REPORT_FETCH_FAILED = 2571, + + + /** * The merchant backend cannot create an instance under the given identifier as one already exists. Use PATCH to modify the existing entry. * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). * (A value of 0 indicates that the error is generated client-side). @@ -3609,7 +3689,7 @@ export enum TalerErrorCode { /** - * The deletion request is for a product that is locked. + * The deletion request is for a product that is locked. The product cannot be deleted until the existing offer to expires. * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). * (A value of 0 indicates that the error is generated client-side). */ @@ -3617,6 +3697,30 @@ export enum TalerErrorCode { /** + * The proposed name for the product group is already in use. You should select a different name. + * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_PRIVATE_PRODUCT_GROUP_CONFLICTING_NAME = 2690, + + + /** + * The proposed name for the money pot is already in use. You should select a different name. + * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_PRIVATE_MONEY_POT_CONFLICTING_NAME = 2691, + + + /** + * The total amount in the money pot is different from the amount required by the request. The client should fetch the current pot total and retry with the latest amount to succeed. + * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). + * (A value of 0 indicates that the error is generated client-side). + */ + MERCHANT_PRIVATE_MONEY_POT_CONFLICTING_TOTAL = 2692, + + + /** * The requested wire method is not supported by the exchange. * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). * (A value of 0 indicates that the error is generated client-side).