@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,82 @@
|
|
|
1
|
+
//#region src/domain/enums/location-type.ts
|
|
2
|
+
const LocationType = {
|
|
3
|
+
view: "view",
|
|
4
|
+
internal: "internal",
|
|
5
|
+
receiving: "receiving",
|
|
6
|
+
storage: "storage",
|
|
7
|
+
picking: "picking",
|
|
8
|
+
packing: "packing",
|
|
9
|
+
shipping: "shipping",
|
|
10
|
+
transit: "transit",
|
|
11
|
+
vendor: "vendor",
|
|
12
|
+
customer: "customer",
|
|
13
|
+
returns: "returns",
|
|
14
|
+
quality_hold: "quality_hold",
|
|
15
|
+
damaged: "damaged",
|
|
16
|
+
scrap: "scrap",
|
|
17
|
+
inventory_loss: "inventory_loss",
|
|
18
|
+
production: "production"
|
|
19
|
+
};
|
|
20
|
+
/** Location types that hold physical stock (non-virtual). */
|
|
21
|
+
const STOCKABLE_LOCATION_TYPES = [
|
|
22
|
+
LocationType.internal,
|
|
23
|
+
LocationType.receiving,
|
|
24
|
+
LocationType.storage,
|
|
25
|
+
LocationType.picking,
|
|
26
|
+
LocationType.packing,
|
|
27
|
+
LocationType.shipping,
|
|
28
|
+
LocationType.transit,
|
|
29
|
+
LocationType.returns,
|
|
30
|
+
LocationType.quality_hold,
|
|
31
|
+
LocationType.damaged,
|
|
32
|
+
LocationType.production
|
|
33
|
+
];
|
|
34
|
+
/** Virtual location types — no physical stock, used for accounting/routing. */
|
|
35
|
+
const VIRTUAL_LOCATION_TYPES = [
|
|
36
|
+
LocationType.view,
|
|
37
|
+
LocationType.vendor,
|
|
38
|
+
LocationType.customer,
|
|
39
|
+
LocationType.scrap,
|
|
40
|
+
LocationType.inventory_loss
|
|
41
|
+
];
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/domain/enums/operation-type.ts
|
|
44
|
+
const OperationType = {
|
|
45
|
+
receipt: "receipt",
|
|
46
|
+
transfer: "transfer",
|
|
47
|
+
shipment: "shipment",
|
|
48
|
+
return: "return",
|
|
49
|
+
adjustment: "adjustment",
|
|
50
|
+
count: "count"
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/domain/enums/stock-status.ts
|
|
54
|
+
const StockStatus = {
|
|
55
|
+
sellable: "sellable",
|
|
56
|
+
damaged: "damaged",
|
|
57
|
+
quarantine: "quarantine",
|
|
58
|
+
hold: "hold",
|
|
59
|
+
returns: "returns",
|
|
60
|
+
expired: "expired",
|
|
61
|
+
in_transit: "in_transit"
|
|
62
|
+
};
|
|
63
|
+
/** Only sellable stock is counted as available for sale. */
|
|
64
|
+
const SELLABLE_STATUSES = [StockStatus.sellable];
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/domain/enums/tracking-mode.ts
|
|
67
|
+
const TrackingMode = {
|
|
68
|
+
none: "none",
|
|
69
|
+
lot: "lot",
|
|
70
|
+
serial: "serial"
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/domain/enums/valuation-method.ts
|
|
74
|
+
const ValuationMethod = {
|
|
75
|
+
wac: "wac",
|
|
76
|
+
fifo: "fifo",
|
|
77
|
+
fefo: "fefo",
|
|
78
|
+
specific: "specific",
|
|
79
|
+
standard: "standard"
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { OperationType as a, VIRTUAL_LOCATION_TYPES as c, StockStatus as i, TrackingMode as n, LocationType as o, SELLABLE_STATUSES as r, STOCKABLE_LOCATION_TYPES as s, ValuationMethod as t };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/events/event-bus.ts
|
|
2
|
+
/**
|
|
3
|
+
* In-process event bus.
|
|
4
|
+
*
|
|
5
|
+
* Serves as the default {@link EventEmitterPort} implementation.
|
|
6
|
+
* Handlers are invoked fire-and-forget; failures are logged but never
|
|
7
|
+
* propagate to the emitter. Replace with a Redis/Kafka adapter for
|
|
8
|
+
* distributed workloads.
|
|
9
|
+
*/
|
|
10
|
+
var InProcessEventBus = class {
|
|
11
|
+
handlers = /* @__PURE__ */ new Map();
|
|
12
|
+
async emit(event, data, _session) {
|
|
13
|
+
const subscribers = this.handlers.get(event);
|
|
14
|
+
if (!subscribers || subscribers.size === 0) return;
|
|
15
|
+
const tasks = [...subscribers].map((handler) => handler(data).catch((err) => {
|
|
16
|
+
console.error(`[FlowEventBus] Handler error on "${event}":`, err);
|
|
17
|
+
}));
|
|
18
|
+
await Promise.all(tasks);
|
|
19
|
+
}
|
|
20
|
+
on(event, handler) {
|
|
21
|
+
let set = this.handlers.get(event);
|
|
22
|
+
if (!set) {
|
|
23
|
+
set = /* @__PURE__ */ new Set();
|
|
24
|
+
this.handlers.set(event, set);
|
|
25
|
+
}
|
|
26
|
+
set.add(handler);
|
|
27
|
+
}
|
|
28
|
+
off(event, handler) {
|
|
29
|
+
const set = this.handlers.get(event);
|
|
30
|
+
if (set) {
|
|
31
|
+
set.delete(handler);
|
|
32
|
+
if (set.size === 0) this.handlers.delete(event);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { InProcessEventBus as t };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
2
|
+
import { t as EventEmitterPort } from "./event-emitter.port-BFh2pasY.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/events/event-bus.d.ts
|
|
5
|
+
type EventHandler = (data: Record<string, unknown>) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* In-process event bus.
|
|
8
|
+
*
|
|
9
|
+
* Serves as the default {@link EventEmitterPort} implementation.
|
|
10
|
+
* Handlers are invoked fire-and-forget; failures are logged but never
|
|
11
|
+
* propagate to the emitter. Replace with a Redis/Kafka adapter for
|
|
12
|
+
* distributed workloads.
|
|
13
|
+
*/
|
|
14
|
+
declare class InProcessEventBus implements EventEmitterPort {
|
|
15
|
+
private handlers;
|
|
16
|
+
emit(event: string, data: Record<string, unknown>, _session?: TransactionSession): Promise<void>;
|
|
17
|
+
on(event: string, handler: EventHandler): void;
|
|
18
|
+
off(event: string, handler: EventHandler): void;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { InProcessEventBus as t };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/events/event-types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Typed event catalog for the @classytic/flow inventory package.
|
|
6
|
+
*
|
|
7
|
+
* All domain event names are defined here as a single `as const` object so
|
|
8
|
+
* consumers get full autocompletion and compile-time safety.
|
|
9
|
+
*/
|
|
10
|
+
declare const FlowEvents: {
|
|
11
|
+
readonly MOVE_GROUP_CREATED: "inventory.move_group.created";
|
|
12
|
+
readonly MOVE_GROUP_CONFIRMED: "inventory.move_group.confirmed";
|
|
13
|
+
readonly MOVE_GROUP_CANCELLED: "inventory.move_group.cancelled";
|
|
14
|
+
readonly MOVE_PLANNED: "inventory.move.planned";
|
|
15
|
+
readonly MOVE_RESERVED: "inventory.move.reserved";
|
|
16
|
+
readonly MOVE_DONE: "inventory.move.done";
|
|
17
|
+
readonly RESERVATION_CREATED: "inventory.reservation.created";
|
|
18
|
+
readonly RESERVATION_RELEASED: "inventory.reservation.released";
|
|
19
|
+
readonly RESERVATION_CONSUMED: "inventory.reservation.consumed";
|
|
20
|
+
readonly TRANSFER_DISPATCHED: "inventory.transfer.dispatched";
|
|
21
|
+
readonly TRANSFER_RECEIVED: "inventory.transfer.received";
|
|
22
|
+
readonly PROCUREMENT_RECEIVED: "inventory.procurement.received";
|
|
23
|
+
readonly COUNT_STARTED: "inventory.count.started";
|
|
24
|
+
readonly COUNT_RECONCILED: "inventory.count.reconciled";
|
|
25
|
+
readonly REPLENISHMENT_TRIGGERED: "inventory.replenishment.triggered";
|
|
26
|
+
readonly STOCK_LOW: "inventory.stock.low";
|
|
27
|
+
readonly ADJUSTMENT_POSTED: "inventory.adjustment.posted";
|
|
28
|
+
readonly LANDED_COST_APPLIED: "inventory.landed_cost.applied";
|
|
29
|
+
};
|
|
30
|
+
/** Union of all flow event names. */
|
|
31
|
+
type FlowEventName = (typeof FlowEvents)[keyof typeof FlowEvents];
|
|
32
|
+
/** Base payload — every event carries organizationId for tenant routing. */
|
|
33
|
+
interface BasePayload {
|
|
34
|
+
organizationId: string;
|
|
35
|
+
}
|
|
36
|
+
interface MoveGroupCreatedPayload extends BasePayload {
|
|
37
|
+
groupId: string;
|
|
38
|
+
groupType: string;
|
|
39
|
+
documentNumber: string;
|
|
40
|
+
procurementOrderId?: string;
|
|
41
|
+
}
|
|
42
|
+
interface MoveGroupConfirmedPayload extends BasePayload {
|
|
43
|
+
groupId: string;
|
|
44
|
+
documentNumber: string;
|
|
45
|
+
}
|
|
46
|
+
interface MoveGroupCancelledPayload extends BasePayload {
|
|
47
|
+
groupId: string;
|
|
48
|
+
documentNumber: string;
|
|
49
|
+
}
|
|
50
|
+
interface MovePlannedPayload extends BasePayload {
|
|
51
|
+
moveId: string;
|
|
52
|
+
moveGroupId: string;
|
|
53
|
+
skuRef: string;
|
|
54
|
+
operationType: string;
|
|
55
|
+
}
|
|
56
|
+
interface MoveDonePayload extends BasePayload {
|
|
57
|
+
moveId: string;
|
|
58
|
+
moveGroupId: string;
|
|
59
|
+
skuRef: string;
|
|
60
|
+
operationType: string;
|
|
61
|
+
quantityDone: number;
|
|
62
|
+
sourceLocationId: string;
|
|
63
|
+
destinationLocationId: string;
|
|
64
|
+
}
|
|
65
|
+
interface ReservationCreatedPayload extends BasePayload {
|
|
66
|
+
reservationId: string;
|
|
67
|
+
skuRef: string;
|
|
68
|
+
locationId: string;
|
|
69
|
+
quantity: number;
|
|
70
|
+
ownerType: string;
|
|
71
|
+
ownerId: string;
|
|
72
|
+
}
|
|
73
|
+
interface ReservationReleasedPayload extends BasePayload {
|
|
74
|
+
reservationId: string;
|
|
75
|
+
skuRef: string;
|
|
76
|
+
locationId: string;
|
|
77
|
+
quantity: number;
|
|
78
|
+
}
|
|
79
|
+
interface ReservationConsumedPayload extends BasePayload {
|
|
80
|
+
reservationId: string;
|
|
81
|
+
skuRef: string;
|
|
82
|
+
locationId: string;
|
|
83
|
+
quantityConsumed: number;
|
|
84
|
+
totalConsumed: number;
|
|
85
|
+
isFullyConsumed: boolean;
|
|
86
|
+
}
|
|
87
|
+
interface TransferDispatchedPayload extends BasePayload {
|
|
88
|
+
groupId: string;
|
|
89
|
+
documentNumber: string;
|
|
90
|
+
}
|
|
91
|
+
interface TransferReceivedPayload extends BasePayload {
|
|
92
|
+
groupId: string;
|
|
93
|
+
documentNumber: string;
|
|
94
|
+
partial?: boolean;
|
|
95
|
+
failedMoves?: Array<{
|
|
96
|
+
moveId: string;
|
|
97
|
+
error: string;
|
|
98
|
+
}>;
|
|
99
|
+
}
|
|
100
|
+
interface ProcurementReceivedPayload extends BasePayload {
|
|
101
|
+
orderId: string;
|
|
102
|
+
orderNumber: string;
|
|
103
|
+
vendorRef: string;
|
|
104
|
+
destinationNodeId: string;
|
|
105
|
+
itemCount: number;
|
|
106
|
+
}
|
|
107
|
+
interface CountStartedPayload extends BasePayload {
|
|
108
|
+
countId: string;
|
|
109
|
+
countNumber: string;
|
|
110
|
+
countType: string;
|
|
111
|
+
lineCount: number;
|
|
112
|
+
}
|
|
113
|
+
interface CountReconciledPayload extends BasePayload {
|
|
114
|
+
countId: string;
|
|
115
|
+
totalLines: number;
|
|
116
|
+
varianceLines: number;
|
|
117
|
+
autoApproved: boolean;
|
|
118
|
+
needsManualReview: boolean;
|
|
119
|
+
moveGroupId: string;
|
|
120
|
+
documentNumber: string;
|
|
121
|
+
}
|
|
122
|
+
interface ReplenishmentTriggeredPayload extends BasePayload {
|
|
123
|
+
ruleId: string;
|
|
124
|
+
skuRef: string;
|
|
125
|
+
currentLevel: number;
|
|
126
|
+
reorderPoint: number;
|
|
127
|
+
}
|
|
128
|
+
interface StockLowPayload extends BasePayload {
|
|
129
|
+
skuRef: string;
|
|
130
|
+
locationId: string;
|
|
131
|
+
currentLevel: number;
|
|
132
|
+
threshold: number;
|
|
133
|
+
}
|
|
134
|
+
interface AdjustmentPostedPayload extends BasePayload {
|
|
135
|
+
countId: string;
|
|
136
|
+
countNumber: string;
|
|
137
|
+
movesPosted: number;
|
|
138
|
+
}
|
|
139
|
+
interface LandedCostAppliedPayload extends BasePayload {
|
|
140
|
+
skuRef: string;
|
|
141
|
+
additionalCost: number;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Maps event names to their typed payloads.
|
|
145
|
+
* Use with `emit<E extends keyof FlowEventMap>(event: E, data: FlowEventMap[E])`.
|
|
146
|
+
*/
|
|
147
|
+
interface FlowEventMap {
|
|
148
|
+
[FlowEvents.MOVE_GROUP_CREATED]: MoveGroupCreatedPayload;
|
|
149
|
+
[FlowEvents.MOVE_GROUP_CONFIRMED]: MoveGroupConfirmedPayload;
|
|
150
|
+
[FlowEvents.MOVE_GROUP_CANCELLED]: MoveGroupCancelledPayload;
|
|
151
|
+
[FlowEvents.MOVE_PLANNED]: MovePlannedPayload;
|
|
152
|
+
[FlowEvents.MOVE_DONE]: MoveDonePayload;
|
|
153
|
+
[FlowEvents.RESERVATION_CREATED]: ReservationCreatedPayload;
|
|
154
|
+
[FlowEvents.RESERVATION_RELEASED]: ReservationReleasedPayload;
|
|
155
|
+
[FlowEvents.RESERVATION_CONSUMED]: ReservationConsumedPayload;
|
|
156
|
+
[FlowEvents.TRANSFER_DISPATCHED]: TransferDispatchedPayload;
|
|
157
|
+
[FlowEvents.TRANSFER_RECEIVED]: TransferReceivedPayload;
|
|
158
|
+
[FlowEvents.PROCUREMENT_RECEIVED]: ProcurementReceivedPayload;
|
|
159
|
+
[FlowEvents.COUNT_STARTED]: CountStartedPayload;
|
|
160
|
+
[FlowEvents.COUNT_RECONCILED]: CountReconciledPayload;
|
|
161
|
+
[FlowEvents.REPLENISHMENT_TRIGGERED]: ReplenishmentTriggeredPayload;
|
|
162
|
+
[FlowEvents.STOCK_LOW]: StockLowPayload;
|
|
163
|
+
[FlowEvents.ADJUSTMENT_POSTED]: AdjustmentPostedPayload;
|
|
164
|
+
[FlowEvents.LANDED_COST_APPLIED]: LandedCostAppliedPayload;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/domain/ports/event-emitter.port.d.ts
|
|
168
|
+
/**
|
|
169
|
+
* Domain event emitter port.
|
|
170
|
+
* In-process default; consumer provides adapters for Redis, Kafka, webhooks, etc.
|
|
171
|
+
*
|
|
172
|
+
* Typed overload ensures compile-time safety for known Flow events.
|
|
173
|
+
* The string fallback keeps the port extensible for consumer-defined events.
|
|
174
|
+
*/
|
|
175
|
+
interface EventEmitterPort {
|
|
176
|
+
emit<E extends keyof FlowEventMap>(event: E, data: FlowEventMap[E], session?: TransactionSession): Promise<void>;
|
|
177
|
+
emit(event: string, data: Record<string, unknown>, session?: TransactionSession): Promise<void>;
|
|
178
|
+
on<E extends keyof FlowEventMap>(event: E, handler: (data: FlowEventMap[E]) => Promise<void>): void;
|
|
179
|
+
on(event: string, handler: (data: Record<string, unknown>) => Promise<void>): void;
|
|
180
|
+
off(event: string, handler: (data: Record<string, unknown>) => Promise<void>): void;
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
export { FlowEvents as i, FlowEventMap as n, FlowEventName as r, EventEmitterPort as t };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/events/event-types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Typed event catalog for the @classytic/flow inventory package.
|
|
4
|
+
*
|
|
5
|
+
* All domain event names are defined here as a single `as const` object so
|
|
6
|
+
* consumers get full autocompletion and compile-time safety.
|
|
7
|
+
*/
|
|
8
|
+
const FlowEvents = {
|
|
9
|
+
MOVE_GROUP_CREATED: "inventory.move_group.created",
|
|
10
|
+
MOVE_GROUP_CONFIRMED: "inventory.move_group.confirmed",
|
|
11
|
+
MOVE_GROUP_CANCELLED: "inventory.move_group.cancelled",
|
|
12
|
+
MOVE_PLANNED: "inventory.move.planned",
|
|
13
|
+
MOVE_RESERVED: "inventory.move.reserved",
|
|
14
|
+
MOVE_DONE: "inventory.move.done",
|
|
15
|
+
RESERVATION_CREATED: "inventory.reservation.created",
|
|
16
|
+
RESERVATION_RELEASED: "inventory.reservation.released",
|
|
17
|
+
RESERVATION_CONSUMED: "inventory.reservation.consumed",
|
|
18
|
+
TRANSFER_DISPATCHED: "inventory.transfer.dispatched",
|
|
19
|
+
TRANSFER_RECEIVED: "inventory.transfer.received",
|
|
20
|
+
PROCUREMENT_RECEIVED: "inventory.procurement.received",
|
|
21
|
+
COUNT_STARTED: "inventory.count.started",
|
|
22
|
+
COUNT_RECONCILED: "inventory.count.reconciled",
|
|
23
|
+
REPLENISHMENT_TRIGGERED: "inventory.replenishment.triggered",
|
|
24
|
+
STOCK_LOW: "inventory.stock.low",
|
|
25
|
+
ADJUSTMENT_POSTED: "inventory.adjustment.posted",
|
|
26
|
+
LANDED_COST_APPLIED: "inventory.landed_cost.applied"
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { FlowEvents as t };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/domain/ports/idempotency.port.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Idempotency port — safe-retry protocol for Flow operations.
|
|
4
|
+
*
|
|
5
|
+
* Two tiers:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Legacy (required)**: `check()` + `save()` — simple check-then-save.
|
|
8
|
+
* Every adapter MUST implement these.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Atomic (optional)**: `claim()` + `complete()` + `release()` — prevents
|
|
11
|
+
* the TOCTOU race under concurrent retries. Adapters SHOULD implement these
|
|
12
|
+
* when the backing store supports atomic locks (Redis SET NX, Mongo upsert).
|
|
13
|
+
*
|
|
14
|
+
* Services prefer the atomic path when available, and fall back to legacy.
|
|
15
|
+
*/
|
|
16
|
+
interface IdempotencyPort {
|
|
17
|
+
/** Check if a result exists for the given key. */
|
|
18
|
+
check<T>(key: string): Promise<{
|
|
19
|
+
hit: true;
|
|
20
|
+
result: T;
|
|
21
|
+
} | {
|
|
22
|
+
hit: false;
|
|
23
|
+
}>;
|
|
24
|
+
/** Save a result for the given key. */
|
|
25
|
+
save<T>(key: string, result: T, ttlMs?: number): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Atomically claim a key. If the key already has a completed result,
|
|
28
|
+
* return it. If it's unclaimed, acquire the lock.
|
|
29
|
+
*
|
|
30
|
+
* @returns
|
|
31
|
+
* - `{ status: 'hit', result }` — a previous execution completed with this result
|
|
32
|
+
* - `{ status: 'acquired' }` — lock acquired, caller should execute then call complete()
|
|
33
|
+
* - `{ status: 'busy' }` — another caller holds the lock; retry later
|
|
34
|
+
*/
|
|
35
|
+
claim?<T>(key: string, ttlMs?: number): Promise<{
|
|
36
|
+
status: 'hit';
|
|
37
|
+
result: T;
|
|
38
|
+
} | {
|
|
39
|
+
status: 'acquired';
|
|
40
|
+
} | {
|
|
41
|
+
status: 'busy';
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Store the result for a previously claimed key.
|
|
45
|
+
* Only the caller that received `{ status: 'acquired' }` should call this.
|
|
46
|
+
*/
|
|
47
|
+
complete?<T>(key: string, result: T): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Release a claim without storing a result (e.g., on error).
|
|
50
|
+
* Allows a future retry to re-acquire the key.
|
|
51
|
+
*/
|
|
52
|
+
release?(key: string): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { IdempotencyPort as t };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { n as StockQuant } from "./stock-quant-CZhgvTu7.mjs";
|
|
2
|
+
import { n as AllocationResult, t as AllocationPolicy } from "./allocation-policy-my_HfzdV.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/reservations/exact-lot.strategy.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Exact-lot allocation — only allocate from a specific lot.
|
|
7
|
+
* Used when a customer or regulation requires a specific batch.
|
|
8
|
+
*/
|
|
9
|
+
declare class ExactLotStrategy implements AllocationPolicy {
|
|
10
|
+
private targetLotId;
|
|
11
|
+
readonly name = "exact_lot";
|
|
12
|
+
constructor(targetLotId: string);
|
|
13
|
+
resolve(_skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/reservations/fefo.strategy.d.ts
|
|
17
|
+
/**
|
|
18
|
+
* FEFO allocation — consume nearest-expiry stock first.
|
|
19
|
+
* Requires quants to carry lot expiry info. Quants without expiry sort last.
|
|
20
|
+
*
|
|
21
|
+
* Used for perishable goods: food, pharma, cosmetics.
|
|
22
|
+
*/
|
|
23
|
+
declare class FefoStrategy implements AllocationPolicy {
|
|
24
|
+
private minShelfLifeDays?;
|
|
25
|
+
readonly name = "fefo";
|
|
26
|
+
constructor(minShelfLifeDays?: number | undefined);
|
|
27
|
+
resolve(_skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/reservations/fifo.strategy.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* FIFO allocation — consume oldest stock first (by inDate).
|
|
33
|
+
* Default strategy for most operations.
|
|
34
|
+
*/
|
|
35
|
+
declare class FifoStrategy implements AllocationPolicy {
|
|
36
|
+
readonly name = "fifo";
|
|
37
|
+
resolve(_skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Shared allocation logic — iterates sorted candidates and picks until quantity is met.
|
|
41
|
+
* Used by all strategies after sorting.
|
|
42
|
+
*/
|
|
43
|
+
declare function allocateFromSorted(sorted: StockQuant[], quantity: number): AllocationResult;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/reservations/lifo.strategy.d.ts
|
|
46
|
+
/**
|
|
47
|
+
* LIFO allocation — consume newest stock first (by inDate DESC).
|
|
48
|
+
* Useful when freshness matters less and you want to keep oldest stock for auditing.
|
|
49
|
+
*/
|
|
50
|
+
declare class LifoStrategy implements AllocationPolicy {
|
|
51
|
+
readonly name = "lifo";
|
|
52
|
+
resolve(_skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/reservations/nearest.strategy.d.ts
|
|
56
|
+
/**
|
|
57
|
+
* Nearest allocation — consume stock closest to pick face (by location sortOrder ASC).
|
|
58
|
+
* Minimizes warehouse travel distance.
|
|
59
|
+
*
|
|
60
|
+
* Requires quants to carry locationSortOrder (set by repository join or denormalized).
|
|
61
|
+
*/
|
|
62
|
+
declare class NearestStrategy implements AllocationPolicy {
|
|
63
|
+
readonly name = "nearest";
|
|
64
|
+
resolve(_skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { FefoStrategy as a, allocateFromSorted as i, LifoStrategy as n, ExactLotStrategy as o, FifoStrategy as r, NearestStrategy as t };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { n as MovePort } from "./move.port-Qg1CYp7h.mjs";
|
|
2
|
+
import { t as FlowContext } from "./index-DFF0GJ4J.mjs";
|
|
3
|
+
import { t as LocationPort } from "./location.port-CValXIpb.mjs";
|
|
4
|
+
import { r as QuantPort } from "./quant.port-BBa66PBT.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/reporting/availability.d.ts
|
|
7
|
+
interface AvailabilityMatrixQuery {
|
|
8
|
+
skuRefs: string[];
|
|
9
|
+
nodeIds?: string[];
|
|
10
|
+
}
|
|
11
|
+
interface AvailabilityMatrixResult {
|
|
12
|
+
matrix: Array<{
|
|
13
|
+
skuRef: string;
|
|
14
|
+
nodes: Array<{
|
|
15
|
+
nodeId: string;
|
|
16
|
+
onHand: number;
|
|
17
|
+
reserved: number;
|
|
18
|
+
available: number;
|
|
19
|
+
incoming: number;
|
|
20
|
+
outgoing: number;
|
|
21
|
+
}>;
|
|
22
|
+
totalOnHand: number;
|
|
23
|
+
totalAvailable: number;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
declare class AvailabilityReport {
|
|
27
|
+
private quantPort;
|
|
28
|
+
private locationPort?;
|
|
29
|
+
constructor(quantPort: QuantPort, locationPort?: LocationPort | undefined);
|
|
30
|
+
getMatrix(query: AvailabilityMatrixQuery, ctx: FlowContext): Promise<AvailabilityMatrixResult>;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/reporting/health-metrics.d.ts
|
|
34
|
+
interface StockHealthMetrics {
|
|
35
|
+
turnoverRate: number;
|
|
36
|
+
daysOfInventory: number;
|
|
37
|
+
stockoutRate: number;
|
|
38
|
+
fillRate: number;
|
|
39
|
+
deadStockPercentage: number;
|
|
40
|
+
expiryRiskValue: number;
|
|
41
|
+
totalSkus: number;
|
|
42
|
+
totalOnHand: number;
|
|
43
|
+
totalValue: number;
|
|
44
|
+
}
|
|
45
|
+
declare class HealthMetricsReport {
|
|
46
|
+
private quantPort;
|
|
47
|
+
constructor(quantPort: QuantPort, _movePort: MovePort);
|
|
48
|
+
generate(ctx: FlowContext): Promise<StockHealthMetrics>;
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/reporting/stock-aging.d.ts
|
|
52
|
+
interface AgingBucket {
|
|
53
|
+
label: string;
|
|
54
|
+
minDays: number;
|
|
55
|
+
maxDays: number;
|
|
56
|
+
quantity: number;
|
|
57
|
+
value: number;
|
|
58
|
+
skuCount: number;
|
|
59
|
+
}
|
|
60
|
+
interface StockAgingResult {
|
|
61
|
+
asOfDate: Date;
|
|
62
|
+
buckets: AgingBucket[];
|
|
63
|
+
slowMoving: Array<{
|
|
64
|
+
skuRef: string;
|
|
65
|
+
locationId: string;
|
|
66
|
+
quantity: number;
|
|
67
|
+
ageDays: number;
|
|
68
|
+
}>;
|
|
69
|
+
deadStock: Array<{
|
|
70
|
+
skuRef: string;
|
|
71
|
+
locationId: string;
|
|
72
|
+
quantity: number;
|
|
73
|
+
ageDays: number;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
declare class StockAgingReport {
|
|
77
|
+
private quantPort;
|
|
78
|
+
constructor(quantPort: QuantPort);
|
|
79
|
+
generate(ctx: FlowContext, nodeId?: string): Promise<StockAgingResult>;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/reporting/turnover.d.ts
|
|
83
|
+
interface TurnoverResult {
|
|
84
|
+
period: {
|
|
85
|
+
start: Date;
|
|
86
|
+
end: Date;
|
|
87
|
+
};
|
|
88
|
+
totalCOGS: number;
|
|
89
|
+
averageInventoryValue: number;
|
|
90
|
+
turnoverRate: number;
|
|
91
|
+
daysOfInventory: number;
|
|
92
|
+
skuBreakdown: Array<{
|
|
93
|
+
skuRef: string;
|
|
94
|
+
unitsSold: number;
|
|
95
|
+
cogs: number;
|
|
96
|
+
avgInventory: number;
|
|
97
|
+
turnover: number;
|
|
98
|
+
}>;
|
|
99
|
+
}
|
|
100
|
+
declare class TurnoverReport {
|
|
101
|
+
private quantPort;
|
|
102
|
+
private movePort;
|
|
103
|
+
constructor(quantPort: QuantPort, movePort: MovePort);
|
|
104
|
+
generate(periodDays: number, ctx: FlowContext): Promise<TurnoverResult>;
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { StockAgingResult as a, AvailabilityMatrixQuery as c, StockAgingReport as i, AvailabilityMatrixResult as l, TurnoverResult as n, HealthMetricsReport as o, AgingBucket as r, StockHealthMetrics as s, TurnoverReport as t, AvailabilityReport as u };
|