@endora-commerce/mod-inventory 0.100.0

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 (223) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/admin/api/warehouses-client.d.ts +22 -0
  4. package/dist/admin/api/warehouses-client.d.ts.map +1 -0
  5. package/dist/admin/api/warehouses-client.js +32 -0
  6. package/dist/admin/api/warehouses-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +43 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +190 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/AvailabilityNotificationsPage.d.ts +15 -0
  12. package/dist/admin/pages/AvailabilityNotificationsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/AvailabilityNotificationsPage.js +111 -0
  14. package/dist/admin/pages/AvailabilityNotificationsPage.js.map +1 -0
  15. package/dist/admin/pages/InventoryPage.d.ts +17 -0
  16. package/dist/admin/pages/InventoryPage.d.ts.map +1 -0
  17. package/dist/admin/pages/InventoryPage.js +135 -0
  18. package/dist/admin/pages/InventoryPage.js.map +1 -0
  19. package/dist/admin/pages/LowStockPage.d.ts +16 -0
  20. package/dist/admin/pages/LowStockPage.d.ts.map +1 -0
  21. package/dist/admin/pages/LowStockPage.js +61 -0
  22. package/dist/admin/pages/LowStockPage.js.map +1 -0
  23. package/dist/admin/pages/StockImportWizard.d.ts +17 -0
  24. package/dist/admin/pages/StockImportWizard.d.ts.map +1 -0
  25. package/dist/admin/pages/StockImportWizard.js +111 -0
  26. package/dist/admin/pages/StockImportWizard.js.map +1 -0
  27. package/dist/admin/pages/WarehouseEditor.d.ts +12 -0
  28. package/dist/admin/pages/WarehouseEditor.d.ts.map +1 -0
  29. package/dist/admin/pages/WarehouseEditor.js +194 -0
  30. package/dist/admin/pages/WarehouseEditor.js.map +1 -0
  31. package/dist/admin/pages/WarehousesList.d.ts +12 -0
  32. package/dist/admin/pages/WarehousesList.d.ts.map +1 -0
  33. package/dist/admin/pages/WarehousesList.js +69 -0
  34. package/dist/admin/pages/WarehousesList.js.map +1 -0
  35. package/dist/admin/zones/ChannelWarehouses.d.ts +49 -0
  36. package/dist/admin/zones/ChannelWarehouses.d.ts.map +1 -0
  37. package/dist/admin/zones/ChannelWarehouses.js +109 -0
  38. package/dist/admin/zones/ChannelWarehouses.js.map +1 -0
  39. package/dist/backend/demo/reset.d.ts +18 -0
  40. package/dist/backend/demo/reset.d.ts.map +1 -0
  41. package/dist/backend/demo/reset.js +20 -0
  42. package/dist/backend/demo/reset.js.map +1 -0
  43. package/dist/backend/demo/rows.d.ts +38 -0
  44. package/dist/backend/demo/rows.d.ts.map +1 -0
  45. package/dist/backend/demo/rows.js +38 -0
  46. package/dist/backend/demo/rows.js.map +1 -0
  47. package/dist/backend/demo/seed.d.ts +26 -0
  48. package/dist/backend/demo/seed.d.ts.map +1 -0
  49. package/dist/backend/demo/seed.js +65 -0
  50. package/dist/backend/demo/seed.js.map +1 -0
  51. package/dist/backend/email-templates/transactional-defaults.d.ts +11 -0
  52. package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
  53. package/dist/backend/email-templates/transactional-defaults.js +49 -0
  54. package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
  55. package/dist/backend/entities/availability-notification.entity.d.ts +49 -0
  56. package/dist/backend/entities/availability-notification.entity.d.ts.map +1 -0
  57. package/dist/backend/entities/availability-notification.entity.js +93 -0
  58. package/dist/backend/entities/availability-notification.entity.js.map +1 -0
  59. package/dist/backend/entities/inventory-threshold.entity.d.ts +24 -0
  60. package/dist/backend/entities/inventory-threshold.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/inventory-threshold.entity.js +61 -0
  62. package/dist/backend/entities/inventory-threshold.entity.js.map +1 -0
  63. package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.d.ts +15 -0
  64. package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.d.ts.map +1 -0
  65. package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.js +51 -0
  66. package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.js.map +1 -0
  67. package/dist/backend/entities/stock-allocation.entity.d.ts +22 -0
  68. package/dist/backend/entities/stock-allocation.entity.d.ts.map +1 -0
  69. package/dist/backend/entities/stock-allocation.entity.js +69 -0
  70. package/dist/backend/entities/stock-allocation.entity.js.map +1 -0
  71. package/dist/backend/entities/stock-level.entity.d.ts +24 -0
  72. package/dist/backend/entities/stock-level.entity.d.ts.map +1 -0
  73. package/dist/backend/entities/stock-level.entity.js +74 -0
  74. package/dist/backend/entities/stock-level.entity.js.map +1 -0
  75. package/dist/backend/entities/warehouse-channel-assignment.entity.d.ts +17 -0
  76. package/dist/backend/entities/warehouse-channel-assignment.entity.d.ts.map +1 -0
  77. package/dist/backend/entities/warehouse-channel-assignment.entity.js +60 -0
  78. package/dist/backend/entities/warehouse-channel-assignment.entity.js.map +1 -0
  79. package/dist/backend/entities/warehouse.entity.d.ts +37 -0
  80. package/dist/backend/entities/warehouse.entity.d.ts.map +1 -0
  81. package/dist/backend/entities/warehouse.entity.js +90 -0
  82. package/dist/backend/entities/warehouse.entity.js.map +1 -0
  83. package/dist/backend/index.d.ts +139 -0
  84. package/dist/backend/index.d.ts.map +1 -0
  85. package/dist/backend/index.js +351 -0
  86. package/dist/backend/index.js.map +1 -0
  87. package/dist/backend/plugin.d.ts +113 -0
  88. package/dist/backend/plugin.d.ts.map +1 -0
  89. package/dist/backend/plugin.js +75 -0
  90. package/dist/backend/plugin.js.map +1 -0
  91. package/dist/backend/prompt-tools.d.ts +23 -0
  92. package/dist/backend/prompt-tools.d.ts.map +1 -0
  93. package/dist/backend/prompt-tools.js +77 -0
  94. package/dist/backend/prompt-tools.js.map +1 -0
  95. package/dist/backend/routes.admin.d.ts +62 -0
  96. package/dist/backend/routes.admin.d.ts.map +1 -0
  97. package/dist/backend/routes.admin.js +383 -0
  98. package/dist/backend/routes.admin.js.map +1 -0
  99. package/dist/backend/routes.d.ts +40 -0
  100. package/dist/backend/routes.d.ts.map +1 -0
  101. package/dist/backend/routes.js +263 -0
  102. package/dist/backend/routes.js.map +1 -0
  103. package/dist/backend/services/audit-references.d.ts +15 -0
  104. package/dist/backend/services/audit-references.d.ts.map +1 -0
  105. package/dist/backend/services/audit-references.js +27 -0
  106. package/dist/backend/services/audit-references.js.map +1 -0
  107. package/dist/backend/services/availability-notification-service.d.ts +155 -0
  108. package/dist/backend/services/availability-notification-service.d.ts.map +1 -0
  109. package/dist/backend/services/availability-notification-service.js +363 -0
  110. package/dist/backend/services/availability-notification-service.js.map +1 -0
  111. package/dist/backend/services/availability-worker.d.ts +57 -0
  112. package/dist/backend/services/availability-worker.d.ts.map +1 -0
  113. package/dist/backend/services/availability-worker.js +135 -0
  114. package/dist/backend/services/availability-worker.js.map +1 -0
  115. package/dist/backend/services/csv-stock-importer.d.ts +52 -0
  116. package/dist/backend/services/csv-stock-importer.d.ts.map +1 -0
  117. package/dist/backend/services/csv-stock-importer.js +154 -0
  118. package/dist/backend/services/csv-stock-importer.js.map +1 -0
  119. package/dist/backend/services/display-band-resolver.d.ts +19 -0
  120. package/dist/backend/services/display-band-resolver.d.ts.map +1 -0
  121. package/dist/backend/services/display-band-resolver.js +21 -0
  122. package/dist/backend/services/display-band-resolver.js.map +1 -0
  123. package/dist/backend/services/effective-fulfilment-strategy.d.ts +16 -0
  124. package/dist/backend/services/effective-fulfilment-strategy.d.ts.map +1 -0
  125. package/dist/backend/services/effective-fulfilment-strategy.js +16 -0
  126. package/dist/backend/services/effective-fulfilment-strategy.js.map +1 -0
  127. package/dist/backend/services/fulfilment-strategy-resolver.d.ts +13 -0
  128. package/dist/backend/services/fulfilment-strategy-resolver.d.ts.map +1 -0
  129. package/dist/backend/services/fulfilment-strategy-resolver.js +13 -0
  130. package/dist/backend/services/fulfilment-strategy-resolver.js.map +1 -0
  131. package/dist/backend/services/inventory-read-port.d.ts +65 -0
  132. package/dist/backend/services/inventory-read-port.d.ts.map +1 -0
  133. package/dist/backend/services/inventory-read-port.js +168 -0
  134. package/dist/backend/services/inventory-read-port.js.map +1 -0
  135. package/dist/backend/services/inventory-reservation-apply-port.d.ts +62 -0
  136. package/dist/backend/services/inventory-reservation-apply-port.d.ts.map +1 -0
  137. package/dist/backend/services/inventory-reservation-apply-port.js +134 -0
  138. package/dist/backend/services/inventory-reservation-apply-port.js.map +1 -0
  139. package/dist/backend/services/low-stock-alert-service.d.ts +87 -0
  140. package/dist/backend/services/low-stock-alert-service.d.ts.map +1 -0
  141. package/dist/backend/services/low-stock-alert-service.js +164 -0
  142. package/dist/backend/services/low-stock-alert-service.js.map +1 -0
  143. package/dist/backend/services/product-threshold-write.service.d.ts +38 -0
  144. package/dist/backend/services/product-threshold-write.service.d.ts.map +1 -0
  145. package/dist/backend/services/product-threshold-write.service.js +80 -0
  146. package/dist/backend/services/product-threshold-write.service.js.map +1 -0
  147. package/dist/backend/services/stock-import.service.d.ts +43 -0
  148. package/dist/backend/services/stock-import.service.d.ts.map +1 -0
  149. package/dist/backend/services/stock-import.service.js +124 -0
  150. package/dist/backend/services/stock-import.service.js.map +1 -0
  151. package/dist/backend/services/stock-level-service.d.ts +146 -0
  152. package/dist/backend/services/stock-level-service.d.ts.map +1 -0
  153. package/dist/backend/services/stock-level-service.js +489 -0
  154. package/dist/backend/services/stock-level-service.js.map +1 -0
  155. package/dist/backend/services/threshold-admin-service.d.ts +73 -0
  156. package/dist/backend/services/threshold-admin-service.d.ts.map +1 -0
  157. package/dist/backend/services/threshold-admin-service.js +167 -0
  158. package/dist/backend/services/threshold-admin-service.js.map +1 -0
  159. package/dist/backend/services/threshold-resolver.d.ts +23 -0
  160. package/dist/backend/services/threshold-resolver.d.ts.map +1 -0
  161. package/dist/backend/services/threshold-resolver.js +29 -0
  162. package/dist/backend/services/threshold-resolver.js.map +1 -0
  163. package/dist/backend/services/threshold-settings-mirror.d.ts +55 -0
  164. package/dist/backend/services/threshold-settings-mirror.d.ts.map +1 -0
  165. package/dist/backend/services/threshold-settings-mirror.js +84 -0
  166. package/dist/backend/services/threshold-settings-mirror.js.map +1 -0
  167. package/dist/backend/services/warehouse-channel-reconciler.d.ts +19 -0
  168. package/dist/backend/services/warehouse-channel-reconciler.d.ts.map +1 -0
  169. package/dist/backend/services/warehouse-channel-reconciler.js +65 -0
  170. package/dist/backend/services/warehouse-channel-reconciler.js.map +1 -0
  171. package/dist/backend/services/warehouse-channel-service.d.ts +57 -0
  172. package/dist/backend/services/warehouse-channel-service.d.ts.map +1 -0
  173. package/dist/backend/services/warehouse-channel-service.js +205 -0
  174. package/dist/backend/services/warehouse-channel-service.js.map +1 -0
  175. package/dist/backend/services/warehouse-country-reference.d.ts +14 -0
  176. package/dist/backend/services/warehouse-country-reference.d.ts.map +1 -0
  177. package/dist/backend/services/warehouse-country-reference.js +27 -0
  178. package/dist/backend/services/warehouse-country-reference.js.map +1 -0
  179. package/dist/backend/services/warehouse-service.d.ts +86 -0
  180. package/dist/backend/services/warehouse-service.d.ts.map +1 -0
  181. package/dist/backend/services/warehouse-service.js +267 -0
  182. package/dist/backend/services/warehouse-service.js.map +1 -0
  183. package/dist/manifest.d.ts +219 -0
  184. package/dist/manifest.d.ts.map +1 -0
  185. package/dist/manifest.js +377 -0
  186. package/dist/manifest.js.map +1 -0
  187. package/dist/migrations/20260503T182812_inventory_workflow.d.ts +39 -0
  188. package/dist/migrations/20260503T182812_inventory_workflow.d.ts.map +1 -0
  189. package/dist/migrations/20260503T182812_inventory_workflow.js +205 -0
  190. package/dist/migrations/20260503T182812_inventory_workflow.js.map +1 -0
  191. package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.d.ts +14 -0
  192. package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.d.ts.map +1 -0
  193. package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.js +18 -0
  194. package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.js.map +1 -0
  195. package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.d.ts +28 -0
  196. package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.d.ts.map +1 -0
  197. package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.js +48 -0
  198. package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.js.map +1 -0
  199. package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.d.ts +44 -0
  200. package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.d.ts.map +1 -0
  201. package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.js +81 -0
  202. package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.js.map +1 -0
  203. package/dist/migrations/20260830T182139_inventory_organization_attribution.d.ts +109 -0
  204. package/dist/migrations/20260830T182139_inventory_organization_attribution.d.ts.map +1 -0
  205. package/dist/migrations/20260830T182139_inventory_organization_attribution.js +178 -0
  206. package/dist/migrations/20260830T182139_inventory_organization_attribution.js.map +1 -0
  207. package/dist/migrations/20260912T125716_inventory_organization_warehouses.d.ts +34 -0
  208. package/dist/migrations/20260912T125716_inventory_organization_warehouses.d.ts.map +1 -0
  209. package/dist/migrations/20260912T125716_inventory_organization_warehouses.js +50 -0
  210. package/dist/migrations/20260912T125716_inventory_organization_warehouses.js.map +1 -0
  211. package/dist/migrations/index.d.ts +32 -0
  212. package/dist/migrations/index.d.ts.map +1 -0
  213. package/dist/migrations/index.js +39 -0
  214. package/dist/migrations/index.js.map +1 -0
  215. package/dist/ports/index.d.ts +183 -0
  216. package/dist/ports/index.d.ts.map +1 -0
  217. package/dist/ports/index.js +2 -0
  218. package/dist/ports/index.js.map +1 -0
  219. package/docs/inventory.md +177 -0
  220. package/i18n/en.json +32 -0
  221. package/i18n/pl.json +32 -0
  222. package/package.json +105 -0
  223. package/tailwind.css +14 -0
