@open-mercato/core 0.10.1-develop.7781.1.079f42c863 → 0.10.1-develop.7783.1.e04ffd226c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- [build:core] found 6211 entry points
1
+ [build:core] found 6212 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 271 entry points
4
4
  [build:core:generated] built successfully
@@ -196,6 +196,19 @@ function resolveAmountInputFromParsed(parsed) {
196
196
  }
197
197
  return null;
198
198
  }
199
+ function hasAmountChanged(incoming, current) {
200
+ if (typeof incoming !== "number" || !Number.isFinite(incoming)) return false;
201
+ return numericStringToNumber(current) !== incoming;
202
+ }
203
+ function resolveAmountInputForUpdate(parsed, record) {
204
+ if (hasAmountChanged(parsed.unitPriceNet, record.unitPriceNet)) {
205
+ return { amount: parsed.unitPriceNet, mode: "net" };
206
+ }
207
+ if (hasAmountChanged(parsed.unitPriceGross, record.unitPriceGross)) {
208
+ return { amount: parsed.unitPriceGross, mode: "gross" };
209
+ }
210
+ return resolveAmountInputFromParsed(parsed);
211
+ }
199
212
  function resolveAmountInputFromRecord(record) {
200
213
  const net = numericStringToNumber(record.unitPriceNet);
201
214
  if (net !== null) return { amount: net, mode: "net" };
@@ -515,7 +528,7 @@ const updatePriceCommand = {
515
528
  }
516
529
  ensureSameTenant(targetPriceKind, targetProduct.tenantId);
517
530
  const taxCalculationService = ctx.container.resolve("taxCalculationService");
518
- const amountInput = resolveAmountInputFromParsed(parsed);
531
+ const amountInput = resolveAmountInputForUpdate(parsed, record);
519
532
  const hasNetInput = Object.prototype.hasOwnProperty.call(parsed, "unitPriceNet");
520
533
  const hasGrossInput = Object.prototype.hasOwnProperty.call(parsed, "unitPriceGross");
521
534
  const hasTaxRateInput = Object.prototype.hasOwnProperty.call(parsed, "taxRate");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/catalog/commands/prices.ts"],
4
- "sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, parseWithCustomFields, setCustomFieldsIfAny, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CatalogOffer, CatalogProduct, CatalogProductPrice, CatalogProductVariant, CatalogPriceKind } from '../data/entities'\nimport { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n priceCreateSchema,\n priceUpdateSchema,\n type PriceCreateInput,\n type PriceUpdateInput,\n} from '../data/validators'\nimport type { TaxCalculationService } from '@open-mercato/core/modules/sales/services/taxCalculationService'\nimport {\n cloneJson,\n commandActorScope,\n commandScopeWhere,\n ensureOrganizationScope,\n ensureSameScope,\n ensureSameTenant,\n ensureTenantScope,\n extractUndoPayload,\n requireVariant,\n requireProduct,\n requireOffer,\n requirePriceKind,\n toNumericString,\n} from './shared'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport {\n capturePriceHistoryEntry,\n hasPriceHistoryRelevantChange,\n priceHistoryInputFromRecord,\n} from '../lib/omnibus'\nimport { resolveOmnibusCache } from '../lib/omnibusCache'\n\nconst priceCrudEvents: CrudEventsConfig = {\n module: 'catalog',\n entity: 'price',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype PriceSnapshot = {\n id: string\n variantId: string | null\n productId: string | null\n offerId: string | null\n organizationId: string\n tenantId: string\n currencyCode: string\n priceKindId: string\n priceKindCode: string\n kind: string\n minQuantity: number\n maxQuantity: number | null\n unitPriceNet: string | null\n unitPriceGross: string | null\n taxRate: string | null\n taxAmount: string | null\n channelId: string | null\n userId: string | null\n userGroupId: string | null\n customerId: string | null\n customerGroupId: string | null\n metadata: Record<string, unknown> | null\n startsAt: string | null\n endsAt: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n}\n\ntype PriceUndoPayload = {\n before?: PriceSnapshot | null\n after?: PriceSnapshot | null\n}\n\nconst PRICE_CHANGE_KEYS = [\n 'currencyCode',\n 'priceKindId',\n 'kind',\n 'minQuantity',\n 'maxQuantity',\n 'unitPriceNet',\n 'unitPriceGross',\n 'taxRate',\n 'taxAmount',\n 'channelId',\n 'userId',\n 'userGroupId',\n 'customerId',\n 'customerGroupId',\n 'metadata',\n 'startsAt',\n 'endsAt',\n] as const satisfies readonly string[]\n\nasync function resolveSnapshotAssociations(\n em: EntityManager,\n snapshot: PriceSnapshot\n): Promise<{\n variant: CatalogProductVariant | null\n product: CatalogProduct\n offer: CatalogOffer | null\n}> {\n const scope = { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }\n let variant: CatalogProductVariant | null = null\n if (snapshot.variantId) {\n variant = await requireVariant(em, snapshot.variantId, scope)\n }\n let product: CatalogProduct | null = null\n if (snapshot.productId) {\n product = await requireProduct(em, snapshot.productId, scope)\n } else if (variant) {\n product =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, scope)\n : variant.product\n }\n if (!product) {\n throw new CrudHttpError(400, { error: 'Price snapshot missing product association.' })\n }\n let offer: CatalogOffer | null = null\n if (snapshot.offerId) {\n offer = await requireOffer(em, snapshot.offerId, scope)\n }\n return { variant, product, offer }\n}\n\nasync function loadPriceSnapshot(\n em: EntityManager,\n id: string,\n scopeWhere: Record<string, unknown> = {},\n): Promise<PriceSnapshot | null> {\n const record = await findOneWithDecryption(\n em,\n CatalogProductPrice,\n { id, ...scopeWhere },\n { populate: ['priceKind', 'product', 'variant', 'offer'] },\n )\n if (!record) return null\n const variantId =\n typeof record.variant === 'string'\n ? record.variant\n : record.variant\n ? record.variant.id\n : null\n const productRef = record.product\n ? record.product\n : typeof record.variant === 'object' && record.variant\n ? record.variant.product\n : null\n const productId =\n typeof productRef === 'string'\n ? productRef\n : productRef\n ? productRef.id\n : null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n const priceKind = record.priceKind\n const priceKindId =\n typeof priceKind === 'string'\n ? priceKind\n : priceKind\n ? priceKind.id\n : null\n if (!priceKindId) {\n throw new CrudHttpError(400, { error: 'Price is missing price kind metadata.' })\n }\n const priceKindCode =\n typeof priceKind === 'object' && priceKind ? priceKind.code : record.kind\n return {\n id: record.id,\n variantId,\n productId,\n offerId: typeof record.offer === 'string' ? record.offer : record.offer ? record.offer.id : null,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n currencyCode: record.currencyCode,\n priceKindId,\n priceKindCode,\n kind: record.kind,\n minQuantity: record.minQuantity,\n maxQuantity: record.maxQuantity ?? null,\n unitPriceNet: record.unitPriceNet ?? null,\n unitPriceGross: record.unitPriceGross ?? null,\n taxRate: record.taxRate ?? null,\n taxAmount: record.taxAmount ?? null,\n channelId: record.channelId ?? null,\n userId: record.userId ?? null,\n userGroupId: record.userGroupId ?? null,\n customerId: record.customerId ?? null,\n customerGroupId: record.customerGroupId ?? null,\n metadata: record.metadata ? cloneJson(record.metadata) : null,\n startsAt: record.startsAt ? record.startsAt.toISOString() : null,\n endsAt: record.endsAt ? record.endsAt.toISOString() : null,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n }\n}\n\nfunction applyPriceSnapshot(em: EntityManager, record: CatalogProductPrice, snapshot: PriceSnapshot): void {\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.currencyCode = snapshot.currencyCode\n record.priceKind = em.getReference(CatalogPriceKind, snapshot.priceKindId)\n record.kind = snapshot.priceKindCode || snapshot.kind\n record.minQuantity = snapshot.minQuantity\n record.maxQuantity = snapshot.maxQuantity ?? null\n record.unitPriceNet = snapshot.unitPriceNet ?? null\n record.unitPriceGross = snapshot.unitPriceGross ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.taxAmount = snapshot.taxAmount ?? null\n record.channelId = snapshot.channelId ?? null\n record.userId = snapshot.userId ?? null\n record.userGroupId = snapshot.userGroupId ?? null\n record.customerId = snapshot.customerId ?? null\n record.customerGroupId = snapshot.customerGroupId ?? null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.startsAt = snapshot.startsAt ? new Date(snapshot.startsAt) : null\n record.endsAt = snapshot.endsAt ? new Date(snapshot.endsAt) : null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n}\n\nfunction priceSeedFromSnapshot(snapshot: PriceSnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n variant: snapshot.variantId ?? null,\n product: snapshot.productId ?? null,\n offer: snapshot.offerId ?? null,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n priceKind: snapshot.priceKindId,\n currencyCode: snapshot.currencyCode,\n kind: snapshot.priceKindCode || snapshot.kind,\n minQuantity: snapshot.minQuantity,\n maxQuantity: snapshot.maxQuantity ?? null,\n unitPriceNet: snapshot.unitPriceNet ?? null,\n unitPriceGross: snapshot.unitPriceGross ?? null,\n taxRate: snapshot.taxRate ?? null,\n taxAmount: snapshot.taxAmount ?? null,\n channelId: snapshot.channelId ?? null,\n userId: snapshot.userId ?? null,\n userGroupId: snapshot.userGroupId ?? null,\n customerId: snapshot.customerId ?? null,\n customerGroupId: snapshot.customerGroupId ?? null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n startsAt: snapshot.startsAt ? new Date(snapshot.startsAt) : null,\n endsAt: snapshot.endsAt ? new Date(snapshot.endsAt) : null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n }\n}\n\ntype PriceAmountInput = {\n amount: number\n mode: 'net' | 'gross'\n}\n\nfunction resolveAmountInputFromParsed(\n parsed: Partial<Pick<PriceCreateInput, 'unitPriceNet' | 'unitPriceGross'>>\n): PriceAmountInput | null {\n if (typeof parsed.unitPriceNet === 'number' && Number.isFinite(parsed.unitPriceNet)) {\n return { amount: parsed.unitPriceNet, mode: 'net' }\n }\n if (typeof parsed.unitPriceGross === 'number' && Number.isFinite(parsed.unitPriceGross)) {\n return { amount: parsed.unitPriceGross, mode: 'gross' }\n }\n return null\n}\n\nfunction resolveAmountInputFromRecord(record: CatalogProductPrice): PriceAmountInput | null {\n const net = numericStringToNumber(record.unitPriceNet)\n if (net !== null) return { amount: net, mode: 'net' }\n const gross = numericStringToNumber(record.unitPriceGross)\n if (gross !== null) return { amount: gross, mode: 'gross' }\n return null\n}\n\nfunction numericStringToNumber(value: string | null | undefined): number | null {\n if (value === undefined || value === null) return null\n const numeric = Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nconst createPriceCommand: CommandHandler<PriceCreateInput, { priceId: string }> = {\n id: 'catalog.prices.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(priceCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const actorScope = commandActorScope(ctx)\n let variant: CatalogProductVariant | null = null\n let product: CatalogProduct | null = null\n if (parsed.variantId) {\n variant = await requireVariant(em, parsed.variantId, actorScope)\n product =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, {\n tenantId: variant.tenantId,\n organizationId: variant.organizationId,\n })\n : variant.product\n }\n if (parsed.productId) {\n const explicitProduct = await requireProduct(em, parsed.productId, actorScope)\n if (product && explicitProduct.id !== product.id) {\n throw new CrudHttpError(400, { error: 'Variant does not belong to the provided product.' })\n }\n product = explicitProduct\n }\n if (!variant && !product) {\n throw new CrudHttpError(400, { error: 'Provide either a variantId or productId for pricing.' })\n }\n const scopeSource = variant ?? product!\n ensureTenantScope(ctx, scopeSource.tenantId)\n ensureOrganizationScope(ctx, scopeSource.organizationId)\n const scopeSourceScope = {\n tenantId: scopeSource.tenantId,\n organizationId: scopeSource.organizationId,\n }\n\n const priceKind = await requirePriceKind(em, parsed.priceKindId, scopeSourceScope)\n ensureSameTenant(priceKind, scopeSource.tenantId)\n\n let offer: CatalogOffer | null = null\n if (parsed.offerId) {\n offer = await requireOffer(em, parsed.offerId, scopeSourceScope)\n ensureSameScope(offer, scopeSource.organizationId, scopeSource.tenantId)\n const offerProduct =\n typeof offer.product === 'string'\n ? await requireProduct(em, offer.product, {\n tenantId: offer.tenantId,\n organizationId: offer.organizationId,\n })\n : offer.product\n if (product && offerProduct.id !== product.id) {\n throw new CrudHttpError(400, { error: 'Offer does not belong to the selected product.' })\n }\n product = offerProduct\n }\n const productEntity = product\n const channelId = parsed.channelId ?? (offer ? offer.channelId : null)\n const taxCalculationService = ctx.container.resolve<TaxCalculationService>('taxCalculationService')\n const amountInput = resolveAmountInputFromParsed(parsed)\n let unitPriceNetValue = toNumericString(parsed.unitPriceNet)\n let unitPriceGrossValue = toNumericString(parsed.unitPriceGross)\n let taxRateValue = toNumericString(parsed.taxRate)\n let taxAmountValue: string | null = null\n if (amountInput) {\n const calculation = await taxCalculationService.calculateUnitAmounts({\n amount: amountInput.amount,\n mode: amountInput.mode,\n organizationId: scopeSource.organizationId,\n tenantId: scopeSource.tenantId,\n taxRateId: parsed.taxRateId ?? null,\n taxRate: parsed.taxRate ?? null,\n })\n unitPriceNetValue = toNumericString(calculation.netAmount)\n unitPriceGrossValue = toNumericString(calculation.grossAmount)\n taxAmountValue = toNumericString(calculation.taxAmount)\n taxRateValue = toNumericString(calculation.taxRate)\n }\n\n const now = new Date()\n const record = em.create(CatalogProductPrice, {\n organizationId: scopeSource.organizationId,\n tenantId: scopeSource.tenantId,\n variant,\n product: productEntity ?? undefined,\n offer: offer ?? undefined,\n priceKind,\n currencyCode: parsed.currencyCode,\n kind: priceKind.code,\n minQuantity: parsed.minQuantity ?? 1,\n maxQuantity: parsed.maxQuantity ?? null,\n unitPriceNet: unitPriceNetValue,\n unitPriceGross: unitPriceGrossValue,\n taxRate: taxRateValue,\n taxAmount: taxAmountValue,\n channelId,\n userId: parsed.userId ?? null,\n userGroupId: parsed.userGroupId ?? null,\n customerId: parsed.customerId ?? null,\n customerGroupId: parsed.customerGroupId ?? null,\n metadata: parsed.metadata ? cloneJson(parsed.metadata) : null,\n startsAt: parsed.startsAt ?? null,\n endsAt: parsed.endsAt ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: priceCrudEvents,\n })\n await capturePriceHistoryEntry(em, priceHistoryInputFromRecord(record), 'create', {\n cache: resolveOmnibusCache(ctx.container),\n })\n return { priceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadPriceSnapshot(em, result.priceId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as PriceSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.create', 'Create product price'),\n resourceKind: 'catalog.price',\n resourceId: result.priceId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: {\n after,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductPrice, { id: after.id })\n if (!record) return\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n em.remove(record)\n await em.flush()\n await capturePriceHistoryEntry(em, after, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.create' },\n cache: resolveOmnibusCache(ctx.container),\n })\n const resetValues = buildCustomFieldResetMap(undefined, after.custom ?? undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n values: resetValues,\n })\n }\n },\n redo: makeCreateRedo<CatalogProductPrice, PriceSnapshot, PriceCreateInput, { priceId: string }>({\n entityClass: CatalogProductPrice,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: priceSeedFromSnapshot,\n buildResult: (entity) => ({ priceId: entity.id }),\n events: priceCrudEvents,\n afterRestore: async ({ em, ctx, entity, snapshot }) => {\n if (snapshot.custom && Object.keys(snapshot.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: entity.id,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n values: snapshot.custom,\n })\n }\n await capturePriceHistoryEntry(em, snapshot, 'create', {\n metadata: { redoneCommand: 'catalog.prices.create' },\n cache: resolveOmnibusCache(ctx.container),\n })\n },\n }),\n}\n\nconst updatePriceCommand: CommandHandler<PriceUpdateInput, { priceId: string }> = {\n id: 'catalog.prices.update',\n async prepare(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(em, id, commandScopeWhere(ctx))\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(priceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n CatalogProductPrice,\n { id: parsed.id, ...commandScopeWhere(ctx) },\n { populate: ['priceKind'] },\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Catalog price not found' })\n const recordScope = { tenantId: record.tenantId, organizationId: record.organizationId }\n const currentVariantRef = record.variant\n let targetVariant: CatalogProductVariant | null = null\n if (typeof currentVariantRef === 'string') {\n targetVariant = await requireVariant(em, currentVariantRef, recordScope)\n } else if (currentVariantRef) {\n targetVariant = currentVariantRef\n }\n const currentProductRef = record.product ?? (targetVariant ? targetVariant.product : null)\n let targetProduct: CatalogProduct | null = null\n if (typeof currentProductRef === 'string') {\n targetProduct = await requireProduct(em, currentProductRef, recordScope)\n } else if (currentProductRef) {\n targetProduct = currentProductRef\n }\n const recordHistoryInput = priceHistoryInputFromRecord(record)\n const historyBefore = {\n ...recordHistoryInput,\n productId: recordHistoryInput.productId ?? targetProduct?.id ?? null,\n }\n\n if (parsed.variantId !== undefined) {\n if (!parsed.variantId) {\n targetVariant = null\n } else {\n targetVariant = await requireVariant(em, parsed.variantId, recordScope)\n targetProduct =\n typeof targetVariant.product === 'string'\n ? await requireProduct(em, targetVariant.product, recordScope)\n : targetVariant.product\n }\n }\n\n if (targetVariant && (targetVariant as CatalogProductVariant | null)?.product === undefined) {\n targetVariant = await requireVariant(em, targetVariant.id, recordScope)\n }\n\n if (parsed.productId !== undefined) {\n if (!parsed.productId) {\n targetProduct = null\n } else {\n const explicitProduct = await requireProduct(em, parsed.productId, recordScope)\n if (targetVariant) {\n const variantProductId =\n typeof targetVariant.product === 'string'\n ? targetVariant.product\n : targetVariant.product.id\n if (variantProductId !== explicitProduct.id) {\n throw new CrudHttpError(400, { error: 'Variant does not belong to the provided product.' })\n }\n }\n targetProduct = explicitProduct\n }\n }\n\n if (!targetVariant && !targetProduct) {\n throw new CrudHttpError(400, { error: 'Price must remain associated with a product or variant.' })\n }\n if (!targetProduct && targetVariant) {\n targetProduct =\n typeof targetVariant.product === 'string'\n ? await requireProduct(em, targetVariant.product, recordScope)\n : targetVariant.product\n }\n if (!targetProduct) {\n throw new CrudHttpError(400, { error: 'Unable to resolve product for price.' })\n }\n\n let targetOffer: CatalogOffer | null = null\n if (record.offer) {\n targetOffer =\n typeof record.offer === 'string'\n ? await requireOffer(em, record.offer, recordScope)\n : record.offer\n }\n if (parsed.offerId !== undefined) {\n if (!parsed.offerId) {\n targetOffer = null\n } else {\n const explicitOffer = await requireOffer(em, parsed.offerId, recordScope)\n ensureSameScope(explicitOffer, targetProduct.organizationId, targetProduct.tenantId)\n const offerProductId =\n typeof explicitOffer.product === 'string'\n ? explicitOffer.product\n : explicitOffer.product.id\n if (offerProductId !== targetProduct.id) {\n throw new CrudHttpError(400, { error: 'Offer does not belong to the selected product.' })\n }\n targetOffer = explicitOffer\n }\n }\n\n ensureTenantScope(ctx, targetProduct.tenantId)\n ensureOrganizationScope(ctx, targetProduct.organizationId)\n\n let targetPriceKind: CatalogPriceKind | null = null\n if (record.priceKind) {\n targetPriceKind =\n typeof record.priceKind === 'string'\n ? await requirePriceKind(em, record.priceKind, recordScope)\n : record.priceKind\n }\n if (parsed.priceKindId !== undefined) {\n if (!parsed.priceKindId) {\n throw new CrudHttpError(400, { error: 'Price kind is required.' })\n }\n targetPriceKind = await requirePriceKind(em, parsed.priceKindId, recordScope)\n }\n if (!targetPriceKind) {\n throw new CrudHttpError(400, { error: 'Price kind is required.' })\n }\n ensureSameTenant(targetPriceKind, targetProduct.tenantId)\n\n const taxCalculationService = ctx.container.resolve<TaxCalculationService>('taxCalculationService')\n const amountInput = resolveAmountInputFromParsed(parsed)\n const hasNetInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceNet')\n const hasGrossInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceGross')\n const hasTaxRateInput = Object.prototype.hasOwnProperty.call(parsed, 'taxRate')\n const hasTaxRateIdInput = Object.prototype.hasOwnProperty.call(parsed, 'taxRateId')\n let taxCalculationResult: Awaited<ReturnType<TaxCalculationService['calculateUnitAmounts']>> | null = null\n let calculationBase = amountInput\n if (!calculationBase && (hasTaxRateInput || hasTaxRateIdInput)) {\n calculationBase = resolveAmountInputFromRecord(record)\n }\n if (calculationBase) {\n const taxRateIdForCalculation = hasTaxRateIdInput ? parsed.taxRateId ?? null : undefined\n const taxRateForCalculation = hasTaxRateInput\n ? parsed.taxRate ?? null\n : taxRateIdForCalculation === null\n ? null\n : record.taxRate ?? null\n taxCalculationResult = await taxCalculationService.calculateUnitAmounts({\n amount: calculationBase.amount,\n mode: calculationBase.mode,\n organizationId: targetProduct.organizationId,\n tenantId: targetProduct.tenantId,\n taxRateId: taxRateIdForCalculation,\n taxRate: taxRateForCalculation,\n })\n }\n\n record.variant = targetVariant\n record.product = targetProduct\n record.offer = targetOffer\n record.organizationId = targetProduct.organizationId\n record.tenantId = targetProduct.tenantId\n record.priceKind = targetPriceKind\n record.kind = targetPriceKind.code\n\n if (parsed.currencyCode !== undefined) record.currencyCode = parsed.currencyCode\n if (parsed.minQuantity !== undefined) record.minQuantity = parsed.minQuantity ?? 1\n if (parsed.maxQuantity !== undefined) record.maxQuantity = parsed.maxQuantity ?? null\n if (taxCalculationResult) {\n record.unitPriceNet = toNumericString(taxCalculationResult.netAmount)\n record.unitPriceGross = toNumericString(taxCalculationResult.grossAmount)\n record.taxRate = toNumericString(taxCalculationResult.taxRate)\n record.taxAmount = toNumericString(taxCalculationResult.taxAmount)\n } else {\n if (hasNetInput) {\n record.unitPriceNet = toNumericString(parsed.unitPriceNet)\n }\n if (hasGrossInput) {\n record.unitPriceGross = toNumericString(parsed.unitPriceGross)\n }\n if (hasTaxRateInput) {\n record.taxRate = toNumericString(parsed.taxRate)\n if (parsed.taxRate == null) {\n record.taxAmount = null\n }\n } else if (hasTaxRateIdInput && parsed.taxRateId === null) {\n record.taxRate = null\n record.taxAmount = null\n }\n }\n if (parsed.channelId !== undefined) {\n record.channelId = parsed.channelId ?? null\n } else if (parsed.offerId !== undefined && targetOffer) {\n record.channelId = targetOffer.channelId\n }\n if (parsed.userId !== undefined) record.userId = parsed.userId ?? null\n if (parsed.userGroupId !== undefined) record.userGroupId = parsed.userGroupId ?? null\n if (parsed.customerId !== undefined) record.customerId = parsed.customerId ?? null\n if (parsed.customerGroupId !== undefined) record.customerGroupId = parsed.customerGroupId ?? null\n if (parsed.metadata !== undefined) {\n record.metadata = parsed.metadata ? cloneJson(parsed.metadata) : null\n }\n if (Object.prototype.hasOwnProperty.call(parsed, 'startsAt')) {\n record.startsAt = parsed.startsAt ?? null\n }\n if (Object.prototype.hasOwnProperty.call(parsed, 'endsAt')) {\n record.endsAt = parsed.endsAt ?? null\n }\n await em.flush()\n if (custom && Object.keys(custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n }\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: priceCrudEvents,\n })\n const historyAfter = priceHistoryInputFromRecord(record)\n if (hasPriceHistoryRelevantChange(historyBefore, historyAfter)) {\n await capturePriceHistoryEntry(em, historyAfter, 'update', {\n cache: resolveOmnibusCache(ctx.container),\n invalidatePrices: [historyBefore],\n previous: historyBefore,\n })\n }\n return { priceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadPriceSnapshot(em, result.priceId)\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as PriceSnapshot | undefined\n const after = snapshots.after as PriceSnapshot | undefined\n if (!before || !after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.update', 'Update product price'),\n resourceKind: 'catalog.price',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as Record<string, unknown>,\n after as Record<string, unknown>,\n PRICE_CHANGE_KEYS\n ),\n payload: {\n undo: {\n before,\n after,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const after = payload?.after\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductPrice, { id: before.id })\n if (!record) {\n const { variant, product, offer } = await resolveSnapshotAssociations(em, before)\n record = em.create(CatalogProductPrice, {\n id: before.id,\n variant,\n product,\n offer,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n currencyCode: before.currencyCode,\n priceKind: em.getReference(CatalogPriceKind, before.priceKindId),\n kind: before.priceKindCode || before.kind,\n minQuantity: before.minQuantity,\n maxQuantity: before.maxQuantity ?? null,\n unitPriceNet: before.unitPriceNet ?? null,\n unitPriceGross: before.unitPriceGross ?? null,\n taxRate: before.taxRate ?? null,\n taxAmount: before.taxAmount ?? null,\n channelId: before.channelId ?? null,\n userId: before.userId ?? null,\n userGroupId: before.userGroupId ?? null,\n customerId: before.customerId ?? null,\n customerGroupId: before.customerGroupId ?? null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n startsAt: before.startsAt ? new Date(before.startsAt) : null,\n endsAt: before.endsAt ? new Date(before.endsAt) : null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyPriceSnapshot(em, record, before)\n await em.flush()\n await capturePriceHistoryEntry(em, before, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.update' },\n cache: resolveOmnibusCache(ctx.container),\n invalidatePrices: [after],\n previous: after,\n })\n const resetValues = buildCustomFieldResetMap(\n before.custom ?? undefined,\n after?.custom ?? undefined\n )\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: resetValues,\n })\n }\n },\n}\n\nconst deletePriceCommand: CommandHandler<\n { body?: Record<string, unknown>; query?: Record<string, unknown> },\n { priceId: string }\n> = {\n id: 'catalog.prices.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(em, id, commandScopeWhere(ctx))\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(em, CatalogProductPrice, { id, ...commandScopeWhere(ctx) })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog price not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const { product } = await resolvePriceRecordAssociations(em, record)\n\n const baseEm = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(baseEm, id)\n\n const deletedPrice = snapshot ?? priceHistoryInputFromRecord(record)\n em.remove(record)\n await em.flush()\n await capturePriceHistoryEntry(em, deletedPrice, 'delete', { cache: resolveOmnibusCache(ctx.container) })\n if (snapshot?.custom && Object.keys(snapshot.custom).length) {\n const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: resetValues,\n })\n }\n }\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n },\n events: priceCrudEvents,\n })\n return { priceId: id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as PriceSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.delete', 'Delete product price'),\n resourceKind: 'catalog.price',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductPrice, { id: before.id })\n if (!record) {\n const { variant, product, offer } = await resolveSnapshotAssociations(em, before)\n record = em.create(CatalogProductPrice, {\n id: before.id,\n variant,\n product,\n offer,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n currencyCode: before.currencyCode,\n priceKind: em.getReference(CatalogPriceKind, before.priceKindId),\n kind: before.priceKindCode || before.kind,\n minQuantity: before.minQuantity,\n maxQuantity: before.maxQuantity ?? null,\n unitPriceNet: before.unitPriceNet ?? null,\n unitPriceGross: before.unitPriceGross ?? null,\n taxRate: before.taxRate ?? null,\n taxAmount: before.taxAmount ?? null,\n channelId: before.channelId ?? null,\n userId: before.userId ?? null,\n userGroupId: before.userGroupId ?? null,\n customerId: before.customerId ?? null,\n customerGroupId: before.customerGroupId ?? null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n startsAt: before.startsAt ? new Date(before.startsAt) : null,\n endsAt: before.endsAt ? new Date(before.endsAt) : null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyPriceSnapshot(em, record, before)\n await em.flush()\n await capturePriceHistoryEntry(em, before, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.delete' },\n cache: resolveOmnibusCache(ctx.container),\n })\n if (before.custom && Object.keys(before.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: before.custom,\n })\n }\n },\n}\n\nregisterCommand(createPriceCommand)\nregisterCommand(updatePriceCommand)\nregisterCommand(deletePriceCommand)\n\nasync function resolvePriceRecordAssociations(\n em: EntityManager,\n record: CatalogProductPrice,\n): Promise<{ product: CatalogProduct; variant: CatalogProductVariant | null }> {\n const scope = { tenantId: record.tenantId, organizationId: record.organizationId }\n const variant = record.variant\n ? (typeof record.variant === 'string'\n ? await requireVariant(em, record.variant, scope)\n : record.variant)\n : null\n if (record.product) {\n const product =\n typeof record.product === 'string'\n ? await requireProduct(em, record.product, scope)\n : record.product\n return { product, variant }\n }\n if (variant?.product) {\n const productRef = variant.product\n const product =\n typeof productRef === 'string'\n ? await requireProduct(em, productRef, scope)\n : productRef\n return { product, variant }\n }\n if (record.offer) {\n const offer =\n typeof record.offer === 'string'\n ? await requireOffer(em, record.offer, scope)\n : record.offer\n const productRef = offer?.product ?? null\n if (productRef) {\n const product =\n typeof productRef === 'string'\n ? await requireProduct(em, productRef, scope)\n : productRef\n return { product, variant }\n }\n }\n throw new CrudHttpError(400, { error: 'Catalog price is not linked to a product.' })\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { buildChanges, requireId, parseWithCustomFields, setCustomFieldsIfAny, emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CatalogOffer, CatalogProduct, CatalogProductPrice, CatalogProductVariant, CatalogPriceKind } from '../data/entities'\nimport { loadCustomFieldSnapshot, buildCustomFieldResetMap } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n priceCreateSchema,\n priceUpdateSchema,\n type PriceCreateInput,\n type PriceUpdateInput,\n} from '../data/validators'\nimport type { TaxCalculationService } from '@open-mercato/core/modules/sales/services/taxCalculationService'\nimport {\n cloneJson,\n commandActorScope,\n commandScopeWhere,\n ensureOrganizationScope,\n ensureSameScope,\n ensureSameTenant,\n ensureTenantScope,\n extractUndoPayload,\n requireVariant,\n requireProduct,\n requireOffer,\n requirePriceKind,\n toNumericString,\n} from './shared'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport {\n capturePriceHistoryEntry,\n hasPriceHistoryRelevantChange,\n priceHistoryInputFromRecord,\n} from '../lib/omnibus'\nimport { resolveOmnibusCache } from '../lib/omnibusCache'\n\nconst priceCrudEvents: CrudEventsConfig = {\n module: 'catalog',\n entity: 'price',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\ntype PriceSnapshot = {\n id: string\n variantId: string | null\n productId: string | null\n offerId: string | null\n organizationId: string\n tenantId: string\n currencyCode: string\n priceKindId: string\n priceKindCode: string\n kind: string\n minQuantity: number\n maxQuantity: number | null\n unitPriceNet: string | null\n unitPriceGross: string | null\n taxRate: string | null\n taxAmount: string | null\n channelId: string | null\n userId: string | null\n userGroupId: string | null\n customerId: string | null\n customerGroupId: string | null\n metadata: Record<string, unknown> | null\n startsAt: string | null\n endsAt: string | null\n createdAt: string\n updatedAt: string\n custom: Record<string, unknown> | null\n}\n\ntype PriceUndoPayload = {\n before?: PriceSnapshot | null\n after?: PriceSnapshot | null\n}\n\nconst PRICE_CHANGE_KEYS = [\n 'currencyCode',\n 'priceKindId',\n 'kind',\n 'minQuantity',\n 'maxQuantity',\n 'unitPriceNet',\n 'unitPriceGross',\n 'taxRate',\n 'taxAmount',\n 'channelId',\n 'userId',\n 'userGroupId',\n 'customerId',\n 'customerGroupId',\n 'metadata',\n 'startsAt',\n 'endsAt',\n] as const satisfies readonly string[]\n\nasync function resolveSnapshotAssociations(\n em: EntityManager,\n snapshot: PriceSnapshot\n): Promise<{\n variant: CatalogProductVariant | null\n product: CatalogProduct\n offer: CatalogOffer | null\n}> {\n const scope = { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId }\n let variant: CatalogProductVariant | null = null\n if (snapshot.variantId) {\n variant = await requireVariant(em, snapshot.variantId, scope)\n }\n let product: CatalogProduct | null = null\n if (snapshot.productId) {\n product = await requireProduct(em, snapshot.productId, scope)\n } else if (variant) {\n product =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, scope)\n : variant.product\n }\n if (!product) {\n throw new CrudHttpError(400, { error: 'Price snapshot missing product association.' })\n }\n let offer: CatalogOffer | null = null\n if (snapshot.offerId) {\n offer = await requireOffer(em, snapshot.offerId, scope)\n }\n return { variant, product, offer }\n}\n\nasync function loadPriceSnapshot(\n em: EntityManager,\n id: string,\n scopeWhere: Record<string, unknown> = {},\n): Promise<PriceSnapshot | null> {\n const record = await findOneWithDecryption(\n em,\n CatalogProductPrice,\n { id, ...scopeWhere },\n { populate: ['priceKind', 'product', 'variant', 'offer'] },\n )\n if (!record) return null\n const variantId =\n typeof record.variant === 'string'\n ? record.variant\n : record.variant\n ? record.variant.id\n : null\n const productRef = record.product\n ? record.product\n : typeof record.variant === 'object' && record.variant\n ? record.variant.product\n : null\n const productId =\n typeof productRef === 'string'\n ? productRef\n : productRef\n ? productRef.id\n : null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n })\n const priceKind = record.priceKind\n const priceKindId =\n typeof priceKind === 'string'\n ? priceKind\n : priceKind\n ? priceKind.id\n : null\n if (!priceKindId) {\n throw new CrudHttpError(400, { error: 'Price is missing price kind metadata.' })\n }\n const priceKindCode =\n typeof priceKind === 'object' && priceKind ? priceKind.code : record.kind\n return {\n id: record.id,\n variantId,\n productId,\n offerId: typeof record.offer === 'string' ? record.offer : record.offer ? record.offer.id : null,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n currencyCode: record.currencyCode,\n priceKindId,\n priceKindCode,\n kind: record.kind,\n minQuantity: record.minQuantity,\n maxQuantity: record.maxQuantity ?? null,\n unitPriceNet: record.unitPriceNet ?? null,\n unitPriceGross: record.unitPriceGross ?? null,\n taxRate: record.taxRate ?? null,\n taxAmount: record.taxAmount ?? null,\n channelId: record.channelId ?? null,\n userId: record.userId ?? null,\n userGroupId: record.userGroupId ?? null,\n customerId: record.customerId ?? null,\n customerGroupId: record.customerGroupId ?? null,\n metadata: record.metadata ? cloneJson(record.metadata) : null,\n startsAt: record.startsAt ? record.startsAt.toISOString() : null,\n endsAt: record.endsAt ? record.endsAt.toISOString() : null,\n createdAt: record.createdAt.toISOString(),\n updatedAt: record.updatedAt.toISOString(),\n custom: Object.keys(custom).length ? custom : null,\n }\n}\n\nfunction applyPriceSnapshot(em: EntityManager, record: CatalogProductPrice, snapshot: PriceSnapshot): void {\n record.organizationId = snapshot.organizationId\n record.tenantId = snapshot.tenantId\n record.currencyCode = snapshot.currencyCode\n record.priceKind = em.getReference(CatalogPriceKind, snapshot.priceKindId)\n record.kind = snapshot.priceKindCode || snapshot.kind\n record.minQuantity = snapshot.minQuantity\n record.maxQuantity = snapshot.maxQuantity ?? null\n record.unitPriceNet = snapshot.unitPriceNet ?? null\n record.unitPriceGross = snapshot.unitPriceGross ?? null\n record.taxRate = snapshot.taxRate ?? null\n record.taxAmount = snapshot.taxAmount ?? null\n record.channelId = snapshot.channelId ?? null\n record.userId = snapshot.userId ?? null\n record.userGroupId = snapshot.userGroupId ?? null\n record.customerId = snapshot.customerId ?? null\n record.customerGroupId = snapshot.customerGroupId ?? null\n record.metadata = snapshot.metadata ? cloneJson(snapshot.metadata) : null\n record.startsAt = snapshot.startsAt ? new Date(snapshot.startsAt) : null\n record.endsAt = snapshot.endsAt ? new Date(snapshot.endsAt) : null\n record.createdAt = new Date(snapshot.createdAt)\n record.updatedAt = new Date(snapshot.updatedAt)\n}\n\nfunction priceSeedFromSnapshot(snapshot: PriceSnapshot): Record<string, unknown> {\n return {\n id: snapshot.id,\n variant: snapshot.variantId ?? null,\n product: snapshot.productId ?? null,\n offer: snapshot.offerId ?? null,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n priceKind: snapshot.priceKindId,\n currencyCode: snapshot.currencyCode,\n kind: snapshot.priceKindCode || snapshot.kind,\n minQuantity: snapshot.minQuantity,\n maxQuantity: snapshot.maxQuantity ?? null,\n unitPriceNet: snapshot.unitPriceNet ?? null,\n unitPriceGross: snapshot.unitPriceGross ?? null,\n taxRate: snapshot.taxRate ?? null,\n taxAmount: snapshot.taxAmount ?? null,\n channelId: snapshot.channelId ?? null,\n userId: snapshot.userId ?? null,\n userGroupId: snapshot.userGroupId ?? null,\n customerId: snapshot.customerId ?? null,\n customerGroupId: snapshot.customerGroupId ?? null,\n metadata: snapshot.metadata ? cloneJson(snapshot.metadata) : null,\n startsAt: snapshot.startsAt ? new Date(snapshot.startsAt) : null,\n endsAt: snapshot.endsAt ? new Date(snapshot.endsAt) : null,\n createdAt: new Date(snapshot.createdAt),\n updatedAt: new Date(snapshot.updatedAt),\n }\n}\n\ntype PriceAmountInput = {\n amount: number\n mode: 'net' | 'gross'\n}\n\nfunction resolveAmountInputFromParsed(\n parsed: Partial<Pick<PriceCreateInput, 'unitPriceNet' | 'unitPriceGross'>>\n): PriceAmountInput | null {\n if (typeof parsed.unitPriceNet === 'number' && Number.isFinite(parsed.unitPriceNet)) {\n return { amount: parsed.unitPriceNet, mode: 'net' }\n }\n if (typeof parsed.unitPriceGross === 'number' && Number.isFinite(parsed.unitPriceGross)) {\n return { amount: parsed.unitPriceGross, mode: 'gross' }\n }\n return null\n}\n\nfunction hasAmountChanged(incoming: unknown, current: string | null | undefined): incoming is number {\n if (typeof incoming !== 'number' || !Number.isFinite(incoming)) return false\n return numericStringToNumber(current) !== incoming\n}\n\nfunction resolveAmountInputForUpdate(\n parsed: Partial<Pick<PriceCreateInput, 'unitPriceNet' | 'unitPriceGross'>>,\n record: CatalogProductPrice\n): PriceAmountInput | null {\n if (hasAmountChanged(parsed.unitPriceNet, record.unitPriceNet)) {\n return { amount: parsed.unitPriceNet, mode: 'net' }\n }\n if (hasAmountChanged(parsed.unitPriceGross, record.unitPriceGross)) {\n return { amount: parsed.unitPriceGross, mode: 'gross' }\n }\n return resolveAmountInputFromParsed(parsed)\n}\n\nfunction resolveAmountInputFromRecord(record: CatalogProductPrice): PriceAmountInput | null {\n const net = numericStringToNumber(record.unitPriceNet)\n if (net !== null) return { amount: net, mode: 'net' }\n const gross = numericStringToNumber(record.unitPriceGross)\n if (gross !== null) return { amount: gross, mode: 'gross' }\n return null\n}\n\nfunction numericStringToNumber(value: string | null | undefined): number | null {\n if (value === undefined || value === null) return null\n const numeric = Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nconst createPriceCommand: CommandHandler<PriceCreateInput, { priceId: string }> = {\n id: 'catalog.prices.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(priceCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const actorScope = commandActorScope(ctx)\n let variant: CatalogProductVariant | null = null\n let product: CatalogProduct | null = null\n if (parsed.variantId) {\n variant = await requireVariant(em, parsed.variantId, actorScope)\n product =\n typeof variant.product === 'string'\n ? await requireProduct(em, variant.product, {\n tenantId: variant.tenantId,\n organizationId: variant.organizationId,\n })\n : variant.product\n }\n if (parsed.productId) {\n const explicitProduct = await requireProduct(em, parsed.productId, actorScope)\n if (product && explicitProduct.id !== product.id) {\n throw new CrudHttpError(400, { error: 'Variant does not belong to the provided product.' })\n }\n product = explicitProduct\n }\n if (!variant && !product) {\n throw new CrudHttpError(400, { error: 'Provide either a variantId or productId for pricing.' })\n }\n const scopeSource = variant ?? product!\n ensureTenantScope(ctx, scopeSource.tenantId)\n ensureOrganizationScope(ctx, scopeSource.organizationId)\n const scopeSourceScope = {\n tenantId: scopeSource.tenantId,\n organizationId: scopeSource.organizationId,\n }\n\n const priceKind = await requirePriceKind(em, parsed.priceKindId, scopeSourceScope)\n ensureSameTenant(priceKind, scopeSource.tenantId)\n\n let offer: CatalogOffer | null = null\n if (parsed.offerId) {\n offer = await requireOffer(em, parsed.offerId, scopeSourceScope)\n ensureSameScope(offer, scopeSource.organizationId, scopeSource.tenantId)\n const offerProduct =\n typeof offer.product === 'string'\n ? await requireProduct(em, offer.product, {\n tenantId: offer.tenantId,\n organizationId: offer.organizationId,\n })\n : offer.product\n if (product && offerProduct.id !== product.id) {\n throw new CrudHttpError(400, { error: 'Offer does not belong to the selected product.' })\n }\n product = offerProduct\n }\n const productEntity = product\n const channelId = parsed.channelId ?? (offer ? offer.channelId : null)\n const taxCalculationService = ctx.container.resolve<TaxCalculationService>('taxCalculationService')\n const amountInput = resolveAmountInputFromParsed(parsed)\n let unitPriceNetValue = toNumericString(parsed.unitPriceNet)\n let unitPriceGrossValue = toNumericString(parsed.unitPriceGross)\n let taxRateValue = toNumericString(parsed.taxRate)\n let taxAmountValue: string | null = null\n if (amountInput) {\n const calculation = await taxCalculationService.calculateUnitAmounts({\n amount: amountInput.amount,\n mode: amountInput.mode,\n organizationId: scopeSource.organizationId,\n tenantId: scopeSource.tenantId,\n taxRateId: parsed.taxRateId ?? null,\n taxRate: parsed.taxRate ?? null,\n })\n unitPriceNetValue = toNumericString(calculation.netAmount)\n unitPriceGrossValue = toNumericString(calculation.grossAmount)\n taxAmountValue = toNumericString(calculation.taxAmount)\n taxRateValue = toNumericString(calculation.taxRate)\n }\n\n const now = new Date()\n const record = em.create(CatalogProductPrice, {\n organizationId: scopeSource.organizationId,\n tenantId: scopeSource.tenantId,\n variant,\n product: productEntity ?? undefined,\n offer: offer ?? undefined,\n priceKind,\n currencyCode: parsed.currencyCode,\n kind: priceKind.code,\n minQuantity: parsed.minQuantity ?? 1,\n maxQuantity: parsed.maxQuantity ?? null,\n unitPriceNet: unitPriceNetValue,\n unitPriceGross: unitPriceGrossValue,\n taxRate: taxRateValue,\n taxAmount: taxAmountValue,\n channelId,\n userId: parsed.userId ?? null,\n userGroupId: parsed.userGroupId ?? null,\n customerId: parsed.customerId ?? null,\n customerGroupId: parsed.customerGroupId ?? null,\n metadata: parsed.metadata ? cloneJson(parsed.metadata) : null,\n startsAt: parsed.startsAt ?? null,\n endsAt: parsed.endsAt ?? null,\n createdAt: now,\n updatedAt: now,\n })\n em.persist(record)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: priceCrudEvents,\n })\n await capturePriceHistoryEntry(em, priceHistoryInputFromRecord(record), 'create', {\n cache: resolveOmnibusCache(ctx.container),\n })\n return { priceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadPriceSnapshot(em, result.priceId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as PriceSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.create', 'Create product price'),\n resourceKind: 'catalog.price',\n resourceId: result.priceId,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: {\n after,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await em.findOne(CatalogProductPrice, { id: after.id })\n if (!record) return\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n em.remove(record)\n await em.flush()\n await capturePriceHistoryEntry(em, after, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.create' },\n cache: resolveOmnibusCache(ctx.container),\n })\n const resetValues = buildCustomFieldResetMap(undefined, after.custom ?? undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: after.id,\n organizationId: after.organizationId,\n tenantId: after.tenantId,\n values: resetValues,\n })\n }\n },\n redo: makeCreateRedo<CatalogProductPrice, PriceSnapshot, PriceCreateInput, { priceId: string }>({\n entityClass: CatalogProductPrice,\n getSnapshotId: (snapshot) => snapshot.id,\n seedFromSnapshot: priceSeedFromSnapshot,\n buildResult: (entity) => ({ priceId: entity.id }),\n events: priceCrudEvents,\n afterRestore: async ({ em, ctx, entity, snapshot }) => {\n if (snapshot.custom && Object.keys(snapshot.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: entity.id,\n organizationId: entity.organizationId,\n tenantId: entity.tenantId,\n values: snapshot.custom,\n })\n }\n await capturePriceHistoryEntry(em, snapshot, 'create', {\n metadata: { redoneCommand: 'catalog.prices.create' },\n cache: resolveOmnibusCache(ctx.container),\n })\n },\n }),\n}\n\nconst updatePriceCommand: CommandHandler<PriceUpdateInput, { priceId: string }> = {\n id: 'catalog.prices.update',\n async prepare(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(em, id, commandScopeWhere(ctx))\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(priceUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(\n em,\n CatalogProductPrice,\n { id: parsed.id, ...commandScopeWhere(ctx) },\n { populate: ['priceKind'] },\n { tenantId: parsed.tenantId, organizationId: parsed.organizationId },\n )\n if (!record) throw new CrudHttpError(404, { error: 'Catalog price not found' })\n const recordScope = { tenantId: record.tenantId, organizationId: record.organizationId }\n const currentVariantRef = record.variant\n let targetVariant: CatalogProductVariant | null = null\n if (typeof currentVariantRef === 'string') {\n targetVariant = await requireVariant(em, currentVariantRef, recordScope)\n } else if (currentVariantRef) {\n targetVariant = currentVariantRef\n }\n const currentProductRef = record.product ?? (targetVariant ? targetVariant.product : null)\n let targetProduct: CatalogProduct | null = null\n if (typeof currentProductRef === 'string') {\n targetProduct = await requireProduct(em, currentProductRef, recordScope)\n } else if (currentProductRef) {\n targetProduct = currentProductRef\n }\n const recordHistoryInput = priceHistoryInputFromRecord(record)\n const historyBefore = {\n ...recordHistoryInput,\n productId: recordHistoryInput.productId ?? targetProduct?.id ?? null,\n }\n\n if (parsed.variantId !== undefined) {\n if (!parsed.variantId) {\n targetVariant = null\n } else {\n targetVariant = await requireVariant(em, parsed.variantId, recordScope)\n targetProduct =\n typeof targetVariant.product === 'string'\n ? await requireProduct(em, targetVariant.product, recordScope)\n : targetVariant.product\n }\n }\n\n if (targetVariant && (targetVariant as CatalogProductVariant | null)?.product === undefined) {\n targetVariant = await requireVariant(em, targetVariant.id, recordScope)\n }\n\n if (parsed.productId !== undefined) {\n if (!parsed.productId) {\n targetProduct = null\n } else {\n const explicitProduct = await requireProduct(em, parsed.productId, recordScope)\n if (targetVariant) {\n const variantProductId =\n typeof targetVariant.product === 'string'\n ? targetVariant.product\n : targetVariant.product.id\n if (variantProductId !== explicitProduct.id) {\n throw new CrudHttpError(400, { error: 'Variant does not belong to the provided product.' })\n }\n }\n targetProduct = explicitProduct\n }\n }\n\n if (!targetVariant && !targetProduct) {\n throw new CrudHttpError(400, { error: 'Price must remain associated with a product or variant.' })\n }\n if (!targetProduct && targetVariant) {\n targetProduct =\n typeof targetVariant.product === 'string'\n ? await requireProduct(em, targetVariant.product, recordScope)\n : targetVariant.product\n }\n if (!targetProduct) {\n throw new CrudHttpError(400, { error: 'Unable to resolve product for price.' })\n }\n\n let targetOffer: CatalogOffer | null = null\n if (record.offer) {\n targetOffer =\n typeof record.offer === 'string'\n ? await requireOffer(em, record.offer, recordScope)\n : record.offer\n }\n if (parsed.offerId !== undefined) {\n if (!parsed.offerId) {\n targetOffer = null\n } else {\n const explicitOffer = await requireOffer(em, parsed.offerId, recordScope)\n ensureSameScope(explicitOffer, targetProduct.organizationId, targetProduct.tenantId)\n const offerProductId =\n typeof explicitOffer.product === 'string'\n ? explicitOffer.product\n : explicitOffer.product.id\n if (offerProductId !== targetProduct.id) {\n throw new CrudHttpError(400, { error: 'Offer does not belong to the selected product.' })\n }\n targetOffer = explicitOffer\n }\n }\n\n ensureTenantScope(ctx, targetProduct.tenantId)\n ensureOrganizationScope(ctx, targetProduct.organizationId)\n\n let targetPriceKind: CatalogPriceKind | null = null\n if (record.priceKind) {\n targetPriceKind =\n typeof record.priceKind === 'string'\n ? await requirePriceKind(em, record.priceKind, recordScope)\n : record.priceKind\n }\n if (parsed.priceKindId !== undefined) {\n if (!parsed.priceKindId) {\n throw new CrudHttpError(400, { error: 'Price kind is required.' })\n }\n targetPriceKind = await requirePriceKind(em, parsed.priceKindId, recordScope)\n }\n if (!targetPriceKind) {\n throw new CrudHttpError(400, { error: 'Price kind is required.' })\n }\n ensureSameTenant(targetPriceKind, targetProduct.tenantId)\n\n const taxCalculationService = ctx.container.resolve<TaxCalculationService>('taxCalculationService')\n const amountInput = resolveAmountInputForUpdate(parsed, record)\n const hasNetInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceNet')\n const hasGrossInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceGross')\n const hasTaxRateInput = Object.prototype.hasOwnProperty.call(parsed, 'taxRate')\n const hasTaxRateIdInput = Object.prototype.hasOwnProperty.call(parsed, 'taxRateId')\n let taxCalculationResult: Awaited<ReturnType<TaxCalculationService['calculateUnitAmounts']>> | null = null\n let calculationBase = amountInput\n if (!calculationBase && (hasTaxRateInput || hasTaxRateIdInput)) {\n calculationBase = resolveAmountInputFromRecord(record)\n }\n if (calculationBase) {\n const taxRateIdForCalculation = hasTaxRateIdInput ? parsed.taxRateId ?? null : undefined\n const taxRateForCalculation = hasTaxRateInput\n ? parsed.taxRate ?? null\n : taxRateIdForCalculation === null\n ? null\n : record.taxRate ?? null\n taxCalculationResult = await taxCalculationService.calculateUnitAmounts({\n amount: calculationBase.amount,\n mode: calculationBase.mode,\n organizationId: targetProduct.organizationId,\n tenantId: targetProduct.tenantId,\n taxRateId: taxRateIdForCalculation,\n taxRate: taxRateForCalculation,\n })\n }\n\n record.variant = targetVariant\n record.product = targetProduct\n record.offer = targetOffer\n record.organizationId = targetProduct.organizationId\n record.tenantId = targetProduct.tenantId\n record.priceKind = targetPriceKind\n record.kind = targetPriceKind.code\n\n if (parsed.currencyCode !== undefined) record.currencyCode = parsed.currencyCode\n if (parsed.minQuantity !== undefined) record.minQuantity = parsed.minQuantity ?? 1\n if (parsed.maxQuantity !== undefined) record.maxQuantity = parsed.maxQuantity ?? null\n if (taxCalculationResult) {\n record.unitPriceNet = toNumericString(taxCalculationResult.netAmount)\n record.unitPriceGross = toNumericString(taxCalculationResult.grossAmount)\n record.taxRate = toNumericString(taxCalculationResult.taxRate)\n record.taxAmount = toNumericString(taxCalculationResult.taxAmount)\n } else {\n if (hasNetInput) {\n record.unitPriceNet = toNumericString(parsed.unitPriceNet)\n }\n if (hasGrossInput) {\n record.unitPriceGross = toNumericString(parsed.unitPriceGross)\n }\n if (hasTaxRateInput) {\n record.taxRate = toNumericString(parsed.taxRate)\n if (parsed.taxRate == null) {\n record.taxAmount = null\n }\n } else if (hasTaxRateIdInput && parsed.taxRateId === null) {\n record.taxRate = null\n record.taxAmount = null\n }\n }\n if (parsed.channelId !== undefined) {\n record.channelId = parsed.channelId ?? null\n } else if (parsed.offerId !== undefined && targetOffer) {\n record.channelId = targetOffer.channelId\n }\n if (parsed.userId !== undefined) record.userId = parsed.userId ?? null\n if (parsed.userGroupId !== undefined) record.userGroupId = parsed.userGroupId ?? null\n if (parsed.customerId !== undefined) record.customerId = parsed.customerId ?? null\n if (parsed.customerGroupId !== undefined) record.customerGroupId = parsed.customerGroupId ?? null\n if (parsed.metadata !== undefined) {\n record.metadata = parsed.metadata ? cloneJson(parsed.metadata) : null\n }\n if (Object.prototype.hasOwnProperty.call(parsed, 'startsAt')) {\n record.startsAt = parsed.startsAt ?? null\n }\n if (Object.prototype.hasOwnProperty.call(parsed, 'endsAt')) {\n record.endsAt = parsed.endsAt ?? null\n }\n await em.flush()\n if (custom && Object.keys(custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n values: custom,\n })\n }\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId,\n tenantId: record.tenantId,\n },\n events: priceCrudEvents,\n })\n const historyAfter = priceHistoryInputFromRecord(record)\n if (hasPriceHistoryRelevantChange(historyBefore, historyAfter)) {\n await capturePriceHistoryEntry(em, historyAfter, 'update', {\n cache: resolveOmnibusCache(ctx.container),\n invalidatePrices: [historyBefore],\n previous: historyBefore,\n })\n }\n return { priceId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadPriceSnapshot(em, result.priceId)\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as PriceSnapshot | undefined\n const after = snapshots.after as PriceSnapshot | undefined\n if (!before || !after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.update', 'Update product price'),\n resourceKind: 'catalog.price',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes: buildChanges(\n before as Record<string, unknown>,\n after as Record<string, unknown>,\n PRICE_CHANGE_KEYS\n ),\n payload: {\n undo: {\n before,\n after,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const after = payload?.after\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductPrice, { id: before.id })\n if (!record) {\n const { variant, product, offer } = await resolveSnapshotAssociations(em, before)\n record = em.create(CatalogProductPrice, {\n id: before.id,\n variant,\n product,\n offer,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n currencyCode: before.currencyCode,\n priceKind: em.getReference(CatalogPriceKind, before.priceKindId),\n kind: before.priceKindCode || before.kind,\n minQuantity: before.minQuantity,\n maxQuantity: before.maxQuantity ?? null,\n unitPriceNet: before.unitPriceNet ?? null,\n unitPriceGross: before.unitPriceGross ?? null,\n taxRate: before.taxRate ?? null,\n taxAmount: before.taxAmount ?? null,\n channelId: before.channelId ?? null,\n userId: before.userId ?? null,\n userGroupId: before.userGroupId ?? null,\n customerId: before.customerId ?? null,\n customerGroupId: before.customerGroupId ?? null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n startsAt: before.startsAt ? new Date(before.startsAt) : null,\n endsAt: before.endsAt ? new Date(before.endsAt) : null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyPriceSnapshot(em, record, before)\n await em.flush()\n await capturePriceHistoryEntry(em, before, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.update' },\n cache: resolveOmnibusCache(ctx.container),\n invalidatePrices: [after],\n previous: after,\n })\n const resetValues = buildCustomFieldResetMap(\n before.custom ?? undefined,\n after?.custom ?? undefined\n )\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: resetValues,\n })\n }\n },\n}\n\nconst deletePriceCommand: CommandHandler<\n { body?: Record<string, unknown>; query?: Record<string, unknown> },\n { priceId: string }\n> = {\n id: 'catalog.prices.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(em, id, commandScopeWhere(ctx))\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Price id is required')\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const record = await findOneWithDecryption(em, CatalogProductPrice, { id, ...commandScopeWhere(ctx) })\n if (!record) throw new CrudHttpError(404, { error: 'Catalog price not found' })\n ensureTenantScope(ctx, record.tenantId)\n ensureOrganizationScope(ctx, record.organizationId)\n const { product } = await resolvePriceRecordAssociations(em, record)\n\n const baseEm = (ctx.container.resolve('em') as EntityManager)\n const snapshot = await loadPriceSnapshot(baseEm, id)\n\n const deletedPrice = snapshot ?? priceHistoryInputFromRecord(record)\n em.remove(record)\n await em.flush()\n await capturePriceHistoryEntry(em, deletedPrice, 'delete', { cache: resolveOmnibusCache(ctx.container) })\n if (snapshot?.custom && Object.keys(snapshot.custom).length) {\n const resetValues = buildCustomFieldResetMap(snapshot.custom, undefined)\n if (Object.keys(resetValues).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: id,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n values: resetValues,\n })\n }\n }\n const de = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: record,\n identifiers: {\n id,\n organizationId: snapshot?.organizationId ?? record.organizationId,\n tenantId: snapshot?.tenantId ?? record.tenantId,\n },\n events: priceCrudEvents,\n })\n return { priceId: id }\n },\n buildLog: async ({ snapshots }) => {\n const before = snapshots.before as PriceSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('catalog.audit.prices.delete', 'Delete product price'),\n resourceKind: 'catalog.price',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: {\n before,\n } satisfies PriceUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<PriceUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let record = await em.findOne(CatalogProductPrice, { id: before.id })\n if (!record) {\n const { variant, product, offer } = await resolveSnapshotAssociations(em, before)\n record = em.create(CatalogProductPrice, {\n id: before.id,\n variant,\n product,\n offer,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n currencyCode: before.currencyCode,\n priceKind: em.getReference(CatalogPriceKind, before.priceKindId),\n kind: before.priceKindCode || before.kind,\n minQuantity: before.minQuantity,\n maxQuantity: before.maxQuantity ?? null,\n unitPriceNet: before.unitPriceNet ?? null,\n unitPriceGross: before.unitPriceGross ?? null,\n taxRate: before.taxRate ?? null,\n taxAmount: before.taxAmount ?? null,\n channelId: before.channelId ?? null,\n userId: before.userId ?? null,\n userGroupId: before.userGroupId ?? null,\n customerId: before.customerId ?? null,\n customerGroupId: before.customerGroupId ?? null,\n metadata: before.metadata ? cloneJson(before.metadata) : null,\n startsAt: before.startsAt ? new Date(before.startsAt) : null,\n endsAt: before.endsAt ? new Date(before.endsAt) : null,\n createdAt: new Date(before.createdAt),\n updatedAt: new Date(before.updatedAt),\n })\n em.persist(record)\n }\n ensureTenantScope(ctx, before.tenantId)\n ensureOrganizationScope(ctx, before.organizationId)\n applyPriceSnapshot(em, record, before)\n await em.flush()\n await capturePriceHistoryEntry(em, before, 'undo', {\n metadata: { undoneCommand: 'catalog.prices.delete' },\n cache: resolveOmnibusCache(ctx.container),\n })\n if (before.custom && Object.keys(before.custom).length) {\n await setCustomFieldsIfAny({\n dataEngine: ctx.container.resolve('dataEngine'),\n entityId: E.catalog.catalog_product_price,\n recordId: before.id,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n values: before.custom,\n })\n }\n },\n}\n\nregisterCommand(createPriceCommand)\nregisterCommand(updatePriceCommand)\nregisterCommand(deletePriceCommand)\n\nasync function resolvePriceRecordAssociations(\n em: EntityManager,\n record: CatalogProductPrice,\n): Promise<{ product: CatalogProduct; variant: CatalogProductVariant | null }> {\n const scope = { tenantId: record.tenantId, organizationId: record.organizationId }\n const variant = record.variant\n ? (typeof record.variant === 'string'\n ? await requireVariant(em, record.variant, scope)\n : record.variant)\n : null\n if (record.product) {\n const product =\n typeof record.product === 'string'\n ? await requireProduct(em, record.product, scope)\n : record.product\n return { product, variant }\n }\n if (variant?.product) {\n const productRef = variant.product\n const product =\n typeof productRef === 'string'\n ? await requireProduct(em, productRef, scope)\n : productRef\n return { product, variant }\n }\n if (record.offer) {\n const offer =\n typeof record.offer === 'string'\n ? await requireOffer(em, record.offer, scope)\n : record.offer\n const productRef = offer?.product ?? null\n if (productRef) {\n const product =\n typeof productRef === 'string'\n ? await requireProduct(em, productRef, scope)\n : productRef\n return { product, variant }\n }\n }\n throw new CrudHttpError(400, { error: 'Catalog price is not linked to a product.' })\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
4
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
5
  import { CustomerUser, CustomerUserRole, CustomerRole } from "@open-mercato/core/modules/customer_accounts/data/entities";
6
6
  import { Organization } from "@open-mercato/core/modules/directory/data/entities";
7
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
7
8
  import { adminCreateUserSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
8
9
  import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
9
10
  import { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
@@ -35,11 +36,23 @@ async function GET(req) {
35
36
  const personEntityId = url.searchParams.get("personEntityId");
36
37
  const roleId = url.searchParams.get("roleId");
37
38
  const search = url.searchParams.get("search");
39
+ const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
38
40
  const where = {
39
41
  tenantId: auth.tenantId,
40
- organizationId: auth.orgId,
41
42
  deletedAt: null
42
43
  };
44
+ if (Array.isArray(organizationScope.filterIds)) {
45
+ if (organizationScope.filterIds.length === 0) {
46
+ return NextResponse.json({
47
+ ok: true,
48
+ items: [],
49
+ total: 0,
50
+ totalPages: 1,
51
+ page
52
+ });
53
+ }
54
+ where.organizationId = { $in: organizationScope.filterIds };
55
+ }
43
56
  if (status === "active") {
44
57
  where.isActive = true;
45
58
  where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: /* @__PURE__ */ new Date() } }];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customer_accounts/api/admin/users.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOwnedCompanyEntity } from '@open-mercato/core/modules/customer_accounts/lib/customerEntityOwnership'\nimport { lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\n\nconst EMAIL_LIKE_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport const metadata = {}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as EntityManager\n\n const url = new URL(req.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1'))\n const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get('pageSize') || '25')))\n const status = url.searchParams.get('status') as 'active' | 'inactive' | 'locked' | null\n const customerEntityId = url.searchParams.get('customerEntityId')\n const personEntityId = url.searchParams.get('personEntityId')\n const roleId = url.searchParams.get('roleId')\n const search = url.searchParams.get('search')\n\n const where: Record<string, unknown> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (status === 'active') {\n where.isActive = true\n where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: new Date() } }]\n } else if (status === 'inactive') {\n where.isActive = false\n } else if (status === 'locked') {\n where.lockedUntil = { $gt: new Date() }\n }\n\n if (customerEntityId) {\n where.customerEntityId = customerEntityId\n }\n\n if (personEntityId) {\n where.personEntityId = personEntityId\n }\n\n if (search) {\n const trimmedSearch = search.trim()\n // email/displayName are stored encrypted, so SQL ILIKE on the ciphertext\n // never matches a plaintext search term. Use search_tokens table for partial\n // matches and emailHash for exact email lookups.\n const searchFilter: Record<string, unknown>[] = []\n\n // Search encrypted fields via search_tokens\n const matchedIds = await findCustomerUserIdsBySearchTokens(em, E.customer_accounts.customer_user, trimmedSearch, auth.tenantId)\n if (matchedIds && matchedIds.length > 0) {\n searchFilter.push({ id: { $in: matchedIds } })\n }\n\n // Also support exact email lookup via emailHash\n if (EMAIL_LIKE_PATTERN.test(search)) {\n searchFilter.push({ emailHash: { $in: lookupHashCandidates(search) } })\n }\n\n if (searchFilter.length > 0) {\n if (where.$or) {\n where.$and = [{ $or: where.$or }, { $or: searchFilter }]\n delete where.$or\n } else {\n where.$or = searchFilter\n }\n } else {\n // No search results found, return empty\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n }\n\n let userIds: string[] | null = null\n if (roleId) {\n // Validate the roleId against the scoped CustomerRole set before touching the\n // junction table. CustomerUserRole carries no tenant/org column of its own,\n // so an unscoped lookup here is a role-UUID existence oracle and is brittle\n // against future code that reads the link rows directly (#2693, defence-in-depth).\n const scopedRole = await findOneWithDecryption(\n em,\n CustomerRole,\n { id: roleId, tenantId: auth.tenantId, organizationId: auth.orgId, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!scopedRole) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n const roleLinks = await findWithDecryption(\n em,\n CustomerUserRole,\n { role: roleId as any, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n userIds = roleLinks.map((link) => (link.user as any)?.id || (link.user as unknown as string))\n if (userIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.id = { $in: userIds }\n }\n\n const offset = (page - 1) * pageSize\n const [users, total] = await findAndCountWithDecryption(\n em,\n CustomerUser,\n where as any,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset,\n },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n\n const pageUserIds = users.map((user) => user.id)\n const userRoleLinks = pageUserIds.length > 0\n ? await findWithDecryption(\n em,\n CustomerUserRole,\n { user: { $in: pageUserIds } as any, deletedAt: null } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n : []\n\n const rolesByUserId = new Map<string, Array<{ id: string; name: string; slug: string }>>()\n for (const link of userRoleLinks) {\n const linkUserId = (link.user as any)?.id ?? (link.user as unknown as string)\n const role = link.role as any\n const bucket = rolesByUserId.get(linkUserId)\n const entry = { id: role.id, name: role.name, slug: role.slug }\n if (bucket) bucket.push(entry)\n else rolesByUserId.set(linkUserId, [entry])\n }\n\n const pageOrganizationIds = Array.from(new Set(\n users.map((user) => user.organizationId).filter((organizationId): organizationId is string => !!organizationId),\n ))\n const organizations = pageOrganizationIds.length > 0\n ? await em.find(Organization, { id: { $in: pageOrganizationIds }, tenant: auth.tenantId, deletedAt: null })\n : []\n const organizationNameById = new Map(organizations.map((organization) => [organization.id, organization.name]))\n\n const items = users.map((user) => {\n const organizationId = user.organizationId ?? null\n return {\n id: user.id,\n email: user.email,\n displayName: user.displayName,\n emailVerified: !!user.emailVerifiedAt,\n isActive: user.isActive,\n lockedUntil: user.lockedUntil || null,\n lastLoginAt: user.lastLoginAt || null,\n customerEntityId: user.customerEntityId || null,\n personEntityId: user.personEntityId || null,\n organizationId,\n organizationName: organizationId ? organizationNameById.get(organizationId) ?? null : null,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt || null,\n roles: rolesByUserId.get(user.id) ?? [],\n }\n })\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({\n ok: true,\n items,\n total,\n totalPages,\n page,\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = adminCreateUserSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as EntityManager\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n\n const existing = await customerUserService.findByEmail(parsed.data.email, auth.tenantId!)\n if (existing) {\n return NextResponse.json({ ok: false, error: 'A user with this email already exists' }, { status: 409 })\n }\n\n // Resolve roles up front, scoped to the caller's tenant AND organization, and\n // reject the request if any requested role is missing from the scoped set.\n // CustomerRole is org-scoped, so omitting organizationId here would let an\n // admin in org A link roles from org B in the same tenant \u2014 a cross-org\n // privilege grant (#2693). Invalid IDs must be rejected, not silently dropped.\n let resolvedRoles: Array<{ id: string }> = []\n if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {\n const requestedRoleIds = parsed.data.roleIds\n const validRoles = await findWithDecryption(\n em,\n CustomerRole,\n {\n id: { $in: requestedRoleIds } as any,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (validRoles.length !== requestedRoleIds.length) {\n const foundIds = new Set(validRoles.map((role) => role.id))\n const missingId = requestedRoleIds.find((roleId) => !foundIds.has(roleId))\n return NextResponse.json({ ok: false, error: `Role ${missingId} not found` }, { status: 400 })\n }\n resolvedRoles = validRoles\n }\n\n // Reject a customerEntityId the caller does not own. Without this check a\n // mislinked company FK persists indefinitely and cross-links the user into\n // another org/company's portal context (#2693).\n if (parsed.data.customerEntityId) {\n const owned = await isOwnedCompanyEntity(em, parsed.data.customerEntityId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n if (!owned) {\n return NextResponse.json({ ok: false, error: 'Company not found' }, { status: 400 })\n }\n }\n\n const user = await customerUserService.createUser(\n parsed.data.email,\n parsed.data.password,\n parsed.data.displayName,\n { tenantId: auth.tenantId!, organizationId: auth.orgId! },\n )\n user.emailVerifiedAt = new Date()\n\n // Persist the user, its company association, and its role links in one\n // transaction so a flush failure on the role loop cannot leave a roleless\n // user committed (privilege gap).\n await em.transactional(async (tx) => {\n tx.persist(user)\n await tx.flush()\n\n if (parsed.data.customerEntityId) {\n await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId })\n }\n\n for (const role of resolvedRoles) {\n const userRole = tx.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n tx.persist(userRole)\n }\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({\n ok: true,\n user: { id: user.id, email: user.email, displayName: user.displayName },\n }, { status: 201 })\n}\n\nconst roleSchema = z.object({ id: z.string().uuid(), name: z.string(), slug: z.string() })\nconst userSchema = z.object({\n id: z.string().uuid(),\n email: z.string(),\n displayName: z.string(),\n emailVerified: z.boolean(),\n isActive: z.boolean(),\n lockedUntil: z.string().datetime().nullable(),\n lastLoginAt: z.string().datetime().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n personEntityId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable().describe('Organization the customer user belongs to'),\n organizationName: z.string().nullable().describe('Resolved organization display name'),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime().nullable(),\n roles: z.array(roleSchema),\n})\n\nconst successSchema = z.object({\n ok: z.literal(true),\n user: z.object({ id: z.string().uuid(), email: z.string(), displayName: z.string() }),\n})\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nasync function findCustomerUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (field) {\n query = query.where('field', '=', field)\n }\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'List customer users (admin)',\n description: 'Returns a paginated list of customer users with roles. Supports filtering by status, company, role, and search.',\n tags: ['Customer Accounts Admin'],\n query: z.object({\n page: z.number().int().positive().optional(),\n pageSize: z.number().int().positive().max(100).optional(),\n status: z.enum(['active', 'inactive', 'locked']).optional(),\n customerEntityId: z.string().uuid().optional(),\n roleId: z.string().uuid().optional(),\n search: z.string().optional(),\n }),\n responses: [{\n status: 200,\n description: 'Paginated user list',\n schema: z.object({ ok: z.literal(true), items: z.array(userSchema), total: z.number(), totalPages: z.number(), page: z.number() }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n ],\n}\n\nconst postMethodDoc: OpenApiMethodDoc = {\n summary: 'Create customer user (admin)',\n description: 'Creates a new customer user directly. Staff-initiated, bypasses signup flow.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: adminCreateUserSchema },\n responses: [{ status: 201, description: 'User created', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 409, description: 'Email already exists', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer user management (admin)',\n methods: {\n GET: getMethodDoc,\n POST: postMethodDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOwnedCompanyEntity } from '@open-mercato/core/modules/customer_accounts/lib/customerEntityOwnership'\nimport { lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\n\nconst EMAIL_LIKE_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport const metadata = {}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as EntityManager\n\n const url = new URL(req.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1'))\n const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get('pageSize') || '25')))\n const status = url.searchParams.get('status') as 'active' | 'inactive' | 'locked' | null\n const customerEntityId = url.searchParams.get('customerEntityId')\n const personEntityId = url.searchParams.get('personEntityId')\n const roleId = url.searchParams.get('roleId')\n const search = url.searchParams.get('search')\n\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const where: Record<string, unknown> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (Array.isArray(organizationScope.filterIds)) {\n if (organizationScope.filterIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.organizationId = { $in: organizationScope.filterIds }\n }\n\n if (status === 'active') {\n where.isActive = true\n where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: new Date() } }]\n } else if (status === 'inactive') {\n where.isActive = false\n } else if (status === 'locked') {\n where.lockedUntil = { $gt: new Date() }\n }\n\n if (customerEntityId) {\n where.customerEntityId = customerEntityId\n }\n\n if (personEntityId) {\n where.personEntityId = personEntityId\n }\n\n if (search) {\n const trimmedSearch = search.trim()\n // email/displayName are stored encrypted, so SQL ILIKE on the ciphertext\n // never matches a plaintext search term. Use search_tokens table for partial\n // matches and emailHash for exact email lookups.\n const searchFilter: Record<string, unknown>[] = []\n\n // Search encrypted fields via search_tokens\n const matchedIds = await findCustomerUserIdsBySearchTokens(em, E.customer_accounts.customer_user, trimmedSearch, auth.tenantId)\n if (matchedIds && matchedIds.length > 0) {\n searchFilter.push({ id: { $in: matchedIds } })\n }\n\n // Also support exact email lookup via emailHash\n if (EMAIL_LIKE_PATTERN.test(search)) {\n searchFilter.push({ emailHash: { $in: lookupHashCandidates(search) } })\n }\n\n if (searchFilter.length > 0) {\n if (where.$or) {\n where.$and = [{ $or: where.$or }, { $or: searchFilter }]\n delete where.$or\n } else {\n where.$or = searchFilter\n }\n } else {\n // No search results found, return empty\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n }\n\n let userIds: string[] | null = null\n if (roleId) {\n // Validate the roleId against the scoped CustomerRole set before touching the\n // junction table. CustomerUserRole carries no tenant/org column of its own,\n // so an unscoped lookup here is a role-UUID existence oracle and is brittle\n // against future code that reads the link rows directly (#2693, defence-in-depth).\n const scopedRole = await findOneWithDecryption(\n em,\n CustomerRole,\n { id: roleId, tenantId: auth.tenantId, organizationId: auth.orgId, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!scopedRole) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n const roleLinks = await findWithDecryption(\n em,\n CustomerUserRole,\n { role: roleId as any, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n userIds = roleLinks.map((link) => (link.user as any)?.id || (link.user as unknown as string))\n if (userIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.id = { $in: userIds }\n }\n\n const offset = (page - 1) * pageSize\n const [users, total] = await findAndCountWithDecryption(\n em,\n CustomerUser,\n where as any,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset,\n },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n\n const pageUserIds = users.map((user) => user.id)\n const userRoleLinks = pageUserIds.length > 0\n ? await findWithDecryption(\n em,\n CustomerUserRole,\n { user: { $in: pageUserIds } as any, deletedAt: null } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n : []\n\n const rolesByUserId = new Map<string, Array<{ id: string; name: string; slug: string }>>()\n for (const link of userRoleLinks) {\n const linkUserId = (link.user as any)?.id ?? (link.user as unknown as string)\n const role = link.role as any\n const bucket = rolesByUserId.get(linkUserId)\n const entry = { id: role.id, name: role.name, slug: role.slug }\n if (bucket) bucket.push(entry)\n else rolesByUserId.set(linkUserId, [entry])\n }\n\n const pageOrganizationIds = Array.from(new Set(\n users.map((user) => user.organizationId).filter((organizationId): organizationId is string => !!organizationId),\n ))\n const organizations = pageOrganizationIds.length > 0\n ? await em.find(Organization, { id: { $in: pageOrganizationIds }, tenant: auth.tenantId, deletedAt: null })\n : []\n const organizationNameById = new Map(organizations.map((organization) => [organization.id, organization.name]))\n\n const items = users.map((user) => {\n const organizationId = user.organizationId ?? null\n return {\n id: user.id,\n email: user.email,\n displayName: user.displayName,\n emailVerified: !!user.emailVerifiedAt,\n isActive: user.isActive,\n lockedUntil: user.lockedUntil || null,\n lastLoginAt: user.lastLoginAt || null,\n customerEntityId: user.customerEntityId || null,\n personEntityId: user.personEntityId || null,\n organizationId,\n organizationName: organizationId ? organizationNameById.get(organizationId) ?? null : null,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt || null,\n roles: rolesByUserId.get(user.id) ?? [],\n }\n })\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({\n ok: true,\n items,\n total,\n totalPages,\n page,\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = adminCreateUserSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as EntityManager\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n\n const existing = await customerUserService.findByEmail(parsed.data.email, auth.tenantId!)\n if (existing) {\n return NextResponse.json({ ok: false, error: 'A user with this email already exists' }, { status: 409 })\n }\n\n // Resolve roles up front, scoped to the caller's tenant AND organization, and\n // reject the request if any requested role is missing from the scoped set.\n // CustomerRole is org-scoped, so omitting organizationId here would let an\n // admin in org A link roles from org B in the same tenant \u2014 a cross-org\n // privilege grant (#2693). Invalid IDs must be rejected, not silently dropped.\n let resolvedRoles: Array<{ id: string }> = []\n if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {\n const requestedRoleIds = parsed.data.roleIds\n const validRoles = await findWithDecryption(\n em,\n CustomerRole,\n {\n id: { $in: requestedRoleIds } as any,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (validRoles.length !== requestedRoleIds.length) {\n const foundIds = new Set(validRoles.map((role) => role.id))\n const missingId = requestedRoleIds.find((roleId) => !foundIds.has(roleId))\n return NextResponse.json({ ok: false, error: `Role ${missingId} not found` }, { status: 400 })\n }\n resolvedRoles = validRoles\n }\n\n // Reject a customerEntityId the caller does not own. Without this check a\n // mislinked company FK persists indefinitely and cross-links the user into\n // another org/company's portal context (#2693).\n if (parsed.data.customerEntityId) {\n const owned = await isOwnedCompanyEntity(em, parsed.data.customerEntityId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n if (!owned) {\n return NextResponse.json({ ok: false, error: 'Company not found' }, { status: 400 })\n }\n }\n\n const user = await customerUserService.createUser(\n parsed.data.email,\n parsed.data.password,\n parsed.data.displayName,\n { tenantId: auth.tenantId!, organizationId: auth.orgId! },\n )\n user.emailVerifiedAt = new Date()\n\n // Persist the user, its company association, and its role links in one\n // transaction so a flush failure on the role loop cannot leave a roleless\n // user committed (privilege gap).\n await em.transactional(async (tx) => {\n tx.persist(user)\n await tx.flush()\n\n if (parsed.data.customerEntityId) {\n await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId })\n }\n\n for (const role of resolvedRoles) {\n const userRole = tx.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n tx.persist(userRole)\n }\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({\n ok: true,\n user: { id: user.id, email: user.email, displayName: user.displayName },\n }, { status: 201 })\n}\n\nconst roleSchema = z.object({ id: z.string().uuid(), name: z.string(), slug: z.string() })\nconst userSchema = z.object({\n id: z.string().uuid(),\n email: z.string(),\n displayName: z.string(),\n emailVerified: z.boolean(),\n isActive: z.boolean(),\n lockedUntil: z.string().datetime().nullable(),\n lastLoginAt: z.string().datetime().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n personEntityId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable().describe('Organization the customer user belongs to'),\n organizationName: z.string().nullable().describe('Resolved organization display name'),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime().nullable(),\n roles: z.array(roleSchema),\n})\n\nconst successSchema = z.object({\n ok: z.literal(true),\n user: z.object({ id: z.string().uuid(), email: z.string(), displayName: z.string() }),\n})\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nasync function findCustomerUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (field) {\n query = query.where('field', '=', field)\n }\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'List customer users (admin)',\n description: 'Returns a paginated list of customer users with roles. Supports filtering by status, company, role, and search.',\n tags: ['Customer Accounts Admin'],\n query: z.object({\n page: z.number().int().positive().optional(),\n pageSize: z.number().int().positive().max(100).optional(),\n status: z.enum(['active', 'inactive', 'locked']).optional(),\n customerEntityId: z.string().uuid().optional(),\n roleId: z.string().uuid().optional(),\n search: z.string().optional(),\n }),\n responses: [{\n status: 200,\n description: 'Paginated user list',\n schema: z.object({ ok: z.literal(true), items: z.array(userSchema), total: z.number(), totalPages: z.number(), page: z.number() }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n ],\n}\n\nconst postMethodDoc: OpenApiMethodDoc = {\n summary: 'Create customer user (admin)',\n description: 'Creates a new customer user directly. Staff-initiated, bypasses signup flow.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: adminCreateUserSchema },\n responses: [{ status: 201, description: 'User created', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 409, description: 'Email already exists', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer user management (admin)',\n methods: {\n GET: getMethodDoc,\n POST: postMethodDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7781.1.079f42c863",
3
+ "version": "0.10.1-develop.7783.1.e04ffd226c",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -266,16 +266,16 @@
266
266
  "zod": "^4.4.3"
267
267
  },
268
268
  "peerDependencies": {
269
- "@open-mercato/ai-assistant": "0.10.1-develop.7781.1.079f42c863",
270
- "@open-mercato/shared": "0.10.1-develop.7781.1.079f42c863",
271
- "@open-mercato/ui": "0.10.1-develop.7781.1.079f42c863",
269
+ "@open-mercato/ai-assistant": "0.10.1-develop.7783.1.e04ffd226c",
270
+ "@open-mercato/shared": "0.10.1-develop.7783.1.e04ffd226c",
271
+ "@open-mercato/ui": "0.10.1-develop.7783.1.e04ffd226c",
272
272
  "react": "^19.0.0",
273
273
  "react-dom": "^19.0.0"
274
274
  },
275
275
  "devDependencies": {
276
- "@open-mercato/ai-assistant": "0.10.1-develop.7781.1.079f42c863",
277
- "@open-mercato/shared": "0.10.1-develop.7781.1.079f42c863",
278
- "@open-mercato/ui": "0.10.1-develop.7781.1.079f42c863",
276
+ "@open-mercato/ai-assistant": "0.10.1-develop.7783.1.e04ffd226c",
277
+ "@open-mercato/shared": "0.10.1-develop.7783.1.e04ffd226c",
278
+ "@open-mercato/ui": "0.10.1-develop.7783.1.e04ffd226c",
279
279
  "@testing-library/dom": "^10.4.1",
280
280
  "@testing-library/jest-dom": "^7.0.1",
281
281
  "@testing-library/react": "^16.3.3",
@@ -287,6 +287,24 @@ function resolveAmountInputFromParsed(
287
287
  return null
288
288
  }
289
289
 
290
+ function hasAmountChanged(incoming: unknown, current: string | null | undefined): incoming is number {
291
+ if (typeof incoming !== 'number' || !Number.isFinite(incoming)) return false
292
+ return numericStringToNumber(current) !== incoming
293
+ }
294
+
295
+ function resolveAmountInputForUpdate(
296
+ parsed: Partial<Pick<PriceCreateInput, 'unitPriceNet' | 'unitPriceGross'>>,
297
+ record: CatalogProductPrice
298
+ ): PriceAmountInput | null {
299
+ if (hasAmountChanged(parsed.unitPriceNet, record.unitPriceNet)) {
300
+ return { amount: parsed.unitPriceNet, mode: 'net' }
301
+ }
302
+ if (hasAmountChanged(parsed.unitPriceGross, record.unitPriceGross)) {
303
+ return { amount: parsed.unitPriceGross, mode: 'gross' }
304
+ }
305
+ return resolveAmountInputFromParsed(parsed)
306
+ }
307
+
290
308
  function resolveAmountInputFromRecord(record: CatalogProductPrice): PriceAmountInput | null {
291
309
  const net = numericStringToNumber(record.unitPriceNet)
292
310
  if (net !== null) return { amount: net, mode: 'net' }
@@ -644,7 +662,7 @@ const updatePriceCommand: CommandHandler<PriceUpdateInput, { priceId: string }>
644
662
  ensureSameTenant(targetPriceKind, targetProduct.tenantId)
645
663
 
646
664
  const taxCalculationService = ctx.container.resolve<TaxCalculationService>('taxCalculationService')
647
- const amountInput = resolveAmountInputFromParsed(parsed)
665
+ const amountInput = resolveAmountInputForUpdate(parsed, record)
648
666
  const hasNetInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceNet')
649
667
  const hasGrossInput = Object.prototype.hasOwnProperty.call(parsed, 'unitPriceGross')
650
668
  const hasTaxRateInput = Object.prototype.hasOwnProperty.call(parsed, 'taxRate')
@@ -7,6 +7,7 @@ import { RbacService } from '@open-mercato/core/modules/auth/services/rbacServic
7
7
  import { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'
8
8
  import { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'
9
9
  import { Organization } from '@open-mercato/core/modules/directory/data/entities'
10
+ import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
10
11
  import type { EntityManager } from '@mikro-orm/postgresql'
11
12
  import { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'
12
13
  import { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'
@@ -46,11 +47,23 @@ export async function GET(req: Request) {
46
47
  const roleId = url.searchParams.get('roleId')
47
48
  const search = url.searchParams.get('search')
48
49
 
50
+ const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
49
51
  const where: Record<string, unknown> = {
50
52
  tenantId: auth.tenantId,
51
- organizationId: auth.orgId,
52
53
  deletedAt: null,
53
54
  }
55
+ if (Array.isArray(organizationScope.filterIds)) {
56
+ if (organizationScope.filterIds.length === 0) {
57
+ return NextResponse.json({
58
+ ok: true,
59
+ items: [],
60
+ total: 0,
61
+ totalPages: 1,
62
+ page,
63
+ })
64
+ }
65
+ where.organizationId = { $in: organizationScope.filterIds }
66
+ }
54
67
 
55
68
  if (status === 'active') {
56
69
  where.isActive = true