@open-mercato/core 0.7.1-develop.7170.1.d95074d7ba → 0.7.1-develop.7172.1.a45dece080

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/wms/commands/sales-order-assignment.js +12 -0
  3. package/dist/modules/wms/commands/sales-order-assignment.js.map +2 -2
  4. package/dist/modules/wms/data/enrichers.js +25 -0
  5. package/dist/modules/wms/data/enrichers.js.map +2 -2
  6. package/dist/modules/wms/lib/enricherCacheTags.js +9 -0
  7. package/dist/modules/wms/lib/enricherCacheTags.js.map +7 -0
  8. package/dist/modules/wms/lib/invalidateInventoryEnricherCache.js +37 -0
  9. package/dist/modules/wms/lib/invalidateInventoryEnricherCache.js.map +7 -0
  10. package/dist/modules/wms/lib/inventoryEnricherCacheSubscriber.js +14 -0
  11. package/dist/modules/wms/lib/inventoryEnricherCacheSubscriber.js.map +7 -0
  12. package/dist/modules/wms/subscribers/invalidate-enricher-cache-balance.js +12 -0
  13. package/dist/modules/wms/subscribers/invalidate-enricher-cache-balance.js.map +7 -0
  14. package/dist/modules/wms/subscribers/invalidate-enricher-cache-inventory.js +12 -0
  15. package/dist/modules/wms/subscribers/invalidate-enricher-cache-inventory.js.map +7 -0
  16. package/dist/modules/wms/subscribers/invalidate-enricher-cache-movement.js +12 -0
  17. package/dist/modules/wms/subscribers/invalidate-enricher-cache-movement.js.map +7 -0
  18. package/dist/modules/wms/subscribers/invalidate-enricher-cache-profile.js +12 -0
  19. package/dist/modules/wms/subscribers/invalidate-enricher-cache-profile.js.map +7 -0
  20. package/dist/modules/wms/subscribers/invalidate-enricher-cache-reservation.js +12 -0
  21. package/dist/modules/wms/subscribers/invalidate-enricher-cache-reservation.js.map +7 -0
  22. package/dist/modules/wms/subscribers/invalidate-enricher-cache-sales-line.js +12 -0
  23. package/dist/modules/wms/subscribers/invalidate-enricher-cache-sales-line.js.map +7 -0
  24. package/dist/modules/wms/subscribers/invalidate-enricher-cache-sales-order.js +12 -0
  25. package/dist/modules/wms/subscribers/invalidate-enricher-cache-sales-order.js.map +7 -0
  26. package/dist/modules/wms/subscribers/invalidate-enricher-cache-variant.js +12 -0
  27. package/dist/modules/wms/subscribers/invalidate-enricher-cache-variant.js.map +7 -0
  28. package/dist/modules/wms/subscribers/invalidate-enricher-cache-warehouse.js +12 -0
  29. package/dist/modules/wms/subscribers/invalidate-enricher-cache-warehouse.js.map +7 -0
  30. package/package.json +18 -18
  31. package/src/modules/wms/commands/sales-order-assignment.ts +20 -0
  32. package/src/modules/wms/data/enrichers.ts +25 -0
  33. package/src/modules/wms/lib/enricherCacheTags.ts +29 -0
  34. package/src/modules/wms/lib/invalidateInventoryEnricherCache.ts +71 -0
  35. package/src/modules/wms/lib/inventoryEnricherCacheSubscriber.ts +30 -0
  36. package/src/modules/wms/subscribers/invalidate-enricher-cache-balance.ts +9 -0
  37. package/src/modules/wms/subscribers/invalidate-enricher-cache-inventory.ts +9 -0
  38. package/src/modules/wms/subscribers/invalidate-enricher-cache-movement.ts +9 -0
  39. package/src/modules/wms/subscribers/invalidate-enricher-cache-profile.ts +9 -0
  40. package/src/modules/wms/subscribers/invalidate-enricher-cache-reservation.ts +9 -0
  41. package/src/modules/wms/subscribers/invalidate-enricher-cache-sales-line.ts +14 -0
  42. package/src/modules/wms/subscribers/invalidate-enricher-cache-sales-order.ts +18 -0
  43. package/src/modules/wms/subscribers/invalidate-enricher-cache-variant.ts +9 -0
  44. package/src/modules/wms/subscribers/invalidate-enricher-cache-warehouse.ts +9 -0
@@ -1,4 +1,4 @@
1
- [build:core] found 4396 entry points
1
+ [build:core] found 4409 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 218 entry points
4
4
  [build:core:generated] built successfully
@@ -6,7 +6,11 @@ import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
6
6
  import { SalesOrderWarehouseAssignment, Warehouse } from "../data/entities.js";
7
7
  import { z } from "zod";
8
8
  import { reserveInventoryForConfirmedOrder } from "../lib/salesOrderInventoryAutomation.js";
9
+ import { invalidateWmsInventoryEnricherCache } from "../lib/invalidateInventoryEnricherCache.js";
9
10
  import { ensureOrganizationScope, ensureTenantScope } from "./shared.js";
