@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@classytic/flow` are documented here.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-03-29
|
|
6
|
+
|
|
7
|
+
Initial public release.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Double-entry inventory engine with location hierarchy
|
|
12
|
+
- Stock quants (materialized read model)
|
|
13
|
+
- Reservation system with expiry and partial consumption
|
|
14
|
+
- FIFO/FEFO/WAC/specific/standard valuation + landed cost allocation
|
|
15
|
+
- Lot and serial traceability with recall support
|
|
16
|
+
- Putaway and removal routing strategies (ABC-velocity, category-zone, closest-to-pick, empty-bin, fixed-location)
|
|
17
|
+
- Route expansion engine (1/2/3-step reception and delivery)
|
|
18
|
+
- Wave picking engine with serpentine (boustrophedon) path optimization
|
|
19
|
+
- 3D warehouse coordinates: zone/aisle/bay/level/bin
|
|
20
|
+
- Odd aisles ascending, even aisles descending — eliminates dead-walking
|
|
21
|
+
- Location consolidation (merge picks for same SKU at same bin)
|
|
22
|
+
- Weight/volume/item cart batching with oversized pick splitting
|
|
23
|
+
- Cross-dock engine with multi-destination routing
|
|
24
|
+
- FIFO assignment to waiting outbound orders
|
|
25
|
+
- Per-destination quantity tracking with remaining-to-storage calculation
|
|
26
|
+
- Procurement order lifecycle (draft → approved → received)
|
|
27
|
+
- Cycle counting with auto-reconciliation and freeze policies
|
|
28
|
+
- Replenishment rules (reorder point, min/max, scheduled)
|
|
29
|
+
- GS1-128 barcode parser with dynamic year rollover
|
|
30
|
+
- Scan resolution chain (GS1 → location → lot → serial → SKU)
|
|
31
|
+
- Multi-tenant support via MongoKit plugins
|
|
32
|
+
- In-process event bus with error isolation
|
|
33
|
+
- Stock packaging with parent/child nesting
|
|
34
|
+
- Config-driven deployment modes (`simple` / `standard` / `enterprise`)
|
|
35
|
+
- `allocation.defaultPolicy` wired to built-in FifoStrategy/FefoStrategy/LifoStrategy
|
|
36
|
+
- `mode`, `routing`, `valuation` accessible at runtime via `flow.services.mode`
|
|
37
|
+
|
|
38
|
+
### Safety & Data Integrity
|
|
39
|
+
|
|
40
|
+
- Atomic quant upsert via MongoDB aggregation pipeline — `quantityAvailable = quantityOnHand - quantityReserved` computed in a single operation, no race window
|
|
41
|
+
- Negative stock prevention — `postMove()` checks `location.allowNegativeStock` before decrementing; throws `NegativeStockError` if insufficient
|
|
42
|
+
- Canonical posting path — `receiveGroup()` delegates to `PostingService.postMove()` per move, enforcing negative stock guard, move transition validation, and `inventory.move.done` event emission for every move
|
|
43
|
+
- Reservation lifecycle correctness:
|
|
44
|
+
- `expire()` releases `quantityReserved` from the quant (matches `cleanupExpired()`)
|
|
45
|
+
- `consume()` validates `quantity > 0`, caps at remaining, throws on overconsumption
|
|
46
|
+
- `consume()` decrements `quantityReserved` on the quant
|
|
47
|
+
- Group fulfillment (`receiveGroup`) consumes + releases all move reservations
|
|
48
|
+
- `releaseReservation` uses correct `consumed` vs `partially_consumed` status and computes `remaining` from pre-update state (no stale reads)
|
|
49
|
+
- Partially done move reconciliation — `receiveGroup()` includes `partially_done` in committable statuses and completes the remaining quantity
|
|
50
|
+
- Input validation — `quantityDone > 0`, `quantity > 0`, non-empty `items[]` enforced at service entry with `ValidationError`
|
|
51
|
+
- Tenant-scoped idempotency keys — all services (reservation, move, procurement) prefix keys with `organizationId:` to prevent cross-org collisions
|
|
52
|
+
- WAC precision — weighted average cost rounded to 4 decimal places; freezes at last known cost when stock goes negative
|
|
53
|
+
- Penny-leak prevention — landed cost post-capitalization uses remainder absorption per item with cross-item correction on the last item
|
|
54
|
+
- Concurrent allocation safety — parallel availability checks + `batchUpsert` for quant reservation locks
|
|
55
|
+
- Event handler error isolation — logged but never propagate to emitter
|
|
56
|
+
- FIFO/FEFO zero-quantity guard — valuation engines return empty result for `quantity <= 0`
|
|
57
|
+
- Move group state machine — explicit status validation in `receiveGroup()` (rejects done/cancelled/draft)
|
|
58
|
+
- Reservation cleanup batching — `cleanupExpired()` processes all expirations in a single transaction
|
|
59
|
+
- Quant `deleteAll()` uses consistent ObjectId filter (matches reads) — `rebuildFromMoveHistory()` correctly clears stale quants
|
|
60
|
+
|
|
61
|
+
### Dependencies
|
|
62
|
+
|
|
63
|
+
- `mongoose` (peer, >=9.0.0) — required
|
|
64
|
+
- `@classytic/mongokit` (peer, >=3.0.0) — required, provides repository pattern + plugins
|
|
65
|
+
|
|
66
|
+
### Test Coverage
|
|
67
|
+
|
|
68
|
+
- 60 test files, 560 test cases
|
|
69
|
+
- Coverage layers: domain unit, engine unit (mocked), service integration (MongoDB), E2E workflow, concurrency stress, data integrity, algorithm correctness
|
|
70
|
+
- Pure-interface files excluded from coverage denominator
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Classytic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# @classytic/flow
|
|
2
|
+
|
|
3
|
+
Production-grade inventory kernel and supply chain engine for MongoDB.
|
|
4
|
+
|
|
5
|
+
Double-entry inventory, location-based stock tracking, reservations, FIFO/FEFO/WAC valuation, lot/serial traceability, putaway/removal routing, wave picking with serpentine path optimization, cross-dock multi-destination routing, procurement, cycle counting, and replenishment — all as pure domain services with zero framework coupling.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { createFlowEngine } from '@classytic/flow';
|
|
11
|
+
import mongoose from 'mongoose';
|
|
12
|
+
|
|
13
|
+
// 1. Create the engine
|
|
14
|
+
const flow = createFlowEngine({
|
|
15
|
+
mongoose: mongoose.connection,
|
|
16
|
+
mode: 'standard',
|
|
17
|
+
catalog: {
|
|
18
|
+
resolveSku: async (skuRef) => productService.findBySku(skuRef),
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// 2. Access models
|
|
23
|
+
const { InventoryNode, Location, StockMoveGroup, StockMove, StockQuant, Reservation } = flow.models;
|
|
24
|
+
|
|
25
|
+
// 3. Context carries tenant + actor — never mixed into domain inputs
|
|
26
|
+
const ctx = { organizationId: 'org_1', actorId: 'user_1' };
|
|
27
|
+
|
|
28
|
+
// 4. Use services (pure async functions — no HTTP, no framework)
|
|
29
|
+
const availability = await flow.services.quant.getAvailability({
|
|
30
|
+
skuRef: 'SKU-RED-M',
|
|
31
|
+
nodeId: 'warehouse_1',
|
|
32
|
+
}, ctx);
|
|
33
|
+
|
|
34
|
+
// 5. Create and execute a transfer
|
|
35
|
+
const group = await flow.services.moveGroup.create({
|
|
36
|
+
groupType: 'transfer',
|
|
37
|
+
sourceNodeId: 'warehouse_1',
|
|
38
|
+
destinationNodeId: 'store_1',
|
|
39
|
+
items: [{
|
|
40
|
+
moveGroupId: '', // auto-assigned
|
|
41
|
+
operationType: 'transfer',
|
|
42
|
+
skuRef: 'SKU-RED-M',
|
|
43
|
+
sourceLocationId: 'loc_storage_wh1',
|
|
44
|
+
destinationLocationId: 'loc_storage_store1',
|
|
45
|
+
quantityPlanned: 50,
|
|
46
|
+
}],
|
|
47
|
+
}, ctx);
|
|
48
|
+
|
|
49
|
+
await flow.services.moveGroup.executeAction(group._id, 'confirm', {}, ctx);
|
|
50
|
+
await flow.services.moveGroup.executeAction(group._id, 'dispatch', {}, ctx);
|
|
51
|
+
await flow.services.moveGroup.executeAction(group._id, 'receive', {}, ctx);
|
|
52
|
+
|
|
53
|
+
// 6. Subscribe to events (bridge to your ledger, notifications, etc.)
|
|
54
|
+
flow.events.on('inventory.move.done', async (payload) => {
|
|
55
|
+
// payload: { organizationId, moveId, skuRef, quantityDone, ... }
|
|
56
|
+
await ledger.postInventoryMovement(payload);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 7. Query active config at runtime
|
|
60
|
+
flow.services.mode; // 'standard'
|
|
61
|
+
flow.services.routing; // { putaway: false, removal: false, crossDock: false }
|
|
62
|
+
flow.services.valuation; // { method: 'wac' }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Simple Mode (single store, basic stock)
|
|
66
|
+
|
|
67
|
+
Flow scales down to simple apps. You don't need locations, routing, or lots — just stock queries and moves:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const ctx = { organizationId: 'org_1', actorId: 'user_1' };
|
|
71
|
+
|
|
72
|
+
// Check availability
|
|
73
|
+
const stock = await flow.services.quant.getAvailability({ skuRef: 'SKU-RED-M' }, ctx);
|
|
74
|
+
// → { quantityOnHand: 50, quantityReserved: 5, quantityAvailable: 45, quantityIncoming: 20, quantityOutgoing: 0 }
|
|
75
|
+
|
|
76
|
+
// Reserve stock for an order
|
|
77
|
+
await flow.services.reservation.reserve({
|
|
78
|
+
reservationType: 'hard',
|
|
79
|
+
skuRef: 'SKU-RED-M',
|
|
80
|
+
locationId: 'loc_default',
|
|
81
|
+
quantity: 3,
|
|
82
|
+
ownerType: 'order',
|
|
83
|
+
ownerId: 'order_123',
|
|
84
|
+
}, ctx);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Features
|
|
88
|
+
|
|
89
|
+
| Feature | Description |
|
|
90
|
+
|---------|-------------|
|
|
91
|
+
| **Double-entry inventory** | Every stock change is a move between locations — never direct mutation |
|
|
92
|
+
| **Location hierarchy** | Warehouse > zone > aisle > bin. Virtual locations for vendor, customer, scrap, transit |
|
|
93
|
+
| **Stock quants** | Materialized read model of on-hand, reserved, available, incoming, outgoing |
|
|
94
|
+
| **Reservations** | First-class records with expiry, partial consumption, overconsumption guard, and pluggable allocation |
|
|
95
|
+
| **Valuation** | WAC (default), FIFO, FEFO, specific identification, standard cost, landed cost with penny-leak prevention |
|
|
96
|
+
| **Lot/serial tracking** | Full traceability with expiry, recall support, and FEFO allocation |
|
|
97
|
+
| **Routing engine** | Putaway strategies, removal strategies, route expansion |
|
|
98
|
+
| **Wave picking** | Serpentine (boustrophedon) path optimization with 3D coordinates, weight/volume/item cart batching with oversized pick splitting |
|
|
99
|
+
| **Cross-dock routing** | Multi-destination assignments — incoming stock auto-routed to waiting outbound orders in FIFO order |
|
|
100
|
+
| **Procurement** | Purchase order lifecycle, receiving, vendor management |
|
|
101
|
+
| **Cycle counting** | Full/cycle/blind counts with auto-reconciliation |
|
|
102
|
+
| **Replenishment** | Min/max, reorder point, safety stock, EOQ, multi-echelon |
|
|
103
|
+
| **Scan resolution + GS1-128** | Device-agnostic barcode/QR/RFID → entity resolution. Built-in GS1-128 parser for GTIN, lot, expiry, serial. |
|
|
104
|
+
| **Stock states** | Sellable, damaged, quarantine, hold, returns, expired, in-transit |
|
|
105
|
+
| **UoM conversion** | Each/case/carton/pallet with per-SKU conversion factors |
|
|
106
|
+
| **Packaging** | StockPackage with parent/child nesting (carton → case → pallet), weight calculation |
|
|
107
|
+
| **Stock ownership** | `ownerRef` on quants — owned vs consignment/third-party in same location |
|
|
108
|
+
| **QC workflow** | Quality hold location + inspection route step |
|
|
109
|
+
| **Multi-tenant** | Organization-scoped via MongoKit multi-tenant plugin |
|
|
110
|
+
| **Events** | In-process emitter — bridge to Redis, webhooks, ledger, notifications |
|
|
111
|
+
| **Capability tiers** | `simple` / `standard` / `enterprise` — config-driven feature gating |
|
|
112
|
+
|
|
113
|
+
## Safety & Data Integrity
|
|
114
|
+
|
|
115
|
+
Flow enforces invariants at the engine level so consumers don't have to:
|
|
116
|
+
|
|
117
|
+
| Guard | Behavior |
|
|
118
|
+
|-------|----------|
|
|
119
|
+
| **Atomic quant updates** | `quantityAvailable = quantityOnHand - quantityReserved` computed in a single MongoDB aggregation pipeline — no race window |
|
|
120
|
+
| **Negative stock prevention** | `postMove()` checks `location.allowNegativeStock` before decrementing; throws `NegativeStockError` if insufficient. Virtual sources (returns, receipts) auto-bypass. |
|
|
121
|
+
| **Canonical posting path** | `receiveGroup()` delegates to `PostingService.postMove()` per move — negative stock guard, move transition validation, and `inventory.move.done` events fire for every move |
|
|
122
|
+
| **Reservation lifecycle** | `expire()` and `consume()` both release `quantityReserved` from the quant. `consume()` validates `quantity > 0` and caps at remaining — no overconsumption. Fulfillment releases all reservations. |
|
|
123
|
+
| **Input validation** | `quantityDone > 0`, `quantity > 0`, non-empty `items[]` enforced at service entry |
|
|
124
|
+
| **Tenant-scoped idempotency** | All idempotency keys prefixed with `organizationId:` (reservation, move, procurement) — no cross-tenant collision |
|
|
125
|
+
| **WAC precision** | Weighted average cost rounded to 4 decimal places; freezes at last known cost when stock goes negative |
|
|
126
|
+
| **Penny-leak prevention** | Landed cost post-capitalization uses remainder absorption — COGS + remaining = exactly the allocated cost per item, with cross-item correction on the last item |
|
|
127
|
+
| **Concurrent allocation safety** | Parallel availability check + `batchUpsert` for quant reservation locks within a single transaction |
|
|
128
|
+
| **Event error isolation** | Handler errors logged but never propagate to emitter |
|
|
129
|
+
| **GS1 year rollover** | Dynamic sliding window (±50 years from current year) instead of hardcoded cutoff |
|
|
130
|
+
| **FIFO/FEFO zero-qty guard** | Valuation engines return empty result for `quantity <= 0` |
|
|
131
|
+
| **Partially done moves** | `receiveGroup()` reconciles `partially_done` moves — completes remaining quantity instead of skipping them |
|
|
132
|
+
|
|
133
|
+
## Subpath Exports
|
|
134
|
+
|
|
135
|
+
| Import | Contents |
|
|
136
|
+
|--------|----------|
|
|
137
|
+
| `@classytic/flow` | `createFlowEngine()` factory + full API |
|
|
138
|
+
| `@classytic/flow/domain` | Entities, value objects, errors |
|
|
139
|
+
| `@classytic/flow/domain/contracts` | `CatalogBridge`, `AuthContext` interfaces |
|
|
140
|
+
| `@classytic/flow/domain/enums` | `LocationType`, `MoveStatus`, `OperationType`, etc. |
|
|
141
|
+
| `@classytic/flow/domain/policies` | `AllocationPolicy`, `RemovalPolicy`, `ValuationPolicy` |
|
|
142
|
+
| `@classytic/flow/models` | Mongoose schemas |
|
|
143
|
+
| `@classytic/flow/repositories` | Repository factories (MongoKit) |
|
|
144
|
+
| `@classytic/flow/services` | All domain services |
|
|
145
|
+
| `@classytic/flow/valuation` | Cost layers, WAC/FIFO/FEFO engine, landed cost |
|
|
146
|
+
| `@classytic/flow/routing` | Putaway, removal, wave engine, cross-dock engine |
|
|
147
|
+
| `@classytic/flow/reservations` | Allocation strategies and reservation service |
|
|
148
|
+
| `@classytic/flow/procurement` | Procurement order lifecycle |
|
|
149
|
+
| `@classytic/flow/counting` | Inventory count and reconciliation |
|
|
150
|
+
| `@classytic/flow/traceability` | Lot/serial trace queries |
|
|
151
|
+
| `@classytic/flow/scanning` | Scan resolution + GS1-128 parser |
|
|
152
|
+
| `@classytic/flow/packaging` | StockPackage service |
|
|
153
|
+
| `@classytic/flow/reporting` | Read models, metrics, stock aging |
|
|
154
|
+
| `@classytic/flow/events` | Event definitions and emitter |
|
|
155
|
+
| `@classytic/flow/types` | All TypeScript types |
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const flow = createFlowEngine({
|
|
161
|
+
// Required
|
|
162
|
+
mongoose: connection,
|
|
163
|
+
|
|
164
|
+
// Required — how Flow resolves product details
|
|
165
|
+
catalog: {
|
|
166
|
+
resolveSku: async (skuRef) => ({ sku: skuRef, displayName: '...', trackingMode: 'none', uom: 'unit' }),
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
// Optional — deployment mode (default: 'standard')
|
|
170
|
+
mode: 'standard',
|
|
171
|
+
|
|
172
|
+
// Optional — multi-tenant (omit for single-tenant)
|
|
173
|
+
multiTenant: { orgField: 'organizationId' },
|
|
174
|
+
|
|
175
|
+
// Optional — valuation method (default: 'wac')
|
|
176
|
+
valuation: { method: 'wac' },
|
|
177
|
+
|
|
178
|
+
// Optional — allocation policy (default: 'fifo') — resolves to built-in FifoStrategy/FefoStrategy/LifoStrategy
|
|
179
|
+
allocation: { defaultPolicy: 'fifo' },
|
|
180
|
+
|
|
181
|
+
// Optional — routing (enterprise mode)
|
|
182
|
+
routing: { putaway: true, removal: true, crossDock: false },
|
|
183
|
+
|
|
184
|
+
// Optional — custom allocation policy (overrides allocation.defaultPolicy)
|
|
185
|
+
policies: { allocation: myCustomPolicy },
|
|
186
|
+
|
|
187
|
+
// Optional — custom event emitter (default: in-process)
|
|
188
|
+
events: { adapter: myRedisEventBus },
|
|
189
|
+
|
|
190
|
+
// Optional — idempotency store
|
|
191
|
+
idempotency: myRedisIdempotency,
|
|
192
|
+
|
|
193
|
+
// Optional — MongoKit plugins per repository
|
|
194
|
+
plugins: {
|
|
195
|
+
quant: [cachePlugin({ adapter: redis, ttl: 30 })],
|
|
196
|
+
move: [auditTrailPlugin()],
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| Option | Type | Default | Description |
|
|
202
|
+
|--------|------|---------|-------------|
|
|
203
|
+
| `mongoose` | `Connection` | required | Mongoose connection |
|
|
204
|
+
| `catalog` | `CatalogBridge` | required | Product/SKU resolution |
|
|
205
|
+
| `mode` | `FlowMode` | `'standard'` | `simple`, `standard`, or `enterprise` |
|
|
206
|
+
| `multiTenant` | `object` | `undefined` | `{ orgField }` for org-scoped isolation |
|
|
207
|
+
| `valuation.method` | `string` | `'wac'` | `wac`, `fifo`, `fefo`, `specific`, `standard` |
|
|
208
|
+
| `allocation.defaultPolicy` | `string` | `'fifo'` | `fifo`, `fefo`, `lifo` — maps to built-in strategy |
|
|
209
|
+
| `routing.putaway` | `boolean` | `false` | Enable putaway strategy engine |
|
|
210
|
+
| `routing.removal` | `boolean` | `false` | Enable removal strategy engine |
|
|
211
|
+
| `routing.crossDock` | `boolean` | `false` | Enable cross-dock multi-destination routing |
|
|
212
|
+
|
|
213
|
+
## What Flow Returns
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const flow = createFlowEngine(config);
|
|
217
|
+
|
|
218
|
+
flow.models // All Mongoose models
|
|
219
|
+
flow.repositories // All MongoKit repositories
|
|
220
|
+
flow.services // All domain services + active config (mode, routing, valuation)
|
|
221
|
+
flow.events // In-process event emitter
|
|
222
|
+
flow.registerPolicy // Register custom allocation policies at runtime
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## What Flow Does NOT Include
|
|
226
|
+
|
|
227
|
+
Flow is a pure domain kernel. These are consumer responsibilities:
|
|
228
|
+
|
|
229
|
+
- **HTTP routes** — use Arc, Express, Fastify, NestJS, Next.js
|
|
230
|
+
- **Durable workflows** — use Streamline, BullMQ, cron
|
|
231
|
+
- **Agent tooling** — wrap `flow.services` in your agent tools or MCP servers
|
|
232
|
+
- **Ledger integration** — subscribe to `flow.events`
|
|
233
|
+
- **Authentication** — pass `AuthContext` to service methods
|
|
234
|
+
|
|
235
|
+
## Dependencies
|
|
236
|
+
|
|
237
|
+
| Dependency | Type | Required |
|
|
238
|
+
|------------|------|----------|
|
|
239
|
+
| `mongoose` | peer | Yes (>=9.0.0) |
|
|
240
|
+
| `@classytic/mongokit` | peer | Yes (>=3.0.0) — provides repository pattern, pagination, plugins |
|
|
241
|
+
|
|
242
|
+
## Reference Docs
|
|
243
|
+
|
|
244
|
+
| Doc | Description |
|
|
245
|
+
|-----|-------------|
|
|
246
|
+
| [Architecture](docs/architecture.md) | Domain model, clean architecture layers, invariants, capability tiers |
|
|
247
|
+
| [Algorithms](docs/algorithms.md) | Valuation, allocation, routing, replenishment formulas |
|
|
248
|
+
| [Workflows](docs/workflows.md) | Inbound, outbound, transfer, counting, return flows |
|
|
249
|
+
| [Plugins & Adapters](docs/plugins-and-adapters.md) | Scan, events, labels, catalog, policies, workflow adapters |
|
|
250
|
+
| [Integration](docs/integration.md) | MongoKit, Arc, Streamline, framework examples |
|
|
251
|
+
| [API Reference](docs/api-reference.md) | Service methods, entity fields, events, errors |
|
|
252
|
+
| [Test Strategy](docs/test-strategy.md) | Test layers, coverage targets, fixtures |
|
|
253
|
+
| [Migration Plan](docs/migration-plan.md) | Step-by-step guide to integrate Flow into an Arc backend |
|
|
254
|
+
| [Changelog](CHANGELOG.md) | Version history and bug fixes |
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { n as StockQuant } from "./stock-quant-CZhgvTu7.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/domain/policies/allocation-policy.d.ts
|
|
4
|
+
interface AllocationResult {
|
|
5
|
+
fulfilled: boolean;
|
|
6
|
+
allocations: Array<{
|
|
7
|
+
quantId: string;
|
|
8
|
+
locationId: string;
|
|
9
|
+
lotId?: string;
|
|
10
|
+
quantity: number;
|
|
11
|
+
}>;
|
|
12
|
+
shortfall: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Allocation policy — decides which quants fulfill a demand.
|
|
16
|
+
* Consumer can provide custom implementations.
|
|
17
|
+
*/
|
|
18
|
+
interface AllocationPolicy {
|
|
19
|
+
name: string;
|
|
20
|
+
resolve(skuRef: string, quantity: number, candidates: StockQuant[]): AllocationResult;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { AllocationResult as n, AllocationPolicy as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/domain/errors/base.ts
|
|
2
|
+
var FlowError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = this.constructor.name;
|
|
6
|
+
}
|
|
7
|
+
toJSON() {
|
|
8
|
+
return {
|
|
9
|
+
code: this.code,
|
|
10
|
+
message: this.message,
|
|
11
|
+
name: this.name
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { FlowError as t };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/domain/contracts/catalog-bridge.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* CatalogBridge — required contract.
|
|
4
|
+
* Consumer implements this to tell Flow how to resolve SKU references.
|
|
5
|
+
* Flow does not own the product catalog.
|
|
6
|
+
*/
|
|
7
|
+
interface CatalogBridge {
|
|
8
|
+
resolveSku(skuRef: string): Promise<SkuDetails | null>;
|
|
9
|
+
syncAvailability?(updates: AvailabilityUpdate[]): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
interface SkuDetails {
|
|
12
|
+
skuRef: string;
|
|
13
|
+
sku: string;
|
|
14
|
+
displayName: string;
|
|
15
|
+
trackingMode: 'none' | 'lot' | 'serial';
|
|
16
|
+
uom: string;
|
|
17
|
+
weight?: number;
|
|
18
|
+
volume?: number;
|
|
19
|
+
barcode?: string[];
|
|
20
|
+
isActive: boolean;
|
|
21
|
+
}
|
|
22
|
+
interface AvailabilityUpdate {
|
|
23
|
+
skuRef: string;
|
|
24
|
+
nodeId: string;
|
|
25
|
+
available: number;
|
|
26
|
+
inStock: boolean;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { CatalogBridge as n, SkuDetails as r, AvailabilityUpdate as t };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { a as QueryOptions, t as FlowContext } from "./index-DFF0GJ4J.mjs";
|
|
2
|
+
import { t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/domain/entities/cost-layer.d.ts
|
|
5
|
+
interface CostLayer {
|
|
6
|
+
_id: string;
|
|
7
|
+
organizationId: string;
|
|
8
|
+
skuRef: string;
|
|
9
|
+
locationId: string;
|
|
10
|
+
lotId?: string;
|
|
11
|
+
remainingQty: number;
|
|
12
|
+
unitCost: number;
|
|
13
|
+
receivedAt: Date;
|
|
14
|
+
expiresAt?: Date;
|
|
15
|
+
procurementRef?: string;
|
|
16
|
+
moveRef: string;
|
|
17
|
+
createdAt?: Date;
|
|
18
|
+
updatedAt?: Date;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/domain/ports/cost-layer.port.d.ts
|
|
22
|
+
interface CostLayerPort {
|
|
23
|
+
create(input: Omit<CostLayer, '_id' | 'createdAt' | 'updatedAt'>, session?: TransactionSession): Promise<CostLayer>;
|
|
24
|
+
drain(id: string, quantity: number, ctx: FlowContext, session?: TransactionSession): Promise<CostLayer>;
|
|
25
|
+
findBySkuRef(skuRef: string, locationId: string, ctx: FlowContext, session?: TransactionSession): Promise<CostLayer[]>;
|
|
26
|
+
findMany(filter: Record<string, unknown>, ctx: FlowContext, session?: TransactionSession, options?: QueryOptions): Promise<CostLayer[]>;
|
|
27
|
+
findOrderedForConsumption(skuRef: string, locationId: string, method: 'fifo' | 'fefo', ctx: FlowContext, session?: TransactionSession): Promise<CostLayer[]>;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { CostLayer as n, CostLayerPort as t };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { t as InsufficientStockError } from "./insufficient-stock.error-Dyr4BYaV.mjs";
|
|
2
|
+
import { t as assertTenantContext } from "./tenant-guard-6Ne-BILP.mjs";
|
|
3
|
+
//#region src/valuation/fifo.engine.ts
|
|
4
|
+
var FifoEngine = class {
|
|
5
|
+
consume(layers, quantity) {
|
|
6
|
+
if (quantity <= 0) return {
|
|
7
|
+
consumed: [],
|
|
8
|
+
totalCost: 0,
|
|
9
|
+
totalQuantity: 0,
|
|
10
|
+
remainingUncosted: 0,
|
|
11
|
+
insufficientLayers: false
|
|
12
|
+
};
|
|
13
|
+
const consumed = [];
|
|
14
|
+
let remaining = quantity;
|
|
15
|
+
let totalCost = 0;
|
|
16
|
+
for (const layer of layers) {
|
|
17
|
+
if (remaining <= 0) break;
|
|
18
|
+
if (layer.remainingQty <= 0) continue;
|
|
19
|
+
const take = Math.min(layer.remainingQty, remaining);
|
|
20
|
+
const cost = take * layer.unitCost;
|
|
21
|
+
consumed.push({
|
|
22
|
+
layerId: layer._id,
|
|
23
|
+
quantity: take,
|
|
24
|
+
unitCost: layer.unitCost,
|
|
25
|
+
totalCost: cost
|
|
26
|
+
});
|
|
27
|
+
totalCost += cost;
|
|
28
|
+
remaining -= take;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
consumed,
|
|
32
|
+
totalCost,
|
|
33
|
+
totalQuantity: quantity - remaining,
|
|
34
|
+
remainingUncosted: remaining,
|
|
35
|
+
insufficientLayers: remaining > 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/valuation/fefo.engine.ts
|
|
41
|
+
var FefoEngine = class {
|
|
42
|
+
fifoEngine = new FifoEngine();
|
|
43
|
+
consume(layers, quantity) {
|
|
44
|
+
return this.fifoEngine.consume(layers, quantity);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/valuation/cost-layer.service.ts
|
|
49
|
+
var CostLayerService = class {
|
|
50
|
+
fifoEngine = new FifoEngine();
|
|
51
|
+
fefoEngine = new FefoEngine();
|
|
52
|
+
constructor(costLayerPort, _unitOfWork) {
|
|
53
|
+
this.costLayerPort = costLayerPort;
|
|
54
|
+
}
|
|
55
|
+
async createLayer(input, session) {
|
|
56
|
+
return this.costLayerPort.create(input, session);
|
|
57
|
+
}
|
|
58
|
+
async consumeLayers(skuRef, locationId, quantity, method, ctx, session) {
|
|
59
|
+
assertTenantContext(ctx);
|
|
60
|
+
const layers = await this.costLayerPort.findOrderedForConsumption(skuRef, locationId, method, ctx, session);
|
|
61
|
+
const result = (method === "fefo" ? this.fefoEngine : this.fifoEngine).consume(layers, quantity);
|
|
62
|
+
if (result.insufficientLayers) throw new InsufficientStockError(skuRef, quantity, quantity - result.remainingUncosted, locationId);
|
|
63
|
+
for (const entry of result.consumed) await this.costLayerPort.drain(entry.layerId, entry.quantity, ctx, session);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
async getValuation(skuRef, locationId, ctx) {
|
|
67
|
+
assertTenantContext(ctx);
|
|
68
|
+
const layers = await this.costLayerPort.findBySkuRef(skuRef, locationId, ctx);
|
|
69
|
+
let totalQuantity = 0;
|
|
70
|
+
let totalValue = 0;
|
|
71
|
+
for (const layer of layers) if (layer.remainingQty > 0) {
|
|
72
|
+
totalQuantity += layer.remainingQty;
|
|
73
|
+
totalValue += layer.remainingQty * layer.unitCost;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
skuRef,
|
|
77
|
+
locationId,
|
|
78
|
+
totalQuantity,
|
|
79
|
+
totalValue: Math.round(totalValue * 100) / 100,
|
|
80
|
+
averageUnitCost: totalQuantity > 0 ? Math.round(totalValue / totalQuantity * 100) / 100 : 0,
|
|
81
|
+
layerCount: layers.filter((l) => l.remainingQty > 0).length
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
export { FefoEngine as n, FifoEngine as r, CostLayerService as t };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { t as FlowContext } from "./index-DFF0GJ4J.mjs";
|
|
2
|
+
import { n as CostLayer, t as CostLayerPort } from "./cost-layer.port-iH9pvZqB.mjs";
|
|
3
|
+
import { n as UnitOfWork, t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/valuation/fifo.engine.d.ts
|
|
6
|
+
interface ConsumptionResult {
|
|
7
|
+
consumed: Array<{
|
|
8
|
+
layerId: string;
|
|
9
|
+
quantity: number;
|
|
10
|
+
unitCost: number;
|
|
11
|
+
totalCost: number;
|
|
12
|
+
}>;
|
|
13
|
+
totalCost: number;
|
|
14
|
+
totalQuantity: number;
|
|
15
|
+
remainingUncosted: number;
|
|
16
|
+
insufficientLayers: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare class FifoEngine {
|
|
19
|
+
consume(layers: CostLayer[], quantity: number): ConsumptionResult;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/valuation/cost-layer.service.d.ts
|
|
23
|
+
interface CreateCostLayerInput {
|
|
24
|
+
organizationId: string;
|
|
25
|
+
skuRef: string;
|
|
26
|
+
locationId: string;
|
|
27
|
+
lotId?: string;
|
|
28
|
+
remainingQty: number;
|
|
29
|
+
unitCost: number;
|
|
30
|
+
receivedAt: Date;
|
|
31
|
+
expiresAt?: Date;
|
|
32
|
+
procurementRef?: string;
|
|
33
|
+
moveRef: string;
|
|
34
|
+
}
|
|
35
|
+
interface InventoryValuation {
|
|
36
|
+
skuRef: string;
|
|
37
|
+
locationId: string;
|
|
38
|
+
totalQuantity: number;
|
|
39
|
+
totalValue: number;
|
|
40
|
+
averageUnitCost: number;
|
|
41
|
+
layerCount: number;
|
|
42
|
+
}
|
|
43
|
+
declare class CostLayerService {
|
|
44
|
+
private costLayerPort;
|
|
45
|
+
private fifoEngine;
|
|
46
|
+
private fefoEngine;
|
|
47
|
+
constructor(costLayerPort: CostLayerPort, _unitOfWork: UnitOfWork);
|
|
48
|
+
createLayer(input: CreateCostLayerInput, session?: TransactionSession): Promise<CostLayer>;
|
|
49
|
+
consumeLayers(skuRef: string, locationId: string, quantity: number, method: 'fifo' | 'fefo', ctx: FlowContext, session?: TransactionSession): Promise<ConsumptionResult>;
|
|
50
|
+
getValuation(skuRef: string, locationId: string, ctx: FlowContext): Promise<InventoryValuation>;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { FifoEngine as a, ConsumptionResult as i, CreateCostLayerInput as n, InventoryValuation as r, CostLayerService as t };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { t as FlowContext } from "./index-DFF0GJ4J.mjs";
|
|
2
|
+
import { t as TransactionSession } from "./unit-of-work.port-CWEkrDKu.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/domain/entities/count-line.d.ts
|
|
5
|
+
interface CountLine {
|
|
6
|
+
_id: string;
|
|
7
|
+
organizationId: string;
|
|
8
|
+
countId: string;
|
|
9
|
+
skuRef: string;
|
|
10
|
+
locationId: string;
|
|
11
|
+
lotId?: string;
|
|
12
|
+
serialCode?: string;
|
|
13
|
+
expectedQuantity: number;
|
|
14
|
+
countedQuantity: number;
|
|
15
|
+
variance: number;
|
|
16
|
+
varianceReason?: string;
|
|
17
|
+
recountFlag?: boolean;
|
|
18
|
+
createdAt?: Date;
|
|
19
|
+
updatedAt?: Date;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/domain/entities/inventory-count.d.ts
|
|
23
|
+
type CountStatus = 'draft' | 'in_progress' | 'submitted' | 'reconciled' | 'approved';
|
|
24
|
+
interface CountScope {
|
|
25
|
+
nodeId?: string;
|
|
26
|
+
locationId?: string;
|
|
27
|
+
skuRefs?: string[];
|
|
28
|
+
}
|
|
29
|
+
interface InventoryCount {
|
|
30
|
+
_id: string;
|
|
31
|
+
organizationId: string;
|
|
32
|
+
countNumber: string;
|
|
33
|
+
countType: 'full' | 'cycle' | 'spot';
|
|
34
|
+
scope: CountScope;
|
|
35
|
+
status: CountStatus;
|
|
36
|
+
freezePolicy: 'hard_freeze' | 'soft_freeze' | 'none';
|
|
37
|
+
startedAt?: Date;
|
|
38
|
+
submittedAt?: Date;
|
|
39
|
+
reconciledAt?: Date;
|
|
40
|
+
createdBy?: string;
|
|
41
|
+
modifiedBy?: string;
|
|
42
|
+
approvedBy?: string;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
createdAt?: Date;
|
|
45
|
+
updatedAt?: Date;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/domain/ports/count.port.d.ts
|
|
49
|
+
interface CountPort {
|
|
50
|
+
create(input: Omit<InventoryCount, '_id' | 'createdAt' | 'updatedAt'>, session?: TransactionSession): Promise<InventoryCount>;
|
|
51
|
+
findById(id: string, ctx: FlowContext, session?: TransactionSession): Promise<InventoryCount | null>;
|
|
52
|
+
updateStatus(id: string, status: CountStatus, updates: Partial<InventoryCount>, session?: TransactionSession): Promise<InventoryCount>;
|
|
53
|
+
submitLines(countId: string, lines: Omit<CountLine, '_id' | 'createdAt' | 'updatedAt'>[], session?: TransactionSession): Promise<CountLine[]>;
|
|
54
|
+
getLines(countId: string, ctx: FlowContext, session?: TransactionSession): Promise<CountLine[]>;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { CountLine as a, InventoryCount as i, CountScope as n, CountStatus as r, CountPort as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as ReconcileResult, i as ReconcileOptions, n as CountingService, o as VarianceReport, r as CreateCountInput, t as CountLineInput } from "../counting.service-CpAxU2G0.mjs";
|
|
2
|
+
export { type CountLineInput, CountingService, type CreateCountInput, type ReconcileOptions, type ReconcileResult, type VarianceReport };
|