@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,205 @@
1
+ import { HttpError } from '@endora-commerce/platform/http';
2
+ import { actorFromContext } from '@endora-commerce/platform/commands';
3
+ import { getTenantContext } from '@endora-commerce/platform/tenancy';
4
+ import { Warehouse } from '../entities/warehouse.entity.js';
5
+ import { WarehouseChannelAssignment } from '../entities/warehouse-channel-assignment.entity.js';
6
+ /**
7
+ * WarehouseChannelService (US3) — manages the m:n binding between
8
+ * warehouses and sales channels with at most one `isDefault = true`
9
+ * row per channel.
10
+ *
11
+ * Invariants:
12
+ * - Every active channel must have at least one warehouse (refuse
13
+ * `unassign` when this would leave the channel orphaned).
14
+ * - At most one assignment per channel carries `isDefault = true`
15
+ * (`setDefault` demotes the previous default in the same flush).
16
+ */
17
+ export class WarehouseChannelService {
18
+ emFactory;
19
+ auditLog;
20
+ constructor(emFactory,
21
+ /** Feature 054 — audits assignment writes co-transactionally when provided. */
22
+ auditLog) {
23
+ this.emFactory = emFactory;
24
+ this.auditLog = auditLog;
25
+ }
26
+ /**
27
+ * Feature 054 — record a co-transactional audit entry for a bridge write on
28
+ * the passed `em` (committed by the caller's flush). Actor is resolved from
29
+ * the ambient TenantContext; the admin routes always carry one.
30
+ */
31
+ #audit(em, action, objectId, stateBefore, stateAfter) {
32
+ if (!this.auditLog)
33
+ return;
34
+ const ctx = getTenantContext();
35
+ const actor = ctx ? actorFromContext(ctx) : null;
36
+ this.auditLog.recordWithin(em, {
37
+ action,
38
+ objectType: 'warehouse_channel_assignment',
39
+ objectId,
40
+ actorAdminUserId: actor?.actorAdminUserId ?? null,
41
+ impersonatedCustomerAccountId: actor?.impersonatedCustomerAccountId ?? null,
42
+ stateBefore,
43
+ stateAfter,
44
+ });
45
+ }
46
+ async listForChannel(channelId) {
47
+ const em = this.emFactory();
48
+ const rows = await em.find(WarehouseChannelAssignment, { salesChannelId: channelId }, {
49
+ orderBy: { sortOrder: 'asc', createdAt: 'asc' },
50
+ });
51
+ if (rows.length === 0)
52
+ return [];
53
+ const warehouses = await em.find(Warehouse, { id: { $in: rows.map((r) => r.warehouseId) } });
54
+ const byId = new Map(warehouses.map((w) => [w.id, w]));
55
+ return rows.map((r) => ({
56
+ id: r.id,
57
+ warehouseId: r.warehouseId,
58
+ warehouseName: byId.get(r.warehouseId)?.name ?? '',
59
+ warehouseCode: byId.get(r.warehouseId)?.code ?? '',
60
+ isDefault: r.isDefault,
61
+ sortOrder: r.sortOrder,
62
+ assignedAt: r.createdAt.toISOString(),
63
+ }));
64
+ }
65
+ async listChannelsForWarehouse(warehouseId) {
66
+ const em = this.emFactory();
67
+ const rows = await em.find(WarehouseChannelAssignment, { warehouseId });
68
+ return rows.map((r) => ({
69
+ salesChannelId: r.salesChannelId,
70
+ isDefault: r.isDefault,
71
+ sortOrder: r.sortOrder,
72
+ }));
73
+ }
74
+ async assign(channelId, input) {
75
+ const em = this.emFactory();
76
+ const warehouse = await em.findOne(Warehouse, { id: input.warehouseId });
77
+ if (!warehouse) {
78
+ throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found');
79
+ }
80
+ const existing = await em.findOne(WarehouseChannelAssignment, {
81
+ salesChannelId: channelId,
82
+ warehouseId: input.warehouseId,
83
+ });
84
+ if (existing) {
85
+ // Idempotent — update sortOrder + isDefault per input but keep id.
86
+ if (input.isDefault === true) {
87
+ await this.demoteOtherDefaults(em, channelId, existing.id);
88
+ existing.isDefault = true;
89
+ }
90
+ if (input.sortOrder !== undefined)
91
+ existing.sortOrder = input.sortOrder;
92
+ this.#audit(em, 'warehouse_channel.assign', existing.id, null, {
93
+ warehouseId: existing.warehouseId,
94
+ salesChannelId: channelId,
95
+ isDefault: existing.isDefault,
96
+ sortOrder: existing.sortOrder,
97
+ });
98
+ await em.flush();
99
+ return this.toDTO(existing, warehouse);
100
+ }
101
+ if (input.isDefault === true) {
102
+ await this.demoteOtherDefaults(em, channelId, null);
103
+ }
104
+ const row = em.create(WarehouseChannelAssignment, {
105
+ warehouseId: input.warehouseId,
106
+ salesChannelId: channelId,
107
+ isDefault: input.isDefault ?? false,
108
+ sortOrder: input.sortOrder ?? 0,
109
+ });
110
+ em.persist(row);
111
+ this.#audit(em, 'warehouse_channel.assign', row.id, null, {
112
+ warehouseId: row.warehouseId,
113
+ salesChannelId: channelId,
114
+ isDefault: row.isDefault,
115
+ sortOrder: row.sortOrder,
116
+ });
117
+ await em.flush();
118
+ return this.toDTO(row, warehouse);
119
+ }
120
+ async patch(channelId, assignmentId, input) {
121
+ const em = this.emFactory();
122
+ const row = await em.findOne(WarehouseChannelAssignment, {
123
+ id: assignmentId,
124
+ salesChannelId: channelId,
125
+ });
126
+ if (!row) {
127
+ throw new HttpError(404, 'CHANNEL_WAREHOUSE_NOT_FOUND', 'Assignment not found');
128
+ }
129
+ if (input.isDefault === true && !row.isDefault) {
130
+ await this.demoteOtherDefaults(em, channelId, row.id);
131
+ row.isDefault = true;
132
+ }
133
+ else if (input.isDefault === false && row.isDefault) {
134
+ // Refuse — channel must keep exactly one default. Operator should
135
+ // promote a different assignment first.
136
+ throw new HttpError(409, 'CHANNEL_NO_WAREHOUSES', 'Promote another warehouse to default before demoting this one');
137
+ }
138
+ if (input.sortOrder !== undefined)
139
+ row.sortOrder = input.sortOrder;
140
+ this.#audit(em, 'warehouse_channel.update', row.id, null, {
141
+ isDefault: row.isDefault,
142
+ sortOrder: row.sortOrder,
143
+ });
144
+ await em.flush();
145
+ const warehouse = await em.findOne(Warehouse, { id: row.warehouseId });
146
+ return this.toDTO(row, warehouse);
147
+ }
148
+ async unassign(channelId, assignmentId) {
149
+ const em = this.emFactory();
150
+ const row = await em.findOne(WarehouseChannelAssignment, {
151
+ id: assignmentId,
152
+ salesChannelId: channelId,
153
+ });
154
+ if (!row) {
155
+ throw new HttpError(404, 'CHANNEL_WAREHOUSE_NOT_FOUND', 'Assignment not found');
156
+ }
157
+ const totalForChannel = await em.count(WarehouseChannelAssignment, {
158
+ salesChannelId: channelId,
159
+ });
160
+ if (totalForChannel <= 1) {
161
+ throw new HttpError(409, 'CHANNEL_NO_WAREHOUSES', 'Channel must have at least one warehouse; assign another before unassigning this one');
162
+ }
163
+ if (row.isDefault) {
164
+ throw new HttpError(409, 'CHANNEL_NO_WAREHOUSES', 'Promote another warehouse to default before unassigning the current default');
165
+ }
166
+ this.#audit(em, 'warehouse_channel.unassign', row.id, {
167
+ warehouseId: row.warehouseId,
168
+ salesChannelId: channelId,
169
+ isDefault: row.isDefault,
170
+ sortOrder: row.sortOrder,
171
+ }, null);
172
+ await em.removeAndFlush(row);
173
+ }
174
+ /**
175
+ * Resolves the warehouses bound to a sales channel for the storefront
176
+ * cumulative-on-hand readout (research §R7).
177
+ */
178
+ async resolveCandidateWarehouseIds(channelId) {
179
+ const em = this.emFactory();
180
+ const rows = await em.find(WarehouseChannelAssignment, { salesChannelId: channelId }, {
181
+ orderBy: { isDefault: 'desc', sortOrder: 'asc', createdAt: 'asc' },
182
+ });
183
+ return rows.map((r) => r.warehouseId);
184
+ }
185
+ async demoteOtherDefaults(em, channelId, keepId) {
186
+ const where = { salesChannelId: channelId, isDefault: true };
187
+ if (keepId)
188
+ where['id'] = { $ne: keepId };
189
+ const rows = await em.find(WarehouseChannelAssignment, where);
190
+ for (const r of rows)
191
+ r.isDefault = false;
192
+ }
193
+ toDTO(row, warehouse) {
194
+ return {
195
+ id: row.id,
196
+ warehouseId: row.warehouseId,
197
+ warehouseName: warehouse.name,
198
+ warehouseCode: warehouse.code,
199
+ isDefault: row.isDefault,
200
+ sortOrder: row.sortOrder,
201
+ assignedAt: row.createdAt.toISOString(),
202
+ };
203
+ }
204
+ }
205
+ //# sourceMappingURL=warehouse-channel-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warehouse-channel-service.js","sourceRoot":"","sources":["../../../src/backend/services/warehouse-channel-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { DictionaryReferenceRegistryPort } from '@endora-commerce/contracts';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ /**
4
+ * Who in `inventory` still points at a country (feature 077, D-87).
5
+ *
6
+ * `dictionaries` joined `warehouses` for its orphan report but deliberately
7
+ * counted **zero** of them before a delete: the country is inside the
8
+ * `address` JSONB column, and its comment said covering that path "requires
9
+ * a separate scan that v1 of the Dictionary feature defers". The scan is one
10
+ * expression against a table this module owns, so moving the question to the
11
+ * module that can answer it closed the gap as a side effect.
12
+ */
13
+ export declare function registerWarehouseCountryReferences(registry: DictionaryReferenceRegistryPort, emFactory: () => EntityManager): void;
14
+ //# sourceMappingURL=warehouse-country-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warehouse-country-reference.d.ts","sourceRoot":"","sources":["../../../src/backend/services/warehouse-country-reference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,+BAA+B,EACzC,SAAS,EAAE,MAAM,aAAa,GAC7B,IAAI,CAqBN"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Who in `inventory` still points at a country (feature 077, D-87).
3
+ *
4
+ * `dictionaries` joined `warehouses` for its orphan report but deliberately
5
+ * counted **zero** of them before a delete: the country is inside the
6
+ * `address` JSONB column, and its comment said covering that path "requires
7
+ * a separate scan that v1 of the Dictionary feature defers". The scan is one
8
+ * expression against a table this module owns, so moving the question to the
9
+ * module that can answer it closed the gap as a side effect.
10
+ */
11
+ export function registerWarehouseCountryReferences(registry, emFactory) {
12
+ registry.register({
13
+ ownerModuleId: 'inventory',
14
+ consumer: 'warehouses',
15
+ tableName: 'warehouses',
16
+ columnName: 'address.countryCode',
17
+ blocking: true,
18
+ countReferences: async (code) => {
19
+ const rows = (await emFactory().execute(`select count(*)::int as n from "warehouses" where "address"->>'countryCode' = ?`, [code]));
20
+ return rows[0]?.n ?? 0;
21
+ },
22
+ usedCodes: async () => (await emFactory().execute(`select "address"->>'countryCode' as code, count(*)::int as count
23
+ from "warehouses" where "address"->>'countryCode' is not null
24
+ group by "address"->>'countryCode'`)),
25
+ });
26
+ }
27
+ //# sourceMappingURL=warehouse-country-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warehouse-country-reference.js","sourceRoot":"","sources":["../../../src/backend/services/warehouse-country-reference.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,UAAU,kCAAkC,CAChD,QAAyC,EACzC,SAA8B;IAE9B,QAAQ,CAAC,QAAQ,CAAC;QAChB,aAAa,EAAE,WAAW;QAC1B,QAAQ,EAAE,YAAY;QACtB,SAAS,EAAE,YAAY;QACvB,UAAU,EAAE,qBAAqB;QACjC,QAAQ,EAAE,IAAI;QACd,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CACrC,iFAAiF,EACjF,CAAC,IAAI,CAAC,CACP,CAAyB,CAAC;YAC3B,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QACD,SAAS,EAAE,KAAK,IAAI,EAAE,CACpB,CAAC,MAAM,SAAS,EAAE,CAAC,OAAO,CACxB;;6CAEqC,CACtC,CAA2C;KAC/C,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,86 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import { type DictionaryValidator } from '@endora-commerce/contracts';
3
+ import { type WarehouseAddress } from '../entities/warehouse.entity.js';
4
+ import type { AuditPort } from '@endora-commerce/platform/kernel';
5
+ import type { InventoryAuditContext } from '../plugin.js';
6
+ export interface WarehouseContact {
7
+ name?: string | null | undefined;
8
+ email?: string | null | undefined;
9
+ phone?: string | null | undefined;
10
+ }
11
+ export interface CreateWarehouseInput {
12
+ name: string;
13
+ code: string;
14
+ active?: boolean;
15
+ description?: string | null;
16
+ address?: WarehouseAddress | null;
17
+ contact?: WarehouseContact | null;
18
+ defaultLowStockThreshold?: number | null;
19
+ }
20
+ export interface UpdateWarehouseInput {
21
+ name?: string;
22
+ active?: boolean;
23
+ description?: string | null;
24
+ address?: WarehouseAddress | null;
25
+ contact?: WarehouseContact | null;
26
+ defaultLowStockThreshold?: number | null;
27
+ }
28
+ export interface WarehouseTotals {
29
+ products: number;
30
+ onHand: number;
31
+ isDefaultForChannelCount: number;
32
+ }
33
+ export interface WarehouseDTO {
34
+ id: string;
35
+ name: string;
36
+ code: string;
37
+ active: boolean;
38
+ description: string | null;
39
+ address: WarehouseAddress | null;
40
+ contact: WarehouseContact | null;
41
+ defaultLowStockThreshold: number | null;
42
+ totals?: WarehouseTotals;
43
+ createdAt: string;
44
+ updatedAt: string;
45
+ }
46
+ /**
47
+ * WarehouseService (US1) — admin-side CRUD + lifecycle for warehouses.
48
+ *
49
+ * The seeded `Default` warehouse is treated as a first-class row but
50
+ * carries two extra invariants:
51
+ * - its `code` cannot change (callers can't supply it on update);
52
+ * - it cannot be deleted while it is the default for any channel.
53
+ *
54
+ * Stock-bearing warehouses (any row in `stock_levels` referencing the
55
+ * warehouse) likewise refuse delete; admins must move stock first.
56
+ */
57
+ export declare class WarehouseService {
58
+ private readonly emFactory;
59
+ private readonly dictionaryValidator?;
60
+ private readonly auditLog?;
61
+ constructor(emFactory: () => EntityManager, dictionaryValidator?: DictionaryValidator | undefined, auditLog?: AuditPort | undefined);
62
+ list(options?: {
63
+ page?: number;
64
+ pageSize?: number;
65
+ activeOnly?: boolean;
66
+ withTotals?: boolean;
67
+ }): Promise<{
68
+ items: WarehouseDTO[];
69
+ page: number;
70
+ pageSize: number;
71
+ total: number;
72
+ }>;
73
+ getById(id: string, options?: {
74
+ withTotals?: boolean;
75
+ }): Promise<WarehouseDTO | null>;
76
+ create(input: CreateWarehouseInput, auditCtx?: InventoryAuditContext): Promise<WarehouseDTO>;
77
+ update(id: string, input: UpdateWarehouseInput, auditCtx?: InventoryAuditContext): Promise<WarehouseDTO>;
78
+ private validateCountry;
79
+ deactivate(id: string, auditCtx?: InventoryAuditContext): Promise<WarehouseDTO>;
80
+ reactivate(id: string, auditCtx?: InventoryAuditContext): Promise<WarehouseDTO>;
81
+ private audit;
82
+ delete(id: string, auditCtx?: InventoryAuditContext): Promise<void>;
83
+ private collectTotals;
84
+ private toDTO;
85
+ }
86
+ //# sourceMappingURL=warehouse-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warehouse-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/warehouse-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,SAAS,EAAE,MAAM,aAAa,EAC9B,mBAAmB,CAAC,EAAE,mBAAmB,YAAA,EACzC,QAAQ,CAAC,EAAE,SAAS,YAAA;IAGjC,IAAI,CAAC,OAAO,GAAE;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA0BpF,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IASzF,MAAM,CACV,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,OAAO,CAAC,YAAY,CAAC;IA4BlB,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,oBAAoB,EAC3B,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,OAAO,CAAC,YAAY,CAAC;YA8CV,eAAe;IAsBvB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;IAI/E,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;YAIvE,KAAK;IAwBb,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;YA2C3D,aAAa;IAgD3B,OAAO,CAAC,KAAK;CAuBd"}
@@ -0,0 +1,267 @@
1
+ import { UniqueConstraintViolationException } from '@mikro-orm/core';
2
+ import { DictionaryReferenceError, dispatchValidatorMode, } from '@endora-commerce/contracts';
3
+ import { HttpError } from '@endora-commerce/platform/http';
4
+ import { Warehouse, DEFAULT_WAREHOUSE_CODE, } from '../entities/warehouse.entity.js';
5
+ import { WarehouseChannelAssignment } from '../entities/warehouse-channel-assignment.entity.js';
6
+ import { StockLevel } from '../entities/stock-level.entity.js';
7
+ /**
8
+ * WarehouseService (US1) — admin-side CRUD + lifecycle for warehouses.
9
+ *
10
+ * The seeded `Default` warehouse is treated as a first-class row but
11
+ * carries two extra invariants:
12
+ * - its `code` cannot change (callers can't supply it on update);
13
+ * - it cannot be deleted while it is the default for any channel.
14
+ *
15
+ * Stock-bearing warehouses (any row in `stock_levels` referencing the
16
+ * warehouse) likewise refuse delete; admins must move stock first.
17
+ */
18
+ export class WarehouseService {
19
+ emFactory;
20
+ dictionaryValidator;
21
+ auditLog;
22
+ constructor(emFactory, dictionaryValidator, auditLog) {
23
+ this.emFactory = emFactory;
24
+ this.dictionaryValidator = dictionaryValidator;
25
+ this.auditLog = auditLog;
26
+ }
27
+ async list(options = {}) {
28
+ const em = this.emFactory();
29
+ const page = Math.max(0, options.page ?? 0);
30
+ const pageSize = Math.min(Math.max(1, options.pageSize ?? 50), 200);
31
+ const where = {};
32
+ if (options.activeOnly)
33
+ where['active'] = true;
34
+ const [rows, total] = await em.findAndCount(Warehouse, where, {
35
+ orderBy: { createdAt: 'asc' },
36
+ offset: page * pageSize,
37
+ limit: pageSize,
38
+ });
39
+ let totalsByWarehouse = new Map();
40
+ if (options.withTotals && rows.length > 0) {
41
+ totalsByWarehouse = await this.collectTotals(em, rows.map((r) => r.id));
42
+ }
43
+ return {
44
+ items: rows.map((r) => this.toDTO(r, totalsByWarehouse.get(r.id))),
45
+ page,
46
+ pageSize,
47
+ total,
48
+ };
49
+ }
50
+ async getById(id, options = {}) {
51
+ const em = this.emFactory();
52
+ const row = await em.findOne(Warehouse, { id });
53
+ if (!row)
54
+ return null;
55
+ if (!options.withTotals)
56
+ return this.toDTO(row);
57
+ const totals = await this.collectTotals(em, [row.id]);
58
+ return this.toDTO(row, totals.get(row.id));
59
+ }
60
+ async create(input, auditCtx) {
61
+ if (input.address?.countryCode) {
62
+ await this.validateCountry(input.address.countryCode, 'create-or-change');
63
+ }
64
+ const em = this.emFactory();
65
+ const row = em.create(Warehouse, {
66
+ name: input.name,
67
+ code: input.code,
68
+ active: input.active ?? true,
69
+ description: input.description ?? null,
70
+ address: input.address ?? null,
71
+ contactName: input.contact?.name ?? null,
72
+ contactEmail: input.contact?.email ?? null,
73
+ contactPhone: input.contact?.phone ?? null,
74
+ defaultLowStockThreshold: input.defaultLowStockThreshold ?? null,
75
+ });
76
+ try {
77
+ await em.persistAndFlush(row);
78
+ }
79
+ catch (err) {
80
+ if (err instanceof UniqueConstraintViolationException) {
81
+ throw new HttpError(409, 'WAREHOUSE_CODE_TAKEN', 'Warehouse code already in use');
82
+ }
83
+ throw err;
84
+ }
85
+ await this.audit('warehouse.create', row, null, snapshotOf(row), auditCtx);
86
+ return this.toDTO(row);
87
+ }
88
+ async update(id, input, auditCtx) {
89
+ const em = this.emFactory();
90
+ const row = await em.findOne(Warehouse, { id });
91
+ if (!row)
92
+ throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found');
93
+ if (input.address?.countryCode) {
94
+ await this.validateCountry(input.address.countryCode, dispatchValidatorMode(row.address?.countryCode, input.address.countryCode));
95
+ }
96
+ const before = snapshotOf(row);
97
+ const activeBefore = row.active;
98
+ if (input.name !== undefined)
99
+ row.name = input.name;
100
+ if (input.active !== undefined)
101
+ row.active = input.active;
102
+ if (input.description !== undefined)
103
+ row.description = input.description;
104
+ if (input.address !== undefined)
105
+ row.address = input.address;
106
+ if (input.contact !== undefined) {
107
+ row.contactName = input.contact?.name ?? null;
108
+ row.contactEmail = input.contact?.email ?? null;
109
+ row.contactPhone = input.contact?.phone ?? null;
110
+ }
111
+ if (input.defaultLowStockThreshold !== undefined) {
112
+ row.defaultLowStockThreshold = input.defaultLowStockThreshold;
113
+ }
114
+ await em.persistAndFlush(row);
115
+ // Feature 024 — emit deactivate / reactivate when the only meaningful
116
+ // change was the active flip; otherwise emit a plain `warehouse.update`.
117
+ const after = snapshotOf(row);
118
+ const activeFlipped = activeBefore !== row.active;
119
+ const isPureActiveFlip = activeFlipped &&
120
+ input.name === undefined &&
121
+ input.description === undefined &&
122
+ input.address === undefined &&
123
+ input.contact === undefined;
124
+ if (isPureActiveFlip && row.active) {
125
+ await this.audit('warehouse.reactivate', row, before, after, auditCtx);
126
+ }
127
+ else if (isPureActiveFlip && !row.active) {
128
+ await this.audit('warehouse.deactivate', row, before, after, auditCtx);
129
+ }
130
+ else {
131
+ await this.audit('warehouse.update', row, before, after, auditCtx);
132
+ }
133
+ return this.toDTO(row);
134
+ }
135
+ async validateCountry(countryCode, mode) {
136
+ if (!this.dictionaryValidator)
137
+ return;
138
+ try {
139
+ await this.dictionaryValidator.validateCountryCode(countryCode, mode);
140
+ }
141
+ catch (err) {
142
+ if (err instanceof DictionaryReferenceError) {
143
+ throw new HttpError(409, err.code, err.code === 'DICTIONARY_ENTRY_INACTIVE'
144
+ ? `Country code ${err.entryCode} is no longer available for warehouses.`
145
+ : `Country code ${err.entryCode} is not recognised.`, [{ path: 'address.countryCode', issue: err.code }]);
146
+ }
147
+ throw err;
148
+ }
149
+ }
150
+ async deactivate(id, auditCtx) {
151
+ return this.update(id, { active: false }, auditCtx);
152
+ }
153
+ async reactivate(id, auditCtx) {
154
+ return this.update(id, { active: true }, auditCtx);
155
+ }
156
+ async audit(action, row, stateBefore, stateAfter, auditCtx) {
157
+ if (!this.auditLog || !auditCtx)
158
+ return;
159
+ await this.auditLog.record({
160
+ actorAdminUserId: auditCtx.actorAdminUserId,
161
+ ...(auditCtx.impersonatedCustomerAccountId !== undefined
162
+ ? { impersonatedCustomerAccountId: auditCtx.impersonatedCustomerAccountId }
163
+ : {}),
164
+ action,
165
+ objectType: 'warehouse',
166
+ objectId: row.id,
167
+ ...(stateBefore !== null ? { stateBefore } : {}),
168
+ stateAfter,
169
+ ...(auditCtx.ipAddress !== undefined ? { ipAddress: auditCtx.ipAddress } : {}),
170
+ ...(auditCtx.userAgent !== undefined ? { userAgent: auditCtx.userAgent } : {}),
171
+ ...(auditCtx.requestId !== undefined ? { requestId: auditCtx.requestId } : {}),
172
+ });
173
+ }
174
+ async delete(id, auditCtx) {
175
+ const em = this.emFactory();
176
+ const row = await em.findOne(Warehouse, { id });
177
+ if (!row)
178
+ throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found');
179
+ if (row.code === DEFAULT_WAREHOUSE_CODE) {
180
+ throw new HttpError(409, 'WAREHOUSE_CANNOT_DELETE_DEFAULT', 'The Default warehouse cannot be deleted');
181
+ }
182
+ const defaultForChannels = await em.count(WarehouseChannelAssignment, {
183
+ warehouseId: id,
184
+ isDefault: true,
185
+ });
186
+ if (defaultForChannels > 0) {
187
+ throw new HttpError(409, 'WAREHOUSE_IS_DEFAULT_FOR_CHANNELS', `Warehouse is the default for ${defaultForChannels} sales channel(s); promote another warehouse first`);
188
+ }
189
+ const stockRows = await em.count(StockLevel, { warehouseId: id });
190
+ if (stockRows > 0) {
191
+ throw new HttpError(409, 'WAREHOUSE_HAS_STOCK', `Warehouse holds stock for ${stockRows} product line(s); move stock to another warehouse first`);
192
+ }
193
+ // Drop any (non-default) channel assignments that still reference the
194
+ // warehouse so the FK doesn't block the delete.
195
+ await em.nativeDelete(WarehouseChannelAssignment, { warehouseId: id });
196
+ await this.audit('warehouse.delete', row, { code: row.code, name: row.name, active: row.active }, {}, auditCtx);
197
+ await em.removeAndFlush(row);
198
+ }
199
+ async collectTotals(em, ids) {
200
+ if (ids.length === 0)
201
+ return new Map();
202
+ // `em.execute`, not `em.getKnex()`: a knex handle carries no transaction
203
+ // context, so the totals shown next to a warehouse a caller has just
204
+ // written inside a transaction would be read from before that write
205
+ // (issue #207).
206
+ const placeholders = ids.map(() => '?').join(', ');
207
+ const [stockTotals, defaultCounts] = await Promise.all([
208
+ em.execute(`select warehouse_id, count(*) as products, sum(on_hand) as on_hand
209
+ from stock_levels
210
+ where warehouse_id in (${placeholders})
211
+ group by warehouse_id`, [...ids]),
212
+ em.execute(`select warehouse_id, count(*) as default_count
213
+ from warehouse_channel_assignments
214
+ where warehouse_id in (${placeholders}) and is_default = true
215
+ group by warehouse_id`, [...ids]),
216
+ ]);
217
+ const map = new Map();
218
+ for (const id of ids) {
219
+ map.set(id, { products: 0, onHand: 0, isDefaultForChannelCount: 0 });
220
+ }
221
+ for (const row of stockTotals) {
222
+ const t = map.get(row.warehouse_id);
223
+ if (!t)
224
+ continue;
225
+ t.products = Number(row.products);
226
+ t.onHand = Number(row.on_hand ?? 0);
227
+ }
228
+ for (const row of defaultCounts) {
229
+ const t = map.get(row.warehouse_id);
230
+ if (!t)
231
+ continue;
232
+ t.isDefaultForChannelCount = Number(row.default_count);
233
+ }
234
+ return map;
235
+ }
236
+ toDTO(row, totals) {
237
+ const contact = row.contactName || row.contactEmail || row.contactPhone
238
+ ? {
239
+ name: row.contactName ?? null,
240
+ email: row.contactEmail ?? null,
241
+ phone: row.contactPhone ?? null,
242
+ }
243
+ : null;
244
+ return {
245
+ id: row.id,
246
+ name: row.name,
247
+ code: row.code,
248
+ active: row.active,
249
+ description: row.description ?? null,
250
+ address: row.address ?? null,
251
+ contact,
252
+ defaultLowStockThreshold: row.defaultLowStockThreshold ?? null,
253
+ ...(totals ? { totals } : {}),
254
+ createdAt: row.createdAt.toISOString(),
255
+ updatedAt: row.updatedAt.toISOString(),
256
+ };
257
+ }
258
+ }
259
+ function snapshotOf(row) {
260
+ return {
261
+ name: row.name,
262
+ code: row.code,
263
+ active: row.active,
264
+ description: row.description ?? null,
265
+ };
266
+ }
267
+ //# sourceMappingURL=warehouse-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warehouse-service.js","sourceRoot":"","sources":["../../../src/backend/services/warehouse-service.ts"],"names":[],"mappings":"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"}