@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/README.md +258 -0
  4. package/dist/allocation-policy-my_HfzdV.d.mts +23 -0
  5. package/dist/base-MWBqRFM2.mjs +16 -0
  6. package/dist/catalog-bridge-K8bdkncJ.d.mts +29 -0
  7. package/dist/cost-layer.port-iH9pvZqB.d.mts +30 -0
  8. package/dist/cost-layer.service-BQ1bs-XN.mjs +86 -0
  9. package/dist/cost-layer.service-DWmo9dQz.d.mts +53 -0
  10. package/dist/count.port-BRqwGbi3.d.mts +57 -0
  11. package/dist/counting/index.d.mts +2 -0
  12. package/dist/counting/index.mjs +2 -0
  13. package/dist/counting.service-BiQXqorv.mjs +232 -0
  14. package/dist/counting.service-CpAxU2G0.d.mts +74 -0
  15. package/dist/domain/contracts/index.d.mts +3 -0
  16. package/dist/domain/contracts/index.mjs +1 -0
  17. package/dist/domain/enums/index.d.mts +2 -0
  18. package/dist/domain/enums/index.mjs +4 -0
  19. package/dist/domain/index.d.mts +24 -0
  20. package/dist/domain/index.mjs +10 -0
  21. package/dist/domain/policies/index.d.mts +4 -0
  22. package/dist/domain/policies/index.mjs +1 -0
  23. package/dist/domain-D5cpMpR0.mjs +96 -0
  24. package/dist/domain-errors-D7S9ydNF.mjs +133 -0
  25. package/dist/enums-C3_z6aHC.mjs +82 -0
  26. package/dist/event-bus-BNmyoJb4.mjs +37 -0
  27. package/dist/event-bus-Um_xrcMY.d.mts +21 -0
  28. package/dist/event-emitter.port-BFh2pasY.d.mts +183 -0
  29. package/dist/event-types-BSqQOvXv.mjs +29 -0
  30. package/dist/events/index.d.mts +3 -0
  31. package/dist/events/index.mjs +3 -0
  32. package/dist/idempotency.port-CTC70JON.d.mts +55 -0
  33. package/dist/index-Bia4m8d2.d.mts +67 -0
  34. package/dist/index-BmNm3oNU2.d.mts +107 -0
  35. package/dist/index-C5PciI9P.d.mts +203 -0
  36. package/dist/index-CMTUKEK_.d.mts +308 -0
  37. package/dist/index-C_aEnozN.d.mts +220 -0
  38. package/dist/index-CulWO137.d.mts +107 -0
  39. package/dist/index-DFF0GJ4J.d.mts +36 -0
  40. package/dist/index-DsE7lZdO.d.mts +11 -0
  41. package/dist/index-DwO9IdNa.d.mts +1 -0
  42. package/dist/index-dtWUZr2a2.d.mts +350 -0
  43. package/dist/index.d.mts +128 -0
  44. package/dist/index.mjs +102 -0
  45. package/dist/insufficient-stock.error-Dyr4BYaV.mjs +15 -0
  46. package/dist/location.port-CValXIpb.d.mts +52 -0
  47. package/dist/lot.port-ChsmvZqs.d.mts +32 -0
  48. package/dist/models/index.d.mts +2 -0
  49. package/dist/models/index.mjs +2 -0
  50. package/dist/models-CHTMbp-G.mjs +1020 -0
  51. package/dist/move-group.port-DHGoQA3d.d.mts +56 -0
  52. package/dist/move-status-DkaFp2GD.mjs +38 -0
  53. package/dist/move.port-Qg1CYp7h.d.mts +89 -0
  54. package/dist/package.service-4tcAwBbr.mjs +95 -0
  55. package/dist/package.service-C605NaBQ.d.mts +42 -0
  56. package/dist/packaging/index.d.mts +2 -0
  57. package/dist/packaging/index.mjs +2 -0
  58. package/dist/procurement/index.d.mts +2 -0
  59. package/dist/procurement/index.mjs +2 -0
  60. package/dist/quant.port-BBa66PBT.d.mts +42 -0
  61. package/dist/removal-policy-BItBB8FD.d.mts +29 -0
  62. package/dist/replenishment-rule.port-DnEYtbyD.d.mts +78 -0
  63. package/dist/replenishment.service-BT9P-HKM.mjs +284 -0
  64. package/dist/replenishment.service-HO0sDhB_.d.mts +89 -0
  65. package/dist/reporting/index.d.mts +2 -0
  66. package/dist/reporting/index.mjs +2 -0
  67. package/dist/reporting-CL5ffrKM.mjs +243 -0
  68. package/dist/repositories/index.d.mts +2 -0
  69. package/dist/repositories/index.mjs +2 -0
  70. package/dist/repositories-nZXJKvLW.mjs +842 -0
  71. package/dist/reservation-status-ZfuTaWG0.mjs +22 -0
  72. package/dist/reservation.port-l9NFQ0si.d.mts +85 -0
  73. package/dist/reservations/index.d.mts +2 -0
  74. package/dist/reservations/index.mjs +2 -0
  75. package/dist/reservations-Cg4wN0QB.mjs +112 -0
  76. package/dist/routing/index.d.mts +362 -0
  77. package/dist/routing/index.mjs +582 -0
  78. package/dist/runtime-config-C0ggPkiK.mjs +40 -0
  79. package/dist/runtime-config-CQLtPPqY.d.mts +38 -0
  80. package/dist/scan-token-CNM9QVLY.d.mts +26 -0
  81. package/dist/scanning/index.d.mts +45 -0
  82. package/dist/scanning/index.mjs +228 -0
  83. package/dist/services/index.d.mts +8 -0
  84. package/dist/services/index.mjs +8 -0
  85. package/dist/services-_lLO4Xbl.mjs +1009 -0
  86. package/dist/stock-move-group-C0DqUfPY.mjs +88 -0
  87. package/dist/stock-package-BIarxbDS.d.mts +19 -0
  88. package/dist/stock-quant-CZhgvTu7.d.mts +41 -0
  89. package/dist/tenant-guard-6Ne-BILP.mjs +12 -0
  90. package/dist/tenant-isolation.error-D3OcKUdx.mjs +11 -0
  91. package/dist/trace.service-B9vAh-l-.d.mts +55 -0
  92. package/dist/trace.service-DE6Eh8_8.mjs +71 -0
  93. package/dist/traceability/index.d.mts +2 -0
  94. package/dist/traceability/index.mjs +2 -0
  95. package/dist/types/index.d.mts +2 -0
  96. package/dist/types/index.mjs +1 -0
  97. package/dist/unit-of-work.port-CWEkrDKu.d.mts +17 -0
  98. package/dist/valuation/index.d.mts +78 -0
  99. package/dist/valuation/index.mjs +103 -0
  100. package/dist/valuation-policy-Dco8c9Vw.d.mts +14 -0
  101. package/dist/virtual-locations-B9zXqPdi.d.mts +38 -0
  102. package/package.json +155 -0