11
+ async function invalidateAssignmentEnricherCache(ctx, tenantId) {
12
+ await invalidateWmsInventoryEnricherCache(ctx.container, tenantId, "warehouse");
13
+ }
10
14
  function resolveScope(ctx, fallback) {
11
15
  return {
12
16
  tenantId: fallback?.tenantId ?? ctx.auth?.tenantId ?? null,
@@ -81,6 +85,7 @@ const assignWarehouseHandler = {
81
85
  existing.assignedBy = ctx.auth?.sub ?? null;
82
86
  existing.updatedAt = /* @__PURE__ */ new Date();
83
87
  await em.flush();
88
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
84
89
  return { assignmentId: existing.id, warehouseId: warehouse.id };
85
90
  }
86
91
  const assignment = em.create(SalesOrderWarehouseAssignment, {
@@ -93,6 +98,7 @@ const assignWarehouseHandler = {
93
98
  });
94
99
  em.persist(assignment);
95
100
  await em.flush();
101
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
96
102
  return { assignmentId: assignment.id, warehouseId: warehouse.id };
97
103
  },
98
104
  captureAfter: async (input, _result, ctx) => {
@@ -136,6 +142,7 @@ const assignWarehouseHandler = {
136
142
  if (!before) {
137
143
  assignment.deletedAt = /* @__PURE__ */ new Date();
138
144
  await em.flush();
145
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
139
146
  return;
140
147
  }
141
148
  const previousWarehouse = await findOneWithDecryption(
@@ -153,11 +160,13 @@ const assignWarehouseHandler = {
153
160
  if (!previousWarehouse) {
154
161
  assignment.deletedAt = /* @__PURE__ */ new Date();
155
162
  await em.flush();
163
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
156
164
  return;
157
165
  }
158
166
  assignment.warehouse = previousWarehouse;
159
167
  assignment.notes = before.notes ?? null;
160
168
  await em.flush();
169
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
161
170
  }
162
171
  }
163
172
  };
@@ -179,6 +188,7 @@ const unassignWarehouseHandler = {
179
188
  if (existing) {
180
189
  existing.deletedAt = /* @__PURE__ */ new Date();
181
190
  await em.flush();
191
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
182
192
  }
183
193
  return { ok: true };
184
194
  },
@@ -212,6 +222,7 @@ const unassignWarehouseHandler = {
212
222
  if (existingDeleted) {
213
223
  existingDeleted.deletedAt = null;
214
224
  await em.flush();
225
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
215
226
  return;
216
227
  }
217
228
  const warehouse = await findOneWithDecryption(
@@ -236,6 +247,7 @@ const unassignWarehouseHandler = {
236
247
  });
237
248
  em.persist(restored);
238
249
  await em.flush();
250
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId);
239
251
  }
240
252
  };
241
253
  registerCommand(assignWarehouseHandler);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/commands/sales-order-assignment.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\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 { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { SalesOrderWarehouseAssignment, Warehouse } from '../data/entities'\nimport type { SalesOrderWarehouseAssignInput, SalesOrderWarehouseUnassignInput } from '../data/validators'\nimport { z } from 'zod'\nimport { reserveInventoryForConfirmedOrder } from '../lib/salesOrderInventoryAutomation'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\n\ntype AssignmentSnapshot = {\n id: string\n salesOrderId: string\n warehouseId: string\n notes: string | null\n organizationId: string\n tenantId: string\n}\n\ntype AssignWarehouseUndoPayload = {\n before: AssignmentSnapshot | null\n after: AssignmentSnapshot | null\n}\n\ntype UnassignWarehouseUndoPayload = {\n before: AssignmentSnapshot | null\n}\n\nfunction resolveScope(\n ctx: CommandRuntimeContext,\n fallback?: { tenantId?: string | null; organizationId?: string | null },\n) {\n return {\n tenantId: fallback?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: fallback?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n}\n\nfunction resolveEm(ctx: CommandRuntimeContext): EntityManager {\n return (ctx.container.resolve('em') as EntityManager).fork()\n}\n\nasync function loadAssignment(\n em: EntityManager,\n salesOrderId: string,\n scope: ReturnType<typeof resolveScope>,\n): Promise<SalesOrderWarehouseAssignment | null> {\n if (!scope.organizationId || !scope.tenantId) return null\n return findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n {\n salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope as { organizationId: string; tenantId: string },\n )\n}\n\nfunction snapshotAssignment(\n assignment: SalesOrderWarehouseAssignment | null,\n): AssignmentSnapshot | null {\n if (!assignment) return null\n const warehouseRel = assignment.warehouse as { id?: string } | undefined\n const warehouseId = warehouseRel?.id ?? ''\n return {\n id: assignment.id,\n salesOrderId: assignment.salesOrderId,\n warehouseId,\n notes: assignment.notes ?? null,\n organizationId: assignment.organizationId,\n tenantId: assignment.tenantId,\n }\n}\n\nconst assignWarehouseHandler: CommandHandler<\n SalesOrderWarehouseAssignInput,\n { assignmentId: string; warehouseId: string }\n> = {\n id: 'wms.sales-order.assign-warehouse',\n\n prepare: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n return { before: snapshotAssignment(existing) }\n },\n\n execute: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n if (!scope.tenantId || !scope.organizationId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const em = resolveEm(ctx)\n\n const warehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: input.warehouseId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope as { organizationId: string; tenantId: string },\n )\n if (!warehouse) throw new CrudHttpError(404, { error: 'Warehouse not found.' })\n ensureTenantScope(ctx, warehouse.tenantId)\n ensureOrganizationScope(ctx, warehouse.organizationId)\n if (!warehouse.isActive) throw new CrudHttpError(422, { error: 'Warehouse is inactive.' })\n\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n\n if (existing) {\n existing.warehouse = warehouse\n existing.notes = input.notes ?? null\n existing.assignedBy = ctx.auth?.sub ?? null\n existing.updatedAt = new Date()\n await em.flush()\n return { assignmentId: existing.id, warehouseId: warehouse.id }\n }\n\n const assignment = em.create(SalesOrderWarehouseAssignment, {\n salesOrderId: input.salesOrderId,\n warehouse,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n assignedBy: ctx.auth?.sub ?? null,\n notes: input.notes ?? null,\n })\n em.persist(assignment)\n await em.flush()\n return { assignmentId: assignment.id, warehouseId: warehouse.id }\n },\n\n captureAfter: async (input, _result, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const updated = await loadAssignment(em, input.salesOrderId, scope)\n return { after: snapshotAssignment(updated) }\n },\n\n buildLog: async ({ input, result, ctx, snapshots }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('wms.audit.salesOrder.assignWarehouse', 'Assign warehouse to order'),\n resourceKind: 'wms.sales_order_warehouse_assignment',\n resourceId: result?.assignmentId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n payload: {\n undo: {\n before: (snapshots as { before?: AssignmentSnapshot | null })?.before ?? null,\n after: (snapshots as { after?: AssignmentSnapshot | null })?.after ?? null,\n } satisfies AssignWarehouseUndoPayload,\n },\n }\n },\n\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignWarehouseUndoPayload>(logEntry)\n if (!payload) return\n const em = resolveEm(ctx)\n\n const after = payload.after\n const before = payload.before\n\n if (after) {\n const scope = { tenantId: after.tenantId, organizationId: after.organizationId }\n const assignment = await findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n { id: after.id, organizationId: after.organizationId, tenantId: after.tenantId },\n undefined,\n scope,\n )\n if (!assignment) return\n\n if (!before) {\n assignment.deletedAt = new Date()\n await em.flush()\n return\n }\n\n const previousWarehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: before.warehouseId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!previousWarehouse) {\n assignment.deletedAt = new Date()\n await em.flush()\n return\n }\n\n assignment.warehouse = previousWarehouse\n assignment.notes = before.notes ?? null\n await em.flush()\n }\n },\n}\n\nconst unassignWarehouseHandler: CommandHandler<\n SalesOrderWarehouseUnassignInput,\n { ok: true }\n> = {\n id: 'wms.sales-order.unassign-warehouse',\n\n prepare: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n return { before: snapshotAssignment(existing) }\n },\n\n execute: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n if (!scope.tenantId || !scope.organizationId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n\n if (existing) {\n existing.deletedAt = new Date()\n await em.flush()\n }\n\n return { ok: true as const }\n },\n\n buildLog: async ({ input, ctx, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = (snapshots as { before?: AssignmentSnapshot | null })?.before ?? null\n return {\n actionLabel: translate('wms.audit.salesOrder.unassignWarehouse', 'Remove warehouse assignment from order'),\n resourceKind: 'wms.sales_order_warehouse_assignment',\n resourceId: before?.id ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n payload: {\n undo: { before } satisfies UnassignWarehouseUndoPayload,\n },\n }\n },\n\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<UnassignWarehouseUndoPayload>(logEntry)\n const before = payload?.before\n if (!before?.salesOrderId || !before.warehouseId) return\n\n const scope = { tenantId: before.tenantId, organizationId: before.organizationId }\n const em = resolveEm(ctx)\n\n const existingDeleted = await findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n undefined,\n scope,\n )\n\n if (existingDeleted) {\n existingDeleted.deletedAt = null\n await em.flush()\n return\n }\n\n const warehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: before.warehouseId,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!warehouse) return\n\n const restored = em.create(SalesOrderWarehouseAssignment, {\n salesOrderId: before.salesOrderId,\n warehouse,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n notes: before.notes ?? null,\n })\n em.persist(restored)\n await em.flush()\n },\n}\n\nregisterCommand(assignWarehouseHandler)\nregisterCommand(unassignWarehouseHandler)\n\nexport const reRunReservationInputSchema = z.object({\n salesOrderId: z.string().uuid(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n})\nexport type ReRunReservationInput = z.infer<typeof reRunReservationInputSchema>\n\nconst reRunReservationHandler: CommandHandler<ReRunReservationInput, { ok: boolean }> = {\n id: 'wms.sales-order.re-run-reservation',\n isUndoable: false,\n async execute(rawInput, ctx) {\n const input = reRunReservationInputSchema.parse(rawInput)\n const eventCtx = {\n resolve: <T>(name: string) => ctx.container.resolve<T>(name),\n }\n await reserveInventoryForConfirmedOrder(\n { orderId: input.salesOrderId, tenantId: input.tenantId, organizationId: input.organizationId },\n eventCtx,\n )\n return { ok: true }\n },\n async buildLog({ input, ctx }) {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('wms.audit.salesOrder.reRunReservation', 'Re-run reservation for order'),\n resourceKind: 'wms.sales_order_reservation',\n resourceId: input?.salesOrderId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n },\n}\n\nregisterCommand(reRunReservationHandler)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\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 { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { SalesOrderWarehouseAssignment, Warehouse } from '../data/entities'\nimport type { SalesOrderWarehouseAssignInput, SalesOrderWarehouseUnassignInput } from '../data/validators'\nimport { z } from 'zod'\nimport { reserveInventoryForConfirmedOrder } from '../lib/salesOrderInventoryAutomation'\nimport { invalidateWmsInventoryEnricherCache } from '../lib/invalidateInventoryEnricherCache'\nimport { ensureOrganizationScope, ensureTenantScope } from './shared'\n\ntype AssignmentSnapshot = {\n id: string\n salesOrderId: string\n warehouseId: string\n notes: string | null\n organizationId: string\n tenantId: string\n}\n\ntype AssignWarehouseUndoPayload = {\n before: AssignmentSnapshot | null\n after: AssignmentSnapshot | null\n}\n\ntype UnassignWarehouseUndoPayload = {\n before: AssignmentSnapshot | null\n}\n\n// These commands emit no WMS event, so the enricher-cache subscribers never see\n// them. `wms.sales-order-inventory` surfaces the assigned warehouse, so every\n// path here that writes an assignment \u2014 including the undo handlers \u2014 drops the\n// warehouse tag itself.\nasync function invalidateAssignmentEnricherCache(\n ctx: CommandRuntimeContext,\n tenantId: string | null | undefined,\n): Promise<void> {\n await invalidateWmsInventoryEnricherCache(ctx.container, tenantId, 'warehouse')\n}\n\nfunction resolveScope(\n ctx: CommandRuntimeContext,\n fallback?: { tenantId?: string | null; organizationId?: string | null },\n) {\n return {\n tenantId: fallback?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: fallback?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n}\n\nfunction resolveEm(ctx: CommandRuntimeContext): EntityManager {\n return (ctx.container.resolve('em') as EntityManager).fork()\n}\n\nasync function loadAssignment(\n em: EntityManager,\n salesOrderId: string,\n scope: ReturnType<typeof resolveScope>,\n): Promise<SalesOrderWarehouseAssignment | null> {\n if (!scope.organizationId || !scope.tenantId) return null\n return findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n {\n salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope as { organizationId: string; tenantId: string },\n )\n}\n\nfunction snapshotAssignment(\n assignment: SalesOrderWarehouseAssignment | null,\n): AssignmentSnapshot | null {\n if (!assignment) return null\n const warehouseRel = assignment.warehouse as { id?: string } | undefined\n const warehouseId = warehouseRel?.id ?? ''\n return {\n id: assignment.id,\n salesOrderId: assignment.salesOrderId,\n warehouseId,\n notes: assignment.notes ?? null,\n organizationId: assignment.organizationId,\n tenantId: assignment.tenantId,\n }\n}\n\nconst assignWarehouseHandler: CommandHandler<\n SalesOrderWarehouseAssignInput,\n { assignmentId: string; warehouseId: string }\n> = {\n id: 'wms.sales-order.assign-warehouse',\n\n prepare: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n return { before: snapshotAssignment(existing) }\n },\n\n execute: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n if (!scope.tenantId || !scope.organizationId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const em = resolveEm(ctx)\n\n const warehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: input.warehouseId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope as { organizationId: string; tenantId: string },\n )\n if (!warehouse) throw new CrudHttpError(404, { error: 'Warehouse not found.' })\n ensureTenantScope(ctx, warehouse.tenantId)\n ensureOrganizationScope(ctx, warehouse.organizationId)\n if (!warehouse.isActive) throw new CrudHttpError(422, { error: 'Warehouse is inactive.' })\n\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n\n if (existing) {\n existing.warehouse = warehouse\n existing.notes = input.notes ?? null\n existing.assignedBy = ctx.auth?.sub ?? null\n existing.updatedAt = new Date()\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n return { assignmentId: existing.id, warehouseId: warehouse.id }\n }\n\n const assignment = em.create(SalesOrderWarehouseAssignment, {\n salesOrderId: input.salesOrderId,\n warehouse,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n assignedBy: ctx.auth?.sub ?? null,\n notes: input.notes ?? null,\n })\n em.persist(assignment)\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n return { assignmentId: assignment.id, warehouseId: warehouse.id }\n },\n\n captureAfter: async (input, _result, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const updated = await loadAssignment(em, input.salesOrderId, scope)\n return { after: snapshotAssignment(updated) }\n },\n\n buildLog: async ({ input, result, ctx, snapshots }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('wms.audit.salesOrder.assignWarehouse', 'Assign warehouse to order'),\n resourceKind: 'wms.sales_order_warehouse_assignment',\n resourceId: result?.assignmentId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n payload: {\n undo: {\n before: (snapshots as { before?: AssignmentSnapshot | null })?.before ?? null,\n after: (snapshots as { after?: AssignmentSnapshot | null })?.after ?? null,\n } satisfies AssignWarehouseUndoPayload,\n },\n }\n },\n\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignWarehouseUndoPayload>(logEntry)\n if (!payload) return\n const em = resolveEm(ctx)\n\n const after = payload.after\n const before = payload.before\n\n if (after) {\n const scope = { tenantId: after.tenantId, organizationId: after.organizationId }\n const assignment = await findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n { id: after.id, organizationId: after.organizationId, tenantId: after.tenantId },\n undefined,\n scope,\n )\n if (!assignment) return\n\n if (!before) {\n assignment.deletedAt = new Date()\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n return\n }\n\n const previousWarehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: before.warehouseId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!previousWarehouse) {\n assignment.deletedAt = new Date()\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n return\n }\n\n assignment.warehouse = previousWarehouse\n assignment.notes = before.notes ?? null\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n }\n },\n}\n\nconst unassignWarehouseHandler: CommandHandler<\n SalesOrderWarehouseUnassignInput,\n { ok: true }\n> = {\n id: 'wms.sales-order.unassign-warehouse',\n\n prepare: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n return { before: snapshotAssignment(existing) }\n },\n\n execute: async (input, ctx) => {\n const scope = resolveScope(ctx, input)\n if (!scope.tenantId || !scope.organizationId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n const em = resolveEm(ctx)\n const existing = await loadAssignment(em, input.salesOrderId, scope)\n\n if (existing) {\n existing.deletedAt = new Date()\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n }\n\n return { ok: true as const }\n },\n\n buildLog: async ({ input, ctx, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = (snapshots as { before?: AssignmentSnapshot | null })?.before ?? null\n return {\n actionLabel: translate('wms.audit.salesOrder.unassignWarehouse', 'Remove warehouse assignment from order'),\n resourceKind: 'wms.sales_order_warehouse_assignment',\n resourceId: before?.id ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n payload: {\n undo: { before } satisfies UnassignWarehouseUndoPayload,\n },\n }\n },\n\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<UnassignWarehouseUndoPayload>(logEntry)\n const before = payload?.before\n if (!before?.salesOrderId || !before.warehouseId) return\n\n const scope = { tenantId: before.tenantId, organizationId: before.organizationId }\n const em = resolveEm(ctx)\n\n const existingDeleted = await findOneWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n undefined,\n scope,\n )\n\n if (existingDeleted) {\n existingDeleted.deletedAt = null\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n return\n }\n\n const warehouse = await findOneWithDecryption(\n em,\n Warehouse,\n {\n id: before.warehouseId,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!warehouse) return\n\n const restored = em.create(SalesOrderWarehouseAssignment, {\n salesOrderId: before.salesOrderId,\n warehouse,\n organizationId: before.organizationId,\n tenantId: before.tenantId,\n notes: before.notes ?? null,\n })\n em.persist(restored)\n await em.flush()\n await invalidateAssignmentEnricherCache(ctx, scope.tenantId)\n },\n}\n\nregisterCommand(assignWarehouseHandler)\nregisterCommand(unassignWarehouseHandler)\n\nexport const reRunReservationInputSchema = z.object({\n salesOrderId: z.string().uuid(),\n organizationId: z.string().uuid(),\n tenantId: z.string().uuid(),\n})\nexport type ReRunReservationInput = z.infer<typeof reRunReservationInputSchema>\n\nconst reRunReservationHandler: CommandHandler<ReRunReservationInput, { ok: boolean }> = {\n id: 'wms.sales-order.re-run-reservation',\n isUndoable: false,\n async execute(rawInput, ctx) {\n const input = reRunReservationInputSchema.parse(rawInput)\n const eventCtx = {\n resolve: <T>(name: string) => ctx.container.resolve<T>(name),\n }\n await reserveInventoryForConfirmedOrder(\n { orderId: input.salesOrderId, tenantId: input.tenantId, organizationId: input.organizationId },\n eventCtx,\n )\n return { ok: true }\n },\n async buildLog({ input, ctx }) {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('wms.audit.salesOrder.reRunReservation', 'Re-run reservation for order'),\n resourceKind: 'wms.sales_order_reservation',\n resourceId: input?.salesOrderId ?? null,\n tenantId: input?.tenantId ?? ctx.auth?.tenantId ?? null,\n organizationId: input?.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n },\n}\n\nregisterCommand(reRunReservationHandler)\n"],
5
+ "mappings": "AACA,SAAS,uBAAuB;AAGhC,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,6BAA6B;AACtC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B,iBAAiB;AAEzD,SAAS,SAAS;AAClB,SAAS,yCAAyC;AAClD,SAAS,2CAA2C;AACpD,SAAS,yBAAyB,yBAAyB;AAwB3D,eAAe,kCACb,KACA,UACe;AACf,QAAM,oCAAoC,IAAI,WAAW,UAAU,WAAW;AAChF;AAEA,SAAS,aACP,KACA,UACA;AACA,SAAO;AAAA,IACL,UAAU,UAAU,YAAY,IAAI,MAAM,YAAY;AAAA,IACtD,gBAAgB,UAAU,kBAAkB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,EAC/F;AACF;AAEA,SAAS,UAAU,KAA2C;AAC5D,SAAQ,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC7D;AAEA,eAAe,eACb,IACA,cACA,OAC+C;AAC/C,MAAI,CAAC,MAAM,kBAAkB,CAAC,MAAM,SAAU,QAAO;AACrD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAAA,MACA,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,mBACP,YAC2B;AAC3B,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,eAAe,WAAW;AAChC,QAAM,cAAc,cAAc,MAAM;AACxC,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,cAAc,WAAW;AAAA,IACzB;AAAA,IACA,OAAO,WAAW,SAAS;AAAA,IAC3B,gBAAgB,WAAW;AAAA,IAC3B,UAAU,WAAW;AAAA,EACvB;AACF;AAEA,MAAM,yBAGF;AAAA,EACF,IAAI;AAAA,EAEJ,SAAS,OAAO,OAAO,QAAQ;AAC7B,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,WAAW,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK;AACnE,WAAO,EAAE,QAAQ,mBAAmB,QAAQ,EAAE;AAAA,EAChD;AAAA,EAEA,SAAS,OAAO,OAAO,QAAQ;AAC7B,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,QAAI,CAAC,MAAM,YAAY,CAAC,MAAM,gBAAgB;AAC5C,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,IACxD;AACA,UAAM,KAAK,UAAU,GAAG;AAExB,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI,MAAM;AAAA,QACV,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,QAChB,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,UAAW,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,uBAAuB,CAAC;AAC9E,sBAAkB,KAAK,UAAU,QAAQ;AACzC,4BAAwB,KAAK,UAAU,cAAc;AACrD,QAAI,CAAC,UAAU,SAAU,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,yBAAyB,CAAC;AAEzF,UAAM,WAAW,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK;AAEnE,QAAI,UAAU;AACZ,eAAS,YAAY;AACrB,eAAS,QAAQ,MAAM,SAAS;AAChC,eAAS,aAAa,IAAI,MAAM,OAAO;AACvC,eAAS,YAAY,oBAAI,KAAK;AAC9B,YAAM,GAAG,MAAM;AACf,YAAM,kCAAkC,KAAK,MAAM,QAAQ;AAC3D,aAAO,EAAE,cAAc,SAAS,IAAI,aAAa,UAAU,GAAG;AAAA,IAChE;AAEA,UAAM,aAAa,GAAG,OAAO,+BAA+B;AAAA,MAC1D,cAAc,MAAM;AAAA,MACpB;AAAA,MACA,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,MAChB,YAAY,IAAI,MAAM,OAAO;AAAA,MAC7B,OAAO,MAAM,SAAS;AAAA,IACxB,CAAC;AACD,OAAG,QAAQ,UAAU;AACrB,UAAM,GAAG,MAAM;AACf,UAAM,kCAAkC,KAAK,MAAM,QAAQ;AAC3D,WAAO,EAAE,cAAc,WAAW,IAAI,aAAa,UAAU,GAAG;AAAA,EAClE;AAAA,EAEA,cAAc,OAAO,OAAO,SAAS,QAAQ;AAC3C,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,UAAU,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK;AAClE,WAAO,EAAE,OAAO,mBAAmB,OAAO,EAAE;AAAA,EAC9C;AAAA,EAEA,UAAU,OAAO,EAAE,OAAO,QAAQ,KAAK,UAAU,MAAM;AACrD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,wCAAwC,2BAA2B;AAAA,MAC1F,cAAc;AAAA,MACd,YAAY,QAAQ,gBAAgB;AAAA,MACpC,UAAU,OAAO,YAAY,IAAI,MAAM,YAAY;AAAA,MACnD,gBAAgB,OAAO,kBAAkB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,MAC1F,SAAS;AAAA,QACP,MAAM;AAAA,UACJ,QAAS,WAAsD,UAAU;AAAA,UACzE,OAAQ,WAAqD,SAAS;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAA+C,QAAQ;AACvE,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,UAAU,GAAG;AAExB,UAAM,QAAQ,QAAQ;AACtB,UAAM,SAAS,QAAQ;AAEvB,QAAI,OAAO;AACT,YAAM,QAAQ,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAC/E,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,MAAM,IAAI,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAAA,QAC/E;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,WAAY;AAEjB,UAAI,CAAC,QAAQ;AACX,mBAAW,YAAY,oBAAI,KAAK;AAChC,cAAM,GAAG,MAAM;AACf,cAAM,kCAAkC,KAAK,MAAM,QAAQ;AAC3D;AAAA,MACF;AAEA,YAAM,oBAAoB,MAAM;AAAA,QAC9B;AAAA,QACA;AAAA,QACA;AAAA,UACE,IAAI,OAAO;AAAA,UACX,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,WAAW;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,mBAAmB;AACtB,mBAAW,YAAY,oBAAI,KAAK;AAChC,cAAM,GAAG,MAAM;AACf,cAAM,kCAAkC,KAAK,MAAM,QAAQ;AAC3D;AAAA,MACF;AAEA,iBAAW,YAAY;AACvB,iBAAW,QAAQ,OAAO,SAAS;AACnC,YAAM,GAAG,MAAM;AACf,YAAM,kCAAkC,KAAK,MAAM,QAAQ;AAAA,IAC7D;AAAA,EACF;AACF;AAEA,MAAM,2BAGF;AAAA,EACF,IAAI;AAAA,EAEJ,SAAS,OAAO,OAAO,QAAQ;AAC7B,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,WAAW,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK;AACnE,WAAO,EAAE,QAAQ,mBAAmB,QAAQ,EAAE;AAAA,EAChD;AAAA,EAEA,SAAS,OAAO,OAAO,QAAQ;AAC7B,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,QAAI,CAAC,MAAM,YAAY,CAAC,MAAM,gBAAgB;AAC5C,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,IACxD;AACA,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,WAAW,MAAM,eAAe,IAAI,MAAM,cAAc,KAAK;AAEnE,QAAI,UAAU;AACZ,eAAS,YAAY,oBAAI,KAAK;AAC9B,YAAM,GAAG,MAAM;AACf,YAAM,kCAAkC,KAAK,MAAM,QAAQ;AAAA,IAC7D;AAEA,WAAO,EAAE,IAAI,KAAc;AAAA,EAC7B;AAAA,EAEA,UAAU,OAAO,EAAE,OAAO,KAAK,UAAU,MAAM;AAC7C,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,SAAU,WAAsD,UAAU;AAChF,WAAO;AAAA,MACL,aAAa,UAAU,0CAA0C,wCAAwC;AAAA,MACzG,cAAc;AAAA,MACd,YAAY,QAAQ,MAAM;AAAA,MAC1B,UAAU,OAAO,YAAY,IAAI,MAAM,YAAY;AAAA,MACnD,gBAAgB,OAAO,kBAAkB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,MAC1F,SAAS;AAAA,QACP,MAAM,EAAE,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAiD,QAAQ;AACzE,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,QAAQ,gBAAgB,CAAC,OAAO,YAAa;AAElD,UAAM,QAAQ,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AACjF,UAAM,KAAK,UAAU,GAAG;AAExB,UAAM,kBAAkB,MAAM;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,EAAE,IAAI,OAAO,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,OAAO,SAAS;AAAA,MAClF;AAAA,MACA;AAAA,IACF;AAEA,QAAI,iBAAiB;AACnB,sBAAgB,YAAY;AAC5B,YAAM,GAAG,MAAM;AACf,YAAM,kCAAkC,KAAK,MAAM,QAAQ;AAC3D;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI,OAAO;AAAA,QACX,gBAAgB,OAAO;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,UAAW;AAEhB,UAAM,WAAW,GAAG,OAAO,+BAA+B;AAAA,MACxD,cAAc,OAAO;AAAA,MACrB;AAAA,MACA,gBAAgB,OAAO;AAAA,MACvB,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO,SAAS;AAAA,IACzB,CAAC;AACD,OAAG,QAAQ,QAAQ;AACnB,UAAM,GAAG,MAAM;AACf,UAAM,kCAAkC,KAAK,MAAM,QAAQ;AAAA,EAC7D;AACF;AAEA,gBAAgB,sBAAsB;AACtC,gBAAgB,wBAAwB;AAEjC,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,cAAc,EAAE,OAAO,EAAE,KAAK;AAAA,EAC9B,gBAAgB,EAAE,OAAO,EAAE,KAAK;AAAA,EAChC,UAAU,EAAE,OAAO,EAAE,KAAK;AAC5B,CAAC;AAGD,MAAM,0BAAkF;AAAA,EACtF,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,4BAA4B,MAAM,QAAQ;AACxD,UAAM,WAAW;AAAA,MACf,SAAS,CAAI,SAAiB,IAAI,UAAU,QAAW,IAAI;AAAA,IAC7D;AACA,UAAM;AAAA,MACJ,EAAE,SAAS,MAAM,cAAc,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,MAC9F;AAAA,IACF;AACA,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AAAA,EACA,MAAM,SAAS,EAAE,OAAO,IAAI,GAAG;AAC7B,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,yCAAyC,8BAA8B;AAAA,MAC9F,cAAc;AAAA,MACd,YAAY,OAAO,gBAAgB;AAAA,MACnC,UAAU,OAAO,YAAY,IAAI,MAAM,YAAY;AAAA,MACnD,gBAAgB,OAAO,kBAAkB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,IAC5F;AAAA,EACF;AACF;AAEA,gBAAgB,uBAAuB;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,11 @@ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
2
  import { E } from "../../../generated/entities.ids.generated.js";
3
3
  import { InventoryBalance, InventoryReservation, ProductInventoryProfile, SalesOrderWarehouseAssignment, Warehouse } from "./entities.js";
4
4
  import { resolvePrimaryWarehouseId } from "../lib/primaryWarehousePolicy.js";
5
+ import {
6
+ WMS_ENRICHER_CACHE_TTL_MS,
7
+ WMS_INVENTORY_CACHE_TAG,
8
+ WMS_WAREHOUSE_CACHE_TAG
9
+ } from "../lib/enricherCacheTags.js";
5
10
  import { resolveWmsIntegrationToggleEnabled } from "../lib/wmsIntegrationToggles.js";
6
11
  const EMPTY_ENRICHMENT = {
7
12
  _wms: {
@@ -262,6 +267,13 @@ const salesOrderInventoryEnricher = {
262
267
  priority: 40,
263
268
  timeout: 2e3,
264
269
  fallback: EMPTY_ENRICHMENT,
270
+ // Reads reservations, balances, the primary warehouse and the explicit
271
+ // assignment, so it depends on both the inventory and the warehouse tags.
272
+ cache: {
273
+ strategy: "read-through",
274
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
275
+ tags: [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG]
276
+ },
265
277
  async enrichOne(record, context) {
266
278
  return (await this.enrichMany([record], context))[0];
267
279
  },
@@ -393,6 +405,13 @@ const catalogProductInventoryEnricher = {
393
405
  priority: 40,
394
406
  timeout: 2e3,
395
407
  fallback: EMPTY_CATALOG_ENRICHMENT,
408
+ // Reads inventory profiles and balances; also reads catalog variants, so the
409
+ // catalog variant events invalidate the inventory tag too.
410
+ cache: {
411
+ strategy: "read-through",
412
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
413
+ tags: [WMS_INVENTORY_CACHE_TAG]
414
+ },
396
415
  async enrichOne(record, context) {
397
416
  return (await this.enrichMany([record], context))[0];
398
417
  },
@@ -453,6 +472,12 @@ const catalogVariantInventoryEnricher = {
453
472
  priority: 40,
454
473
  timeout: 2e3,
455
474
  fallback: EMPTY_CATALOG_ENRICHMENT,
475
+ // Reads inventory profiles and balances for the variant only.
476
+ cache: {
477
+ strategy: "read-through",
478
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
479
+ tags: [WMS_INVENTORY_CACHE_TAG]
480
+ },
456
481
  async enrichOne(record, context) {
457
482
  return (await this.enrichMany([record], context))[0];
458
483
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/wms/data/enrichers.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ResponseEnricher, EnricherContext } from '@open-mercato/shared/lib/crud/response-enricher'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { FeatureTogglesService } from '@open-mercato/core/modules/feature_toggles/lib/feature-flag-check'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { InventoryBalance, InventoryReservation, ProductInventoryProfile, SalesOrderWarehouseAssignment, Warehouse } from './entities'\nimport { resolvePrimaryWarehouseId } from '../lib/primaryWarehousePolicy'\n\ntype SalesOrderRecord = Record<string, unknown> & { id?: string }\ntype CatalogProductRecord = Record<string, unknown> & { id?: string }\ntype CatalogVariantRecord = Record<string, unknown> & { id?: string; product_id?: string; productId?: string }\n\ntype SalesOrderLineRow = {\n id?: string\n order_id?: string | null\n product_variant_id?: string | null\n quantity?: string | number | null\n line_number?: number | null\n status?: string | null\n}\n\ntype CatalogVariantRow = {\n id?: string\n product_id?: string | null\n}\ntype ReservationStatus = 'unreserved' | 'partially_reserved' | 'fully_reserved'\ntype ReorderState = 'no_profile' | 'healthy' | 'below_reorder_point' | 'below_safety_stock'\n\ntype InventoryProfileSummary = {\n profileId: string\n catalogProductId: string\n catalogVariantId: string | null\n defaultUom: string\n defaultStrategy: string\n trackLot: boolean\n trackSerial: boolean\n trackExpiration: boolean\n reorderPoint: string\n safetyStock: string\n}\n\ntype StockSummaryItem = {\n catalogVariantId: string\n onHand: string\n reserved: string\n allocated: string\n available: string\n}\n\ntype ReorderStatus = {\n state: ReorderState\n available: string\n reorderPoint: string\n safetyStock: string\n}\n\ntype SalesOrderWmsEnrichment = {\n _wms: {\n assignedWarehouseId: string | null\n assignedWarehouseName: string | null\n isExplicitlyAssigned: boolean\n stockSummary: Array<{\n catalogVariantId: string\n available: string\n reserved: string\n }>\n reservationSummary: {\n status: ReservationStatus\n reservationIds: string[]\n }\n }\n}\n\ntype CatalogWmsEnrichment = {\n _wms: {\n inventoryProfile: InventoryProfileSummary | null\n stockSummary: StockSummaryItem[]\n reorderStatus: ReorderStatus\n }\n}\n\ntype EnricherScope = EnricherContext & { em: EntityManager }\ntype Scope = { organizationId: string; tenantId: string }\ntype BalanceAggregate = { onHand: number; reserved: number; allocated: number; available: number }\n\nimport { resolveWmsIntegrationToggleEnabled } from '../lib/wmsIntegrationToggles'\nconst EMPTY_ENRICHMENT: SalesOrderWmsEnrichment = {\n _wms: {\n assignedWarehouseId: null,\n assignedWarehouseName: null,\n isExplicitlyAssigned: false,\n stockSummary: [],\n reservationSummary: {\n status: 'unreserved',\n reservationIds: [],\n },\n },\n}\n\nconst EMPTY_CATALOG_ENRICHMENT: CatalogWmsEnrichment = {\n _wms: {\n inventoryProfile: null,\n stockSummary: [],\n reorderStatus: {\n state: 'no_profile',\n available: '0',\n reorderPoint: '0',\n safetyStock: '0',\n },\n },\n}\n\nfunction extractRecordId(record: SalesOrderRecord): string | null {\n return typeof record.id === 'string' && record.id.trim().length > 0 ? record.id : null\n}\n\nfunction extractOrderIdFromLine(line: SalesOrderLineRow): string | null {\n return typeof line.order_id === 'string' && line.order_id.length > 0 ? line.order_id : null\n}\n\nfunction extractProductIdFromVariant(variant: CatalogVariantRow): string | null {\n return typeof variant.product_id === 'string' && variant.product_id.length > 0\n ? variant.product_id\n : null\n}\n\nfunction extractWarehouseId(reservation: InventoryReservation): string | null {\n const relation = reservation.warehouse as { id?: string } | undefined\n return typeof relation?.id === 'string' ? relation.id : null\n}\n\nfunction addToMap(map: Map<string, number>, key: string, value: string | number | null | undefined) {\n const numeric = typeof value === 'number' ? value : Number(value ?? 0)\n map.set(key, (map.get(key) ?? 0) + numeric)\n}\n\nfunction formatQuantity(value: number): string {\n if (!Number.isFinite(value) || value === 0) return '0'\n const normalized = value.toFixed(4).replace(/\\.?0+$/, '')\n return normalized.length > 0 ? normalized : '0'\n}\n\nfunction toInventoryProfileSummary(profile: ProductInventoryProfile): InventoryProfileSummary {\n return {\n profileId: profile.id,\n catalogProductId: profile.catalogProductId,\n catalogVariantId: profile.catalogVariantId ?? null,\n defaultUom: profile.defaultUom,\n defaultStrategy: profile.defaultStrategy,\n trackLot: profile.trackLot,\n trackSerial: profile.trackSerial,\n trackExpiration: profile.trackExpiration,\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n }\n}\n\nfunction resolveReservationStatus(requiredQuantity: number, reservedQuantity: number): ReservationStatus {\n if (requiredQuantity <= 0 || reservedQuantity <= 0) return 'unreserved'\n if (reservedQuantity + 0.0001 < requiredQuantity) return 'partially_reserved'\n return 'fully_reserved'\n}\n\nfunction resolveReorderStatus(\n availableQuantity: number,\n profile: ProductInventoryProfile | null | undefined,\n): ReorderStatus {\n if (!profile) {\n return {\n state: 'no_profile',\n available: formatQuantity(availableQuantity),\n reorderPoint: '0',\n safetyStock: '0',\n }\n }\n\n const reorderPoint = Number(profile.reorderPoint)\n const safetyStock = Number(profile.safetyStock)\n let state: ReorderState = 'healthy'\n\n if (availableQuantity <= safetyStock) {\n state = 'below_safety_stock'\n } else if (availableQuantity <= reorderPoint) {\n state = 'below_reorder_point'\n }\n\n return {\n state,\n available: formatQuantity(availableQuantity),\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n }\n}\n\nfunction buildBalanceSummaryByVariant(\n balances: InventoryBalance[],\n): Map<string, BalanceAggregate> {\n const byVariant = new Map<string, BalanceAggregate>()\n\n for (const balance of balances) {\n const current = byVariant.get(balance.catalogVariantId) ?? {\n onHand: 0,\n reserved: 0,\n allocated: 0,\n available: 0,\n }\n\n current.onHand += Number(balance.quantityOnHand)\n current.reserved += Number(balance.quantityReserved)\n current.allocated += Number(balance.quantityAllocated)\n current.available +=\n Number(balance.quantityOnHand) - Number(balance.quantityReserved) - Number(balance.quantityAllocated)\n\n byVariant.set(balance.catalogVariantId, current)\n }\n\n return byVariant\n}\n\nfunction buildStockSummaryItem(\n catalogVariantId: string,\n aggregate: BalanceAggregate | undefined,\n): StockSummaryItem {\n return {\n catalogVariantId,\n onHand: formatQuantity(aggregate?.onHand ?? 0),\n reserved: formatQuantity(aggregate?.reserved ?? 0),\n allocated: formatQuantity(aggregate?.allocated ?? 0),\n available: formatQuantity(aggregate?.available ?? 0),\n }\n}\n\nasync function isSalesOrderInventoryEnabled(context: EnricherContext): Promise<boolean> {\n const container = context.container as { resolve?: (name: string) => unknown } | undefined\n if (!container?.resolve) return true\n try {\n const featureTogglesService = container.resolve('featureTogglesService') as FeatureTogglesService | undefined\n const em = container.resolve('em') as EntityManager | undefined\n if (!featureTogglesService || !em) return true\n return resolveWmsIntegrationToggleEnabled(\n featureTogglesService,\n em,\n 'wms_integration_sales_order_inventory',\n context.tenantId,\n )\n } catch {\n return true\n }\n}\n\nasync function loadExplicitAssignments(\n em: EntityManager,\n orderIds: string[],\n scope: Scope,\n): Promise<SalesOrderWarehouseAssignment[]> {\n if (orderIds.length === 0) return []\n return findWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n {\n salesOrderId: { $in: orderIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadWarehousesByIds(\n em: EntityManager,\n warehouseIds: string[],\n scope: Scope,\n): Promise<Warehouse[]> {\n if (warehouseIds.length === 0) return []\n return findWithDecryption(\n em,\n Warehouse,\n {\n id: { $in: warehouseIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadOrderLines(\n context: EnricherContext,\n orderIds: string[],\n scope: Scope,\n): Promise<SalesOrderLineRow[]> {\n if (orderIds.length === 0) return []\n // Read sales order lines via QueryEngine instead of importing the sales ORM\n // entity. Keeps the WMS module decoupled from sales internals; queryEngine\n // resolves the base table via Mikro-ORM metadata.\n const container = context.container as { resolve: (name: string) => unknown }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const result = await queryEngine.query<SalesOrderLineRow>(E.sales.sales_order_line, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n filters: { order_id: { $in: orderIds } },\n fields: ['id', 'order_id', 'product_variant_id', 'quantity', 'line_number', 'status'],\n sort: [{ field: 'line_number', dir: 'asc' as never }],\n page: { page: 1, pageSize: 5000 },\n })\n return result.items\n}\n\nasync function loadReservations(\n em: EntityManager,\n orderIds: string[],\n scope: Scope,\n): Promise<InventoryReservation[]> {\n if (orderIds.length === 0) return []\n return findWithDecryption(\n em,\n InventoryReservation,\n {\n sourceType: 'order',\n sourceId: { $in: orderIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { orderBy: { createdAt: 'asc' } },\n scope,\n )\n}\n\nasync function loadBalances(\n em: EntityManager,\n variantIds: string[],\n scope: Scope,\n): Promise<InventoryBalance[]> {\n if (variantIds.length === 0) return []\n return findWithDecryption(\n em,\n InventoryBalance,\n {\n catalogVariantId: { $in: variantIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadCatalogVariants(\n context: EnricherContext,\n productIds: string[],\n scope: Scope,\n): Promise<CatalogVariantRow[]> {\n if (productIds.length === 0) return []\n const container = context.container as { resolve: (name: string) => unknown }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const result = await queryEngine.query<CatalogVariantRow>(E.catalog.catalog_product_variant, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n filters: { product_id: { $in: productIds } },\n fields: ['id', 'product_id'],\n sort: [{ field: 'created_at', dir: 'asc' as never }],\n page: { page: 1, pageSize: 5000 },\n })\n return result.items\n}\n\nasync function loadProductInventoryProfiles(\n em: EntityManager,\n productIds: string[],\n scope: Scope,\n): Promise<ProductInventoryProfile[]> {\n if (productIds.length === 0) return []\n return findWithDecryption(\n em,\n ProductInventoryProfile,\n {\n catalogProductId: { $in: productIds },\n catalogVariantId: null,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadVariantInventoryProfiles(\n em: EntityManager,\n variantIds: string[],\n scope: Scope,\n): Promise<ProductInventoryProfile[]> {\n if (variantIds.length === 0) return []\n return findWithDecryption(\n em,\n ProductInventoryProfile,\n {\n catalogVariantId: { $in: variantIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nconst salesOrderInventoryEnricher: ResponseEnricher<SalesOrderRecord, SalesOrderWmsEnrichment> = {\n id: 'wms.sales-order-inventory',\n targetEntity: E.sales.sales_order,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_ENRICHMENT,\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<SalesOrderRecord & SalesOrderWmsEnrichment>\n const enabled = await isSalesOrderInventoryEnabled(context)\n if (!enabled) return records as Array<SalesOrderRecord & SalesOrderWmsEnrichment>\n\n const orderIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (orderIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const lines = await loadOrderLines(context, orderIds, scope)\n\n const variantIds = Array.from(\n new Set(\n lines\n .map((line) => line.product_variant_id)\n .filter((value): value is string => typeof value === 'string' && value.length > 0),\n ),\n )\n\n const [reservations, balances, primaryWarehouseId, explicitAssignments] = await Promise.all([\n loadReservations(em, orderIds, scope),\n loadBalances(em, variantIds, scope),\n resolvePrimaryWarehouseId(em, scope),\n loadExplicitAssignments(em, orderIds, scope),\n ])\n\n const explicitWarehouseIdsByOrder = new Map<string, string>()\n for (const assignment of explicitAssignments) {\n const warehouseRel = assignment.warehouse as { id?: string } | undefined\n const warehouseId = warehouseRel?.id\n if (warehouseId) {\n explicitWarehouseIdsByOrder.set(assignment.salesOrderId, warehouseId)\n }\n }\n\n const reservationWarehouseIds = reservations\n .map(extractWarehouseId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n const allRelevantWarehouseIds = Array.from(\n new Set([\n ...Array.from(explicitWarehouseIdsByOrder.values()),\n ...reservationWarehouseIds,\n ...(primaryWarehouseId ? [primaryWarehouseId] : []),\n ]),\n )\n const warehouses = await loadWarehousesByIds(em, allRelevantWarehouseIds, scope)\n const warehouseNamesById = new Map(warehouses.map((w) => [w.id, w.name]))\n\n const variantAvailability = new Map<string, number>()\n const variantReserved = new Map<string, number>()\n for (const balance of balances) {\n addToMap(\n variantAvailability,\n balance.catalogVariantId,\n Number(balance.quantityOnHand) - Number(balance.quantityReserved) - Number(balance.quantityAllocated),\n )\n addToMap(variantReserved, balance.catalogVariantId, balance.quantityReserved)\n }\n\n const variantOrderBySalesOrder = new Map<string, string[]>()\n const requiredBySalesOrder = new Map<string, number>()\n\n for (const line of lines) {\n const orderId = extractOrderIdFromLine(line)\n const variantId = line.product_variant_id ?? null\n if (!orderId || !variantId) continue\n\n const variantOrder = variantOrderBySalesOrder.get(orderId) ?? []\n if (!variantOrder.includes(variantId)) {\n variantOrder.push(variantId)\n variantOrderBySalesOrder.set(orderId, variantOrder)\n }\n requiredBySalesOrder.set(orderId, (requiredBySalesOrder.get(orderId) ?? 0) + Number(line.quantity))\n }\n\n const activeReservationsBySalesOrder = new Map<string, InventoryReservation[]>()\n const reservedBySalesOrder = new Map<string, number>()\n\n for (const reservation of reservations) {\n if (reservation.status !== 'active') continue\n const orderId = reservation.sourceId\n const activeReservations = activeReservationsBySalesOrder.get(orderId) ?? []\n activeReservations.push(reservation)\n activeReservationsBySalesOrder.set(orderId, activeReservations)\n reservedBySalesOrder.set(orderId, (reservedBySalesOrder.get(orderId) ?? 0) + Number(reservation.quantity))\n }\n\n return records.map((record) => {\n const orderId = extractRecordId(record)\n if (!orderId) {\n return { ...record, ...EMPTY_ENRICHMENT }\n }\n\n const reservationsForOrder = activeReservationsBySalesOrder.get(orderId) ?? []\n const warehouseIds = Array.from(\n new Set(\n reservationsForOrder\n .map(extractWarehouseId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0),\n ),\n )\n const variantIdsForOrder = variantOrderBySalesOrder.get(orderId) ?? []\n\n const explicitWarehouseId = explicitWarehouseIdsByOrder.get(orderId) ?? null\n const isExplicitlyAssigned = explicitWarehouseId !== null\n\n let assignedWarehouseId: string | null\n if (isExplicitlyAssigned) {\n assignedWarehouseId = explicitWarehouseId\n } else if (warehouseIds.length === 1) {\n assignedWarehouseId = warehouseIds[0]\n } else if (warehouseIds.length === 0 && primaryWarehouseId) {\n assignedWarehouseId = primaryWarehouseId\n } else {\n assignedWarehouseId = null\n }\n\n const assignedWarehouseName = assignedWarehouseId\n ? (warehouseNamesById.get(assignedWarehouseId) ?? null)\n : null\n\n return {\n ...record,\n _wms: {\n assignedWarehouseId,\n assignedWarehouseName,\n isExplicitlyAssigned,\n stockSummary: variantIdsForOrder.map((catalogVariantId) => ({\n catalogVariantId,\n available: formatQuantity(variantAvailability.get(catalogVariantId) ?? 0),\n reserved: formatQuantity(variantReserved.get(catalogVariantId) ?? 0),\n })),\n reservationSummary: {\n status: resolveReservationStatus(\n requiredBySalesOrder.get(orderId) ?? 0,\n reservedBySalesOrder.get(orderId) ?? 0,\n ),\n reservationIds: reservationsForOrder.map((reservation) => reservation.id),\n },\n },\n }\n })\n },\n}\n\nconst catalogProductInventoryEnricher: ResponseEnricher<CatalogProductRecord, CatalogWmsEnrichment> = {\n id: 'wms.catalog-product-inventory',\n targetEntity: E.catalog.catalog_product,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_CATALOG_ENRICHMENT,\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<CatalogProductRecord & CatalogWmsEnrichment>\n\n const productIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (productIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_CATALOG_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const variants = await loadCatalogVariants(context, productIds, scope)\n const variantIds = variants\n .map((variant) => variant.id)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n const [productProfiles, balances] = await Promise.all([\n loadProductInventoryProfiles(em, productIds, scope),\n loadBalances(em, variantIds, scope),\n ])\n\n const profileByProductId = new Map(\n productProfiles.map((profile) => [profile.catalogProductId, profile] as const),\n )\n const balanceByVariantId = buildBalanceSummaryByVariant(balances)\n const variantIdsByProductId = new Map<string, string[]>()\n\n for (const variant of variants) {\n const productId = extractProductIdFromVariant(variant)\n if (!productId || typeof variant.id !== 'string') continue\n const entries = variantIdsByProductId.get(productId) ?? []\n entries.push(variant.id)\n variantIdsByProductId.set(productId, entries)\n }\n\n return records.map((record) => {\n const productId = extractRecordId(record)\n if (!productId) {\n return { ...record, ...EMPTY_CATALOG_ENRICHMENT }\n }\n\n const profile = profileByProductId.get(productId) ?? null\n const stockSummary = (variantIdsByProductId.get(productId) ?? []).map((variantId) =>\n buildStockSummaryItem(variantId, balanceByVariantId.get(variantId)),\n )\n const totalAvailable = stockSummary.reduce(\n (sum, item) => sum + Number(item.available),\n 0,\n )\n\n return {\n ...record,\n _wms: {\n inventoryProfile: profile ? toInventoryProfileSummary(profile) : null,\n stockSummary,\n reorderStatus: resolveReorderStatus(totalAvailable, profile),\n },\n }\n })\n },\n}\n\nconst catalogVariantInventoryEnricher: ResponseEnricher<CatalogVariantRecord, CatalogWmsEnrichment> = {\n id: 'wms.catalog-variant-inventory',\n targetEntity: E.catalog.catalog_product_variant,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_CATALOG_ENRICHMENT,\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<CatalogVariantRecord & CatalogWmsEnrichment>\n\n const variantIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (variantIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_CATALOG_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const [profiles, balances] = await Promise.all([\n loadVariantInventoryProfiles(em, variantIds, scope),\n loadBalances(em, variantIds, scope),\n ])\n\n const profileByVariantId = new Map(\n profiles\n .filter((profile): profile is ProductInventoryProfile & { catalogVariantId: string } => typeof profile.catalogVariantId === 'string')\n .map((profile) => [profile.catalogVariantId, profile] as const),\n )\n const balanceByVariantId = buildBalanceSummaryByVariant(balances)\n\n return records.map((record) => {\n const variantId = extractRecordId(record)\n if (!variantId) {\n return { ...record, ...EMPTY_CATALOG_ENRICHMENT }\n }\n\n const profile = profileByVariantId.get(variantId) ?? null\n const stockSummary = [buildStockSummaryItem(variantId, balanceByVariantId.get(variantId))]\n\n return {\n ...record,\n _wms: {\n inventoryProfile: profile ? toInventoryProfileSummary(profile) : null,\n stockSummary,\n reorderStatus: resolveReorderStatus(Number(stockSummary[0]?.available ?? 0), profile),\n },\n }\n })\n },\n}\n\nexport const enrichers: ResponseEnricher[] = [\n salesOrderInventoryEnricher,\n catalogProductInventoryEnricher,\n catalogVariantInventoryEnricher,\n]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ResponseEnricher, EnricherContext } from '@open-mercato/shared/lib/crud/response-enricher'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { FeatureTogglesService } from '@open-mercato/core/modules/feature_toggles/lib/feature-flag-check'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { InventoryBalance, InventoryReservation, ProductInventoryProfile, SalesOrderWarehouseAssignment, Warehouse } from './entities'\nimport { resolvePrimaryWarehouseId } from '../lib/primaryWarehousePolicy'\nimport {\n WMS_ENRICHER_CACHE_TTL_MS,\n WMS_INVENTORY_CACHE_TAG,\n WMS_WAREHOUSE_CACHE_TAG,\n} from '../lib/enricherCacheTags'\n\ntype SalesOrderRecord = Record<string, unknown> & { id?: string }\ntype CatalogProductRecord = Record<string, unknown> & { id?: string }\ntype CatalogVariantRecord = Record<string, unknown> & { id?: string; product_id?: string; productId?: string }\n\ntype SalesOrderLineRow = {\n id?: string\n order_id?: string | null\n product_variant_id?: string | null\n quantity?: string | number | null\n line_number?: number | null\n status?: string | null\n}\n\ntype CatalogVariantRow = {\n id?: string\n product_id?: string | null\n}\ntype ReservationStatus = 'unreserved' | 'partially_reserved' | 'fully_reserved'\ntype ReorderState = 'no_profile' | 'healthy' | 'below_reorder_point' | 'below_safety_stock'\n\ntype InventoryProfileSummary = {\n profileId: string\n catalogProductId: string\n catalogVariantId: string | null\n defaultUom: string\n defaultStrategy: string\n trackLot: boolean\n trackSerial: boolean\n trackExpiration: boolean\n reorderPoint: string\n safetyStock: string\n}\n\ntype StockSummaryItem = {\n catalogVariantId: string\n onHand: string\n reserved: string\n allocated: string\n available: string\n}\n\ntype ReorderStatus = {\n state: ReorderState\n available: string\n reorderPoint: string\n safetyStock: string\n}\n\ntype SalesOrderWmsEnrichment = {\n _wms: {\n assignedWarehouseId: string | null\n assignedWarehouseName: string | null\n isExplicitlyAssigned: boolean\n stockSummary: Array<{\n catalogVariantId: string\n available: string\n reserved: string\n }>\n reservationSummary: {\n status: ReservationStatus\n reservationIds: string[]\n }\n }\n}\n\ntype CatalogWmsEnrichment = {\n _wms: {\n inventoryProfile: InventoryProfileSummary | null\n stockSummary: StockSummaryItem[]\n reorderStatus: ReorderStatus\n }\n}\n\ntype EnricherScope = EnricherContext & { em: EntityManager }\ntype Scope = { organizationId: string; tenantId: string }\ntype BalanceAggregate = { onHand: number; reserved: number; allocated: number; available: number }\n\nimport { resolveWmsIntegrationToggleEnabled } from '../lib/wmsIntegrationToggles'\nconst EMPTY_ENRICHMENT: SalesOrderWmsEnrichment = {\n _wms: {\n assignedWarehouseId: null,\n assignedWarehouseName: null,\n isExplicitlyAssigned: false,\n stockSummary: [],\n reservationSummary: {\n status: 'unreserved',\n reservationIds: [],\n },\n },\n}\n\nconst EMPTY_CATALOG_ENRICHMENT: CatalogWmsEnrichment = {\n _wms: {\n inventoryProfile: null,\n stockSummary: [],\n reorderStatus: {\n state: 'no_profile',\n available: '0',\n reorderPoint: '0',\n safetyStock: '0',\n },\n },\n}\n\nfunction extractRecordId(record: SalesOrderRecord): string | null {\n return typeof record.id === 'string' && record.id.trim().length > 0 ? record.id : null\n}\n\nfunction extractOrderIdFromLine(line: SalesOrderLineRow): string | null {\n return typeof line.order_id === 'string' && line.order_id.length > 0 ? line.order_id : null\n}\n\nfunction extractProductIdFromVariant(variant: CatalogVariantRow): string | null {\n return typeof variant.product_id === 'string' && variant.product_id.length > 0\n ? variant.product_id\n : null\n}\n\nfunction extractWarehouseId(reservation: InventoryReservation): string | null {\n const relation = reservation.warehouse as { id?: string } | undefined\n return typeof relation?.id === 'string' ? relation.id : null\n}\n\nfunction addToMap(map: Map<string, number>, key: string, value: string | number | null | undefined) {\n const numeric = typeof value === 'number' ? value : Number(value ?? 0)\n map.set(key, (map.get(key) ?? 0) + numeric)\n}\n\nfunction formatQuantity(value: number): string {\n if (!Number.isFinite(value) || value === 0) return '0'\n const normalized = value.toFixed(4).replace(/\\.?0+$/, '')\n return normalized.length > 0 ? normalized : '0'\n}\n\nfunction toInventoryProfileSummary(profile: ProductInventoryProfile): InventoryProfileSummary {\n return {\n profileId: profile.id,\n catalogProductId: profile.catalogProductId,\n catalogVariantId: profile.catalogVariantId ?? null,\n defaultUom: profile.defaultUom,\n defaultStrategy: profile.defaultStrategy,\n trackLot: profile.trackLot,\n trackSerial: profile.trackSerial,\n trackExpiration: profile.trackExpiration,\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n }\n}\n\nfunction resolveReservationStatus(requiredQuantity: number, reservedQuantity: number): ReservationStatus {\n if (requiredQuantity <= 0 || reservedQuantity <= 0) return 'unreserved'\n if (reservedQuantity + 0.0001 < requiredQuantity) return 'partially_reserved'\n return 'fully_reserved'\n}\n\nfunction resolveReorderStatus(\n availableQuantity: number,\n profile: ProductInventoryProfile | null | undefined,\n): ReorderStatus {\n if (!profile) {\n return {\n state: 'no_profile',\n available: formatQuantity(availableQuantity),\n reorderPoint: '0',\n safetyStock: '0',\n }\n }\n\n const reorderPoint = Number(profile.reorderPoint)\n const safetyStock = Number(profile.safetyStock)\n let state: ReorderState = 'healthy'\n\n if (availableQuantity <= safetyStock) {\n state = 'below_safety_stock'\n } else if (availableQuantity <= reorderPoint) {\n state = 'below_reorder_point'\n }\n\n return {\n state,\n available: formatQuantity(availableQuantity),\n reorderPoint: profile.reorderPoint,\n safetyStock: profile.safetyStock,\n }\n}\n\nfunction buildBalanceSummaryByVariant(\n balances: InventoryBalance[],\n): Map<string, BalanceAggregate> {\n const byVariant = new Map<string, BalanceAggregate>()\n\n for (const balance of balances) {\n const current = byVariant.get(balance.catalogVariantId) ?? {\n onHand: 0,\n reserved: 0,\n allocated: 0,\n available: 0,\n }\n\n current.onHand += Number(balance.quantityOnHand)\n current.reserved += Number(balance.quantityReserved)\n current.allocated += Number(balance.quantityAllocated)\n current.available +=\n Number(balance.quantityOnHand) - Number(balance.quantityReserved) - Number(balance.quantityAllocated)\n\n byVariant.set(balance.catalogVariantId, current)\n }\n\n return byVariant\n}\n\nfunction buildStockSummaryItem(\n catalogVariantId: string,\n aggregate: BalanceAggregate | undefined,\n): StockSummaryItem {\n return {\n catalogVariantId,\n onHand: formatQuantity(aggregate?.onHand ?? 0),\n reserved: formatQuantity(aggregate?.reserved ?? 0),\n allocated: formatQuantity(aggregate?.allocated ?? 0),\n available: formatQuantity(aggregate?.available ?? 0),\n }\n}\n\nasync function isSalesOrderInventoryEnabled(context: EnricherContext): Promise<boolean> {\n const container = context.container as { resolve?: (name: string) => unknown } | undefined\n if (!container?.resolve) return true\n try {\n const featureTogglesService = container.resolve('featureTogglesService') as FeatureTogglesService | undefined\n const em = container.resolve('em') as EntityManager | undefined\n if (!featureTogglesService || !em) return true\n return resolveWmsIntegrationToggleEnabled(\n featureTogglesService,\n em,\n 'wms_integration_sales_order_inventory',\n context.tenantId,\n )\n } catch {\n return true\n }\n}\n\nasync function loadExplicitAssignments(\n em: EntityManager,\n orderIds: string[],\n scope: Scope,\n): Promise<SalesOrderWarehouseAssignment[]> {\n if (orderIds.length === 0) return []\n return findWithDecryption(\n em,\n SalesOrderWarehouseAssignment,\n {\n salesOrderId: { $in: orderIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadWarehousesByIds(\n em: EntityManager,\n warehouseIds: string[],\n scope: Scope,\n): Promise<Warehouse[]> {\n if (warehouseIds.length === 0) return []\n return findWithDecryption(\n em,\n Warehouse,\n {\n id: { $in: warehouseIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadOrderLines(\n context: EnricherContext,\n orderIds: string[],\n scope: Scope,\n): Promise<SalesOrderLineRow[]> {\n if (orderIds.length === 0) return []\n // Read sales order lines via QueryEngine instead of importing the sales ORM\n // entity. Keeps the WMS module decoupled from sales internals; queryEngine\n // resolves the base table via Mikro-ORM metadata.\n const container = context.container as { resolve: (name: string) => unknown }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const result = await queryEngine.query<SalesOrderLineRow>(E.sales.sales_order_line, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n filters: { order_id: { $in: orderIds } },\n fields: ['id', 'order_id', 'product_variant_id', 'quantity', 'line_number', 'status'],\n sort: [{ field: 'line_number', dir: 'asc' as never }],\n page: { page: 1, pageSize: 5000 },\n })\n return result.items\n}\n\nasync function loadReservations(\n em: EntityManager,\n orderIds: string[],\n scope: Scope,\n): Promise<InventoryReservation[]> {\n if (orderIds.length === 0) return []\n return findWithDecryption(\n em,\n InventoryReservation,\n {\n sourceType: 'order',\n sourceId: { $in: orderIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n { orderBy: { createdAt: 'asc' } },\n scope,\n )\n}\n\nasync function loadBalances(\n em: EntityManager,\n variantIds: string[],\n scope: Scope,\n): Promise<InventoryBalance[]> {\n if (variantIds.length === 0) return []\n return findWithDecryption(\n em,\n InventoryBalance,\n {\n catalogVariantId: { $in: variantIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadCatalogVariants(\n context: EnricherContext,\n productIds: string[],\n scope: Scope,\n): Promise<CatalogVariantRow[]> {\n if (productIds.length === 0) return []\n const container = context.container as { resolve: (name: string) => unknown }\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const result = await queryEngine.query<CatalogVariantRow>(E.catalog.catalog_product_variant, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n filters: { product_id: { $in: productIds } },\n fields: ['id', 'product_id'],\n sort: [{ field: 'created_at', dir: 'asc' as never }],\n page: { page: 1, pageSize: 5000 },\n })\n return result.items\n}\n\nasync function loadProductInventoryProfiles(\n em: EntityManager,\n productIds: string[],\n scope: Scope,\n): Promise<ProductInventoryProfile[]> {\n if (productIds.length === 0) return []\n return findWithDecryption(\n em,\n ProductInventoryProfile,\n {\n catalogProductId: { $in: productIds },\n catalogVariantId: null,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nasync function loadVariantInventoryProfiles(\n em: EntityManager,\n variantIds: string[],\n scope: Scope,\n): Promise<ProductInventoryProfile[]> {\n if (variantIds.length === 0) return []\n return findWithDecryption(\n em,\n ProductInventoryProfile,\n {\n catalogVariantId: { $in: variantIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nconst salesOrderInventoryEnricher: ResponseEnricher<SalesOrderRecord, SalesOrderWmsEnrichment> = {\n id: 'wms.sales-order-inventory',\n targetEntity: E.sales.sales_order,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_ENRICHMENT,\n // Reads reservations, balances, the primary warehouse and the explicit\n // assignment, so it depends on both the inventory and the warehouse tags.\n cache: {\n strategy: 'read-through',\n ttl: WMS_ENRICHER_CACHE_TTL_MS,\n tags: [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG],\n },\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<SalesOrderRecord & SalesOrderWmsEnrichment>\n const enabled = await isSalesOrderInventoryEnabled(context)\n if (!enabled) return records as Array<SalesOrderRecord & SalesOrderWmsEnrichment>\n\n const orderIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (orderIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const lines = await loadOrderLines(context, orderIds, scope)\n\n const variantIds = Array.from(\n new Set(\n lines\n .map((line) => line.product_variant_id)\n .filter((value): value is string => typeof value === 'string' && value.length > 0),\n ),\n )\n\n const [reservations, balances, primaryWarehouseId, explicitAssignments] = await Promise.all([\n loadReservations(em, orderIds, scope),\n loadBalances(em, variantIds, scope),\n resolvePrimaryWarehouseId(em, scope),\n loadExplicitAssignments(em, orderIds, scope),\n ])\n\n const explicitWarehouseIdsByOrder = new Map<string, string>()\n for (const assignment of explicitAssignments) {\n const warehouseRel = assignment.warehouse as { id?: string } | undefined\n const warehouseId = warehouseRel?.id\n if (warehouseId) {\n explicitWarehouseIdsByOrder.set(assignment.salesOrderId, warehouseId)\n }\n }\n\n const reservationWarehouseIds = reservations\n .map(extractWarehouseId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n const allRelevantWarehouseIds = Array.from(\n new Set([\n ...Array.from(explicitWarehouseIdsByOrder.values()),\n ...reservationWarehouseIds,\n ...(primaryWarehouseId ? [primaryWarehouseId] : []),\n ]),\n )\n const warehouses = await loadWarehousesByIds(em, allRelevantWarehouseIds, scope)\n const warehouseNamesById = new Map(warehouses.map((w) => [w.id, w.name]))\n\n const variantAvailability = new Map<string, number>()\n const variantReserved = new Map<string, number>()\n for (const balance of balances) {\n addToMap(\n variantAvailability,\n balance.catalogVariantId,\n Number(balance.quantityOnHand) - Number(balance.quantityReserved) - Number(balance.quantityAllocated),\n )\n addToMap(variantReserved, balance.catalogVariantId, balance.quantityReserved)\n }\n\n const variantOrderBySalesOrder = new Map<string, string[]>()\n const requiredBySalesOrder = new Map<string, number>()\n\n for (const line of lines) {\n const orderId = extractOrderIdFromLine(line)\n const variantId = line.product_variant_id ?? null\n if (!orderId || !variantId) continue\n\n const variantOrder = variantOrderBySalesOrder.get(orderId) ?? []\n if (!variantOrder.includes(variantId)) {\n variantOrder.push(variantId)\n variantOrderBySalesOrder.set(orderId, variantOrder)\n }\n requiredBySalesOrder.set(orderId, (requiredBySalesOrder.get(orderId) ?? 0) + Number(line.quantity))\n }\n\n const activeReservationsBySalesOrder = new Map<string, InventoryReservation[]>()\n const reservedBySalesOrder = new Map<string, number>()\n\n for (const reservation of reservations) {\n if (reservation.status !== 'active') continue\n const orderId = reservation.sourceId\n const activeReservations = activeReservationsBySalesOrder.get(orderId) ?? []\n activeReservations.push(reservation)\n activeReservationsBySalesOrder.set(orderId, activeReservations)\n reservedBySalesOrder.set(orderId, (reservedBySalesOrder.get(orderId) ?? 0) + Number(reservation.quantity))\n }\n\n return records.map((record) => {\n const orderId = extractRecordId(record)\n if (!orderId) {\n return { ...record, ...EMPTY_ENRICHMENT }\n }\n\n const reservationsForOrder = activeReservationsBySalesOrder.get(orderId) ?? []\n const warehouseIds = Array.from(\n new Set(\n reservationsForOrder\n .map(extractWarehouseId)\n .filter((value): value is string => typeof value === 'string' && value.length > 0),\n ),\n )\n const variantIdsForOrder = variantOrderBySalesOrder.get(orderId) ?? []\n\n const explicitWarehouseId = explicitWarehouseIdsByOrder.get(orderId) ?? null\n const isExplicitlyAssigned = explicitWarehouseId !== null\n\n let assignedWarehouseId: string | null\n if (isExplicitlyAssigned) {\n assignedWarehouseId = explicitWarehouseId\n } else if (warehouseIds.length === 1) {\n assignedWarehouseId = warehouseIds[0]\n } else if (warehouseIds.length === 0 && primaryWarehouseId) {\n assignedWarehouseId = primaryWarehouseId\n } else {\n assignedWarehouseId = null\n }\n\n const assignedWarehouseName = assignedWarehouseId\n ? (warehouseNamesById.get(assignedWarehouseId) ?? null)\n : null\n\n return {\n ...record,\n _wms: {\n assignedWarehouseId,\n assignedWarehouseName,\n isExplicitlyAssigned,\n stockSummary: variantIdsForOrder.map((catalogVariantId) => ({\n catalogVariantId,\n available: formatQuantity(variantAvailability.get(catalogVariantId) ?? 0),\n reserved: formatQuantity(variantReserved.get(catalogVariantId) ?? 0),\n })),\n reservationSummary: {\n status: resolveReservationStatus(\n requiredBySalesOrder.get(orderId) ?? 0,\n reservedBySalesOrder.get(orderId) ?? 0,\n ),\n reservationIds: reservationsForOrder.map((reservation) => reservation.id),\n },\n },\n }\n })\n },\n}\n\nconst catalogProductInventoryEnricher: ResponseEnricher<CatalogProductRecord, CatalogWmsEnrichment> = {\n id: 'wms.catalog-product-inventory',\n targetEntity: E.catalog.catalog_product,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_CATALOG_ENRICHMENT,\n // Reads inventory profiles and balances; also reads catalog variants, so the\n // catalog variant events invalidate the inventory tag too.\n cache: {\n strategy: 'read-through',\n ttl: WMS_ENRICHER_CACHE_TTL_MS,\n tags: [WMS_INVENTORY_CACHE_TAG],\n },\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<CatalogProductRecord & CatalogWmsEnrichment>\n\n const productIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (productIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_CATALOG_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const variants = await loadCatalogVariants(context, productIds, scope)\n const variantIds = variants\n .map((variant) => variant.id)\n .filter((value): value is string => typeof value === 'string' && value.length > 0)\n const [productProfiles, balances] = await Promise.all([\n loadProductInventoryProfiles(em, productIds, scope),\n loadBalances(em, variantIds, scope),\n ])\n\n const profileByProductId = new Map(\n productProfiles.map((profile) => [profile.catalogProductId, profile] as const),\n )\n const balanceByVariantId = buildBalanceSummaryByVariant(balances)\n const variantIdsByProductId = new Map<string, string[]>()\n\n for (const variant of variants) {\n const productId = extractProductIdFromVariant(variant)\n if (!productId || typeof variant.id !== 'string') continue\n const entries = variantIdsByProductId.get(productId) ?? []\n entries.push(variant.id)\n variantIdsByProductId.set(productId, entries)\n }\n\n return records.map((record) => {\n const productId = extractRecordId(record)\n if (!productId) {\n return { ...record, ...EMPTY_CATALOG_ENRICHMENT }\n }\n\n const profile = profileByProductId.get(productId) ?? null\n const stockSummary = (variantIdsByProductId.get(productId) ?? []).map((variantId) =>\n buildStockSummaryItem(variantId, balanceByVariantId.get(variantId)),\n )\n const totalAvailable = stockSummary.reduce(\n (sum, item) => sum + Number(item.available),\n 0,\n )\n\n return {\n ...record,\n _wms: {\n inventoryProfile: profile ? toInventoryProfileSummary(profile) : null,\n stockSummary,\n reorderStatus: resolveReorderStatus(totalAvailable, profile),\n },\n }\n })\n },\n}\n\nconst catalogVariantInventoryEnricher: ResponseEnricher<CatalogVariantRecord, CatalogWmsEnrichment> = {\n id: 'wms.catalog-variant-inventory',\n targetEntity: E.catalog.catalog_product_variant,\n features: ['wms.view'],\n priority: 40,\n timeout: 2000,\n fallback: EMPTY_CATALOG_ENRICHMENT,\n // Reads inventory profiles and balances for the variant only.\n cache: {\n strategy: 'read-through',\n ttl: WMS_ENRICHER_CACHE_TTL_MS,\n tags: [WMS_INVENTORY_CACHE_TAG],\n },\n\n async enrichOne(record, context: EnricherScope) {\n return (await this.enrichMany!([record], context))[0]\n },\n\n async enrichMany(records, context: EnricherScope) {\n if (records.length === 0) return records as Array<CatalogVariantRecord & CatalogWmsEnrichment>\n\n const variantIds = records\n .map(extractRecordId)\n .filter((value): value is string => Boolean(value))\n\n if (variantIds.length === 0) {\n return records.map((record) => ({ ...record, ...EMPTY_CATALOG_ENRICHMENT }))\n }\n\n const em = context.em.fork()\n const scope = { organizationId: context.organizationId, tenantId: context.tenantId }\n\n const [profiles, balances] = await Promise.all([\n loadVariantInventoryProfiles(em, variantIds, scope),\n loadBalances(em, variantIds, scope),\n ])\n\n const profileByVariantId = new Map(\n profiles\n .filter((profile): profile is ProductInventoryProfile & { catalogVariantId: string } => typeof profile.catalogVariantId === 'string')\n .map((profile) => [profile.catalogVariantId, profile] as const),\n )\n const balanceByVariantId = buildBalanceSummaryByVariant(balances)\n\n return records.map((record) => {\n const variantId = extractRecordId(record)\n if (!variantId) {\n return { ...record, ...EMPTY_CATALOG_ENRICHMENT }\n }\n\n const profile = profileByVariantId.get(variantId) ?? null\n const stockSummary = [buildStockSummaryItem(variantId, balanceByVariantId.get(variantId))]\n\n return {\n ...record,\n _wms: {\n inventoryProfile: profile ? toInventoryProfileSummary(profile) : null,\n stockSummary,\n reorderStatus: resolveReorderStatus(Number(stockSummary[0]?.available ?? 0), profile),\n },\n }\n })\n },\n}\n\nexport const enrichers: ResponseEnricher[] = [\n salesOrderInventoryEnricher,\n catalogProductInventoryEnricher,\n catalogVariantInventoryEnricher,\n]\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,9 @@
1
+ const WMS_INVENTORY_CACHE_TAG = "wms:inventory";
2
+ const WMS_WAREHOUSE_CACHE_TAG = "wms:warehouse";
3
+ const WMS_ENRICHER_CACHE_TTL_MS = 3e4;
4
+ export {
5
+ WMS_ENRICHER_CACHE_TTL_MS,
6
+ WMS_INVENTORY_CACHE_TAG,
7
+ WMS_WAREHOUSE_CACHE_TAG
8
+ };
9
+ //# sourceMappingURL=enricherCacheTags.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/lib/enricherCacheTags.ts"],
4
+ "sourcesContent": ["/**\n * Cache tags shared by the WMS inventory enrichers and the write surfaces that\n * invalidate them. Read side and write side MUST use these constants so the two\n * cannot drift \u2014 a tag typo on either side is silent, and the only symptom is\n * inventory that stays stale until the TTL expires.\n *\n * The enricher runner adds `tenant:`, `organization:` and `enricher:` tags of\n * its own, and the cache service prefixes every tag with the current cache\n * tenant, so these are deliberately coarse collection tags rather than\n * per-warehouse ones: `ResponseEnricher.cache.tags` is a static array read from\n * the enricher definition and cannot vary per call. Collection tags\n * over-invalidate slightly; a per-warehouse scheme that the write side could\n * miss would under-invalidate, which is the failure that actually shows wrong\n * stock to a user.\n */\n\n/** Inventory quantities: balances, reservations, movements and profiles. */\nexport const WMS_INVENTORY_CACHE_TAG = 'wms:inventory'\n\n/** Warehouse identity and sales-order warehouse assignment. */\nexport const WMS_WAREHOUSE_CACHE_TAG = 'wms:warehouse'\n\n/**\n * Short by design. The tag invalidations below are the primary freshness\n * mechanism; this TTL is the unconditional backstop for anything they miss \u2014\n * a write that failed to resolve the cache service, a cross-process race, or a\n * write surface added later without wiring invalidation.\n */\nexport const WMS_ENRICHER_CACHE_TTL_MS = 30_000\n"],
5
+ "mappings": "AAiBO,MAAM,0BAA0B;AAGhC,MAAM,0BAA0B;AAQhC,MAAM,4BAA4B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,37 @@
1
+ import { runWithCacheTenant } from "@open-mercato/cache";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import {
4
+ WMS_INVENTORY_CACHE_TAG,
5
+ WMS_WAREHOUSE_CACHE_TAG
6
+ } from "./enricherCacheTags.js";
7
+ const logger = createLogger("wms").child({ component: "enricher-cache" });
8
+ function resolveCache(container) {
9
+ if (!container?.resolve) return null;
10
+ for (const name of ["cache", "cacheService"]) {
11
+ try {
12
+ const candidate = container.resolve(name);
13
+ if (candidate && typeof candidate.deleteByTags === "function") return candidate;
14
+ } catch {
15
+ }
16
+ }
17
+ return null;
18
+ }
19
+ function tagsForScope(scope) {
20
+ if (scope === "inventory") return [WMS_INVENTORY_CACHE_TAG];
21
+ if (scope === "warehouse") return [WMS_WAREHOUSE_CACHE_TAG];
22
+ return [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG];
23
+ }
24
+ async function invalidateWmsInventoryEnricherCache(container, tenantId, scope = "all") {
25
+ if (!tenantId) return;
26
+ const cache = resolveCache(container);
27
+ if (!cache) return;
28
+ try {
29
+ await runWithCacheTenant(tenantId, () => cache.deleteByTags(tagsForScope(scope)));
30
+ } catch (err) {
31
+ logger.warn("Failed to invalidate WMS inventory enricher cache", { tenantId, scope, err });
32
+ }
33
+ }
34
+ export {
35
+ invalidateWmsInventoryEnricherCache
36
+ };
37
+ //# sourceMappingURL=invalidateInventoryEnricherCache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/lib/invalidateInventoryEnricherCache.ts"],
4
+ "sourcesContent": ["// Drop the cached WMS inventory enrichment for a tenant after a write that\n// changed the data it was derived from.\n//\n// The three WMS inventory enrichers cache their cross-module reads read-through\n// with a short TTL (see `enricherCacheTags.ts`). Every write surface that moves\n// balances, reservations, movements, profiles, warehouses or the sales-order\n// warehouse assignment drops the matching tags here, so a user sees the new\n// stock on the next request rather than after the TTL.\n//\n// The cache service prefixes tags with the *current* cache tenant, which is an\n// AsyncLocalStorage value set on the request path. A subscriber or command side\n// effect running outside that scope would otherwise write its invalidation\n// against the `tenant:global:` prefix and silently miss every entry the request\n// path stored. `runWithCacheTenant` re-enters the right scope, mirroring\n// `directory/subscribers/invalidateOrgScopeCache.ts`.\n\nimport { runWithCacheTenant } from '@open-mercato/cache'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport {\n WMS_INVENTORY_CACHE_TAG,\n WMS_WAREHOUSE_CACHE_TAG,\n} from './enricherCacheTags'\n\nconst logger = createLogger('wms').child({ component: 'enricher-cache' })\n\ntype CacheService = {\n deleteByTags(tags: string[]): Promise<number>\n}\n\nexport type EnricherCacheScope = 'inventory' | 'warehouse' | 'all'\n\ntype Resolver = { resolve: <T = unknown>(name: string) => T }\n\nfunction resolveCache(container: Resolver | null | undefined): CacheService | null {\n if (!container?.resolve) return null\n for (const name of ['cache', 'cacheService']) {\n try {\n const candidate = container.resolve<CacheService>(name)\n if (candidate && typeof candidate.deleteByTags === 'function') return candidate\n } catch {\n // try the next registration name\n }\n }\n return null\n}\n\nfunction tagsForScope(scope: EnricherCacheScope): string[] {\n if (scope === 'inventory') return [WMS_INVENTORY_CACHE_TAG]\n if (scope === 'warehouse') return [WMS_WAREHOUSE_CACHE_TAG]\n return [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG]\n}\n\n/**\n * Best-effort tag invalidation. A failure is logged and swallowed: an\n * invalidation that throws must never fail the write that triggered it, and the\n * enricher TTL bounds how long a missed drop can serve stale data.\n */\nexport async function invalidateWmsInventoryEnricherCache(\n container: Resolver | null | undefined,\n tenantId: string | null | undefined,\n scope: EnricherCacheScope = 'all',\n): Promise<void> {\n if (!tenantId) return\n const cache = resolveCache(container)\n if (!cache) return\n try {\n await runWithCacheTenant(tenantId, () => cache.deleteByTags(tagsForScope(scope)))\n } catch (err) {\n logger.warn('Failed to invalidate WMS inventory enricher cache', { tenantId, scope, err })\n }\n}\n"],
5
+ "mappings": "AAgBA,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,MAAM,SAAS,aAAa,KAAK,EAAE,MAAM,EAAE,WAAW,iBAAiB,CAAC;AAUxE,SAAS,aAAa,WAA6D;AACjF,MAAI,CAAC,WAAW,QAAS,QAAO;AAChC,aAAW,QAAQ,CAAC,SAAS,cAAc,GAAG;AAC5C,QAAI;AACF,YAAM,YAAY,UAAU,QAAsB,IAAI;AACtD,UAAI,aAAa,OAAO,UAAU,iBAAiB,WAAY,QAAO;AAAA,IACxE,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAAqC;AACzD,MAAI,UAAU,YAAa,QAAO,CAAC,uBAAuB;AAC1D,MAAI,UAAU,YAAa,QAAO,CAAC,uBAAuB;AAC1D,SAAO,CAAC,yBAAyB,uBAAuB;AAC1D;AAOA,eAAsB,oCACpB,WACA,UACA,QAA4B,OACb;AACf,MAAI,CAAC,SAAU;AACf,QAAM,QAAQ,aAAa,SAAS;AACpC,MAAI,CAAC,MAAO;AACZ,MAAI;AACF,UAAM,mBAAmB,UAAU,MAAM,MAAM,aAAa,aAAa,KAAK,CAAC,CAAC;AAAA,EAClF,SAAS,KAAK;AACZ,WAAO,KAAK,qDAAqD,EAAE,UAAU,OAAO,IAAI,CAAC;AAAA,EAC3F;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import {
2
+ invalidateWmsInventoryEnricherCache
3
+ } from "./invalidateInventoryEnricherCache.js";
4
+ function createInventoryEnricherCacheHandler(scope) {
5
+ return async function handle(payload, ctx) {
6
+ const data = payload ?? {};
7
+ const tenantId = typeof data.tenantId === "string" && data.tenantId.length > 0 ? data.tenantId : ctx.tenantId ?? null;
8
+ await invalidateWmsInventoryEnricherCache(ctx, tenantId, scope);
9
+ };
10
+ }
11
+ export {
12
+ createInventoryEnricherCacheHandler
13
+ };
14
+ //# sourceMappingURL=inventoryEnricherCacheSubscriber.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/lib/inventoryEnricherCacheSubscriber.ts"],
4
+ "sourcesContent": ["// Shared handler behind the WMS inventory enricher-cache invalidation\n// subscribers. Each subscriber file binds it to one event pattern, because\n// `metadata.event` takes a single pattern and the matcher is single-segment \u2014\n// `wms.*` does NOT match `wms.inventory_balance.created`, so every event family\n// the enrichers read from is registered explicitly.\n//\n// It lives here rather than under `subscribers/` because the module generator\n// scans that directory recursively and treats every file it finds as a\n// subscriber.\n\nimport {\n invalidateWmsInventoryEnricherCache,\n type EnricherCacheScope,\n} from './invalidateInventoryEnricherCache'\n\ntype SubscriberContext = {\n resolve: <T = unknown>(name: string) => T\n tenantId?: string | null\n}\n\nexport function createInventoryEnricherCacheHandler(scope: EnricherCacheScope) {\n return async function handle(payload: unknown, ctx: SubscriberContext): Promise<void> {\n const data = (payload ?? {}) as Record<string, unknown>\n const tenantId =\n typeof data.tenantId === 'string' && data.tenantId.length > 0\n ? data.tenantId\n : ctx.tenantId ?? null\n await invalidateWmsInventoryEnricherCache(ctx, tenantId, scope)\n }\n}\n"],
5
+ "mappings": "AAUA;AAAA,EACE;AAAA,OAEK;AAOA,SAAS,oCAAoC,OAA2B;AAC7E,SAAO,eAAe,OAAO,SAAkB,KAAuC;AACpF,UAAM,OAAQ,WAAW,CAAC;AAC1B,UAAM,WACJ,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,SAAS,IACxD,KAAK,WACL,IAAI,YAAY;AACtB,UAAM,oCAAoC,KAAK,UAAU,KAAK;AAAA,EAChE;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.inventory_balance.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-balance"
6
+ };
7
+ var invalidate_enricher_cache_balance_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_balance_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-balance.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-balance.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.inventory_balance.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-balance',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,4CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.inventory.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-inventory"
6
+ };
7
+ var invalidate_enricher_cache_inventory_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_inventory_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-inventory.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-inventory.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.inventory.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-inventory',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,8CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.inventory_movement.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-movement"
6
+ };
7
+ var invalidate_enricher_cache_movement_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_movement_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-movement.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-movement.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.inventory_movement.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-movement',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,6CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.inventory_profile.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-profile"
6
+ };
7
+ var invalidate_enricher_cache_profile_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_profile_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-profile.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-profile.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.inventory_profile.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-profile',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,4CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.inventory_reservation.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-reservation"
6
+ };
7
+ var invalidate_enricher_cache_reservation_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_reservation_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-reservation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-reservation.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.inventory_reservation.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-reservation',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,gDAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "sales.line.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-sales-line"
6
+ };
7
+ var invalidate_enricher_cache_sales_line_default = createInventoryEnricherCacheHandler("warehouse");
8
+ export {
9
+ invalidate_enricher_cache_sales_line_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-sales-line.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-sales-line.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\n// The sales module declares `sales.line.*` in its event registry and documents\n// it publicly, but no write surface emits it today \u2014 line writes ride the order\n// commands' `sales.order.*`. Registering the pattern anyway costs one ephemeral\n// subscription and means that wiring those events later cannot silently\n// reintroduce the staleness `invalidate-enricher-cache-sales-order.ts` closes.\nexport const metadata = {\n event: 'sales.line.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-sales-line',\n}\n\nexport default createInventoryEnricherCacheHandler('warehouse')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAO7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,+CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "sales.order.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-sales-order"
6
+ };
7
+ var invalidate_enricher_cache_sales_order_default = createInventoryEnricherCacheHandler("warehouse");
8
+ export {
9
+ invalidate_enricher_cache_sales_order_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-sales-order.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-sales-order.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\n// `wms.sales-order-inventory` reads the order's lines, and both the required\n// quantity behind `reservationSummary.status` and the variant list behind\n// `stockSummary` come from them. Order lines are written inside the sales order\n// commands, which emit `sales.order.*` \u2014 not a WMS event \u2014 so without this the\n// enrichment would stay stale for the whole TTL after a line edit.\n//\n// Scope `warehouse` rather than `inventory`: `wms:warehouse` is declared by the\n// sales-order enricher alone, so this drops exactly its entries and leaves the\n// two catalog enrichers' caches intact.\nexport const metadata = {\n event: 'sales.order.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-sales-order',\n}\n\nexport default createInventoryEnricherCacheHandler('warehouse')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAW7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,gDAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "catalog.variant.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-variant"
6
+ };
7
+ var invalidate_enricher_cache_variant_default = createInventoryEnricherCacheHandler("inventory");
8
+ export {
9
+ invalidate_enricher_cache_variant_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-variant.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-variant.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'catalog.variant.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-variant',\n}\n\nexport default createInventoryEnricherCacheHandler('inventory')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,4CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
@@ -0,0 +1,12 @@
1
+ import { createInventoryEnricherCacheHandler } from "../lib/inventoryEnricherCacheSubscriber.js";
2
+ const metadata = {
3
+ event: "wms.warehouse.*",
4
+ persistent: false,
5
+ id: "wms:invalidate-enricher-cache-warehouse"
6
+ };
7
+ var invalidate_enricher_cache_warehouse_default = createInventoryEnricherCacheHandler("warehouse");
8
+ export {
9
+ invalidate_enricher_cache_warehouse_default as default,
10
+ metadata
11
+ };
12
+ //# sourceMappingURL=invalidate-enricher-cache-warehouse.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/wms/subscribers/invalidate-enricher-cache-warehouse.ts"],
4
+ "sourcesContent": ["import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'\n\nexport const metadata = {\n event: 'wms.warehouse.*',\n persistent: false,\n id: 'wms:invalidate-enricher-cache-warehouse',\n}\n\nexport default createInventoryEnricherCacheHandler('warehouse')\n"],
5
+ "mappings": "AAAA,SAAS,2CAA2C;AAE7C,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAEA,IAAO,8CAAQ,oCAAoC,WAAW;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.7.1-develop.7170.1.d95074d7ba",
3
+ "version": "0.7.1-develop.7172.1.a45dece080",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -228,22 +228,22 @@
228
228
  }
229
229
  },
230
230
  "dependencies": {
231
- "@mikro-orm/core": "^7.1.8",
232
- "@mikro-orm/decorators": "^7.1.8",
233
- "@mikro-orm/postgresql": "^7.1.8",
231
+ "@mikro-orm/core": "^7.1.14",
232
+ "@mikro-orm/decorators": "^7.1.14",
233
+ "@mikro-orm/postgresql": "^7.1.14",
234
234
  "@types/leaflet": "^1.9.12",
235
235
  "@types/leaflet.markercluster": "^1.5.5",
236
236
  "@types/sanitize-html": "^2.13.0",
237
- "@types/semver": "^7.5.8",
238
- "@xyflow/react": "^12.11.2",
239
- "ai": "^7.0.4",
237
+ "@types/semver": "^7.8.0",
238
+ "@xyflow/react": "^12.11.5",
239
+ "ai": "^7.0.83",
240
240
  "date-fns": "4.4.0",
241
241
  "date-fns-tz": "^3.2.0",
242
242
  "leaflet": "^1.9.4",
243
243
  "leaflet.markercluster": "^1.5.3",
244
244
  "mammoth": "^1.9.0",
245
245
  "pdfjs-dist": "^6.2.108",
246
- "resend": "^6.18.1",
246
+ "resend": "^6.24.0",
247
247
  "sanitize-html": "2.17.5",
248
248
  "semver": "^7.8.5",
249
249
  "sharp": "^0.35.3",
@@ -252,23 +252,23 @@
252
252
  "zod": "^4.4.3"
253
253
  },
254
254
  "peerDependencies": {
255
- "@open-mercato/ai-assistant": "0.7.1-develop.7170.1.d95074d7ba",
256
- "@open-mercato/shared": "0.7.1-develop.7170.1.d95074d7ba",
257
- "@open-mercato/ui": "0.7.1-develop.7170.1.d95074d7ba",
255
+ "@open-mercato/ai-assistant": "0.7.1-develop.7172.1.a45dece080",
256
+ "@open-mercato/shared": "0.7.1-develop.7172.1.a45dece080",
257
+ "@open-mercato/ui": "0.7.1-develop.7172.1.a45dece080",
258
258
  "react": "^19.0.0",
259
259
  "react-dom": "^19.0.0"
260
260
  },
261
261
  "devDependencies": {
262
- "@open-mercato/ai-assistant": "0.7.1-develop.7170.1.d95074d7ba",
263
- "@open-mercato/shared": "0.7.1-develop.7170.1.d95074d7ba",
264
- "@open-mercato/ui": "0.7.1-develop.7170.1.d95074d7ba",
262
+ "@open-mercato/ai-assistant": "0.7.1-develop.7172.1.a45dece080",
263
+ "@open-mercato/shared": "0.7.1-develop.7172.1.a45dece080",
264
+ "@open-mercato/ui": "0.7.1-develop.7172.1.a45dece080",
265
265
  "@testing-library/dom": "^10.4.1",
266
- "@testing-library/jest-dom": "^7.0.0",
267
- "@testing-library/react": "^16.3.1",
266
+ "@testing-library/jest-dom": "^7.0.1",
267
+ "@testing-library/react": "^16.3.3",
268
268
  "@types/chance": "^1.1.8",
269
269
  "@types/jest": "^30.0.0",
270
- "@types/react": "^19.2.17",
271
- "@types/react-dom": "^19.2.3",
270
+ "@types/react": "^19.2.18",
271
+ "@types/react-dom": "^19.2.5",
272
272
  "chance": "^1.1.13",
273
273
  "jest": "^30.4.2",
274
274
  "jest-environment-jsdom": "^30.4.1",
@@ -10,6 +10,7 @@ import { SalesOrderWarehouseAssignment, Warehouse } from '../data/entities'
10
10
  import type { SalesOrderWarehouseAssignInput, SalesOrderWarehouseUnassignInput } from '../data/validators'
11
11
  import { z } from 'zod'
12
12
  import { reserveInventoryForConfirmedOrder } from '../lib/salesOrderInventoryAutomation'
13
+ import { invalidateWmsInventoryEnricherCache } from '../lib/invalidateInventoryEnricherCache'
13
14
  import { ensureOrganizationScope, ensureTenantScope } from './shared'
14
15
 
15
16
  type AssignmentSnapshot = {
@@ -30,6 +31,17 @@ type UnassignWarehouseUndoPayload = {
30
31
  before: AssignmentSnapshot | null
31
32
  }
32
33
 
34
+ // These commands emit no WMS event, so the enricher-cache subscribers never see
35
+ // them. `wms.sales-order-inventory` surfaces the assigned warehouse, so every
36
+ // path here that writes an assignment — including the undo handlers — drops the
37
+ // warehouse tag itself.
38
+ async function invalidateAssignmentEnricherCache(
39
+ ctx: CommandRuntimeContext,
40
+ tenantId: string | null | undefined,
41
+ ): Promise<void> {
42
+ await invalidateWmsInventoryEnricherCache(ctx.container, tenantId, 'warehouse')
43
+ }
44
+
33
45
  function resolveScope(
34
46
  ctx: CommandRuntimeContext,
35
47
  fallback?: { tenantId?: string | null; organizationId?: string | null },
@@ -125,6 +137,7 @@ const assignWarehouseHandler: CommandHandler<
125
137
  existing.assignedBy = ctx.auth?.sub ?? null
126
138
  existing.updatedAt = new Date()
127
139
  await em.flush()
140
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
128
141
  return { assignmentId: existing.id, warehouseId: warehouse.id }
129
142
  }
130
143
 
@@ -138,6 +151,7 @@ const assignWarehouseHandler: CommandHandler<
138
151
  })
139
152
  em.persist(assignment)
140
153
  await em.flush()
154
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
141
155
  return { assignmentId: assignment.id, warehouseId: warehouse.id }
142
156
  },
143
157
 
@@ -187,6 +201,7 @@ const assignWarehouseHandler: CommandHandler<
187
201
  if (!before) {
188
202
  assignment.deletedAt = new Date()
189
203
  await em.flush()
204
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
190
205
  return
191
206
  }
192
207
 
@@ -205,12 +220,14 @@ const assignWarehouseHandler: CommandHandler<
205
220
  if (!previousWarehouse) {
206
221
  assignment.deletedAt = new Date()
207
222
  await em.flush()
223
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
208
224
  return
209
225
  }
210
226
 
211
227
  assignment.warehouse = previousWarehouse
212
228
  assignment.notes = before.notes ?? null
213
229
  await em.flush()
230
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
214
231
  }
215
232
  },
216
233
  }
@@ -239,6 +256,7 @@ const unassignWarehouseHandler: CommandHandler<
239
256
  if (existing) {
240
257
  existing.deletedAt = new Date()
241
258
  await em.flush()
259
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
242
260
  }
243
261
 
244
262
  return { ok: true as const }
@@ -278,6 +296,7 @@ const unassignWarehouseHandler: CommandHandler<
278
296
  if (existingDeleted) {
279
297
  existingDeleted.deletedAt = null
280
298
  await em.flush()
299
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
281
300
  return
282
301
  }
283
302
 
@@ -304,6 +323,7 @@ const unassignWarehouseHandler: CommandHandler<
304
323
  })