@@ -0,0 +1,77 @@
1
+ import { SearchWarehousesParamsSchema, SetStockLevelParamsSchema, } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { StockLevel } from './entities/stock-level.entity.js';
4
+ import { Warehouse } from './entities/warehouse.entity.js';
5
+ import { StockLevelService } from './services/stock-level-service.js';
6
+ import { WarehouseService } from './services/warehouse-service.js';
7
+ const RESULT_LIMIT = 20;
8
+ function label(name) {
9
+ return name['en'] ?? Object.values(name)[0] ?? '(unnamed)';
10
+ }
11
+ export function inventoryPromptTools(deps) {
12
+ const warehouseService = new WarehouseService(deps.emFactory);
13
+ const stockLevelService = new StockLevelService(deps.emFactory, deps.catalogProducts, deps.catalogCategories, deps.eventBus, deps.auditLogService);
14
+ const searchWarehouses = {
15
+ id: 'inventory.search_warehouses',
16
+ moduleId: 'inventory',
17
+ kind: 'resolver',
18
+ description: 'Search warehouses by a name or code fragment (case-insensitive). Returns up to 20 matches as {id, label, code, active}.',
19
+ requiredPermission: 'catalog:read',
20
+ paramsSchema: SearchWarehousesParamsSchema,
21
+ execute: async (params) => {
22
+ const { items } = await warehouseService.list({ pageSize: 200 });
23
+ const needle = params.q.toLowerCase();
24
+ return items
25
+ .filter((w) => w.name.toLowerCase().includes(needle) || w.code.toLowerCase().includes(needle))
26
+ .slice(0, RESULT_LIMIT)
27
+ .map((w) => ({ id: w.id, label: w.name, code: w.code, active: w.active }));
28
+ },
29
+ };
30
+ const setStockLevel = {
31
+ id: 'inventory.set_stock_level',
32
+ moduleId: 'inventory',
33
+ kind: 'mutation',
34
+ description: 'Set the ABSOLUTE on-hand stock quantity of a product in a warehouse. Resolve the product via catalog.search_products and the warehouse via inventory.search_warehouses first. This is captured into a plan the operator must confirm; it is not executed immediately.',
35
+ requiredPermission: 'catalog:write',
36
+ paramsSchema: SetStockLevelParamsSchema,
37
+ preview: async (params) => {
38
+ // D-75 — this module's own fork, not the caller's manager. A preview
39
+ // reads committed rows only, so a fresh fork sees exactly what the
40
+ // request's manager saw.
41
+ const em = deps.emFactory();
42
+ const product = await deps.catalogProducts.findById(params.productId);
43
+ if (!product)
44
+ throw new HttpError(404, 'PRODUCT_NOT_FOUND', 'Product not found.');
45
+ const warehouse = await em.findOne(Warehouse, { id: params.warehouseId });
46
+ if (!warehouse)
47
+ throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found.');
48
+ const level = await em.findOne(StockLevel, {
49
+ productId: params.productId,
50
+ warehouseId: params.warehouseId,
51
+ variantId: null,
52
+ });
53
+ const current = level?.onHand ?? 0;
54
+ return {
55
+ headline: `Set stock of "${label(product.name)}" in warehouse "${warehouse.name}" to ${params.quantity}`,
56
+ current: { onHand: current },
57
+ affectedCount: 1,
58
+ sample: [{ id: product.id, label: label(product.name) }],
59
+ };
60
+ },
61
+ execute: async (params, ctx) => {
62
+ const result = await stockLevelService.setOnHand({
63
+ productId: params.productId,
64
+ warehouseId: params.warehouseId,
65
+ onHand: params.quantity,
66
+ }, {
67
+ actorAdminUserId: ctx.adminUserId,
68
+ ipAddress: ctx.auditCtx.ipAddress ?? null,
69
+ userAgent: ctx.auditCtx.userAgent ?? null,
70
+ requestId: ctx.auditCtx.requestId ?? null,
71
+ });
72
+ return { before: result.before, after: result.after };
73
+ },
74
+ };
75
+ return [searchWarehouses, setStockLevel];
76
+ }
77
+ //# sourceMappingURL=prompt-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-tools.js","sourceRoot":"","sources":["../../src/backend/prompt-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import { type CatalogProductReadPort } from '@endora-commerce/contracts';
4
+ import type { WarehouseService } from './services/warehouse-service.js';
5
+ import type { StockLevelService } from './services/stock-level-service.js';
6
+ import type { WarehouseChannelService } from './services/warehouse-channel-service.js';
7
+ import type { ThresholdAdminService } from './services/threshold-admin-service.js';
8
+ import type { LowStockAlertService } from './services/low-stock-alert-service.js';
9
+ import type { AvailabilityNotificationService } from './services/availability-notification-service.js';
10
+ import type { CsvStockImporter } from './services/csv-stock-importer.js';
11
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
12
+ /**
13
+ * Inventory admin routes — feature 010 (US1 + US2).
14
+ *
15
+ * **Every gate below is this module's own** (2026-08-29). All 21 used to
16
+ * enforce a code somebody else owns: nine reads on `orders:read` and twelve
17
+ * writes on `catalog:write`. Neither names the data touched, which is the
18
+ * discriminator `specs/080-f4-real-scope/payments-permission-ownership.md` §7.2
19
+ * sets and `specs/080-f4-real-scope/first-deployment-window.md` §2 applies per route rather than
20
+ * per module. A warehouse is a physical location with an address, not catalogue
21
+ * data; a channel↔warehouse assignment is fulfilment routing, deciding which
22
+ * stock a channel may sell; a stock level, a display-band threshold, a CSV
23
+ * import and a back-in-stock queue entry are all rows in this module's own
24
+ * tables. Nothing here reads or writes a product, a price or an order.
25
+ *
26
+ * The read side was the mirror of the same defect and is worth stating on its
27
+ * own: a merchandiser holding `catalog:read` could not see stock at all, while
28
+ * anyone holding `orders:read` could enumerate every warehouse and its address.
29
+ *
30
+ * `inventory:read` / `inventory:write`, and no third code. The one route that
31
+ * genuinely joins another module's table — the roster's `products` join in
32
+ * `StockLevelService` — is a *reach* and is ledgered as one; it is not an
33
+ * authority question, and it is refused to a catalogue editor exactly as the
34
+ * rest of this file now is.
35
+ *
36
+ * Surfaces:
37
+ * - Legacy single-bucket stock-level read/write (foundation 001 backward
38
+ * compat — kept until callers migrate to the per-warehouse PUT below).
39
+ * - Warehouse CRUD (US1).
40
+ * - Inventory landing KPIs + per-product roster + per-warehouse setOnHand
41
+ * (US2).
42
+ */
43
+ export interface InventoryAdminDeps {
44
+ emFactory: () => EntityManager;
45
+ /** `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C). */
46
+ catalogProducts: CatalogProductReadPort;
47
+ warehouseService: WarehouseService;
48
+ stockLevelService: StockLevelService;
49
+ warehouseChannelService: WarehouseChannelService;
50
+ thresholdAdminService: ThresholdAdminService;
51
+ lowStockAlertService: LowStockAlertService;
52
+ availabilityNotificationService: AvailabilityNotificationService;
53
+ csvStockImporter: CsvStockImporter;
54
+ requireAdmin: RequireAdminFactory;
55
+ /** Feature 024 — resolves admin actor identity for audit entries. */
56
+ resolveAdminAuditContext?: (req: FastifyRequest) => {
57
+ actorAdminUserId: string;
58
+ impersonatedCustomerAccountId?: string | null;
59
+ };
60
+ }
61
+ export declare function registerInventoryAdminRoutes(app: FastifyInstance, deps: InventoryAdminDeps): Promise<void>;
62
+ //# sourceMappingURL=routes.admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAQL,KAAK,sBAAsB,EAC5B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,2EAA2E;IAC3E,eAAe,EAAE,sBAAsB,CAAC;IACxC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,uBAAuB,EAAE,uBAAuB,CAAC;IACjD,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,+BAA+B,EAAE,+BAA+B,CAAC;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,mBAAmB,CAAC;IAClC,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAClD,gBAAgB,EAAE,MAAM,CAAC;QACzB,6BAA6B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/C,CAAC;CACH;AA6BD,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CA8ff"}
@@ -0,0 +1,383 @@
1
+ import { z } from 'zod';
2
+ import { assignWarehouseToChannelRequestSchema, createWarehouseRequestSchema, patchInventoryThresholdsRequestSchema, patchWarehouseChannelAssignmentRequestSchema, setProductWarehouseLowStockThresholdsRequestSchema, setStockLevelRequestSchema, updateWarehouseRequestSchema, } from '@endora-commerce/contracts';
3
+ import { StockLevel } from './entities/stock-level.entity.js';
4
+ import { DEFAULT_WAREHOUSE_ID } from './entities/warehouse.entity.js';
5
+ /** Build the per-request audit context for inventory route handlers. */
6
+ function buildAuditCtx(request, resolver) {
7
+ const base = resolver?.(request);
8
+ if (!base)
9
+ return undefined;
10
+ return {
11
+ actorAdminUserId: base.actorAdminUserId,
12
+ impersonatedCustomerAccountId: base.impersonatedCustomerAccountId ?? null,
13
+ ipAddress: request.ip ?? null,
14
+ userAgent: typeof request.headers['user-agent'] === 'string'
15
+ ? request.headers['user-agent']
16
+ : null,
17
+ requestId: request.id,
18
+ };
19
+ }
20
+ const setLegacyStockLevelSchema = z.object({
21
+ productId: z.string().uuid(),
22
+ variantId: z.string().uuid().nullable().optional(),
23
+ onHand: z.number().int().nonnegative(),
24
+ });
25
+ export async function registerInventoryAdminRoutes(app, deps) {
26
+ const { emFactory, catalogProducts, warehouseService, stockLevelService, warehouseChannelService, thresholdAdminService, lowStockAlertService, availabilityNotificationService, csvStockImporter, requireAdmin, } = deps;
27
+ // ---------------------------------------------------------------------------
28
+ // Inventory landing KPIs (US2)
29
+ // ---------------------------------------------------------------------------
30
+ app.get('/api/v1/admin/inventory', { preHandler: requireAdmin('inventory:read') }, async () => {
31
+ const data = await stockLevelService.listLandingKpis();
32
+ return { data };
33
+ });
34
+ // ---------------------------------------------------------------------------
35
+ // Per-product roster + per-warehouse setOnHand (US2)
36
+ // ---------------------------------------------------------------------------
37
+ app.get('/api/v1/admin/inventory/levels', { preHandler: requireAdmin('inventory:read') }, async (request) => {
38
+ const q = (request.query ?? {});
39
+ const search = q['q']?.trim();
40
+ const lowOnly = q['low'] === '1' || q['low'] === 'true';
41
+ const outOnly = q['out'] === '1' || q['out'] === 'true';
42
+ const data = await stockLevelService.listRoster({
43
+ ...(q['productId'] ? { productId: q['productId'] } : {}),
44
+ ...(q['warehouseId'] ? { warehouseId: q['warehouseId'] } : {}),
45
+ ...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
46
+ ...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
47
+ ...(search ? { q: search } : {}),
48
+ ...(lowOnly ? { lowOnly: true } : {}),
49
+ ...(outOnly ? { outOnly: true } : {}),
50
+ });
51
+ return data;
52
+ });
53
+ app.put('/api/v1/admin/inventory/levels', {
54
+ preHandler: requireAdmin('inventory:write'),
55
+ schema: { body: setStockLevelRequestSchema },
56
+ }, async (request) => {
57
+ const body = setStockLevelRequestSchema.parse(request.body);
58
+ const data = await stockLevelService.setOnHand({
59
+ productId: body.productId,
60
+ warehouseId: body.warehouseId,
61
+ ...(body.variantId ? { variantId: body.variantId } : {}),
62
+ onHand: body.onHand,
63
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
64
+ return { data };
65
+ });
66
+ // Per-(product, warehouse) low-stock thresholds. Consulted only when the
67
+ // product runs in `lowStockThresholdMode = 'per_warehouse'`, but writeable
68
+ // regardless of mode so admins can pre-populate values before switching.
69
+ app.put('/api/v1/admin/inventory/warehouse-low-stock-thresholds', {
70
+ preHandler: requireAdmin('inventory:write'),
71
+ schema: { body: setProductWarehouseLowStockThresholdsRequestSchema },
72
+ }, async (request, reply) => {
73
+ const body = setProductWarehouseLowStockThresholdsRequestSchema.parse(request.body);
74
+ await stockLevelService.setProductWarehouseThresholds({
75
+ productId: body.productId,
76
+ entries: body.thresholds,
77
+ });
78
+ return reply.status(204).send();
79
+ });
80
+ // ---------------------------------------------------------------------------
81
+ // Warehouse CRUD (US1)
82
+ // ---------------------------------------------------------------------------
83
+ app.get('/api/v1/admin/warehouses', { preHandler: requireAdmin('inventory:read') }, async (request) => {
84
+ const q = (request.query ?? {});
85
+ const data = await warehouseService.list({
86
+ ...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
87
+ ...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
88
+ activeOnly: q['activeOnly'] === 'true',
89
+ withTotals: q['withTotals'] !== 'false',
90
+ });
91
+ return data;
92
+ });
93
+ app.get('/api/v1/admin/warehouses/:id', { preHandler: requireAdmin('inventory:read') }, async (request, reply) => {
94
+ const { id } = request.params;
95
+ const data = await warehouseService.getById(id, { withTotals: true });
96
+ if (!data) {
97
+ reply.status(404);
98
+ return {
99
+ error: {
100
+ code: 'WAREHOUSE_NOT_FOUND',
101
+ message: 'Warehouse not found',
102
+ requestId: request.id,
103
+ },
104
+ };
105
+ }
106
+ return { data };
107
+ });
108
+ app.post('/api/v1/admin/warehouses', {
109
+ preHandler: requireAdmin('inventory:write'),
110
+ schema: { body: createWarehouseRequestSchema },
111
+ }, async (request, reply) => {
112
+ const body = createWarehouseRequestSchema.parse(request.body);
113
+ const data = await warehouseService.create({
114
+ name: body.name,
115
+ code: body.code,
116
+ ...(body.active !== undefined ? { active: body.active } : {}),
117
+ ...(body.description !== undefined ? { description: body.description ?? null } : {}),
118
+ ...(body.address !== undefined ? { address: body.address ?? null } : {}),
119
+ ...(body.contact !== undefined ? { contact: body.contact ?? null } : {}),
120
+ ...(body.defaultLowStockThreshold !== undefined
121
+ ? { defaultLowStockThreshold: body.defaultLowStockThreshold ?? null }
122
+ : {}),
123
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
124
+ reply.status(201);
125
+ return { data };
126
+ });
127
+ app.patch('/api/v1/admin/warehouses/:id', {
128
+ preHandler: requireAdmin('inventory:write'),
129
+ schema: { body: updateWarehouseRequestSchema },
130
+ }, async (request) => {
131
+ const { id } = request.params;
132
+ const body = updateWarehouseRequestSchema.parse(request.body);
133
+ const data = await warehouseService.update(id, {
134
+ ...(body.name !== undefined ? { name: body.name } : {}),
135
+ ...(body.active !== undefined ? { active: body.active } : {}),
136
+ ...(body.description !== undefined ? { description: body.description ?? null } : {}),
137
+ ...(body.address !== undefined ? { address: body.address ?? null } : {}),
138
+ ...(body.contact !== undefined ? { contact: body.contact ?? null } : {}),
139
+ ...(body.defaultLowStockThreshold !== undefined
140
+ ? { defaultLowStockThreshold: body.defaultLowStockThreshold ?? null }
141
+ : {}),
142
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
143
+ return { data };
144
+ });
145
+ app.delete('/api/v1/admin/warehouses/:id', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
146
+ const { id } = request.params;
147
+ await warehouseService.delete(id, buildAuditCtx(request, deps.resolveAdminAuditContext));
148
+ reply.status(204);
149
+ });
150
+ // ---------------------------------------------------------------------------
151
+ // Low-stock alerts (US4)
152
+ // ---------------------------------------------------------------------------
153
+ app.get('/api/v1/admin/inventory/low-stock', { preHandler: requireAdmin('inventory:read') }, async () => {
154
+ const items = await lowStockAlertService.listLowStock();
155
+ return { items };
156
+ });
157
+ // ---------------------------------------------------------------------------
158
+ // Display-band thresholds (US5)
159
+ // ---------------------------------------------------------------------------
160
+ app.get('/api/v1/admin/inventory/thresholds', { preHandler: requireAdmin('inventory:read') }, async () => {
161
+ const data = await thresholdAdminService.read();
162
+ return { data };
163
+ });
164
+ app.patch('/api/v1/admin/inventory/thresholds', {
165
+ preHandler: requireAdmin('inventory:write'),
166
+ schema: { body: patchInventoryThresholdsRequestSchema },
167
+ }, async (request) => {
168
+ const body = patchInventoryThresholdsRequestSchema.parse(request.body);
169
+ const data = await thresholdAdminService.patch({
170
+ ...(body.global ? { global: body.global } : {}),
171
+ ...(body.perCategory ? { perCategory: body.perCategory } : {}),
172
+ ...(body.perProduct ? { perProduct: body.perProduct } : {}),
173
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
174
+ return { data };
175
+ });
176
+ // ---------------------------------------------------------------------------
177
+ // CSV stock import (US7)
178
+ // ---------------------------------------------------------------------------
179
+ app.post('/api/v1/admin/inventory/import', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
180
+ const q = (request.query ?? {});
181
+ const body = (request.body ?? {});
182
+ if (!body.csv || !body.warehouseId) {
183
+ reply.status(400);
184
+ return {
185
+ error: {
186
+ code: 'VALIDATION_FAILED',
187
+ message: 'csv + warehouseId are required',
188
+ requestId: request.id,
189
+ },
190
+ };
191
+ }
192
+ const result = await csvStockImporter.run({
193
+ csv: body.csv,
194
+ warehouseId: body.warehouseId,
195
+ dryRun: q['dryRun'] === 'true',
196
+ ...(q['fileName'] !== undefined ? { fileName: q['fileName'] } : {}),
197
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
198
+ return { data: result };
199
+ });
200
+ // ---------------------------------------------------------------------------
201
+ // Availability notifications (US6)
202
+ // ---------------------------------------------------------------------------
203
+ app.get('/api/v1/admin/inventory/availability-notifications', { preHandler: requireAdmin('inventory:read') }, async (request) => {
204
+ const q = (request.query ?? {});
205
+ const data = await availabilityNotificationService.listForAdmin({
206
+ ...(q['productId'] ? { productId: q['productId'] } : {}),
207
+ ...(q['status'] ? { status: q['status'] } : {}),
208
+ ...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
209
+ ...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
210
+ });
211
+ return data;
212
+ });
213
+ app.patch('/api/v1/admin/inventory/availability-notifications/:id', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
214
+ const { id } = request.params;
215
+ const body = (request.body ?? {});
216
+ if (body.status !== 'cancelled') {
217
+ reply.status(400);
218
+ return {
219
+ error: {
220
+ code: 'VALIDATION_FAILED',
221
+ message: 'Only `status: "cancelled"` is supported on this endpoint',
222
+ requestId: request.id,
223
+ },
224
+ };
225
+ }
226
+ await availabilityNotificationService.cancel(id);
227
+ reply.status(204);
228
+ return null;
229
+ });
230
+ // ---------------------------------------------------------------------------
231
+ // Sales channel ↔ warehouse binding (US3)
232
+ // ---------------------------------------------------------------------------
233
+ app.get('/api/v1/admin/sales-channels/:channelId/warehouses', { preHandler: requireAdmin('inventory:read') }, async (request) => {
234
+ const { channelId } = request.params;
235
+ const items = await warehouseChannelService.listForChannel(channelId);
236
+ return { items };
237
+ });
238
+ app.post('/api/v1/admin/sales-channels/:channelId/warehouses', {
239
+ preHandler: requireAdmin('inventory:write'),
240
+ schema: { body: assignWarehouseToChannelRequestSchema },
241
+ }, async (request, reply) => {
242
+ const { channelId } = request.params;
243
+ const body = assignWarehouseToChannelRequestSchema.parse(request.body);
244
+ const data = await warehouseChannelService.assign(channelId, {
245
+ warehouseId: body.warehouseId,
246
+ ...(body.isDefault !== undefined ? { isDefault: body.isDefault } : {}),
247
+ ...(body.sortOrder !== undefined ? { sortOrder: body.sortOrder } : {}),
248
+ });
249
+ reply.status(201);
250
+ return { data };
251
+ });
252
+ app.patch('/api/v1/admin/sales-channels/:channelId/warehouses/:assignmentId', {
253
+ preHandler: requireAdmin('inventory:write'),
254
+ schema: { body: patchWarehouseChannelAssignmentRequestSchema },
255
+ }, async (request) => {
256
+ const { channelId, assignmentId } = request.params;
257
+ const body = patchWarehouseChannelAssignmentRequestSchema.parse(request.body);
258
+ const data = await warehouseChannelService.patch(channelId, assignmentId, {
259
+ ...(body.isDefault !== undefined ? { isDefault: body.isDefault } : {}),
260
+ ...(body.sortOrder !== undefined ? { sortOrder: body.sortOrder } : {}),
261
+ });
262
+ return { data };
263
+ });
264
+ app.delete('/api/v1/admin/sales-channels/:channelId/warehouses/:assignmentId', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
265
+ const { channelId, assignmentId } = request.params;
266
+ await warehouseChannelService.unassign(channelId, assignmentId);
267
+ reply.status(204);
268
+ });
269
+ // ---------------------------------------------------------------------------
270
+ // Foundation 001 backward-compat — single-bucket stock list + write.
271
+ // Both routes default to the seeded Default warehouse.
272
+ //
273
+ // The two callers this header used to name are gone: the admin import posts to
274
+ // `/inventory/import` and the catalog seed writes `StockLevel` through the
275
+ // EntityManager. Nothing in the repository calls either route — no admin
276
+ // screen, no admin API client call, no seed, no script — and the module's
277
+ // documentation page lists neither. They are kept for a deployment's own
278
+ // integration, which is the only caller they can still have (issue #125).
279
+ //
280
+ // Issue #139 (D-66) ruled the `GET .../legacy` below deletable on the
281
+ // condition that `GET .../levels` is a strict superset of it, "and if
282
+ // `/levels` turns out not to cover a field this returns, that gap is the
283
+ // finding and the deletion waits". It does not: `/levels` aggregates per
284
+ // product across variants and warehouses, so it answers with neither
285
+ // `variantId` nor the row's `updatedAt` (nor the `stock_levels` row id, nor
286
+ // the pre-computed `available`, both derivable). The deletion therefore
287
+ // waits on a per-row read that covers those two fields, not on a caller
288
+ // census — that census is done, and it is zero.
289
+ // ---------------------------------------------------------------------------
290
+ /**
291
+ * @deprecated Use `PUT /api/v1/admin/inventory/levels`, which takes an
292
+ * explicit `warehouseId` instead of writing into the seeded Default one.
293
+ * Kept for a deployment's own integration until the access log or the
294
+ * deployment owner confirms nothing calls it (issue #139, D-66 part 3).
295
+ *
296
+ * Delegates to `StockLevelService.setOnHand`, the single point that emits
297
+ * `inventory.adjusted.v1` (issue #139). Until then this handler wrote
298
+ * `StockLevel.onHand` straight through the EntityManager, so a restock
299
+ * performed here fired no back-in-stock fan-out (US6) and no low-stock
300
+ * crossing alert (US4), and validated neither the product nor the warehouse
301
+ * — a typo'd product id created a stock row for a product that does not
302
+ * exist. The delegation is the whole of that repair; the response shape and
303
+ * the `catalog:write` gate are unchanged.
304
+ *
305
+ * The audited path for a stock change is `StockLevelService`
306
+ * (`stock_level.adjust`); this endpoint recorded nothing until issue #122,
307
+ * which the coverage scan could not see until it reached route files. The
308
+ * delegate records the same action token, so the two paths still land in one
309
+ * audit history.
310
+ *
311
+ * Deliberately **not** a Command (issue #125). A Command is what an undo
312
+ * attaches to, and an on-hand count is not a value an undo may restore:
313
+ * between the write and the undo, orders reserve and release stock, so
314
+ * putting back the number that stood before would overwrite movements
315
+ * nobody asked to reverse. The other two paths that set stock — the service
316
+ * behind `/levels` and the CSV importer — take the same view. Nor may this
317
+ * row's action diverge from theirs: an auditor reading a product's stock
318
+ * history reads one action, not three.
319
+ */
320
+ app.put('/api/v1/admin/inventory', {
321
+ preHandler: requireAdmin('inventory:write'),
322
+ schema: { body: setLegacyStockLevelSchema },
323
+ }, async (request) => {
324
+ const body = setLegacyStockLevelSchema.parse(request.body);
325
+ const variantId = body.variantId ?? null;
326
+ await stockLevelService.setOnHand({
327
+ productId: body.productId,
328
+ warehouseId: DEFAULT_WAREHOUSE_ID,
329
+ variantId,
330
+ onHand: body.onHand,
331
+ }, buildAuditCtx(request, deps.resolveAdminAuditContext));
332
+ // Re-read for the foundation-001 response shape: `setOnHand` answers with
333
+ // the before/after counts, this route has always answered with the row.
334
+ const row = await emFactory().findOneOrFail(StockLevel, {
335
+ productId: body.productId,
336
+ variantId,
337
+ warehouseId: DEFAULT_WAREHOUSE_ID,
338
+ });
339
+ return {
340
+ data: {
341
+ id: row.id,
342
+ productId: row.productId,
343
+ variantId: row.variantId ?? null,
344
+ onHand: row.onHand,
345
+ reserved: row.reserved,
346
+ available: row.onHand - row.reserved,
347
+ },
348
+ };
349
+ });
350
+ app.get('/api/v1/admin/inventory/legacy', { preHandler: requireAdmin('inventory:read') }, async (request) => {
351
+ const q = (request.query ?? {});
352
+ const where = {};
353
+ if (q['productId'])
354
+ where['productId'] = q['productId'];
355
+ const limit = Math.min(Math.max(Number.parseInt(q['limit'] ?? '200', 10), 1), 500);
356
+ const em = emFactory();
357
+ const rows = await em.find(StockLevel, where, {
358
+ limit,
359
+ orderBy: { updatedAt: 'desc' },
360
+ });
361
+ const productIds = Array.from(new Set(rows.map((r) => r.productId)));
362
+ const products = await catalogProducts.findByIds(productIds);
363
+ const productById = new Map(products.map((p) => [p.id, p]));
364
+ return {
365
+ data: rows.map((r) => {
366
+ const p = productById.get(r.productId);
367
+ return {
368
+ id: r.id,
369
+ productId: r.productId,
370
+ variantId: r.variantId ?? null,
371
+ warehouseId: r.warehouseId,
372
+ onHand: r.onHand,
373
+ reserved: r.reserved,
374
+ available: r.onHand - r.reserved,
375
+ productSku: p?.sku ?? null,
376
+ productName: p?.name ?? null,
377
+ updatedAt: r.updatedAt.toISOString(),
378
+ };
379
+ }),
380
+ };
381
+ });
382
+ }
383
+ //# sourceMappingURL=routes.admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
2
+ import { type CatalogCategoryReadPort, type CatalogProductReadPort, type CustomerAccountReadPort } from '@endora-commerce/contracts';
3
+ import type { EntityManager } from '@mikro-orm/postgresql';
4
+ import type { AvailabilityNotificationService } from './services/availability-notification-service.js';
5
+ import type { WarehouseChannelService } from './services/warehouse-channel-service.js';
6
+ import type { StockLevelService } from './services/stock-level-service.js';
7
+ import type { SalesChannelResolutionPort } from '@endora-commerce/platform/kernel';
8
+ import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
9
+ export interface InventoryRoutesDeps {
10
+ emFactory: () => EntityManager;
11
+ /** `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C). */
12
+ catalogProducts: CatalogProductReadPort;
13
+ /** `catalogCategoryReadPort`, owned by `catalog` — the threshold chain. */
14
+ catalogCategories: CatalogCategoryReadPort;
15
+ /** `customerAccountReadPort`, owned by `customer_accounts` (feature 075). */
16
+ customerAccounts: CustomerAccountReadPort;
17
+ availabilityService: AvailabilityNotificationService;
18
+ requireCustomer: (req: FastifyRequest, reply: unknown) => Promise<void>;
19
+ resolveCustomerContext: (req: FastifyRequest) => {
20
+ customerAccountId: string;
21
+ organizationId: string;
22
+ };
23
+ /** Optional — present in production composition. Used to resolve the
24
+ * channel for the storefront-public stock readout. */
25
+ channelResolver?: SalesChannelResolutionPort;
26
+ warehouseChannelService?: WarehouseChannelService;
27
+ stockLevelService?: StockLevelService;
28
+ settingsService?: SettingsReadPort;
29
+ /**
30
+ * Feature 026 US4 — Per-request resolver that returns the warehouse-id
31
+ * allow-list for the caller's Organization, or `null` when no restriction
32
+ * applies (anonymous request, no-org Customer, or org has no warehouses
33
+ * explicitly assigned). When the returned list is non-empty, the
34
+ * storefront stock-figure endpoint intersects its candidate warehouse
35
+ * set with this list before summing on-hand.
36
+ */
37
+ resolveOrganizationWarehouseAllowList?: (req: FastifyRequest) => Promise<string[] | null>;
38
+ }
39
+ export declare function registerInventoryRoutes(app: FastifyInstance, deps: InventoryRoutesDeps): Promise<void>;
40
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,EAIL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAG7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAQnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,2EAA2E;IAC3E,eAAe,EAAE,sBAAsB,CAAC;IACxC,2EAA2E;IAC3E,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C,6EAA6E;IAC7E,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,mBAAmB,EAAE,+BAA+B,CAAC;IACrD,eAAe,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,sBAAsB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAC/C,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF;2DACuD;IACvD,eAAe,CAAC,EAAE,0BAA0B,CAAC;IAC7C,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC;;;;;;;OAOG;IACH,qCAAqC,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;CAC3F;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CA8Of"}