@open-mercato/core 0.6.4-develop.4239.1.4a264a5828 → 0.6.4-develop.4254.1.7a123d970c

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.
Files changed (80) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/authFixtures.js +70 -1
  3. package/dist/helpers/integration/authFixtures.js.map +2 -2
  4. package/dist/helpers/integration/dbFixtures.js +98 -0
  5. package/dist/helpers/integration/dbFixtures.js.map +7 -0
  6. package/dist/modules/business_rules/api/execute/route.js +2 -1
  7. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  8. package/dist/modules/business_rules/api/rules/route.js +10 -0
  9. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  10. package/dist/modules/business_rules/cli.js +6 -0
  11. package/dist/modules/business_rules/cli.js.map +2 -2
  12. package/dist/modules/business_rules/lib/rule-engine.js +116 -9
  13. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  14. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -2
  15. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
  16. package/dist/modules/catalog/api/products/route.js +21 -4
  17. package/dist/modules/catalog/api/products/route.js.map +2 -2
  18. package/dist/modules/catalog/lib/pricing.js +6 -0
  19. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  20. package/dist/modules/catalog/services/catalogPricingService.js +5 -1
  21. package/dist/modules/catalog/services/catalogPricingService.js.map +2 -2
  22. package/dist/modules/customers/api/activities/route.js +15 -2
  23. package/dist/modules/customers/api/activities/route.js.map +2 -2
  24. package/dist/modules/customers/api/comments/route.js +15 -3
  25. package/dist/modules/customers/api/comments/route.js.map +2 -2
  26. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -4
  27. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  28. package/dist/modules/customers/api/companies/[id]/route.js +2 -4
  29. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  30. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -4
  31. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  32. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -4
  33. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  34. package/dist/modules/customers/api/deals/[id]/route.js +2 -9
  35. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  36. package/dist/modules/customers/api/deals/[id]/stats/route.js +2 -9
  37. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  38. package/dist/modules/customers/api/entity-roles-factory.js +2 -8
  39. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  40. package/dist/modules/customers/api/people/[id]/companies/context.js +2 -4
  41. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  42. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -4
  43. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  44. package/dist/modules/customers/api/people/[id]/route.js +2 -4
  45. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  46. package/dist/modules/directory/utils/organizationScopeGuard.js +22 -0
  47. package/dist/modules/directory/utils/organizationScopeGuard.js.map +7 -0
  48. package/dist/modules/workflows/cli.js +8 -0
  49. package/dist/modules/workflows/cli.js.map +2 -2
  50. package/dist/modules/workflows/lib/seeds.js +8 -4
  51. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  52. package/dist/modules/workflows/setup.js +3 -1
  53. package/dist/modules/workflows/setup.js.map +2 -2
  54. package/package.json +7 -7
  55. package/src/helpers/integration/authFixtures.ts +98 -0
  56. package/src/helpers/integration/dbFixtures.ts +144 -0
  57. package/src/modules/business_rules/api/execute/route.ts +2 -1
  58. package/src/modules/business_rules/api/rules/route.ts +10 -0
  59. package/src/modules/business_rules/cli.ts +6 -0
  60. package/src/modules/business_rules/lib/rule-engine.ts +163 -9
  61. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +3 -2
  62. package/src/modules/catalog/api/products/route.ts +23 -4
  63. package/src/modules/catalog/lib/pricing.ts +9 -0
  64. package/src/modules/catalog/services/catalogPricingService.ts +6 -0
  65. package/src/modules/customers/api/activities/route.ts +16 -5
  66. package/src/modules/customers/api/comments/route.ts +15 -5
  67. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -4
  68. package/src/modules/customers/api/companies/[id]/route.ts +2 -5
  69. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -4
  70. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -4
  71. package/src/modules/customers/api/deals/[id]/route.ts +2 -9
  72. package/src/modules/customers/api/deals/[id]/stats/route.ts +2 -9
  73. package/src/modules/customers/api/entity-roles-factory.ts +2 -12
  74. package/src/modules/customers/api/people/[id]/companies/context.ts +2 -5
  75. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -5
  76. package/src/modules/customers/api/people/[id]/route.ts +2 -5
  77. package/src/modules/directory/utils/organizationScopeGuard.ts +39 -0
  78. package/src/modules/workflows/cli.ts +8 -0
  79. package/src/modules/workflows/lib/seeds.ts +13 -3
  80. package/src/modules/workflows/setup.ts +3 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/catalog/api/products/route.ts"],
