@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,124 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { StockLevel } from '../entities/stock-level.entity.js';
3
+ import { Warehouse } from '../entities/warehouse.entity.js';
4
+ import { DEFAULT_WAREHOUSE_ID } from '../entities/warehouse.entity.js';
5
+ /**
6
+ * Bulk import of stock levels (feature 075, D-74).
7
+ *
8
+ * The counterpart of `catalog`'s bulk import, and it exists for the same three
9
+ * reasons: the write is one Command and one audit row, validation runs before
10
+ * anything is applied, and the whole run is all-or-nothing.
11
+ *
12
+ * Two things came back to this module with it. The rows are `stock_levels`, and
13
+ * `import_export` was writing them while declaring a dependency on `catalog` and
14
+ * `auth` only — an edge no check in the tree could see, because this module
15
+ * ships the migration and that one ships none. And the seeded warehouse was
16
+ * addressed by a **copy** of the deterministic UUID this module exports, pasted
17
+ * into a function body; a duplicated constant is a coupling `check:module-boundary`
18
+ * is structurally unable to report, since it reads import specifiers.
19
+ *
20
+ * The row carries no warehouse, which is the pre-existing contract of the CSV
21
+ * path rather than a simplification taken here: foundation 001's sheet has no
22
+ * warehouse column, so every row lands in the default warehouse. The
23
+ * warehouse-scoped importer is `CsvStockImporter`, reached from this module's
24
+ * own admin route.
25
+ */
26
+ export class InventoryStockImportService {
27
+ commandBus;
28
+ products;
29
+ constructor(commandBus,
30
+ /**
31
+ * SKU → product id. A spreadsheet addresses a product the way an operator
32
+ * does, so somebody has to ask `catalog`; doing it here is what lets the
33
+ * rejected-row message ("unknown product_sku") be raised by the module that
34
+ * knows the answer.
35
+ */
36
+ products) {
37
+ this.commandBus = commandBus;
38
+ this.products = products;
39
+ }
40
+ async importStockLevels(rows) {
41
+ if (rows.length === 0)
42
+ return { imported: 0, errors: [] };
43
+ // Outside the Command: resolving a SKU is a read of another module, and it
44
+ // does not belong inside this module's transaction.
45
+ const products = await this.products.findBySkus(rows.map((row) => row.productSku));
46
+ const idBySku = new Map(products.map((product) => [product.sku, product.id]));
47
+ return this.commandBus.run({
48
+ action: 'inventory.stock_levels.import',
49
+ objectType: 'stock_level',
50
+ objectId: randomUUID(),
51
+ run: async ({ em }) => {
52
+ const errors = [];
53
+ for (const [index, row] of rows.entries()) {
54
+ if (!idBySku.has(row.productSku)) {
55
+ errors.push({ index, reason: `unknown product_sku: ${row.productSku}` });
56
+ }
57
+ }
58
+ if (errors.length > 0)
59
+ return { result: { imported: 0, errors }, skipAudit: true };
60
+ const warehouseCodes = [
61
+ ...new Set(rows.map((row) => row.warehouseCode).filter((code) => !!code)),
62
+ ];
63
+ const warehouseIdByCode = new Map();
64
+ if (warehouseCodes.length > 0) {
65
+ const warehouses = await em.find(Warehouse, { code: { $in: warehouseCodes } });
66
+ for (const warehouse of warehouses) {
67
+ warehouseIdByCode.set(warehouse.code, warehouse.id);
68
+ }
69
+ for (const [index, row] of rows.entries()) {
70
+ if (row.warehouseCode && !warehouseIdByCode.has(row.warehouseCode)) {
71
+ errors.push({ index, reason: `unknown warehouse_code: ${row.warehouseCode}` });
72
+ }
73
+ }
74
+ if (errors.length > 0)
75
+ return { result: { imported: 0, errors }, skipAudit: true };
76
+ }
77
+ const productIds = [...new Set(rows.map((row) => idBySku.get(row.productSku)))];
78
+ const warehouseIds = [
79
+ ...new Set(rows.map((row) => row.warehouseCode ? warehouseIdByCode.get(row.warehouseCode) : DEFAULT_WAREHOUSE_ID)),
80
+ ];
81
+ const levels = await em.find(StockLevel, {
82
+ productId: { $in: productIds },
83
+ warehouseId: { $in: warehouseIds },
84
+ });
85
+ const byKey = new Map(levels.map((level) => [
86
+ levelKey(level.productId, level.variantId ?? null, level.warehouseId),
87
+ level,
88
+ ]));
89
+ for (const row of rows) {
90
+ const productId = idBySku.get(row.productSku);
91
+ const variantId = row.variantId ?? null;
92
+ const warehouseId = row.warehouseCode
93
+ ? warehouseIdByCode.get(row.warehouseCode)
94
+ : DEFAULT_WAREHOUSE_ID;
95
+ const existing = byKey.get(levelKey(productId, variantId, warehouseId));
96
+ if (existing) {
97
+ existing.onHand = row.onHand;
98
+ continue;
99
+ }
100
+ const created = em.create(StockLevel, {
101
+ productId,
102
+ ...(variantId !== null ? { variantId } : {}),
103
+ warehouseId,
104
+ onHand: row.onHand,
105
+ });
106
+ // A second row for the same product and variant updates what the first
107
+ // one created, rather than inserting a duplicate the partial-unique
108
+ // index would refuse at flush.
109
+ byKey.set(levelKey(productId, variantId, warehouseId), created);
110
+ }
111
+ await em.flush();
112
+ return {
113
+ result: { imported: rows.length, errors: [] },
114
+ after: { rows: rows.length, warehouseIds },
115
+ };
116
+ },
117
+ });
118
+ }
119
+ }
120
+ /** `(productId, variantId, warehouseId)` as one map key. */
121
+ function levelKey(productId, variantId, warehouseId) {
122
+ return `${productId}:${variantId ?? ''}:${warehouseId}`;
123
+ }
124
+ //# sourceMappingURL=stock-import.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-import.service.js","sourceRoot":"","sources":["../../../src/backend/services/stock-import.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { CatalogCategoryReadPort, CatalogProductReadPort } from '@endora-commerce/contracts';
3
+ import type { EventBus } from '@endora-commerce/platform/events';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ import type { InventoryAuditContext } from '../plugin.js';
6
+ export interface InventoryAdjustedEvent {
7
+ eventId: string;
8
+ occurredAt: string;
9
+ productId: string;
10
+ warehouseId: string;
11
+ variantId: string | null;
12
+ before: number;
13
+ after: number;
14
+ }
15
+ import { DEFAULT_WAREHOUSE_ID } from '../entities/warehouse.entity.js';
16
+ import { type DisplayBand } from './display-band-resolver.js';
17
+ export interface PerWarehouseStockRow {
18
+ warehouseId: string;
19
+ warehouseCode: string;
20
+ onHand: number;
21
+ reserved: number;
22
+ /** Effective low-stock threshold resolved per-warehouse (see roster docs). */
23
+ lowStockThreshold: number | null;
24
+ }
25
+ export interface StockLevelRow {
26
+ productId: string;
27
+ productSku: string;
28
+ productName: string;
29
+ manageStock: boolean;
30
+ backorderEnabled: boolean;
31
+ lowStockThreshold: number | null;
32
+ lowStockThresholdMode: 'cumulative' | 'per_warehouse';
33
+ perWarehouse: PerWarehouseStockRow[];
34
+ cumulativeOnHand: number;
35
+ displayBand: DisplayBand;
36
+ isLowStock: boolean;
37
+ isOutOfStock: boolean;
38
+ }
39
+ export interface InventoryLandingKpis {
40
+ totalProductsTracked: number;
41
+ totalOnHand: number;
42
+ outOfStockCount: number;
43
+ lowStockCount: number;
44
+ perWarehouseTotals: Array<{
45
+ warehouseId: string;
46
+ warehouseCode: string;
47
+ onHand: number;
48
+ }>;
49
+ }
50
+ export interface SetOnHandInput {
51
+ productId: string;
52
+ warehouseId: string;
53
+ variantId?: string | null;
54
+ onHand: number;
55
+ }
56
+ export interface SetOnHandResult {
57
+ before: number;
58
+ after: number;
59
+ productId: string;
60
+ warehouseId: string;
61
+ variantId: string | null;
62
+ }
63
+ /**
64
+ * StockLevelService (US2) — read + mutate per-(product, warehouse) on-hand.
65
+ *
66
+ * The setter is the single point that emits `inventory.adjusted.v1` so the
67
+ * crossing detector (US4) and the restock fan-out (US6) can react. Reserved
68
+ * counters stay driven by the order service.
69
+ */
70
+ export declare class StockLevelService {
71
+ private readonly emFactory;
72
+ /**
73
+ * `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C).
74
+ * Every roster row, every KPI and every band this service computes joins a
75
+ * product it does not own; it used to load the entity, so an operator who
76
+ * switched `catalog` off still got a full stock roster out of it.
77
+ */
78
+ private readonly catalogProducts;
79
+ /** `catalogCategoryReadPort`, owned by `catalog` — the threshold chain. */
80
+ private readonly catalogCategories;
81
+ private readonly eventBus?;
82
+ private readonly auditLog?;
83
+ constructor(emFactory: () => EntityManager,
84
+ /**
85
+ * `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C).
86
+ * Every roster row, every KPI and every band this service computes joins a
87
+ * product it does not own; it used to load the entity, so an operator who
88
+ * switched `catalog` off still got a full stock roster out of it.
89
+ */
90
+ catalogProducts: CatalogProductReadPort,
91
+ /** `catalogCategoryReadPort`, owned by `catalog` — the threshold chain. */
92
+ catalogCategories: CatalogCategoryReadPort, eventBus?: EventBus | undefined, auditLog?: AuditPort | undefined);
93
+ listLandingKpis(): Promise<InventoryLandingKpis>;
94
+ listRoster(filter?: {
95
+ productId?: string;
96
+ warehouseId?: string;
97
+ page?: number;
98
+ pageSize?: number;
99
+ /** Case-insensitive substring search across product SKU, name, and id. */
100
+ q?: string;
101
+ /** When true, return only products whose cumulative on-hand is at or
102
+ * below the low-stock threshold (and >0 — out-of-stock is its own filter). */
103
+ lowOnly?: boolean;
104
+ /** When true, return only products whose cumulative on-hand is 0
105
+ * (and `manageStock = true` — backorderable / unmanaged products
106
+ * aren't considered out). */
107
+ outOnly?: boolean;
108
+ }): Promise<{
109
+ items: StockLevelRow[];
110
+ page: number;
111
+ pageSize: number;
112
+ total: number;
113
+ }>;
114
+ /**
115
+ * Replace the per-warehouse low-stock threshold map for one product.
116
+ * Entries with `threshold === null` are deleted; the rest are upserted.
117
+ * Warehouse rows not mentioned in `entries` are left untouched (callers
118
+ * that want a hard replace should explicitly include `threshold: null`
119
+ * for every (product, warehouse) they wish to clear).
120
+ */
121
+ setProductWarehouseThresholds(input: {
122
+ productId: string;
123
+ entries: Array<{
124
+ warehouseId: string;
125
+ threshold: number | null;
126
+ }>;
127
+ }): Promise<void>;
128
+ setOnHand(input: SetOnHandInput, auditCtx?: InventoryAuditContext): Promise<SetOnHandResult>;
129
+ /**
130
+ * Feature 062 — batch availability indication for the external catalog
131
+ * surface. Mirrors the storefront per-product stock readout: cumulative
132
+ * on-hand (optionally restricted to the channel's candidate warehouses)
133
+ * mapped through the product/category/global threshold chain to a display
134
+ * band; `inStock` is the same channel-public flag the storefront derives
135
+ * (`manageStock && onHand <= 0` ⇒ out of stock). Products with no stock
136
+ * rows still get an entry (out_of_stock or `available` when unmanaged).
137
+ */
138
+ resolveAvailabilityBands(productIds: string[], candidateWarehouseIds?: string[]): Promise<Map<string, {
139
+ band: DisplayBand;
140
+ inStock: boolean;
141
+ }>>;
142
+ private loadGlobalThresholds;
143
+ private loadProductThresholds;
144
+ }
145
+ export { DEFAULT_WAREHOUSE_ID };
146
+ //# sourceMappingURL=stock-level-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-level-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/stock-level-service.ts"],"names":[],"mappings":"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"}