@open-mercato/core 0.8.1-develop.7221.1.ece7ca874e → 0.8.1-develop.7227.1.9248c14599
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/catalog/backend/catalog/prices/[id]/edit/page.js +166 -0
- package/dist/modules/catalog/backend/catalog/prices/[id]/edit/page.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/prices/[id]/edit/page.meta.js +16 -0
- package/dist/modules/catalog/backend/catalog/prices/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/prices/create/page.js +68 -0
- package/dist/modules/catalog/backend/catalog/prices/create/page.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/prices/create/page.meta.js +25 -0
- package/dist/modules/catalog/backend/catalog/prices/create/page.meta.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/prices/page.js +11 -0
- package/dist/modules/catalog/backend/catalog/prices/page.js.map +7 -0
- package/dist/modules/catalog/backend/catalog/prices/page.meta.js +16 -0
- package/dist/modules/catalog/backend/catalog/prices/page.meta.js.map +7 -0
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +201 -0
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +7 -0
- package/dist/modules/catalog/components/prices/PricesDataTable.js +273 -0
- package/dist/modules/catalog/components/prices/PricesDataTable.js.map +7 -0
- package/dist/modules/catalog/components/prices/normalizePriceRecord.js +43 -0
- package/dist/modules/catalog/components/prices/normalizePriceRecord.js.map +7 -0
- package/dist/modules/catalog/components/prices/priceFormFields.js +195 -0
- package/dist/modules/catalog/components/prices/priceFormFields.js.map +7 -0
- package/dist/modules/catalog/components/prices/useBatchLabels.js +42 -0
- package/dist/modules/catalog/components/prices/useBatchLabels.js.map +7 -0
- package/dist/modules/catalog/data/validators.js +21 -2
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/extension-points.js +2 -1
- package/dist/modules/catalog/extension-points.js.map +2 -2
- package/dist/modules/catalog/lib/pricing.js +37 -8
- package/dist/modules/catalog/lib/pricing.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +5 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/profile/communication-channels/ConnectChannelMenu.js +88 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/ConnectChannelMenu.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +17 -19
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +7 -2
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +11 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/queue-import-history.js +40 -7
- package/dist/modules/communication_channels/commands/queue-import-history.js.map +2 -2
- package/dist/modules/communication_channels/extension-points.js +1 -1
- package/dist/modules/communication_channels/extension-points.js.map +1 -1
- package/dist/modules/communication_channels/lib/connect-channel.js +33 -0
- package/dist/modules/communication_channels/lib/connect-channel.js.map +2 -2
- package/dist/modules/communication_channels/lib/import-history-limits.js +69 -0
- package/dist/modules/communication_channels/lib/import-history-limits.js.map +7 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js +36 -2
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +2 -2
- package/dist/modules/messages/api/route.js +5 -1
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +3 -2
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/data/validators.js +12 -2
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/messages/subscribers/message-notification.js +4 -1
- package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +19 -1
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/catalog/AGENTS.md +6 -2
- package/src/modules/catalog/backend/catalog/prices/[id]/edit/page.meta.ts +12 -0
- package/src/modules/catalog/backend/catalog/prices/[id]/edit/page.tsx +198 -0
- package/src/modules/catalog/backend/catalog/prices/create/page.meta.ts +23 -0
- package/src/modules/catalog/backend/catalog/prices/create/page.tsx +70 -0
- package/src/modules/catalog/backend/catalog/prices/page.meta.ts +12 -0
- package/src/modules/catalog/backend/catalog/prices/page.tsx +14 -0
- package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +243 -0
- package/src/modules/catalog/components/prices/PricesDataTable.tsx +320 -0
- package/src/modules/catalog/components/prices/normalizePriceRecord.ts +71 -0
- package/src/modules/catalog/components/prices/priceFormFields.tsx +228 -0
- package/src/modules/catalog/components/prices/useBatchLabels.ts +57 -0
- package/src/modules/catalog/data/validators.ts +43 -2
- package/src/modules/catalog/extension-points.ts +1 -0
- package/src/modules/catalog/i18n/de.json +72 -0
- package/src/modules/catalog/i18n/en.json +72 -0
- package/src/modules/catalog/i18n/es.json +72 -0
- package/src/modules/catalog/i18n/ko.json +72 -0
- package/src/modules/catalog/i18n/pl.json +72 -0
- package/src/modules/catalog/lib/pricing.ts +103 -9
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +6 -1
- package/src/modules/communication_channels/backend/profile/communication-channels/ConnectChannelMenu.tsx +120 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +20 -17
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +9 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +17 -2
- package/src/modules/communication_channels/commands/queue-import-history.ts +59 -11
- package/src/modules/communication_channels/extension-points.ts +1 -1
- package/src/modules/communication_channels/i18n/de.json +4 -3
- package/src/modules/communication_channels/i18n/en.json +4 -3
- package/src/modules/communication_channels/i18n/es.json +4 -3
- package/src/modules/communication_channels/i18n/ko.json +4 -3
- package/src/modules/communication_channels/i18n/pl.json +4 -3
- package/src/modules/communication_channels/lib/adapter.ts +16 -2
- package/src/modules/communication_channels/lib/connect-channel.ts +49 -0
- package/src/modules/communication_channels/lib/import-history-limits.ts +109 -0
- package/src/modules/communication_channels/workers/channel-import-history.ts +46 -5
- package/src/modules/messages/api/route.ts +8 -2
- package/src/modules/messages/commands/messages.ts +17 -2
- package/src/modules/messages/data/validators.ts +26 -11
- package/src/modules/messages/subscribers/message-notification.ts +17 -5
- package/src/modules/query_index/lib/engine.ts +23 -1
- package/src/modules/sales/AGENTS.md +2 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
4
|
+
function useBatchLabels(path, ids, mapItem) {
|
|
5
|
+
const [labels, setLabels] = React.useState({});
|
|
6
|
+
const key = React.useMemo(
|
|
7
|
+
() => Array.from(new Set(ids.filter((id) => typeof id === "string" && id.length > 0))).sort((a, b) => a < b ? -1 : a > b ? 1 : 0).join(","),
|
|
8
|
+
[ids]
|
|
9
|
+
);
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
if (!key) return;
|
|
12
|
+
let cancelled = false;
|
|
13
|
+
async function load() {
|
|
14
|
+
try {
|
|
15
|
+
const idCount = key.split(",").length;
|
|
16
|
+
const payload = await readApiResultOrThrow(
|
|
17
|
+
`${path}?ids=${encodeURIComponent(key)}&pageSize=${Math.min(100, idCount)}`,
|
|
18
|
+
void 0,
|
|
19
|
+
{ fallback: { items: [] } }
|
|
20
|
+
);
|
|
21
|
+
if (cancelled) return;
|
|
22
|
+
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
23
|
+
const next = {};
|
|
24
|
+
for (const item of items) {
|
|
25
|
+
const mapped = mapItem(item);
|
|
26
|
+
if (mapped) next[mapped.id] = mapped.label;
|
|
27
|
+
}
|
|
28
|
+
setLabels((prev) => ({ ...prev, ...next }));
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
void load();
|
|
33
|
+
return () => {
|
|
34
|
+
cancelled = true;
|
|
35
|
+
};
|
|
36
|
+
}, [key, path, mapItem]);
|
|
37
|
+
return labels;
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
useBatchLabels
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=useBatchLabels.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/catalog/components/prices/useBatchLabels.ts"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\n\ntype LabelMap = Record<string, string>\n\n/**\n * Resolves display names for a page's worth of foreign ids in one batched\n * request via the CRUD factory's generic `?ids=` narrowing (see\n * `packages/core/AGENTS.md` \u00A7 API Interceptors) instead of one lookup per row.\n */\nexport function useBatchLabels(\n path: string,\n ids: string[],\n mapItem: (item: Record<string, unknown>) => { id: string; label: string } | null,\n): LabelMap {\n const [labels, setLabels] = React.useState<LabelMap>({})\n const key = React.useMemo(\n () =>\n Array.from(new Set(ids.filter((id): id is string => typeof id === 'string' && id.length > 0)))\n .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))\n .join(','),\n [ids],\n )\n\n React.useEffect(() => {\n if (!key) return\n let cancelled = false\n async function load() {\n try {\n const idCount = key.split(',').length\n const payload = await readApiResultOrThrow<{ items?: Array<Record<string, unknown>> }>(\n `${path}?ids=${encodeURIComponent(key)}&pageSize=${Math.min(100, idCount)}`,\n undefined,\n { fallback: { items: [] } },\n )\n if (cancelled) return\n const items = Array.isArray(payload.items) ? payload.items : []\n const next: LabelMap = {}\n for (const item of items) {\n const mapped = mapItem(item)\n if (mapped) next[mapped.id] = mapped.label\n }\n setLabels((prev) => ({ ...prev, ...next }))\n } catch {\n // Best-effort: callers fall back to the raw id when a label is missing.\n }\n }\n void load()\n return () => {\n cancelled = true\n }\n }, [key, path, mapItem])\n\n return labels\n}\n"],
|
|
5
|
+
"mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,4BAA4B;AAS9B,SAAS,eACd,MACA,KACA,SACU;AACV,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAmB,CAAC,CAAC;AACvD,QAAM,MAAM,MAAM;AAAA,IAChB,MACE,MAAM,KAAK,IAAI,IAAI,IAAI,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,EAC1F,KAAK,CAAC,GAAG,MAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAE,EAC3C,KAAK,GAAG;AAAA,IACb,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAK;AACV,QAAI,YAAY;AAChB,mBAAe,OAAO;AACpB,UAAI;AACF,cAAM,UAAU,IAAI,MAAM,GAAG,EAAE;AAC/B,cAAM,UAAU,MAAM;AAAA,UACpB,GAAG,IAAI,QAAQ,mBAAmB,GAAG,CAAC,aAAa,KAAK,IAAI,KAAK,OAAO,CAAC;AAAA,UACzE;AAAA,UACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,QAC5B;AACA,YAAI,UAAW;AACf,cAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,cAAM,OAAiB,CAAC;AACxB,mBAAW,QAAQ,OAAO;AACxB,gBAAM,SAAS,QAAQ,IAAI;AAC3B,cAAI,OAAQ,MAAK,OAAO,EAAE,IAAI,OAAO;AAAA,QACvC;AACA,kBAAU,CAAC,UAAU,EAAE,GAAG,MAAM,GAAG,KAAK,EAAE;AAAA,MAC5C,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,KAAK;AACV,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,KAAK,MAAM,OAAO,CAAC;AAEvB,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -303,7 +303,7 @@ const priceKindCreateSchema = tenantScoped.extend({
|
|
|
303
303
|
const priceKindUpdateSchema = z.object({
|
|
304
304
|
id: uuid()
|
|
305
305
|
}).merge(priceKindCreateSchema.partial());
|
|
306
|
-
const
|
|
306
|
+
const priceBaseSchema = scoped.extend({
|
|
307
307
|
variantId: uuid().optional(),
|
|
308
308
|
productId: uuid().optional(),
|
|
309
309
|
offerId: uuid().optional(),
|
|
@@ -324,9 +324,28 @@ const priceCreateSchema = scoped.extend({
|
|
|
324
324
|
startsAt: z.coerce.date().optional(),
|
|
325
325
|
endsAt: z.coerce.date().optional()
|
|
326
326
|
});
|
|
327
|
+
const priceQuantityRangeRefinement = (input, ctx) => {
|
|
328
|
+
if (input.minQuantity != null && input.maxQuantity != null && input.maxQuantity < input.minQuantity) {
|
|
329
|
+
ctx.addIssue({
|
|
330
|
+
code: z.ZodIssueCode.custom,
|
|
331
|
+
path: ["maxQuantity"],
|
|
332
|
+
message: "catalog.prices.validation.quantityRange"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const priceValidityWindowRefinement = (input, ctx) => {
|
|
337
|
+
if (input.startsAt && input.endsAt && input.endsAt < input.startsAt) {
|
|
338
|
+
ctx.addIssue({
|
|
339
|
+
code: z.ZodIssueCode.custom,
|
|
340
|
+
path: ["endsAt"],
|
|
341
|
+
message: "catalog.prices.validation.validityWindow"
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const priceCreateSchema = priceBaseSchema.superRefine(priceQuantityRangeRefinement).superRefine(priceValidityWindowRefinement);
|
|
327
346
|
const priceUpdateSchema = z.object({
|
|
328
347
|
id: uuid()
|
|
329
|
-
}).merge(
|
|
348
|
+
}).merge(priceBaseSchema.partial()).superRefine(priceQuantityRangeRefinement).superRefine(priceValidityWindowRefinement);
|
|
330
349
|
const categoryCreateSchema = scoped.extend({
|
|
331
350
|
name: z.string().trim().min(1).max(255),
|
|
332
351
|
slug: slugSchema.optional().nullable(),
|
|
@@ -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 {\n CATALOG_EXCISE_CATEGORIES,\n CATALOG_GTIN_TYPES,\n CATALOG_GTU_CODES,\n CATALOG_HAZMAT_PACKING_GROUPS,\n CATALOG_PRICE_DISPLAY_MODES,\n CATALOG_PRODUCT_TYPES,\n} from './types'\nimport { isValidGtin, normalizeGtinValue } from '../lib/gtin'\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.nullable().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 countryOfOriginCode: z\n .string()\n .trim()\n .toUpperCase()\n .regex(/^[A-Z]{2}$/, 'catalog.products.validation.countryCodeInvalid')\n .nullable()\n .optional(),\n pkwiuCode: z.string().trim().min(1).max(32).nullable().optional(),\n cnCode: z.string().trim().min(1).max(32).nullable().optional(),\n hsCode: z.string().trim().min(1).max(32).nullable().optional(),\n taxClassificationCode: z.string().trim().min(1).max(64).nullable().optional(),\n gtuCodes: z\n .array(z.enum(CATALOG_GTU_CODES))\n .max(13)\n .transform((codes) => Array.from(new Set(codes)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)))\n .nullable()\n .optional(),\n ageMin: z.coerce.number().int().min(0).max(120).nullable().optional(),\n isExciseGood: z.boolean().optional(),\n exciseCategory: z.enum(CATALOG_EXCISE_CATEGORIES).nullable().optional(),\n requiresPrescription: z.boolean().optional(),\n hazmatClass: z.string().trim().min(1).max(32).nullable().optional(),\n unNumber: z\n .string()\n .trim()\n .regex(/^(?:UN)?[0-9]{4}$/i, 'catalog.products.validation.unNumberInvalid')\n .transform((value) => {\n const upper = value.toUpperCase()\n return upper.startsWith('UN') ? upper : `UN${upper}`\n })\n .nullable()\n .optional(),\n hazmatPackingGroup: z.enum(CATALOG_HAZMAT_PACKING_GROUPS).nullable().optional(),\n containsLithiumBattery: z.boolean().optional(),\n launchAt: z.coerce.date().nullable().optional(),\n endOfLifeAt: z.coerce.date().nullable().optional(),\n availableFrom: z.coerce.date().nullable().optional(),\n availableUntil: z.coerce.date().nullable().optional(),\n minOrderQty: z.coerce.number().int().min(1).nullable().optional(),\n maxOrderQty: z.coerce.number().int().min(1).nullable().optional(),\n orderQtyIncrement: z.coerce.number().int().min(1).nullable().optional(),\n requiresShipping: z.boolean().optional(),\n isQuoteOnly: z.boolean().optional(),\n seoTitle: z.string().trim().min(1).max(255).nullable().optional(),\n seoDescription: z.string().trim().min(1).max(1000).nullable().optional(),\n canonicalUrl: z\n .string()\n .trim()\n .max(500)\n .regex(/^https?:\\/\\/\\S+$/, 'catalog.products.validation.canonicalUrlInvalid')\n .nullable()\n .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\n// Cross-field checks chained on the exported schemas only \u2014 refining the base\n// schema itself would break `.partial()` in the update schema.\nconst productComplianceCrossFieldRefinement = (\n input: {\n minOrderQty?: number | null\n maxOrderQty?: number | null\n launchAt?: Date | null\n endOfLifeAt?: Date | null\n availableFrom?: Date | null\n availableUntil?: Date | null\n },\n ctx: z.RefinementCtx,\n) => {\n if (\n input.minOrderQty != null &&\n input.maxOrderQty != null &&\n input.maxOrderQty < input.minOrderQty\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['maxOrderQty'],\n message: 'catalog.products.validation.orderQtyRange',\n })\n }\n if (input.launchAt && input.endOfLifeAt && input.endOfLifeAt < input.launchAt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['endOfLifeAt'],\n message: 'catalog.products.validation.lifecycleDateRange',\n })\n }\n if (\n input.availableFrom &&\n input.availableUntil &&\n input.availableUntil < input.availableFrom\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['availableUntil'],\n message: 'catalog.products.validation.availabilityDateRange',\n })\n }\n}\n\nexport const productCreateSchema = productBaseSchema\n .superRefine(productUomCrossFieldRefinement)\n .superRefine(productComplianceCrossFieldRefinement)\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 .superRefine(productComplianceCrossFieldRefinement)\n\n// Base schema without refinements (used for .partial() in update schema)\nconst variantBaseSchema = 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 gtinType: z.enum(CATALOG_GTIN_TYPES).nullable().optional(),\n hsCode: z.string().trim().min(1).max(32).nullable().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\n// Payload-level GTIN check. Partial updates carrying only one of\n// (gtinType, barcode) are re-validated against the merged record state in the\n// catalog.variants.update command \u2014 zod cannot see the stored half here.\nconst variantGtinRefinement =\n (mode: 'create' | 'update') =>\n (\n input: {\n gtinType?: (typeof CATALOG_GTIN_TYPES)[number] | null\n barcode?: string\n },\n ctx: z.RefinementCtx,\n ) => {\n const gtinType = input.gtinType\n if (!gtinType) return\n const barcode = input.barcode\n if (barcode === undefined) {\n if (mode === 'create') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinBarcodeRequired',\n })\n }\n return\n }\n if (!barcode.trim().length) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinBarcodeRequired',\n })\n return\n }\n if (!isValidGtin(gtinType, normalizeGtinValue(gtinType, barcode))) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinChecksum',\n })\n }\n }\n\nexport const variantCreateSchema = variantBaseSchema.superRefine(\n variantGtinRefinement('create'),\n)\n\nexport const variantUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(variantBaseSchema.partial())\n .superRefine(variantGtinRefinement('update'))\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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",
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\nimport {\n CATALOG_EXCISE_CATEGORIES,\n CATALOG_GTIN_TYPES,\n CATALOG_GTU_CODES,\n CATALOG_HAZMAT_PACKING_GROUPS,\n CATALOG_PRICE_DISPLAY_MODES,\n CATALOG_PRODUCT_TYPES,\n} from './types'\nimport { isValidGtin, normalizeGtinValue } from '../lib/gtin'\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.nullable().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 countryOfOriginCode: z\n .string()\n .trim()\n .toUpperCase()\n .regex(/^[A-Z]{2}$/, 'catalog.products.validation.countryCodeInvalid')\n .nullable()\n .optional(),\n pkwiuCode: z.string().trim().min(1).max(32).nullable().optional(),\n cnCode: z.string().trim().min(1).max(32).nullable().optional(),\n hsCode: z.string().trim().min(1).max(32).nullable().optional(),\n taxClassificationCode: z.string().trim().min(1).max(64).nullable().optional(),\n gtuCodes: z\n .array(z.enum(CATALOG_GTU_CODES))\n .max(13)\n .transform((codes) => Array.from(new Set(codes)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)))\n .nullable()\n .optional(),\n ageMin: z.coerce.number().int().min(0).max(120).nullable().optional(),\n isExciseGood: z.boolean().optional(),\n exciseCategory: z.enum(CATALOG_EXCISE_CATEGORIES).nullable().optional(),\n requiresPrescription: z.boolean().optional(),\n hazmatClass: z.string().trim().min(1).max(32).nullable().optional(),\n unNumber: z\n .string()\n .trim()\n .regex(/^(?:UN)?[0-9]{4}$/i, 'catalog.products.validation.unNumberInvalid')\n .transform((value) => {\n const upper = value.toUpperCase()\n return upper.startsWith('UN') ? upper : `UN${upper}`\n })\n .nullable()\n .optional(),\n hazmatPackingGroup: z.enum(CATALOG_HAZMAT_PACKING_GROUPS).nullable().optional(),\n containsLithiumBattery: z.boolean().optional(),\n launchAt: z.coerce.date().nullable().optional(),\n endOfLifeAt: z.coerce.date().nullable().optional(),\n availableFrom: z.coerce.date().nullable().optional(),\n availableUntil: z.coerce.date().nullable().optional(),\n minOrderQty: z.coerce.number().int().min(1).nullable().optional(),\n maxOrderQty: z.coerce.number().int().min(1).nullable().optional(),\n orderQtyIncrement: z.coerce.number().int().min(1).nullable().optional(),\n requiresShipping: z.boolean().optional(),\n isQuoteOnly: z.boolean().optional(),\n seoTitle: z.string().trim().min(1).max(255).nullable().optional(),\n seoDescription: z.string().trim().min(1).max(1000).nullable().optional(),\n canonicalUrl: z\n .string()\n .trim()\n .max(500)\n .regex(/^https?:\\/\\/\\S+$/, 'catalog.products.validation.canonicalUrlInvalid')\n .nullable()\n .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\n// Cross-field checks chained on the exported schemas only \u2014 refining the base\n// schema itself would break `.partial()` in the update schema.\nconst productComplianceCrossFieldRefinement = (\n input: {\n minOrderQty?: number | null\n maxOrderQty?: number | null\n launchAt?: Date | null\n endOfLifeAt?: Date | null\n availableFrom?: Date | null\n availableUntil?: Date | null\n },\n ctx: z.RefinementCtx,\n) => {\n if (\n input.minOrderQty != null &&\n input.maxOrderQty != null &&\n input.maxOrderQty < input.minOrderQty\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['maxOrderQty'],\n message: 'catalog.products.validation.orderQtyRange',\n })\n }\n if (input.launchAt && input.endOfLifeAt && input.endOfLifeAt < input.launchAt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['endOfLifeAt'],\n message: 'catalog.products.validation.lifecycleDateRange',\n })\n }\n if (\n input.availableFrom &&\n input.availableUntil &&\n input.availableUntil < input.availableFrom\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['availableUntil'],\n message: 'catalog.products.validation.availabilityDateRange',\n })\n }\n}\n\nexport const productCreateSchema = productBaseSchema\n .superRefine(productUomCrossFieldRefinement)\n .superRefine(productComplianceCrossFieldRefinement)\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 .superRefine(productComplianceCrossFieldRefinement)\n\n// Base schema without refinements (used for .partial() in update schema)\nconst variantBaseSchema = 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 gtinType: z.enum(CATALOG_GTIN_TYPES).nullable().optional(),\n hsCode: z.string().trim().min(1).max(32).nullable().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\n// Payload-level GTIN check. Partial updates carrying only one of\n// (gtinType, barcode) are re-validated against the merged record state in the\n// catalog.variants.update command \u2014 zod cannot see the stored half here.\nconst variantGtinRefinement =\n (mode: 'create' | 'update') =>\n (\n input: {\n gtinType?: (typeof CATALOG_GTIN_TYPES)[number] | null\n barcode?: string\n },\n ctx: z.RefinementCtx,\n ) => {\n const gtinType = input.gtinType\n if (!gtinType) return\n const barcode = input.barcode\n if (barcode === undefined) {\n if (mode === 'create') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinBarcodeRequired',\n })\n }\n return\n }\n if (!barcode.trim().length) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinBarcodeRequired',\n })\n return\n }\n if (!isValidGtin(gtinType, normalizeGtinValue(gtinType, barcode))) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['barcode'],\n message: 'catalog.variants.validation.gtinChecksum',\n })\n }\n }\n\nexport const variantCreateSchema = variantBaseSchema.superRefine(\n variantGtinRefinement('create'),\n)\n\nexport const variantUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(variantBaseSchema.partial())\n .superRefine(variantGtinRefinement('update'))\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\n// Base schema without refinements (used for .partial() in the update schema).\nconst priceBaseSchema = 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\n// Both checks only fire when both sides of the comparison are present in this\n// payload \u2014 a partial update touching only one side is not re-validated against\n// the stored record here (same limitation the GTIN cross-field check documents\n// above: zod cannot see the merged state, only the command layer can).\nconst priceQuantityRangeRefinement = (\n input: { minQuantity?: number; maxQuantity?: number },\n ctx: z.RefinementCtx,\n) => {\n if (\n input.minQuantity != null &&\n input.maxQuantity != null &&\n input.maxQuantity < input.minQuantity\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['maxQuantity'],\n message: 'catalog.prices.validation.quantityRange',\n })\n }\n}\n\nconst priceValidityWindowRefinement = (\n input: { startsAt?: Date; endsAt?: Date },\n ctx: z.RefinementCtx,\n) => {\n if (input.startsAt && input.endsAt && input.endsAt < input.startsAt) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['endsAt'],\n message: 'catalog.prices.validation.validityWindow',\n })\n }\n}\n\nexport const priceCreateSchema = priceBaseSchema\n .superRefine(priceQuantityRangeRefinement)\n .superRefine(priceValidityWindowRefinement)\n\nexport const priceUpdateSchema = z\n .object({\n id: uuid(),\n })\n .merge(priceBaseSchema.partial())\n .superRefine(priceQuantityRangeRefinement)\n .superRefine(priceValidityWindowRefinement)\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
|
}
|
|
@@ -16,7 +16,8 @@ const extensionPoints = defineModuleExtensionPoints({
|
|
|
16
16
|
tableId: "catalog.products.list",
|
|
17
17
|
source: "components/products/ProductsDataTable.tsx"
|
|
18
18
|
}),
|
|
19
|
-
categoriesTable: dataTableExtensionHost({ tableId: "catalog.categories.list", source: "components/categories/CategoriesDataTable.tsx" })
|
|
19
|
+
categoriesTable: dataTableExtensionHost({ tableId: "catalog.categories.list", source: "components/categories/CategoriesDataTable.tsx" }),
|
|
20
|
+
pricesTable: dataTableExtensionHost({ tableId: "catalog.prices.list", source: "components/prices/PricesDataTable.tsx" })
|
|
20
21
|
}
|
|
21
22
|
});
|
|
22
23
|
var extension_points_default = extensionPoints;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/catalog/extension-points.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n crudFormExtensionHost,\n dataTableExtensionHost,\n defineModuleExtensionPoints,\n} from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const extensionPoints = defineModuleExtensionPoints({\n moduleId: 'catalog',\n hosts: {\n productForm: crudFormExtensionHost({\n entityId: 'catalog.product',\n spotId: 'crud-form:catalog.product',\n source: 'backend/catalog/products/[id]/page.tsx',\n }),\n productsTable: dataTableExtensionHost({\n baseSpotId: 'data-table:catalog.products',\n tableId: 'catalog.products.list',\n source: 'components/products/ProductsDataTable.tsx',\n }),\n categoriesTable: dataTableExtensionHost({ tableId: 'catalog.categories.list', source: 'components/categories/CategoriesDataTable.tsx' }),\n },\n})\n\nexport default extensionPoints\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,kBAAkB,4BAA4B;AAAA,EACzD,UAAU;AAAA,EACV,OAAO;AAAA,IACL,aAAa,sBAAsB;AAAA,MACjC,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAAA,IACD,eAAe,uBAAuB;AAAA,MACpC,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,IACD,iBAAiB,uBAAuB,EAAE,SAAS,2BAA2B,QAAQ,gDAAgD,CAAC;AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n crudFormExtensionHost,\n dataTableExtensionHost,\n defineModuleExtensionPoints,\n} from '@open-mercato/shared/modules/widgets/extension-points'\n\nexport const extensionPoints = defineModuleExtensionPoints({\n moduleId: 'catalog',\n hosts: {\n productForm: crudFormExtensionHost({\n entityId: 'catalog.product',\n spotId: 'crud-form:catalog.product',\n source: 'backend/catalog/products/[id]/page.tsx',\n }),\n productsTable: dataTableExtensionHost({\n baseSpotId: 'data-table:catalog.products',\n tableId: 'catalog.products.list',\n source: 'components/products/ProductsDataTable.tsx',\n }),\n categoriesTable: dataTableExtensionHost({ tableId: 'catalog.categories.list', source: 'components/categories/CategoriesDataTable.tsx' }),\n pricesTable: dataTableExtensionHost({ tableId: 'catalog.prices.list', source: 'components/prices/PricesDataTable.tsx' }),\n },\n})\n\nexport default extensionPoints\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,kBAAkB,4BAA4B;AAAA,EACzD,UAAU;AAAA,EACV,OAAO;AAAA,IACL,aAAa,sBAAsB;AAAA,MACjC,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AAAA,IACD,eAAe,uBAAuB;AAAA,MACpC,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,IACD,iBAAiB,uBAAuB,EAAE,SAAS,2BAA2B,QAAQ,gDAAgD,CAAC;AAAA,IACvI,aAAa,uBAAuB,EAAE,SAAS,uBAAuB,QAAQ,wCAAwC,CAAC;AAAA,EACzH;AACF,CAAC;AAED,IAAO,2BAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -32,10 +32,28 @@ function matchesContext(row, ctx) {
|
|
|
32
32
|
if (row.userId && ctx.userId !== row.userId) return false;
|
|
33
33
|
if (row.userGroupId && ctx.userGroupId !== row.userGroupId) return false;
|
|
34
34
|
if (row.customerId && ctx.customerId !== row.customerId) return false;
|
|
35
|
-
if (row.customerGroupId
|
|
35
|
+
if (row.customerGroupId) {
|
|
36
|
+
const candidateGroupIds = ctx.customerGroupIds ?? (ctx.customerGroupId ? [ctx.customerGroupId] : []);
|
|
37
|
+
if (!candidateGroupIds.includes(row.customerGroupId)) return false;
|
|
38
|
+
}
|
|
39
|
+
if (ctx.currencyCode && row.currencyCode !== ctx.currencyCode) return false;
|
|
36
40
|
if (ctx.offerId && resolvePriceOfferId(row) && resolvePriceOfferId(row) !== ctx.offerId) return false;
|
|
37
41
|
return true;
|
|
38
42
|
}
|
|
43
|
+
function buildPriceRowFilter(ctx) {
|
|
44
|
+
const customerGroupIds = ctx.customerGroupIds ?? (ctx.customerGroupId ? [ctx.customerGroupId] : []);
|
|
45
|
+
const clauses = [
|
|
46
|
+
ctx.customerId ? { $or: [{ customerId: null }, { customerId: ctx.customerId }] } : { customerId: null },
|
|
47
|
+
customerGroupIds.length ? { $or: [{ customerGroupId: null }, { customerGroupId: { $in: customerGroupIds } }] } : { customerGroupId: null },
|
|
48
|
+
ctx.userId ? { $or: [{ userId: null }, { userId: ctx.userId }] } : { userId: null },
|
|
49
|
+
ctx.userGroupId ? { $or: [{ userGroupId: null }, { userGroupId: ctx.userGroupId }] } : { userGroupId: null },
|
|
50
|
+
ctx.channelId ? { $or: [{ channelId: null }, { channelId: ctx.channelId }] } : { channelId: null }
|
|
51
|
+
];
|
|
52
|
+
if (ctx.currencyCode) {
|
|
53
|
+
clauses.push({ currencyCode: ctx.currencyCode });
|
|
54
|
+
}
|
|
55
|
+
return { $and: clauses };
|
|
56
|
+
}
|
|
39
57
|
function scorePrice(row) {
|
|
40
58
|
const resolvedKind = resolvePriceKindCode(row);
|
|
41
59
|
let score = 0;
|
|
@@ -69,16 +87,26 @@ function selectBestPrice(rows, ctx) {
|
|
|
69
87
|
});
|
|
70
88
|
return candidates[0];
|
|
71
89
|
}
|
|
72
|
-
const
|
|
73
|
-
function
|
|
74
|
-
|
|
90
|
+
const GLOBAL_PRICING_REGISTRY_KEY = "__openMercatoCatalogPricingRegistry__";
|
|
91
|
+
function getPricingRegistryState() {
|
|
92
|
+
const globalRegistry = globalThis;
|
|
93
|
+
if (!globalRegistry[GLOBAL_PRICING_REGISTRY_KEY]) {
|
|
94
|
+
globalRegistry[GLOBAL_PRICING_REGISTRY_KEY] = { resolvers: [] };
|
|
95
|
+
}
|
|
96
|
+
return globalRegistry[GLOBAL_PRICING_REGISTRY_KEY];
|
|
97
|
+
}
|
|
98
|
+
function sortResolvers(state) {
|
|
99
|
+
state.resolvers.sort((a, b) => b.priority - a.priority);
|
|
75
100
|
}
|
|
76
101
|
function registerCatalogPricingResolver(resolver, options) {
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
const state = getPricingRegistryState();
|
|
103
|
+
const id = options?.id;
|
|
104
|
+
if (id && state.resolvers.some((entry) => entry.id === id)) return;
|
|
105
|
+
state.resolvers.push({ id, resolver, priority: options?.priority ?? 0 });
|
|
106
|
+
sortResolvers(state);
|
|
79
107
|
}
|
|
80
108
|
function resetCatalogPricingResolvers() {
|
|
81
|
-
|
|
109
|
+
getPricingRegistryState().resolvers.splice(0);
|
|
82
110
|
}
|
|
83
111
|
async function resolveCatalogPrice(rows, ctx, options) {
|
|
84
112
|
let workingRows = rows;
|
|
@@ -101,7 +129,7 @@ async function resolveCatalogPrice(rows, ctx, options) {
|
|
|
101
129
|
});
|
|
102
130
|
if (resolved !== void 0) return resolved;
|
|
103
131
|
}
|
|
104
|
-
for (const { resolver } of
|
|
132
|
+
for (const { resolver } of getPricingRegistryState().resolvers) {
|
|
105
133
|
const result = await resolver(workingRows, workingContext);
|
|
106
134
|
if (result !== void 0) {
|
|
107
135
|
resolved = result ?? null;
|
|
@@ -129,6 +157,7 @@ async function resolveCatalogPriceBatch(entries, options) {
|
|
|
129
157
|
);
|
|
130
158
|
}
|
|
131
159
|
export {
|
|
160
|
+
buildPriceRowFilter,
|
|
132
161
|
registerCatalogPricingResolver,
|
|
133
162
|
resetCatalogPricingResolvers,
|
|
134
163
|
resolveCatalogPrice,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/lib/pricing.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EventBus } from '@open-mercato/events'\nimport type {\n CatalogOffer,\n CatalogPriceKind,\n CatalogProduct,\n CatalogProductPrice,\n CatalogProductVariant,\n} from '../data/entities'\n\nexport type PricingContext = {\n channelId?: string | null\n offerId?: string | null\n userId?: string | null\n userGroupId?: string | null\n customerId?: string | null\n customerGroupId?: string | null\n quantity: number\n date: Date\n}\n\nexport type PriceRow = CatalogProductPrice & {\n product?: CatalogProduct | string | null\n variant?: CatalogProductVariant | string | null\n offer?: CatalogOffer | string | null\n priceKind?: CatalogPriceKind | string | null\n}\n\nexport function resolvePriceVariantId(row: PriceRow): string | null {\n if (!row.variant) return null\n return typeof row.variant === 'string' ? row.variant : row.variant.id\n}\n\nexport function resolvePriceOfferId(row: PriceRow): string | null {\n if (!row.offer) return null\n return typeof row.offer === 'string' ? row.offer : row.offer.id\n}\n\nexport function resolvePriceChannelId(row: PriceRow): string | null {\n if (!row.offer) return row.channelId ?? null\n if (typeof row.offer === 'string') return row.channelId ?? null\n return row.channelId ?? row.offer.channelId ?? null\n}\n\nexport function resolvePriceKindCode(row: PriceRow): string {\n if (row.priceKind) {\n if (typeof row.priceKind === 'string') return row.priceKind\n return row.priceKind.code ?? row.kind ?? ''\n }\n return row.kind ?? ''\n}\n\nfunction matchesContext(row: PriceRow, ctx: PricingContext): boolean {\n const { quantity, date } = ctx\n if (row.minQuantity && quantity < row.minQuantity) return false\n if (row.maxQuantity && quantity > row.maxQuantity) return false\n if (row.startsAt && date < row.startsAt) return false\n if (row.endsAt && date > row.endsAt) return false\n if (row.channelId || (row.offer && resolvePriceChannelId(row))) {\n const channel = resolvePriceChannelId(row)\n if (channel && ctx.channelId && channel !== ctx.channelId) return false\n if (channel && !ctx.channelId) return false\n }\n if (row.userId && ctx.userId !== row.userId) return false\n if (row.userGroupId && ctx.userGroupId !== row.userGroupId) return false\n if (row.customerId && ctx.customerId !== row.customerId) return false\n if (row.customerGroupId && ctx.customerGroupId !== row.customerGroupId) return false\n if (ctx.offerId && resolvePriceOfferId(row) && resolvePriceOfferId(row) !== ctx.offerId) return false\n return true\n}\n\nfunction scorePrice(row: PriceRow): number {\n const resolvedKind = resolvePriceKindCode(row)\n let score = 0\n if (resolvedKind === 'custom') score += 5\n else if (resolvedKind === 'tier') score += 3\n else if (resolvedKind === 'promotion' || row.priceKind?.isPromotion) score += 4\n else score += 2\n if (row.variant) score += 8\n if (row.offer) score += 6\n if (row.channelId) score += 5\n if (row.userId) score += 5\n if (row.userGroupId) score += 4\n if (row.customerId) score += 4\n if (row.customerGroupId) score += 3\n if (row.minQuantity && row.minQuantity > 1) score += 1\n return score\n}\n\nexport function selectBestPrice(rows: PriceRow[], ctx: PricingContext): PriceRow | null {\n const candidates = rows.filter((row) => matchesContext(row, ctx))\n if (!candidates.length) return null\n candidates.sort((a, b) => {\n const scoreDiff = scorePrice(b) - scorePrice(a)\n if (scoreDiff !== 0) return scoreDiff\n const startA = a.startsAt ? a.startsAt.getTime() : 0\n const startB = b.startsAt ? b.startsAt.getTime() : 0\n if (startA !== startB) return startB - startA\n // minQuantity tie-break is direction-dependent on the resolved kind.\n // Within the same kind we pick the more specific tier (higher minQuantity wins \u2014 issue #1706).\n // Across kinds we keep the pre-#1706 ascending order so a row whose kind has a higher\n // scoreBase still wins when scorePrice's \"+1 for minQuantity > 1\" bonus produces a\n // cross-kind collision (e.g. promotion[minQty=1]=4 vs tier[minQty=3]=4).\n if (resolvePriceKindCode(a) === resolvePriceKindCode(b)) {\n return (b.minQuantity ?? 1) - (a.minQuantity ?? 1)\n }\n return (a.minQuantity ?? 1) - (b.minQuantity ?? 1)\n })\n return candidates[0]\n}\n\nexport type CatalogPricingResolver = (\n rows: PriceRow[],\n ctx: PricingContext\n) => PriceRow | null | undefined | Promise<PriceRow | null | undefined>\n\ntype RegisteredResolver = {\n resolver: CatalogPricingResolver\n priority: number\n}\n\nconst pricingResolvers: RegisteredResolver[] = []\n\nfunction sortResolvers(): void {\n pricingResolvers.sort((a, b) => b.priority - a.priority)\n}\n\nexport function registerCatalogPricingResolver(\n resolver: CatalogPricingResolver,\n options?: { priority?: number }\n): void {\n pricingResolvers.push({ resolver, priority: options?.priority ?? 0 })\n sortResolvers()\n}\n\nexport function resetCatalogPricingResolvers(): void {\n pricingResolvers.splice(0, pricingResolvers.length)\n}\n\nexport async function resolveCatalogPrice(\n rows: PriceRow[],\n ctx: PricingContext,\n options?: { eventBus?: EventBus | null }\n): Promise<PriceRow | null> {\n let workingRows = rows\n let workingContext = ctx\n const eventBus = options?.eventBus ?? null\n let resolved: PriceRow | null | undefined\n\n if (eventBus) {\n await eventBus.emitEvent('catalog.pricing.resolve.before', {\n rows: workingRows,\n context: workingContext,\n setRows(next: PriceRow[]) {\n if (Array.isArray(next)) workingRows = next\n },\n setContext(next: PricingContext) {\n if (next) workingContext = next\n },\n setResult(next: PriceRow | null) {\n resolved = next\n },\n })\n if (resolved !== undefined) return resolved\n }\n\n for (const { resolver } of pricingResolvers) {\n const result = await resolver(workingRows, workingContext)\n if (result !== undefined) {\n resolved = result ?? null\n break\n }\n }\n\n if (resolved === undefined) {\n resolved = selectBestPrice(workingRows, workingContext)\n }\n\n if (eventBus) {\n await eventBus.emitEvent('catalog.pricing.resolve.after', {\n rows: workingRows,\n context: workingContext,\n result: resolved ?? null,\n setResult(next: PriceRow | null) {\n resolved = next\n },\n })\n }\n\n return resolved ?? null\n}\n\nexport async function resolveCatalogPriceBatch(\n entries: Array<{ rows: PriceRow[]; context: PricingContext }>,\n options?: { eventBus?: EventBus | null }\n): Promise<Array<PriceRow | null>> {\n return Promise.all(\n entries.map(({ rows, context }) => resolveCatalogPrice(rows, context, options))\n )\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { EventBus } from '@open-mercato/events'\nimport type { FilterQuery } from '@mikro-orm/postgresql'\nimport type {\n CatalogOffer,\n CatalogPriceKind,\n CatalogProduct,\n CatalogProductPrice,\n CatalogProductVariant,\n} from '../data/entities'\n\nexport type PricingContext = {\n channelId?: string | null\n offerId?: string | null\n userId?: string | null\n userGroupId?: string | null\n customerId?: string | null\n /** @deprecated use `customerGroupIds` \u2014 kept for backward compatibility, read as a one-element set when `customerGroupIds` is absent. */\n customerGroupId?: string | null\n /** Set membership: a row's `customerGroupId` matches when it appears in this list. Falls back to `customerGroupId` (above) when omitted. */\n customerGroupIds?: string[]\n /** When set, only rows in this currency match. Omitted (the default): no currency filtering, unchanged legacy behavior. */\n currencyCode?: string | null\n quantity: number\n date: Date\n}\n\nexport type PriceRow = CatalogProductPrice & {\n product?: CatalogProduct | string | null\n variant?: CatalogProductVariant | string | null\n offer?: CatalogOffer | string | null\n priceKind?: CatalogPriceKind | string | null\n}\n\nexport function resolvePriceVariantId(row: PriceRow): string | null {\n if (!row.variant) return null\n return typeof row.variant === 'string' ? row.variant : row.variant.id\n}\n\nexport function resolvePriceOfferId(row: PriceRow): string | null {\n if (!row.offer) return null\n return typeof row.offer === 'string' ? row.offer : row.offer.id\n}\n\nexport function resolvePriceChannelId(row: PriceRow): string | null {\n if (!row.offer) return row.channelId ?? null\n if (typeof row.offer === 'string') return row.channelId ?? null\n return row.channelId ?? row.offer.channelId ?? null\n}\n\nexport function resolvePriceKindCode(row: PriceRow): string {\n if (row.priceKind) {\n if (typeof row.priceKind === 'string') return row.priceKind\n return row.priceKind.code ?? row.kind ?? ''\n }\n return row.kind ?? ''\n}\n\nfunction matchesContext(row: PriceRow, ctx: PricingContext): boolean {\n const { quantity, date } = ctx\n if (row.minQuantity && quantity < row.minQuantity) return false\n if (row.maxQuantity && quantity > row.maxQuantity) return false\n if (row.startsAt && date < row.startsAt) return false\n if (row.endsAt && date > row.endsAt) return false\n if (row.channelId || (row.offer && resolvePriceChannelId(row))) {\n const channel = resolvePriceChannelId(row)\n if (channel && ctx.channelId && channel !== ctx.channelId) return false\n if (channel && !ctx.channelId) return false\n }\n if (row.userId && ctx.userId !== row.userId) return false\n if (row.userGroupId && ctx.userGroupId !== row.userGroupId) return false\n if (row.customerId && ctx.customerId !== row.customerId) return false\n if (row.customerGroupId) {\n const candidateGroupIds = ctx.customerGroupIds ?? (ctx.customerGroupId ? [ctx.customerGroupId] : [])\n if (!candidateGroupIds.includes(row.customerGroupId)) return false\n }\n if (ctx.currencyCode && row.currencyCode !== ctx.currencyCode) return false\n if (ctx.offerId && resolvePriceOfferId(row) && resolvePriceOfferId(row) !== ctx.offerId) return false\n return true\n}\n\n/**\n * The narrowing half of `matchesContext` expressed as a MikroORM filter, for\n * callers that fetch `CatalogProductPrice` rows by product/variant today and\n * would otherwise load every contracted customer's rows just to discard\n * almost all of them (per-customer pricing at scale \u2014 see\n * `.ai/specs/2026-08-21-pricing-engine.md` \u00A7 Row narrowing).\n *\n * Covers only the dimensions that are plain column comparisons: customer,\n * customer-group, user, user-group, channel, currency. Quantity bounds,\n * validity windows, and offer-derived channel resolution stay in\n * `matchesContext` \u2014 they are cheap over an already-narrowed set and are not\n * expressible as one column predicate (offer's own `channelId` lives on a\n * different table).\n *\n * **Invariant (one-directional, this is the whole point of the helper):**\n * `matchesContext(row, ctx)` implies this filter admits `row`. The filter\n * MAY admit rows `matchesContext` later rejects (a slightly wide fetch) \u2014\n * it MUST NEVER exclude a row `matchesContext` would have accepted, because\n * that is a silently wrong price with no error anywhere. Verified by the\n * property-based test in `lib/__tests__/buildPriceRowFilter.property.test.ts`.\n */\nexport function buildPriceRowFilter(ctx: PricingContext): FilterQuery<CatalogProductPrice> {\n const customerGroupIds = ctx.customerGroupIds ?? (ctx.customerGroupId ? [ctx.customerGroupId] : [])\n\n const clauses: FilterQuery<CatalogProductPrice>[] = [\n ctx.customerId\n ? { $or: [{ customerId: null }, { customerId: ctx.customerId }] }\n : { customerId: null },\n customerGroupIds.length\n ? { $or: [{ customerGroupId: null }, { customerGroupId: { $in: customerGroupIds } }] }\n : { customerGroupId: null },\n ctx.userId\n ? { $or: [{ userId: null }, { userId: ctx.userId }] }\n : { userId: null },\n ctx.userGroupId\n ? { $or: [{ userGroupId: null }, { userGroupId: ctx.userGroupId }] }\n : { userGroupId: null },\n ctx.channelId\n ? { $or: [{ channelId: null }, { channelId: ctx.channelId }] }\n : { channelId: null },\n ]\n\n if (ctx.currencyCode) {\n clauses.push({ currencyCode: ctx.currencyCode })\n }\n\n return { $and: clauses } as FilterQuery<CatalogProductPrice>\n}\n\nfunction scorePrice(row: PriceRow): number {\n const resolvedKind = resolvePriceKindCode(row)\n let score = 0\n if (resolvedKind === 'custom') score += 5\n else if (resolvedKind === 'tier') score += 3\n else if (resolvedKind === 'promotion' || row.priceKind?.isPromotion) score += 4\n else score += 2\n if (row.variant) score += 8\n if (row.offer) score += 6\n if (row.channelId) score += 5\n if (row.userId) score += 5\n if (row.userGroupId) score += 4\n if (row.customerId) score += 4\n if (row.customerGroupId) score += 3\n if (row.minQuantity && row.minQuantity > 1) score += 1\n return score\n}\n\nexport function selectBestPrice(rows: PriceRow[], ctx: PricingContext): PriceRow | null {\n const candidates = rows.filter((row) => matchesContext(row, ctx))\n if (!candidates.length) return null\n candidates.sort((a, b) => {\n const scoreDiff = scorePrice(b) - scorePrice(a)\n if (scoreDiff !== 0) return scoreDiff\n const startA = a.startsAt ? a.startsAt.getTime() : 0\n const startB = b.startsAt ? b.startsAt.getTime() : 0\n if (startA !== startB) return startB - startA\n // minQuantity tie-break is direction-dependent on the resolved kind.\n // Within the same kind we pick the more specific tier (higher minQuantity wins \u2014 issue #1706).\n // Across kinds we keep the pre-#1706 ascending order so a row whose kind has a higher\n // scoreBase still wins when scorePrice's \"+1 for minQuantity > 1\" bonus produces a\n // cross-kind collision (e.g. promotion[minQty=1]=4 vs tier[minQty=3]=4).\n if (resolvePriceKindCode(a) === resolvePriceKindCode(b)) {\n return (b.minQuantity ?? 1) - (a.minQuantity ?? 1)\n }\n return (a.minQuantity ?? 1) - (b.minQuantity ?? 1)\n })\n return candidates[0]\n}\n\nexport type CatalogPricingResolver = (\n rows: PriceRow[],\n ctx: PricingContext\n) => PriceRow | null | undefined | Promise<PriceRow | null | undefined>\n\ntype RegisteredResolver = {\n id?: string\n resolver: CatalogPricingResolver\n priority: number\n}\n\ntype PricingRegistryState = {\n resolvers: RegisteredResolver[]\n}\n\n// `globalThis`-keyed so the registry survives duplicated module instances\n// (a standalone app built from this monorepo, or any dev/build setup that\n// loads `catalog` through more than one chunk) \u2014 the same failure class\n// already fixed once for the ORM entity registry\n// (see `packages/shared/src/modules/integrations/types.ts` for the identical\n// pattern). A module-local array is invisible across instances: a resolver\n// registered from one instance would silently never run for resolution\n// happening in another.\nconst GLOBAL_PRICING_REGISTRY_KEY = '__openMercatoCatalogPricingRegistry__' as const\n\ntype GlobalPricingRegistry = typeof globalThis & {\n [GLOBAL_PRICING_REGISTRY_KEY]?: PricingRegistryState\n}\n\nfunction getPricingRegistryState(): PricingRegistryState {\n const globalRegistry = globalThis as GlobalPricingRegistry\n if (!globalRegistry[GLOBAL_PRICING_REGISTRY_KEY]) {\n globalRegistry[GLOBAL_PRICING_REGISTRY_KEY] = { resolvers: [] }\n }\n return globalRegistry[GLOBAL_PRICING_REGISTRY_KEY]\n}\n\nfunction sortResolvers(state: PricingRegistryState): void {\n // `Array.prototype.sort` is stable (ES2019+): resolvers registered at the\n // same priority keep their registration order. This is the documented,\n // tested same-priority tie-break \u2014 see `catalog/AGENTS.md` \u00A7 Price\n // selection order and the registry test in `lib/__tests__/pricing.test.ts`.\n state.resolvers.sort((a, b) => b.priority - a.priority)\n}\n\nexport function registerCatalogPricingResolver(\n resolver: CatalogPricingResolver,\n options?: { priority?: number; id?: string }\n): void {\n const state = getPricingRegistryState()\n const id = options?.id\n // Dedupe by id so a documented extension point stays HMR-safe for every\n // future registrant, not just one caller: re-registering the same id is a\n // no-op instead of appending a duplicate entry on every module reload.\n if (id && state.resolvers.some((entry) => entry.id === id)) return\n state.resolvers.push({ id, resolver, priority: options?.priority ?? 0 })\n sortResolvers(state)\n}\n\nexport function resetCatalogPricingResolvers(): void {\n getPricingRegistryState().resolvers.splice(0)\n}\n\nexport async function resolveCatalogPrice(\n rows: PriceRow[],\n ctx: PricingContext,\n options?: { eventBus?: EventBus | null }\n): Promise<PriceRow | null> {\n let workingRows = rows\n let workingContext = ctx\n const eventBus = options?.eventBus ?? null\n let resolved: PriceRow | null | undefined\n\n if (eventBus) {\n await eventBus.emitEvent('catalog.pricing.resolve.before', {\n rows: workingRows,\n context: workingContext,\n setRows(next: PriceRow[]) {\n if (Array.isArray(next)) workingRows = next\n },\n setContext(next: PricingContext) {\n if (next) workingContext = next\n },\n setResult(next: PriceRow | null) {\n resolved = next\n },\n })\n if (resolved !== undefined) return resolved\n }\n\n for (const { resolver } of getPricingRegistryState().resolvers) {\n const result = await resolver(workingRows, workingContext)\n if (result !== undefined) {\n resolved = result ?? null\n break\n }\n }\n\n if (resolved === undefined) {\n resolved = selectBestPrice(workingRows, workingContext)\n }\n\n if (eventBus) {\n await eventBus.emitEvent('catalog.pricing.resolve.after', {\n rows: workingRows,\n context: workingContext,\n result: resolved ?? null,\n setResult(next: PriceRow | null) {\n resolved = next\n },\n })\n }\n\n return resolved ?? null\n}\n\nexport async function resolveCatalogPriceBatch(\n entries: Array<{ rows: PriceRow[]; context: PricingContext }>,\n options?: { eventBus?: EventBus | null }\n): Promise<Array<PriceRow | null>> {\n return Promise.all(\n entries.map(({ rows, context }) => resolveCatalogPrice(rows, context, options))\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,7 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
|
|
|
6
6
|
import { CommunicationChannel } from "../../../../../data/entities.js";
|
|
7
7
|
import { ChannelAccessDeniedError, assertCanManageChannel } from "../../../../../lib/access-control.js";
|
|
8
8
|
import {
|
|
9
|
+
getQueueImportHistorySchema,
|
|
9
10
|
queueImportHistory,
|
|
10
11
|
queueImportHistorySchema
|
|
11
12
|
} from "../../../../../commands/queue-import-history.js";
|
|
@@ -20,6 +21,9 @@ const metadata = {
|
|
|
20
21
|
}
|
|
21
22
|
};
|
|
22
23
|
const bodySchema = queueImportHistorySchema.omit({ channelId: true });
|
|
24
|
+
function buildBodySchema() {
|
|
25
|
+
return getQueueImportHistorySchema().omit({ channelId: true });
|
|
26
|
+
}
|
|
23
27
|
async function POST(req, context) {
|
|
24
28
|
const { id } = await context.params;
|
|
25
29
|
if (!z.string().uuid().safeParse(id).success) {
|
|
@@ -36,7 +40,7 @@ async function POST(req, context) {
|
|
|
36
40
|
} catch {
|
|
37
41
|
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
38
42
|
}
|
|
39
|
-
const parsed =
|
|
43
|
+
const parsed = buildBodySchema().safeParse(rawBody);
|
|
40
44
|
if (!parsed.success) {
|
|
41
45
|
const first = parsed.error.issues[0];
|
|
42
46
|
return NextResponse.json(
|
package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/communication_channels/api/post/channels/%5Bid%5D/import-history/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { CommunicationChannel } from '../../../../../data/entities'\nimport { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../../lib/access-control'\nimport {\n queueImportHistory,\n queueImportHistorySchema,\n} from '../../../../../commands/queue-import-history'\nimport { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('communication_channels').child({ component: 'import-history' })\n\ntype RbacServiceLike = {\n loadAcl: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>\n}\n\n/**\n * Spec B \u00A7 Phase B6 \u2014 operator-triggered backlog import.\n *\n * Enqueues a `channel-import-history` job that calls\n * `adapter.importHistory(...)` paginated up to `maxMessages`, routes each\n * message through `ingest-inbound-message`, and reports progress on a\n * `ProgressJob` consumed by the existing ProgressTopBar.\n *\n * Owner self-service: a user may import history for their OWN mailbox (gated by\n * `connect_user_channel`). Importing into a shared/tenant-wide channel still\n * requires `communication_channels.channel.import_history` \u2014 enforced per\n * channel type by `assertCanManageChannel` in the handler.\n */\nexport const metadata = {\n path: '/communication_channels/channels/[id]/import-history',\n POST: {\n requireAuth: true,\n requireFeatures: ['communication_channels.connect_user_channel'],\n },\n}\n\ntype RouteContext = {\n params: Promise<{ id: string }> | { id: string }\n}\n\nconst bodySchema = queueImportHistorySchema.omit({ channelId: true })\n\nexport async function POST(req: Request, context: RouteContext): Promise<Response> {\n const { id } = await context.params\n if (!z.string().uuid().safeParse(id).success) {\n return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n // Parse body defensively \u2014 the route is called from a CrudForm dialog, but\n // also from automated tests / scripts.\n let rawBody: unknown\n try {\n const text = await req.text()\n rawBody = text ? JSON.parse(text) : {}\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const parsed =
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,6BAA6B;AAEtC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B,8BAA8B;AACjE;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAkC;AAC3C,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,wBAAwB,EAAE,MAAM,EAAE,WAAW,iBAAiB,CAAC;AAsBpF,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,iBAAiB,CAAC,6CAA6C;AAAA,EACjE;AACF;AAMA,MAAM,aAAa,yBAAyB,KAAK,EAAE,WAAW,KAAK,CAAC;AAEpE,eAAsB,KAAK,KAAc,SAA0C;AACjF,QAAM,EAAE,GAAG,IAAI,MAAM,QAAQ;AAC7B,MAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS;AAC5C,WAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC3E;AAEA,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,OAAO,CAAC,MAAM,UAAU;AACjC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AAIA,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,cAAU,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EACvC,QAAQ;AACN,WAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1E;AACA,QAAM,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { CommunicationChannel } from '../../../../../data/entities'\nimport { ChannelAccessDeniedError, assertCanManageChannel } from '../../../../../lib/access-control'\nimport {\n getQueueImportHistorySchema,\n queueImportHistory,\n queueImportHistorySchema,\n} from '../../../../../commands/queue-import-history'\nimport { validateRouteMutationGuard } from '../../../../../lib/route-mutation-guard'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('communication_channels').child({ component: 'import-history' })\n\ntype RbacServiceLike = {\n loadAcl: (\n userId: string,\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<{ isSuperAdmin: boolean; features: string[]; organizations: string[] | null }>\n}\n\n/**\n * Spec B \u00A7 Phase B6 \u2014 operator-triggered backlog import.\n *\n * Enqueues a `channel-import-history` job that calls\n * `adapter.importHistory(...)` paginated up to `maxMessages`, routes each\n * message through `ingest-inbound-message`, and reports progress on a\n * `ProgressJob` consumed by the existing ProgressTopBar.\n *\n * Owner self-service: a user may import history for their OWN mailbox (gated by\n * `connect_user_channel`). Importing into a shared/tenant-wide channel still\n * requires `communication_channels.channel.import_history` \u2014 enforced per\n * channel type by `assertCanManageChannel` in the handler.\n */\nexport const metadata = {\n path: '/communication_channels/channels/[id]/import-history',\n POST: {\n requireAuth: true,\n requireFeatures: ['communication_channels.connect_user_channel'],\n },\n}\n\ntype RouteContext = {\n params: Promise<{ id: string }> | { id: string }\n}\n\nconst bodySchema = queueImportHistorySchema.omit({ channelId: true })\n\nfunction buildBodySchema() {\n return getQueueImportHistorySchema().omit({ channelId: true })\n}\n\nexport async function POST(req: Request, context: RouteContext): Promise<Response> {\n const { id } = await context.params\n if (!z.string().uuid().safeParse(id).success) {\n return NextResponse.json({ error: 'Invalid channel id' }, { status: 400 })\n }\n\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub || !auth?.tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n // Parse body defensively \u2014 the route is called from a CrudForm dialog, but\n // also from automated tests / scripts.\n let rawBody: unknown\n try {\n const text = await req.text()\n rawBody = text ? JSON.parse(text) : {}\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const parsed = buildBodySchema().safeParse(rawBody)\n if (!parsed.success) {\n const first = parsed.error.issues[0]\n return NextResponse.json(\n {\n error: first?.message ?? 'Invalid request',\n fieldErrors: first ? { [first.path.join('.')]: first.message } : undefined,\n },\n { status: 400 },\n )\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager).fork()\n const organizationId = (auth as { orgId?: string | null }).orgId ?? null\n if (!organizationId) {\n return NextResponse.json({ error: 'No organization scope' }, { status: 400 })\n }\n const dscope = { tenantId: auth.tenantId as string, organizationId }\n\n // Per-user access guard \u2014 load the channel just to check ownership; the\n // command repeats the lookup but at the cost of one extra read we get a\n // clean 404 for non-owners (instead of a generic 500 from inside the cmd).\n const channel = await findOneWithDecryption(\n em,\n CommunicationChannel,\n {\n id,\n tenantId: auth.tenantId as string,\n organizationId,\n deletedAt: null,\n },\n undefined,\n dscope,\n )\n if (!channel) {\n return NextResponse.json({ error: 'Channel not found' }, { status: 404 })\n }\n\n let userFeatures: string[] = []\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike\n const acl = await rbac.loadAcl(auth.sub as string, {\n tenantId: auth.tenantId as string,\n organizationId,\n })\n userFeatures = acl?.isSuperAdmin ? ['*'] : Array.isArray(acl?.features) ? acl.features : []\n } catch {\n userFeatures = []\n }\n try {\n assertCanManageChannel(\n { userId: (channel as { userId?: string | null }).userId },\n auth.sub as string,\n userFeatures,\n 'communication_channels.channel.import_history',\n )\n } catch (err) {\n if (err instanceof ChannelAccessDeniedError) {\n return NextResponse.json({ error: 'Channel not found' }, { status: 404 })\n }\n throw err\n }\n\n const guard = await validateRouteMutationGuard({\n container,\n req,\n auth,\n input: {\n resourceKind: 'communication_channels.channel',\n resourceId: id,\n operation: 'custom',\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n },\n })\n if ('response' in guard) return guard.response\n\n try {\n const result = await queueImportHistory({\n container,\n scope: {\n tenantId: auth.tenantId as string,\n organizationId,\n userId: auth.sub as string,\n },\n input: { channelId: id, ...parsed.data },\n })\n await guard.afterSuccess()\n // 202 Accepted: durable work runs in the channel-import-history worker; the\n // response carries progressJobId for the ProgressTopBar (progress module contract).\n return NextResponse.json({ ok: true, ...result }, { status: 202 })\n } catch (err) {\n const candidate = err as CrudFormError\n if (candidate && typeof candidate.status === 'number') {\n return NextResponse.json(\n {\n error: candidate.message,\n fieldErrors: candidate.fieldErrors,\n },\n { status: candidate.status },\n )\n }\n logger.error('failed to enqueue for channel', { channelId: id, err })\n return NextResponse.json(\n { error: err instanceof Error ? err.message : 'Failed to queue import-history' },\n { status: 500 },\n )\n }\n}\n\nexport const openApi = {\n tags: ['CommunicationChannels'],\n methods: {\n POST: {\n summary: 'Queue a backlog import for a channel (Spec B \u00A7 Phase B6)',\n tags: ['CommunicationChannels'],\n requestBody: {\n required: true,\n contentType: 'application/json',\n schema: bodySchema,\n },\n responses: [\n { status: 202, description: 'Import job queued; returns { progressJobId }' },\n { status: 400, description: 'Invalid channel id or unsupported provider' },\n { status: 401, description: 'Unauthorized' },\n { status: 404, description: 'Channel not found / not accessible' },\n { status: 409, description: 'Channel is not connected (requires reauth / error)' },\n {\n status: 429,\n description: 'Another import is already running for this channel',\n },\n ],\n },\n },\n}\n\nexport default POST\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronDown } from "lucide-react";
|
|
5
|
+
import { extensionPoints } from "@open-mercato/core/modules/communication_channels/extension-points";
|
|
6
|
+
import { Button } from "@open-mercato/ui/primitives/button";
|
|
7
|
+
import { InjectionSpot, useInjectionWidgets } from "@open-mercato/ui/backend/injection/InjectionSpot";
|
|
8
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
9
|
+
import { cn } from "@open-mercato/shared/lib/utils";
|
|
10
|
+
const CONNECT_MENU_TRIGGER_ID = "communication-channels-connect-trigger";
|
|
11
|
+
const CONNECT_MENU_PANEL_ID = "communication-channels-connect-menu";
|
|
12
|
+
function ConnectChannelMenu({ onConnected }) {
|
|
13
|
+
const t = useT();
|
|
14
|
+
const spotId = extensionPoints.hosts.profileConnect.spotId;
|
|
15
|
+
const [open, setOpen] = React.useState(false);
|
|
16
|
+
const containerRef = React.useRef(null);
|
|
17
|
+
const triggerRef = React.useRef(null);
|
|
18
|
+
const context = React.useMemo(() => ({ reload: onConnected }), [onConnected]);
|
|
19
|
+
const { widgets } = useInjectionWidgets(spotId, { context, triggerOnLoad: true });
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (!open) return;
|
|
22
|
+
function isInsideDialog(target) {
|
|
23
|
+
const element = target;
|
|
24
|
+
return typeof element?.closest === "function" && element.closest('[role="dialog"]') !== null;
|
|
25
|
+
}
|
|
26
|
+
function onDocumentMouseDown(event) {
|
|
27
|
+
if (isInsideDialog(event.target)) return;
|
|
28
|
+
const target = event.target;
|
|
29
|
+
if (containerRef.current && !containerRef.current.contains(target)) setOpen(false);
|
|
30
|
+
}
|
|
31
|
+
function onKeyDown(event) {
|
|
32
|
+
if (event.key !== "Escape") return;
|
|
33
|
+
setOpen(false);
|
|
34
|
+
triggerRef.current?.focus();
|
|
35
|
+
}
|
|
36
|
+
document.addEventListener("mousedown", onDocumentMouseDown);
|
|
37
|
+
document.addEventListener("keydown", onKeyDown);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener("mousedown", onDocumentMouseDown);
|
|
40
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
41
|
+
};
|
|
42
|
+
}, [open]);
|
|
43
|
+
if (widgets.length === 0) return null;
|
|
44
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-block text-left", children: [
|
|
45
|
+
/* @__PURE__ */ jsxs(
|
|
46
|
+
Button,
|
|
47
|
+
{
|
|
48
|
+
ref: triggerRef,
|
|
49
|
+
id: CONNECT_MENU_TRIGGER_ID,
|
|
50
|
+
type: "button",
|
|
51
|
+
variant: "outline",
|
|
52
|
+
"aria-expanded": open,
|
|
53
|
+
"aria-controls": CONNECT_MENU_PANEL_ID,
|
|
54
|
+
onClick: () => setOpen((current) => !current),
|
|
55
|
+
children: [
|
|
56
|
+
t("communication_channels.profile.connect.menu", "Connect channel"),
|
|
57
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("size-4 transition-transform", open && "rotate-180") })
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
id: CONNECT_MENU_PANEL_ID,
|
|
65
|
+
role: "group",
|
|
66
|
+
"aria-labelledby": CONNECT_MENU_TRIGGER_ID,
|
|
67
|
+
"data-testid": "connect-channel-menu-panel",
|
|
68
|
+
className: cn(
|
|
69
|
+
// Anchoring flips with the header: the header wraps on a phone, which
|
|
70
|
+
// moves the trigger to the left edge, and right-anchoring a panel
|
|
71
|
+
// wider than the trigger then pushes it off-screen. Left-anchor below
|
|
72
|
+
// `sm`, right-anchor from `sm` up where the trigger sits right. The
|
|
73
|
+
// `max-w-` clamp (mirroring RowActions) keeps a long provider label
|
|
74
|
+
// from widening the panel past the viewport either way.
|
|
75
|
+
"absolute left-0 top-full z-dropdown mt-2 w-max min-w-56 max-w-[calc(100vw-1rem)] flex-col items-stretch gap-2 rounded-md border bg-background p-2 shadow-md sm:left-auto sm:right-0",
|
|
76
|
+
open ? "flex" : "hidden"
|
|
77
|
+
),
|
|
78
|
+
children: /* @__PURE__ */ jsx(InjectionSpot, { spotId, context, data: {}, widgetsOverride: widgets })
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] });
|
|
82
|
+
}
|
|
83
|
+
var ConnectChannelMenu_default = ConnectChannelMenu;
|
|
84
|
+
export {
|
|
85
|
+
ConnectChannelMenu,
|
|
86
|
+
ConnectChannelMenu_default as default
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=ConnectChannelMenu.js.map
|