4
- "sourcesContent": ["import { z } from \"zod\";\nimport type { EntityManager } from \"@mikro-orm/postgresql\";\nimport { makeCrudRoute } from \"@open-mercato/shared/lib/crud/factory\";\nimport { CrudHttpError } from \"@open-mercato/shared/lib/crud/errors\";\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { resolveTranslations } from \"@open-mercato/shared/lib/i18n/server\";\nimport {\n CatalogOffer,\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductUnitConversion,\n CatalogProductVariant,\n CatalogProductTagAssignment,\n} from \"../../data/entities\";\nimport { CATALOG_PRODUCT_TYPES } from \"../../data/types\";\nimport type { CatalogProductType } from \"../../data/types\";\nimport {\n productCreateSchema,\n productUpdateSchema,\n} from \"../../data/validators\";\nimport { parseScopedCommandInput, resolveCrudRecordId } from \"../utils\";\nimport { splitCustomFieldPayload } from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { E } from \"#generated/entities.ids.generated\";\nimport * as F from \"#generated/entities/catalog_product\";\nimport { parseBooleanFlag, sanitizeSearchTerm } from \"../helpers\";\nimport { escapeLikePattern } from \"@open-mercato/shared/lib/db/escapeLikePattern\";\nimport type { CrudCtx } from \"@open-mercato/shared/lib/crud/factory\";\nimport { buildScopedWhere } from \"@open-mercato/shared/lib/api/crud\";\nimport {\n resolvePriceChannelId,\n resolvePriceOfferId,\n resolvePriceVariantId,\n resolvePriceKindCode,\n type PricingContext,\n type PriceRow,\n} from \"../../lib/pricing\";\nimport type { CatalogPricingService } from \"../../services/catalogPricingService\";\nimport { fieldsetCodeRegex } from \"@open-mercato/core/modules/entities/data/validators\";\nimport { SalesChannel } from \"@open-mercato/core/modules/sales/data/entities\";\nimport {\n createCatalogCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from \"../openapi\";\nimport { findWithDecryption } from \"@open-mercato/shared/lib/encryption/find\";\nimport { canonicalizeUnitCode, toUnitLookupKey } from \"../../lib/unitCodes\";\nconst rawBodySchema = z.object({}).passthrough();\n\nconst UUID_REGEX =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n status: z.string().optional(),\n isActive: z.string().optional(),\n configurable: z.string().optional(),\n productType: z.enum(CATALOG_PRODUCT_TYPES).optional(),\n channelIds: z.string().optional(),\n channelId: z.string().uuid().optional(),\n categoryIds: z.string().optional(),\n tagIds: z.string().optional(),\n offerId: z.string().uuid().optional(),\n userId: z.string().uuid().optional(),\n userGroupId: z.string().uuid().optional(),\n customerId: z.string().uuid().optional(),\n customerGroupId: z.string().uuid().optional(),\n quantity: z.coerce.number().min(1).max(100000).optional(),\n quantityUnit: z.string().trim().max(50).optional(),\n priceDate: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum([\"asc\", \"desc\"]).optional(),\n withDeleted: z.coerce.boolean().optional(),\n customFieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .passthrough();\n\ntype ProductsQuery = z.infer<typeof listSchema>;\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [\"catalog.products.view\"] },\n POST: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n PUT: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n DELETE: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n};\n\nexport const metadata = routeMetadata;\n\nexport function parseIdList(raw?: string): string[] {\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => UUID_REGEX.test(value));\n}\n\nexport async function buildProductFilters(\n query: ProductsQuery,\n ctx: CrudCtx,\n): Promise<Record<string, unknown>> {\n const filters: Record<string, unknown> = {};\n const em = (ctx.container.resolve(\"em\") as EntityManager).fork();\n const restrictedProductIds: { value: Set<string> | null } = { value: null };\n\n const intersectProductIds = (ids: string[]) => {\n const normalized = ids.filter(\n (id): id is string => typeof id === \"string\" && id.trim().length > 0,\n );\n const current = new Set(normalized);\n if (!current.size) {\n restrictedProductIds.value = new Set();\n return;\n }\n if (!restrictedProductIds.value) {\n restrictedProductIds.value = current;\n return;\n }\n restrictedProductIds.value = new Set(\n Array.from(restrictedProductIds.value).filter((id) => current.has(id)),\n );\n };\n\n const applyRestrictedProducts = () => {\n if (!restrictedProductIds.value) return;\n if (restrictedProductIds.value.size === 0) {\n filters.id = { $eq: \"00000000-0000-0000-0000-000000000000\" };\n return;\n }\n const ids = Array.from(restrictedProductIds.value);\n const existing = filters.id as Record<string, unknown> | undefined;\n if (existing && typeof existing === \"object\") {\n if (\n \"$eq\" in existing &&\n typeof (existing as { $eq?: unknown }).$eq === \"string\"\n ) {\n const target = (existing as { $eq: string }).$eq;\n if (!restrictedProductIds.value.has(target)) {\n filters.id = { $eq: \"00000000-0000-0000-0000-000000000000\" };\n }\n return;\n }\n if (\n \"$in\" in existing &&\n Array.isArray((existing as { $in?: unknown }).$in)\n ) {\n const subset = (existing as { $in: string[] }).$in.filter((id) =>\n restrictedProductIds.value!.has(id),\n );\n filters.id = subset.length\n ? { $in: subset }\n : { $eq: \"00000000-0000-0000-0000-000000000000\" };\n return;\n }\n }\n filters.id = ids.length === 1 ? { $eq: ids[0] } : { $in: ids };\n };\n if (query.id) {\n filters.id = { $eq: query.id };\n }\n if (query.status && query.status.trim()) {\n filters.status_entry_id = { $eq: query.status.trim() };\n }\n const active = parseBooleanFlag(query.isActive);\n if (active !== undefined) {\n filters.is_active = active;\n }\n const configurable = parseBooleanFlag(query.configurable);\n if (configurable !== undefined) {\n filters.is_configurable = configurable;\n }\n if (query.productType) {\n filters.product_type = { $eq: query.productType };\n }\n const scope = {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n };\n const term = sanitizeSearchTerm(query.search);\n if (term) {\n const like = `%${escapeLikePattern(term)}%`;\n const searchMatches = await findWithDecryption(\n em,\n CatalogProduct,\n {\n ...scope,\n ...(query.withDeleted ? {} : { deletedAt: null }),\n $or: [\n { title: { $ilike: like } },\n { subtitle: { $ilike: like } },\n { description: { $ilike: like } },\n { sku: { $ilike: like } },\n { handle: { $ilike: like } },\n ],\n },\n { fields: [\"id\"] },\n scope,\n );\n const productIds = searchMatches\n .map((product) => product.id)\n .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n intersectProductIds(productIds);\n }\n\n const channelFilterIds = parseIdList(query.channelIds);\n if (channelFilterIds.length) {\n const offerRows = await findWithDecryption(\n em,\n CatalogOffer,\n {\n channelId: { $in: channelFilterIds },\n deletedAt: null,\n ...scope,\n },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = offerRows\n .map((offer) =>\n typeof offer.product === \"string\"\n ? offer.product\n : (offer.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n\n const categoryFilterIds = parseIdList(query.categoryIds);\n if (categoryFilterIds.length) {\n const assignments = await findWithDecryption(\n em,\n CatalogProductCategoryAssignment,\n { category: { $in: categoryFilterIds }, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = assignments\n .map((assignment) =>\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n\n const tagFilterIds = parseIdList(query.tagIds);\n if (tagFilterIds.length) {\n const assignments = await findWithDecryption(\n em,\n CatalogProductTagAssignment,\n { tag: { $in: tagFilterIds }, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = assignments\n .map((assignment) =>\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n const customFieldset =\n typeof query.customFieldset === \"string\" &&\n query.customFieldset.trim().length\n ? query.customFieldset.trim()\n : null;\n const tenantId = ctx.auth?.tenantId ?? null;\n try {\n const scopedEm = ctx.container.resolve(\"em\") as EntityManager;\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.catalog.catalog_product],\n query,\n em: scopedEm,\n tenantId,\n fieldset: customFieldset ?? undefined,\n });\n Object.assign(filters, cfFilters);\n } catch (err) {\n // Custom field filter parsing may fail for non-existent or misconfigured fields.\n // Fall back to base filters to avoid blocking the product listing.\n if (process.env.NODE_ENV === 'development') console.warn('[catalog:products] custom field filter error', err);\n }\n applyRestrictedProducts();\n return filters;\n}\n\nexport function buildPricingContext(\n query: ProductsQuery,\n channelFallback: string | null,\n): PricingContext {\n const quantity = Number.isFinite(Number(query.quantity))\n ? Number(query.quantity)\n : 1;\n const parsedDate = query.priceDate ? new Date(query.priceDate) : new Date();\n const channelId = query.channelId ?? channelFallback ?? null;\n return {\n channelId,\n offerId: query.offerId ?? null,\n userId: query.userId ?? null,\n userGroupId: query.userGroupId ?? null,\n customerId: query.customerId ?? null,\n customerGroupId: query.customerGroupId ?? null,\n quantity: Number.isFinite(quantity) && quantity > 0 ? quantity : 1,\n date: Number.isNaN(parsedDate.getTime()) ? new Date() : parsedDate,\n };\n}\n\ntype ProductListItem = Record<string, unknown> & {\n id?: string;\n title?: string | null;\n subtitle?: string | null;\n description?: string | null;\n sku?: string | null;\n handle?: string | null;\n product_type?: CatalogProductType | null;\n primary_currency_code?: string | null;\n default_unit?: string | null;\n default_sales_unit?: string | null;\n default_sales_unit_quantity?: number | null;\n uom_rounding_scale?: number | null;\n uom_rounding_mode?: \"half_up\" | \"down\" | \"up\" | null;\n unit_price_enabled?: boolean | null;\n unit_price_reference_unit?: \"kg\" | \"l\" | \"m2\" | \"m3\" | \"pc\" | null;\n unit_price_base_quantity?: number | null;\n default_media_id?: string | null;\n default_media_url?: string | null;\n weight_value?: string | null;\n weightValue?: string | null;\n weight_unit?: string | null;\n weightUnit?: string | null;\n dimensions?: Record<string, unknown> | null;\n custom_fieldset_code?: string | null;\n option_schema_id?: string | null;\n offers?: Array<Record<string, unknown>>;\n channelIds?: string[];\n categories?: Array<Record<string, unknown>>;\n categoryIds?: string[];\n tags?: string[];\n};\n\nasync function decorateProductsAfterList(\n payload: { items?: ProductListItem[] },\n ctx: CrudCtx & { query: ProductsQuery },\n): Promise<void> {\n const items = Array.isArray(payload?.items) ? payload.items : [];\n if (!items.length) return;\n const productIds = items\n .map((item) => (typeof item.id === \"string\" ? item.id : null))\n .filter((id): id is string => !!id);\n if (!productIds.length) return;\n try {\n const em = (ctx.container.resolve(\"em\") as EntityManager).fork();\n const scope = {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n };\n const offers = await findWithDecryption(\n em,\n CatalogOffer,\n { product: { $in: productIds }, deletedAt: null, ...scope },\n { orderBy: { createdAt: \"asc\" } },\n scope,\n );\n const channelIds = Array.from(\n new Set(\n offers\n .map((offer) => offer.channelId)\n .filter(\n (id): id is string => typeof id === \"string\" && id.length > 0,\n ),\n ),\n );\n const channelLookup = new Map<\n string,\n { name?: string | null; code?: string | null }\n >();\n if (channelIds.length) {\n const scopedChannelsWhere = buildScopedWhere(\n { id: { $in: channelIds } },\n {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n organizationIds: Array.isArray(ctx.organizationIds)\n ? ctx.organizationIds\n : undefined,\n tenantId: ctx.auth?.tenantId ?? null,\n },\n );\n const channels = await findWithDecryption(em, SalesChannel, scopedChannelsWhere, {\n fields: [\"id\", \"name\", \"code\"],\n });\n for (const channel of channels) {\n channelLookup.set(channel.id, {\n name: channel.name,\n code: channel.code ?? null,\n });\n }\n }\n const offersByProduct = new Map<string, Array<Record<string, unknown>>>();\n for (const offer of offers) {\n const productId =\n typeof offer.product === \"string\"\n ? offer.product\n : (offer.product?.id ?? null);\n if (!productId) continue;\n const channelInfo = channelLookup.get(offer.channelId);\n const entry = offersByProduct.get(productId) ?? [];\n entry.push({\n id: offer.id,\n channelId: offer.channelId,\n channelName: channelInfo?.name ?? null,\n channelCode: channelInfo?.code ?? null,\n title: offer.title,\n description: offer.description ?? null,\n isActive: offer.isActive,\n defaultMediaId: offer.defaultMediaId ?? null,\n defaultMediaUrl: offer.defaultMediaUrl ?? null,\n metadata: offer.metadata ?? null,\n });\n offersByProduct.set(productId, entry);\n }\n\n const categoryAssignments = await findWithDecryption(\n em,\n CatalogProductCategoryAssignment,\n { product: { $in: productIds }, ...scope },\n { populate: [\"category\"], orderBy: { position: \"asc\" } },\n scope,\n );\n const parentIds = new Set<string>();\n for (const assignment of categoryAssignments) {\n const category =\n typeof assignment.category === \"string\"\n ? null\n : (assignment.category ?? null);\n if (!category) continue;\n const parentId = category.parentId ?? null;\n if (parentId) parentIds.add(parentId);\n }\n const parentCategories = parentIds.size\n ? await findWithDecryption(\n em,\n CatalogProductCategory,\n { id: { $in: Array.from(parentIds) }, ...scope },\n { fields: [\"id\", \"name\"] },\n scope,\n )\n : [];\n const parentNameById = new Map<string, string | null>();\n for (const parent of parentCategories) {\n parentNameById.set(parent.id, parent.name ?? null);\n }\n const categoriesByProduct = new Map<\n string,\n Array<{\n id: string;\n name: string | null;\n treePath: string | null;\n parentId: string | null;\n parentName: string | null;\n }>\n >();\n for (const assignment of categoryAssignments) {\n const productId =\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null);\n if (!productId) continue;\n const category =\n typeof assignment.category === \"string\"\n ? null\n : (assignment.category ?? null);\n if (!category) continue;\n const parentId = category.parentId ?? null;\n const parentName = parentId\n ? (parentNameById.get(parentId) ?? null)\n : null;\n const bucket = categoriesByProduct.get(productId) ?? [];\n bucket.push({\n id: category.id,\n name: category.name ?? null,\n treePath: category.treePath ?? null,\n parentId,\n parentName,\n });\n categoriesByProduct.set(productId, bucket);\n }\n\n const tagAssignments = await findWithDecryption(\n em,\n CatalogProductTagAssignment,\n { product: { $in: productIds } },\n { populate: [\"tag\"] },\n {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n },\n );\n const tagsByProduct = new Map<string, string[]>();\n for (const assignment of tagAssignments) {\n const productId =\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null);\n if (!productId) continue;\n const tag =\n typeof assignment.tag === \"string\" ? null : (assignment.tag ?? null);\n if (!tag) continue;\n const label =\n typeof tag.label === \"string\" && tag.label.trim().length\n ? tag.label\n : null;\n if (!label) continue;\n const bucket = tagsByProduct.get(productId) ?? [];\n bucket.push(label);\n tagsByProduct.set(productId, bucket);\n }\n\n const variants = await findWithDecryption(\n em,\n CatalogProductVariant,\n { product: { $in: productIds }, deletedAt: null, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const variantToProduct = new Map<string, string>();\n for (const variant of variants) {\n const productId =\n typeof variant.product === \"string\"\n ? variant.product\n : (variant.product?.id ?? null);\n if (!productId) continue;\n variantToProduct.set(variant.id, productId);\n }\n const variantIds = Array.from(variantToProduct.keys());\n const priceWhere =\n variantIds.length > 0\n ? {\n $or: [\n { product: { $in: productIds } },\n { variant: { $in: variantIds } },\n ],\n }\n : { product: { $in: productIds } };\n const priceRows = await findWithDecryption(\n em,\n CatalogProductPrice,\n { ...priceWhere, ...scope },\n { populate: [\"offer\", \"variant\", \"product\", \"priceKind\"] },\n scope,\n );\n const pricesByProduct = new Map<string, PriceRow[]>();\n for (const price of priceRows) {\n let productId: string | null = null;\n if (price.product) {\n productId =\n typeof price.product === \"string\"\n ? price.product\n : (price.product?.id ?? null);\n } else if (price.variant) {\n const variantId =\n typeof price.variant === \"string\" ? price.variant : price.variant.id;\n productId = variantToProduct.get(variantId) ?? null;\n }\n if (!productId) continue;\n const entry = pricesByProduct.get(productId) ?? [];\n entry.push(price);\n pricesByProduct.set(productId, entry);\n }\n\n const requestQuantityUnitKey = toUnitLookupKey(\n ctx.query.quantityUnit,\n );\n const conversionsByProduct = new Map<string, Map<string, number>>();\n const conversionOrganizationId =\n ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null;\n const conversionTenantId = ctx.auth?.tenantId ?? null;\n if (\n requestQuantityUnitKey &&\n productIds.length &&\n conversionOrganizationId &&\n conversionTenantId\n ) {\n const conversionRows = await findWithDecryption(\n em,\n CatalogProductUnitConversion,\n {\n product: { $in: productIds },\n organizationId: conversionOrganizationId,\n tenantId: conversionTenantId,\n deletedAt: null,\n isActive: true,\n },\n { fields: [\"id\", \"product\", \"unitCode\", \"toBaseFactor\"] },\n { organizationId: conversionOrganizationId, tenantId: conversionTenantId },\n );\n for (const row of conversionRows) {\n const productId =\n typeof row.product === \"string\"\n ? row.product\n : (row.product?.id ?? null);\n const unitKey = toUnitLookupKey(row.unitCode);\n const factor = Number(row.toBaseFactor);\n if (!productId || !unitKey || !Number.isFinite(factor) || factor <= 0)\n continue;\n const bucket =\n conversionsByProduct.get(productId) ?? new Map<string, number>();\n bucket.set(unitKey, factor);\n conversionsByProduct.set(productId, bucket);\n }\n }\n\n const channelFilterIds = parseIdList(ctx.query.channelIds);\n const channelContext =\n ctx.query.channelId ??\n (channelFilterIds.length === 1 ? channelFilterIds[0] : null);\n const pricingContext = buildPricingContext(ctx.query, channelContext);\n const pricingService = ctx.container.resolve<CatalogPricingService>(\n \"catalogPricingService\",\n );\n\n for (const item of items) {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (!id) continue;\n const offerEntries = offersByProduct.get(id) ?? [];\n item.offers = offerEntries;\n const channelIds = Array.from(\n new Set(\n offerEntries\n .map((offer) =>\n typeof offer.channelId === \"string\" ? offer.channelId : null,\n )\n .filter((channelId): channelId is string => !!channelId),\n ),\n );\n item.channelIds = channelIds;\n const categories = categoriesByProduct.get(id) ?? [];\n item.categories = categories;\n item.categoryIds = categories.map((category) => category.id);\n item.tags = tagsByProduct.get(id) ?? [];\n const priceCandidates = pricesByProduct.get(id) ?? [];\n const normalizedQuantityForPricing = (() => {\n if (!requestQuantityUnitKey) return pricingContext.quantity;\n const baseUnit = toUnitLookupKey(item.default_unit);\n if (!baseUnit || requestQuantityUnitKey === baseUnit)\n return pricingContext.quantity;\n const productConversions = conversionsByProduct.get(id);\n const factor = productConversions?.get(requestQuantityUnitKey) ?? null;\n if (!factor || !Number.isFinite(factor) || factor <= 0) {\n if (process.env.NODE_ENV === 'development') console.warn(`[catalog.products] Invalid conversion factor for product=${id} unit=${requestQuantityUnitKey} factor=${factor}`);\n return pricingContext.quantity;\n }\n const normalized = pricingContext.quantity * factor;\n return Number.isFinite(normalized) && normalized > 0\n ? normalized\n : pricingContext.quantity;\n })();\n const channelScopedContext =\n pricingContext.channelId || channelIds.length !== 1\n ? pricingContext\n : { ...pricingContext, channelId: channelIds[0] };\n const best = await pricingService.resolvePrice(priceCandidates, {\n ...channelScopedContext,\n quantity: normalizedQuantityForPricing,\n });\n if (best) {\n item.pricing = {\n kind: resolvePriceKindCode(best),\n price_kind_id:\n typeof best.priceKind === \"string\"\n ? best.priceKind\n : (best.priceKind?.id ?? null),\n price_kind_code: resolvePriceKindCode(best),\n currency_code: best.currencyCode,\n unit_price_net: best.unitPriceNet,\n unit_price_gross: best.unitPriceGross,\n min_quantity: best.minQuantity,\n max_quantity: best.maxQuantity ?? null,\n tax_rate: best.taxRate ?? null,\n tax_amount: best.taxAmount ?? null,\n scope: {\n variant_id: resolvePriceVariantId(best),\n offer_id: resolvePriceOfferId(best),\n channel_id: resolvePriceChannelId(best),\n user_id: best.userId ?? null,\n user_group_id: best.userGroupId ?? null,\n customer_id: best.customerId ?? null,\n customer_group_id: best.customerGroupId ?? null,\n },\n };\n } else {\n item.pricing = null;\n }\n }\n } catch (error) {\n console.error(\"[decorateProductsAfterList] Failed to load unit conversions\", error);\n }\n\n const searchTerm = ctx.query.search ? sanitizeSearchTerm(ctx.query.search) : null;\n if (searchTerm && !ctx.query.sortField && Array.isArray(payload.items)) {\n const needle = searchTerm.toLowerCase();\n payload.items.sort((a, b) => {\n const scoreA = scoreProductSearchRelevance(needle, a.title, a.sku);\n const scoreB = scoreProductSearchRelevance(needle, b.title, b.sku);\n if (scoreA !== scoreB) return scoreA - scoreB;\n return (a.title ?? \"\").localeCompare(b.title ?? \"\");\n });\n }\n}\n\nexport function scoreProductSearchRelevance(\n needle: string,\n title: string | null | undefined,\n sku: string | null | undefined,\n): number {\n const t = (title ?? \"\").toLowerCase();\n const s = (sku ?? \"\").toLowerCase();\n if (t === needle) return 0;\n if (s === needle) return 1;\n if (t.startsWith(needle)) return 2;\n if (s.startsWith(needle)) return 3;\n if (t.includes(needle)) return 4;\n if (s.includes(needle)) return 5;\n return 6;\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CatalogProduct,\n idField: \"id\",\n orgField: \"organizationId\",\n tenantField: \"tenantId\",\n softDeleteField: \"deletedAt\",\n },\n indexer: {\n entityType: E.catalog.catalog_product,\n },\n list: {\n schema: listSchema,\n entityId: E.catalog.catalog_product,\n fields: [\n F.id,\n F.title,\n F.subtitle,\n F.description,\n F.sku,\n F.handle,\n \"tax_rate_id\",\n \"tax_rate\",\n F.product_type,\n F.status_entry_id,\n F.primary_currency_code,\n F.default_unit,\n \"default_sales_unit\",\n \"default_sales_unit_quantity\",\n \"uom_rounding_scale\",\n \"uom_rounding_mode\",\n \"unit_price_enabled\",\n \"unit_price_reference_unit\",\n \"unit_price_base_quantity\",\n F.default_media_id,\n F.default_media_url,\n F.weight_value,\n F.weight_unit,\n F.dimensions,\n F.is_configurable,\n F.is_active,\n F.metadata,\n \"custom_fieldset_code\",\n \"option_schema_id\",\n F.created_at,\n F.updated_at,\n ],\n decorateCustomFields: { entityIds: [E.catalog.catalog_product] },\n sortFieldMap: {\n title: F.title,\n sku: F.sku,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: buildProductFilters,\n transformItem: (item: ProductListItem | null | undefined) => {\n if (!item) return item;\n const normalized = { ...item };\n const cfEntries = extractAllCustomFieldEntries(item);\n for (const key of Object.keys(normalized)) {\n if (key.startsWith(\"cf:\")) {\n delete normalized[key];\n }\n }\n const defaultUnit = canonicalizeUnitCode(normalized.default_unit) ?? null;\n const defaultSalesUnit =\n canonicalizeUnitCode(normalized.default_sales_unit) ?? null;\n const unitPriceReferenceUnit =\n canonicalizeUnitCode(normalized.unit_price_reference_unit) ?? null;\n return {\n ...normalized,\n default_unit: defaultUnit,\n default_sales_unit: defaultSalesUnit,\n unit_price_reference_unit: unitPriceReferenceUnit,\n ...cfEntries,\n unit_price: {\n enabled: Boolean(normalized.unit_price_enabled),\n reference_unit: unitPriceReferenceUnit,\n base_quantity: normalized.unit_price_base_quantity ?? null,\n },\n };\n },\n },\n hooks: {\n afterList: decorateProductsAfterList,\n },\n actions: {\n create: {\n commandId: \"catalog.products.create\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const parsed = parseScopedCommandInput(\n productCreateSchema,\n raw ?? {},\n ctx,\n translate,\n );\n const { base, custom } = splitCustomFieldPayload(parsed);\n return Object.keys(custom).length\n ? { ...base, customFields: custom }\n : base;\n },\n response: ({ result }) => ({\n id: result?.productId ?? result?.id ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: \"catalog.products.update\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const parsed = parseScopedCommandInput(\n productUpdateSchema,\n raw ?? {},\n ctx,\n translate,\n );\n const { base, custom } = splitCustomFieldPayload(parsed);\n return Object.keys(custom).length\n ? { ...base, customFields: custom }\n : base;\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: \"catalog.products.delete\",\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations();\n const id = resolveCrudRecordId(parsed, ctx, translate);\n if (!id)\n throw new CrudHttpError(400, {\n error: translate(\n \"catalog.errors.id_required\",\n \"Product id is required.\",\n ),\n });\n return { id };\n },\n response: () => ({ ok: true }),\n },\n },\n});\n\nexport const GET = crud.GET;\nexport const POST = crud.POST;\nexport const PUT = crud.PUT;\nexport const DELETE = crud.DELETE;\n\nconst productListItemSchema = z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n subtitle: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n sku: z.string().nullable().optional(),\n handle: z.string().nullable().optional(),\n product_type: z.string().nullable().optional(),\n status_entry_id: z.string().uuid().nullable().optional(),\n primary_currency_code: z.string().nullable().optional(),\n default_unit: z.string().nullable().optional(),\n default_sales_unit: z.string().nullable().optional(),\n default_sales_unit_quantity: z.number().nullable().optional(),\n uom_rounding_scale: z.number().nullable().optional(),\n uom_rounding_mode: z.enum([\"half_up\", \"down\", \"up\"]).nullable().optional(),\n unit_price_enabled: z.boolean().nullable().optional(),\n unit_price_reference_unit: z\n .enum([\"kg\", \"l\", \"m2\", \"m3\", \"pc\"])\n .nullable()\n .optional(),\n unit_price_base_quantity: z.number().nullable().optional(),\n unit_price: z\n .object({\n enabled: z.boolean(),\n reference_unit: z.enum([\"kg\", \"l\", \"m2\", \"m3\", \"pc\"]).nullable(),\n base_quantity: z.number().nullable(),\n })\n .optional(),\n default_media_id: z.string().uuid().nullable().optional(),\n default_media_url: z.string().nullable().optional(),\n weight_value: z.number().nullable().optional(),\n weight_unit: z.string().nullable().optional(),\n dimensions: z.record(z.string(), z.unknown()).nullable().optional(),\n is_configurable: z.boolean().nullable().optional(),\n is_active: z.boolean().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_fieldset_code: z.string().nullable().optional(),\n option_schema_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n offers: z.array(z.record(z.string(), z.unknown())).optional(),\n channelIds: z.array(z.string()).optional(),\n categories: z.array(z.record(z.string(), z.unknown())).optional(),\n categoryIds: z.array(z.string()).optional(),\n tags: z.array(z.string()).optional(),\n pricing: z.record(z.string(), z.unknown()).nullable().optional(),\n});\n\nexport const openApi = createCatalogCrudOpenApi({\n resourceName: \"Product\",\n pluralName: \"Products\",\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(productListItemSchema),\n create: {\n schema: productCreateSchema,\n description: \"Creates a new product in the catalog.\",\n },\n update: {\n schema: productUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: \"Updates an existing product by id.\",\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: \"Deletes a product by id.\",\n },\n});\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAElB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AAEtC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB,2BAA2B;AAC7D,SAAS,+BAA+B;AACxC,SAAS,SAAS;AAClB,YAAY,OAAO;AACnB,SAAS,kBAAkB,0BAA0B;AACrD,SAAS,yBAAyB;AAElC,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC,SAAS,sBAAsB,uBAAuB;AACtD,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,aACJ;AAEF,MAAM,aAAa,EAChB,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,aAAa,EAAE,KAAK,qBAAqB,EAAE,SAAS;AAAA,EACpD,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACtC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACnC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACxC,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACvC,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC5C,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAM,EAAE,SAAS;AAAA,EACxD,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,SAAS;AAAA,EACjD,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EAC1C,aAAa,EAAE,OAAO,QAAQ,EAAE,SAAS;AAAA,EACzC,gBAAgB,EAAE,OAAO,EAAE,MAAM,iBAAiB,EAAE,SAAS;AAC/D,CAAC,EACA,YAAY;AAIf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AAAA,EACrE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACxE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACvE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAC5E;AAEO,MAAM,WAAW;AAEjB,SAAS,YAAY,KAAwB;AAClD,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,SAAO,IACJ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,WAAW,KAAK,KAAK,CAAC;AAC7C;AAEA,eAAsB,oBACpB,OACA,KACkC;AAClC,QAAM,UAAmC,CAAC;AAC1C,QAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAM,uBAAsD,EAAE,OAAO,KAAK;AAE1E,QAAM,sBAAsB,CAAC,QAAkB;AAC7C,UAAM,aAAa,IAAI;AAAA,MACrB,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,KAAK,EAAE,SAAS;AAAA,IACrE;AACA,UAAM,UAAU,IAAI,IAAI,UAAU;AAClC,QAAI,CAAC,QAAQ,MAAM;AACjB,2BAAqB,QAAQ,oBAAI,IAAI;AACrC;AAAA,IACF;AACA,QAAI,CAAC,qBAAqB,OAAO;AAC/B,2BAAqB,QAAQ;AAC7B;AAAA,IACF;AACA,yBAAqB,QAAQ,IAAI;AAAA,MAC/B,MAAM,KAAK,qBAAqB,KAAK,EAAE,OAAO,CAAC,OAAO,QAAQ,IAAI,EAAE,CAAC;AAAA,IACvE;AAAA,EACF;AAEA,QAAM,0BAA0B,MAAM;AACpC,QAAI,CAAC,qBAAqB,MAAO;AACjC,QAAI,qBAAqB,MAAM,SAAS,GAAG;AACzC,cAAQ,KAAK,EAAE,KAAK,uCAAuC;AAC3D;AAAA,IACF;AACA,UAAM,MAAM,MAAM,KAAK,qBAAqB,KAAK;AACjD,UAAM,WAAW,QAAQ;AACzB,QAAI,YAAY,OAAO,aAAa,UAAU;AAC5C,UACE,SAAS,YACT,OAAQ,SAA+B,QAAQ,UAC/C;AACA,cAAM,SAAU,SAA6B;AAC7C,YAAI,CAAC,qBAAqB,MAAM,IAAI,MAAM,GAAG;AAC3C,kBAAQ,KAAK,EAAE,KAAK,uCAAuC;AAAA,QAC7D;AACA;AAAA,MACF;AACA,UACE,SAAS,YACT,MAAM,QAAS,SAA+B,GAAG,GACjD;AACA,cAAM,SAAU,SAA+B,IAAI;AAAA,UAAO,CAAC,OACzD,qBAAqB,MAAO,IAAI,EAAE;AAAA,QACpC;AACA,gBAAQ,KAAK,OAAO,SAChB,EAAE,KAAK,OAAO,IACd,EAAE,KAAK,uCAAuC;AAClD;AAAA,MACF;AAAA,IACF;AACA,YAAQ,KAAK,IAAI,WAAW,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,IAAI;AAAA,EAC/D;AACA,MAAI,MAAM,IAAI;AACZ,YAAQ,KAAK,EAAE,KAAK,MAAM,GAAG;AAAA,EAC/B;AACA,MAAI,MAAM,UAAU,MAAM,OAAO,KAAK,GAAG;AACvC,YAAQ,kBAAkB,EAAE,KAAK,MAAM,OAAO,KAAK,EAAE;AAAA,EACvD;AACA,QAAM,SAAS,iBAAiB,MAAM,QAAQ;AAC9C,MAAI,WAAW,QAAW;AACxB,YAAQ,YAAY;AAAA,EACtB;AACA,QAAM,eAAe,iBAAiB,MAAM,YAAY;AACxD,MAAI,iBAAiB,QAAW;AAC9B,YAAQ,kBAAkB;AAAA,EAC5B;AACA,MAAI,MAAM,aAAa;AACrB,YAAQ,eAAe,EAAE,KAAK,MAAM,YAAY;AAAA,EAClD;AACA,QAAM,QAAQ;AAAA,IACZ,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,IACjE,UAAU,IAAI,MAAM,YAAY;AAAA,EAClC;AACA,QAAM,OAAO,mBAAmB,MAAM,MAAM;AAC5C,MAAI,MAAM;AACR,UAAM,OAAO,IAAI,kBAAkB,IAAI,CAAC;AACxC,UAAM,gBAAgB,MAAM;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,GAAI,MAAM,cAAc,CAAC,IAAI,EAAE,WAAW,KAAK;AAAA,QAC/C,KAAK;AAAA,UACH,EAAE,OAAO,EAAE,QAAQ,KAAK,EAAE;AAAA,UAC1B,EAAE,UAAU,EAAE,QAAQ,KAAK,EAAE;AAAA,UAC7B,EAAE,aAAa,EAAE,QAAQ,KAAK,EAAE;AAAA,UAChC,EAAE,KAAK,EAAE,QAAQ,KAAK,EAAE;AAAA,UACxB,EAAE,QAAQ,EAAE,QAAQ,KAAK,EAAE;AAAA,QAC7B;AAAA,MACF;AAAA,MACA,EAAE,QAAQ,CAAC,IAAI,EAAE;AAAA,MACjB;AAAA,IACF;AACA,UAAM,aAAa,cAChB,IAAI,CAAC,YAAY,QAAQ,EAAE,EAC3B,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACvE,wBAAoB,UAAU;AAAA,EAChC;AAEA,QAAM,mBAAmB,YAAY,MAAM,UAAU;AACrD,MAAI,iBAAiB,QAAQ;AAC3B,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAW,EAAE,KAAK,iBAAiB;AAAA,QACnC,WAAW;AAAA,QACX,GAAG;AAAA,MACL;AAAA,MACA,EAAE,QAAQ,CAAC,MAAM,SAAS,EAAE;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,aAAa,UAChB;AAAA,MAAI,CAAC,UACJ,OAAO,MAAM,YAAY,WACrB,MAAM,UACL,MAAM,SAAS,MAAM;AAAA,IAC5B,EACC,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE;AACpC,wBAAoB,UAAU;AAAA,EAChC;AAEA,QAAM,oBAAoB,YAAY,MAAM,WAAW;AACvD,MAAI,kBAAkB,QAAQ;AAC5B,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,UAAU,EAAE,KAAK,kBAAkB,GAAG,GAAG,MAAM;AAAA,MACjD,EAAE,QAAQ,CAAC,MAAM,SAAS,EAAE;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,aAAa,YAChB;AAAA,MAAI,CAAC,eACJ,OAAO,WAAW,YAAY,WAC1B,WAAW,UACV,WAAW,SAAS,MAAM;AAAA,IACjC,EACC,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE;AACpC,wBAAoB,UAAU;AAAA,EAChC;AAEA,QAAM,eAAe,YAAY,MAAM,MAAM;AAC7C,MAAI,aAAa,QAAQ;AACvB,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,KAAK,EAAE,KAAK,aAAa,GAAG,GAAG,MAAM;AAAA,MACvC,EAAE,QAAQ,CAAC,MAAM,SAAS,EAAE;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,aAAa,YAChB;AAAA,MAAI,CAAC,eACJ,OAAO,WAAW,YAAY,WAC1B,WAAW,UACV,WAAW,SAAS,MAAM;AAAA,IACjC,EACC,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE;AACpC,wBAAoB,UAAU;AAAA,EAChC;AACA,QAAM,iBACJ,OAAO,MAAM,mBAAmB,YAChC,MAAM,eAAe,KAAK,EAAE,SACxB,MAAM,eAAe,KAAK,IAC1B;AACN,QAAM,WAAW,IAAI,MAAM,YAAY;AACvC,MAAI;AACF,UAAM,WAAW,IAAI,UAAU,QAAQ,IAAI;AAC3C,UAAM,YAAY,MAAM,iCAAiC;AAAA,MACvD,WAAW,CAAC,EAAE,QAAQ,eAAe;AAAA,MACrC;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,UAAU,kBAAkB;AAAA,IAC9B,CAAC;AACD,WAAO,OAAO,SAAS,SAAS;AAAA,EAClC,SAAS,KAAK;AAGZ,QAAI,QAAQ,IAAI,aAAa,cAAe,SAAQ,KAAK,gDAAgD,GAAG;AAAA,EAC9G;AACA,0BAAwB;AACxB,SAAO;AACT;AAEO,SAAS,oBACd,OACA,iBACgB;AAChB,QAAM,WAAW,OAAO,SAAS,OAAO,MAAM,QAAQ,CAAC,IACnD,OAAO,MAAM,QAAQ,IACrB;AACJ,QAAM,aAAa,MAAM,YAAY,IAAI,KAAK,MAAM,SAAS,IAAI,oBAAI,KAAK;AAC1E,QAAM,YAAY,MAAM,aAAa,mBAAmB;AACxD,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MAAM,WAAW;AAAA,IAC1B,QAAQ,MAAM,UAAU;AAAA,IACxB,aAAa,MAAM,eAAe;AAAA,IAClC,YAAY,MAAM,cAAc;AAAA,IAChC,iBAAiB,MAAM,mBAAmB;AAAA,IAC1C,UAAU,OAAO,SAAS,QAAQ,KAAK,WAAW,IAAI,WAAW;AAAA,IACjE,MAAM,OAAO,MAAM,WAAW,QAAQ,CAAC,IAAI,oBAAI,KAAK,IAAI;AAAA,EAC1D;AACF;AAmCA,eAAe,0BACb,SACA,KACe;AACf,QAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC/D,MAAI,CAAC,MAAM,OAAQ;AACnB,QAAM,aAAa,MAChB,IAAI,CAAC,SAAU,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,IAAK,EAC5D,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE;AACpC,MAAI,CAAC,WAAW,OAAQ;AACxB,MAAI;AACF,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ;AAAA,MACZ,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,MACjE,UAAU,IAAI,MAAM,YAAY;AAAA,IAClC;AACA,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,SAAS,EAAE,KAAK,WAAW,GAAG,WAAW,MAAM,GAAG,MAAM;AAAA,MAC1D,EAAE,SAAS,EAAE,WAAW,MAAM,EAAE;AAAA,MAChC;AAAA,IACF;AACA,UAAM,aAAa,MAAM;AAAA,MACvB,IAAI;AAAA,QACF,OACG,IAAI,CAAC,UAAU,MAAM,SAAS,EAC9B;AAAA,UACC,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS;AAAA,QAC9D;AAAA,MACJ;AAAA,IACF;AACA,UAAM,gBAAgB,oBAAI,IAGxB;AACF,QAAI,WAAW,QAAQ;AACrB,YAAM,sBAAsB;AAAA,QAC1B,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE;AAAA,QAC1B;AAAA,UACE,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,UACjE,iBAAiB,MAAM,QAAQ,IAAI,eAAe,IAC9C,IAAI,kBACJ;AAAA,UACJ,UAAU,IAAI,MAAM,YAAY;AAAA,QAClC;AAAA,MACF;AACA,YAAM,WAAW,MAAM,mBAAmB,IAAI,cAAc,qBAAqB;AAAA,QAC/E,QAAQ,CAAC,MAAM,QAAQ,MAAM;AAAA,MAC/B,CAAC;AACD,iBAAW,WAAW,UAAU;AAC9B,sBAAc,IAAI,QAAQ,IAAI;AAAA,UAC5B,MAAM,QAAQ;AAAA,UACd,MAAM,QAAQ,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,kBAAkB,oBAAI,IAA4C;AACxE,eAAW,SAAS,QAAQ;AAC1B,YAAM,YACJ,OAAO,MAAM,YAAY,WACrB,MAAM,UACL,MAAM,SAAS,MAAM;AAC5B,UAAI,CAAC,UAAW;AAChB,YAAM,cAAc,cAAc,IAAI,MAAM,SAAS;AACrD,YAAM,QAAQ,gBAAgB,IAAI,SAAS,KAAK,CAAC;AACjD,YAAM,KAAK;AAAA,QACT,IAAI,MAAM;AAAA,QACV,WAAW,MAAM;AAAA,QACjB,aAAa,aAAa,QAAQ;AAAA,QAClC,aAAa,aAAa,QAAQ;AAAA,QAClC,OAAO,MAAM;AAAA,QACb,aAAa,MAAM,eAAe;AAAA,QAClC,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM,kBAAkB;AAAA,QACxC,iBAAiB,MAAM,mBAAmB;AAAA,QAC1C,UAAU,MAAM,YAAY;AAAA,MAC9B,CAAC;AACD,sBAAgB,IAAI,WAAW,KAAK;AAAA,IACtC;AAEA,UAAM,sBAAsB,MAAM;AAAA,MAChC;AAAA,MACA;AAAA,MACA,EAAE,SAAS,EAAE,KAAK,WAAW,GAAG,GAAG,MAAM;AAAA,MACzC,EAAE,UAAU,CAAC,UAAU,GAAG,SAAS,EAAE,UAAU,MAAM,EAAE;AAAA,MACvD;AAAA,IACF;AACA,UAAM,YAAY,oBAAI,IAAY;AAClC,eAAW,cAAc,qBAAqB;AAC5C,YAAM,WACJ,OAAO,WAAW,aAAa,WAC3B,OACC,WAAW,YAAY;AAC9B,UAAI,CAAC,SAAU;AACf,YAAM,WAAW,SAAS,YAAY;AACtC,UAAI,SAAU,WAAU,IAAI,QAAQ;AAAA,IACtC;AACA,UAAM,mBAAmB,UAAU,OAC/B,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,EAAE,IAAI,EAAE,KAAK,MAAM,KAAK,SAAS,EAAE,GAAG,GAAG,MAAM;AAAA,MAC/C,EAAE,QAAQ,CAAC,MAAM,MAAM,EAAE;AAAA,MACzB;AAAA,IACF,IACA,CAAC;AACL,UAAM,iBAAiB,oBAAI,IAA2B;AACtD,eAAW,UAAU,kBAAkB;AACrC,qBAAe,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI;AAAA,IACnD;AACA,UAAM,sBAAsB,oBAAI,IAS9B;AACF,eAAW,cAAc,qBAAqB;AAC5C,YAAM,YACJ,OAAO,WAAW,YAAY,WAC1B,WAAW,UACV,WAAW,SAAS,MAAM;AACjC,UAAI,CAAC,UAAW;AAChB,YAAM,WACJ,OAAO,WAAW,aAAa,WAC3B,OACC,WAAW,YAAY;AAC9B,UAAI,CAAC,SAAU;AACf,YAAM,WAAW,SAAS,YAAY;AACtC,YAAM,aAAa,WACd,eAAe,IAAI,QAAQ,KAAK,OACjC;AACJ,YAAM,SAAS,oBAAoB,IAAI,SAAS,KAAK,CAAC;AACtD,aAAO,KAAK;AAAA,QACV,IAAI,SAAS;AAAA,QACb,MAAM,SAAS,QAAQ;AAAA,QACvB,UAAU,SAAS,YAAY;AAAA,QAC/B;AAAA,QACA;AAAA,MACF,CAAC;AACD,0BAAoB,IAAI,WAAW,MAAM;AAAA,IAC3C;AAEA,UAAM,iBAAiB,MAAM;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE;AAAA,MAC/B,EAAE,UAAU,CAAC,KAAK,EAAE;AAAA,MACpB;AAAA,QACE,UAAU,IAAI,MAAM,YAAY;AAAA,QAChC,gBAAgB,IAAI,MAAM,SAAS;AAAA,MACrC;AAAA,IACF;AACA,UAAM,gBAAgB,oBAAI,IAAsB;AAChD,eAAW,cAAc,gBAAgB;AACvC,YAAM,YACJ,OAAO,WAAW,YAAY,WAC1B,WAAW,UACV,WAAW,SAAS,MAAM;AACjC,UAAI,CAAC,UAAW;AAChB,YAAM,MACJ,OAAO,WAAW,QAAQ,WAAW,OAAQ,WAAW,OAAO;AACjE,UAAI,CAAC,IAAK;AACV,YAAM,QACJ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAC9C,IAAI,QACJ;AACN,UAAI,CAAC,MAAO;AACZ,YAAM,SAAS,cAAc,IAAI,SAAS,KAAK,CAAC;AAChD,aAAO,KAAK,KAAK;AACjB,oBAAc,IAAI,WAAW,MAAM;AAAA,IACrC;AAEA,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA,EAAE,SAAS,EAAE,KAAK,WAAW,GAAG,WAAW,MAAM,GAAG,MAAM;AAAA,MAC1D,EAAE,QAAQ,CAAC,MAAM,SAAS,EAAE;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,mBAAmB,oBAAI,IAAoB;AACjD,eAAW,WAAW,UAAU;AAC9B,YAAM,YACJ,OAAO,QAAQ,YAAY,WACvB,QAAQ,UACP,QAAQ,SAAS,MAAM;AAC9B,UAAI,CAAC,UAAW;AAChB,uBAAiB,IAAI,QAAQ,IAAI,SAAS;AAAA,IAC5C;AACA,UAAM,aAAa,MAAM,KAAK,iBAAiB,KAAK,CAAC;AACrD,UAAM,aACJ,WAAW,SAAS,IAChB;AAAA,MACE,KAAK;AAAA,QACH,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE;AAAA,QAC/B,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE;AAAA,MACjC;AAAA,IACF,IACA,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE;AACrC,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,MACA,EAAE,GAAG,YAAY,GAAG,MAAM;AAAA,MAC1B,EAAE,UAAU,CAAC,SAAS,WAAW,WAAW,WAAW,EAAE;AAAA,MACzD;AAAA,IACF;AACA,UAAM,kBAAkB,oBAAI,IAAwB;AACpD,eAAW,SAAS,WAAW;AAC7B,UAAI,YAA2B;AAC/B,UAAI,MAAM,SAAS;AACjB,oBACE,OAAO,MAAM,YAAY,WACrB,MAAM,UACL,MAAM,SAAS,MAAM;AAAA,MAC9B,WAAW,MAAM,SAAS;AACxB,cAAM,YACJ,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU,MAAM,QAAQ;AACpE,oBAAY,iBAAiB,IAAI,SAAS,KAAK;AAAA,MACjD;AACA,UAAI,CAAC,UAAW;AAChB,YAAM,QAAQ,gBAAgB,IAAI,SAAS,KAAK,CAAC;AACjD,YAAM,KAAK,KAAK;AAChB,sBAAgB,IAAI,WAAW,KAAK;AAAA,IACtC;AAEA,UAAM,yBAAyB;AAAA,MAC7B,IAAI,MAAM;AAAA,IACZ;AACA,UAAM,uBAAuB,oBAAI,IAAiC;AAClE,UAAM,2BACJ,IAAI,0BAA0B,IAAI,MAAM,SAAS;AACnD,UAAM,qBAAqB,IAAI,MAAM,YAAY;AACjD,QACE,0BACA,WAAW,UACX,4BACA,oBACA;AACA,YAAM,iBAAiB,MAAM;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,UACE,SAAS,EAAE,KAAK,WAAW;AAAA,UAC3B,gBAAgB;AAAA,UAChB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,UAAU;AAAA,QACZ;AAAA,QACA,EAAE,QAAQ,CAAC,MAAM,WAAW,YAAY,cAAc,EAAE;AAAA,QACxD,EAAE,gBAAgB,0BAA0B,UAAU,mBAAmB;AAAA,MAC3E;AACA,iBAAW,OAAO,gBAAgB;AAChC,cAAM,YACJ,OAAO,IAAI,YAAY,WACnB,IAAI,UACH,IAAI,SAAS,MAAM;AAC1B,cAAM,UAAU,gBAAgB,IAAI,QAAQ;AAC5C,cAAM,SAAS,OAAO,IAAI,YAAY;AACtC,YAAI,CAAC,aAAa,CAAC,WAAW,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU;AAClE;AACF,cAAM,SACJ,qBAAqB,IAAI,SAAS,KAAK,oBAAI,IAAoB;AACjE,eAAO,IAAI,SAAS,MAAM;AAC1B,6BAAqB,IAAI,WAAW,MAAM;AAAA,MAC5C;AAAA,IACF;AAEA,UAAM,mBAAmB,YAAY,IAAI,MAAM,UAAU;AACzD,UAAM,iBACJ,IAAI,MAAM,cACT,iBAAiB,WAAW,IAAI,iBAAiB,CAAC,IAAI;AACzD,UAAM,iBAAiB,oBAAoB,IAAI,OAAO,cAAc;AACpE,UAAM,iBAAiB,IAAI,UAAU;AAAA,MACnC;AAAA,IACF;AAEA,eAAW,QAAQ,OAAO;AACxB,YAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,UAAI,CAAC,GAAI;AACT,YAAM,eAAe,gBAAgB,IAAI,EAAE,KAAK,CAAC;AACjD,WAAK,SAAS;AACd,YAAMA,cAAa,MAAM;AAAA,QACvB,IAAI;AAAA,UACF,aACG;AAAA,YAAI,CAAC,UACJ,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAAA,UAC1D,EACC,OAAO,CAAC,cAAmC,CAAC,CAAC,SAAS;AAAA,QAC3D;AAAA,MACF;AACA,WAAK,aAAaA;AAClB,YAAM,aAAa,oBAAoB,IAAI,EAAE,KAAK,CAAC;AACnD,WAAK,aAAa;AAClB,WAAK,cAAc,WAAW,IAAI,CAAC,aAAa,SAAS,EAAE;AAC3D,WAAK,OAAO,cAAc,IAAI,EAAE,KAAK,CAAC;AACtC,YAAM,kBAAkB,gBAAgB,IAAI,EAAE,KAAK,CAAC;AACpD,YAAM,gCAAgC,MAAM;AAC1C,YAAI,CAAC,uBAAwB,QAAO,eAAe;AACnD,cAAM,WAAW,gBAAgB,KAAK,YAAY;AAClD,YAAI,CAAC,YAAY,2BAA2B;AAC1C,iBAAO,eAAe;AACxB,cAAM,qBAAqB,qBAAqB,IAAI,EAAE;AACtD,cAAM,SAAS,oBAAoB,IAAI,sBAAsB,KAAK;AAClE,YAAI,CAAC,UAAU,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AACtD,cAAI,QAAQ,IAAI,aAAa,cAAe,SAAQ,KAAK,4DAA4D,EAAE,SAAS,sBAAsB,WAAW,MAAM,EAAE;AACzK,iBAAO,eAAe;AAAA,QACxB;AACA,cAAM,aAAa,eAAe,WAAW;AAC7C,eAAO,OAAO,SAAS,UAAU,KAAK,aAAa,IAC/C,aACA,eAAe;AAAA,MACrB,GAAG;AACH,YAAM,uBACJ,eAAe,aAAaA,YAAW,WAAW,IAC9C,iBACA,EAAE,GAAG,gBAAgB,WAAWA,YAAW,CAAC,EAAE;AACpD,YAAM,OAAO,MAAM,eAAe,aAAa,iBAAiB;AAAA,QAC9D,GAAG;AAAA,QACH,UAAU;AAAA,MACZ,CAAC;AACD,UAAI,MAAM;AACR,aAAK,UAAU;AAAA,UACb,MAAM,qBAAqB,IAAI;AAAA,UAC/B,eACE,OAAO,KAAK,cAAc,WACtB,KAAK,YACJ,KAAK,WAAW,MAAM;AAAA,UAC7B,iBAAiB,qBAAqB,IAAI;AAAA,UAC1C,eAAe,KAAK;AAAA,UACpB,gBAAgB,KAAK;AAAA,UACrB,kBAAkB,KAAK;AAAA,UACvB,cAAc,KAAK;AAAA,UACnB,cAAc,KAAK,eAAe;AAAA,UAClC,UAAU,KAAK,WAAW;AAAA,UAC1B,YAAY,KAAK,aAAa;AAAA,UAC9B,OAAO;AAAA,YACL,YAAY,sBAAsB,IAAI;AAAA,YACtC,UAAU,oBAAoB,IAAI;AAAA,YAClC,YAAY,sBAAsB,IAAI;AAAA,YACtC,SAAS,KAAK,UAAU;AAAA,YACxB,eAAe,KAAK,eAAe;AAAA,YACnC,aAAa,KAAK,cAAc;AAAA,YAChC,mBAAmB,KAAK,mBAAmB;AAAA,UAC7C;AAAA,QACF;AAAA,MACF,OAAO;AACL,aAAK,UAAU;AAAA,MACjB;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,+DAA+D,KAAK;AAAA,EACpF;AAEA,QAAM,aAAa,IAAI,MAAM,SAAS,mBAAmB,IAAI,MAAM,MAAM,IAAI;AAC7E,MAAI,cAAc,CAAC,IAAI,MAAM,aAAa,MAAM,QAAQ,QAAQ,KAAK,GAAG;AACtE,UAAM,SAAS,WAAW,YAAY;AACtC,YAAQ,MAAM,KAAK,CAAC,GAAG,MAAM;AAC3B,YAAM,SAAS,4BAA4B,QAAQ,EAAE,OAAO,EAAE,GAAG;AACjE,YAAM,SAAS,4BAA4B,QAAQ,EAAE,OAAO,EAAE,GAAG;AACjE,UAAI,WAAW,OAAQ,QAAO,SAAS;AACvC,cAAQ,EAAE,SAAS,IAAI,cAAc,EAAE,SAAS,EAAE;AAAA,IACpD,CAAC;AAAA,EACH;AACF;AAEO,SAAS,4BACd,QACA,OACA,KACQ;AACR,QAAM,KAAK,SAAS,IAAI,YAAY;AACpC,QAAM,KAAK,OAAO,IAAI,YAAY;AAClC,MAAI,MAAM,OAAQ,QAAO;AACzB,MAAI,MAAM,OAAQ,QAAO;AACzB,MAAI,EAAE,WAAW,MAAM,EAAG,QAAO;AACjC,MAAI,EAAE,WAAW,MAAM,EAAG,QAAO;AACjC,MAAI,EAAE,SAAS,MAAM,EAAG,QAAO;AAC/B,MAAI,EAAE,SAAS,MAAM,EAAG,QAAO;AAC/B,SAAO;AACT;AAEA,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS;AAAA,IACP,YAAY,EAAE,QAAQ;AAAA,EACxB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,QAAQ;AAAA,IACpB,QAAQ;AAAA,MACN,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF;AAAA,MACA;AAAA,MACA,EAAE;AAAA,MACF,EAAE;AAAA,IACJ;AAAA,IACA,sBAAsB,EAAE,WAAW,CAAC,EAAE,QAAQ,eAAe,EAAE;AAAA,IAC/D,cAAc;AAAA,MACZ,OAAO,EAAE;AAAA,MACT,KAAK,EAAE;AAAA,MACP,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,IACf;AAAA,IACA,cAAc;AAAA,IACd,eAAe,CAAC,SAA6C;AAC3D,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,aAAa,EAAE,GAAG,KAAK;AAC7B,YAAM,YAAY,6BAA6B,IAAI;AACnD,iBAAW,OAAO,OAAO,KAAK,UAAU,GAAG;AACzC,YAAI,IAAI,WAAW,KAAK,GAAG;AACzB,iBAAO,WAAW,GAAG;AAAA,QACvB;AAAA,MACF;AACA,YAAM,cAAc,qBAAqB,WAAW,YAAY,KAAK;AACrE,YAAM,mBACJ,qBAAqB,WAAW,kBAAkB,KAAK;AACzD,YAAM,yBACJ,qBAAqB,WAAW,yBAAyB,KAAK;AAChE,aAAO;AAAA,QACL,GAAG;AAAA,QACH,cAAc;AAAA,QACd,oBAAoB;AAAA,QACpB,2BAA2B;AAAA,QAC3B,GAAG;AAAA,QACH,YAAY;AAAA,UACV,SAAS,QAAQ,WAAW,kBAAkB;AAAA,UAC9C,gBAAgB;AAAA,UAChB,eAAe,WAAW,4BAA4B;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,WAAW;AAAA,EACb;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS;AAAA,UACb;AAAA,UACA,OAAO,CAAC;AAAA,UACR;AAAA,UACA;AAAA,QACF;AACA,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,MAAM;AACvD,eAAO,OAAO,KAAK,MAAM,EAAE,SACvB,EAAE,GAAG,MAAM,cAAc,OAAO,IAChC;AAAA,MACN;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,IAAI,QAAQ,aAAa,QAAQ,MAAM;AAAA,MACzC;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS;AAAA,UACb;AAAA,UACA,OAAO,CAAC;AAAA,UACR;AAAA,UACA;AAAA,QACF;AACA,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,MAAM;AACvD,eAAO,OAAO,KAAK,MAAM,EAAE,SACvB,EAAE,GAAG,MAAM,cAAc,OAAO,IAChC;AAAA,MACN;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,KAAK,oBAAoB,QAAQ,KAAK,SAAS;AACrD,YAAI,CAAC;AACH,gBAAM,IAAI,cAAc,KAAK;AAAA,YAC3B,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AACH,eAAO,EAAE,GAAG;AAAA,MACd;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AACF,CAAC;AAEM,MAAM,MAAM,KAAK;AACjB,MAAM,OAAO,KAAK;AAClB,MAAM,MAAM,KAAK;AACjB,MAAM,SAAS,KAAK;AAE3B,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACnD,6BAA6B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5D,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACnD,mBAAmB,EAAE,KAAK,CAAC,WAAW,QAAQ,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACzE,oBAAoB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;AAAA,EACpD,2BAA2B,EACxB,KAAK,CAAC,MAAM,KAAK,MAAM,MAAM,IAAI,CAAC,EAClC,SAAS,EACT,SAAS;AAAA,EACZ,0BAA0B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACzD,YAAY,EACT,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ;AAAA,IACnB,gBAAgB,EAAE,KAAK,CAAC,MAAM,KAAK,MAAM,MAAM,IAAI,CAAC,EAAE,SAAS;AAAA,IAC/D,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,CAAC,EACA,SAAS;AAAA,EACZ,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,mBAAmB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC7C,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EAClE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EAChE,sBAAsB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACrD,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3C,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAS;AAAA,EAC5D,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACzC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAS;AAAA,EAChE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EAC1C,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AACjE,CAAC;AAEM,MAAM,UAAU,yBAAyB;AAAA,EAC9C,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB,8BAA8B,qBAAqB;AAAA,EACvE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
4
+ "sourcesContent": ["import { z } from \"zod\";\nimport type { EntityManager } from \"@mikro-orm/postgresql\";\nimport { makeCrudRoute } from \"@open-mercato/shared/lib/crud/factory\";\nimport { CrudHttpError } from \"@open-mercato/shared/lib/crud/errors\";\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { resolveTranslations } from \"@open-mercato/shared/lib/i18n/server\";\nimport {\n CatalogOffer,\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductUnitConversion,\n CatalogProductVariant,\n CatalogProductTagAssignment,\n} from \"../../data/entities\";\nimport { CATALOG_PRODUCT_TYPES } from \"../../data/types\";\nimport type { CatalogProductType } from \"../../data/types\";\nimport {\n productCreateSchema,\n productUpdateSchema,\n} from \"../../data/validators\";\nimport { parseScopedCommandInput, resolveCrudRecordId } from \"../utils\";\nimport { splitCustomFieldPayload } from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { E } from \"#generated/entities.ids.generated\";\nimport * as F from \"#generated/entities/catalog_product\";\nimport { parseBooleanFlag, sanitizeSearchTerm } from \"../helpers\";\nimport { escapeLikePattern } from \"@open-mercato/shared/lib/db/escapeLikePattern\";\nimport type { CrudCtx } from \"@open-mercato/shared/lib/crud/factory\";\nimport { buildScopedWhere } from \"@open-mercato/shared/lib/api/crud\";\nimport {\n resolvePriceChannelId,\n resolvePriceOfferId,\n resolvePriceVariantId,\n resolvePriceKindCode,\n type PricingContext,\n type PriceRow,\n} from \"../../lib/pricing\";\nimport type { CatalogPricingService } from \"../../services/catalogPricingService\";\nimport { fieldsetCodeRegex } from \"@open-mercato/core/modules/entities/data/validators\";\nimport { SalesChannel } from \"@open-mercato/core/modules/sales/data/entities\";\nimport {\n createCatalogCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from \"../openapi\";\nimport { findWithDecryption } from \"@open-mercato/shared/lib/encryption/find\";\nimport { canonicalizeUnitCode, toUnitLookupKey } from \"../../lib/unitCodes\";\nconst rawBodySchema = z.object({}).passthrough();\n\nconst UUID_REGEX =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n search: z.string().optional(),\n status: z.string().optional(),\n isActive: z.string().optional(),\n configurable: z.string().optional(),\n productType: z.enum(CATALOG_PRODUCT_TYPES).optional(),\n channelIds: z.string().optional(),\n channelId: z.string().uuid().optional(),\n categoryIds: z.string().optional(),\n tagIds: z.string().optional(),\n offerId: z.string().uuid().optional(),\n userId: z.string().uuid().optional(),\n userGroupId: z.string().uuid().optional(),\n customerId: z.string().uuid().optional(),\n customerGroupId: z.string().uuid().optional(),\n quantity: z.coerce.number().min(1).max(100000).optional(),\n quantityUnit: z.string().trim().max(50).optional(),\n priceDate: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum([\"asc\", \"desc\"]).optional(),\n withDeleted: z.coerce.boolean().optional(),\n customFieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .passthrough();\n\ntype ProductsQuery = z.infer<typeof listSchema>;\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [\"catalog.products.view\"] },\n POST: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n PUT: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n DELETE: { requireAuth: true, requireFeatures: [\"catalog.products.manage\"] },\n};\n\nexport const metadata = routeMetadata;\n\nexport function parseIdList(raw?: string): string[] {\n if (!raw) return [];\n return raw\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => UUID_REGEX.test(value));\n}\n\nexport async function buildProductFilters(\n query: ProductsQuery,\n ctx: CrudCtx,\n): Promise<Record<string, unknown>> {\n const filters: Record<string, unknown> = {};\n const em = (ctx.container.resolve(\"em\") as EntityManager).fork();\n const restrictedProductIds: { value: Set<string> | null } = { value: null };\n\n const intersectProductIds = (ids: string[]) => {\n const normalized = ids.filter(\n (id): id is string => typeof id === \"string\" && id.trim().length > 0,\n );\n const current = new Set(normalized);\n if (!current.size) {\n restrictedProductIds.value = new Set();\n return;\n }\n if (!restrictedProductIds.value) {\n restrictedProductIds.value = current;\n return;\n }\n restrictedProductIds.value = new Set(\n Array.from(restrictedProductIds.value).filter((id) => current.has(id)),\n );\n };\n\n const applyRestrictedProducts = () => {\n if (!restrictedProductIds.value) return;\n if (restrictedProductIds.value.size === 0) {\n filters.id = { $eq: \"00000000-0000-0000-0000-000000000000\" };\n return;\n }\n const ids = Array.from(restrictedProductIds.value);\n const existing = filters.id as Record<string, unknown> | undefined;\n if (existing && typeof existing === \"object\") {\n if (\n \"$eq\" in existing &&\n typeof (existing as { $eq?: unknown }).$eq === \"string\"\n ) {\n const target = (existing as { $eq: string }).$eq;\n if (!restrictedProductIds.value.has(target)) {\n filters.id = { $eq: \"00000000-0000-0000-0000-000000000000\" };\n }\n return;\n }\n if (\n \"$in\" in existing &&\n Array.isArray((existing as { $in?: unknown }).$in)\n ) {\n const subset = (existing as { $in: string[] }).$in.filter((id) =>\n restrictedProductIds.value!.has(id),\n );\n filters.id = subset.length\n ? { $in: subset }\n : { $eq: \"00000000-0000-0000-0000-000000000000\" };\n return;\n }\n }\n filters.id = ids.length === 1 ? { $eq: ids[0] } : { $in: ids };\n };\n if (query.id) {\n filters.id = { $eq: query.id };\n }\n if (query.status && query.status.trim()) {\n filters.status_entry_id = { $eq: query.status.trim() };\n }\n const active = parseBooleanFlag(query.isActive);\n if (active !== undefined) {\n filters.is_active = active;\n }\n const configurable = parseBooleanFlag(query.configurable);\n if (configurable !== undefined) {\n filters.is_configurable = configurable;\n }\n if (query.productType) {\n filters.product_type = { $eq: query.productType };\n }\n const scope = {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n };\n const term = sanitizeSearchTerm(query.search);\n if (term) {\n const like = `%${escapeLikePattern(term)}%`;\n const searchMatches = await findWithDecryption(\n em,\n CatalogProduct,\n {\n ...scope,\n ...(query.withDeleted ? {} : { deletedAt: null }),\n $or: [\n { title: { $ilike: like } },\n { subtitle: { $ilike: like } },\n { description: { $ilike: like } },\n { sku: { $ilike: like } },\n { handle: { $ilike: like } },\n ],\n },\n { fields: [\"id\"] },\n scope,\n );\n const productIds = searchMatches\n .map((product) => product.id)\n .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n intersectProductIds(productIds);\n }\n\n const channelFilterIds = parseIdList(query.channelIds);\n if (channelFilterIds.length) {\n const offerRows = await findWithDecryption(\n em,\n CatalogOffer,\n {\n channelId: { $in: channelFilterIds },\n deletedAt: null,\n ...scope,\n },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = offerRows\n .map((offer) =>\n typeof offer.product === \"string\"\n ? offer.product\n : (offer.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n\n const categoryFilterIds = parseIdList(query.categoryIds);\n if (categoryFilterIds.length) {\n const assignments = await findWithDecryption(\n em,\n CatalogProductCategoryAssignment,\n { category: { $in: categoryFilterIds }, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = assignments\n .map((assignment) =>\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n\n const tagFilterIds = parseIdList(query.tagIds);\n if (tagFilterIds.length) {\n const assignments = await findWithDecryption(\n em,\n CatalogProductTagAssignment,\n { tag: { $in: tagFilterIds }, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const productIds = assignments\n .map((assignment) =>\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null),\n )\n .filter((id): id is string => !!id);\n intersectProductIds(productIds);\n }\n const customFieldset =\n typeof query.customFieldset === \"string\" &&\n query.customFieldset.trim().length\n ? query.customFieldset.trim()\n : null;\n const tenantId = ctx.auth?.tenantId ?? null;\n try {\n const scopedEm = ctx.container.resolve(\"em\") as EntityManager;\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.catalog.catalog_product],\n query,\n em: scopedEm,\n tenantId,\n fieldset: customFieldset ?? undefined,\n });\n Object.assign(filters, cfFilters);\n } catch (err) {\n // Custom field filter parsing may fail for non-existent or misconfigured fields.\n // Fall back to base filters to avoid blocking the product listing.\n if (process.env.NODE_ENV === 'development') console.warn('[catalog:products] custom field filter error', err);\n }\n applyRestrictedProducts();\n return filters;\n}\n\nexport function buildPricingContext(\n query: ProductsQuery,\n channelFallback: string | null,\n): PricingContext {\n const quantity = Number.isFinite(Number(query.quantity))\n ? Number(query.quantity)\n : 1;\n const parsedDate = query.priceDate ? new Date(query.priceDate) : new Date();\n const channelId = query.channelId ?? channelFallback ?? null;\n return {\n channelId,\n offerId: query.offerId ?? null,\n userId: query.userId ?? null,\n userGroupId: query.userGroupId ?? null,\n customerId: query.customerId ?? null,\n customerGroupId: query.customerGroupId ?? null,\n quantity: Number.isFinite(quantity) && quantity > 0 ? quantity : 1,\n date: Number.isNaN(parsedDate.getTime()) ? new Date() : parsedDate,\n };\n}\n\ntype ProductListItem = Record<string, unknown> & {\n id?: string;\n title?: string | null;\n subtitle?: string | null;\n description?: string | null;\n sku?: string | null;\n handle?: string | null;\n product_type?: CatalogProductType | null;\n primary_currency_code?: string | null;\n default_unit?: string | null;\n default_sales_unit?: string | null;\n default_sales_unit_quantity?: number | null;\n uom_rounding_scale?: number | null;\n uom_rounding_mode?: \"half_up\" | \"down\" | \"up\" | null;\n unit_price_enabled?: boolean | null;\n unit_price_reference_unit?: \"kg\" | \"l\" | \"m2\" | \"m3\" | \"pc\" | null;\n unit_price_base_quantity?: number | null;\n default_media_id?: string | null;\n default_media_url?: string | null;\n weight_value?: string | null;\n weightValue?: string | null;\n weight_unit?: string | null;\n weightUnit?: string | null;\n dimensions?: Record<string, unknown> | null;\n custom_fieldset_code?: string | null;\n option_schema_id?: string | null;\n offers?: Array<Record<string, unknown>>;\n channelIds?: string[];\n categories?: Array<Record<string, unknown>>;\n categoryIds?: string[];\n tags?: string[];\n};\n\nasync function decorateProductsAfterList(\n payload: { items?: ProductListItem[] },\n ctx: CrudCtx & { query: ProductsQuery },\n): Promise<void> {\n const items = Array.isArray(payload?.items) ? payload.items : [];\n if (!items.length) return;\n const productIds = items\n .map((item) => (typeof item.id === \"string\" ? item.id : null))\n .filter((id): id is string => !!id);\n if (!productIds.length) return;\n try {\n const em = (ctx.container.resolve(\"em\") as EntityManager).fork();\n const scope = {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n tenantId: ctx.auth?.tenantId ?? null,\n };\n const offers = await findWithDecryption(\n em,\n CatalogOffer,\n { product: { $in: productIds }, deletedAt: null, ...scope },\n { orderBy: { createdAt: \"asc\" } },\n scope,\n );\n const channelIds = Array.from(\n new Set(\n offers\n .map((offer) => offer.channelId)\n .filter(\n (id): id is string => typeof id === \"string\" && id.length > 0,\n ),\n ),\n );\n const channelLookup = new Map<\n string,\n { name?: string | null; code?: string | null }\n >();\n if (channelIds.length) {\n const scopedChannelsWhere = buildScopedWhere(\n { id: { $in: channelIds } },\n {\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n organizationIds: Array.isArray(ctx.organizationIds)\n ? ctx.organizationIds\n : undefined,\n tenantId: ctx.auth?.tenantId ?? null,\n },\n );\n const channels = await findWithDecryption(em, SalesChannel, scopedChannelsWhere, {\n fields: [\"id\", \"name\", \"code\"],\n });\n for (const channel of channels) {\n channelLookup.set(channel.id, {\n name: channel.name,\n code: channel.code ?? null,\n });\n }\n }\n const offersByProduct = new Map<string, Array<Record<string, unknown>>>();\n for (const offer of offers) {\n const productId =\n typeof offer.product === \"string\"\n ? offer.product\n : (offer.product?.id ?? null);\n if (!productId) continue;\n const channelInfo = channelLookup.get(offer.channelId);\n const entry = offersByProduct.get(productId) ?? [];\n entry.push({\n id: offer.id,\n channelId: offer.channelId,\n channelName: channelInfo?.name ?? null,\n channelCode: channelInfo?.code ?? null,\n title: offer.title,\n description: offer.description ?? null,\n isActive: offer.isActive,\n defaultMediaId: offer.defaultMediaId ?? null,\n defaultMediaUrl: offer.defaultMediaUrl ?? null,\n metadata: offer.metadata ?? null,\n });\n offersByProduct.set(productId, entry);\n }\n\n const categoryAssignments = await findWithDecryption(\n em,\n CatalogProductCategoryAssignment,\n { product: { $in: productIds }, ...scope },\n { populate: [\"category\"], orderBy: { position: \"asc\" } },\n scope,\n );\n const parentIds = new Set<string>();\n for (const assignment of categoryAssignments) {\n const category =\n typeof assignment.category === \"string\"\n ? null\n : (assignment.category ?? null);\n if (!category) continue;\n const parentId = category.parentId ?? null;\n if (parentId) parentIds.add(parentId);\n }\n const parentCategories = parentIds.size\n ? await findWithDecryption(\n em,\n CatalogProductCategory,\n { id: { $in: Array.from(parentIds) }, ...scope },\n { fields: [\"id\", \"name\"] },\n scope,\n )\n : [];\n const parentNameById = new Map<string, string | null>();\n for (const parent of parentCategories) {\n parentNameById.set(parent.id, parent.name ?? null);\n }\n const categoriesByProduct = new Map<\n string,\n Array<{\n id: string;\n name: string | null;\n treePath: string | null;\n parentId: string | null;\n parentName: string | null;\n }>\n >();\n for (const assignment of categoryAssignments) {\n const productId =\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null);\n if (!productId) continue;\n const category =\n typeof assignment.category === \"string\"\n ? null\n : (assignment.category ?? null);\n if (!category) continue;\n const parentId = category.parentId ?? null;\n const parentName = parentId\n ? (parentNameById.get(parentId) ?? null)\n : null;\n const bucket = categoriesByProduct.get(productId) ?? [];\n bucket.push({\n id: category.id,\n name: category.name ?? null,\n treePath: category.treePath ?? null,\n parentId,\n parentName,\n });\n categoriesByProduct.set(productId, bucket);\n }\n\n const tagAssignments = await findWithDecryption(\n em,\n CatalogProductTagAssignment,\n { product: { $in: productIds } },\n { populate: [\"tag\"] },\n {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.auth?.orgId ?? null,\n },\n );\n const tagsByProduct = new Map<string, string[]>();\n for (const assignment of tagAssignments) {\n const productId =\n typeof assignment.product === \"string\"\n ? assignment.product\n : (assignment.product?.id ?? null);\n if (!productId) continue;\n const tag =\n typeof assignment.tag === \"string\" ? null : (assignment.tag ?? null);\n if (!tag) continue;\n const label =\n typeof tag.label === \"string\" && tag.label.trim().length\n ? tag.label\n : null;\n if (!label) continue;\n const bucket = tagsByProduct.get(productId) ?? [];\n bucket.push(label);\n tagsByProduct.set(productId, bucket);\n }\n\n const variants = await findWithDecryption(\n em,\n CatalogProductVariant,\n { product: { $in: productIds }, deletedAt: null, ...scope },\n { fields: [\"id\", \"product\"] },\n scope,\n );\n const variantToProduct = new Map<string, string>();\n for (const variant of variants) {\n const productId =\n typeof variant.product === \"string\"\n ? variant.product\n : (variant.product?.id ?? null);\n if (!productId) continue;\n variantToProduct.set(variant.id, productId);\n }\n const variantIds = Array.from(variantToProduct.keys());\n const priceWhere =\n variantIds.length > 0\n ? {\n $or: [\n { product: { $in: productIds } },\n { variant: { $in: variantIds } },\n ],\n }\n : { product: { $in: productIds } };\n const priceRows = await findWithDecryption(\n em,\n CatalogProductPrice,\n { ...priceWhere, ...scope },\n { populate: [\"offer\", \"variant\", \"product\", \"priceKind\"] },\n scope,\n );\n const pricesByProduct = new Map<string, PriceRow[]>();\n for (const price of priceRows) {\n let productId: string | null = null;\n if (price.product) {\n productId =\n typeof price.product === \"string\"\n ? price.product\n : (price.product?.id ?? null);\n } else if (price.variant) {\n const variantId =\n typeof price.variant === \"string\" ? price.variant : price.variant.id;\n productId = variantToProduct.get(variantId) ?? null;\n }\n if (!productId) continue;\n const entry = pricesByProduct.get(productId) ?? [];\n entry.push(price);\n pricesByProduct.set(productId, entry);\n }\n\n const requestQuantityUnitKey = toUnitLookupKey(\n ctx.query.quantityUnit,\n );\n const conversionsByProduct = new Map<string, Map<string, number>>();\n const conversionOrganizationId =\n ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null;\n const conversionTenantId = ctx.auth?.tenantId ?? null;\n if (\n requestQuantityUnitKey &&\n productIds.length &&\n conversionOrganizationId &&\n conversionTenantId\n ) {\n const conversionRows = await findWithDecryption(\n em,\n CatalogProductUnitConversion,\n {\n product: { $in: productIds },\n organizationId: conversionOrganizationId,\n tenantId: conversionTenantId,\n deletedAt: null,\n isActive: true,\n },\n { fields: [\"id\", \"product\", \"unitCode\", \"toBaseFactor\"] },\n { organizationId: conversionOrganizationId, tenantId: conversionTenantId },\n );\n for (const row of conversionRows) {\n const productId =\n typeof row.product === \"string\"\n ? row.product\n : (row.product?.id ?? null);\n const unitKey = toUnitLookupKey(row.unitCode);\n const factor = Number(row.toBaseFactor);\n if (!productId || !unitKey || !Number.isFinite(factor) || factor <= 0)\n continue;\n const bucket =\n conversionsByProduct.get(productId) ?? new Map<string, number>();\n bucket.set(unitKey, factor);\n conversionsByProduct.set(productId, bucket);\n }\n }\n\n const channelFilterIds = parseIdList(ctx.query.channelIds);\n const channelContext =\n ctx.query.channelId ??\n (channelFilterIds.length === 1 ? channelFilterIds[0] : null);\n const pricingContext = buildPricingContext(ctx.query, channelContext);\n const pricingService = ctx.container.resolve<CatalogPricingService>(\n \"catalogPricingService\",\n );\n\n const pricingEntries: Array<{ rows: PriceRow[]; context: PricingContext } | null> = [];\n for (const item of items) {\n const id = typeof item.id === \"string\" ? item.id : null;\n if (!id) {\n pricingEntries.push(null);\n continue;\n }\n const offerEntries = offersByProduct.get(id) ?? [];\n item.offers = offerEntries;\n const channelIds = Array.from(\n new Set(\n offerEntries\n .map((offer) =>\n typeof offer.channelId === \"string\" ? offer.channelId : null,\n )\n .filter((channelId): channelId is string => !!channelId),\n ),\n );\n item.channelIds = channelIds;\n const categories = categoriesByProduct.get(id) ?? [];\n item.categories = categories;\n item.categoryIds = categories.map((category) => category.id);\n item.tags = tagsByProduct.get(id) ?? [];\n const priceCandidates = pricesByProduct.get(id) ?? [];\n const normalizedQuantityForPricing = (() => {\n if (!requestQuantityUnitKey) return pricingContext.quantity;\n const baseUnit = toUnitLookupKey(item.default_unit);\n if (!baseUnit || requestQuantityUnitKey === baseUnit)\n return pricingContext.quantity;\n const productConversions = conversionsByProduct.get(id);\n const factor = productConversions?.get(requestQuantityUnitKey) ?? null;\n if (!factor || !Number.isFinite(factor) || factor <= 0) {\n if (process.env.NODE_ENV === 'development') console.warn(`[catalog.products] Invalid conversion factor for product=${id} unit=${requestQuantityUnitKey} factor=${factor}`);\n return pricingContext.quantity;\n }\n const normalized = pricingContext.quantity * factor;\n return Number.isFinite(normalized) && normalized > 0\n ? normalized\n : pricingContext.quantity;\n })();\n const channelScopedContext =\n pricingContext.channelId || channelIds.length !== 1\n ? pricingContext\n : { ...pricingContext, channelId: channelIds[0] };\n pricingEntries.push({\n rows: priceCandidates,\n context: { ...channelScopedContext, quantity: normalizedQuantityForPricing },\n });\n }\n\n const resolveInputs: Array<{ rows: PriceRow[]; context: PricingContext }> = [];\n const resolveIndices: number[] = [];\n for (let i = 0; i < pricingEntries.length; i++) {\n if (pricingEntries[i] !== null) {\n resolveInputs.push(pricingEntries[i]!);\n resolveIndices.push(i);\n }\n }\n const priceResults = await pricingService.resolvePriceMany(resolveInputs);\n\n for (let i = 0; i < resolveIndices.length; i++) {\n const item = items[resolveIndices[i]];\n const best = priceResults[i];\n if (best) {\n item.pricing = {\n kind: resolvePriceKindCode(best),\n price_kind_id:\n typeof best.priceKind === \"string\"\n ? best.priceKind\n : (best.priceKind?.id ?? null),\n price_kind_code: resolvePriceKindCode(best),\n currency_code: best.currencyCode,\n unit_price_net: best.unitPriceNet,\n unit_price_gross: best.unitPriceGross,\n min_quantity: best.minQuantity,\n max_quantity: best.maxQuantity ?? null,\n tax_rate: best.taxRate ?? null,\n tax_amount: best.taxAmount ?? null,\n scope: {\n variant_id: resolvePriceVariantId(best),\n offer_id: resolvePriceOfferId(best),\n channel_id: resolvePriceChannelId(best),\n user_id: best.userId ?? null,\n user_group_id: best.userGroupId ?? null,\n customer_id: best.customerId ?? null,\n customer_group_id: best.customerGroupId ?? null,\n },\n };\n } else {\n item.pricing = null;\n }\n }\n } catch (error) {\n console.error(\"[decorateProductsAfterList] Failed to load unit conversions\", error);\n }\n\n const searchTerm = ctx.query.search ? sanitizeSearchTerm(ctx.query.search) : null;\n if (searchTerm && !ctx.query.sortField && Array.isArray(payload.items)) {\n const needle = searchTerm.toLowerCase();\n payload.items.sort((a, b) => {\n const scoreA = scoreProductSearchRelevance(needle, a.title, a.sku);\n const scoreB = scoreProductSearchRelevance(needle, b.title, b.sku);\n if (scoreA !== scoreB) return scoreA - scoreB;\n return (a.title ?? \"\").localeCompare(b.title ?? \"\");\n });\n }\n}\n\nexport function scoreProductSearchRelevance(\n needle: string,\n title: string | null | undefined,\n sku: string | null | undefined,\n): number {\n const t = (title ?? \"\").toLowerCase();\n const s = (sku ?? \"\").toLowerCase();\n if (t === needle) return 0;\n if (s === needle) return 1;\n if (t.startsWith(needle)) return 2;\n if (s.startsWith(needle)) return 3;\n if (t.includes(needle)) return 4;\n if (s.includes(needle)) return 5;\n return 6;\n}\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CatalogProduct,\n idField: \"id\",\n orgField: \"organizationId\",\n tenantField: \"tenantId\",\n softDeleteField: \"deletedAt\",\n },\n indexer: {\n entityType: E.catalog.catalog_product,\n },\n list: {\n schema: listSchema,\n entityId: E.catalog.catalog_product,\n fields: [\n F.id,\n F.title,\n F.subtitle,\n F.description,\n F.sku,\n F.handle,\n \"tax_rate_id\",\n \"tax_rate\",\n F.product_type,\n F.status_entry_id,\n F.primary_currency_code,\n F.default_unit,\n \"default_sales_unit\",\n \"default_sales_unit_quantity\",\n \"uom_rounding_scale\",\n \"uom_rounding_mode\",\n \"unit_price_enabled\",\n \"unit_price_reference_unit\",\n \"unit_price_base_quantity\",\n F.default_media_id,\n F.default_media_url,\n F.weight_value,\n F.weight_unit,\n F.dimensions,\n F.is_configurable,\n F.is_active,\n F.metadata,\n \"custom_fieldset_code\",\n \"option_schema_id\",\n F.created_at,\n F.updated_at,\n ],\n decorateCustomFields: { entityIds: [E.catalog.catalog_product] },\n sortFieldMap: {\n title: F.title,\n sku: F.sku,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n buildFilters: buildProductFilters,\n transformItem: (item: ProductListItem | null | undefined) => {\n if (!item) return item;\n const normalized = { ...item };\n const cfEntries = extractAllCustomFieldEntries(item);\n for (const key of Object.keys(normalized)) {\n if (key.startsWith(\"cf:\")) {\n delete normalized[key];\n }\n }\n const defaultUnit = canonicalizeUnitCode(normalized.default_unit) ?? null;\n const defaultSalesUnit =\n canonicalizeUnitCode(normalized.default_sales_unit) ?? null;\n const unitPriceReferenceUnit =\n canonicalizeUnitCode(normalized.unit_price_reference_unit) ?? null;\n return {\n ...normalized,\n default_unit: defaultUnit,\n default_sales_unit: defaultSalesUnit,\n unit_price_reference_unit: unitPriceReferenceUnit,\n ...cfEntries,\n unit_price: {\n enabled: Boolean(normalized.unit_price_enabled),\n reference_unit: unitPriceReferenceUnit,\n base_quantity: normalized.unit_price_base_quantity ?? null,\n },\n };\n },\n },\n hooks: {\n afterList: decorateProductsAfterList,\n },\n actions: {\n create: {\n commandId: \"catalog.products.create\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const parsed = parseScopedCommandInput(\n productCreateSchema,\n raw ?? {},\n ctx,\n translate,\n );\n const { base, custom } = splitCustomFieldPayload(parsed);\n return Object.keys(custom).length\n ? { ...base, customFields: custom }\n : base;\n },\n response: ({ result }) => ({\n id: result?.productId ?? result?.id ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: \"catalog.products.update\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const parsed = parseScopedCommandInput(\n productUpdateSchema,\n raw ?? {},\n ctx,\n translate,\n );\n const { base, custom } = splitCustomFieldPayload(parsed);\n return Object.keys(custom).length\n ? { ...base, customFields: custom }\n : base;\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: \"catalog.products.delete\",\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations();\n const id = resolveCrudRecordId(parsed, ctx, translate);\n if (!id)\n throw new CrudHttpError(400, {\n error: translate(\n \"catalog.errors.id_required\",\n \"Product id is required.\",\n ),\n });\n return { id };\n },\n response: () => ({ ok: true }),\n },\n },\n});\n\nexport const GET = crud.GET;\nexport const POST = crud.POST;\nexport const PUT = crud.PUT;\nexport const DELETE = crud.DELETE;\n\nconst productListItemSchema = z.object({\n id: z.string().uuid(),\n title: z.string().nullable().optional(),\n subtitle: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n sku: z.string().nullable().optional(),\n handle: z.string().nullable().optional(),\n product_type: z.string().nullable().optional(),\n status_entry_id: z.string().uuid().nullable().optional(),\n primary_currency_code: z.string().nullable().optional(),\n default_unit: z.string().nullable().optional(),\n default_sales_unit: z.string().nullable().optional(),\n default_sales_unit_quantity: z.number().nullable().optional(),\n uom_rounding_scale: z.number().nullable().optional(),\n uom_rounding_mode: z.enum([\"half_up\", \"down\", \"up\"]).nullable().optional(),\n unit_price_enabled: z.boolean().nullable().optional(),\n unit_price_reference_unit: z\n .enum([\"kg\", \"l\", \"m2\", \"m3\", \"pc\"])\n .nullable()\n .optional(),\n unit_price_base_quantity: z.number().nullable().optional(),\n unit_price: z\n .object({\n enabled: z.boolean(),\n reference_unit: z.enum([\"kg\", \"l\", \"m2\", \"m3\", \"pc\"]).nullable(),\n base_quantity: z.number().nullable(),\n })\n .optional(),\n default_media_id: z.string().uuid().nullable().optional(),\n default_media_url: z.string().nullable().optional(),\n weight_value: z.number().nullable().optional(),\n weight_unit: z.string().nullable().optional(),\n dimensions: z.record(z.string(), z.unknown()).nullable().optional(),\n is_configurable: z.boolean().nullable().optional(),\n is_active: z.boolean().nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_fieldset_code: z.string().nullable().optional(),\n option_schema_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n updated_at: z.string().nullable().optional(),\n offers: z.array(z.record(z.string(), z.unknown())).optional(),\n channelIds: z.array(z.string()).optional(),\n categories: z.array(z.record(z.string(), z.unknown())).optional(),\n categoryIds: z.array(z.string()).optional(),\n tags: z.array(z.string()).optional(),\n pricing: z.record(z.string(), z.unknown()).nullable().optional(),\n});\n\nexport const openApi = createCatalogCrudOpenApi({\n resourceName: \"Product\",\n pluralName: \"Products\",\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(productListItemSchema),\n create: {\n schema: productCreateSchema,\n description: \"Creates a new product in the catalog.\",\n },\n update: {\n schema: productUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: \"Updates an existing product by id.\",\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: \"Deletes a product by id.\",\n },\n});\n"],
5
+ "mappings": "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",
6
6
  "names": ["channelIds"]
7
7
  }
