@open-mercato/core 0.4.8-develop-4e71d95aba → 0.4.8-develop-2acbd97ec3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/catalog/data/validators.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { CATALOG_PRICE_DISPLAY_MODES, CATALOG_PRODUCT_TYPES } from './types'\nimport { REFERENCE_UNIT_CODES } from '../lib/unitCodes'\n\nconst uuid = () => z.string().uuid()\n\nconst scoped = z.object({\n organizationId: uuid(),\n tenantId: uuid(),\n})\n\nconst tenantScoped = z.object({\n tenantId: uuid(),\n})\n\nconst currencyCodeSchema = z\n .string()\n .trim()\n .regex(/^[A-Z]{3}$/, 'currency code must be a three-letter ISO code')\n\nconst metadataSchema = z.record(z.string(), z.unknown()).optional()\n\nconst slugSchema = z\n .string()\n .trim()\n .toLowerCase()\n .regex(/^[a-z0-9\\-_]+$/, 'code must contain lowercase letters, digits, hyphen, or underscore')\n .max(150)\n\nconst handleSchema = z\n .string()\n .trim()\n .toLowerCase()\n .regex(/^[a-z0-9\\-_]+$/, 'handle must contain lowercase letters, digits, hyphen, or underscore')\n .max(150)\n\nconst skuSchema = z\n .string()\n .trim()\n .regex(/^[A-Za-z0-9\\-_\\.]+$/, 'SKU may include letters, numbers, hyphen, underscore, or period')\n .max(191)\n\nconst variantOptionValuesSchema = z\n .record(\n z\n .string()\n .trim()\n .min(1)\n .max(191),\n z.string().trim().max(255)\n )\n .optional()\n\nconst optionChoiceSchema = z.object({\n code: slugSchema,\n label: z.string().trim().max(255).optional(),\n})\n\nconst optionDefinitionSchema = z.object({\n code: slugSchema,\n label: z.string().trim().min(1).max(255),\n description: z.string().trim().max(2000).optional(),\n inputType: z.enum(['select', 'text', 'textarea', 'number']),\n isRequired: z.boolean().optional(),\n isMultiple: z.boolean().optional(),\n choices: z.array(optionChoiceSchema).max(200).optional(),\n})\n\nconst optionSchema = z.object({\n version: z.number().int().min(1).optional(),\n name: z.string().trim().max(255).optional(),\n description: z.string().trim().max(4000).optional(),\n options: z.array(optionDefinitionSchema).max(64),\n})\n\nconst tagLabelSchema = z.string().trim().min(1).max(100)\n\nconst offerBaseSchema = z.object({\n channelId: uuid(),\n title: z.string().trim().min(1).max(255),\n description: z.string().trim().max(4000).optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n metadata: metadataSchema,\n isActive: z.boolean().optional(),\n})\n\nconst offerInputSchema = offerBaseSchema.extend({\n id: uuid().optional(),\n})\n\nexport const offerCreateSchema = scoped.merge(\n offerBaseSchema.extend({\n productId: uuid(),\n })\n)\n\nexport const offerUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(\n offerBaseSchema\n .extend({\n productId: uuid().optional(),\n })\n .partial()\n )\n\nconst productTypeSchema = z.enum(CATALOG_PRODUCT_TYPES)\nconst uomRoundingModeSchema = z.enum(['half_up', 'down', 'up'])\nconst unitPriceReferenceUnitSchema = z.enum(REFERENCE_UNIT_CODES)\nconst unitPriceConfigSchema = z.object({\n enabled: z.boolean().optional(),\n referenceUnit: unitPriceReferenceUnitSchema.nullable().optional(),\n baseQuantity: z.coerce.number().positive().optional(),\n})\n\nfunction productUomCrossFieldRefinement(\n input: {\n defaultUnit?: string | null\n defaultSalesUnit?: string | null\n unitPriceEnabled?: boolean\n unitPriceReferenceUnit?: string | null\n unitPriceBaseQuantity?: number\n unitPrice?: { enabled?: boolean; referenceUnit?: string | null; baseQuantity?: number }\n },\n ctx: z.RefinementCtx,\n) {\n const defaultUnit = typeof input.defaultUnit === 'string' ? input.defaultUnit.trim() : ''\n const defaultSalesUnit =\n typeof input.defaultSalesUnit === 'string' ? input.defaultSalesUnit.trim() : ''\n if (defaultSalesUnit && !defaultUnit) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultSalesUnit'],\n message: 'catalog.products.validation.baseUnitRequired',\n })\n }\n const unitPriceEnabled = input.unitPrice?.enabled ?? input.unitPriceEnabled ?? false\n if (!unitPriceEnabled) return\n const referenceUnit =\n input.unitPrice?.referenceUnit ?? input.unitPriceReferenceUnit ?? null\n const baseQuantity =\n input.unitPrice?.baseQuantity ?? input.unitPriceBaseQuantity ?? null\n if (!referenceUnit) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['unitPrice'],\n message: 'catalog.products.validation.referenceUnitRequired',\n })\n }\n if (baseQuantity === null || baseQuantity === undefined || Number(baseQuantity) <= 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['unitPrice'],\n message: 'catalog.products.unitPrice.errors.baseQuantity',\n })\n }\n}\n\n// Base schema without refinements (used for .partial() in update schema)\nconst productBaseSchema = scoped.extend({\n title: z.string().trim().min(1).max(255),\n subtitle: z.string().trim().max(255).optional(),\n description: z.string().trim().max(4000).optional(),\n sku: skuSchema.optional(),\n handle: handleSchema.optional(),\n taxRateId: uuid().nullable().optional(),\n taxRate: z.coerce.number().min(0).max(100).optional().nullable(),\n productType: productTypeSchema.default('simple'),\n statusEntryId: uuid().optional(),\n primaryCurrencyCode: currencyCodeSchema.optional(),\n defaultUnit: z.string().trim().max(50).optional().nullable(),\n defaultSalesUnit: z.string().trim().max(50).optional().nullable(),\n defaultSalesUnitQuantity: z.coerce.number().positive().optional(),\n uomRoundingScale: z.coerce.number().int().min(0).max(6).optional(),\n uomRoundingMode: uomRoundingModeSchema.optional(),\n unitPriceEnabled: z.boolean().optional(),\n unitPriceReferenceUnit: unitPriceReferenceUnitSchema.nullable().optional(),\n unitPriceBaseQuantity: z.coerce.number().positive().optional(),\n unitPrice: unitPriceConfigSchema.optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n weightValue: z.coerce.number().min(0).optional().nullable(),\n weightUnit: z.string().trim().max(25).optional().nullable(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .optional()\n .nullable(),\n optionSchemaId: uuid().nullable().optional(),\n optionSchema: optionSchema.optional(),\n customFieldsetCode: slugSchema.nullable().optional(),\n isConfigurable: z.boolean().optional(),\n isActive: z.boolean().optional(),\n metadata: metadataSchema,\n offers: z.array(offerInputSchema.omit({ id: true })).optional(),\n categoryIds: z.array(uuid()).max(100).optional(),\n tags: z.array(tagLabelSchema).max(100).optional(),\n})\n\nexport const productCreateSchema = productBaseSchema\n .superRefine(productUomCrossFieldRefinement)\n\nexport const productUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(productBaseSchema.partial())\n .extend({\n productType: productTypeSchema.optional(),\n })\n .superRefine(productUomCrossFieldRefinement)\n\nexport const variantCreateSchema = scoped.extend({\n productId: uuid(),\n name: z.string().trim().max(255).optional(),\n sku: z\n .string()\n .trim()\n .regex(/^[A-Za-z0-9\\-_\\.]+$/)\n .max(191)\n .optional(),\n barcode: z.string().trim().max(191).optional(),\n statusEntryId: uuid().optional(),\n isDefault: z.boolean().optional(),\n isActive: z.boolean().optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n weightValue: z.coerce.number().min(0).optional(),\n weightUnit: z.string().trim().max(25).optional(),\n taxRateId: uuid().nullable().optional(),\n taxRate: z.coerce.number().min(0).max(100).optional().nullable(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .optional(),\n metadata: metadataSchema,\n optionValues: variantOptionValuesSchema,\n customFieldsetCode: slugSchema.nullable().optional(),\n})\n\nexport const variantUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(variantCreateSchema.partial())\n\nexport const optionSchemaTemplateCreateSchema = scoped.extend({\n name: z.string().trim().min(1).max(255),\n code: slugSchema.optional(),\n description: z.string().trim().max(4000).optional(),\n schema: optionSchema,\n metadata: metadataSchema,\n isActive: z.boolean().optional(),\n})\n\nexport const optionSchemaTemplateUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(optionSchemaTemplateCreateSchema.partial())\n\nconst priceDisplayModeSchema = z.enum(CATALOG_PRICE_DISPLAY_MODES)\n\nexport const priceKindCreateSchema = tenantScoped.extend({\n code: slugSchema,\n title: z.string().trim().min(1).max(255),\n displayMode: priceDisplayModeSchema.default('excluding-tax'),\n currencyCode: currencyCodeSchema.optional(),\n isPromotion: z.boolean().optional(),\n isActive: z.boolean().optional(),\n})\n\nexport const priceKindUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(priceKindCreateSchema.partial())\n\nexport const priceCreateSchema = scoped.extend({\n variantId: uuid().optional(),\n productId: uuid().optional(),\n offerId: uuid().optional(),\n currencyCode: currencyCodeSchema,\n priceKindId: uuid(),\n minQuantity: z.coerce.number().int().min(1).optional(),\n maxQuantity: z.coerce.number().int().min(1).optional(),\n unitPriceNet: z.coerce.number().min(0).optional(),\n unitPriceGross: z.coerce.number().min(0).optional(),\n taxRate: z.coerce.number().min(0).max(100).optional(),\n taxRateId: uuid().nullable().optional(),\n channelId: uuid().optional(),\n userId: uuid().optional(),\n userGroupId: uuid().optional(),\n customerId: uuid().optional(),\n customerGroupId: uuid().optional(),\n metadata: metadataSchema,\n startsAt: z.coerce.date().optional(),\n endsAt: z.coerce.date().optional(),\n})\n\nexport const priceUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(priceCreateSchema.partial())\n\nexport const categoryCreateSchema = scoped.extend({\n name: z.string().trim().min(1).max(255),\n slug: slugSchema.optional().nullable(),\n description: z.string().trim().max(2000).optional(),\n parentId: uuid().optional().nullable(),\n isActive: z.boolean().optional(),\n})\n\nexport const categoryUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(categoryCreateSchema.partial())\n\nexport const productUnitConversionCreateSchema = scoped.extend({\n productId: uuid(),\n unitCode: z.string().trim().min(1).max(50),\n toBaseFactor: z.coerce.number().positive().max(1_000_000),\n sortOrder: z.coerce.number().int().optional(),\n isActive: z.boolean().optional(),\n metadata: metadataSchema,\n})\n\nexport const productUnitConversionUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(productUnitConversionCreateSchema.omit({ productId: true }).partial())\n\nexport const productUnitConversionDeleteSchema = scoped.extend({\n id: uuid(),\n})\n\nexport type ProductCreateInput = z.infer<typeof productCreateSchema>\nexport type ProductUpdateInput = z.infer<typeof productUpdateSchema>\nexport type VariantCreateInput = z.infer<typeof variantCreateSchema>\nexport type VariantUpdateInput = z.infer<typeof variantUpdateSchema>\nexport type OptionSchemaTemplateCreateInput = z.infer<typeof optionSchemaTemplateCreateSchema>\nexport type OptionSchemaTemplateUpdateInput = z.infer<typeof optionSchemaTemplateUpdateSchema>\nexport type PriceKindCreateInput = z.infer<typeof priceKindCreateSchema>\nexport type PriceKindUpdateInput = z.infer<typeof priceKindUpdateSchema>\nexport type PriceCreateInput = z.infer<typeof priceCreateSchema>\nexport type PriceUpdateInput = z.infer<typeof priceUpdateSchema>\nexport type CategoryCreateInput = z.infer<typeof categoryCreateSchema>\nexport type CategoryUpdateInput = z.infer<typeof categoryUpdateSchema>\nexport type OfferInput = z.infer<typeof offerInputSchema>\nexport type OfferCreateInput = z.infer<typeof offerCreateSchema>\nexport type OfferUpdateInput = z.infer<typeof offerUpdateSchema>\nexport type ProductUnitConversionCreateInput = z.infer<typeof productUnitConversionCreateSchema>\nexport type ProductUnitConversionUpdateInput = z.infer<typeof productUnitConversionUpdateSchema>\nexport type ProductUnitConversionDeleteInput = z.infer<typeof productUnitConversionDeleteSchema>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { CATALOG_PRICE_DISPLAY_MODES, CATALOG_PRODUCT_TYPES } from './types'\nimport { REFERENCE_UNIT_CODES } from '../lib/unitCodes'\nimport {\n getCatalogPriceAmountValidationMessage,\n validateCatalogPriceAmountInput,\n} from '../lib/priceValidation'\n\nconst uuid = () => z.string().uuid()\n\nconst scoped = z.object({\n organizationId: uuid(),\n tenantId: uuid(),\n})\n\nconst tenantScoped = z.object({\n tenantId: uuid(),\n})\n\nconst currencyCodeSchema = z\n .string()\n .trim()\n .regex(/^[A-Z]{3}$/, 'currency code must be a three-letter ISO code')\n\nconst metadataSchema = z.record(z.string(), z.unknown()).optional()\n\nconst slugSchema = z\n .string()\n .trim()\n .toLowerCase()\n .regex(/^[a-z0-9\\-_]+$/, 'code must contain lowercase letters, digits, hyphen, or underscore')\n .max(150)\n\nconst handleSchema = z\n .string()\n .trim()\n .toLowerCase()\n .regex(/^[a-z0-9\\-_]+$/, 'handle must contain lowercase letters, digits, hyphen, or underscore')\n .max(150)\n\nconst skuSchema = z\n .string()\n .trim()\n .regex(/^[A-Za-z0-9\\-_\\.]+$/, 'SKU may include letters, numbers, hyphen, underscore, or period')\n .max(191)\n\nconst variantOptionValuesSchema = z\n .record(\n z\n .string()\n .trim()\n .min(1)\n .max(191),\n z.string().trim().max(255)\n )\n .optional()\n\nconst optionChoiceSchema = z.object({\n code: slugSchema,\n label: z.string().trim().max(255).optional(),\n})\n\nconst optionDefinitionSchema = z.object({\n code: slugSchema,\n label: z.string().trim().min(1).max(255),\n description: z.string().trim().max(2000).optional(),\n inputType: z.enum(['select', 'text', 'textarea', 'number']),\n isRequired: z.boolean().optional(),\n isMultiple: z.boolean().optional(),\n choices: z.array(optionChoiceSchema).max(200).optional(),\n})\n\nconst optionSchema = z.object({\n version: z.number().int().min(1).optional(),\n name: z.string().trim().max(255).optional(),\n description: z.string().trim().max(4000).optional(),\n options: z.array(optionDefinitionSchema).max(64),\n})\n\nconst tagLabelSchema = z.string().trim().min(1).max(100)\n\nconst offerBaseSchema = z.object({\n channelId: uuid(),\n title: z.string().trim().min(1).max(255),\n description: z.string().trim().max(4000).optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n metadata: metadataSchema,\n isActive: z.boolean().optional(),\n})\n\nconst offerInputSchema = offerBaseSchema.extend({\n id: uuid().optional(),\n})\n\nexport const offerCreateSchema = scoped.merge(\n offerBaseSchema.extend({\n productId: uuid(),\n })\n)\n\nexport const offerUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(\n offerBaseSchema\n .extend({\n productId: uuid().optional(),\n })\n .partial()\n )\n\nconst productTypeSchema = z.enum(CATALOG_PRODUCT_TYPES)\nconst uomRoundingModeSchema = z.enum(['half_up', 'down', 'up'])\nconst unitPriceReferenceUnitSchema = z.enum(REFERENCE_UNIT_CODES)\nconst unitPriceConfigSchema = z.object({\n enabled: z.boolean().optional(),\n referenceUnit: unitPriceReferenceUnitSchema.nullable().optional(),\n baseQuantity: z.coerce.number().positive().optional(),\n})\n\nconst catalogPriceAmountSchema = z\n .custom<number>((value) => validateCatalogPriceAmountInput(value).ok, {\n message: getCatalogPriceAmountValidationMessage(),\n })\n .transform((value) => {\n const result = validateCatalogPriceAmountInput(value)\n if (!result.ok) {\n throw new Error('catalogPriceAmountSchema transform reached invalid state')\n }\n return result.numeric\n })\n\nfunction productUomCrossFieldRefinement(\n input: {\n defaultUnit?: string | null\n defaultSalesUnit?: string | null\n unitPriceEnabled?: boolean\n unitPriceReferenceUnit?: string | null\n unitPriceBaseQuantity?: number\n unitPrice?: { enabled?: boolean; referenceUnit?: string | null; baseQuantity?: number }\n },\n ctx: z.RefinementCtx,\n) {\n const defaultUnit = typeof input.defaultUnit === 'string' ? input.defaultUnit.trim() : ''\n const defaultSalesUnit =\n typeof input.defaultSalesUnit === 'string' ? input.defaultSalesUnit.trim() : ''\n if (defaultSalesUnit && !defaultUnit) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['defaultSalesUnit'],\n message: 'catalog.products.validation.baseUnitRequired',\n })\n }\n const unitPriceEnabled = input.unitPrice?.enabled ?? input.unitPriceEnabled ?? false\n if (!unitPriceEnabled) return\n const referenceUnit =\n input.unitPrice?.referenceUnit ?? input.unitPriceReferenceUnit ?? null\n const baseQuantity =\n input.unitPrice?.baseQuantity ?? input.unitPriceBaseQuantity ?? null\n if (!referenceUnit) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['unitPrice'],\n message: 'catalog.products.validation.referenceUnitRequired',\n })\n }\n if (baseQuantity === null || baseQuantity === undefined || Number(baseQuantity) <= 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['unitPrice'],\n message: 'catalog.products.unitPrice.errors.baseQuantity',\n })\n }\n}\n\n// Base schema without refinements (used for .partial() in update schema)\nconst productBaseSchema = scoped.extend({\n title: z.string().trim().min(1).max(255),\n subtitle: z.string().trim().max(255).optional(),\n description: z.string().trim().max(4000).optional(),\n sku: skuSchema.optional(),\n handle: handleSchema.optional(),\n taxRateId: uuid().nullable().optional(),\n taxRate: z.coerce.number().min(0).max(100).optional().nullable(),\n productType: productTypeSchema.default('simple'),\n statusEntryId: uuid().optional(),\n primaryCurrencyCode: currencyCodeSchema.optional(),\n defaultUnit: z.string().trim().max(50).optional().nullable(),\n defaultSalesUnit: z.string().trim().max(50).optional().nullable(),\n defaultSalesUnitQuantity: z.coerce.number().positive().optional(),\n uomRoundingScale: z.coerce.number().int().min(0).max(6).optional(),\n uomRoundingMode: uomRoundingModeSchema.optional(),\n unitPriceEnabled: z.boolean().optional(),\n unitPriceReferenceUnit: unitPriceReferenceUnitSchema.nullable().optional(),\n unitPriceBaseQuantity: z.coerce.number().positive().optional(),\n unitPrice: unitPriceConfigSchema.optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n weightValue: z.coerce.number().min(0).optional().nullable(),\n weightUnit: z.string().trim().max(25).optional().nullable(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .optional()\n .nullable(),\n optionSchemaId: uuid().nullable().optional(),\n optionSchema: optionSchema.optional(),\n customFieldsetCode: slugSchema.nullable().optional(),\n isConfigurable: z.boolean().optional(),\n isActive: z.boolean().optional(),\n metadata: metadataSchema,\n offers: z.array(offerInputSchema.omit({ id: true })).optional(),\n categoryIds: z.array(uuid()).max(100).optional(),\n tags: z.array(tagLabelSchema).max(100).optional(),\n})\n\nexport const productCreateSchema = productBaseSchema\n .superRefine(productUomCrossFieldRefinement)\n\nexport const productUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(productBaseSchema.partial())\n .extend({\n productType: productTypeSchema.optional(),\n })\n .superRefine(productUomCrossFieldRefinement)\n\nexport const variantCreateSchema = scoped.extend({\n productId: uuid(),\n name: z.string().trim().max(255).optional(),\n sku: z\n .string()\n .trim()\n .regex(/^[A-Za-z0-9\\-_\\.]+$/)\n .max(191)\n .optional(),\n barcode: z.string().trim().max(191).optional(),\n statusEntryId: uuid().optional(),\n isDefault: z.boolean().optional(),\n isActive: z.boolean().optional(),\n defaultMediaId: uuid().optional().nullable(),\n defaultMediaUrl: z.string().trim().max(500).optional().nullable(),\n weightValue: z.coerce.number().min(0).optional(),\n weightUnit: z.string().trim().max(25).optional(),\n taxRateId: uuid().nullable().optional(),\n taxRate: z.coerce.number().min(0).max(100).optional().nullable(),\n dimensions: z\n .object({\n width: z.coerce.number().min(0).optional(),\n height: z.coerce.number().min(0).optional(),\n depth: z.coerce.number().min(0).optional(),\n unit: z.string().trim().max(25).optional(),\n })\n .optional(),\n metadata: metadataSchema,\n optionValues: variantOptionValuesSchema,\n customFieldsetCode: slugSchema.nullable().optional(),\n})\n\nexport const variantUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(variantCreateSchema.partial())\n\nexport const optionSchemaTemplateCreateSchema = scoped.extend({\n name: z.string().trim().min(1).max(255),\n code: slugSchema.optional(),\n description: z.string().trim().max(4000).optional(),\n schema: optionSchema,\n metadata: metadataSchema,\n isActive: z.boolean().optional(),\n})\n\nexport const optionSchemaTemplateUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(optionSchemaTemplateCreateSchema.partial())\n\nconst priceDisplayModeSchema = z.enum(CATALOG_PRICE_DISPLAY_MODES)\n\nexport const priceKindCreateSchema = tenantScoped.extend({\n code: slugSchema,\n title: z.string().trim().min(1).max(255),\n displayMode: priceDisplayModeSchema.default('excluding-tax'),\n currencyCode: currencyCodeSchema.optional(),\n isPromotion: z.boolean().optional(),\n isActive: z.boolean().optional(),\n})\n\nexport const priceKindUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(priceKindCreateSchema.partial())\n\nexport const priceCreateSchema = scoped.extend({\n variantId: uuid().optional(),\n productId: uuid().optional(),\n offerId: uuid().optional(),\n currencyCode: currencyCodeSchema,\n priceKindId: uuid(),\n minQuantity: z.coerce.number().int().min(1).optional(),\n maxQuantity: z.coerce.number().int().min(1).optional(),\n unitPriceNet: catalogPriceAmountSchema.optional(),\n unitPriceGross: catalogPriceAmountSchema.optional(),\n taxRate: z.coerce.number().min(0).max(100).optional(),\n taxRateId: uuid().nullable().optional(),\n channelId: uuid().optional(),\n userId: uuid().optional(),\n userGroupId: uuid().optional(),\n customerId: uuid().optional(),\n customerGroupId: uuid().optional(),\n metadata: metadataSchema,\n startsAt: z.coerce.date().optional(),\n endsAt: z.coerce.date().optional(),\n})\n\nexport const priceUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(priceCreateSchema.partial())\n\nexport const categoryCreateSchema = scoped.extend({\n name: z.string().trim().min(1).max(255),\n slug: slugSchema.optional().nullable(),\n description: z.string().trim().max(2000).optional(),\n parentId: uuid().optional().nullable(),\n isActive: z.boolean().optional(),\n})\n\nexport const categoryUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(categoryCreateSchema.partial())\n\nexport const productUnitConversionCreateSchema = scoped.extend({\n productId: uuid(),\n unitCode: z.string().trim().min(1).max(50),\n toBaseFactor: z.coerce.number().positive().max(1_000_000),\n sortOrder: z.coerce.number().int().optional(),\n isActive: z.boolean().optional(),\n metadata: metadataSchema,\n})\n\nexport const productUnitConversionUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(productUnitConversionCreateSchema.omit({ productId: true }).partial())\n\nexport const productUnitConversionDeleteSchema = scoped.extend({\n id: uuid(),\n})\n\nexport type ProductCreateInput = z.infer<typeof productCreateSchema>\nexport type ProductUpdateInput = z.infer<typeof productUpdateSchema>\nexport type VariantCreateInput = z.infer<typeof variantCreateSchema>\nexport type VariantUpdateInput = z.infer<typeof variantUpdateSchema>\nexport type OptionSchemaTemplateCreateInput = z.infer<typeof optionSchemaTemplateCreateSchema>\nexport type OptionSchemaTemplateUpdateInput = z.infer<typeof optionSchemaTemplateUpdateSchema>\nexport type PriceKindCreateInput = z.infer<typeof priceKindCreateSchema>\nexport type PriceKindUpdateInput = z.infer<typeof priceKindUpdateSchema>\nexport type PriceCreateInput = z.infer<typeof priceCreateSchema>\nexport type PriceUpdateInput = z.infer<typeof priceUpdateSchema>\nexport type CategoryCreateInput = z.infer<typeof categoryCreateSchema>\nexport type CategoryUpdateInput = z.infer<typeof categoryUpdateSchema>\nexport type OfferInput = z.infer<typeof offerInputSchema>\nexport type OfferCreateInput = z.infer<typeof offerCreateSchema>\nexport type OfferUpdateInput = z.infer<typeof offerUpdateSchema>\nexport type ProductUnitConversionCreateInput = z.infer<typeof productUnitConversionCreateSchema>\nexport type ProductUnitConversionUpdateInput = z.infer<typeof productUnitConversionUpdateSchema>\nexport type ProductUnitConversionDeleteInput = z.infer<typeof productUnitConversionDeleteSchema>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,45 @@
1
+ const CATALOG_PRICE_MAX_INTEGER_DIGITS = 12;
2
+ const CATALOG_PRICE_MAX_FRACTION_DIGITS = 4;
3
+ function normalizeCatalogPriceRawValue(value) {
4
+ if (typeof value === "number") {
5
+ if (!Number.isFinite(value)) return null;
6
+ return String(value);
7
+ }
8
+ if (typeof value !== "string") return null;
9
+ const normalized = value.trim().replace(/\s+/g, "");
10
+ return normalized.length ? normalized : null;
11
+ }
12
+ function validateCatalogPriceAmountInput(value) {
13
+ const raw = normalizeCatalogPriceRawValue(value);
14
+ if (!raw) return { ok: false, reason: "invalid_format" };
15
+ if (raw.startsWith("-")) return { ok: false, reason: "negative" };
16
+ if (!/^\d+(?:\.\d+)?$/.test(raw)) {
17
+ return { ok: false, reason: "invalid_format" };
18
+ }
19
+ const numeric = Number(raw);
20
+ if (!Number.isFinite(numeric)) return { ok: false, reason: "not_finite" };
21
+ if (numeric < 0) return { ok: false, reason: "negative" };
22
+ const [integerPartRaw, fractionPart = ""] = raw.split(".");
23
+ const integerPart = integerPartRaw.replace(/^0+(?=\d)/, "");
24
+ if (integerPart.length > CATALOG_PRICE_MAX_INTEGER_DIGITS) {
25
+ return { ok: false, reason: "too_many_integer_digits" };
26
+ }
27
+ if (fractionPart.length > CATALOG_PRICE_MAX_FRACTION_DIGITS) {
28
+ return { ok: false, reason: "too_many_fraction_digits" };
29
+ }
30
+ return { ok: true, numeric };
31
+ }
32
+ function isCatalogPriceAmountInputValid(value) {
33
+ return validateCatalogPriceAmountInput(value).ok;
34
+ }
35
+ function getCatalogPriceAmountValidationMessage() {
36
+ return `Price must be a valid non-negative amount with at most ${CATALOG_PRICE_MAX_INTEGER_DIGITS} digits before the decimal point and ${CATALOG_PRICE_MAX_FRACTION_DIGITS} decimal places.`;
37
+ }
38
+ export {
39
+ CATALOG_PRICE_MAX_FRACTION_DIGITS,
40
+ CATALOG_PRICE_MAX_INTEGER_DIGITS,
41
+ getCatalogPriceAmountValidationMessage,
42
+ isCatalogPriceAmountInputValid,
43
+ validateCatalogPriceAmountInput
44
+ };
45
+ //# sourceMappingURL=priceValidation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/catalog/lib/priceValidation.ts"],
4
+ "sourcesContent": ["export const CATALOG_PRICE_MAX_INTEGER_DIGITS = 12\nexport const CATALOG_PRICE_MAX_FRACTION_DIGITS = 4\n\nexport type CatalogPriceAmountValidationReason =\n | 'invalid_format'\n | 'not_finite'\n | 'negative'\n | 'too_many_integer_digits'\n | 'too_many_fraction_digits'\n\nexport type CatalogPriceAmountValidationResult =\n | { ok: true; numeric: number }\n | { ok: false; reason: CatalogPriceAmountValidationReason }\n\nfunction normalizeCatalogPriceRawValue(value: unknown): string | null {\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return null\n return String(value)\n }\n if (typeof value !== 'string') return null\n const normalized = value.trim().replace(/\\s+/g, '')\n return normalized.length ? normalized : null\n}\n\nexport function validateCatalogPriceAmountInput(\n value: unknown,\n): CatalogPriceAmountValidationResult {\n const raw = normalizeCatalogPriceRawValue(value)\n if (!raw) return { ok: false, reason: 'invalid_format' }\n if (raw.startsWith('-')) return { ok: false, reason: 'negative' }\n if (!/^\\d+(?:\\.\\d+)?$/.test(raw)) {\n return { ok: false, reason: 'invalid_format' }\n }\n\n const numeric = Number(raw)\n if (!Number.isFinite(numeric)) return { ok: false, reason: 'not_finite' }\n if (numeric < 0) return { ok: false, reason: 'negative' }\n\n const [integerPartRaw, fractionPart = ''] = raw.split('.')\n const integerPart = integerPartRaw.replace(/^0+(?=\\d)/, '')\n if (integerPart.length > CATALOG_PRICE_MAX_INTEGER_DIGITS) {\n return { ok: false, reason: 'too_many_integer_digits' }\n }\n if (fractionPart.length > CATALOG_PRICE_MAX_FRACTION_DIGITS) {\n return { ok: false, reason: 'too_many_fraction_digits' }\n }\n\n return { ok: true, numeric }\n}\n\nexport function isCatalogPriceAmountInputValid(value: unknown): boolean {\n return validateCatalogPriceAmountInput(value).ok\n}\n\nexport function getCatalogPriceAmountValidationMessage(): string {\n return `Price must be a valid non-negative amount with at most ${CATALOG_PRICE_MAX_INTEGER_DIGITS} digits before the decimal point and ${CATALOG_PRICE_MAX_FRACTION_DIGITS} decimal places.`\n}\n"],
5
+ "mappings": "AAAO,MAAM,mCAAmC;AACzC,MAAM,oCAAoC;AAajD,SAAS,8BAA8B,OAA+B;AACpE,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,WAAO,OAAO,KAAK;AAAA,EACrB;AACA,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAClD,SAAO,WAAW,SAAS,aAAa;AAC1C;AAEO,SAAS,gCACd,OACoC;AACpC,QAAM,MAAM,8BAA8B,KAAK;AAC/C,MAAI,CAAC,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AACvD,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAChE,MAAI,CAAC,kBAAkB,KAAK,GAAG,GAAG;AAChC,WAAO,EAAE,IAAI,OAAO,QAAQ,iBAAiB;AAAA,EAC/C;AAEA,QAAM,UAAU,OAAO,GAAG;AAC1B,MAAI,CAAC,OAAO,SAAS,OAAO,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACxE,MAAI,UAAU,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAExD,QAAM,CAAC,gBAAgB,eAAe,EAAE,IAAI,IAAI,MAAM,GAAG;AACzD,QAAM,cAAc,eAAe,QAAQ,aAAa,EAAE;AAC1D,MAAI,YAAY,SAAS,kCAAkC;AACzD,WAAO,EAAE,IAAI,OAAO,QAAQ,0BAA0B;AAAA,EACxD;AACA,MAAI,aAAa,SAAS,mCAAmC;AAC3D,WAAO,EAAE,IAAI,OAAO,QAAQ,2BAA2B;AAAA,EACzD;AAEA,SAAO,EAAE,IAAI,MAAM,QAAQ;AAC7B;AAEO,SAAS,+BAA+B,OAAyB;AACtE,SAAO,gCAAgC,KAAK,EAAE;AAChD;AAEO,SAAS,yCAAiD;AAC/D,SAAO,0DAA0D,gCAAgC,wCAAwC,iCAAiC;AAC5K;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.4.8-develop-4e71d95aba",
3
+ "version": "0.4.8-develop-2acbd97ec3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -217,10 +217,10 @@
217
217
  "semver": "^7.6.3"
218
218
  },