@@ -0,0 +1,88 @@
1
+ import { t as FlowError } from "./base-MWBqRFM2.mjs";
2
+ import { o as MoveAlreadyPostedError } from "./domain-errors-D7S9ydNF.mjs";
3
+ import { r as isValidMoveTransition } from "./move-status-DkaFp2GD.mjs";
4
+ //#region src/domain/constants/virtual-locations.ts
5
+ /** Package defaults — used when the host app doesn't provide overrides. */
6
+ const DEFAULT_VIRTUAL_LOCATIONS = {
7
+ vendor: "vendor",
8
+ customer: "customer",
9
+ adjustment: "inventory_loss",
10
+ transit: "transit",
11
+ scrap: "scrap"
12
+ };
13
+ /**
14
+ * Resolve the final virtual location map by merging host-app overrides
15
+ * on top of the package defaults.
16
+ */
17
+ function resolveVirtualLocations(overrides) {
18
+ return Object.freeze({
19
+ ...DEFAULT_VIRTUAL_LOCATIONS,
20
+ ...overrides
21
+ });
22
+ }
23
+ /**
24
+ * Build the list of location IDs that should skip the negative-stock guard
25
+ * because they represent stock entering or leaving the system boundary.
26
+ */
27
+ function buildVirtualSourceTypes(locations) {
28
+ return Object.freeze([
29
+ locations.vendor,
30
+ locations.customer,
31
+ locations.adjustment,
32
+ locations.scrap,
33
+ locations.transit
34
+ ]);
35
+ }
36
+ //#endregion
37
+ //#region src/domain/errors/invalid-transition.error.ts
38
+ var InvalidTransitionError = class extends FlowError {
39
+ code = "INVALID_TRANSITION";
40
+ httpStatus = 422;
41
+ constructor(entityType, entityId, from, to) {
42
+ super(`Invalid transition for ${entityType} ${entityId}: ${from} → ${to}`);
43
+ this.entityType = entityType;
44
+ this.entityId = entityId;
45
+ this.from = from;
46
+ this.to = to;
47
+ }
48
+ };
49
+ //#endregion
50
+ //#region src/domain/errors/reservation-expired.error.ts
51
+ var ReservationExpiredError = class extends FlowError {
52
+ code = "RESERVATION_EXPIRED";
53
+ httpStatus = 410;
54
+ constructor(reservationId) {
55
+ super(`Reservation ${reservationId} has expired and can no longer be consumed`);
56
+ this.reservationId = reservationId;
57
+ }
58
+ };
59
+ //#endregion
60
+ //#region src/domain/entities/stock-move.ts
61
+ /**
62
+ * Validate a move status transition. Throws if invalid.
63
+ */
64
+ function assertMoveTransition(move, targetStatus) {
65
+ if (move.status === "done") throw new MoveAlreadyPostedError(move._id);
66
+ if (!isValidMoveTransition(move.status, targetStatus)) throw new InvalidTransitionError("StockMove", move._id, move.status, targetStatus);
67
+ }
68
+ //#endregion
69
+ //#region src/domain/entities/stock-move-group.ts
70
+ const MOVE_GROUP_STATUS_TRANSITIONS = {
71
+ draft: ["confirmed", "cancelled"],
72
+ confirmed: [
73
+ "allocated",
74
+ "in_progress",
75
+ "cancelled"
76
+ ],
77
+ allocated: ["in_progress", "cancelled"],
78
+ in_progress: [
79
+ "done",
80
+ "partially_done",
81
+ "cancelled"
82
+ ],
83
+ partially_done: ["done", "cancelled"],
84
+ done: [],
85
+ cancelled: []
86
+ };
87
+ //#endregion
88
+ export { DEFAULT_VIRTUAL_LOCATIONS as a, InvalidTransitionError as i, assertMoveTransition as n, buildVirtualSourceTypes as o, ReservationExpiredError as r, resolveVirtualLocations as s, MOVE_GROUP_STATUS_TRANSITIONS as t };
@@ -0,0 +1,19 @@
1
+ //#region src/domain/entities/stock-package.d.ts
2
+ interface StockPackage {
3
+ _id: string;
4
+ organizationId: string;
5
+ name: string;
6
+ barcode: string;
7
+ packageTypeId?: string;
8
+ parentPackageId?: string;
9
+ locationId?: string;
10
+ destinationLocationId?: string;
11
+ baseWeight?: number;
12
+ maxWeight?: number;
13
+ packageUse: 'disposable' | 'reusable';
14
+ metadata?: Record<string, unknown>;
15
+ createdAt?: Date;
16
+ updatedAt?: Date;
17
+ }
18
+ //#endregion
19
+ export { StockPackage as t };
@@ -0,0 +1,41 @@
1
+ import { i as StockStatus } from "./index-CulWO137.mjs";
2
+
3
+ //#region src/domain/entities/stock-quant.d.ts
4
+ /**
5
+ * Materialized stock balance per unique identity tuple.
6
+ * Derived from move history, not the source of truth.
7
+ *
8
+ * Unique identity: { organizationId, skuRef, locationId, lotId, ownerRef }
9
+ */
10
+ interface StockQuant {
11
+ _id: string;
12
+ organizationId: string;
13
+ skuRef: string;
14
+ locationId: string;
15
+ lotId?: string;
16
+ ownerRef?: string;
17
+ stockStatus: StockStatus;
18
+ quantityOnHand: number;
19
+ quantityReserved: number;
20
+ quantityAvailable: number;
21
+ quantityIncoming?: number;
22
+ quantityOutgoing?: number;
23
+ inDate: Date;
24
+ lotExpiresAt?: Date;
25
+ unitCost?: number;
26
+ lastMovementAt?: Date;
27
+ createdAt?: Date;
28
+ updatedAt?: Date;
29
+ }
30
+ /**
31
+ * The 5-field unique identity for a quant.
32
+ */
33
+ interface QuantIdentity {
34
+ organizationId: string;
35
+ skuRef: string;
36
+ locationId: string;
37
+ lotId?: string;
38
+ ownerRef?: string;
39
+ }
40
+ //#endregion
41
+ export { StockQuant as n, QuantIdentity as t };
@@ -0,0 +1,12 @@
1
+ import { t as TenantIsolationError } from "./tenant-isolation.error-D3OcKUdx.mjs";
2
+ //#region src/utils/tenant-guard.ts
3
+ /**
4
+ * Assert that the given context carries a valid organizationId.
5
+ * Must be called at the entry point of every service method to enforce
6
+ * tenant isolation before any database access.
7
+ */
8
+ function assertTenantContext(ctx) {
9
+ if (!ctx.organizationId) throw new TenantIsolationError();
10
+ }
11
+ //#endregion
12
+ export { assertTenantContext as t };
@@ -0,0 +1,11 @@
1
+ import { t as FlowError } from "./base-MWBqRFM2.mjs";
2
+ //#region src/domain/errors/tenant-isolation.error.ts
3
+ var TenantIsolationError = class extends FlowError {
4
+ code = "TENANT_ISOLATION";
5
+ httpStatus = 403;
6
+ constructor(message = "Tenant isolation violation: organizationId mismatch or missing") {
7
+ super(message);
8
+ }
9
+ };
10
+ //#endregion
11
+ export { TenantIsolationError as t };
@@ -0,0 +1,55 @@
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 StockLot, t as LotPort } from "./lot.port-ChsmvZqs.mjs";
4
+ import { r as QuantPort } from "./quant.port-BBa66PBT.mjs";
5
+
6
+ //#region src/traceability/trace.service.d.ts
7
+ interface TraceResult {
8
+ lot: StockLot;
9
+ currentLocations: Array<{
10
+ locationId: string;
11
+ quantity: number;
12
+ }>;
13
+ movementHistory: StockMove[];
14
+ totalQuantity: number;
15
+ }
16
+ interface RecallResult {
17
+ lot: StockLot;
18
+ affectedLocations: Array<{
19
+ locationId: string;
20
+ quantity: number;
21
+ }>;
22
+ shippedMoves: StockMove[];
23
+ totalInWarehouse: number;
24
+ totalShipped: number;
25
+ }
26
+ /**
27
+ * TraceService — lot and serial traceability queries.
28
+ * Aggregates data from lots, moves, and quants to produce full trace and recall reports.
29
+ */
30
+ declare class TraceService {
31
+ private lotPort;
32
+ private movePort;
33
+ private quantPort;
34
+ constructor(lotPort: LotPort, movePort: MovePort, quantPort: QuantPort);
35
+ /**
36
+ * Trace a lot: find current locations, quantities, and full movement history.
37
+ */
38
+ traceLot(lotCode: string, skuRef: string, ctx: FlowContext): Promise<TraceResult | null>;
39
+ /**
40
+ * Trace a serial: identical to traceLot but resolves by serial code.
41
+ */
42
+ traceSerial(serialCode: string, skuRef: string, ctx: FlowContext): Promise<TraceResult | null>;
43
+ /**
44
+ * Recall a lot: determine affected warehouse locations and shipments to external locations.
45
+ */
46
+ recallLot(lotCode: string, skuRef: string, ctx: FlowContext): Promise<RecallResult | null>;
47
+ private buildTrace;
48
+ /**
49
+ * Find all moves that reference a lot — either through trackingAssignments.lotId
50
+ * or via the move-level filter.
51
+ */
52
+ private findMovesForLot;
53
+ }
54
+ //#endregion
55
+ export { TraceResult as n, TraceService as r, RecallResult as t };
@@ -0,0 +1,71 @@
1
+ //#region src/traceability/trace.service.ts
2
+ /**
3
+ * TraceService — lot and serial traceability queries.
4
+ * Aggregates data from lots, moves, and quants to produce full trace and recall reports.
5
+ */
6
+ var TraceService = class {
7
+ constructor(lotPort, movePort, quantPort) {
8
+ this.lotPort = lotPort;
9
+ this.movePort = movePort;
10
+ this.quantPort = quantPort;
11
+ }
12
+ /**
13
+ * Trace a lot: find current locations, quantities, and full movement history.
14
+ */
15
+ async traceLot(lotCode, skuRef, ctx) {
16
+ const lot = await this.lotPort.findByCode(lotCode, skuRef, ctx);
17
+ if (!lot) return null;
18
+ return this.buildTrace(lot, ctx);
19
+ }
20
+ /**
21
+ * Trace a serial: identical to traceLot but resolves by serial code.
22
+ */
23
+ async traceSerial(serialCode, skuRef, ctx) {
24
+ const lot = await this.lotPort.findBySerial(serialCode, skuRef, ctx);
25
+ if (!lot) return null;
26
+ return this.buildTrace(lot, ctx);
27
+ }
28
+ /**
29
+ * Recall a lot: determine affected warehouse locations and shipments to external locations.
30
+ */
31
+ async recallLot(lotCode, skuRef, ctx) {
32
+ const lot = await this.lotPort.findByCode(lotCode, skuRef, ctx);
33
+ if (!lot) return null;
34
+ const [quants, moves] = await Promise.all([this.quantPort.findMany({ lotId: lot._id }, ctx), this.findMovesForLot(lot._id, ctx)]);
35
+ const affectedLocations = quants.filter((q) => q.quantityOnHand > 0).map((q) => ({
36
+ locationId: q.locationId,
37
+ quantity: q.quantityOnHand
38
+ }));
39
+ const totalInWarehouse = affectedLocations.reduce((sum, l) => sum + l.quantity, 0);
40
+ const shippedMoves = moves.filter((m) => m.operationType === "shipment");
41
+ return {
42
+ lot,
43
+ affectedLocations,
44
+ shippedMoves,
45
+ totalInWarehouse,
46
+ totalShipped: shippedMoves.reduce((sum, m) => sum + (m.quantityDone ?? m.quantityPlanned), 0)
47
+ };
48
+ }
49
+ async buildTrace(lot, ctx) {
50
+ const [quants, moves] = await Promise.all([this.quantPort.findMany({ lotId: lot._id }, ctx), this.findMovesForLot(lot._id, ctx)]);
51
+ const currentLocations = quants.filter((q) => q.quantityOnHand > 0).map((q) => ({
52
+ locationId: q.locationId,
53
+ quantity: q.quantityOnHand
54
+ }));
55
+ return {
56
+ lot,
57
+ currentLocations,
58
+ movementHistory: moves,
59
+ totalQuantity: currentLocations.reduce((sum, l) => sum + l.quantity, 0)
60
+ };
61
+ }
62
+ /**
63
+ * Find all moves that reference a lot — either through trackingAssignments.lotId
64
+ * or via the move-level filter.
65
+ */
66
+ async findMovesForLot(lotId, ctx) {
67
+ return this.movePort.findMany({ "trackingAssignments.lotId": lotId }, ctx);
68
+ }
69
+ };
70
+ //#endregion
71
+ export { TraceService as t };
@@ -0,0 +1,2 @@
1
+ import { n as TraceResult, r as TraceService, t as RecallResult } from "../trace.service-B9vAh-l-.mjs";
2
+ export { type RecallResult, type TraceResult, TraceService };
@@ -0,0 +1,2 @@
1
+ import { t as TraceService } from "../trace.service-DE6Eh8_8.mjs";
2
+ export { TraceService };
@@ -0,0 +1,2 @@
1
+ import { a as QueryOptions, i as PaginatedResult, n as FlowMode, r as ListQuery, t as FlowContext } from "../index-DFF0GJ4J.mjs";
2
+ export { FlowContext, FlowMode, ListQuery, PaginatedResult, QueryOptions };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ //#region src/domain/ports/unit-of-work.port.d.ts
2
+ /**
3
+ * Opaque transaction session handle.
4
+ * Services never inspect this. Repositories cast to mongoose.ClientSession.
5
+ */
6
+ interface TransactionSession {
7
+ readonly _brand: 'TransactionSession';
8
+ }
9
+ /**
10
+ * Unit of Work — provides transactional boundaries.
11
+ * All state mutations that must be atomic go through withTransaction().
12
+ */
13
+ interface UnitOfWork {
14
+ withTransaction<T>(fn: (session: TransactionSession) => Promise<T>): Promise<T>;
15
+ }
16
+ //#endregion
17
+ export { UnitOfWork as n, TransactionSession as t };
@@ -0,0 +1,78 @@
1
+ import { n as CostLayer } from "../cost-layer.port-iH9pvZqB.mjs";
2
+ import { a as FifoEngine, i as ConsumptionResult, n as CreateCostLayerInput, r as InventoryValuation, t as CostLayerService } from "../cost-layer.service-DWmo9dQz.mjs";
3
+
4
+ //#region src/valuation/fefo.engine.d.ts
5
+ declare class FefoEngine {
6
+ private fifoEngine;
7
+ consume(layers: CostLayer[], quantity: number): ConsumptionResult;
8
+ }
9
+ //#endregion
10
+ //#region src/valuation/landed-cost.service.d.ts
11
+ type AllocationMethod = 'by_value' | 'by_quantity' | 'by_weight' | 'by_volume' | 'equal';
12
+ interface LandedCostInput {
13
+ totalCost: number;
14
+ method: AllocationMethod;
15
+ items: Array<{
16
+ skuRef: string;
17
+ quantity: number;
18
+ value: number;
19
+ weight?: number;
20
+ volume?: number;
21
+ }>;
22
+ }
23
+ interface LandedCostAllocation {
24
+ items: Array<{
25
+ skuRef: string;
26
+ allocatedCost: number;
27
+ newUnitCost: number;
28
+ }>;
29
+ totalAllocated: number;
30
+ }
31
+ interface PostCapitalizeInput {
32
+ additionalCost: number;
33
+ method: AllocationMethod;
34
+ items: Array<{
35
+ skuRef: string;
36
+ currentQuantity: number;
37
+ quantitySold: number;
38
+ currentUnitCost: number;
39
+ originalReceiptQuantity: number;
40
+ value?: number;
41
+ weight?: number;
42
+ volume?: number;
43
+ }>;
44
+ }
45
+ interface PostCapitalizeItem {
46
+ skuRef: string;
47
+ allocatedCost: number;
48
+ additionalCostPerUnit: number;
49
+ newUnitCost: number;
50
+ cogsAdjustment: number;
51
+ remainingStockAdjustment: number;
52
+ }
53
+ interface PostCapitalizeResult {
54
+ items: PostCapitalizeItem[];
55
+ totalAdditionalCost: number;
56
+ totalCogsAdjustment: number;
57
+ totalRemainingStockAdjustment: number;
58
+ }
59
+ declare class LandedCostService {
60
+ allocate(input: LandedCostInput): LandedCostAllocation;
61
+ /**
62
+ * Post-capitalization — retroactively apply late cost invoices to already-received stock.
63
+ *
64
+ * When a freight/duty invoice arrives weeks after goods are received,
65
+ * this method distributes the cost across the original receipt items,
66
+ * recalculates unit costs, and separates the adjustment into:
67
+ * - COGS adjustment (for units already sold)
68
+ * - Remaining stock adjustment (for units still in inventory)
69
+ */
70
+ postCapitalize(input: PostCapitalizeInput): PostCapitalizeResult;
71
+ }
72
+ //#endregion
73
+ //#region src/valuation/wac.engine.d.ts
74
+ declare class WacEngine {
75
+ calculate(oldQty: number, oldCost: number, inQty: number, inCost: number): number;
76
+ }
77
+ //#endregion
78
+ export { type AllocationMethod, type ConsumptionResult, CostLayerService, type CreateCostLayerInput, FefoEngine, FifoEngine, type InventoryValuation, type LandedCostAllocation, type LandedCostInput, LandedCostService, WacEngine };
@@ -0,0 +1,103 @@
1
+ import { n as FefoEngine, r as FifoEngine, t as CostLayerService } from "../cost-layer.service-BQ1bs-XN.mjs";
2
+ //#region src/valuation/landed-cost.service.ts
3
+ var LandedCostService = class {
4
+ allocate(input) {
5
+ const { totalCost, method, items } = input;
6
+ let allocations;
7
+ switch (method) {
8
+ case "by_value": {
9
+ const total = items.reduce((s, i) => s + i.value, 0);
10
+ allocations = items.map((i) => total > 0 ? i.value / total * totalCost : 0);
11
+ break;
12
+ }
13
+ case "by_quantity": {
14
+ const total = items.reduce((s, i) => s + i.quantity, 0);
15
+ allocations = items.map((i) => total > 0 ? i.quantity / total * totalCost : 0);
16
+ break;
17
+ }
18
+ case "by_weight": {
19
+ const total = items.reduce((s, i) => s + (i.weight ?? 0), 0);
20
+ allocations = items.map((i) => total > 0 ? (i.weight ?? 0) / total * totalCost : 0);
21
+ break;
22
+ }
23
+ case "by_volume": {
24
+ const total = items.reduce((s, i) => s + (i.volume ?? 0), 0);
25
+ allocations = items.map((i) => total > 0 ? (i.volume ?? 0) / total * totalCost : 0);
26
+ break;
27
+ }
28
+ case "equal":
29
+ allocations = items.length > 0 ? items.map(() => totalCost / items.length) : [];
30
+ break;
31
+ default: allocations = items.map(() => 0);
32
+ }
33
+ const diff = totalCost - allocations.reduce((s, a) => s + a, 0);
34
+ if (allocations.length > 0 && Math.abs(diff) > .001) allocations[allocations.length - 1] += diff;
35
+ return {
36
+ items: items.map((item, i) => ({
37
+ skuRef: item.skuRef,
38
+ allocatedCost: Math.round(allocations[i] * 100) / 100,
39
+ newUnitCost: item.quantity > 0 ? Math.round((item.value + allocations[i]) / item.quantity * 100) / 100 : 0
40
+ })),
41
+ totalAllocated: totalCost
42
+ };
43
+ }
44
+ /**
45
+ * Post-capitalization — retroactively apply late cost invoices to already-received stock.
46
+ *
47
+ * When a freight/duty invoice arrives weeks after goods are received,
48
+ * this method distributes the cost across the original receipt items,
49
+ * recalculates unit costs, and separates the adjustment into:
50
+ * - COGS adjustment (for units already sold)
51
+ * - Remaining stock adjustment (for units still in inventory)
52
+ */
53
+ postCapitalize(input) {
54
+ const { additionalCost, method, items } = input;
55
+ const allocationItems = items.map((item) => ({
56
+ skuRef: item.skuRef,
57
+ quantity: item.originalReceiptQuantity,
58
+ value: item.currentUnitCost * item.originalReceiptQuantity,
59
+ weight: item.weight,
60
+ volume: item.volume
61
+ }));
62
+ const allocation = this.allocate({
63
+ totalCost: additionalCost,
64
+ method,
65
+ items: allocationItems
66
+ });
67
+ const resultItems = items.map((item, i) => {
68
+ const allocatedCost = allocation.items[i].allocatedCost;
69
+ const additionalCostPerUnit = item.originalReceiptQuantity > 0 ? Math.round(allocatedCost / item.originalReceiptQuantity * 100) / 100 : 0;
70
+ const newUnitCost = Math.round((item.currentUnitCost + additionalCostPerUnit) * 100) / 100;
71
+ const cogsAdjustment = Math.round(item.quantitySold * additionalCostPerUnit * 100) / 100;
72
+ const remainingStockAdjustment = Math.round((allocatedCost - cogsAdjustment) * 100) / 100;
73
+ return {
74
+ skuRef: item.skuRef,
75
+ allocatedCost,
76
+ additionalCostPerUnit,
77
+ newUnitCost,
78
+ cogsAdjustment,
79
+ remainingStockAdjustment
80
+ };
81
+ });
82
+ const totalSplit = resultItems.reduce((s, i) => s + i.cogsAdjustment + i.remainingStockAdjustment, 0);
83
+ const crossItemDiff = Math.round((additionalCost - totalSplit) * 100) / 100;
84
+ if (resultItems.length > 0 && Math.abs(crossItemDiff) > 0) resultItems[resultItems.length - 1].remainingStockAdjustment = Math.round((resultItems[resultItems.length - 1].remainingStockAdjustment + crossItemDiff) * 100) / 100;
85
+ return {
86
+ items: resultItems,
87
+ totalAdditionalCost: additionalCost,
88
+ totalCogsAdjustment: resultItems.reduce((s, i) => s + i.cogsAdjustment, 0),
89
+ totalRemainingStockAdjustment: resultItems.reduce((s, i) => s + i.remainingStockAdjustment, 0)
90
+ };
91
+ }
92
+ };
93
+ //#endregion
94
+ //#region src/valuation/wac.engine.ts
95
+ var WacEngine = class {
96
+ calculate(oldQty, oldCost, inQty, inCost) {
97
+ const totalQty = oldQty + inQty;
98
+ if (totalQty <= 0) return oldCost;
99
+ return Math.round((oldQty * oldCost + inQty * inCost) / totalQty * 1e4) / 1e4;
100
+ }
101
+ };
102
+ //#endregion
103
+ export { CostLayerService, FefoEngine, FifoEngine, LandedCostService, WacEngine };
@@ -0,0 +1,14 @@
1
+ //#region src/domain/policies/valuation-policy.d.ts
2
+ /**
3
+ * Valuation policy — config object, not a strategy interface.
4
+ * The valuation engine reads this.
5
+ */
6
+ interface ValuationPolicy {
7
+ method: 'wac' | 'fifo' | 'fefo' | 'specific' | 'standard';
8
+ scope: 'organization' | 'node' | 'sku';
9
+ landedCostAllocation: 'by_value' | 'by_quantity' | 'by_weight' | 'by_volume' | 'equal';
10
+ negativeCostHandling: 'freeze_last' | 'zero' | 'error';
11
+ interNodeTransferCost: 'sender_cost' | 'standard_cost' | 'transfer_price';
12
+ }
13
+ //#endregion
14
+ export { ValuationPolicy as t };
@@ -0,0 +1,38 @@
1
+ //#region src/domain/constants/virtual-locations.d.ts
2
+ /**
3
+ * Virtual location sentinels used by Flow services.
4
+ *
5
+ * These are string IDs that represent logical locations outside the
6
+ * physical warehouse graph. They appear in StockMove.sourceLocationId
7
+ * and StockMove.destinationLocationId but do not correspond to real
8
+ * Location documents.
9
+ *
10
+ * Host apps override these defaults via `FlowConfig.virtualLocations`.
11
+ */
12
+ /** Shape of the virtual location map — every key has a string sentinel value. */
13
+ interface VirtualLocationMap {
14
+ /** Source location for inbound receipts from external vendors. */
15
+ vendor: string;
16
+ /** Source location for customer returns / destination for outbound shipments. */
17
+ customer: string;
18
+ /** Catch-all for inventory adjustments (shrinkage, found stock, count reconciliation). */
19
+ adjustment: string;
20
+ /** Transit location for in-flight inter-branch transfers. */
21
+ transit: string;
22
+ /** Scrap / write-off destination. */
23
+ scrap: string;
24
+ }
25
+ /** Package defaults — used when the host app doesn't provide overrides. */
26
+ declare const DEFAULT_VIRTUAL_LOCATIONS: VirtualLocationMap;
27
+ /**
28
+ * Resolve the final virtual location map by merging host-app overrides
29
+ * on top of the package defaults.
30
+ */
31
+ declare function resolveVirtualLocations(overrides?: Partial<VirtualLocationMap>): VirtualLocationMap;
32
+ /**
33
+ * Build the list of location IDs that should skip the negative-stock guard
34
+ * because they represent stock entering or leaving the system boundary.
35
+ */
36
+ declare function buildVirtualSourceTypes(locations: VirtualLocationMap): readonly string[];
37
+ //#endregion
38
+ export { resolveVirtualLocations as i, VirtualLocationMap as n, buildVirtualSourceTypes as r, DEFAULT_VIRTUAL_LOCATIONS as t };