@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,77 @@
|
|
|
1
|
+
import { SearchWarehousesParamsSchema, SetStockLevelParamsSchema, } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { StockLevel } from './entities/stock-level.entity.js';
|
|
4
|
+
import { Warehouse } from './entities/warehouse.entity.js';
|
|
5
|
+
import { StockLevelService } from './services/stock-level-service.js';
|
|
6
|
+
import { WarehouseService } from './services/warehouse-service.js';
|
|
7
|
+
const RESULT_LIMIT = 20;
|
|
8
|
+
function label(name) {
|
|
9
|
+
return name['en'] ?? Object.values(name)[0] ?? '(unnamed)';
|
|
10
|
+
}
|
|
11
|
+
export function inventoryPromptTools(deps) {
|
|
12
|
+
const warehouseService = new WarehouseService(deps.emFactory);
|
|
13
|
+
const stockLevelService = new StockLevelService(deps.emFactory, deps.catalogProducts, deps.catalogCategories, deps.eventBus, deps.auditLogService);
|
|
14
|
+
const searchWarehouses = {
|
|
15
|
+
id: 'inventory.search_warehouses',
|
|
16
|
+
moduleId: 'inventory',
|
|
17
|
+
kind: 'resolver',
|
|
18
|
+
description: 'Search warehouses by a name or code fragment (case-insensitive). Returns up to 20 matches as {id, label, code, active}.',
|
|
19
|
+
requiredPermission: 'catalog:read',
|
|
20
|
+
paramsSchema: SearchWarehousesParamsSchema,
|
|
21
|
+
execute: async (params) => {
|
|
22
|
+
const { items } = await warehouseService.list({ pageSize: 200 });
|
|
23
|
+
const needle = params.q.toLowerCase();
|
|
24
|
+
return items
|
|
25
|
+
.filter((w) => w.name.toLowerCase().includes(needle) || w.code.toLowerCase().includes(needle))
|
|
26
|
+
.slice(0, RESULT_LIMIT)
|
|
27
|
+
.map((w) => ({ id: w.id, label: w.name, code: w.code, active: w.active }));
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const setStockLevel = {
|
|
31
|
+
id: 'inventory.set_stock_level',
|
|
32
|
+
moduleId: 'inventory',
|
|
33
|
+
kind: 'mutation',
|
|
34
|
+
description: 'Set the ABSOLUTE on-hand stock quantity of a product in a warehouse. Resolve the product via catalog.search_products and the warehouse via inventory.search_warehouses first. This is captured into a plan the operator must confirm; it is not executed immediately.',
|
|
35
|
+
requiredPermission: 'catalog:write',
|
|
36
|
+
paramsSchema: SetStockLevelParamsSchema,
|
|
37
|
+
preview: async (params) => {
|
|
38
|
+
// D-75 — this module's own fork, not the caller's manager. A preview
|
|
39
|
+
// reads committed rows only, so a fresh fork sees exactly what the
|
|
40
|
+
// request's manager saw.
|
|
41
|
+
const em = deps.emFactory();
|
|
42
|
+
const product = await deps.catalogProducts.findById(params.productId);
|
|
43
|
+
if (!product)
|
|
44
|
+
throw new HttpError(404, 'PRODUCT_NOT_FOUND', 'Product not found.');
|
|
45
|
+
const warehouse = await em.findOne(Warehouse, { id: params.warehouseId });
|
|
46
|
+
if (!warehouse)
|
|
47
|
+
throw new HttpError(404, 'WAREHOUSE_NOT_FOUND', 'Warehouse not found.');
|
|
48
|
+
const level = await em.findOne(StockLevel, {
|
|
49
|
+
productId: params.productId,
|
|
50
|
+
warehouseId: params.warehouseId,
|
|
51
|
+
variantId: null,
|
|
52
|
+
});
|
|
53
|
+
const current = level?.onHand ?? 0;
|
|
54
|
+
return {
|
|
55
|
+
headline: `Set stock of "${label(product.name)}" in warehouse "${warehouse.name}" to ${params.quantity}`,
|
|
56
|
+
current: { onHand: current },
|
|
57
|
+
affectedCount: 1,
|
|
58
|
+
sample: [{ id: product.id, label: label(product.name) }],
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
execute: async (params, ctx) => {
|
|
62
|
+
const result = await stockLevelService.setOnHand({
|
|
63
|
+
productId: params.productId,
|
|
64
|
+
warehouseId: params.warehouseId,
|
|
65
|
+
onHand: params.quantity,
|
|
66
|
+
}, {
|
|
67
|
+
actorAdminUserId: ctx.adminUserId,
|
|
68
|
+
ipAddress: ctx.auditCtx.ipAddress ?? null,
|
|
69
|
+
userAgent: ctx.auditCtx.userAgent ?? null,
|
|
70
|
+
requestId: ctx.auditCtx.requestId ?? null,
|
|
71
|
+
});
|
|
72
|
+
return { before: result.before, after: result.after };
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
return [searchWarehouses, setStockLevel];
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=prompt-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-tools.js","sourceRoot":"","sources":["../../src/backend/prompt-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
3
|
+
import { type CatalogProductReadPort } from '@endora-commerce/contracts';
|
|
4
|
+
import type { WarehouseService } from './services/warehouse-service.js';
|
|
5
|
+
import type { StockLevelService } from './services/stock-level-service.js';
|
|
6
|
+
import type { WarehouseChannelService } from './services/warehouse-channel-service.js';
|
|
7
|
+
import type { ThresholdAdminService } from './services/threshold-admin-service.js';
|
|
8
|
+
import type { LowStockAlertService } from './services/low-stock-alert-service.js';
|
|
9
|
+
import type { AvailabilityNotificationService } from './services/availability-notification-service.js';
|
|
10
|
+
import type { CsvStockImporter } from './services/csv-stock-importer.js';
|
|
11
|
+
import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
|
|
12
|
+
/**
|
|
13
|
+
* Inventory admin routes — feature 010 (US1 + US2).
|
|
14
|
+
*
|
|
15
|
+
* **Every gate below is this module's own** (2026-08-29). All 21 used to
|
|
16
|
+
* enforce a code somebody else owns: nine reads on `orders:read` and twelve
|
|
17
|
+
* writes on `catalog:write`. Neither names the data touched, which is the
|
|
18
|
+
* discriminator `specs/080-f4-real-scope/payments-permission-ownership.md` §7.2
|
|
19
|
+
* sets and `specs/080-f4-real-scope/first-deployment-window.md` §2 applies per route rather than
|
|
20
|
+
* per module. A warehouse is a physical location with an address, not catalogue
|
|
21
|
+
* data; a channel↔warehouse assignment is fulfilment routing, deciding which
|
|
22
|
+
* stock a channel may sell; a stock level, a display-band threshold, a CSV
|
|
23
|
+
* import and a back-in-stock queue entry are all rows in this module's own
|
|
24
|
+
* tables. Nothing here reads or writes a product, a price or an order.
|
|
25
|
+
*
|
|
26
|
+
* The read side was the mirror of the same defect and is worth stating on its
|
|
27
|
+
* own: a merchandiser holding `catalog:read` could not see stock at all, while
|
|
28
|
+
* anyone holding `orders:read` could enumerate every warehouse and its address.
|
|
29
|
+
*
|
|
30
|
+
* `inventory:read` / `inventory:write`, and no third code. The one route that
|
|
31
|
+
* genuinely joins another module's table — the roster's `products` join in
|
|
32
|
+
* `StockLevelService` — is a *reach* and is ledgered as one; it is not an
|
|
33
|
+
* authority question, and it is refused to a catalogue editor exactly as the
|
|
34
|
+
* rest of this file now is.
|
|
35
|
+
*
|
|
36
|
+
* Surfaces:
|
|
37
|
+
* - Legacy single-bucket stock-level read/write (foundation 001 backward
|
|
38
|
+
* compat — kept until callers migrate to the per-warehouse PUT below).
|
|
39
|
+
* - Warehouse CRUD (US1).
|
|
40
|
+
* - Inventory landing KPIs + per-product roster + per-warehouse setOnHand
|
|
41
|
+
* (US2).
|
|
42
|
+
*/
|
|
43
|
+
export interface InventoryAdminDeps {
|
|
44
|
+
emFactory: () => EntityManager;
|
|
45
|
+
/** `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C). */
|
|
46
|
+
catalogProducts: CatalogProductReadPort;
|
|
47
|
+
warehouseService: WarehouseService;
|
|
48
|
+
stockLevelService: StockLevelService;
|
|
49
|
+
warehouseChannelService: WarehouseChannelService;
|
|
50
|
+
thresholdAdminService: ThresholdAdminService;
|
|
51
|
+
lowStockAlertService: LowStockAlertService;
|
|
52
|
+
availabilityNotificationService: AvailabilityNotificationService;
|
|
53
|
+
csvStockImporter: CsvStockImporter;
|
|
54
|
+
requireAdmin: RequireAdminFactory;
|
|
55
|
+
/** Feature 024 — resolves admin actor identity for audit entries. */
|
|
56
|
+
resolveAdminAuditContext?: (req: FastifyRequest) => {
|
|
57
|
+
actorAdminUserId: string;
|
|
58
|
+
impersonatedCustomerAccountId?: string | null;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export declare function registerInventoryAdminRoutes(app: FastifyInstance, deps: InventoryAdminDeps): Promise<void>;
|
|
62
|
+
//# sourceMappingURL=routes.admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.d.ts","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAQL,KAAK,sBAAsB,EAC5B,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,2EAA2E;IAC3E,eAAe,EAAE,sBAAsB,CAAC;IACxC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,uBAAuB,EAAE,uBAAuB,CAAC;IACjD,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,+BAA+B,EAAE,+BAA+B,CAAC;IACjE,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,mBAAmB,CAAC;IAClC,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAClD,gBAAgB,EAAE,MAAM,CAAC;QACzB,6BAA6B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/C,CAAC;CACH;AA6BD,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CA8ff"}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { assignWarehouseToChannelRequestSchema, createWarehouseRequestSchema, patchInventoryThresholdsRequestSchema, patchWarehouseChannelAssignmentRequestSchema, setProductWarehouseLowStockThresholdsRequestSchema, setStockLevelRequestSchema, updateWarehouseRequestSchema, } from '@endora-commerce/contracts';
|
|
3
|
+
import { StockLevel } from './entities/stock-level.entity.js';
|
|
4
|
+
import { DEFAULT_WAREHOUSE_ID } from './entities/warehouse.entity.js';
|
|
5
|
+
/** Build the per-request audit context for inventory route handlers. */
|
|
6
|
+
function buildAuditCtx(request, resolver) {
|
|
7
|
+
const base = resolver?.(request);
|
|
8
|
+
if (!base)
|
|
9
|
+
return undefined;
|
|
10
|
+
return {
|
|
11
|
+
actorAdminUserId: base.actorAdminUserId,
|
|
12
|
+
impersonatedCustomerAccountId: base.impersonatedCustomerAccountId ?? null,
|
|
13
|
+
ipAddress: request.ip ?? null,
|
|
14
|
+
userAgent: typeof request.headers['user-agent'] === 'string'
|
|
15
|
+
? request.headers['user-agent']
|
|
16
|
+
: null,
|
|
17
|
+
requestId: request.id,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const setLegacyStockLevelSchema = z.object({
|
|
21
|
+
productId: z.string().uuid(),
|
|
22
|
+
variantId: z.string().uuid().nullable().optional(),
|
|
23
|
+
onHand: z.number().int().nonnegative(),
|
|
24
|
+
});
|
|
25
|
+
export async function registerInventoryAdminRoutes(app, deps) {
|
|
26
|
+
const { emFactory, catalogProducts, warehouseService, stockLevelService, warehouseChannelService, thresholdAdminService, lowStockAlertService, availabilityNotificationService, csvStockImporter, requireAdmin, } = deps;
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Inventory landing KPIs (US2)
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
app.get('/api/v1/admin/inventory', { preHandler: requireAdmin('inventory:read') }, async () => {
|
|
31
|
+
const data = await stockLevelService.listLandingKpis();
|
|
32
|
+
return { data };
|
|
33
|
+
});
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Per-product roster + per-warehouse setOnHand (US2)
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
app.get('/api/v1/admin/inventory/levels', { preHandler: requireAdmin('inventory:read') }, async (request) => {
|
|
38
|
+
const q = (request.query ?? {});
|
|
39
|
+
const search = q['q']?.trim();
|
|
40
|
+
const lowOnly = q['low'] === '1' || q['low'] === 'true';
|
|
41
|
+
const outOnly = q['out'] === '1' || q['out'] === 'true';
|
|
42
|
+
const data = await stockLevelService.listRoster({
|
|
43
|
+
...(q['productId'] ? { productId: q['productId'] } : {}),
|
|
44
|
+
...(q['warehouseId'] ? { warehouseId: q['warehouseId'] } : {}),
|
|
45
|
+
...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
|
|
46
|
+
...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
|
|
47
|
+
...(search ? { q: search } : {}),
|
|
48
|
+
...(lowOnly ? { lowOnly: true } : {}),
|
|
49
|
+
...(outOnly ? { outOnly: true } : {}),
|
|
50
|
+
});
|
|
51
|
+
return data;
|
|
52
|
+
});
|
|
53
|
+
app.put('/api/v1/admin/inventory/levels', {
|
|
54
|
+
preHandler: requireAdmin('inventory:write'),
|
|
55
|
+
schema: { body: setStockLevelRequestSchema },
|
|
56
|
+
}, async (request) => {
|
|
57
|
+
const body = setStockLevelRequestSchema.parse(request.body);
|
|
58
|
+
const data = await stockLevelService.setOnHand({
|
|
59
|
+
productId: body.productId,
|
|
60
|
+
warehouseId: body.warehouseId,
|
|
61
|
+
...(body.variantId ? { variantId: body.variantId } : {}),
|
|
62
|
+
onHand: body.onHand,
|
|
63
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
64
|
+
return { data };
|
|
65
|
+
});
|
|
66
|
+
// Per-(product, warehouse) low-stock thresholds. Consulted only when the
|
|
67
|
+
// product runs in `lowStockThresholdMode = 'per_warehouse'`, but writeable
|
|
68
|
+
// regardless of mode so admins can pre-populate values before switching.
|
|
69
|
+
app.put('/api/v1/admin/inventory/warehouse-low-stock-thresholds', {
|
|
70
|
+
preHandler: requireAdmin('inventory:write'),
|
|
71
|
+
schema: { body: setProductWarehouseLowStockThresholdsRequestSchema },
|
|
72
|
+
}, async (request, reply) => {
|
|
73
|
+
const body = setProductWarehouseLowStockThresholdsRequestSchema.parse(request.body);
|
|
74
|
+
await stockLevelService.setProductWarehouseThresholds({
|
|
75
|
+
productId: body.productId,
|
|
76
|
+
entries: body.thresholds,
|
|
77
|
+
});
|
|
78
|
+
return reply.status(204).send();
|
|
79
|
+
});
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Warehouse CRUD (US1)
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
app.get('/api/v1/admin/warehouses', { preHandler: requireAdmin('inventory:read') }, async (request) => {
|
|
84
|
+
const q = (request.query ?? {});
|
|
85
|
+
const data = await warehouseService.list({
|
|
86
|
+
...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
|
|
87
|
+
...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
|
|
88
|
+
activeOnly: q['activeOnly'] === 'true',
|
|
89
|
+
withTotals: q['withTotals'] !== 'false',
|
|
90
|
+
});
|
|
91
|
+
return data;
|
|
92
|
+
});
|
|
93
|
+
app.get('/api/v1/admin/warehouses/:id', { preHandler: requireAdmin('inventory:read') }, async (request, reply) => {
|
|
94
|
+
const { id } = request.params;
|
|
95
|
+
const data = await warehouseService.getById(id, { withTotals: true });
|
|
96
|
+
if (!data) {
|
|
97
|
+
reply.status(404);
|
|
98
|
+
return {
|
|
99
|
+
error: {
|
|
100
|
+
code: 'WAREHOUSE_NOT_FOUND',
|
|
101
|
+
message: 'Warehouse not found',
|
|
102
|
+
requestId: request.id,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { data };
|
|
107
|
+
});
|
|
108
|
+
app.post('/api/v1/admin/warehouses', {
|
|
109
|
+
preHandler: requireAdmin('inventory:write'),
|
|
110
|
+
schema: { body: createWarehouseRequestSchema },
|
|
111
|
+
}, async (request, reply) => {
|
|
112
|
+
const body = createWarehouseRequestSchema.parse(request.body);
|
|
113
|
+
const data = await warehouseService.create({
|
|
114
|
+
name: body.name,
|
|
115
|
+
code: body.code,
|
|
116
|
+
...(body.active !== undefined ? { active: body.active } : {}),
|
|
117
|
+
...(body.description !== undefined ? { description: body.description ?? null } : {}),
|
|
118
|
+
...(body.address !== undefined ? { address: body.address ?? null } : {}),
|
|
119
|
+
...(body.contact !== undefined ? { contact: body.contact ?? null } : {}),
|
|
120
|
+
...(body.defaultLowStockThreshold !== undefined
|
|
121
|
+
? { defaultLowStockThreshold: body.defaultLowStockThreshold ?? null }
|
|
122
|
+
: {}),
|
|
123
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
124
|
+
reply.status(201);
|
|
125
|
+
return { data };
|
|
126
|
+
});
|
|
127
|
+
app.patch('/api/v1/admin/warehouses/:id', {
|
|
128
|
+
preHandler: requireAdmin('inventory:write'),
|
|
129
|
+
schema: { body: updateWarehouseRequestSchema },
|
|
130
|
+
}, async (request) => {
|
|
131
|
+
const { id } = request.params;
|
|
132
|
+
const body = updateWarehouseRequestSchema.parse(request.body);
|
|
133
|
+
const data = await warehouseService.update(id, {
|
|
134
|
+
...(body.name !== undefined ? { name: body.name } : {}),
|
|
135
|
+
...(body.active !== undefined ? { active: body.active } : {}),
|
|
136
|
+
...(body.description !== undefined ? { description: body.description ?? null } : {}),
|
|
137
|
+
...(body.address !== undefined ? { address: body.address ?? null } : {}),
|
|
138
|
+
...(body.contact !== undefined ? { contact: body.contact ?? null } : {}),
|
|
139
|
+
...(body.defaultLowStockThreshold !== undefined
|
|
140
|
+
? { defaultLowStockThreshold: body.defaultLowStockThreshold ?? null }
|
|
141
|
+
: {}),
|
|
142
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
143
|
+
return { data };
|
|
144
|
+
});
|
|
145
|
+
app.delete('/api/v1/admin/warehouses/:id', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
|
|
146
|
+
const { id } = request.params;
|
|
147
|
+
await warehouseService.delete(id, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
148
|
+
reply.status(204);
|
|
149
|
+
});
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Low-stock alerts (US4)
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
app.get('/api/v1/admin/inventory/low-stock', { preHandler: requireAdmin('inventory:read') }, async () => {
|
|
154
|
+
const items = await lowStockAlertService.listLowStock();
|
|
155
|
+
return { items };
|
|
156
|
+
});
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Display-band thresholds (US5)
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
app.get('/api/v1/admin/inventory/thresholds', { preHandler: requireAdmin('inventory:read') }, async () => {
|
|
161
|
+
const data = await thresholdAdminService.read();
|
|
162
|
+
return { data };
|
|
163
|
+
});
|
|
164
|
+
app.patch('/api/v1/admin/inventory/thresholds', {
|
|
165
|
+
preHandler: requireAdmin('inventory:write'),
|
|
166
|
+
schema: { body: patchInventoryThresholdsRequestSchema },
|
|
167
|
+
}, async (request) => {
|
|
168
|
+
const body = patchInventoryThresholdsRequestSchema.parse(request.body);
|
|
169
|
+
const data = await thresholdAdminService.patch({
|
|
170
|
+
...(body.global ? { global: body.global } : {}),
|
|
171
|
+
...(body.perCategory ? { perCategory: body.perCategory } : {}),
|
|
172
|
+
...(body.perProduct ? { perProduct: body.perProduct } : {}),
|
|
173
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
174
|
+
return { data };
|
|
175
|
+
});
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// CSV stock import (US7)
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
app.post('/api/v1/admin/inventory/import', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
|
|
180
|
+
const q = (request.query ?? {});
|
|
181
|
+
const body = (request.body ?? {});
|
|
182
|
+
if (!body.csv || !body.warehouseId) {
|
|
183
|
+
reply.status(400);
|
|
184
|
+
return {
|
|
185
|
+
error: {
|
|
186
|
+
code: 'VALIDATION_FAILED',
|
|
187
|
+
message: 'csv + warehouseId are required',
|
|
188
|
+
requestId: request.id,
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const result = await csvStockImporter.run({
|
|
193
|
+
csv: body.csv,
|
|
194
|
+
warehouseId: body.warehouseId,
|
|
195
|
+
dryRun: q['dryRun'] === 'true',
|
|
196
|
+
...(q['fileName'] !== undefined ? { fileName: q['fileName'] } : {}),
|
|
197
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
198
|
+
return { data: result };
|
|
199
|
+
});
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Availability notifications (US6)
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
app.get('/api/v1/admin/inventory/availability-notifications', { preHandler: requireAdmin('inventory:read') }, async (request) => {
|
|
204
|
+
const q = (request.query ?? {});
|
|
205
|
+
const data = await availabilityNotificationService.listForAdmin({
|
|
206
|
+
...(q['productId'] ? { productId: q['productId'] } : {}),
|
|
207
|
+
...(q['status'] ? { status: q['status'] } : {}),
|
|
208
|
+
...(q['page'] ? { page: Number.parseInt(q['page'], 10) } : {}),
|
|
209
|
+
...(q['pageSize'] ? { pageSize: Number.parseInt(q['pageSize'], 10) } : {}),
|
|
210
|
+
});
|
|
211
|
+
return data;
|
|
212
|
+
});
|
|
213
|
+
app.patch('/api/v1/admin/inventory/availability-notifications/:id', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
|
|
214
|
+
const { id } = request.params;
|
|
215
|
+
const body = (request.body ?? {});
|
|
216
|
+
if (body.status !== 'cancelled') {
|
|
217
|
+
reply.status(400);
|
|
218
|
+
return {
|
|
219
|
+
error: {
|
|
220
|
+
code: 'VALIDATION_FAILED',
|
|
221
|
+
message: 'Only `status: "cancelled"` is supported on this endpoint',
|
|
222
|
+
requestId: request.id,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
await availabilityNotificationService.cancel(id);
|
|
227
|
+
reply.status(204);
|
|
228
|
+
return null;
|
|
229
|
+
});
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Sales channel ↔ warehouse binding (US3)
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
app.get('/api/v1/admin/sales-channels/:channelId/warehouses', { preHandler: requireAdmin('inventory:read') }, async (request) => {
|
|
234
|
+
const { channelId } = request.params;
|
|
235
|
+
const items = await warehouseChannelService.listForChannel(channelId);
|
|
236
|
+
return { items };
|
|
237
|
+
});
|
|
238
|
+
app.post('/api/v1/admin/sales-channels/:channelId/warehouses', {
|
|
239
|
+
preHandler: requireAdmin('inventory:write'),
|
|
240
|
+
schema: { body: assignWarehouseToChannelRequestSchema },
|
|
241
|
+
}, async (request, reply) => {
|
|
242
|
+
const { channelId } = request.params;
|
|
243
|
+
const body = assignWarehouseToChannelRequestSchema.parse(request.body);
|
|
244
|
+
const data = await warehouseChannelService.assign(channelId, {
|
|
245
|
+
warehouseId: body.warehouseId,
|
|
246
|
+
...(body.isDefault !== undefined ? { isDefault: body.isDefault } : {}),
|
|
247
|
+
...(body.sortOrder !== undefined ? { sortOrder: body.sortOrder } : {}),
|
|
248
|
+
});
|
|
249
|
+
reply.status(201);
|
|
250
|
+
return { data };
|
|
251
|
+
});
|
|
252
|
+
app.patch('/api/v1/admin/sales-channels/:channelId/warehouses/:assignmentId', {
|
|
253
|
+
preHandler: requireAdmin('inventory:write'),
|
|
254
|
+
schema: { body: patchWarehouseChannelAssignmentRequestSchema },
|
|
255
|
+
}, async (request) => {
|
|
256
|
+
const { channelId, assignmentId } = request.params;
|
|
257
|
+
const body = patchWarehouseChannelAssignmentRequestSchema.parse(request.body);
|
|
258
|
+
const data = await warehouseChannelService.patch(channelId, assignmentId, {
|
|
259
|
+
...(body.isDefault !== undefined ? { isDefault: body.isDefault } : {}),
|
|
260
|
+
...(body.sortOrder !== undefined ? { sortOrder: body.sortOrder } : {}),
|
|
261
|
+
});
|
|
262
|
+
return { data };
|
|
263
|
+
});
|
|
264
|
+
app.delete('/api/v1/admin/sales-channels/:channelId/warehouses/:assignmentId', { preHandler: requireAdmin('inventory:write') }, async (request, reply) => {
|
|
265
|
+
const { channelId, assignmentId } = request.params;
|
|
266
|
+
await warehouseChannelService.unassign(channelId, assignmentId);
|
|
267
|
+
reply.status(204);
|
|
268
|
+
});
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
// Foundation 001 backward-compat — single-bucket stock list + write.
|
|
271
|
+
// Both routes default to the seeded Default warehouse.
|
|
272
|
+
//
|
|
273
|
+
// The two callers this header used to name are gone: the admin import posts to
|
|
274
|
+
// `/inventory/import` and the catalog seed writes `StockLevel` through the
|
|
275
|
+
// EntityManager. Nothing in the repository calls either route — no admin
|
|
276
|
+
// screen, no admin API client call, no seed, no script — and the module's
|
|
277
|
+
// documentation page lists neither. They are kept for a deployment's own
|
|
278
|
+
// integration, which is the only caller they can still have (issue #125).
|
|
279
|
+
//
|
|
280
|
+
// Issue #139 (D-66) ruled the `GET .../legacy` below deletable on the
|
|
281
|
+
// condition that `GET .../levels` is a strict superset of it, "and if
|
|
282
|
+
// `/levels` turns out not to cover a field this returns, that gap is the
|
|
283
|
+
// finding and the deletion waits". It does not: `/levels` aggregates per
|
|
284
|
+
// product across variants and warehouses, so it answers with neither
|
|
285
|
+
// `variantId` nor the row's `updatedAt` (nor the `stock_levels` row id, nor
|
|
286
|
+
// the pre-computed `available`, both derivable). The deletion therefore
|
|
287
|
+
// waits on a per-row read that covers those two fields, not on a caller
|
|
288
|
+
// census — that census is done, and it is zero.
|
|
289
|
+
// ---------------------------------------------------------------------------
|
|
290
|
+
/**
|
|
291
|
+
* @deprecated Use `PUT /api/v1/admin/inventory/levels`, which takes an
|
|
292
|
+
* explicit `warehouseId` instead of writing into the seeded Default one.
|
|
293
|
+
* Kept for a deployment's own integration until the access log or the
|
|
294
|
+
* deployment owner confirms nothing calls it (issue #139, D-66 part 3).
|
|
295
|
+
*
|
|
296
|
+
* Delegates to `StockLevelService.setOnHand`, the single point that emits
|
|
297
|
+
* `inventory.adjusted.v1` (issue #139). Until then this handler wrote
|
|
298
|
+
* `StockLevel.onHand` straight through the EntityManager, so a restock
|
|
299
|
+
* performed here fired no back-in-stock fan-out (US6) and no low-stock
|
|
300
|
+
* crossing alert (US4), and validated neither the product nor the warehouse
|
|
301
|
+
* — a typo'd product id created a stock row for a product that does not
|
|
302
|
+
* exist. The delegation is the whole of that repair; the response shape and
|
|
303
|
+
* the `catalog:write` gate are unchanged.
|
|
304
|
+
*
|
|
305
|
+
* The audited path for a stock change is `StockLevelService`
|
|
306
|
+
* (`stock_level.adjust`); this endpoint recorded nothing until issue #122,
|
|
307
|
+
* which the coverage scan could not see until it reached route files. The
|
|
308
|
+
* delegate records the same action token, so the two paths still land in one
|
|
309
|
+
* audit history.
|
|
310
|
+
*
|
|
311
|
+
* Deliberately **not** a Command (issue #125). A Command is what an undo
|
|
312
|
+
* attaches to, and an on-hand count is not a value an undo may restore:
|
|
313
|
+
* between the write and the undo, orders reserve and release stock, so
|
|
314
|
+
* putting back the number that stood before would overwrite movements
|
|
315
|
+
* nobody asked to reverse. The other two paths that set stock — the service
|
|
316
|
+
* behind `/levels` and the CSV importer — take the same view. Nor may this
|
|
317
|
+
* row's action diverge from theirs: an auditor reading a product's stock
|
|
318
|
+
* history reads one action, not three.
|
|
319
|
+
*/
|
|
320
|
+
app.put('/api/v1/admin/inventory', {
|
|
321
|
+
preHandler: requireAdmin('inventory:write'),
|
|
322
|
+
schema: { body: setLegacyStockLevelSchema },
|
|
323
|
+
}, async (request) => {
|
|
324
|
+
const body = setLegacyStockLevelSchema.parse(request.body);
|
|
325
|
+
const variantId = body.variantId ?? null;
|
|
326
|
+
await stockLevelService.setOnHand({
|
|
327
|
+
productId: body.productId,
|
|
328
|
+
warehouseId: DEFAULT_WAREHOUSE_ID,
|
|
329
|
+
variantId,
|
|
330
|
+
onHand: body.onHand,
|
|
331
|
+
}, buildAuditCtx(request, deps.resolveAdminAuditContext));
|
|
332
|
+
// Re-read for the foundation-001 response shape: `setOnHand` answers with
|
|
333
|
+
// the before/after counts, this route has always answered with the row.
|
|
334
|
+
const row = await emFactory().findOneOrFail(StockLevel, {
|
|
335
|
+
productId: body.productId,
|
|
336
|
+
variantId,
|
|
337
|
+
warehouseId: DEFAULT_WAREHOUSE_ID,
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
data: {
|
|
341
|
+
id: row.id,
|
|
342
|
+
productId: row.productId,
|
|
343
|
+
variantId: row.variantId ?? null,
|
|
344
|
+
onHand: row.onHand,
|
|
345
|
+
reserved: row.reserved,
|
|
346
|
+
available: row.onHand - row.reserved,
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
app.get('/api/v1/admin/inventory/legacy', { preHandler: requireAdmin('inventory:read') }, async (request) => {
|
|
351
|
+
const q = (request.query ?? {});
|
|
352
|
+
const where = {};
|
|
353
|
+
if (q['productId'])
|
|
354
|
+
where['productId'] = q['productId'];
|
|
355
|
+
const limit = Math.min(Math.max(Number.parseInt(q['limit'] ?? '200', 10), 1), 500);
|
|
356
|
+
const em = emFactory();
|
|
357
|
+
const rows = await em.find(StockLevel, where, {
|
|
358
|
+
limit,
|
|
359
|
+
orderBy: { updatedAt: 'desc' },
|
|
360
|
+
});
|
|
361
|
+
const productIds = Array.from(new Set(rows.map((r) => r.productId)));
|
|
362
|
+
const products = await catalogProducts.findByIds(productIds);
|
|
363
|
+
const productById = new Map(products.map((p) => [p.id, p]));
|
|
364
|
+
return {
|
|
365
|
+
data: rows.map((r) => {
|
|
366
|
+
const p = productById.get(r.productId);
|
|
367
|
+
return {
|
|
368
|
+
id: r.id,
|
|
369
|
+
productId: r.productId,
|
|
370
|
+
variantId: r.variantId ?? null,
|
|
371
|
+
warehouseId: r.warehouseId,
|
|
372
|
+
onHand: r.onHand,
|
|
373
|
+
reserved: r.reserved,
|
|
374
|
+
available: r.onHand - r.reserved,
|
|
375
|
+
productSku: p?.sku ?? null,
|
|
376
|
+
productName: p?.name ?? null,
|
|
377
|
+
updatedAt: r.updatedAt.toISOString(),
|
|
378
|
+
};
|
|
379
|
+
}),
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=routes.admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.admin.js","sourceRoot":"","sources":["../../src/backend/routes.admin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FastifyInstance, FastifyRequest } from 'fastify';
|
|
2
|
+
import { type CatalogCategoryReadPort, type CatalogProductReadPort, type CustomerAccountReadPort } from '@endora-commerce/contracts';
|
|
3
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
4
|
+
import type { AvailabilityNotificationService } from './services/availability-notification-service.js';
|
|
5
|
+
import type { WarehouseChannelService } from './services/warehouse-channel-service.js';
|
|
6
|
+
import type { StockLevelService } from './services/stock-level-service.js';
|
|
7
|
+
import type { SalesChannelResolutionPort } from '@endora-commerce/platform/kernel';
|
|
8
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
9
|
+
export interface InventoryRoutesDeps {
|
|
10
|
+
emFactory: () => EntityManager;
|
|
11
|
+
/** `catalogProductReadPort`, owned by `catalog` (feature 075, Phase C). */
|
|
12
|
+
catalogProducts: CatalogProductReadPort;
|
|
13
|
+
/** `catalogCategoryReadPort`, owned by `catalog` — the threshold chain. */
|
|
14
|
+
catalogCategories: CatalogCategoryReadPort;
|
|
15
|
+
/** `customerAccountReadPort`, owned by `customer_accounts` (feature 075). */
|
|
16
|
+
customerAccounts: CustomerAccountReadPort;
|
|
17
|
+
availabilityService: AvailabilityNotificationService;
|
|
18
|
+
requireCustomer: (req: FastifyRequest, reply: unknown) => Promise<void>;
|
|
19
|
+
resolveCustomerContext: (req: FastifyRequest) => {
|
|
20
|
+
customerAccountId: string;
|
|
21
|
+
organizationId: string;
|
|
22
|
+
};
|
|
23
|
+
/** Optional — present in production composition. Used to resolve the
|
|
24
|
+
* channel for the storefront-public stock readout. */
|
|
25
|
+
channelResolver?: SalesChannelResolutionPort;
|
|
26
|
+
warehouseChannelService?: WarehouseChannelService;
|
|
27
|
+
stockLevelService?: StockLevelService;
|
|
28
|
+
settingsService?: SettingsReadPort;
|
|
29
|
+
/**
|
|
30
|
+
* Feature 026 US4 — Per-request resolver that returns the warehouse-id
|
|
31
|
+
* allow-list for the caller's Organization, or `null` when no restriction
|
|
32
|
+
* applies (anonymous request, no-org Customer, or org has no warehouses
|
|
33
|
+
* explicitly assigned). When the returned list is non-empty, the
|
|
34
|
+
* storefront stock-figure endpoint intersects its candidate warehouse
|
|
35
|
+
* set with this list before summing on-hand.
|
|
36
|
+
*/
|
|
37
|
+
resolveOrganizationWarehouseAllowList?: (req: FastifyRequest) => Promise<string[] | null>;
|
|
38
|
+
}
|
|
39
|
+
export declare function registerInventoryRoutes(app: FastifyInstance, deps: InventoryRoutesDeps): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,EAIL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAG7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iDAAiD,CAAC;AACvG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AACvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAQnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,2EAA2E;IAC3E,eAAe,EAAE,sBAAsB,CAAC;IACxC,2EAA2E;IAC3E,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C,6EAA6E;IAC7E,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,mBAAmB,EAAE,+BAA+B,CAAC;IACrD,eAAe,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,sBAAsB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAC/C,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF;2DACuD;IACvD,eAAe,CAAC,EAAE,0BAA0B,CAAC;IAC7C,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC;;;;;;;OAOG;IACH,qCAAqC,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;CAC3F;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CA8Of"}
|