@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,489 @@
|
|
|
1
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { StockLevel } from '../entities/stock-level.entity.js';
|
|
4
|
+
import { Warehouse, DEFAULT_WAREHOUSE_ID } from '../entities/warehouse.entity.js';
|
|
5
|
+
import { InventoryThreshold } from '../entities/inventory-threshold.entity.js';
|
|
6
|
+
import { resolveThresholds, } from './threshold-resolver.js';
|
|
7
|
+
import { resolveDisplayBand, } from './display-band-resolver.js';
|
|
8
|
+
/**
|
|
9
|
+
* StockLevelService (US2) — read + mutate per-(product, warehouse) on-hand.
|
|
10
|
+
*
|
|
11
|
+
* The setter is the single point that emits `inventory.adjusted.v1` so the
|
|
12
|
+
* crossing detector (US4) and the restock fan-out (US6) can react. Reserved
|
|
13
|
+
* counters stay driven by the order service.
|
|
14
|
+
*/
|
|
15
|
+
export class StockLevelService {
|
|
16
|
+
emFactory;
|
|
17
|
+
catalogProducts;
|
|
18
|
+
catalogCategories;
|
|
19
|
+
eventBus;
|
|
20
|
+
auditLog;
|
|
21
|
+
constructor(emFactory,
|
|
22
|
+
/**
|
|
23
|
+
* `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C).
|
|
24
|
+
* Every roster row, every KPI and every band this service computes joins a
|
|
25
|
+
* product it does not own; it used to load the entity, so an operator who
|
|
26
|
+
* switched `catalog` off still got a full stock roster out of it.
|
|
27
|
+
*/
|
|
28
|
+
catalogProducts,
|
|
29
|
+
/** `catalogCategoryReadPort`, owned by `catalog` — the threshold chain. */
|
|
30
|
+
catalogCategories, eventBus, auditLog) {
|
|
31
|
+
this.emFactory = emFactory;
|
|
32
|
+
this.catalogProducts = catalogProducts;
|
|
33
|
+
this.catalogCategories = catalogCategories;
|
|
34
|
+
this.eventBus = eventBus;
|
|
35
|
+
this.auditLog = auditLog;
|
|
36
|
+
}
|
|
37
|
+
async listLandingKpis() {
|
|
38
|
+
const em = this.emFactory();
|
|
39
|
+
// `em.execute`, not `em.getKnex()`: a knex handle takes its own pooled
|
|
40
|
+
// connection, so every KPI here would answer from outside a transaction the
|
|
41
|
+
// caller holds open while the port reads below answer from inside it —
|
|
42
|
+
// one screen, two views of the same rows (issue #207).
|
|
43
|
+
const [productsTracked, totalOnHandRow, perWarehouse] = await Promise.all([
|
|
44
|
+
em.execute(`select count(distinct product_id) as count from stock_levels`),
|
|
45
|
+
em.execute(`select sum(on_hand) as sum from stock_levels`),
|
|
46
|
+
em.execute(`select w.id, w.code, coalesce(sum(sl.on_hand), 0) as on_hand
|
|
47
|
+
from warehouses w
|
|
48
|
+
left join stock_levels sl on sl.warehouse_id = w.id
|
|
49
|
+
group by w.id, w.code
|
|
50
|
+
order by w.code`),
|
|
51
|
+
]);
|
|
52
|
+
const productsByOnHand = (await em.execute(`select product_id, sum(on_hand) as on_hand from stock_levels group by product_id`));
|
|
53
|
+
const products = await this.catalogProducts.findByIds(productsByOnHand.map((r) => r.product_id));
|
|
54
|
+
const productById = new Map(products.map((p) => [p.id, p]));
|
|
55
|
+
let outOfStock = 0;
|
|
56
|
+
let lowStock = 0;
|
|
57
|
+
for (const row of productsByOnHand) {
|
|
58
|
+
const p = productById.get(row.product_id);
|
|
59
|
+
if (!p)
|
|
60
|
+
continue;
|
|
61
|
+
if (!p.manageStock)
|
|
62
|
+
continue;
|
|
63
|
+
const onHand = Number(row.on_hand ?? 0);
|
|
64
|
+
if (onHand <= 0)
|
|
65
|
+
outOfStock += 1;
|
|
66
|
+
else if (p.lowStockThreshold !== null && onHand <= p.lowStockThreshold) {
|
|
67
|
+
lowStock += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
totalProductsTracked: Number(productsTracked[0]?.count ?? 0),
|
|
72
|
+
totalOnHand: Number(totalOnHandRow[0]?.sum ?? 0),
|
|
73
|
+
outOfStockCount: outOfStock,
|
|
74
|
+
lowStockCount: lowStock,
|
|
75
|
+
perWarehouseTotals: perWarehouse.map((r) => ({
|
|
76
|
+
warehouseId: r.id,
|
|
77
|
+
warehouseCode: r.code,
|
|
78
|
+
onHand: Number(r.on_hand ?? 0),
|
|
79
|
+
})),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async listRoster(filter = {}) {
|
|
83
|
+
const em = this.emFactory();
|
|
84
|
+
const page = Math.max(0, filter.page ?? 0);
|
|
85
|
+
const pageSize = Math.min(Math.max(1, filter.pageSize ?? 50), 500);
|
|
86
|
+
// The visible roster is per-product (one row aggregates all warehouses
|
|
87
|
+
// for that product), so we paginate over products — not over the raw
|
|
88
|
+
// `stock_levels` rows. Building a single SQL aggregate gets us:
|
|
89
|
+
// 1. A consistent `total` that matches the displayed item count.
|
|
90
|
+
// 2. A place to push down the SKU/name search and low/out filters
|
|
91
|
+
// instead of post-filtering on the JS side.
|
|
92
|
+
const knex = em.getKnex();
|
|
93
|
+
const trimmedQ = filter.q?.trim().toLowerCase();
|
|
94
|
+
const baseQuery = knex({ p: 'products' })
|
|
95
|
+
.innerJoin({ sl: 'stock_levels' }, 'sl.product_id', 'p.id')
|
|
96
|
+
.modify((qb) => {
|
|
97
|
+
if (filter.productId)
|
|
98
|
+
qb.where('p.id', filter.productId);
|
|
99
|
+
if (filter.warehouseId)
|
|
100
|
+
qb.where('sl.warehouse_id', filter.warehouseId);
|
|
101
|
+
if (trimmedQ) {
|
|
102
|
+
const needle = `%${trimmedQ}%`;
|
|
103
|
+
qb.andWhere((inner) => {
|
|
104
|
+
inner
|
|
105
|
+
.whereRaw('LOWER("p"."sku") LIKE ?', [needle])
|
|
106
|
+
.orWhereRaw('LOWER("p"."name"::text) LIKE ?', [needle])
|
|
107
|
+
.orWhereRaw('LOWER("p"."id"::text) LIKE ?', [needle]);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.groupBy('p.id', 'p.sku', 'p.manage_stock', 'p.low_stock_threshold')
|
|
112
|
+
.modify((qb) => {
|
|
113
|
+
if (filter.outOnly) {
|
|
114
|
+
qb.havingRaw('"p"."manage_stock" IS NOT FALSE AND COALESCE(SUM("sl"."on_hand"), 0) <= 0');
|
|
115
|
+
}
|
|
116
|
+
else if (filter.lowOnly) {
|
|
117
|
+
qb.havingRaw('"p"."manage_stock" IS NOT FALSE AND "p"."low_stock_threshold" IS NOT NULL AND COALESCE(SUM("sl"."on_hand"), 0) > 0 AND COALESCE(SUM("sl"."on_hand"), 0) <= "p"."low_stock_threshold"');
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Total count of matching products. We wrap the grouped query in a
|
|
121
|
+
// subselect so `count(*)` counts groups, not rows.
|
|
122
|
+
// Both pages run through `em.execute(builder)`, not by awaiting the builder:
|
|
123
|
+
// a knex handle takes its own pooled connection, so the roster a caller
|
|
124
|
+
// inside a transaction is shown would be computed from rows that
|
|
125
|
+
// transaction has not written yet (issue #207). The builder is kept rather
|
|
126
|
+
// than rewritten as a statement because the filters above are assembled at
|
|
127
|
+
// runtime; `execute` compiles it and runs it with the EntityManager's
|
|
128
|
+
// transaction context, so the SQL is byte-for-byte the one this method
|
|
129
|
+
// already sent.
|
|
130
|
+
const totalRows = (await em.execute(knex.from(baseQuery.clone().select('p.id')).as('grouped').count('* as count')));
|
|
131
|
+
const total = Number(totalRows[0]?.count ?? 0);
|
|
132
|
+
const idRows = (await em.execute(baseQuery
|
|
133
|
+
.clone()
|
|
134
|
+
.select('p.id')
|
|
135
|
+
.orderByRaw('MAX("sl"."updated_at") DESC')
|
|
136
|
+
.offset(page * pageSize)
|
|
137
|
+
.limit(pageSize)));
|
|
138
|
+
const productIds = idRows.map((r) => r.id);
|
|
139
|
+
if (productIds.length === 0) {
|
|
140
|
+
return { items: [], page, pageSize, total };
|
|
141
|
+
}
|
|
142
|
+
// Pull cumulative across ALL warehouses for the candidate products so the
|
|
143
|
+
// band/threshold logic uses the right total even if the filter narrowed
|
|
144
|
+
// the row set.
|
|
145
|
+
const idPlaceholders = productIds.map(() => '?').join(', ');
|
|
146
|
+
const cumulativeRows = (await em.execute(`select product_id, warehouse_id, on_hand, reserved
|
|
147
|
+
from stock_levels
|
|
148
|
+
where product_id in (${idPlaceholders})`, [...productIds]));
|
|
149
|
+
const products = await this.catalogProducts.findByIds(productIds);
|
|
150
|
+
const productById = new Map(products.map((p) => [p.id, p]));
|
|
151
|
+
const warehouses = await em.find(Warehouse, {});
|
|
152
|
+
const warehouseById = new Map(warehouses.map((w) => [w.id, w]));
|
|
153
|
+
// Asked of `catalog` rather than joined out of its `product_categories`
|
|
154
|
+
// table — see `resolveAvailabilityBands` for the whole reason (feature 075,
|
|
155
|
+
// the `inventory` shard).
|
|
156
|
+
const assignments = await this.catalogCategories.listAssignmentsForProducts(productIds);
|
|
157
|
+
const categoryIdsByProduct = new Map();
|
|
158
|
+
for (const assignment of assignments) {
|
|
159
|
+
const list = categoryIdsByProduct.get(assignment.productId) ?? [];
|
|
160
|
+
list.push(assignment.categoryId);
|
|
161
|
+
categoryIdsByProduct.set(assignment.productId, list);
|
|
162
|
+
}
|
|
163
|
+
const categoryIds = Array.from(new Set(assignments.map((a) => a.categoryId)));
|
|
164
|
+
const categories = await this.catalogCategories.findByIds(categoryIds);
|
|
165
|
+
const categoryById = new Map(categories.map((c) => [c.id, c]));
|
|
166
|
+
const globalThresholds = await this.loadGlobalThresholds(em);
|
|
167
|
+
// Pull every per-(product, warehouse) low-stock threshold for the
|
|
168
|
+
// candidate products in a single round-trip. `null` means there is
|
|
169
|
+
// no explicit row; callers fall back to the warehouse default.
|
|
170
|
+
const perWarehouseThresholdRows = (await em.execute(`select product_id, warehouse_id, threshold
|
|
171
|
+
from product_warehouse_low_stock_thresholds
|
|
172
|
+
where product_id in (${idPlaceholders})`, [...productIds]));
|
|
173
|
+
const explicitPerWarehouseThreshold = new Map();
|
|
174
|
+
for (const row of perWarehouseThresholdRows) {
|
|
175
|
+
explicitPerWarehouseThreshold.set(`${row.product_id}:${row.warehouse_id}`, Number(row.threshold));
|
|
176
|
+
}
|
|
177
|
+
const items = [];
|
|
178
|
+
for (const productId of productIds) {
|
|
179
|
+
const product = productById.get(productId);
|
|
180
|
+
if (!product)
|
|
181
|
+
continue;
|
|
182
|
+
const productCumulative = cumulativeRows.filter((r) => r.product_id === productId);
|
|
183
|
+
const cumulativeOnHand = productCumulative.reduce((sum, r) => sum + Number(r.on_hand ?? 0), 0);
|
|
184
|
+
const productCategoryIds = categoryIdsByProduct.get(product.id) ?? [];
|
|
185
|
+
const categoryThresholds = productCategoryIds
|
|
186
|
+
.map((cid) => categoryById.get(cid))
|
|
187
|
+
.filter((c) => Boolean(c))
|
|
188
|
+
.map((c) => ({
|
|
189
|
+
high: c.inventoryThresholdHigh,
|
|
190
|
+
medium: c.inventoryThresholdMedium,
|
|
191
|
+
low: c.inventoryThresholdLow,
|
|
192
|
+
}));
|
|
193
|
+
const productThresholds = (await this.loadProductThresholds(em, product.id)) ?? null;
|
|
194
|
+
const thresholds = resolveThresholds({
|
|
195
|
+
productThresholds,
|
|
196
|
+
categoryThresholds,
|
|
197
|
+
globalThresholds,
|
|
198
|
+
});
|
|
199
|
+
const displayBand = resolveDisplayBand({
|
|
200
|
+
manageStock: product.manageStock,
|
|
201
|
+
cumulativeOnHand,
|
|
202
|
+
thresholds,
|
|
203
|
+
});
|
|
204
|
+
const productName = (typeof product.name === 'object' && product.name !== null
|
|
205
|
+
? Object.values(product.name)[0]
|
|
206
|
+
: product.sku) ?? product.sku;
|
|
207
|
+
const mode = product.lowStockThresholdMode;
|
|
208
|
+
// Resolve the effective low-stock threshold per warehouse:
|
|
209
|
+
// 1. explicit row in `product_warehouse_low_stock_thresholds`
|
|
210
|
+
// 2. `warehouses.default_low_stock_threshold`
|
|
211
|
+
// 3. null (no threshold for that warehouse)
|
|
212
|
+
// Surfaced regardless of mode so the admin UI can show / edit it.
|
|
213
|
+
const perWarehouseWithThreshold = productCumulative.map((r) => {
|
|
214
|
+
const explicit = explicitPerWarehouseThreshold.get(`${productId}:${r.warehouse_id}`);
|
|
215
|
+
const wh = warehouseById.get(r.warehouse_id);
|
|
216
|
+
const resolved = explicit !== undefined ? explicit : wh?.defaultLowStockThreshold ?? null;
|
|
217
|
+
return {
|
|
218
|
+
warehouseId: r.warehouse_id,
|
|
219
|
+
warehouseCode: wh?.code ?? '',
|
|
220
|
+
onHand: Number(r.on_hand ?? 0),
|
|
221
|
+
reserved: Number(r.reserved ?? 0),
|
|
222
|
+
lowStockThreshold: resolved,
|
|
223
|
+
};
|
|
224
|
+
});
|
|
225
|
+
// Top-level `lowStockThreshold` + `isLowStock` semantics depend on mode:
|
|
226
|
+
// - cumulative: one threshold against summed on-hand. Falls back to the
|
|
227
|
+
// MAX(warehouse.defaultLowStockThreshold) across the product's
|
|
228
|
+
// warehouses (most-permissive wins) when the product has none.
|
|
229
|
+
// - per_warehouse: each warehouse evaluated independently; product is
|
|
230
|
+
// "low" if ANY warehouse has on_hand > 0 and on_hand <= its threshold.
|
|
231
|
+
let topLevelThreshold;
|
|
232
|
+
let isLowStock = false;
|
|
233
|
+
if (mode === 'per_warehouse') {
|
|
234
|
+
topLevelThreshold = product.lowStockThreshold ?? null;
|
|
235
|
+
for (const pw of perWarehouseWithThreshold) {
|
|
236
|
+
if (pw.lowStockThreshold !== null &&
|
|
237
|
+
pw.onHand > 0 &&
|
|
238
|
+
pw.onHand <= pw.lowStockThreshold) {
|
|
239
|
+
isLowStock = true;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
let effective = product.lowStockThreshold ?? null;
|
|
246
|
+
if (effective == null) {
|
|
247
|
+
let warehouseFallback = null;
|
|
248
|
+
for (const pw of perWarehouseWithThreshold) {
|
|
249
|
+
const wh = warehouseById.get(pw.warehouseId);
|
|
250
|
+
const wt = wh?.defaultLowStockThreshold ?? null;
|
|
251
|
+
if (wt != null && (warehouseFallback == null || wt > warehouseFallback)) {
|
|
252
|
+
warehouseFallback = wt;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
effective = warehouseFallback;
|
|
256
|
+
}
|
|
257
|
+
topLevelThreshold = effective;
|
|
258
|
+
isLowStock =
|
|
259
|
+
effective !== null &&
|
|
260
|
+
cumulativeOnHand > 0 &&
|
|
261
|
+
cumulativeOnHand <= effective;
|
|
262
|
+
}
|
|
263
|
+
items.push({
|
|
264
|
+
productId,
|
|
265
|
+
productSku: product.sku,
|
|
266
|
+
productName: String(productName),
|
|
267
|
+
manageStock: product.manageStock,
|
|
268
|
+
backorderEnabled: product.backorderEnabled,
|
|
269
|
+
lowStockThreshold: topLevelThreshold,
|
|
270
|
+
lowStockThresholdMode: mode,
|
|
271
|
+
perWarehouse: perWarehouseWithThreshold,
|
|
272
|
+
cumulativeOnHand,
|
|
273
|
+
displayBand,
|
|
274
|
+
isLowStock: product.manageStock && isLowStock,
|
|
275
|
+
isOutOfStock: product.manageStock && cumulativeOnHand <= 0,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return { items, page, pageSize, total };
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Replace the per-warehouse low-stock threshold map for one product.
|
|
282
|
+
* Entries with `threshold === null` are deleted; the rest are upserted.
|
|
283
|
+
* Warehouse rows not mentioned in `entries` are left untouched (callers
|
|
284
|
+
* that want a hard replace should explicitly include `threshold: null`
|
|
285
|
+
* for every (product, warehouse) they wish to clear).
|
|
286
|
+
*/
|
|
287
|
+
async setProductWarehouseThresholds(input) {
|
|
288
|
+
const em = this.emFactory();
|
|
289
|
+
const product = await this.catalogProducts.findById(input.productId);
|
|
290
|
+
if (!product) {
|
|
291
|
+
throw new HttpError(404, 'PRODUCT_NOT_FOUND', 'Product not found');
|
|
292
|
+
}
|
|
293
|
+
if (input.entries.length === 0)
|
|
294
|
+
return;
|
|
295
|
+
const warehouseIds = input.entries.map((e) => e.warehouseId);
|
|
296
|
+
const warehouses = await em.find(Warehouse, { id: { $in: warehouseIds } });
|
|
297
|
+
const knownWarehouseIds = new Set(warehouses.map((w) => w.id));
|
|
298
|
+
for (const e of input.entries) {
|
|
299
|
+
if (!knownWarehouseIds.has(e.warehouseId)) {
|
|
300
|
+
throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', `Warehouse ${e.warehouseId} not found`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const conn = em.getConnection();
|
|
304
|
+
const txCtx = em.getTransactionContext();
|
|
305
|
+
const deletes = input.entries.filter((e) => e.threshold === null);
|
|
306
|
+
const upserts = input.entries.filter((e) => e.threshold !== null);
|
|
307
|
+
if (deletes.length > 0) {
|
|
308
|
+
const placeholders = deletes.map(() => '?').join(',');
|
|
309
|
+
await conn.execute(`delete from "product_warehouse_low_stock_thresholds"
|
|
310
|
+
where "product_id" = ? and "warehouse_id" in (${placeholders})`, [input.productId, ...deletes.map((d) => d.warehouseId)], 'run', txCtx);
|
|
311
|
+
}
|
|
312
|
+
for (const u of upserts) {
|
|
313
|
+
await conn.execute(`insert into "product_warehouse_low_stock_thresholds"
|
|
314
|
+
("product_id", "warehouse_id", "threshold", "created_at", "updated_at")
|
|
315
|
+
values (?, ?, ?, now(), now())
|
|
316
|
+
on conflict ("product_id", "warehouse_id") do update
|
|
317
|
+
set "threshold" = excluded."threshold",
|
|
318
|
+
"updated_at" = now()`, [input.productId, u.warehouseId, u.threshold], 'run', txCtx);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async setOnHand(input, auditCtx) {
|
|
322
|
+
const em = this.emFactory();
|
|
323
|
+
const product = await this.catalogProducts.findById(input.productId);
|
|
324
|
+
if (!product)
|
|
325
|
+
throw new HttpError(404, 'PRODUCT_NOT_FOUND', 'Product not found');
|
|
326
|
+
const warehouse = await em.findOne(Warehouse, { id: input.warehouseId });
|
|
327
|
+
if (!warehouse)
|
|
328
|
+
throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found');
|
|
329
|
+
const variantId = input.variantId ?? null;
|
|
330
|
+
let row = await em.findOne(StockLevel, {
|
|
331
|
+
productId: input.productId,
|
|
332
|
+
warehouseId: input.warehouseId,
|
|
333
|
+
variantId,
|
|
334
|
+
});
|
|
335
|
+
const before = row?.onHand ?? 0;
|
|
336
|
+
if (row) {
|
|
337
|
+
row.onHand = input.onHand;
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
row = em.create(StockLevel, {
|
|
341
|
+
productId: input.productId,
|
|
342
|
+
warehouseId: input.warehouseId,
|
|
343
|
+
...(variantId ? { variantId } : {}),
|
|
344
|
+
onHand: input.onHand,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
await em.persistAndFlush(row);
|
|
348
|
+
if (this.eventBus && before !== input.onHand) {
|
|
349
|
+
this.eventBus.emit('inventory.adjusted.v1', {
|
|
350
|
+
eventId: randomUUID(),
|
|
351
|
+
occurredAt: new Date().toISOString(),
|
|
352
|
+
productId: input.productId,
|
|
353
|
+
warehouseId: input.warehouseId,
|
|
354
|
+
variantId,
|
|
355
|
+
before,
|
|
356
|
+
after: input.onHand,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
// Feature 024 — audit stock adjustments so the dashboard surfaces
|
|
360
|
+
// them. Snapshot carries the SKU + warehouse code so the row stays
|
|
361
|
+
// readable after either reference is renamed.
|
|
362
|
+
if (this.auditLog && auditCtx && before !== input.onHand) {
|
|
363
|
+
await this.auditLog.record({
|
|
364
|
+
actorAdminUserId: auditCtx.actorAdminUserId,
|
|
365
|
+
...(auditCtx.impersonatedCustomerAccountId !== undefined
|
|
366
|
+
? { impersonatedCustomerAccountId: auditCtx.impersonatedCustomerAccountId }
|
|
367
|
+
: {}),
|
|
368
|
+
action: 'stock_level.adjust',
|
|
369
|
+
objectType: 'stock_level',
|
|
370
|
+
objectId: `${input.productId}:${input.warehouseId}`,
|
|
371
|
+
stateBefore: { onHand: before },
|
|
372
|
+
stateAfter: {
|
|
373
|
+
productId: input.productId,
|
|
374
|
+
productSku: product.sku,
|
|
375
|
+
warehouseId: input.warehouseId,
|
|
376
|
+
warehouseCode: warehouse.code,
|
|
377
|
+
onHand: input.onHand,
|
|
378
|
+
delta: input.onHand - before,
|
|
379
|
+
},
|
|
380
|
+
...(auditCtx.ipAddress !== undefined ? { ipAddress: auditCtx.ipAddress } : {}),
|
|
381
|
+
...(auditCtx.userAgent !== undefined ? { userAgent: auditCtx.userAgent } : {}),
|
|
382
|
+
...(auditCtx.requestId !== undefined ? { requestId: auditCtx.requestId } : {}),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
before,
|
|
387
|
+
after: input.onHand,
|
|
388
|
+
productId: input.productId,
|
|
389
|
+
warehouseId: input.warehouseId,
|
|
390
|
+
variantId,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Feature 062 — batch availability indication for the external catalog
|
|
395
|
+
* surface. Mirrors the storefront per-product stock readout: cumulative
|
|
396
|
+
* on-hand (optionally restricted to the channel's candidate warehouses)
|
|
397
|
+
* mapped through the product/category/global threshold chain to a display
|
|
398
|
+
* band; `inStock` is the same channel-public flag the storefront derives
|
|
399
|
+
* (`manageStock && onHand <= 0` ⇒ out of stock). Products with no stock
|
|
400
|
+
* rows still get an entry (out_of_stock or `available` when unmanaged).
|
|
401
|
+
*/
|
|
402
|
+
async resolveAvailabilityBands(productIds, candidateWarehouseIds) {
|
|
403
|
+
const out = new Map();
|
|
404
|
+
if (productIds.length === 0)
|
|
405
|
+
return out;
|
|
406
|
+
const em = this.emFactory();
|
|
407
|
+
const products = await this.catalogProducts.findByIds(productIds);
|
|
408
|
+
// `em.execute`, not `em.getKnex()`: a knex handle carries no transaction
|
|
409
|
+
// context, so the band a caller inside a transaction is shown would be
|
|
410
|
+
// computed from rows that transaction has not written yet (issue #207).
|
|
411
|
+
const productPlaceholders = productIds.map(() => '?').join(', ');
|
|
412
|
+
const warehouseFilter = candidateWarehouseIds && candidateWarehouseIds.length > 0
|
|
413
|
+
? ` and warehouse_id in (${candidateWarehouseIds.map(() => '?').join(', ')})`
|
|
414
|
+
: '';
|
|
415
|
+
const sumRows = (await em.execute(`select product_id, sum(on_hand) as on_hand
|
|
416
|
+
from stock_levels
|
|
417
|
+
where product_id in (${productPlaceholders})${warehouseFilter}
|
|
418
|
+
group by product_id`, [...productIds, ...(warehouseFilter === '' ? [] : (candidateWarehouseIds ?? []))]));
|
|
419
|
+
const onHandByProduct = new Map(sumRows.map((r) => [r.product_id, Number(r.on_hand ?? 0)]));
|
|
420
|
+
const globalThresholds = await this.loadGlobalThresholds(em);
|
|
421
|
+
const productThresholdRows = await em.find(InventoryThreshold, {
|
|
422
|
+
scopeKind: 'product',
|
|
423
|
+
scopeId: { $in: productIds },
|
|
424
|
+
});
|
|
425
|
+
const productThresholdByProduct = new Map(productThresholdRows.map((row) => [
|
|
426
|
+
row.scopeId,
|
|
427
|
+
{
|
|
428
|
+
high: row.thresholdHigh ?? null,
|
|
429
|
+
medium: row.thresholdMedium ?? null,
|
|
430
|
+
low: row.thresholdLow ?? null,
|
|
431
|
+
},
|
|
432
|
+
]));
|
|
433
|
+
// `product_categories` is `catalog`'s bridge table and this module used to
|
|
434
|
+
// join it here in raw SQL — a boundary crossing that names no import
|
|
435
|
+
// specifier, one line above the `findByIds` that already asked the owner
|
|
436
|
+
// the next question (feature 075, the `inventory` shard). The port answers
|
|
437
|
+
// both halves now. No `activeOnly`: a threshold set on a category an
|
|
438
|
+
// operator deactivated still governs the stock band of the products in it,
|
|
439
|
+
// exactly as the join this replaces did.
|
|
440
|
+
const assignments = await this.catalogCategories.listAssignmentsForProducts(productIds);
|
|
441
|
+
const categoryIds = Array.from(new Set(assignments.map((a) => a.categoryId)));
|
|
442
|
+
const categories = await this.catalogCategories.findByIds(categoryIds);
|
|
443
|
+
const categoryById = new Map(categories.map((c) => [c.id, c]));
|
|
444
|
+
for (const product of products) {
|
|
445
|
+
const cumulativeOnHand = onHandByProduct.get(product.id) ?? 0;
|
|
446
|
+
const categoryThresholds = assignments
|
|
447
|
+
.filter((a) => a.productId === product.id)
|
|
448
|
+
.map((a) => categoryById.get(a.categoryId))
|
|
449
|
+
.filter((c) => Boolean(c))
|
|
450
|
+
.map((c) => ({
|
|
451
|
+
high: c.inventoryThresholdHigh,
|
|
452
|
+
medium: c.inventoryThresholdMedium,
|
|
453
|
+
low: c.inventoryThresholdLow,
|
|
454
|
+
}));
|
|
455
|
+
const thresholds = resolveThresholds({
|
|
456
|
+
productThresholds: productThresholdByProduct.get(product.id) ?? null,
|
|
457
|
+
categoryThresholds,
|
|
458
|
+
globalThresholds,
|
|
459
|
+
});
|
|
460
|
+
const manageStock = product.manageStock;
|
|
461
|
+
const band = resolveDisplayBand({ manageStock, cumulativeOnHand, thresholds });
|
|
462
|
+
out.set(product.id, {
|
|
463
|
+
band,
|
|
464
|
+
inStock: !(manageStock && cumulativeOnHand <= 0),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return out;
|
|
468
|
+
}
|
|
469
|
+
async loadGlobalThresholds(em) {
|
|
470
|
+
const row = await em.findOne(InventoryThreshold, { scopeKind: 'global', scopeId: null });
|
|
471
|
+
return {
|
|
472
|
+
high: row?.thresholdHigh ?? 100,
|
|
473
|
+
medium: row?.thresholdMedium ?? 20,
|
|
474
|
+
low: row?.thresholdLow ?? 1,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async loadProductThresholds(em, productId) {
|
|
478
|
+
const row = await em.findOne(InventoryThreshold, { scopeKind: 'product', scopeId: productId });
|
|
479
|
+
if (!row)
|
|
480
|
+
return undefined;
|
|
481
|
+
return {
|
|
482
|
+
high: row.thresholdHigh ?? null,
|
|
483
|
+
medium: row.thresholdMedium ?? null,
|
|
484
|
+
low: row.thresholdLow ?? null,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
export { DEFAULT_WAREHOUSE_ID };
|
|
489
|
+
//# sourceMappingURL=stock-level-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stock-level-service.js","sourceRoot":"","sources":["../../../src/backend/services/stock-level-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { CatalogCategoryReadPort, CatalogCategoryWritePort, CatalogProductReadPort } from '@endora-commerce/contracts';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { InventoryAuditContext } from '../plugin.js';
|
|
5
|
+
export interface ThresholdTriple {
|
|
6
|
+
high: number | null;
|
|
7
|
+
medium: number | null;
|
|
8
|
+
low: number | null;
|
|
9
|
+
}
|
|
10
|
+
export type ThresholdTriplePartial = {
|
|
11
|
+
high?: number | null | undefined;
|
|
12
|
+
medium?: number | null | undefined;
|
|
13
|
+
low?: number | null | undefined;
|
|
14
|
+
};
|
|
15
|
+
export interface ThresholdsView {
|
|
16
|
+
global: ThresholdTriple;
|
|
17
|
+
perCategory: Array<ThresholdTriple & {
|
|
18
|
+
categoryId: string;
|
|
19
|
+
}>;
|
|
20
|
+
perProduct: Array<ThresholdTriple & {
|
|
21
|
+
productId: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export interface ThresholdsPatch {
|
|
25
|
+
global?: ThresholdTriplePartial;
|
|
26
|
+
perCategory?: Array<{
|
|
27
|
+
categoryId: string;
|
|
28
|
+
} & ThresholdTriplePartial>;
|
|
29
|
+
perProduct?: Array<{
|
|
30
|
+
productId: string;
|
|
31
|
+
} & ThresholdTriplePartial>;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* ThresholdAdminService (US5 / FR-017).
|
|
35
|
+
*
|
|
36
|
+
* Reads + writes the inventory display-band thresholds. Storage:
|
|
37
|
+
* - global → singleton row in `inventory_thresholds` (scopeKind='global')
|
|
38
|
+
* - perCategory → `categories.inventory_threshold_*` columns
|
|
39
|
+
* - perProduct → `inventory_thresholds` rows with scopeKind='product'
|
|
40
|
+
*
|
|
41
|
+
* The split exists for read-path performance: category thresholds inline
|
|
42
|
+
* with the category row keep the storefront resolver one query light.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ThresholdAdminService {
|
|
45
|
+
private readonly emFactory;
|
|
46
|
+
/** `catalogCategoryReadPort`, owned by `catalog` (feature 075, Phase C). */
|
|
47
|
+
private readonly catalogCategories;
|
|
48
|
+
/**
|
|
49
|
+
* `catalogCategoryWritePort`, owned by `catalog`. The per-category half of
|
|
50
|
+
* these thresholds lives in three columns on `catalog`'s `categories` row,
|
|
51
|
+
* so the write belongs to `catalog` and this module hands it the patch.
|
|
52
|
+
*/
|
|
53
|
+
private readonly catalogCategoryWrites;
|
|
54
|
+
/** `catalogProductReadPort`, owned by `catalog` — the per-product existence check. */
|
|
55
|
+
private readonly catalogProducts;
|
|
56
|
+
private readonly auditLog?;
|
|
57
|
+
constructor(emFactory: () => EntityManager,
|
|
58
|
+
/** `catalogCategoryReadPort`, owned by `catalog` (feature 075, Phase C). */
|
|
59
|
+
catalogCategories: CatalogCategoryReadPort,
|
|
60
|
+
/**
|
|
61
|
+
* `catalogCategoryWritePort`, owned by `catalog`. The per-category half of
|
|
62
|
+
* these thresholds lives in three columns on `catalog`'s `categories` row,
|
|
63
|
+
* so the write belongs to `catalog` and this module hands it the patch.
|
|
64
|
+
*/
|
|
65
|
+
catalogCategoryWrites: CatalogCategoryWritePort,
|
|
66
|
+
/** `catalogProductReadPort`, owned by `catalog` — the per-product existence check. */
|
|
67
|
+
catalogProducts: CatalogProductReadPort, auditLog?: AuditPort | undefined);
|
|
68
|
+
read(): Promise<ThresholdsView>;
|
|
69
|
+
patch(input: ThresholdsPatch, auditCtx?: InventoryAuditContext): Promise<ThresholdsView>;
|
|
70
|
+
private applyGlobal;
|
|
71
|
+
private applyProduct;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=threshold-admin-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-admin-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/threshold-admin-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACnC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,KAAK,CAAC,eAAe,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,UAAU,EAAE,KAAK,CAAC,eAAe,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,sBAAsB,CAAC,CAAC;IACrE,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,sBAAsB,CAAC,CAAC;CACpE;AAED;;;;;;;;;;GAUG;AACH,qBAAa,qBAAqB;IAE9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IACtC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAXT,SAAS,EAAE,MAAM,aAAa;IAC/C,4EAA4E;IAC3D,iBAAiB,EAAE,uBAAuB;IAC3D;;;;OAIG;IACc,qBAAqB,EAAE,wBAAwB;IAChE,sFAAsF;IACrE,eAAe,EAAE,sBAAsB,EACvC,QAAQ,CAAC,EAAE,SAAS,YAAA;IAGjC,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC;IAgC/B,KAAK,CACT,KAAK,EAAE,eAAe,EACtB,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,OAAO,CAAC,cAAc,CAAC;YAuEZ,WAAW;YAWX,YAAY;CAmB3B"}
|