@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.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/admin/api/warehouses-client.d.ts +22 -0
- package/dist/admin/api/warehouses-client.d.ts.map +1 -0
- package/dist/admin/api/warehouses-client.js +32 -0
- package/dist/admin/api/warehouses-client.js.map +1 -0
- package/dist/admin/index.d.ts +43 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +190 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AvailabilityNotificationsPage.d.ts +15 -0
- package/dist/admin/pages/AvailabilityNotificationsPage.d.ts.map +1 -0
- package/dist/admin/pages/AvailabilityNotificationsPage.js +111 -0
- package/dist/admin/pages/AvailabilityNotificationsPage.js.map +1 -0
- package/dist/admin/pages/InventoryPage.d.ts +17 -0
- package/dist/admin/pages/InventoryPage.d.ts.map +1 -0
- package/dist/admin/pages/InventoryPage.js +135 -0
- package/dist/admin/pages/InventoryPage.js.map +1 -0
- package/dist/admin/pages/LowStockPage.d.ts +16 -0
- package/dist/admin/pages/LowStockPage.d.ts.map +1 -0
- package/dist/admin/pages/LowStockPage.js +61 -0
- package/dist/admin/pages/LowStockPage.js.map +1 -0
- package/dist/admin/pages/StockImportWizard.d.ts +17 -0
- package/dist/admin/pages/StockImportWizard.d.ts.map +1 -0
- package/dist/admin/pages/StockImportWizard.js +111 -0
- package/dist/admin/pages/StockImportWizard.js.map +1 -0
- package/dist/admin/pages/WarehouseEditor.d.ts +12 -0
- package/dist/admin/pages/WarehouseEditor.d.ts.map +1 -0
- package/dist/admin/pages/WarehouseEditor.js +194 -0
- package/dist/admin/pages/WarehouseEditor.js.map +1 -0
- package/dist/admin/pages/WarehousesList.d.ts +12 -0
- package/dist/admin/pages/WarehousesList.d.ts.map +1 -0
- package/dist/admin/pages/WarehousesList.js +69 -0
- package/dist/admin/pages/WarehousesList.js.map +1 -0
- package/dist/admin/zones/ChannelWarehouses.d.ts +49 -0
- package/dist/admin/zones/ChannelWarehouses.d.ts.map +1 -0
- package/dist/admin/zones/ChannelWarehouses.js +109 -0
- package/dist/admin/zones/ChannelWarehouses.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +18 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +20 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +38 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +38 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +26 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +65 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts +11 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.js +49 -0
- package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
- package/dist/backend/entities/availability-notification.entity.d.ts +49 -0
- package/dist/backend/entities/availability-notification.entity.d.ts.map +1 -0
- package/dist/backend/entities/availability-notification.entity.js +93 -0
- package/dist/backend/entities/availability-notification.entity.js.map +1 -0
- package/dist/backend/entities/inventory-threshold.entity.d.ts +24 -0
- package/dist/backend/entities/inventory-threshold.entity.d.ts.map +1 -0
- package/dist/backend/entities/inventory-threshold.entity.js +61 -0
- package/dist/backend/entities/inventory-threshold.entity.js.map +1 -0
- package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.d.ts +15 -0
- package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.d.ts.map +1 -0
- package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.js +51 -0
- package/dist/backend/entities/product-warehouse-low-stock-threshold.entity.js.map +1 -0
- package/dist/backend/entities/stock-allocation.entity.d.ts +22 -0
- package/dist/backend/entities/stock-allocation.entity.d.ts.map +1 -0
- package/dist/backend/entities/stock-allocation.entity.js +69 -0
- package/dist/backend/entities/stock-allocation.entity.js.map +1 -0
- package/dist/backend/entities/stock-level.entity.d.ts +24 -0
- package/dist/backend/entities/stock-level.entity.d.ts.map +1 -0
- package/dist/backend/entities/stock-level.entity.js +74 -0
- package/dist/backend/entities/stock-level.entity.js.map +1 -0
- package/dist/backend/entities/warehouse-channel-assignment.entity.d.ts +17 -0
- package/dist/backend/entities/warehouse-channel-assignment.entity.d.ts.map +1 -0
- package/dist/backend/entities/warehouse-channel-assignment.entity.js +60 -0
- package/dist/backend/entities/warehouse-channel-assignment.entity.js.map +1 -0
- package/dist/backend/entities/warehouse.entity.d.ts +37 -0
- package/dist/backend/entities/warehouse.entity.d.ts.map +1 -0
- package/dist/backend/entities/warehouse.entity.js +90 -0
- package/dist/backend/entities/warehouse.entity.js.map +1 -0
- package/dist/backend/index.d.ts +139 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +351 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +113 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +75 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/prompt-tools.d.ts +23 -0
- package/dist/backend/prompt-tools.d.ts.map +1 -0
- package/dist/backend/prompt-tools.js +77 -0
- package/dist/backend/prompt-tools.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +62 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +383 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.d.ts +40 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +263 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/audit-references.d.ts +15 -0
- package/dist/backend/services/audit-references.d.ts.map +1 -0
- package/dist/backend/services/audit-references.js +27 -0
- package/dist/backend/services/audit-references.js.map +1 -0
- package/dist/backend/services/availability-notification-service.d.ts +155 -0
- package/dist/backend/services/availability-notification-service.d.ts.map +1 -0
- package/dist/backend/services/availability-notification-service.js +363 -0
- package/dist/backend/services/availability-notification-service.js.map +1 -0
- package/dist/backend/services/availability-worker.d.ts +57 -0
- package/dist/backend/services/availability-worker.d.ts.map +1 -0
- package/dist/backend/services/availability-worker.js +135 -0
- package/dist/backend/services/availability-worker.js.map +1 -0
- package/dist/backend/services/csv-stock-importer.d.ts +52 -0
- package/dist/backend/services/csv-stock-importer.d.ts.map +1 -0
- package/dist/backend/services/csv-stock-importer.js +154 -0
- package/dist/backend/services/csv-stock-importer.js.map +1 -0
- package/dist/backend/services/display-band-resolver.d.ts +19 -0
- package/dist/backend/services/display-band-resolver.d.ts.map +1 -0
- package/dist/backend/services/display-band-resolver.js +21 -0
- package/dist/backend/services/display-band-resolver.js.map +1 -0
- package/dist/backend/services/effective-fulfilment-strategy.d.ts +16 -0
- package/dist/backend/services/effective-fulfilment-strategy.d.ts.map +1 -0
- package/dist/backend/services/effective-fulfilment-strategy.js +16 -0
- package/dist/backend/services/effective-fulfilment-strategy.js.map +1 -0
- package/dist/backend/services/fulfilment-strategy-resolver.d.ts +13 -0
- package/dist/backend/services/fulfilment-strategy-resolver.d.ts.map +1 -0
- package/dist/backend/services/fulfilment-strategy-resolver.js +13 -0
- package/dist/backend/services/fulfilment-strategy-resolver.js.map +1 -0
- package/dist/backend/services/inventory-read-port.d.ts +65 -0
- package/dist/backend/services/inventory-read-port.d.ts.map +1 -0
- package/dist/backend/services/inventory-read-port.js +168 -0
- package/dist/backend/services/inventory-read-port.js.map +1 -0
- package/dist/backend/services/inventory-reservation-apply-port.d.ts +62 -0
- package/dist/backend/services/inventory-reservation-apply-port.d.ts.map +1 -0
- package/dist/backend/services/inventory-reservation-apply-port.js +134 -0
- package/dist/backend/services/inventory-reservation-apply-port.js.map +1 -0
- package/dist/backend/services/low-stock-alert-service.d.ts +87 -0
- package/dist/backend/services/low-stock-alert-service.d.ts.map +1 -0
- package/dist/backend/services/low-stock-alert-service.js +164 -0
- package/dist/backend/services/low-stock-alert-service.js.map +1 -0
- package/dist/backend/services/product-threshold-write.service.d.ts +38 -0
- package/dist/backend/services/product-threshold-write.service.d.ts.map +1 -0
- package/dist/backend/services/product-threshold-write.service.js +80 -0
- package/dist/backend/services/product-threshold-write.service.js.map +1 -0
- package/dist/backend/services/stock-import.service.d.ts +43 -0
- package/dist/backend/services/stock-import.service.d.ts.map +1 -0
- package/dist/backend/services/stock-import.service.js +124 -0
- package/dist/backend/services/stock-import.service.js.map +1 -0
- package/dist/backend/services/stock-level-service.d.ts +146 -0
- package/dist/backend/services/stock-level-service.d.ts.map +1 -0
- package/dist/backend/services/stock-level-service.js +489 -0
- package/dist/backend/services/stock-level-service.js.map +1 -0
- package/dist/backend/services/threshold-admin-service.d.ts +73 -0
- package/dist/backend/services/threshold-admin-service.d.ts.map +1 -0
- package/dist/backend/services/threshold-admin-service.js +167 -0
- package/dist/backend/services/threshold-admin-service.js.map +1 -0
- package/dist/backend/services/threshold-resolver.d.ts +23 -0
- package/dist/backend/services/threshold-resolver.d.ts.map +1 -0
- package/dist/backend/services/threshold-resolver.js +29 -0
- package/dist/backend/services/threshold-resolver.js.map +1 -0
- package/dist/backend/services/threshold-settings-mirror.d.ts +55 -0
- package/dist/backend/services/threshold-settings-mirror.d.ts.map +1 -0
- package/dist/backend/services/threshold-settings-mirror.js +84 -0
- package/dist/backend/services/threshold-settings-mirror.js.map +1 -0
- package/dist/backend/services/warehouse-channel-reconciler.d.ts +19 -0
- package/dist/backend/services/warehouse-channel-reconciler.d.ts.map +1 -0
- package/dist/backend/services/warehouse-channel-reconciler.js +65 -0
- package/dist/backend/services/warehouse-channel-reconciler.js.map +1 -0
- package/dist/backend/services/warehouse-channel-service.d.ts +57 -0
- package/dist/backend/services/warehouse-channel-service.d.ts.map +1 -0
- package/dist/backend/services/warehouse-channel-service.js +205 -0
- package/dist/backend/services/warehouse-channel-service.js.map +1 -0
- package/dist/backend/services/warehouse-country-reference.d.ts +14 -0
- package/dist/backend/services/warehouse-country-reference.d.ts.map +1 -0
- package/dist/backend/services/warehouse-country-reference.js +27 -0
- package/dist/backend/services/warehouse-country-reference.js.map +1 -0
- package/dist/backend/services/warehouse-service.d.ts +86 -0
- package/dist/backend/services/warehouse-service.d.ts.map +1 -0
- package/dist/backend/services/warehouse-service.js +267 -0
- package/dist/backend/services/warehouse-service.js.map +1 -0
- package/dist/manifest.d.ts +219 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +377 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260503T182812_inventory_workflow.d.ts +39 -0
- package/dist/migrations/20260503T182812_inventory_workflow.d.ts.map +1 -0
- package/dist/migrations/20260503T182812_inventory_workflow.js +205 -0
- package/dist/migrations/20260503T182812_inventory_workflow.js.map +1 -0
- package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.d.ts +14 -0
- package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.d.ts.map +1 -0
- package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.js +18 -0
- package/dist/migrations/20260611T140347_inventory_warehouse_default_low_stock_threshold.js.map +1 -0
- package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.d.ts +28 -0
- package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.d.ts.map +1 -0
- package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.js +48 -0
- package/dist/migrations/20260611T140348_inventory_per_warehouse_low_stock_thresholds.js.map +1 -0
- package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.d.ts +44 -0
- package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.d.ts.map +1 -0
- package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.js +81 -0
- package/dist/migrations/20260818T081243_inventory_stock_allocation_order_item_fk.js.map +1 -0
- package/dist/migrations/20260830T182139_inventory_organization_attribution.d.ts +109 -0
- package/dist/migrations/20260830T182139_inventory_organization_attribution.d.ts.map +1 -0
- package/dist/migrations/20260830T182139_inventory_organization_attribution.js +178 -0
- package/dist/migrations/20260830T182139_inventory_organization_attribution.js.map +1 -0
- package/dist/migrations/20260912T125716_inventory_organization_warehouses.d.ts +34 -0
- package/dist/migrations/20260912T125716_inventory_organization_warehouses.d.ts.map +1 -0
- package/dist/migrations/20260912T125716_inventory_organization_warehouses.js +50 -0
- package/dist/migrations/20260912T125716_inventory_organization_warehouses.js.map +1 -0
- package/dist/migrations/index.d.ts +32 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +39 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +183 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/inventory.md +177 -0
- package/i18n/en.json +32 -0
- package/i18n/pl.json +32 -0
- package/package.json +105 -0
- 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"}
|