219
219
  "peerDependencies": {
220
- "@open-mercato/shared": "0.4.8-develop-4e71d95aba"
220
+ "@open-mercato/shared": "0.4.8-develop-2acbd97ec3"
221
221
  },
222
222
  "devDependencies": {
223
- "@open-mercato/shared": "0.4.8-develop-4e71d95aba",
223
+ "@open-mercato/shared": "0.4.8-develop-2acbd97ec3",
224
224
  "@testing-library/dom": "^10.4.1",
225
225
  "@testing-library/jest-dom": "^6.9.1",
226
226
  "@testing-library/react": "^16.3.1",
@@ -20,14 +20,49 @@ const profileResponseSchema = z.object({
20
20
 
21
21
  const passwordSchema = buildPasswordSchema()
22
22
 
23
- const updateSchema = z.object({
23
+ const updateSchemaBase = z.object({
24
24
  email: z.string().email().optional(),
25
+ currentPassword: z.string().trim().min(1).optional(),
25
26
  password: passwordSchema.optional(),
26
- }).refine((data) => Boolean(data.email || data.password), {
27
- message: 'Provide an email or password.',
28
- path: ['email'],
29
27
  })
30
28
 
29
+ function buildUpdateSchema(translate: (key: string, fallback: string) => string) {
30
+ return updateSchemaBase.superRefine((data, ctx) => {
31
+ if (!data.email && !data.password) {
32
+ ctx.addIssue({
33
+ code: z.ZodIssueCode.custom,
34
+ message: translate(
35
+ 'auth.profile.form.errors.emailOrPasswordRequired',
36
+ 'Provide an email or password.',
37
+ ),
38
+ path: ['email'],
39
+ })
40
+ }
41
+ if (data.password && !data.currentPassword) {
42
+ ctx.addIssue({
43
+ code: z.ZodIssueCode.custom,
44
+ message: translate(
45
+ 'auth.profile.form.errors.currentPasswordRequired',
46
+ 'Current password is required.',
47
+ ),
48
+ path: ['currentPassword'],
49
+ })
50
+ }
51
+ if (data.currentPassword && !data.password) {
52
+ ctx.addIssue({
53
+ code: z.ZodIssueCode.custom,
54
+ message: translate(
55
+ 'auth.profile.form.errors.newPasswordRequired',
56
+ 'New password is required.',
57
+ ),
58
+ path: ['password'],
59
+ })
60
+ }
61
+ })
62
+ }
63
+
64
+ const updateSchema = buildUpdateSchema((_key, fallback) => fallback)
65
+
31
66
  const profileUpdateResponseSchema = z.object({
32
67
  ok: z.literal(true),
33
68
  email: z.string().email(),
@@ -83,7 +118,7 @@ export async function PUT(req: Request) {
83
118
  }
84
119
  try {
85
120
  const body = await req.json().catch(() => ({}))
86
- const parsed = updateSchema.safeParse(body)
121
+ const parsed = buildUpdateSchema(translate).safeParse(body)
87
122
  if (!parsed.success) {
88
123
  return NextResponse.json(
89
124
  {
@@ -94,6 +129,35 @@ export async function PUT(req: Request) {
94
129
  )
95
130
  }
96
131
  const container = await createRequestContainer()
132
+ const em = (container.resolve('em') as EntityManager)
133
+ const authService = container.resolve('authService') as AuthService
134
+ if (parsed.data.password) {
135
+ const user = await findOneWithDecryption(
136
+ em,
137
+ User,
138
+ { id: auth.sub, deletedAt: null },
139
+ undefined,
140
+ { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },
141
+ )
142
+ if (!user) {
143
+ return NextResponse.json({ error: translate('auth.users.form.errors.notFound', 'User not found') }, { status: 404 })
144
+ }
145
+ const currentPassword = parsed.data.currentPassword?.trim() ?? ''
146
+ const isCurrentPasswordValid = await authService.verifyPassword(user, currentPassword)
147
+ if (!isCurrentPasswordValid) {
148
+ const message = translate(
149
+ 'auth.profile.form.errors.currentPasswordInvalid',
150
+ 'Current password is incorrect.',
151
+ )
152
+ return NextResponse.json(
153
+ {
154
+ error: message,
155
+ issues: [{ path: ['currentPassword'], message }],
156
+ },
157
+ { status: 400 },
158
+ )
159
+ }
160
+ }
97
161
  const commandBus = (container.resolve('commandBus') as CommandBus)
98
162
  const ctx = buildCommandContext(container, auth, req)
99
163
  const { result } = await commandBus.execute<{ id: string; email?: string; password?: string }, User>(
@@ -107,7 +171,6 @@ export async function PUT(req: Request) {
107
171
  ctx,
108
172
  },
109
173
  )
110
- const authService = container.resolve('authService') as AuthService
111
174
  const roles = await authService.getUserRoles(result, result.tenantId ? String(result.tenantId) : null)
112
175
  const jwt = signJwt({
113
176
  sub: String(result.id),
@@ -24,6 +24,7 @@ type ProfileUpdateResponse = {
24
24
 
25
25
  type ProfileFormValues = {
26
26
  email: string
27
+ currentPassword?: string
27
28
  password?: string
28
29
  confirmPassword?: string
29
30
  }
@@ -70,13 +71,22 @@ export default function AuthProfilePage() {
70
71
 
71
72
  const fields = React.useMemo<CrudField[]>(() => [
72
73
  { id: 'email', label: t('auth.profile.form.email', 'Email'), type: 'text', required: true },
74
+ {
75
+ id: 'currentPassword',
76
+ label: t('auth.profile.form.currentPassword', 'Current password'),
77
+ type: 'password',
78
+ },
73
79
  {
74
80
  id: 'password',
75
81
  label: t('auth.profile.form.password', 'New password'),
76
- type: 'text',
82
+ type: 'password',
77
83
  description: passwordDescription,
78
84
  },
79
- { id: 'confirmPassword', label: t('auth.profile.form.confirmPassword', 'Confirm new password'), type: 'text' },
85
+ {
86
+ id: 'confirmPassword',
87
+ label: t('auth.profile.form.confirmPassword', 'Confirm new password'),
88
+ type: 'password',
89
+ },
80
90
  ], [passwordDescription, t])
81
91
 
82
92
  const schema = React.useMemo(() => {
@@ -87,12 +97,37 @@ export default function AuthProfilePage() {
87
97
  const optionalPasswordSchema = z.union([z.literal(''), passwordSchema]).optional()
88
98
  return z.object({
89
99
  email: z.string().trim().min(1, t('auth.profile.form.errors.emailRequired', 'Email is required.')),
100
+ currentPassword: z.string().optional(),
90
101
  password: optionalPasswordSchema,
91
102
  confirmPassword: z.string().optional(),
92
103
  }).superRefine((values, ctx) => {
104
+ const currentPassword = values.currentPassword?.trim() ?? ''
93
105
  const password = values.password?.trim() ?? ''
94
106
  const confirmPassword = values.confirmPassword?.trim() ?? ''
95
- if ((password || confirmPassword) && password !== confirmPassword) {
107
+ const hasPasswordIntent = Boolean(currentPassword || password || confirmPassword)
108
+
109
+ if (hasPasswordIntent && !currentPassword) {
110
+ ctx.addIssue({
111
+ code: z.ZodIssueCode.custom,
112
+ message: t('auth.profile.form.errors.currentPasswordRequired', 'Current password is required.'),
113
+ path: ['currentPassword'],
114
+ })
115
+ }
116
+ if (hasPasswordIntent && !password) {
117
+ ctx.addIssue({
118
+ code: z.ZodIssueCode.custom,
119
+ message: t('auth.profile.form.errors.newPasswordRequired', 'New password is required.'),
120
+ path: ['password'],
121
+ })
122
+ }
123
+ if (hasPasswordIntent && !confirmPassword) {
124
+ ctx.addIssue({
125
+ code: z.ZodIssueCode.custom,
126
+ message: t('auth.profile.form.errors.confirmPasswordRequired', 'Please confirm the new password.'),
127
+ path: ['confirmPassword'],
128
+ })
129
+ }
130
+ if (password && confirmPassword && password !== confirmPassword) {
96
131
  ctx.addIssue({
97
132
  code: z.ZodIssueCode.custom,
98
133
  message: t('auth.profile.form.errors.passwordMismatch', 'Passwords do not match.'),
@@ -104,14 +139,16 @@ export default function AuthProfilePage() {
104
139
 
105
140
  const handleSubmit = React.useCallback(async (values: ProfileFormValues) => {
106
141
  const nextEmail = values.email?.trim() ?? ''
142
+ const currentPassword = values.currentPassword?.trim() ?? ''
107
143
  const password = values.password?.trim() ?? ''
108
144
 
109
145
  if (!password && nextEmail === email) {
110
146
  throw createCrudFormError(t('auth.profile.form.errors.noChanges', 'No changes to save.'))
111
147
  }
112
148
 
113
- const payload: { email: string; password?: string } = { email: nextEmail }
149
+ const payload: { email: string; currentPassword?: string; password?: string } = { email: nextEmail }
114
150
  if (password) payload.password = password
151
+ if (password) payload.currentPassword = currentPassword
115
152
 
116
153
  const result = await readApiResultOrThrow<ProfileUpdateResponse>(
117
154
  '/api/auth/profile',
@@ -158,6 +195,7 @@ export default function AuthProfilePage() {
158
195
  fields={fields}
159
196
  initialValues={{
160
197
  email,
198
+ currentPassword: '',
161
199
  password: '',
162
200
  confirmPassword: '',
163
201
  }}
@@ -23,6 +23,7 @@ type ProfileUpdateResponse = {
23
23
 
24
24
  type ProfileFormValues = {
25
25
  email: string
26
+ currentPassword?: string
26
27
  password?: string
27
28
  confirmPassword?: string
28
29
  }
@@ -69,13 +70,22 @@ export default function ProfileChangePasswordPage() {
69
70
 
70
71
  const fields = React.useMemo<CrudField[]>(() => [
71
72
  { id: 'email', label: t('auth.profile.form.email', 'Email'), type: 'text', required: true },
73
+ {
74
+ id: 'currentPassword',
75
+ label: t('auth.profile.form.currentPassword', 'Current password'),
76
+ type: 'password',
77
+ },
72
78
  {
73
79
  id: 'password',
74
80
  label: t('auth.profile.form.password', 'New password'),
75
- type: 'text',
81
+ type: 'password',
76
82
  description: passwordDescription,
77
83
  },
78
- { id: 'confirmPassword', label: t('auth.profile.form.confirmPassword', 'Confirm new password'), type: 'text' },
84
+ {
85
+ id: 'confirmPassword',
86
+ label: t('auth.profile.form.confirmPassword', 'Confirm new password'),
87
+ type: 'password',
88
+ },
79
89
  ], [passwordDescription, t])
80
90
 
81
91
  const schema = React.useMemo(() => {
@@ -86,12 +96,37 @@ export default function ProfileChangePasswordPage() {
86
96
  const optionalPasswordSchema = z.union([z.literal(''), passwordSchema]).optional()
87
97
  return z.object({
88
98
  email: z.string().trim().min(1, t('auth.profile.form.errors.emailRequired', 'Email is required.')),
99
+ currentPassword: z.string().optional(),
89
100
  password: optionalPasswordSchema,
90
101
  confirmPassword: z.string().optional(),
91
102
  }).superRefine((values, ctx) => {
103
+ const currentPassword = values.currentPassword?.trim() ?? ''
92
104
  const password = values.password?.trim() ?? ''
93
105
  const confirmPassword = values.confirmPassword?.trim() ?? ''
94
- if ((password || confirmPassword) && password !== confirmPassword) {
106
+ const hasPasswordIntent = Boolean(currentPassword || password || confirmPassword)
107
+
108
+ if (hasPasswordIntent && !currentPassword) {
109
+ ctx.addIssue({
110
+ code: z.ZodIssueCode.custom,
111
+ message: t('auth.profile.form.errors.currentPasswordRequired', 'Current password is required.'),
112
+ path: ['currentPassword'],
113
+ })
114
+ }
115
+ if (hasPasswordIntent && !password) {
116
+ ctx.addIssue({
117
+ code: z.ZodIssueCode.custom,
118
+ message: t('auth.profile.form.errors.newPasswordRequired', 'New password is required.'),
119
+ path: ['password'],
120
+ })
121
+ }
122
+ if (hasPasswordIntent && !confirmPassword) {
123
+ ctx.addIssue({
124
+ code: z.ZodIssueCode.custom,
125
+ message: t('auth.profile.form.errors.confirmPasswordRequired', 'Please confirm the new password.'),
126
+ path: ['confirmPassword'],
127
+ })
128
+ }
129
+ if (password && confirmPassword && password !== confirmPassword) {
95
130
  ctx.addIssue({
96
131
  code: z.ZodIssueCode.custom,
97
132
  message: t('auth.profile.form.errors.passwordMismatch', 'Passwords do not match.'),
@@ -103,14 +138,16 @@ export default function ProfileChangePasswordPage() {
103
138
 
104
139
  const handleSubmit = React.useCallback(async (values: ProfileFormValues) => {
105
140
  const nextEmail = values.email?.trim() ?? ''
141
+ const currentPassword = values.currentPassword?.trim() ?? ''
106
142
  const password = values.password?.trim() ?? ''
107
143
 
108
144
  if (!password && nextEmail === email) {
109
145
  throw createCrudFormError(t('auth.profile.form.errors.noChanges', 'No changes to save.'))
110
146
  }
111
147
 
112
- const payload: { email: string; password?: string } = { email: nextEmail }
148
+ const payload: { email: string; currentPassword?: string; password?: string } = { email: nextEmail }
113
149
  if (password) payload.password = password
150
+ if (password) payload.currentPassword = currentPassword
114
151
 
115
152
  const result = await readApiResultOrThrow<ProfileUpdateResponse>(
116
153
  '/api/auth/profile',
@@ -158,6 +195,7 @@ export default function ProfileChangePasswordPage() {
158
195
  fields={fields}
159
196
  initialValues={{
160
197
  email,
198
+ currentPassword: '',
161
199
  password: '',
162
200
  confirmPassword: '',
163
201
  }}
@@ -65,10 +65,16 @@
65
65
  "auth.password.requirements.special": "Ein Sonderzeichen",
66
66
  "auth.password.requirements.uppercase": "Ein Großbuchstabe",
67
67
  "auth.profile.form.confirmPassword": "Neues Passwort bestätigen",
68
+ "auth.profile.form.currentPassword": "Aktuelles Passwort",
68
69
  "auth.profile.form.email": "E-Mail",
70
+ "auth.profile.form.errors.confirmPasswordRequired": "Bitte bestätige das neue Passwort.",
71
+ "auth.profile.form.errors.currentPasswordInvalid": "Das aktuelle Passwort ist falsch.",
72
+ "auth.profile.form.errors.currentPasswordRequired": "Das aktuelle Passwort ist erforderlich.",
73
+ "auth.profile.form.errors.emailOrPasswordRequired": "Gib eine E-Mail-Adresse oder ein Passwort an.",
69
74
  "auth.profile.form.errors.emailRequired": "E-Mail ist erforderlich.",
70
75
  "auth.profile.form.errors.invalid": "Ungültige Profilaktualisierung.",
71
76
  "auth.profile.form.errors.load": "Profil konnte nicht geladen werden.",
77
+ "auth.profile.form.errors.newPasswordRequired": "Neues Passwort ist erforderlich.",
72
78
  "auth.profile.form.errors.noChanges": "Keine Änderungen zu speichern.",
73
79
  "auth.profile.form.errors.passwordMismatch": "Die Passwörter stimmen nicht überein.",
74
80
  "auth.profile.form.errors.passwordRequirements": "Das Passwort muss die Anforderungen erfüllen.",
@@ -65,10 +65,16 @@
65
65
  "auth.password.requirements.special": "One special character",
66
66
  "auth.password.requirements.uppercase": "One uppercase letter",
67
67
  "auth.profile.form.confirmPassword": "Confirm new password",
68
+ "auth.profile.form.currentPassword": "Current password",
68
69
  "auth.profile.form.email": "Email",
70
+ "auth.profile.form.errors.confirmPasswordRequired": "Please confirm the new password.",
71
+ "auth.profile.form.errors.currentPasswordInvalid": "Current password is incorrect.",
72
+ "auth.profile.form.errors.currentPasswordRequired": "Current password is required.",
73
+ "auth.profile.form.errors.emailOrPasswordRequired": "Provide an email or password.",
69
74
  "auth.profile.form.errors.emailRequired": "Email is required.",
70
75
  "auth.profile.form.errors.invalid": "Invalid profile update.",
71
76
  "auth.profile.form.errors.load": "Failed to load profile.",
77
+ "auth.profile.form.errors.newPasswordRequired": "New password is required.",
72
78
  "auth.profile.form.errors.noChanges": "No changes to save.",
73
79
  "auth.profile.form.errors.passwordMismatch": "Passwords do not match.",
74
80
  "auth.profile.form.errors.passwordRequirements": "Password must meet the requirements.",
@@ -65,10 +65,16 @@
65
65
  "auth.password.requirements.special": "Un carácter especial",
66
66
  "auth.password.requirements.uppercase": "Una letra mayúscula",
67
67
  "auth.profile.form.confirmPassword": "Confirmar nueva contraseña",
68
+ "auth.profile.form.currentPassword": "Contraseña actual",
68
69
  "auth.profile.form.email": "Correo electrónico",
70
+ "auth.profile.form.errors.confirmPasswordRequired": "Confirma la nueva contraseña.",
71
+ "auth.profile.form.errors.currentPasswordInvalid": "La contraseña actual es incorrecta.",
72
+ "auth.profile.form.errors.currentPasswordRequired": "La contraseña actual es obligatoria.",
73
+ "auth.profile.form.errors.emailOrPasswordRequired": "Proporciona un correo electrónico o una contraseña.",
69
74
  "auth.profile.form.errors.emailRequired": "El correo electrónico es obligatorio.",
70
75
  "auth.profile.form.errors.invalid": "Actualización de perfil inválida.",
71
76
  "auth.profile.form.errors.load": "No se pudo cargar el perfil.",
77
+ "auth.profile.form.errors.newPasswordRequired": "La nueva contraseña es obligatoria.",
72
78
  "auth.profile.form.errors.noChanges": "No hay cambios para guardar.",
73
79
  "auth.profile.form.errors.passwordMismatch": "Las contraseñas no coinciden.",
74
80
  "auth.profile.form.errors.passwordRequirements": "La contraseña debe cumplir los requisitos.",
@@ -65,10 +65,16 @@
65
65
  "auth.password.requirements.special": "Jeden znak specjalny",
66
66
  "auth.password.requirements.uppercase": "Jedna wielka litera",
67
67
  "auth.profile.form.confirmPassword": "Potwierdź nowe hasło",
68
+ "auth.profile.form.currentPassword": "Obecne hasło",
68
69
  "auth.profile.form.email": "Email",
70
+ "auth.profile.form.errors.confirmPasswordRequired": "Potwierdź nowe hasło.",
71
+ "auth.profile.form.errors.currentPasswordInvalid": "Obecne hasło jest nieprawidłowe.",
72
+ "auth.profile.form.errors.currentPasswordRequired": "Obecne hasło jest wymagane.",
73
+ "auth.profile.form.errors.emailOrPasswordRequired": "Podaj adres e-mail lub hasło.",
69
74
  "auth.profile.form.errors.emailRequired": "Email jest wymagany.",
70
75
  "auth.profile.form.errors.invalid": "Nieprawidłowa aktualizacja profilu.",
71
76
  "auth.profile.form.errors.load": "Nie udało się wczytać profilu.",
77
+ "auth.profile.form.errors.newPasswordRequired": "Nowe hasło jest wymagane.",
72
78
  "auth.profile.form.errors.noChanges": "Brak zmian do zapisania.",
73
79
  "auth.profile.form.errors.passwordMismatch": "Hasła nie są zgodne.",
74
80
  "auth.profile.form.errors.passwordRequirements": "Hasło musi spełniać wymagania.",
@@ -2,7 +2,7 @@
2
2
 
3
3
  import type { ProductMediaItem } from './ProductMediaManager'
4
4
  import { createLocalId, type PriceKindSummary } from './productForm'
5
- import { parseNumericInput } from './productFormUtils'
5
+ import { isCatalogPriceAmountInputValid } from '../../lib/priceValidation'
6
6
 
7
7
  export type OptionDefinition = {
8
8
  id: string
@@ -107,8 +107,7 @@ export function findInvalidVariantPriceKinds(
107
107
  const draft = priceDrafts?.[kind.id]
108
108
  const amount = typeof draft?.amount === 'string' ? draft.amount.trim() : ''
109
109
  if (!amount) continue
110
- const numeric = parseNumericInput(amount)
111
- if (!Number.isFinite(numeric) || numeric < 0) invalid.push(kind.id)
110
+ if (!isCatalogPriceAmountInputValid(amount)) invalid.push(kind.id)
112
111
  }
113
112
  return invalid
114
113
  }
@@ -1,6 +1,10 @@
1
1
  import { z } from 'zod'
2
2
  import { CATALOG_PRICE_DISPLAY_MODES, CATALOG_PRODUCT_TYPES } from './types'
3
3
  import { REFERENCE_UNIT_CODES } from '../lib/unitCodes'
4
+ import {
5
+ getCatalogPriceAmountValidationMessage,
6
+ validateCatalogPriceAmountInput,
7
+ } from '../lib/priceValidation'
4
8
 
5
9
  const uuid = () => z.string().uuid()
6
10
 
@@ -116,6 +120,18 @@ const unitPriceConfigSchema = z.object({
116
120
  baseQuantity: z.coerce.number().positive().optional(),
117
121
  })
118
122
 
123
+ const catalogPriceAmountSchema = z
124
+ .custom<number>((value) => validateCatalogPriceAmountInput(value).ok, {
125
+ message: getCatalogPriceAmountValidationMessage(),
126
+ })
127
+ .transform((value) => {
128
+ const result = validateCatalogPriceAmountInput(value)
129
+ if (!result.ok) {
130
+ throw new Error('catalogPriceAmountSchema transform reached invalid state')
131
+ }
132
+ return result.numeric
133
+ })
134
+
119
135
  function productUomCrossFieldRefinement(
120
136
  input: {
121
137
  defaultUnit?: string | null
@@ -295,8 +311,8 @@ export const priceCreateSchema = scoped.extend({
295
311
  priceKindId: uuid(),
296
312
  minQuantity: z.coerce.number().int().min(1).optional(),
297
313
  maxQuantity: z.coerce.number().int().min(1).optional(),
298
- unitPriceNet: z.coerce.number().min(0).optional(),
299
- unitPriceGross: z.coerce.number().min(0).optional(),
314
+ unitPriceNet: catalogPriceAmountSchema.optional(),
315
+ unitPriceGross: catalogPriceAmountSchema.optional(),
300
316
  taxRate: z.coerce.number().min(0).max(100).optional(),
301
317
  taxRateId: uuid().nullable().optional(),
302
318
  channelId: uuid().optional(),