-
-
Notifications
You must be signed in to change notification settings - Fork 932
/
default-stock-location-strategy.ts
107 lines (97 loc) · 4.01 KB
/
default-stock-location-strategy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { ID } from '@vendure/common/lib/shared-types';
import { RequestContext } from '../../api/common/request-context';
import { Injector } from '../../common/injector';
import { idsAreEqual } from '../../common/utils';
import { TransactionalConnection } from '../../connection/transactional-connection';
import { OrderLine } from '../../entity/order-line/order-line.entity';
import { StockLevel } from '../../entity/stock-level/stock-level.entity';
import { StockLocation } from '../../entity/stock-location/stock-location.entity';
import { Allocation } from '../../entity/stock-movement/allocation.entity';
import { AvailableStock, LocationWithQuantity, StockLocationStrategy } from './stock-location-strategy';
/**
* @description
* The DefaultStockLocationStrategy is the default implementation of the {@link StockLocationStrategy}.
* It assumes only a single StockLocation and that all stock is allocated from that location.
*
* @docsCategory products & stock
* @since 2.0.0
*/
export class DefaultStockLocationStrategy implements StockLocationStrategy {
protected connection: TransactionalConnection;
init(injector: Injector) {
this.connection = injector.get(TransactionalConnection);
}
getAvailableStock(ctx: RequestContext, productVariantId: ID, stockLevels: StockLevel[]): AvailableStock {
let stockOnHand = 0;
let stockAllocated = 0;
for (const stockLevel of stockLevels) {
stockOnHand += stockLevel.stockOnHand;
stockAllocated += stockLevel.stockAllocated;
}
return { stockOnHand, stockAllocated };
}
forAllocation(
ctx: RequestContext,
stockLocations: StockLocation[],
orderLine: OrderLine,
quantity: number,
): LocationWithQuantity[] | Promise<LocationWithQuantity[]> {
return [{ location: stockLocations[0], quantity }];
}
async forCancellation(
ctx: RequestContext,
stockLocations: StockLocation[],
orderLine: OrderLine,
quantity: number,
): Promise<LocationWithQuantity[]> {
return this.getLocationsBasedOnAllocations(ctx, stockLocations, orderLine, quantity);
}
async forRelease(
ctx: RequestContext,
stockLocations: StockLocation[],
orderLine: OrderLine,
quantity: number,
): Promise<LocationWithQuantity[]> {
return this.getLocationsBasedOnAllocations(ctx, stockLocations, orderLine, quantity);
}
async forSale(
ctx: RequestContext,
stockLocations: StockLocation[],
orderLine: OrderLine,
quantity: number,
): Promise<LocationWithQuantity[]> {
return this.getLocationsBasedOnAllocations(ctx, stockLocations, orderLine, quantity);
}
private async getLocationsBasedOnAllocations(
ctx: RequestContext,
stockLocations: StockLocation[],
orderLine: OrderLine,
quantity: number,
) {
const allocations = await this.connection.getRepository(ctx, Allocation).find({
where: {
orderLine: { id: orderLine.id },
},
});
let unallocated = quantity;
const quantityByLocationId = new Map<ID, number>();
for (const allocation of allocations) {
if (unallocated <= 0) {
break;
}
const qtyAtLocation = quantityByLocationId.get(allocation.stockLocationId);
const qtyToAdd = Math.min(allocation.quantity, unallocated);
if (qtyAtLocation != null) {
quantityByLocationId.set(allocation.stockLocationId, qtyAtLocation + qtyToAdd);
} else {
quantityByLocationId.set(allocation.stockLocationId, qtyToAdd);
}
unallocated -= qtyToAdd;
}
return [...quantityByLocationId.entries()].map(([locationId, qty]) => ({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
location: stockLocations.find(l => idsAreEqual(l.id, locationId))!,
quantity: qty,
}));
}
}