@@ -120,10 +120,16 @@ async function resolveCatalogPrice(rows, ctx, options) {
120
120
  }
121
121
  return resolved ?? null;
122
122
  }
123
+ async function resolveCatalogPriceBatch(entries, options) {
124
+ return Promise.all(
125
+ entries.map(({ rows, context }) => resolveCatalogPrice(rows, context, options))
126
+ );
127
+ }
123
128
  export {
124
129
  registerCatalogPricingResolver,
125
130
  resetCatalogPricingResolvers,
126
131
  resolveCatalogPrice,
132
+ resolveCatalogPriceBatch,
127
133
  resolvePriceChannelId,
128
134
  resolvePriceKindCode,
129
135
  resolvePriceOfferId,
@@ -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 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"],
5
- "mappings": "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",
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 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": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import {
2
- resolveCatalogPrice
2
+ resolveCatalogPrice,
3
+ resolveCatalogPriceBatch
3
4
  } from "../lib/pricing.js";
4
5
  class DefaultCatalogPricingService {
5
6
  constructor(eventBus) {
@@ -8,6 +9,9 @@ class DefaultCatalogPricingService {
8
9
  async resolvePrice(rows, context) {
9
10
  return resolveCatalogPrice(rows, context, { eventBus: this.eventBus });
10
11
  }
12
+ async resolvePriceMany(entries) {
13
+ return resolveCatalogPriceBatch(entries, { eventBus: this.eventBus });
14
+ }
11
15
  }
12
16
  export {
13
17
  DefaultCatalogPricingService
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/catalog/services/catalogPricingService.ts"],
4
- "sourcesContent": ["import type { EventBus } from '@open-mercato/events'\nimport {\n resolveCatalogPrice,\n type PriceRow,\n type PricingContext,\n} from '../lib/pricing'\n\nexport interface CatalogPricingService {\n resolvePrice(rows: PriceRow[], context: PricingContext): Promise<PriceRow | null>\n}\n\nexport class DefaultCatalogPricingService implements CatalogPricingService {\n constructor(private readonly eventBus?: EventBus | null) {}\n\n async resolvePrice(rows: PriceRow[], context: PricingContext): Promise<PriceRow | null> {\n return resolveCatalogPrice(rows, context, { eventBus: this.eventBus })\n }\n}\n\nexport type { PriceRow, PricingContext }\n"],
5
- "mappings": "AACA;AAAA,EACE;AAAA,OAGK;AAMA,MAAM,6BAA8D;AAAA,EACzE,YAA6B,UAA4B;AAA5B;AAAA,EAA6B;AAAA,EAE1D,MAAM,aAAa,MAAkB,SAAmD;AACtF,WAAO,oBAAoB,MAAM,SAAS,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACvE;AACF;",
4
+ "sourcesContent": ["import type { EventBus } from '@open-mercato/events'\nimport {\n resolveCatalogPrice,\n resolveCatalogPriceBatch,\n type PriceRow,\n type PricingContext,\n} from '../lib/pricing'\n\nexport interface CatalogPricingService {\n resolvePrice(rows: PriceRow[], context: PricingContext): Promise<PriceRow | null>\n resolvePriceMany(entries: Array<{ rows: PriceRow[]; context: PricingContext }>): Promise<Array<PriceRow | null>>\n}\n\nexport class DefaultCatalogPricingService implements CatalogPricingService {\n constructor(private readonly eventBus?: EventBus | null) {}\n\n async resolvePrice(rows: PriceRow[], context: PricingContext): Promise<PriceRow | null> {\n return resolveCatalogPrice(rows, context, { eventBus: this.eventBus })\n }\n\n async resolvePriceMany(entries: Array<{ rows: PriceRow[]; context: PricingContext }>): Promise<Array<PriceRow | null>> {\n return resolveCatalogPriceBatch(entries, { eventBus: this.eventBus })\n }\n}\n\nexport type { PriceRow, PricingContext }\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAOA,MAAM,6BAA8D;AAAA,EACzE,YAA6B,UAA4B;AAA5B;AAAA,EAA6B;AAAA,EAE1D,MAAM,aAAa,MAAkB,SAAmD;AACtF,WAAO,oBAAoB,MAAM,SAAS,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACvE;AAAA,EAEA,MAAM,iBAAiB,SAAgG;AACrH,WAAO,yBAAyB,SAAS,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACtE;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { z } from "zod";
3
- import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
+ import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
4
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
5
5
  import {
6
6
  runCrudMutationGuardAfterSuccess,
@@ -126,9 +126,21 @@ async function decorateActivityItems(em, items, decryptionScope) {
126
126
  items.map((item) => typeof item.dealId === "string" ? item.dealId : null).filter((value) => !!value)
127
127
  )
128
128
  );
129
+ if (dealIds.length > 0 && (!decryptionScope?.tenantId || !decryptionScope?.organizationId)) {
130
+ const { translate } = await resolveTranslations();
131
+ throw new CrudHttpError(400, {
132
+ error: translate("customers.errors.tenant_required", "Tenant context is required")
133
+ });
134
+ }
129
135
  const [users, deals] = await Promise.all([
130
136
  authorIds.length > 0 ? em.find(User, { id: { $in: authorIds } }) : Promise.resolve([]),
131
- dealIds.length > 0 ? decryptionScope ? findWithDecryption(em, CustomerDeal, { id: { $in: dealIds } }, void 0, decryptionScope) : em.find(CustomerDeal, { id: { $in: dealIds } }) : Promise.resolve([])
137
+ dealIds.length > 0 && decryptionScope ? findWithDecryption(
138
+ em,
139
+ CustomerDeal,
140
+ { id: { $in: dealIds }, tenantId: decryptionScope.tenantId, organizationId: decryptionScope.organizationId },
141
+ void 0,
142
+ decryptionScope
143
+ ) : Promise.resolve([])
132
144
  ]);
133
145
  const userMap = new Map(
134
146
  users.map((user) => [
@@ -586,6 +598,7 @@ export {
586
598
  GET,
587
599
  POST,
588
600
  PUT,
601
+ decorateActivityItems,
589
602
  metadata,
590
603
  openApi
591
604
  };