@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,154 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { StockLevel } from '../entities/stock-level.entity.js';
3
+ import { Warehouse } from '../entities/warehouse.entity.js';
4
+ /**
5
+ * CSV stock importer (US7 / FR-031).
6
+ *
7
+ * Accepts a CSV with a `sku,onHand` header row and applies the on-hand
8
+ * value for each product *into the chosen warehouse*. The format is:
9
+ *
10
+ * sku,onHand
11
+ * PRD-001,42
12
+ * PRD-002,0
13
+ *
14
+ * Rows whose SKU does not resolve to a Product produce a
15
+ * `product_not_found` error. Negative or non-integer onHand values
16
+ * produce `invalid_quantity`. Malformed rows (wrong column count, no
17
+ * comma) produce `malformed_row`. The function returns a summary for
18
+ * the operator UI; the file itself is never persisted.
19
+ *
20
+ * Implementation note (research §R8): Node-native streaming + manual
21
+ * row split keeps the dep graph minimal. The XLSX path lives in a
22
+ * sibling file and uses `read-excel-file` (selected per T003).
23
+ */
24
+ export class CsvStockImporter {
25
+ emFactory;
26
+ catalogProducts;
27
+ eventBus;
28
+ auditLog;
29
+ constructor(emFactory,
30
+ /**
31
+ * `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C). A
32
+ * sheet addresses a product by SKU, so somebody has to ask `catalog`; the
33
+ * `product_not_found` row error is raised from its answer.
34
+ */
35
+ catalogProducts, eventBus, auditLog) {
36
+ this.emFactory = emFactory;
37
+ this.catalogProducts = catalogProducts;
38
+ this.eventBus = eventBus;
39
+ this.auditLog = auditLog;
40
+ }
41
+ async run(input, auditCtx) {
42
+ const lines = input.csv.split(/\r?\n/);
43
+ const errors = [];
44
+ let rowsRead = 0;
45
+ let rowsApplied = 0;
46
+ let rowsSkipped = 0;
47
+ if (lines.length === 0) {
48
+ return { rowsRead: 0, rowsApplied: 0, rowsSkipped: 0, errors };
49
+ }
50
+ const header = (lines[0] ?? '').split(',').map((c) => c.trim().toLowerCase());
51
+ const skuIdx = header.indexOf('sku');
52
+ const onHandIdx = header.indexOf('onhand');
53
+ if (skuIdx === -1 || onHandIdx === -1) {
54
+ return {
55
+ rowsRead: 0,
56
+ rowsApplied: 0,
57
+ rowsSkipped: 0,
58
+ errors: [
59
+ {
60
+ row: 1,
61
+ sku: '',
62
+ reason: 'malformed_row',
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ const em = this.emFactory();
68
+ const dataLines = lines.slice(1).filter((l) => l.trim().length > 0);
69
+ rowsRead = dataLines.length;
70
+ for (let i = 0; i < dataLines.length; i++) {
71
+ const lineNo = i + 2;
72
+ const cells = (dataLines[i] ?? '').split(',').map((c) => c.trim());
73
+ if (cells.length < Math.max(skuIdx, onHandIdx) + 1) {
74
+ errors.push({ row: lineNo, sku: '', reason: 'malformed_row' });
75
+ rowsSkipped += 1;
76
+ continue;
77
+ }
78
+ const sku = cells[skuIdx] ?? '';
79
+ const onHandRaw = cells[onHandIdx] ?? '';
80
+ const onHand = Number.parseInt(onHandRaw, 10);
81
+ if (!Number.isFinite(onHand) || onHand < 0 || String(onHand) !== onHandRaw) {
82
+ errors.push({ row: lineNo, sku, reason: 'invalid_quantity' });
83
+ rowsSkipped += 1;
84
+ continue;
85
+ }
86
+ const product = await this.catalogProducts.findBySku(sku);
87
+ if (!product) {
88
+ errors.push({ row: lineNo, sku, reason: 'product_not_found' });
89
+ rowsSkipped += 1;
90
+ continue;
91
+ }
92
+ if (input.dryRun) {
93
+ rowsApplied += 1;
94
+ continue;
95
+ }
96
+ let row = await em.findOne(StockLevel, {
97
+ productId: product.id,
98
+ warehouseId: input.warehouseId,
99
+ variantId: null,
100
+ });
101
+ const before = row?.onHand ?? 0;
102
+ if (row) {
103
+ row.onHand = onHand;
104
+ }
105
+ else {
106
+ row = em.create(StockLevel, {
107
+ productId: product.id,
108
+ warehouseId: input.warehouseId,
109
+ onHand,
110
+ });
111
+ }
112
+ await em.persistAndFlush(row);
113
+ rowsApplied += 1;
114
+ if (this.eventBus && before !== onHand) {
115
+ this.eventBus.emit('inventory.adjusted.v1', {
116
+ eventId: randomUUID(),
117
+ occurredAt: new Date().toISOString(),
118
+ productId: product.id,
119
+ warehouseId: input.warehouseId,
120
+ variantId: null,
121
+ before,
122
+ after: onHand,
123
+ });
124
+ }
125
+ }
126
+ // Feature 024 — single summary audit row per CSV import, never per
127
+ // CSV line. Dry runs are not audited (no state changed).
128
+ if (this.auditLog && auditCtx && !input.dryRun && rowsApplied > 0) {
129
+ const warehouse = await em.findOne(Warehouse, { id: input.warehouseId });
130
+ await this.auditLog.record({
131
+ actorAdminUserId: auditCtx.actorAdminUserId,
132
+ ...(auditCtx.impersonatedCustomerAccountId !== undefined
133
+ ? { impersonatedCustomerAccountId: auditCtx.impersonatedCustomerAccountId }
134
+ : {}),
135
+ action: 'stock_level.bulk_import',
136
+ objectType: 'bulk_operation',
137
+ objectId: randomUUID(),
138
+ stateAfter: {
139
+ warehouseId: input.warehouseId,
140
+ warehouseCode: warehouse?.code ?? null,
141
+ fileName: input.fileName ?? null,
142
+ rowsProcessed: rowsApplied,
143
+ rowsSkipped,
144
+ rowsErrored: errors.length,
145
+ },
146
+ ...(auditCtx.ipAddress !== undefined ? { ipAddress: auditCtx.ipAddress } : {}),
147
+ ...(auditCtx.userAgent !== undefined ? { userAgent: auditCtx.userAgent } : {}),
148
+ ...(auditCtx.requestId !== undefined ? { requestId: auditCtx.requestId } : {}),
149
+ });
150
+ }
151
+ return { rowsRead, rowsApplied, rowsSkipped, errors };
152
+ }
153
+ }
154
+ //# sourceMappingURL=csv-stock-importer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csv-stock-importer.js","sourceRoot":"","sources":["../../../src/backend/services/csv-stock-importer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ThresholdLevel } from './threshold-resolver.js';
2
+ /**
3
+ * Display-band resolver — pure function (feature 010 / FR-017 to FR-020).
4
+ *
5
+ * Maps cumulative on-hand + thresholds to one of:
6
+ * `high` | `medium` | `low` | `out_of_stock` | `available`
7
+ *
8
+ * `available` is the special bucket returned when `manageStock = false`
9
+ * — the storefront treats the product as always available regardless
10
+ * of stock numbers (FR-022).
11
+ */
12
+ export type DisplayBand = 'high' | 'medium' | 'low' | 'out_of_stock' | 'available';
13
+ export interface ResolveDisplayBandInput {
14
+ manageStock: boolean;
15
+ cumulativeOnHand: number;
16
+ thresholds: ThresholdLevel;
17
+ }
18
+ export declare function resolveDisplayBand(input: ResolveDisplayBandInput): DisplayBand;
19
+ //# sourceMappingURL=display-band-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display-band-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/display-band-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,cAAc,GAAG,WAAW,CAAC;AAEnF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAmB9E"}
@@ -0,0 +1,21 @@
1
+ export function resolveDisplayBand(input) {
2
+ if (!input.manageStock)
3
+ return 'available';
4
+ const onHand = Math.max(0, input.cumulativeOnHand);
5
+ if (onHand <= 0)
6
+ return 'out_of_stock';
7
+ const high = input.thresholds.high;
8
+ if (high !== null && high !== undefined && onHand >= high)
9
+ return 'high';
10
+ const medium = input.thresholds.medium;
11
+ if (medium !== null && medium !== undefined && onHand >= medium)
12
+ return 'medium';
13
+ const low = input.thresholds.low;
14
+ if (low !== null && low !== undefined && onHand >= low)
15
+ return 'low';
16
+ // No applicable threshold (or below `low`) but on-hand > 0: treat
17
+ // as low. The display-mode `available_or_not` collapses this to
18
+ // `available` at render time.
19
+ return 'low';
20
+ }
21
+ //# sourceMappingURL=display-band-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display-band-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/display-band-resolver.ts"],"names":[],"mappings":"AAoBA,MAAM,UAAU,kBAAkB,CAAC,KAA8B;IAC/D,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,WAAW,CAAC;IAE3C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACnD,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,cAAc,CAAC;IAEvC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;IACnC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC;IAEzE,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;IACvC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,MAAM;QAAE,OAAO,QAAQ,CAAC;IAEjF,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;IACjC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IAErE,kEAAkE;IAClE,gEAAgE;IAChE,8BAA8B;IAC9B,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Effective fulfilment-strategy resolver — pure function.
3
+ *
4
+ * The implementation moved to `@endora-commerce/contracts` in feature 075's Phase P
5
+ * (FR-013): it is pure over its arguments — three configuration layers in, one
6
+ * resolved strategy out — so switching `inventory` off cannot change the
7
+ * answer and a gated port answering 503 would be a bug rather than a degrade.
8
+ *
9
+ * `orders` calls it at placement, inside its own transaction, through a
10
+ * dynamic import. Publishing it lets that cut remove the dynamic import rather
11
+ * than wrap it.
12
+ *
13
+ * Re-exported here for the length of Phase P, which cuts no consumer.
14
+ */
15
+ export { type FulfilmentLayer, type EffectiveFulfilment, resolveEffectiveFulfilmentStrategy, } from '@endora-commerce/contracts';
16
+ //# sourceMappingURL=effective-fulfilment-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-fulfilment-strategy.d.ts","sourceRoot":"","sources":["../../../src/backend/services/effective-fulfilment-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,kCAAkC,GACnC,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Effective fulfilment-strategy resolver — pure function.
3
+ *
4
+ * The implementation moved to `@endora-commerce/contracts` in feature 075's Phase P
5
+ * (FR-013): it is pure over its arguments — three configuration layers in, one
6
+ * resolved strategy out — so switching `inventory` off cannot change the
7
+ * answer and a gated port answering 503 would be a bug rather than a degrade.
8
+ *
9
+ * `orders` calls it at placement, inside its own transaction, through a
10
+ * dynamic import. Publishing it lets that cut remove the dynamic import rather
11
+ * than wrap it.
12
+ *
13
+ * Re-exported here for the length of Phase P, which cuts no consumer.
14
+ */
15
+ export { resolveEffectiveFulfilmentStrategy, } from '@endora-commerce/contracts';
16
+ //# sourceMappingURL=effective-fulfilment-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"effective-fulfilment-strategy.js","sourceRoot":"","sources":["../../../src/backend/services/effective-fulfilment-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAGL,kCAAkC,GACnC,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Fulfilment-strategy resolver — pure function (feature 010 / FR-029…FR-033,
3
+ * research §R4).
4
+ *
5
+ * The implementation moved to `@endora-commerce/contracts` in feature 075's Phase P
6
+ * (FR-013), with its twin in `effective-fulfilment-strategy.ts` and for the
7
+ * same reason: candidate warehouses in, an allocation decision out, and no
8
+ * table read anywhere in it.
9
+ *
10
+ * Re-exported here for the length of Phase P, which cuts no consumer.
11
+ */
12
+ export { type CandidateWarehouse, type AllocationDecision, type ResolveAllocationsInput, type AllocationOutcome, resolveAllocations, } from '@endora-commerce/contracts';
13
+ //# sourceMappingURL=fulfilment-strategy-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulfilment-strategy-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/fulfilment-strategy-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,kBAAkB,GACnB,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Fulfilment-strategy resolver — pure function (feature 010 / FR-029…FR-033,
3
+ * research §R4).
4
+ *
5
+ * The implementation moved to `@endora-commerce/contracts` in feature 075's Phase P
6
+ * (FR-013), with its twin in `effective-fulfilment-strategy.ts` and for the
7
+ * same reason: candidate warehouses in, an allocation decision out, and no
8
+ * table read anywhere in it.
9
+ *
10
+ * Re-exported here for the length of Phase P, which cuts no consumer.
11
+ */
12
+ export { resolveAllocations, } from '@endora-commerce/contracts';
13
+ //# sourceMappingURL=fulfilment-strategy-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulfilment-strategy-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/fulfilment-strategy-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAKL,kBAAkB,GACnB,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,65 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CandidateWarehouse, ChannelWarehouse, InventoryStockReadPort, StockLevelRecord, WarehouseChannelAssignmentRecord, WarehouseRecord } from '@endora-commerce/contracts';
3
+ import { StockLevel } from '../entities/stock-level.entity.js';
4
+ import { Warehouse } from '../entities/warehouse.entity.js';
5
+ import { WarehouseChannelAssignment } from '../entities/warehouse-channel-assignment.entity.js';
6
+ /**
7
+ * The stock read model `inventory` publishes (feature 075, Phase P).
8
+ *
9
+ * `orders` reads stock at placement, and it does so through a **dynamic**
10
+ * import inside a method body — invisible to a reviewer scanning the import
11
+ * block, which is why the boundary check was taught to see one. What it
12
+ * assembles there is this module's business: candidate warehouses for a line
13
+ * on a channel, with `available = onHand - reserved`, joined across three
14
+ * tables and ordered by warehouse code.
15
+ *
16
+ * `candidatesFor` is therefore the method with no direct predecessor — the
17
+ * others are transcriptions. It exists because the assembly, not the rows, is
18
+ * what the caller wanted, and assembling it in `orders` put this module's join
19
+ * in another module's transaction.
20
+ */
21
+ export declare class InventoryStockReadService implements InventoryStockReadPort {
22
+ private readonly emFactory;
23
+ constructor(emFactory: () => EntityManager);
24
+ listStockForProducts(productIds: readonly string[], options?: {
25
+ warehouseId?: string;
26
+ }): Promise<StockLevelRecord[]>;
27
+ listWarehouses(options?: {
28
+ activeOnly?: boolean;
29
+ }): Promise<WarehouseRecord[]>;
30
+ listChannelAssignments(salesChannelId: string): Promise<WarehouseChannelAssignmentRecord[]>;
31
+ /**
32
+ * The channel → warehouse binding placement allocates against (D-94.4).
33
+ *
34
+ * This was a knex join inside `orders`' placement transaction —
35
+ * `warehouse_channel_assignments as a` joined to `warehouses as w`, two of
36
+ * this module's tables named by hand in another module's method body, with
37
+ * the empty-channel fallback spelled out of a UUID constant copied from
38
+ * `warehouse.entity.ts`. Both halves are this module's business, so both are
39
+ * here: the ordering placement walks (`isDefault desc, sortOrder asc,
40
+ * createdAt asc`, which is *not* `listChannelAssignments`' ordering) and the
41
+ * default-warehouse fallback.
42
+ *
43
+ * The fallback is not dead code kept for tests. The boot-time
44
+ * `WarehouseChannelReconciler` binds the default warehouse to every channel,
45
+ * so a production deployment does not reach it — but a seed-skipped
46
+ * environment, and a channel whose only bound warehouses have been
47
+ * deactivated, both do, and answering nothing there would refuse every line
48
+ * rather than allocate against the default.
49
+ *
50
+ * Read through this module's own `EntityManager`, deliberately: it is the
51
+ * *binding*, not the stock. The caller reads and locks `stock_levels` itself,
52
+ * on its own transaction — see the port's doc comment for why
53
+ * `candidatesFor` cannot stand in here.
54
+ */
55
+ listChannelWarehouses(salesChannelId: string): Promise<ChannelWarehouse[]>;
56
+ candidatesFor(input: {
57
+ productId: string;
58
+ variantId?: string | null;
59
+ salesChannelId: string | null;
60
+ }): Promise<CandidateWarehouse[]>;
61
+ }
62
+ export declare function toWarehouseRecord(warehouse: Warehouse): WarehouseRecord;
63
+ export declare function toStockLevelRecord(level: StockLevel): StockLevelRecord;
64
+ export declare function toWarehouseChannelAssignmentRecord(assignment: WarehouseChannelAssignment): WarehouseChannelAssignmentRecord;
65
+ //# sourceMappingURL=inventory-read-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-read-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/inventory-read-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,gCAAgC,EAChC,eAAe,EAChB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAGL,SAAS,EACV,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,oDAAoD,CAAC;AAEhG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,yBAA0B,YAAW,sBAAsB;IAC1D,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,oBAAoB,CACxB,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACjC,OAAO,CAAC,gBAAgB,EAAE,CAAC;IASxB,cAAc,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAS9E,sBAAsB,CAC1B,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,gCAAgC,EAAE,CAAC;IAS9C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqC1E,aAAa,CAAC,KAAK,EAAE;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;CAsClC;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,eAAe,CAevE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,gBAAgB,CAWtE;AAED,wBAAgB,kCAAkC,CAChD,UAAU,EAAE,0BAA0B,GACrC,gCAAgC,CASlC"}
@@ -0,0 +1,168 @@
1
+ import { StockLevel } from '../entities/stock-level.entity.js';
2
+ import { DEFAULT_WAREHOUSE_CODE, DEFAULT_WAREHOUSE_ID, Warehouse, } from '../entities/warehouse.entity.js';
3
+ import { WarehouseChannelAssignment } from '../entities/warehouse-channel-assignment.entity.js';
4
+ /**
5
+ * The stock read model `inventory` publishes (feature 075, Phase P).
6
+ *
7
+ * `orders` reads stock at placement, and it does so through a **dynamic**
8
+ * import inside a method body — invisible to a reviewer scanning the import
9
+ * block, which is why the boundary check was taught to see one. What it
10
+ * assembles there is this module's business: candidate warehouses for a line
11
+ * on a channel, with `available = onHand - reserved`, joined across three
12
+ * tables and ordered by warehouse code.
13
+ *
14
+ * `candidatesFor` is therefore the method with no direct predecessor — the
15
+ * others are transcriptions. It exists because the assembly, not the rows, is
16
+ * what the caller wanted, and assembling it in `orders` put this module's join
17
+ * in another module's transaction.
18
+ */
19
+ export class InventoryStockReadService {
20
+ emFactory;
21
+ constructor(emFactory) {
22
+ this.emFactory = emFactory;
23
+ }
24
+ async listStockForProducts(productIds, options) {
25
+ if (productIds.length === 0)
26
+ return [];
27
+ const rows = await this.emFactory().find(StockLevel, {
28
+ productId: { $in: [...productIds] },
29
+ ...(options?.warehouseId ? { warehouseId: options.warehouseId } : {}),
30
+ });
31
+ return rows.map(toStockLevelRecord);
32
+ }
33
+ async listWarehouses(options) {
34
+ const rows = await this.emFactory().find(Warehouse, options?.activeOnly ? { active: true } : {}, { orderBy: { code: 'asc' } });
35
+ return rows.map(toWarehouseRecord);
36
+ }
37
+ async listChannelAssignments(salesChannelId) {
38
+ const rows = await this.emFactory().find(WarehouseChannelAssignment, { salesChannelId }, { orderBy: { sortOrder: 'asc', id: 'asc' } });
39
+ return rows.map(toWarehouseChannelAssignmentRecord);
40
+ }
41
+ /**
42
+ * The channel → warehouse binding placement allocates against (D-94.4).
43
+ *
44
+ * This was a knex join inside `orders`' placement transaction —
45
+ * `warehouse_channel_assignments as a` joined to `warehouses as w`, two of
46
+ * this module's tables named by hand in another module's method body, with
47
+ * the empty-channel fallback spelled out of a UUID constant copied from
48
+ * `warehouse.entity.ts`. Both halves are this module's business, so both are
49
+ * here: the ordering placement walks (`isDefault desc, sortOrder asc,
50
+ * createdAt asc`, which is *not* `listChannelAssignments`' ordering) and the
51
+ * default-warehouse fallback.
52
+ *
53
+ * The fallback is not dead code kept for tests. The boot-time
54
+ * `WarehouseChannelReconciler` binds the default warehouse to every channel,
55
+ * so a production deployment does not reach it — but a seed-skipped
56
+ * environment, and a channel whose only bound warehouses have been
57
+ * deactivated, both do, and answering nothing there would refuse every line
58
+ * rather than allocate against the default.
59
+ *
60
+ * Read through this module's own `EntityManager`, deliberately: it is the
61
+ * *binding*, not the stock. The caller reads and locks `stock_levels` itself,
62
+ * on its own transaction — see the port's doc comment for why
63
+ * `candidatesFor` cannot stand in here.
64
+ */
65
+ async listChannelWarehouses(salesChannelId) {
66
+ const em = this.emFactory();
67
+ const assignments = await em.find(WarehouseChannelAssignment, { salesChannelId }, { orderBy: { isDefault: 'desc', sortOrder: 'asc', createdAt: 'asc' } });
68
+ if (assignments.length > 0) {
69
+ const warehouses = await em.find(Warehouse, {
70
+ active: true,
71
+ id: { $in: assignments.map((a) => a.warehouseId) },
72
+ });
73
+ const byId = new Map(warehouses.map((w) => [w.id, w]));
74
+ const bound = assignments.flatMap((assignment) => {
75
+ const warehouse = byId.get(assignment.warehouseId);
76
+ // The `active` filter is a join condition, not a post-filter with a
77
+ // fabricated stand-in: a deactivated warehouse is not a candidate.
78
+ if (!warehouse)
79
+ return [];
80
+ return [
81
+ {
82
+ warehouseId: assignment.warehouseId,
83
+ warehouseCode: warehouse.code,
84
+ isDefault: assignment.isDefault,
85
+ },
86
+ ];
87
+ });
88
+ if (bound.length > 0)
89
+ return bound;
90
+ }
91
+ return [
92
+ {
93
+ warehouseId: DEFAULT_WAREHOUSE_ID,
94
+ warehouseCode: DEFAULT_WAREHOUSE_CODE,
95
+ isDefault: true,
96
+ },
97
+ ];
98
+ }
99
+ async candidatesFor(input) {
100
+ const em = this.emFactory();
101
+ // Which warehouses serve this channel. No channel, or a channel nobody
102
+ // assigned a warehouse to, means "every active warehouse" — the same
103
+ // fallback the availability resolver already applies, so a deployment that
104
+ // never configured the channel mapping keeps working.
105
+ const assignments = input.salesChannelId === null
106
+ ? []
107
+ : await em.find(WarehouseChannelAssignment, { salesChannelId: input.salesChannelId });
108
+ const assignedIds = assignments.map((a) => a.warehouseId);
109
+ const defaultIds = new Set(assignments.filter((a) => a.isDefault).map((a) => a.warehouseId));
110
+ const warehouses = await em.find(Warehouse, { active: true, ...(assignedIds.length > 0 ? { id: { $in: assignedIds } } : {}) }, { orderBy: { code: 'asc' } });
111
+ if (warehouses.length === 0)
112
+ return [];
113
+ const stock = await em.find(StockLevel, {
114
+ productId: input.productId,
115
+ variantId: input.variantId ?? null,
116
+ warehouseId: { $in: warehouses.map((w) => w.id) },
117
+ });
118
+ const byWarehouse = new Map(stock.map((s) => [s.warehouseId, s]));
119
+ return warehouses.map((warehouse) => {
120
+ const level = byWarehouse.get(warehouse.id);
121
+ return {
122
+ warehouseId: warehouse.id,
123
+ warehouseCode: warehouse.code,
124
+ available: level ? level.onHand - level.reserved : 0,
125
+ isDefault: defaultIds.has(warehouse.id),
126
+ };
127
+ });
128
+ }
129
+ }
130
+ export function toWarehouseRecord(warehouse) {
131
+ return {
132
+ id: warehouse.id,
133
+ name: warehouse.name,
134
+ code: warehouse.code,
135
+ active: warehouse.active,
136
+ description: warehouse.description ?? null,
137
+ address: warehouse.address ?? null,
138
+ contactName: warehouse.contactName ?? null,
139
+ contactEmail: warehouse.contactEmail ?? null,
140
+ contactPhone: warehouse.contactPhone ?? null,
141
+ defaultLowStockThreshold: warehouse.defaultLowStockThreshold ?? null,
142
+ createdAt: warehouse.createdAt,
143
+ updatedAt: warehouse.updatedAt,
144
+ };
145
+ }
146
+ export function toStockLevelRecord(level) {
147
+ return {
148
+ id: level.id,
149
+ productId: level.productId,
150
+ variantId: level.variantId ?? null,
151
+ warehouseId: level.warehouseId,
152
+ onHand: level.onHand,
153
+ reserved: level.reserved,
154
+ createdAt: level.createdAt,
155
+ updatedAt: level.updatedAt,
156
+ };
157
+ }
158
+ export function toWarehouseChannelAssignmentRecord(assignment) {
159
+ return {
160
+ id: assignment.id,
161
+ warehouseId: assignment.warehouseId,
162
+ salesChannelId: assignment.salesChannelId,
163
+ isDefault: assignment.isDefault,
164
+ sortOrder: assignment.sortOrder,
165
+ createdAt: assignment.createdAt,
166
+ };
167
+ }
168
+ //# sourceMappingURL=inventory-read-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-read-port.js","sourceRoot":"","sources":["../../../src/backend/services/inventory-read-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { InventoryReservationApplyPort, PlacementStockSnapshot } from '../../ports/index.js';
3
+ /**
4
+ * `inventoryReservationApplyPort` — the stock reservation order placement
5
+ * performs, written on the caller's `EntityManager` (feature 080, T048; D-169).
6
+ *
7
+ * `orders` held this module's `StockLevel` and `StockAllocation` classes for it
8
+ * until T048, and held them **twice**: statically at the top of
9
+ * `order-service.ts` for the reservation, and through an `await import()`
10
+ * inside `releaseAllocations` for the release. The header comment above those
11
+ * static imports claimed D-94.4 had removed the dynamic pair; it had not, and a
12
+ * dynamic import is invisible to a reviewer scanning the block, which is
13
+ * exactly the property a boundary exception must not have. Both spellings are
14
+ * gone with this file.
15
+ *
16
+ * **The transaction is unchanged, and that is the point of the conversion.**
17
+ * The `PESSIMISTIC_WRITE` is still taken on placement's own `EntityManager` and
18
+ * still held until the order commits; the allocation rows are still written
19
+ * after the order items are flushed, because
20
+ * `stock_allocations_order_item_fk` is `on delete restrict`; the release still
21
+ * runs in the transaction `releaseAllocations` opens. What changed is that the
22
+ * statements are written by the module that owns the two tables, and that what
23
+ * crosses the boundary is `PlacementStockSnapshot` — published records —
24
+ * instead of managed rows the consumer could move any column of (D-77's first
25
+ * narrowing).
26
+ *
27
+ * The port's own doc block, in `../ports/index.js`, states the constraint, the
28
+ * lock and the off-state answer.
29
+ */
30
+ export declare class InventoryReservationApplyService implements InventoryReservationApplyPort {
31
+ lockAvailabilityForPlacement(em: EntityManager, input: {
32
+ productId: string;
33
+ variantId: string | null;
34
+ warehouseIds: readonly string[];
35
+ }): Promise<PlacementStockSnapshot[]>;
36
+ reserveForPlacement(em: EntityManager, input: {
37
+ productId: string;
38
+ variantId: string | null;
39
+ allocations: ReadonlyArray<{
40
+ warehouseId: string;
41
+ quantity: number;
42
+ }>;
43
+ }): Promise<void>;
44
+ recordAllocationsForOrderItems(em: EntityManager, input: {
45
+ allocations: ReadonlyArray<{
46
+ orderItemId: string;
47
+ warehouseId: string;
48
+ quantity: number;
49
+ isBackorder: boolean;
50
+ }>;
51
+ }): Promise<void>;
52
+ releaseForOrderItems(em: EntityManager, input: {
53
+ items: ReadonlyArray<{
54
+ orderItemId: string;
55
+ productId: string;
56
+ variantId: string | null;
57
+ }>;
58
+ }): Promise<{
59
+ released: number;
60
+ }>;
61
+ }
62
+ //# sourceMappingURL=inventory-reservation-apply-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-reservation-apply-port.d.ts","sourceRoot":"","sources":["../../../src/backend/services/inventory-reservation-apply-port.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,KAAK,EACV,6BAA6B,EAC7B,sBAAsB,EACvB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,gCAAiC,YAAW,6BAA6B;IAC9E,4BAA4B,CAChC,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;KACjC,GACA,OAAO,CAAC,sBAAsB,EAAE,CAAC;IAqB9B,mBAAmB,CACvB,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,WAAW,EAAE,aAAa,CAAC;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACvE,GACA,OAAO,CAAC,IAAI,CAAC;IAsCV,8BAA8B,CAClC,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE;QACL,WAAW,EAAE,aAAa,CAAC;YACzB,WAAW,EAAE,MAAM,CAAC;YACpB,WAAW,EAAE,MAAM,CAAC;YACpB,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,EAAE,OAAO,CAAC;SACtB,CAAC,CAAC;KACJ,GACA,OAAO,CAAC,IAAI,CAAC;IAiBV,oBAAoB,CACxB,EAAE,EAAE,aAAa,EACjB,KAAK,EAAE;QACL,KAAK,EAAE,aAAa,CAAC;YACnB,WAAW,EAAE,MAAM,CAAC;YACpB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;SAC1B,CAAC,CAAC;KACJ,GACA,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CA+BjC"}