@classytic/flow 0.1.4
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/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +258 -0
- package/dist/allocation-policy-my_HfzdV.d.mts +23 -0
- package/dist/base-MWBqRFM2.mjs +16 -0
- package/dist/catalog-bridge-K8bdkncJ.d.mts +29 -0
- package/dist/cost-layer.port-iH9pvZqB.d.mts +30 -0
- package/dist/cost-layer.service-BQ1bs-XN.mjs +86 -0
- package/dist/cost-layer.service-DWmo9dQz.d.mts +53 -0
- package/dist/count.port-BRqwGbi3.d.mts +57 -0
- package/dist/counting/index.d.mts +2 -0
- package/dist/counting/index.mjs +2 -0
- package/dist/counting.service-BiQXqorv.mjs +232 -0
- package/dist/counting.service-CpAxU2G0.d.mts +74 -0
- package/dist/domain/contracts/index.d.mts +3 -0
- package/dist/domain/contracts/index.mjs +1 -0
- package/dist/domain/enums/index.d.mts +2 -0
- package/dist/domain/enums/index.mjs +4 -0
- package/dist/domain/index.d.mts +24 -0
- package/dist/domain/index.mjs +10 -0
- package/dist/domain/policies/index.d.mts +4 -0
- package/dist/domain/policies/index.mjs +1 -0
- package/dist/domain-D5cpMpR0.mjs +96 -0
- package/dist/domain-errors-D7S9ydNF.mjs +133 -0
- package/dist/enums-C3_z6aHC.mjs +82 -0
- package/dist/event-bus-BNmyoJb4.mjs +37 -0
- package/dist/event-bus-Um_xrcMY.d.mts +21 -0
- package/dist/event-emitter.port-BFh2pasY.d.mts +183 -0
- package/dist/event-types-BSqQOvXv.mjs +29 -0
- package/dist/events/index.d.mts +3 -0
- package/dist/events/index.mjs +3 -0
- package/dist/idempotency.port-CTC70JON.d.mts +55 -0
- package/dist/index-Bia4m8d2.d.mts +67 -0
- package/dist/index-BmNm3oNU2.d.mts +107 -0
- package/dist/index-C5PciI9P.d.mts +203 -0
- package/dist/index-CMTUKEK_.d.mts +308 -0
- package/dist/index-C_aEnozN.d.mts +220 -0
- package/dist/index-CulWO137.d.mts +107 -0
- package/dist/index-DFF0GJ4J.d.mts +36 -0
- package/dist/index-DsE7lZdO.d.mts +11 -0
- package/dist/index-DwO9IdNa.d.mts +1 -0
- package/dist/index-dtWUZr2a2.d.mts +350 -0
- package/dist/index.d.mts +128 -0
- package/dist/index.mjs +102 -0
- package/dist/insufficient-stock.error-Dyr4BYaV.mjs +15 -0
- package/dist/location.port-CValXIpb.d.mts +52 -0
- package/dist/lot.port-ChsmvZqs.d.mts +32 -0
- package/dist/models/index.d.mts +2 -0
- package/dist/models/index.mjs +2 -0
- package/dist/models-CHTMbp-G.mjs +1020 -0
- package/dist/move-group.port-DHGoQA3d.d.mts +56 -0
- package/dist/move-status-DkaFp2GD.mjs +38 -0
- package/dist/move.port-Qg1CYp7h.d.mts +89 -0
- package/dist/package.service-4tcAwBbr.mjs +95 -0
- package/dist/package.service-C605NaBQ.d.mts +42 -0
- package/dist/packaging/index.d.mts +2 -0
- package/dist/packaging/index.mjs +2 -0
- package/dist/procurement/index.d.mts +2 -0
- package/dist/procurement/index.mjs +2 -0
- package/dist/quant.port-BBa66PBT.d.mts +42 -0
- package/dist/removal-policy-BItBB8FD.d.mts +29 -0
- package/dist/replenishment-rule.port-DnEYtbyD.d.mts +78 -0
- package/dist/replenishment.service-BT9P-HKM.mjs +284 -0
- package/dist/replenishment.service-HO0sDhB_.d.mts +89 -0
- package/dist/reporting/index.d.mts +2 -0
- package/dist/reporting/index.mjs +2 -0
- package/dist/reporting-CL5ffrKM.mjs +243 -0
- package/dist/repositories/index.d.mts +2 -0
- package/dist/repositories/index.mjs +2 -0
- package/dist/repositories-nZXJKvLW.mjs +842 -0
- package/dist/reservation-status-ZfuTaWG0.mjs +22 -0
- package/dist/reservation.port-l9NFQ0si.d.mts +85 -0
- package/dist/reservations/index.d.mts +2 -0
- package/dist/reservations/index.mjs +2 -0
- package/dist/reservations-Cg4wN0QB.mjs +112 -0
- package/dist/routing/index.d.mts +362 -0
- package/dist/routing/index.mjs +582 -0
- package/dist/runtime-config-C0ggPkiK.mjs +40 -0
- package/dist/runtime-config-CQLtPPqY.d.mts +38 -0
- package/dist/scan-token-CNM9QVLY.d.mts +26 -0
- package/dist/scanning/index.d.mts +45 -0
- package/dist/scanning/index.mjs +228 -0
- package/dist/services/index.d.mts +8 -0
- package/dist/services/index.mjs +8 -0
- package/dist/services-_lLO4Xbl.mjs +1009 -0
- package/dist/stock-move-group-C0DqUfPY.mjs +88 -0
- package/dist/stock-package-BIarxbDS.d.mts +19 -0
- package/dist/stock-quant-CZhgvTu7.d.mts +41 -0
- package/dist/tenant-guard-6Ne-BILP.mjs +12 -0
- package/dist/tenant-isolation.error-D3OcKUdx.mjs +11 -0
- package/dist/trace.service-B9vAh-l-.d.mts +55 -0
- package/dist/trace.service-DE6Eh8_8.mjs +71 -0
- package/dist/traceability/index.d.mts +2 -0
- package/dist/traceability/index.mjs +2 -0
- package/dist/types/index.d.mts +2 -0
- package/dist/types/index.mjs +1 -0
- package/dist/unit-of-work.port-CWEkrDKu.d.mts +17 -0
- package/dist/valuation/index.d.mts +78 -0
- package/dist/valuation/index.mjs +103 -0
- package/dist/valuation-policy-Dco8c9Vw.d.mts +14 -0
- package/dist/virtual-locations-B9zXqPdi.d.mts +38 -0
- package/package.json +155 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/domain/enums/group-type.ts
|
|
2
|
+
const GroupType = {
|
|
3
|
+
receipt: "receipt",
|
|
4
|
+
transfer: "transfer",
|
|
5
|
+
shipment: "shipment",
|
|
6
|
+
return: "return",
|
|
7
|
+
adjustment: "adjustment",
|
|
8
|
+
pick_wave: "pick_wave",
|
|
9
|
+
count_reconciliation: "count_reconciliation"
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/domain/enums/reservation-status.ts
|
|
13
|
+
const ReservationStatus = {
|
|
14
|
+
active: "active",
|
|
15
|
+
partially_consumed: "partially_consumed",
|
|
16
|
+
consumed: "consumed",
|
|
17
|
+
released: "released",
|
|
18
|
+
expired: "expired",
|
|
19
|
+
cancelled: "cancelled"
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { GroupType as n, ReservationStatus as t };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { a as ReservationStatus } from "./index-CulWO137.mjs";
|
|
2
|
+
import { t as FlowContext } from "./index-DFF0GJ4J.mjs";
|
|
3
|
+
import { t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/domain/entities/inventory-node.d.ts
|
|
6
|
+
interface Address {
|
|
7
|
+
street?: string;
|
|
8
|
+
city?: string;
|
|
9
|
+
state?: string;
|
|
10
|
+
postalCode?: string;
|
|
11
|
+
country?: string;
|
|
12
|
+
coordinates?: {
|
|
13
|
+
lat: number;
|
|
14
|
+
lng: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
interface InventoryNode {
|
|
18
|
+
_id: string;
|
|
19
|
+
organizationId: string;
|
|
20
|
+
code: string;
|
|
21
|
+
name: string;
|
|
22
|
+
type: string;
|
|
23
|
+
status: 'active' | 'inactive';
|
|
24
|
+
timezone?: string;
|
|
25
|
+
currency?: string;
|
|
26
|
+
address?: Address;
|
|
27
|
+
capabilities?: string[];
|
|
28
|
+
isDefault?: boolean;
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
createdAt?: Date;
|
|
31
|
+
updatedAt?: Date;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/domain/entities/reservation.d.ts
|
|
35
|
+
interface Reservation {
|
|
36
|
+
_id: string;
|
|
37
|
+
organizationId: string;
|
|
38
|
+
reservationType: 'soft' | 'hard';
|
|
39
|
+
ownerType: string;
|
|
40
|
+
ownerId: string;
|
|
41
|
+
skuRef: string;
|
|
42
|
+
locationId: string;
|
|
43
|
+
lotId?: string;
|
|
44
|
+
quantity: number;
|
|
45
|
+
quantityConsumed?: number;
|
|
46
|
+
status: ReservationStatus;
|
|
47
|
+
expiresAt?: Date;
|
|
48
|
+
allocationPolicy?: string;
|
|
49
|
+
createdBy?: string;
|
|
50
|
+
createdAt?: Date;
|
|
51
|
+
releasedAt?: Date;
|
|
52
|
+
updatedAt?: Date;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/domain/ports/node.port.d.ts
|
|
56
|
+
interface NodePort {
|
|
57
|
+
findById(id: string, ctx: FlowContext, session?: TransactionSession): Promise<InventoryNode | null>;
|
|
58
|
+
findDefault(ctx: FlowContext, session?: TransactionSession): Promise<InventoryNode | null>;
|
|
59
|
+
list(ctx: FlowContext, session?: TransactionSession): Promise<InventoryNode[]>;
|
|
60
|
+
create(input: Omit<InventoryNode, '_id' | 'createdAt' | 'updatedAt'>, session?: TransactionSession): Promise<InventoryNode>;
|
|
61
|
+
update(id: string, updates: Partial<InventoryNode>, ctx: FlowContext, session?: TransactionSession): Promise<InventoryNode>;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/domain/ports/reservation.port.d.ts
|
|
65
|
+
interface CreateReservationInput {
|
|
66
|
+
organizationId: string;
|
|
67
|
+
reservationType: 'soft' | 'hard';
|
|
68
|
+
ownerType: string;
|
|
69
|
+
ownerId: string;
|
|
70
|
+
skuRef: string;
|
|
71
|
+
locationId: string;
|
|
72
|
+
lotId?: string;
|
|
73
|
+
quantity: number;
|
|
74
|
+
expiresAt?: Date;
|
|
75
|
+
allocationPolicy?: string;
|
|
76
|
+
}
|
|
77
|
+
interface ReservationPort {
|
|
78
|
+
create(input: CreateReservationInput, session?: TransactionSession): Promise<Reservation>;
|
|
79
|
+
findById(id: string, ctx: FlowContext, session?: TransactionSession): Promise<Reservation | null>;
|
|
80
|
+
findByOwner(ownerType: string, ownerId: string, ctx: FlowContext, session?: TransactionSession): Promise<Reservation[]>;
|
|
81
|
+
updateStatus(id: string, status: ReservationStatus, updates: Partial<Reservation>, session?: TransactionSession): Promise<Reservation>;
|
|
82
|
+
findExpired(asOf: Date, ctx: FlowContext, session?: TransactionSession): Promise<Reservation[]>;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { Address as a, Reservation as i, ReservationPort as n, InventoryNode as o, NodePort as r, CreateReservationInput as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as FifoStrategy, i as FefoStrategy, n as ExactLotStrategy, o as allocateFromSorted, r as LifoStrategy, t as NearestStrategy } from "../reservations-Cg4wN0QB.mjs";
|
|
2
|
+
export { ExactLotStrategy, FefoStrategy, FifoStrategy, LifoStrategy, NearestStrategy, allocateFromSorted };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//#region src/reservations/fifo.strategy.ts
|
|
2
|
+
/**
|
|
3
|
+
* FIFO allocation — consume oldest stock first (by inDate).
|
|
4
|
+
* Default strategy for most operations.
|
|
5
|
+
*/
|
|
6
|
+
var FifoStrategy = class {
|
|
7
|
+
name = "fifo";
|
|
8
|
+
resolve(_skuRef, quantity, candidates) {
|
|
9
|
+
return allocateFromSorted([...candidates].sort((a, b) => new Date(a.inDate).getTime() - new Date(b.inDate).getTime()), quantity);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Shared allocation logic — iterates sorted candidates and picks until quantity is met.
|
|
14
|
+
* Used by all strategies after sorting.
|
|
15
|
+
*/
|
|
16
|
+
function allocateFromSorted(sorted, quantity) {
|
|
17
|
+
const allocations = [];
|
|
18
|
+
let remaining = quantity;
|
|
19
|
+
for (const q of sorted) {
|
|
20
|
+
if (remaining <= 0) break;
|
|
21
|
+
const available = q.quantityOnHand - q.quantityReserved;
|
|
22
|
+
if (available <= 0) continue;
|
|
23
|
+
const take = Math.min(available, remaining);
|
|
24
|
+
allocations.push({
|
|
25
|
+
quantId: q._id,
|
|
26
|
+
locationId: q.locationId,
|
|
27
|
+
lotId: q.lotId,
|
|
28
|
+
quantity: take
|
|
29
|
+
});
|
|
30
|
+
remaining -= take;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
fulfilled: remaining <= 0,
|
|
34
|
+
allocations,
|
|
35
|
+
shortfall: Math.max(0, remaining)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/reservations/fefo.strategy.ts
|
|
40
|
+
/**
|
|
41
|
+
* FEFO allocation — consume nearest-expiry stock first.
|
|
42
|
+
* Requires quants to carry lot expiry info. Quants without expiry sort last.
|
|
43
|
+
*
|
|
44
|
+
* Used for perishable goods: food, pharma, cosmetics.
|
|
45
|
+
*/
|
|
46
|
+
var FefoStrategy = class {
|
|
47
|
+
name = "fefo";
|
|
48
|
+
constructor(minShelfLifeDays) {
|
|
49
|
+
this.minShelfLifeDays = minShelfLifeDays;
|
|
50
|
+
}
|
|
51
|
+
resolve(_skuRef, quantity, candidates) {
|
|
52
|
+
let eligible = candidates;
|
|
53
|
+
if (this.minShelfLifeDays != null) {
|
|
54
|
+
const threshold = new Date(Date.now() + this.minShelfLifeDays * 864e5);
|
|
55
|
+
eligible = candidates.filter((q) => !q.lotExpiresAt || new Date(q.lotExpiresAt).getTime() > threshold.getTime());
|
|
56
|
+
}
|
|
57
|
+
return allocateFromSorted([...eligible].sort((a, b) => {
|
|
58
|
+
return (a.lotExpiresAt ? new Date(a.lotExpiresAt).getTime() : Number.POSITIVE_INFINITY) - (b.lotExpiresAt ? new Date(b.lotExpiresAt).getTime() : Number.POSITIVE_INFINITY);
|
|
59
|
+
}), quantity);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/reservations/lifo.strategy.ts
|
|
64
|
+
/**
|
|
65
|
+
* LIFO allocation — consume newest stock first (by inDate DESC).
|
|
66
|
+
* Useful when freshness matters less and you want to keep oldest stock for auditing.
|
|
67
|
+
*/
|
|
68
|
+
var LifoStrategy = class {
|
|
69
|
+
name = "lifo";
|
|
70
|
+
resolve(_skuRef, quantity, candidates) {
|
|
71
|
+
return allocateFromSorted([...candidates].sort((a, b) => new Date(b.inDate).getTime() - new Date(a.inDate).getTime()), quantity);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/reservations/exact-lot.strategy.ts
|
|
76
|
+
/**
|
|
77
|
+
* Exact-lot allocation — only allocate from a specific lot.
|
|
78
|
+
* Used when a customer or regulation requires a specific batch.
|
|
79
|
+
*/
|
|
80
|
+
var ExactLotStrategy = class {
|
|
81
|
+
name = "exact_lot";
|
|
82
|
+
constructor(targetLotId) {
|
|
83
|
+
this.targetLotId = targetLotId;
|
|
84
|
+
}
|
|
85
|
+
resolve(_skuRef, quantity, candidates) {
|
|
86
|
+
const filtered = candidates.filter((q) => q.lotId === this.targetLotId);
|
|
87
|
+
if (filtered.length === 0) return {
|
|
88
|
+
fulfilled: false,
|
|
89
|
+
allocations: [],
|
|
90
|
+
shortfall: quantity
|
|
91
|
+
};
|
|
92
|
+
return allocateFromSorted([...filtered].sort((a, b) => new Date(a.inDate).getTime() - new Date(b.inDate).getTime()), quantity);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/reservations/nearest.strategy.ts
|
|
97
|
+
/**
|
|
98
|
+
* Nearest allocation — consume stock closest to pick face (by location sortOrder ASC).
|
|
99
|
+
* Minimizes warehouse travel distance.
|
|
100
|
+
*
|
|
101
|
+
* Requires quants to carry locationSortOrder (set by repository join or denormalized).
|
|
102
|
+
*/
|
|
103
|
+
var NearestStrategy = class {
|
|
104
|
+
name = "nearest";
|
|
105
|
+
resolve(_skuRef, quantity, candidates) {
|
|
106
|
+
return allocateFromSorted([...candidates].sort((a, b) => {
|
|
107
|
+
return (a.locationSortOrder ?? Number.MAX_SAFE_INTEGER) - (b.locationSortOrder ?? Number.MAX_SAFE_INTEGER);
|
|
108
|
+
}), quantity);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
export { FifoStrategy as a, FefoStrategy as i, ExactLotStrategy as n, allocateFromSorted as o, LifoStrategy as r, NearestStrategy as t };
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { n as MovePort, r as StockMove } from "../move.port-Qg1CYp7h.mjs";
|
|
2
|
+
import { t as FlowContext } from "../index-DFF0GJ4J.mjs";
|
|
3
|
+
import { n as Location, t as LocationPort } from "../location.port-CValXIpb.mjs";
|
|
4
|
+
import { n as StockQuant } from "../stock-quant-CZhgvTu7.mjs";
|
|
5
|
+
import { r as QuantPort } from "../quant.port-BBa66PBT.mjs";
|
|
6
|
+
import { n as AllocationResult } from "../allocation-policy-my_HfzdV.mjs";
|
|
7
|
+
import { n as PutawayPolicy, t as RemovalPolicy } from "../removal-policy-BItBB8FD.mjs";
|
|
8
|
+
|
|
9
|
+
//#region src/routing/cross-dock.engine.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* Per-destination cross-dock assignment.
|
|
12
|
+
* Each assignment maps exact quantities to exact shipping destinations.
|
|
13
|
+
*/
|
|
14
|
+
interface CrossDockAssignment {
|
|
15
|
+
destinationLocationId: string;
|
|
16
|
+
quantity: number;
|
|
17
|
+
moveIds: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Cross-dock evaluation result with multi-destination routing.
|
|
21
|
+
*/
|
|
22
|
+
interface CrossDockResult {
|
|
23
|
+
hasDemand: boolean;
|
|
24
|
+
demandQuantity: number;
|
|
25
|
+
crossDockQuantity: number;
|
|
26
|
+
remainingToStorage: number;
|
|
27
|
+
/** Per-destination assignments (replaces single crossDockDestination) */
|
|
28
|
+
assignments: CrossDockAssignment[];
|
|
29
|
+
/** @deprecated Use assignments[0].destinationLocationId — kept for backward compat */
|
|
30
|
+
crossDockDestination: string | null;
|
|
31
|
+
waitingMoveIds: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Cross-Dock Engine
|
|
35
|
+
*
|
|
36
|
+
* Evaluates whether incoming stock should bypass storage and route directly
|
|
37
|
+
* to shipping destinations. Supports multi-destination routing:
|
|
38
|
+
*
|
|
39
|
+
* Normal: Receive → Storage → Pick → Pack → Ship
|
|
40
|
+
* Cross-dock: Receive → Ship-Dock-1 (15 units for Order A)
|
|
41
|
+
* → Ship-Dock-2 (10 units for Order B)
|
|
42
|
+
* → Storage (remaining 5 units)
|
|
43
|
+
*/
|
|
44
|
+
declare class CrossDockEngine {
|
|
45
|
+
private movePort;
|
|
46
|
+
constructor(movePort: MovePort, _quantPort: QuantPort);
|
|
47
|
+
evaluate(skuRef: string, incomingQuantity: number, _nodeId: string, ctx: FlowContext): Promise<CrossDockResult>;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/routing/putaway.engine.d.ts
|
|
51
|
+
/**
|
|
52
|
+
* Putaway engine — resolves where received goods should be stored.
|
|
53
|
+
* Tries registered strategies in priority order, then falls back to default.
|
|
54
|
+
*/
|
|
55
|
+
declare class PutawayEngine {
|
|
56
|
+
private defaultLocationResolver;
|
|
57
|
+
private strategies;
|
|
58
|
+
constructor(_locationPort: LocationPort, defaultLocationResolver: (nodeId: string, ctx: FlowContext) => Promise<string | null>);
|
|
59
|
+
registerStrategy(strategy: PutawayPolicy): void;
|
|
60
|
+
resolve(skuRef: string, quantity: number, nodeId: string, context: {
|
|
61
|
+
lotId?: string;
|
|
62
|
+
vendorRef?: string;
|
|
63
|
+
skuCategory?: string;
|
|
64
|
+
}, ctx: FlowContext): Promise<{
|
|
65
|
+
locationId: string;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/routing/removal.engine.d.ts
|
|
70
|
+
/**
|
|
71
|
+
* Removal engine — delegates stock removal decisions to a RemovalPolicy.
|
|
72
|
+
*/
|
|
73
|
+
declare class RemovalEngine {
|
|
74
|
+
private defaultPolicy;
|
|
75
|
+
constructor(defaultPolicy: RemovalPolicy);
|
|
76
|
+
resolve(skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/routing/route-expander.d.ts
|
|
80
|
+
interface RouteStep {
|
|
81
|
+
sourceLocationType: string;
|
|
82
|
+
destinationLocationType: string;
|
|
83
|
+
operationType: string;
|
|
84
|
+
}
|
|
85
|
+
interface RouteTemplate {
|
|
86
|
+
name: string;
|
|
87
|
+
steps: RouteStep[];
|
|
88
|
+
}
|
|
89
|
+
interface RouteConfig {
|
|
90
|
+
receptionSteps: 1 | 2 | 3;
|
|
91
|
+
deliverySteps: 1 | 2 | 3;
|
|
92
|
+
customRoutes?: RouteTemplate[];
|
|
93
|
+
}
|
|
94
|
+
interface ExpandedRoute {
|
|
95
|
+
steps: Array<{
|
|
96
|
+
sourceLocationId: string;
|
|
97
|
+
destinationLocationId: string;
|
|
98
|
+
operationType: string;
|
|
99
|
+
sequence: number;
|
|
100
|
+
}>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Interface for resolving location types to actual location IDs.
|
|
104
|
+
* Consumer provides this — typically queries Location by (nodeId, type).
|
|
105
|
+
*/
|
|
106
|
+
interface LocationResolver {
|
|
107
|
+
resolveByType(nodeId: string, locationType: string): Promise<string>;
|
|
108
|
+
}
|
|
109
|
+
declare class RouteExpander {
|
|
110
|
+
private config;
|
|
111
|
+
private customRoutes;
|
|
112
|
+
constructor(config?: RouteConfig);
|
|
113
|
+
/**
|
|
114
|
+
* Register a custom route template.
|
|
115
|
+
*/
|
|
116
|
+
registerRoute(route: RouteTemplate): void;
|
|
117
|
+
/**
|
|
118
|
+
* Expand a high-level operation into planned move steps.
|
|
119
|
+
* Resolves location types to actual location IDs using the LocationResolver.
|
|
120
|
+
*
|
|
121
|
+
* Route matching priority (6-level resolution):
|
|
122
|
+
* 1. Demand-specific routes (options.routeId) — highest
|
|
123
|
+
* 2. Packaging/UoM routes
|
|
124
|
+
* 3. SKU-specific routes
|
|
125
|
+
* 4. SKU category routes
|
|
126
|
+
* 5. Node/warehouse routes
|
|
127
|
+
* 6. Operation type default route — lowest
|
|
128
|
+
*/
|
|
129
|
+
expand(operationType: 'receipt' | 'shipment' | 'transfer', nodeId: string, locations: LocationResolver, options?: {
|
|
130
|
+
skuRef?: string;
|
|
131
|
+
routeId?: string;
|
|
132
|
+
}): Promise<ExpandedRoute>;
|
|
133
|
+
/**
|
|
134
|
+
* Get the default route template based on operation type and configured step count.
|
|
135
|
+
*
|
|
136
|
+
* Complexity modes:
|
|
137
|
+
* - One-step: ship (small shop)
|
|
138
|
+
* - Two-step: pick → ship (standard warehouse)
|
|
139
|
+
* - Three-step: pick → pack → ship (e-commerce fulfillment)
|
|
140
|
+
*
|
|
141
|
+
* Cross-dock (transfer): receive → ship (bypass storage)
|
|
142
|
+
*/
|
|
143
|
+
getDefaultTemplate(operationType: 'receipt' | 'shipment' | 'transfer'): RouteStep[];
|
|
144
|
+
/**
|
|
145
|
+
* Auto-generated reception routes per warehouse:
|
|
146
|
+
* one_step: [vendor → stock]
|
|
147
|
+
* two_steps: [vendor → input, input → stock]
|
|
148
|
+
* three_steps: [vendor → input, input → qc, qc → stock]
|
|
149
|
+
*/
|
|
150
|
+
private getReceptionTemplate;
|
|
151
|
+
/**
|
|
152
|
+
* Auto-generated delivery routes per warehouse:
|
|
153
|
+
* one_step: [stock → customer]
|
|
154
|
+
* two_steps: [stock → shipping, shipping → customer]
|
|
155
|
+
* three_steps: [stock → packing, packing → shipping, shipping → customer]
|
|
156
|
+
*/
|
|
157
|
+
private getDeliveryTemplate;
|
|
158
|
+
/**
|
|
159
|
+
* Resolve location type references to actual location IDs.
|
|
160
|
+
*/
|
|
161
|
+
private resolveSteps;
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/routing/strategies/abc-velocity.strategy.d.ts
|
|
165
|
+
type VelocityClass = 'A' | 'B' | 'C';
|
|
166
|
+
interface AbcZoneMapping {
|
|
167
|
+
A: string;
|
|
168
|
+
B: string;
|
|
169
|
+
C: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* ABC-velocity putaway strategy.
|
|
173
|
+
* Classifies SKUs by velocity (A/B/C) and routes to appropriate zones.
|
|
174
|
+
* A-items go near dock (low sortOrder), C-items go far back.
|
|
175
|
+
*/
|
|
176
|
+
declare class AbcVelocityStrategy implements PutawayPolicy {
|
|
177
|
+
private velocityClassifier;
|
|
178
|
+
private zoneMappings;
|
|
179
|
+
readonly name = "abc_velocity";
|
|
180
|
+
constructor(velocityClassifier: (skuRef: string) => VelocityClass, zoneMappings: AbcZoneMapping);
|
|
181
|
+
resolve(skuRef: string, _quantity: number, _nodeId: string, _context: {
|
|
182
|
+
lotId?: string;
|
|
183
|
+
vendorRef?: string;
|
|
184
|
+
skuCategory?: string;
|
|
185
|
+
}): Promise<{
|
|
186
|
+
locationId: string;
|
|
187
|
+
} | null>;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/routing/strategies/category-zone.strategy.d.ts
|
|
191
|
+
/**
|
|
192
|
+
* Category-zone putaway strategy.
|
|
193
|
+
* Routes goods to zones based on their SKU category.
|
|
194
|
+
*/
|
|
195
|
+
declare class CategoryZoneStrategy implements PutawayPolicy {
|
|
196
|
+
private categoryZoneMap;
|
|
197
|
+
readonly name = "category_zone";
|
|
198
|
+
constructor(categoryZoneMap: Map<string, string>);
|
|
199
|
+
resolve(_skuRef: string, _quantity: number, _nodeId: string, context: {
|
|
200
|
+
lotId?: string;
|
|
201
|
+
vendorRef?: string;
|
|
202
|
+
skuCategory?: string;
|
|
203
|
+
}): Promise<{
|
|
204
|
+
locationId: string;
|
|
205
|
+
} | null>;
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/routing/strategies/closest-to-pick.strategy.d.ts
|
|
209
|
+
/**
|
|
210
|
+
* Closest-to-pick putaway strategy.
|
|
211
|
+
* Selects the storage location with the lowest sortOrder (closest to pick face)
|
|
212
|
+
* that has remaining capacity.
|
|
213
|
+
*/
|
|
214
|
+
declare class ClosestToPickStrategy implements PutawayPolicy {
|
|
215
|
+
private locationPort;
|
|
216
|
+
private ctx;
|
|
217
|
+
readonly name = "closest_to_pick";
|
|
218
|
+
constructor(locationPort: LocationPort, ctx: FlowContext);
|
|
219
|
+
resolve(_skuRef: string, quantity: number, nodeId: string, _context: {
|
|
220
|
+
lotId?: string;
|
|
221
|
+
vendorRef?: string;
|
|
222
|
+
skuCategory?: string;
|
|
223
|
+
}): Promise<{
|
|
224
|
+
locationId: string;
|
|
225
|
+
} | null>;
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/routing/strategies/empty-bin.strategy.d.ts
|
|
229
|
+
/**
|
|
230
|
+
* Empty-bin putaway strategy.
|
|
231
|
+
* Finds the first storage location in the node that has zero quants.
|
|
232
|
+
*/
|
|
233
|
+
declare class EmptyBinStrategy implements PutawayPolicy {
|
|
234
|
+
private locationPort;
|
|
235
|
+
private quantPort;
|
|
236
|
+
private ctx;
|
|
237
|
+
readonly name = "empty_bin";
|
|
238
|
+
constructor(locationPort: LocationPort, quantPort: QuantPort, ctx: FlowContext);
|
|
239
|
+
resolve(_skuRef: string, _quantity: number, nodeId: string, _context: {
|
|
240
|
+
lotId?: string;
|
|
241
|
+
vendorRef?: string;
|
|
242
|
+
skuCategory?: string;
|
|
243
|
+
}): Promise<{
|
|
244
|
+
locationId: string;
|
|
245
|
+
} | null>;
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/routing/strategies/fixed-location.strategy.d.ts
|
|
249
|
+
/**
|
|
250
|
+
* Fixed-location putaway strategy.
|
|
251
|
+
* Maps specific SKUs to predetermined storage locations.
|
|
252
|
+
*/
|
|
253
|
+
declare class FixedLocationStrategy implements PutawayPolicy {
|
|
254
|
+
private skuLocationMap;
|
|
255
|
+
readonly name = "fixed_location";
|
|
256
|
+
constructor(skuLocationMap: Map<string, string>);
|
|
257
|
+
resolve(skuRef: string, _quantity: number, _nodeId: string, _context: {
|
|
258
|
+
lotId?: string;
|
|
259
|
+
vendorRef?: string;
|
|
260
|
+
skuCategory?: string;
|
|
261
|
+
}): Promise<{
|
|
262
|
+
locationId: string;
|
|
263
|
+
} | null>;
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/routing/wave.engine.d.ts
|
|
267
|
+
interface ConsolidatedPick {
|
|
268
|
+
skuRef: string;
|
|
269
|
+
locationId: string;
|
|
270
|
+
totalQuantity: number;
|
|
271
|
+
moveIds: string[];
|
|
272
|
+
sortKey: string;
|
|
273
|
+
}
|
|
274
|
+
interface WaveSummary {
|
|
275
|
+
totalMoves: number;
|
|
276
|
+
totalQuantity: number;
|
|
277
|
+
uniqueLocations: number;
|
|
278
|
+
uniqueSkus: number;
|
|
279
|
+
}
|
|
280
|
+
/** Cart constraint for sub-wave batching */
|
|
281
|
+
interface CartConstraints {
|
|
282
|
+
maxWeight?: number;
|
|
283
|
+
maxVolume?: number;
|
|
284
|
+
maxItems?: number;
|
|
285
|
+
}
|
|
286
|
+
/** A single cart-load (sub-wave) */
|
|
287
|
+
interface CartBatch {
|
|
288
|
+
picks: ConsolidatedPick[];
|
|
289
|
+
totalWeight: number;
|
|
290
|
+
totalVolume: number;
|
|
291
|
+
totalItems: number;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Wave Engine — production-grade pick-path optimization.
|
|
295
|
+
*
|
|
296
|
+
* Two sorting modes:
|
|
297
|
+
* 1. **sortOrder** (simple) — single integer, works for small warehouses
|
|
298
|
+
* 2. **coordinates** (3D) — zone/aisle/bay/level/bin tuple, serpentine path for large fulfillment centers
|
|
299
|
+
*
|
|
300
|
+
* Also supports:
|
|
301
|
+
* - Location consolidation (merge picks for same SKU at same bin)
|
|
302
|
+
* - Weight/volume/item batching (split into cart-sized sub-waves with pick splitting)
|
|
303
|
+
* - Wave summary for display
|
|
304
|
+
*/
|
|
305
|
+
declare class WaveEngine {
|
|
306
|
+
/**
|
|
307
|
+
* Sort moves by location for optimal pick path.
|
|
308
|
+
* Uses 3D coordinates if available, falls back to sortOrder.
|
|
309
|
+
* Does not mutate input.
|
|
310
|
+
*/
|
|
311
|
+
optimizePickPath(moves: StockMove[], locationMap?: Map<string, Location>): StockMove[];
|
|
312
|
+
/**
|
|
313
|
+
* Consolidate moves for same SKU at same location into single pick lines.
|
|
314
|
+
*/
|
|
315
|
+
consolidateByLocation(moves: StockMove[], locationMap?: Map<string, Location>): ConsolidatedPick[];
|
|
316
|
+
/**
|
|
317
|
+
* Split a consolidated pick list into cart-sized batches.
|
|
318
|
+
* Each batch respects weight, volume, and item count constraints.
|
|
319
|
+
* Oversized picks are split across multiple carts — no quantity is lost.
|
|
320
|
+
*/
|
|
321
|
+
batchIntoCarts(picks: ConsolidatedPick[], constraints: CartConstraints, skuWeights?: Map<string, number>, skuVolumes?: Map<string, number>): CartBatch[];
|
|
322
|
+
/**
|
|
323
|
+
* Create a summary of the wave.
|
|
324
|
+
*/
|
|
325
|
+
createWaveSummary(moves: StockMove[]): WaveSummary;
|
|
326
|
+
/**
|
|
327
|
+
* Build a lexicographic sort key from location coordinates or metadata.
|
|
328
|
+
*
|
|
329
|
+
* Uses boustrophedon (serpentine/ox-plow) traversal for 3D coordinates:
|
|
330
|
+
* - Odd aisles: bays ascending (1→2→3→…)
|
|
331
|
+
* - Even aisles: bays descending (…→3→2→1)
|
|
332
|
+
* This eliminates dead-walking back to the front of each aisle.
|
|
333
|
+
*/
|
|
334
|
+
private buildSortKey;
|
|
335
|
+
private buildCoordinateSortKey;
|
|
336
|
+
private buildMetadataSortKey;
|
|
337
|
+
private padSortOrder;
|
|
338
|
+
private createEmptyBatch;
|
|
339
|
+
private addPickToBatch;
|
|
340
|
+
private wouldExceedConstraints;
|
|
341
|
+
/**
|
|
342
|
+
* Flush the current batch, optionally fitting a partial quantity first.
|
|
343
|
+
* Item-count overflow skips partial fitting (the constraint is on lines, not units).
|
|
344
|
+
*/
|
|
345
|
+
private flushAndFillPartial;
|
|
346
|
+
/**
|
|
347
|
+
* Recalculate remaining quantity for a pick after batches have been flushed.
|
|
348
|
+
*/
|
|
349
|
+
private recalculateRemaining;
|
|
350
|
+
/**
|
|
351
|
+
* Calculate how many units fit in the cart. Returns `wanted` if no
|
|
352
|
+
* weight/volume constraints apply (item-count-only or unconstrained).
|
|
353
|
+
*/
|
|
354
|
+
private calculateFittingQuantity;
|
|
355
|
+
/**
|
|
356
|
+
* Maximum units that fit in the remaining cart capacity by weight/volume.
|
|
357
|
+
* Returns 0 if even 1 unit won't fit.
|
|
358
|
+
*/
|
|
359
|
+
private maxFittingUnits;
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
export { AbcVelocityStrategy, type AbcZoneMapping, CategoryZoneStrategy, ClosestToPickStrategy, type ConsolidatedPick, type CrossDockAssignment, CrossDockEngine, type CrossDockResult, EmptyBinStrategy, type ExpandedRoute, FixedLocationStrategy, type LocationResolver, PutawayEngine, RemovalEngine, type RouteConfig, RouteExpander, type RouteStep, type RouteTemplate, type VelocityClass, WaveEngine, type WaveSummary };
|