305
324
  em.persist(restored)
306
325
  await em.flush()
326
+ await invalidateAssignmentEnricherCache(ctx, scope.tenantId)
307
327
  },
308
328
  }
309
329
 
@@ -6,6 +6,11 @@ import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
6
6
  import { E } from '#generated/entities.ids.generated'
7
7
  import { InventoryBalance, InventoryReservation, ProductInventoryProfile, SalesOrderWarehouseAssignment, Warehouse } from './entities'
8
8
  import { resolvePrimaryWarehouseId } from '../lib/primaryWarehousePolicy'
9
+ import {
10
+ WMS_ENRICHER_CACHE_TTL_MS,
11
+ WMS_INVENTORY_CACHE_TAG,
12
+ WMS_WAREHOUSE_CACHE_TAG,
13
+ } from '../lib/enricherCacheTags'
9
14
 
10
15
  type SalesOrderRecord = Record<string, unknown> & { id?: string }
11
16
  type CatalogProductRecord = Record<string, unknown> & { id?: string }
@@ -419,6 +424,13 @@ const salesOrderInventoryEnricher: ResponseEnricher<SalesOrderRecord, SalesOrder
419
424
  priority: 40,
420
425
  timeout: 2000,
421
426
  fallback: EMPTY_ENRICHMENT,
