taler-typescript-core

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

commit b0265357733b2cfcb245508297bbb5c317e478ec
parent 48b2b3454334bb6af3ca1c628208f187c058f6e2
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 31 Oct 2026 11:18:36 +0100

stick to proper spelling of build-in units

Diffstat:
Mpackages/taler-harness/src/integrationtests/merchant-webui-transactional-flows.ts | 4+++-
Mpackages/taler-merchant-webui/src/screens/CreateProductScreen.test.tsx | 2+-
Mpackages/taler-merchant-webui/src/screens/CreateProductScreen.tsx | 75++++++++++++++++++++++++++++++++++++++++-----------------------------------
3 files changed, 44 insertions(+), 37 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/merchant-webui-transactional-flows.ts b/packages/taler-harness/src/integrationtests/merchant-webui-transactional-flows.ts @@ -237,7 +237,9 @@ export async function testLockedProductDeletion( description: "Locked inventory regression fixture", price: "CHF:1", unit_price: ["CHF:1"], - unit: "piece", + // Builtin unit slug: the backend resolves a product's unit against the + // unit tables and answers 404 MERCHANT_GENERIC_UNIT_UNKNOWN otherwise. + unit: "Piece", total_stock: 10, }), ); diff --git a/packages/taler-merchant-webui/src/screens/CreateProductScreen.test.tsx b/packages/taler-merchant-webui/src/screens/CreateProductScreen.test.tsx @@ -30,7 +30,7 @@ const product = { soldCount: undefined, category: "", categories: [], - unit: "piece", + unit: "Piece", totalStock: -1, }; diff --git a/packages/taler-merchant-webui/src/screens/CreateProductScreen.tsx b/packages/taler-merchant-webui/src/screens/CreateProductScreen.tsx @@ -69,71 +69,76 @@ export interface CreateProductScreenProps { * a component, and pogen extracts only strings written as t`` templates. As * plain literals no unit name or description reached the catalogue, so the * whole list stayed English in every language. + * + * Every slug has to be one of the backend's builtin unit identifiers: since + * protocol v25 the backend resolves a product's unit against its unit tables + * and answers 404 MERCHANT_GENERIC_UNIT_UNKNOWN for anything else. The + * `allowFraction` flags mirror `unit_allow_fraction` of those builtins. */ function predefinedUnits( t: TranslateFn, ): { slug: string; name: string; allowFraction: boolean; desc: string }[] { return [ { - slug: "piece", + slug: "Piece", name: t`Piece`, allowFraction: false, desc: t`Customers order whole pieces.`, }, { - slug: "bottle", - name: t`Bottle`, - allowFraction: false, - desc: t`Customers order whole bottles.`, - }, - { - slug: "box", - name: t`Box`, - allowFraction: false, - desc: t`Customers order whole boxes.`, - }, - { - slug: "portion", - name: t`Portion`, + slug: "Set", + name: t`Set`, allowFraction: false, - desc: t`Customers order whole portions.`, + desc: t`Customers order whole sets.`, }, { - slug: "kilogram", + slug: "WeightUnitKg", name: t`Kilogram (kg)`, allowFraction: true, desc: t`Customers can order fractions of a kilogram.`, }, { - slug: "gram", + slug: "WeightUnitG", name: t`Gram (g)`, allowFraction: true, desc: t`Customers can order fractional grams.`, }, { - slug: "litre", + slug: "VolumeUnitLitre", name: t`Litre (l)`, allowFraction: true, desc: t`Customers can order fractions of a litre.`, }, { - slug: "millilitre", + slug: "VolumeUnitCm3", name: t`Millilitre (ml)`, allowFraction: true, desc: t`Customers can order fractional millilitres.`, }, { - slug: "metre", + slug: "SizeUnitM", name: t`Metre (m)`, allowFraction: true, desc: t`Customers can order fractional metres.`, }, { - slug: "hour", + slug: "SizeUnitCm", + name: t`Centimetre (cm)`, + allowFraction: true, + desc: t`Customers can order fractional centimetres.`, + }, + { + slug: "TimeUnitHour", name: t`Hour (h)`, allowFraction: true, desc: t`Customers can order fractional hours.`, }, + { + slug: "TimeUnitDay", + name: t`Day (d)`, + allowFraction: true, + desc: t`Customers can order fractional days.`, + }, ]; } @@ -158,16 +163,16 @@ function formatAsAmountString( } const PREDEFINED_UNIT_SLUGS = new Set([ - "piece", - "bottle", - "box", - "portion", - "kilogram", - "gram", - "litre", - "millilitre", - "metre", - "hour", + "Piece", + "Set", + "WeightUnitKg", + "WeightUnitG", + "VolumeUnitLitre", + "VolumeUnitCm3", + "SizeUnitM", + "SizeUnitCm", + "TimeUnitHour", + "TimeUnitDay", ]); function dateInputValue(timestamp: TalerProtocolTimestamp | undefined): string { @@ -207,7 +212,7 @@ export function CreateProductScreen({ const isEditing = !!editId; const existing = initialProduct || productResource?.data; - const existingUnit = existing?.unit || "piece"; + const existingUnit = existing?.unit || "Piece"; // Form State (Spec 07 ยง 7.2) const [name, setName] = useState<string>(existing?.name || ""); @@ -274,7 +279,7 @@ export function CreateProductScreen({ setDescription(existing.description || ""); setImage(existing.image || ""); setPrice(existing.price || (currency ? `${currency}:2.50` : "")); - const loadedUnit = existing.unit || "piece"; + const loadedUnit = existing.unit || "Piece"; setUnit(PREDEFINED_UNIT_SLUGS.has(loadedUnit) ? loadedUnit : "other"); setCustomUnit(PREDEFINED_UNIT_SLUGS.has(loadedUnit) ? "" : loadedUnit); @@ -385,7 +390,7 @@ export function CreateProductScreen({ setErrorMsg(""); setIsSubmitting(true); - const finalUnit = unit === "other" ? customUnit || "piece" : unit; + const finalUnit = unit === "other" ? customUnit || "Piece" : unit; const finalProductId = productId || `prod_${slugify(name)}`; const formattedPrice: AmountString = formatAsAmountString(price, currency);