427
+ // Reads reservations, balances, the primary warehouse and the explicit
428
+ // assignment, so it depends on both the inventory and the warehouse tags.
429
+ cache: {
430
+ strategy: 'read-through',
431
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
432
+ tags: [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG],
433
+ },
422
434
 
423
435
  async enrichOne(record, context: EnricherScope) {
424
436
  return (await this.enrichMany!([record], context))[0]
@@ -583,6 +595,13 @@ const catalogProductInventoryEnricher: ResponseEnricher<CatalogProductRecord, Ca
583
595
  priority: 40,
584
596
  timeout: 2000,
585
597
  fallback: EMPTY_CATALOG_ENRICHMENT,
598
+ // Reads inventory profiles and balances; also reads catalog variants, so the
599
+ // catalog variant events invalidate the inventory tag too.
600
+ cache: {
601
+ strategy: 'read-through',
602
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
603
+ tags: [WMS_INVENTORY_CACHE_TAG],
604
+ },
586
605
 
587
606
  async enrichOne(record, context: EnricherScope) {
588
607
  return (await this.enrichMany!([record], context))[0]
@@ -659,6 +678,12 @@ const catalogVariantInventoryEnricher: ResponseEnricher<CatalogVariantRecord, Ca
659
678
  priority: 40,
660
679
  timeout: 2000,
661
680
  fallback: EMPTY_CATALOG_ENRICHMENT,
681
+ // Reads inventory profiles and balances for the variant only.
682
+ cache: {
683
+ strategy: 'read-through',
684
+ ttl: WMS_ENRICHER_CACHE_TTL_MS,
685
+ tags: [WMS_INVENTORY_CACHE_TAG],
686
+ },
662
687
 
663
688
  async enrichOne(record, context: EnricherScope) {
664
689
  return (await this.enrichMany!([record], context))[0]
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Cache tags shared by the WMS inventory enrichers and the write surfaces that
3
+ * invalidate them. Read side and write side MUST use these constants so the two
4
+ * cannot drift — a tag typo on either side is silent, and the only symptom is
5
+ * inventory that stays stale until the TTL expires.
6
+ *
7
+ * The enricher runner adds `tenant:`, `organization:` and `enricher:` tags of
8
+ * its own, and the cache service prefixes every tag with the current cache
9
+ * tenant, so these are deliberately coarse collection tags rather than
10
+ * per-warehouse ones: `ResponseEnricher.cache.tags` is a static array read from
11
+ * the enricher definition and cannot vary per call. Collection tags
12
+ * over-invalidate slightly; a per-warehouse scheme that the write side could
13
+ * miss would under-invalidate, which is the failure that actually shows wrong
14
+ * stock to a user.
15
+ */
16
+
17
+ /** Inventory quantities: balances, reservations, movements and profiles. */
18
+ export const WMS_INVENTORY_CACHE_TAG = 'wms:inventory'
19
+
20
+ /** Warehouse identity and sales-order warehouse assignment. */
21
+ export const WMS_WAREHOUSE_CACHE_TAG = 'wms:warehouse'
22
+
23
+ /**
24
+ * Short by design. The tag invalidations below are the primary freshness
25
+ * mechanism; this TTL is the unconditional backstop for anything they miss —
26
+ * a write that failed to resolve the cache service, a cross-process race, or a
27
+ * write surface added later without wiring invalidation.
28
+ */
29
+ export const WMS_ENRICHER_CACHE_TTL_MS = 30_000
@@ -0,0 +1,71 @@
1
+ // Drop the cached WMS inventory enrichment for a tenant after a write that
2
+ // changed the data it was derived from.
3
+ //
4
+ // The three WMS inventory enrichers cache their cross-module reads read-through
5
+ // with a short TTL (see `enricherCacheTags.ts`). Every write surface that moves
6
+ // balances, reservations, movements, profiles, warehouses or the sales-order
7
+ // warehouse assignment drops the matching tags here, so a user sees the new
8
+ // stock on the next request rather than after the TTL.
9
+ //
10
+ // The cache service prefixes tags with the *current* cache tenant, which is an
11
+ // AsyncLocalStorage value set on the request path. A subscriber or command side
12
+ // effect running outside that scope would otherwise write its invalidation
13
+ // against the `tenant:global:` prefix and silently miss every entry the request
14
+ // path stored. `runWithCacheTenant` re-enters the right scope, mirroring
15
+ // `directory/subscribers/invalidateOrgScopeCache.ts`.
16
+
17
+ import { runWithCacheTenant } from '@open-mercato/cache'
18
+ import { createLogger } from '@open-mercato/shared/lib/logger'
19
+ import {
20
+ WMS_INVENTORY_CACHE_TAG,
21
+ WMS_WAREHOUSE_CACHE_TAG,
22
+ } from './enricherCacheTags'
23
+
24
+ const logger = createLogger('wms').child({ component: 'enricher-cache' })
25
+
26
+ type CacheService = {
27
+ deleteByTags(tags: string[]): Promise<number>
28
+ }
29
+
30
+ export type EnricherCacheScope = 'inventory' | 'warehouse' | 'all'
31
+
32
+ type Resolver = { resolve: <T = unknown>(name: string) => T }
33
+
34
+ function resolveCache(container: Resolver | null | undefined): CacheService | null {
35
+ if (!container?.resolve) return null
36
+ for (const name of ['cache', 'cacheService']) {
37
+ try {
38
+ const candidate = container.resolve<CacheService>(name)
39
+ if (candidate && typeof candidate.deleteByTags === 'function') return candidate
40
+ } catch {
41
+ // try the next registration name
42
+ }
43
+ }
44
+ return null
45
+ }
46
+
47
+ function tagsForScope(scope: EnricherCacheScope): string[] {
48
+ if (scope === 'inventory') return [WMS_INVENTORY_CACHE_TAG]
49
+ if (scope === 'warehouse') return [WMS_WAREHOUSE_CACHE_TAG]
50
+ return [WMS_INVENTORY_CACHE_TAG, WMS_WAREHOUSE_CACHE_TAG]
51
+ }
52
+
53
+ /**
54
+ * Best-effort tag invalidation. A failure is logged and swallowed: an
55
+ * invalidation that throws must never fail the write that triggered it, and the
56
+ * enricher TTL bounds how long a missed drop can serve stale data.
57
+ */
58
+ export async function invalidateWmsInventoryEnricherCache(
59
+ container: Resolver | null | undefined,
60
+ tenantId: string | null | undefined,
61
+ scope: EnricherCacheScope = 'all',
62
+ ): Promise<void> {
63
+ if (!tenantId) return
64
+ const cache = resolveCache(container)
65
+ if (!cache) return
66
+ try {
67
+ await runWithCacheTenant(tenantId, () => cache.deleteByTags(tagsForScope(scope)))
68
+ } catch (err) {
69
+ logger.warn('Failed to invalidate WMS inventory enricher cache', { tenantId, scope, err })
70
+ }
71
+ }
@@ -0,0 +1,30 @@
1
+ // Shared handler behind the WMS inventory enricher-cache invalidation
2
+ // subscribers. Each subscriber file binds it to one event pattern, because
3
+ // `metadata.event` takes a single pattern and the matcher is single-segment —
4
+ // `wms.*` does NOT match `wms.inventory_balance.created`, so every event family
5
+ // the enrichers read from is registered explicitly.
6
+ //
7
+ // It lives here rather than under `subscribers/` because the module generator
8
+ // scans that directory recursively and treats every file it finds as a
9
+ // subscriber.
10
+
11
+ import {
12
+ invalidateWmsInventoryEnricherCache,
13
+ type EnricherCacheScope,
14
+ } from './invalidateInventoryEnricherCache'
15
+
16
+ type SubscriberContext = {
17
+ resolve: <T = unknown>(name: string) => T
18
+ tenantId?: string | null
19
+ }
20
+
21
+ export function createInventoryEnricherCacheHandler(scope: EnricherCacheScope) {
22
+ return async function handle(payload: unknown, ctx: SubscriberContext): Promise<void> {
23
+ const data = (payload ?? {}) as Record<string, unknown>
24
+ const tenantId =
25
+ typeof data.tenantId === 'string' && data.tenantId.length > 0
26
+ ? data.tenantId
27
+ : ctx.tenantId ?? null
28
+ await invalidateWmsInventoryEnricherCache(ctx, tenantId, scope)
29
+ }
30
+ }
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.inventory_balance.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-balance',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.inventory.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-inventory',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.inventory_movement.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-movement',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.inventory_profile.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-profile',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.inventory_reservation.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-reservation',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,14 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ // The sales module declares `sales.line.*` in its event registry and documents
4
+ // it publicly, but no write surface emits it today — line writes ride the order
5
+ // commands' `sales.order.*`. Registering the pattern anyway costs one ephemeral
6
+ // subscription and means that wiring those events later cannot silently
7
+ // reintroduce the staleness `invalidate-enricher-cache-sales-order.ts` closes.
8
+ export const metadata = {
9
+ event: 'sales.line.*',
10
+ persistent: false,
11
+ id: 'wms:invalidate-enricher-cache-sales-line',
12
+ }
13
+
14
+ export default createInventoryEnricherCacheHandler('warehouse')
@@ -0,0 +1,18 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ // `wms.sales-order-inventory` reads the order's lines, and both the required
4
+ // quantity behind `reservationSummary.status` and the variant list behind
5
+ // `stockSummary` come from them. Order lines are written inside the sales order
6
+ // commands, which emit `sales.order.*` — not a WMS event — so without this the
7
+ // enrichment would stay stale for the whole TTL after a line edit.
8
+ //
9
+ // Scope `warehouse` rather than `inventory`: `wms:warehouse` is declared by the
10
+ // sales-order enricher alone, so this drops exactly its entries and leaves the
11
+ // two catalog enrichers' caches intact.
12
+ export const metadata = {
13
+ event: 'sales.order.*',
14
+ persistent: false,
15
+ id: 'wms:invalidate-enricher-cache-sales-order',
16
+ }
17
+
18
+ export default createInventoryEnricherCacheHandler('warehouse')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'catalog.variant.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-variant',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('inventory')
@@ -0,0 +1,9 @@
1
+ import { createInventoryEnricherCacheHandler } from '../lib/inventoryEnricherCacheSubscriber'
2
+
3
+ export const metadata = {
4
+ event: 'wms.warehouse.*',
5
+ persistent: false,
6
+ id: 'wms:invalidate-enricher-cache-warehouse',
7
+ }
8
+
9
+ export default createInventoryEnricherCacheHandler('warehouse')