@unchainedshop/utils 3.0.0-alpha2 → 3.0.0-alpha4
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/lib/calculation.d.ts +1 -1
- package/lib/calculation.d.ts.map +1 -1
- package/lib/director/BaseAdapter.d.ts +10 -1
- package/lib/director/BaseAdapter.d.ts.map +1 -1
- package/lib/director/BaseAdapter.js.map +1 -1
- package/lib/director/BaseDirector.d.ts +9 -1
- package/lib/director/BaseDirector.d.ts.map +1 -1
- package/lib/director/BaseDirector.js.map +1 -1
- package/lib/director/BaseDiscountAdapter.d.ts +36 -1
- package/lib/director/BaseDiscountAdapter.d.ts.map +1 -1
- package/lib/director/BaseDiscountAdapter.js.map +1 -1
- package/lib/director/BaseDiscountDirector.d.ts +11 -1
- package/lib/director/BaseDiscountDirector.d.ts.map +1 -1
- package/lib/director/BaseDiscountDirector.js.map +1 -1
- package/lib/director/BasePricingAdapter.d.ts +45 -2
- package/lib/director/BasePricingAdapter.d.ts.map +1 -1
- package/lib/director/BasePricingAdapter.js.map +1 -1
- package/lib/director/BasePricingDirector.d.ts +14 -1
- package/lib/director/BasePricingDirector.d.ts.map +1 -1
- package/lib/director/BasePricingDirector.js.map +1 -1
- package/lib/director/BasePricingSheet.d.ts +45 -1
- package/lib/director/BasePricingSheet.d.ts.map +1 -1
- package/lib/director/BasePricingSheet.js.map +1 -1
- package/lib/locale-helpers.d.ts +8 -6
- package/lib/locale-helpers.d.ts.map +1 -1
- package/lib/locale-helpers.js +8 -10
- package/lib/locale-helpers.js.map +1 -1
- package/lib/object-invert.d.ts +1 -1
- package/lib/object-invert.d.ts.map +1 -1
- package/lib/object-invert.js.map +1 -1
- package/lib/utils-index.d.ts +17 -8
- package/lib/utils-index.d.ts.map +1 -1
- package/lib/utils-index.js +12 -8
- package/lib/utils-index.js.map +1 -1
- package/package.json +5 -7
- package/src/calculation.test.ts +24 -32
- package/src/calculation.ts +1 -1
- package/src/director/BaseAdapter.ts +13 -1
- package/src/director/BaseDirector.ts +8 -1
- package/src/director/BaseDiscountAdapter.ts +32 -1
- package/src/director/BaseDiscountDirector.ts +15 -2
- package/src/director/BasePricingAdapter.ts +59 -11
- package/src/director/BasePricingDirector.ts +36 -9
- package/src/director/BasePricingSheet.ts +45 -5
- package/src/director/base-director.test.ts +55 -66
- package/src/director/base-discount-adapter.test.ts +22 -25
- package/src/director/base-price-sheet.test.ts +50 -56
- package/src/locale-helpers.ts +17 -16
- package/src/object-invert.ts +1 -1
- package/src/utils-index.test.ts +47 -57
- package/src/utils-index.ts +20 -8
- package/lib/schemas/AddressSchema.d.ts +0 -3
- package/lib/schemas/AddressSchema.d.ts.map +0 -1
- package/lib/schemas/AddressSchema.js +0 -13
- package/lib/schemas/AddressSchema.js.map +0 -1
- package/lib/schemas/ContactSchema.d.ts +0 -3
- package/lib/schemas/ContactSchema.d.ts.map +0 -1
- package/lib/schemas/ContactSchema.js +0 -6
- package/lib/schemas/ContactSchema.js.map +0 -1
- package/lib/schemas/UsersSchema.d.ts +0 -5
- package/lib/schemas/UsersSchema.d.ts.map +0 -1
- package/lib/schemas/UsersSchema.js +0 -52
- package/lib/schemas/UsersSchema.js.map +0 -1
- package/lib/schemas/commonSchemaFields.d.ts +0 -35
- package/lib/schemas/commonSchemaFields.d.ts.map +0 -1
- package/lib/schemas/commonSchemaFields.js +0 -28
- package/lib/schemas/commonSchemaFields.js.map +0 -1
- package/lib/schemas/index.d.ts +0 -43
- package/lib/schemas/index.d.ts.map +0 -1
- package/lib/schemas/index.js +0 -14
- package/lib/schemas/index.js.map +0 -1
- package/src/schemas/AddressSchema.ts +0 -16
- package/src/schemas/ContactSchema.ts +0 -9
- package/src/schemas/UsersSchema.ts +0 -67
- package/src/schemas/commonSchemaFields.ts +0 -29
- package/src/schemas/index.ts +0 -18
package/src/calculation.test.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
applyRate,
|
|
3
|
+
resolveRatioAndTaxDivisorForPricingSheet,
|
|
4
|
+
roundToNext,
|
|
5
|
+
resolveAmountAndTax,
|
|
6
|
+
applyDiscountToMultipleShares,
|
|
7
|
+
calculateAmountToSplit,
|
|
8
|
+
} from './calculation';
|
|
2
9
|
|
|
3
10
|
describe('roundToNext', () => {
|
|
4
11
|
it('rounds to the next multiple of precision correctly when the value is positive', () => {
|
|
@@ -20,10 +27,9 @@ describe('roundToNext', () => {
|
|
|
20
27
|
it('returns the same value when the precision is 1', () => {
|
|
21
28
|
expect(roundToNext(5, 1)).toBe(5);
|
|
22
29
|
});
|
|
23
|
-
|
|
24
30
|
});
|
|
25
31
|
|
|
26
|
-
describe(
|
|
32
|
+
describe('resolveRatioAndTaxDivisorForPricingSheet', () => {
|
|
27
33
|
it('total is 0 and pricing is provided', () => {
|
|
28
34
|
const pricing: any = {
|
|
29
35
|
taxSum: () => 10,
|
|
@@ -63,11 +69,9 @@ describe("resolveRatioAndTaxDivisorForPricingSheet", () => {
|
|
|
63
69
|
const result = resolveRatioAndTaxDivisorForPricingSheet(pricing, 20);
|
|
64
70
|
expect(result).toEqual({ ratio: 1, taxDivisor: 1 });
|
|
65
71
|
});
|
|
72
|
+
});
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
describe("resolveAmountAndTax", () => {
|
|
74
|
+
describe('resolveAmountAndTax', () => {
|
|
71
75
|
it('ratio is 0 and taxDivisor is 0', () => {
|
|
72
76
|
const result = resolveAmountAndTax({ ratio: 0, taxDivisor: 0 }, 100);
|
|
73
77
|
expect(result).toEqual([0, 0]);
|
|
@@ -83,7 +87,6 @@ describe("resolveAmountAndTax", () => {
|
|
|
83
87
|
expect(result).toEqual([0, 0]);
|
|
84
88
|
});
|
|
85
89
|
|
|
86
|
-
|
|
87
90
|
it('ratio and taxDivisor are not 0', () => {
|
|
88
91
|
const result = resolveAmountAndTax({ ratio: 0.5, taxDivisor: 2 }, 100);
|
|
89
92
|
expect(result).toEqual([50, 25]);
|
|
@@ -93,9 +96,9 @@ describe("resolveAmountAndTax", () => {
|
|
|
93
96
|
const result = resolveAmountAndTax({ ratio: Number.NaN, taxDivisor: Number.NaN }, 100);
|
|
94
97
|
expect(result).toEqual([0, 0]);
|
|
95
98
|
});
|
|
96
|
-
})
|
|
99
|
+
});
|
|
97
100
|
|
|
98
|
-
describe(
|
|
101
|
+
describe('resolveAmountAndTax', () => {
|
|
99
102
|
it('shares are empty', () => {
|
|
100
103
|
const result = applyDiscountToMultipleShares([], 100);
|
|
101
104
|
expect(result).toEqual([0, 0]);
|
|
@@ -115,12 +118,12 @@ describe("resolveAmountAndTax", () => {
|
|
|
115
118
|
const result = applyDiscountToMultipleShares(shares, 100);
|
|
116
119
|
expect(result).toEqual([70, 31.666666666666664]);
|
|
117
120
|
});
|
|
118
|
-
})
|
|
121
|
+
});
|
|
119
122
|
|
|
120
123
|
describe('applyRate', () => {
|
|
121
124
|
it('applies the rate correctly when a rate is provided', () => {
|
|
122
125
|
const configuration = {
|
|
123
|
-
rate: 0.1
|
|
126
|
+
rate: 0.1,
|
|
124
127
|
};
|
|
125
128
|
const amount = 100;
|
|
126
129
|
expect(applyRate(configuration, amount)).toBe(10);
|
|
@@ -128,7 +131,7 @@ describe('applyRate', () => {
|
|
|
128
131
|
|
|
129
132
|
it('applies the fixed rate correctly when a fixed rate is provided', () => {
|
|
130
133
|
const configuration = {
|
|
131
|
-
fixedRate: 50
|
|
134
|
+
fixedRate: 50,
|
|
132
135
|
};
|
|
133
136
|
const amount = 100;
|
|
134
137
|
expect(applyRate(configuration, amount)).toBe(50);
|
|
@@ -136,7 +139,7 @@ describe('applyRate', () => {
|
|
|
136
139
|
|
|
137
140
|
it('applies the fixed rate correctly when a fixed rate is provided and the amount is less than fixed rate', () => {
|
|
138
141
|
const configuration = {
|
|
139
|
-
fixedRate: 150
|
|
142
|
+
fixedRate: 150,
|
|
140
143
|
};
|
|
141
144
|
const amount = 100;
|
|
142
145
|
expect(applyRate(configuration, amount)).toBe(100);
|
|
@@ -144,48 +147,43 @@ describe('applyRate', () => {
|
|
|
144
147
|
|
|
145
148
|
it('applies the rate correctly when rate is less than or equal to 0', () => {
|
|
146
149
|
const configuration = {
|
|
147
|
-
rate: -0.1
|
|
150
|
+
rate: -0.1,
|
|
148
151
|
};
|
|
149
152
|
const amount = 100;
|
|
150
153
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
151
154
|
});
|
|
152
155
|
it('applies the rate correctly when only rate is provided and amount is negative', () => {
|
|
153
156
|
const configuration = {
|
|
154
|
-
rate: 0.1
|
|
157
|
+
rate: 0.1,
|
|
155
158
|
};
|
|
156
159
|
const amount = -100;
|
|
157
160
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
158
161
|
});
|
|
159
162
|
|
|
160
|
-
|
|
161
163
|
it('applies the rate correctly when only rate is provided and amount is negative', () => {
|
|
162
164
|
const configuration = {
|
|
163
|
-
rate: 0.1
|
|
165
|
+
rate: 0.1,
|
|
164
166
|
};
|
|
165
167
|
const amount = -100;
|
|
166
168
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
167
169
|
});
|
|
168
170
|
|
|
169
|
-
|
|
170
171
|
it('applies the rate correctly when only rate is provided and amount is negative', () => {
|
|
171
172
|
const configuration = {
|
|
172
|
-
rate: 0.1
|
|
173
|
+
rate: 0.1,
|
|
173
174
|
};
|
|
174
175
|
const amount = -100;
|
|
175
176
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
176
177
|
});
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
|
|
180
179
|
it('applies the rate correctly when only rate is provided and amount is negative', () => {
|
|
181
180
|
const configuration = {
|
|
182
|
-
rate: 0.1
|
|
181
|
+
rate: 0.1,
|
|
183
182
|
};
|
|
184
183
|
const amount = -100;
|
|
185
184
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
186
185
|
});
|
|
187
186
|
|
|
188
|
-
|
|
189
187
|
it('returns 0 when rate and fixed rate are not provided', () => {
|
|
190
188
|
const configuration = {};
|
|
191
189
|
const amount = 100;
|
|
@@ -194,7 +192,7 @@ describe('applyRate', () => {
|
|
|
194
192
|
|
|
195
193
|
it('applies the rate correctly when only rate is provided and amount is negative', () => {
|
|
196
194
|
const configuration = {
|
|
197
|
-
rate: 0.1
|
|
195
|
+
rate: 0.1,
|
|
198
196
|
};
|
|
199
197
|
const amount = -100;
|
|
200
198
|
expect(applyRate(configuration, amount)).toBe(-10);
|
|
@@ -205,11 +203,8 @@ describe('applyRate', () => {
|
|
|
205
203
|
const amount = 100;
|
|
206
204
|
expect(applyRate(configuration, amount)).toBe(0);
|
|
207
205
|
});
|
|
208
|
-
|
|
209
|
-
|
|
210
206
|
});
|
|
211
207
|
|
|
212
|
-
|
|
213
208
|
describe('calculateAmountToSplit', () => {
|
|
214
209
|
it('calculates the correct amount to split when using a rate', () => {
|
|
215
210
|
const configuration = {
|
|
@@ -235,7 +230,6 @@ describe('calculateAmountToSplit', () => {
|
|
|
235
230
|
expect(calculateAmountToSplit(configuration, amount)).toBe(0);
|
|
236
231
|
});
|
|
237
232
|
|
|
238
|
-
|
|
239
233
|
it('returns 0 when amount is negative', () => {
|
|
240
234
|
const configuration = {
|
|
241
235
|
rate: 0.1,
|
|
@@ -251,6 +245,4 @@ describe('calculateAmountToSplit', () => {
|
|
|
251
245
|
const amount = 1000;
|
|
252
246
|
expect(calculateAmountToSplit(configuration, amount)).toBe(0);
|
|
253
247
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
});
|
|
248
|
+
});
|
package/src/calculation.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IBasePricingSheet, PricingCalculation } from '
|
|
1
|
+
import { IBasePricingSheet, PricingCalculation } from './director/BasePricingSheet.js';
|
|
2
2
|
|
|
3
3
|
export const roundToNext = (value: number, precision: number) =>
|
|
4
4
|
Math.ceil(value / precision) * precision;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import { IBaseAdapter } from '@unchainedshop/types/common.js';
|
|
2
1
|
import { log, LogLevel } from '@unchainedshop/logger';
|
|
3
2
|
|
|
3
|
+
export interface IBaseAdapter {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
version: string;
|
|
7
|
+
log: (
|
|
8
|
+
message: string,
|
|
9
|
+
options?: {
|
|
10
|
+
level?: LogLevel;
|
|
11
|
+
[x: string]: any;
|
|
12
|
+
},
|
|
13
|
+
) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
4
16
|
export const BaseAdapter: Omit<IBaseAdapter, 'key' | 'label' | 'version'> = {
|
|
5
17
|
log(message: string, { level = LogLevel.Debug, ...options } = {}) {
|
|
6
18
|
return log(message, { level, ...options });
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import { IBaseAdapter, IBaseDirector } from '@unchainedshop/types/common.js';
|
|
2
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
+
import { IBaseAdapter } from './BaseAdapter.js';
|
|
3
|
+
|
|
4
|
+
export interface IBaseDirector<Adapter extends IBaseAdapter> {
|
|
5
|
+
getAdapters: (options?: { adapterFilter?: (adapter: Adapter) => boolean }) => Array<Adapter>;
|
|
6
|
+
getAdapter: (key: string) => Adapter;
|
|
7
|
+
registerAdapter: (A: Adapter) => void;
|
|
8
|
+
unregisterAdapter: (key: string) => boolean;
|
|
9
|
+
}
|
|
3
10
|
|
|
4
11
|
export const BaseDirector = <AdapterType extends IBaseAdapter>(
|
|
5
12
|
directorName: string,
|
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import { log, LogLevel } from '@unchainedshop/logger';
|
|
2
|
+
import { UnchainedCore } from '@unchainedshop/core';
|
|
3
|
+
import { IBaseAdapter } from './BaseAdapter.js';
|
|
4
|
+
import { IPricingSheet, PricingCalculation } from './BasePricingSheet.js';
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
export interface DiscountContext {
|
|
7
|
+
code?: string;
|
|
8
|
+
orderDiscount?: { _id?: string; orderId: string };
|
|
9
|
+
order?: { _id?: string; userId: string };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type IDiscountAdapter<DiscountConfiguration> = IBaseAdapter & {
|
|
13
|
+
orderIndex: number;
|
|
14
|
+
|
|
15
|
+
isManualAdditionAllowed: (code: string) => Promise<boolean>;
|
|
16
|
+
isManualRemovalAllowed: () => Promise<boolean>;
|
|
17
|
+
|
|
18
|
+
actions: (params: {
|
|
19
|
+
context: DiscountContext & UnchainedCore;
|
|
20
|
+
}) => Promise<DiscountAdapterActions<DiscountConfiguration>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface DiscountAdapterActions<DiscountConfiguration> {
|
|
24
|
+
isValidForSystemTriggering: () => Promise<boolean>;
|
|
25
|
+
isValidForCodeTriggering: (params: { code: string }) => Promise<boolean>;
|
|
26
|
+
|
|
27
|
+
discountForPricingAdapterKey: (params: {
|
|
28
|
+
pricingAdapterKey: string;
|
|
29
|
+
calculationSheet: IPricingSheet<PricingCalculation>;
|
|
30
|
+
}) => DiscountConfiguration;
|
|
31
|
+
|
|
32
|
+
reserve: (params: { code: string }) => Promise<any>;
|
|
33
|
+
release: () => Promise<void>;
|
|
34
|
+
}
|
|
4
35
|
|
|
5
36
|
export const BaseDiscountAdapter: Omit<IDiscountAdapter<unknown>, 'key' | 'label' | 'version'> = {
|
|
6
37
|
orderIndex: 0,
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { IDiscountAdapter, IDiscountDirector } from '@unchainedshop/types/discount.js';
|
|
2
1
|
import { log } from '@unchainedshop/logger';
|
|
3
|
-
import { BaseDirector } from './BaseDirector.js';
|
|
2
|
+
import { BaseDirector, IBaseDirector } from './BaseDirector.js';
|
|
3
|
+
import { DiscountContext, IDiscountAdapter } from './BaseDiscountAdapter.js';
|
|
4
|
+
import { UnchainedCore } from '@unchainedshop/core';
|
|
5
|
+
|
|
6
|
+
export type IDiscountDirector<DiscountConfiguration> = IBaseDirector<
|
|
7
|
+
IDiscountAdapter<DiscountConfiguration>
|
|
8
|
+
> & {
|
|
9
|
+
actions: (
|
|
10
|
+
discountContext: DiscountContext,
|
|
11
|
+
unchainedAPI: UnchainedCore,
|
|
12
|
+
) => Promise<{
|
|
13
|
+
resolveDiscountKeyFromStaticCode: (params: { code: string }) => Promise<string | null>;
|
|
14
|
+
findSystemDiscounts: () => Promise<Array<string>>;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
4
17
|
|
|
5
18
|
export const BaseDiscountDirector = <DiscountConfigurationType>(
|
|
6
19
|
directorName: string,
|
|
@@ -1,16 +1,64 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
IPricingSheet,
|
|
4
|
-
IPricingAdapter,
|
|
5
|
-
PricingCalculation,
|
|
6
|
-
IPricingAdapterActions,
|
|
7
|
-
} from '@unchainedshop/types/pricing.js';
|
|
1
|
+
import { UnchainedCore } from '@unchainedshop/core';
|
|
2
|
+
import { User } from '@unchainedshop/core-users';
|
|
8
3
|
import { log, LogLevel } from '@unchainedshop/logger';
|
|
4
|
+
import { IPricingSheet, PricingCalculation } from './BasePricingSheet.js';
|
|
5
|
+
import { IBaseAdapter } from './BaseAdapter.js';
|
|
6
|
+
import { Discount } from './BasePricingDirector.js';
|
|
7
|
+
|
|
8
|
+
type Order = { _id?: string };
|
|
9
|
+
type OrderDiscount = { _id?: string };
|
|
10
|
+
|
|
11
|
+
interface IDiscountableItem {
|
|
12
|
+
_id?: string;
|
|
13
|
+
orderId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BasePricingAdapterContext extends UnchainedCore {
|
|
17
|
+
order?: Order;
|
|
18
|
+
user: User;
|
|
19
|
+
discounts: Array<OrderDiscount>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type BasePricingContext =
|
|
23
|
+
| {
|
|
24
|
+
order?: Order;
|
|
25
|
+
user?: User;
|
|
26
|
+
discounts?: Array<OrderDiscount>;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
item: IDiscountableItem;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export interface IPricingAdapterActions<
|
|
33
|
+
Calculation extends PricingCalculation,
|
|
34
|
+
PricingAdapterContext extends BasePricingAdapterContext,
|
|
35
|
+
> {
|
|
36
|
+
calculate: () => Promise<Array<Calculation>>;
|
|
37
|
+
getCalculation: () => Array<Calculation>;
|
|
38
|
+
getContext: () => PricingAdapterContext;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type IPricingAdapter<
|
|
42
|
+
PricingAdapterContext extends BasePricingAdapterContext,
|
|
43
|
+
Calculation extends PricingCalculation,
|
|
44
|
+
Sheet extends IPricingSheet<Calculation>,
|
|
45
|
+
DiscountConfiguration = unknown,
|
|
46
|
+
> = IBaseAdapter & {
|
|
47
|
+
orderIndex: number;
|
|
48
|
+
|
|
49
|
+
isActivatedFor: (context: PricingAdapterContext) => boolean;
|
|
50
|
+
|
|
51
|
+
actions: (params: {
|
|
52
|
+
context: PricingAdapterContext;
|
|
53
|
+
calculationSheet: Sheet;
|
|
54
|
+
discounts: Array<Discount<DiscountConfiguration>>;
|
|
55
|
+
}) => IPricingAdapterActions<Calculation, PricingAdapterContext> & { resultSheet: () => Sheet };
|
|
56
|
+
};
|
|
9
57
|
|
|
10
58
|
export const BasePricingAdapter = <
|
|
11
|
-
|
|
59
|
+
PricingAdapterContext extends BasePricingAdapterContext,
|
|
12
60
|
Calculation extends PricingCalculation,
|
|
13
|
-
>(): IPricingAdapter<
|
|
61
|
+
>(): IPricingAdapter<PricingAdapterContext, Calculation, IPricingSheet<Calculation>> => ({
|
|
14
62
|
key: '',
|
|
15
63
|
label: '',
|
|
16
64
|
version: '',
|
|
@@ -22,7 +70,7 @@ export const BasePricingAdapter = <
|
|
|
22
70
|
|
|
23
71
|
actions: (params) => {
|
|
24
72
|
const calculation = [];
|
|
25
|
-
const actions: IPricingAdapterActions<Calculation,
|
|
73
|
+
const actions: IPricingAdapterActions<Calculation, PricingAdapterContext> = {
|
|
26
74
|
calculate: async () => {
|
|
27
75
|
return [];
|
|
28
76
|
},
|
|
@@ -30,7 +78,7 @@ export const BasePricingAdapter = <
|
|
|
30
78
|
getContext: () => params.context,
|
|
31
79
|
};
|
|
32
80
|
|
|
33
|
-
return actions as IPricingAdapterActions<Calculation,
|
|
81
|
+
return actions as IPricingAdapterActions<Calculation, PricingAdapterContext> & {
|
|
34
82
|
resultSheet: () => IPricingSheet<Calculation>;
|
|
35
83
|
};
|
|
36
84
|
},
|
|
@@ -1,15 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { log, LogLevel } from '@unchainedshop/logger';
|
|
2
|
+
import { BaseDirector, IBaseDirector } from './BaseDirector.js';
|
|
3
|
+
import { UnchainedCore } from '@unchainedshop/core';
|
|
2
4
|
import {
|
|
3
5
|
BasePricingAdapterContext,
|
|
4
6
|
BasePricingContext,
|
|
5
|
-
IPricingDirector,
|
|
6
7
|
IPricingAdapter,
|
|
7
|
-
IPricingSheet,
|
|
8
|
-
PricingCalculation,
|
|
9
8
|
IPricingAdapterActions,
|
|
10
|
-
} from '
|
|
11
|
-
import {
|
|
12
|
-
|
|
9
|
+
} from './BasePricingAdapter.js';
|
|
10
|
+
import { IPricingSheet, PricingCalculation } from './BasePricingSheet.js';
|
|
11
|
+
export interface Discount<DiscountConfiguration> {
|
|
12
|
+
discountId: string;
|
|
13
|
+
configuration: DiscountConfiguration;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type IPricingDirector<
|
|
17
|
+
PricingContext extends BasePricingContext,
|
|
18
|
+
Calculation extends PricingCalculation,
|
|
19
|
+
PricingAdapterContext extends BasePricingAdapterContext,
|
|
20
|
+
PricingAdapterSheet extends IPricingSheet<Calculation>,
|
|
21
|
+
Adapter extends IPricingAdapter<PricingAdapterContext, Calculation, PricingAdapterSheet>,
|
|
22
|
+
> = IBaseDirector<Adapter> & {
|
|
23
|
+
buildPricingContext: (
|
|
24
|
+
context: PricingContext,
|
|
25
|
+
unchainedAPI: UnchainedCore,
|
|
26
|
+
) => Promise<PricingAdapterContext>;
|
|
27
|
+
actions: (
|
|
28
|
+
pricingContext: PricingContext,
|
|
29
|
+
unchainedAPI: UnchainedCore,
|
|
30
|
+
buildPricingContext?: (
|
|
31
|
+
pricingCtx: PricingContext,
|
|
32
|
+
_unchainedAPI: UnchainedCore,
|
|
33
|
+
) => Promise<PricingAdapterContext>,
|
|
34
|
+
) => Promise<
|
|
35
|
+
IPricingAdapterActions<Calculation, PricingAdapterContext> & {
|
|
36
|
+
calculationSheet: () => PricingAdapterSheet;
|
|
37
|
+
}
|
|
38
|
+
>;
|
|
39
|
+
};
|
|
13
40
|
|
|
14
41
|
export const BasePricingDirector = <
|
|
15
42
|
DirectorContext extends BasePricingContext,
|
|
@@ -61,10 +88,10 @@ export const BasePricingDirector = <
|
|
|
61
88
|
context.discounts.map(async (discount) => ({
|
|
62
89
|
discountId: discount._id,
|
|
63
90
|
configuration: await context.modules.orders.discounts.configurationForPricingAdapterKey(
|
|
64
|
-
discount,
|
|
91
|
+
discount as any,
|
|
65
92
|
Adapter.key,
|
|
66
93
|
this.calculationSheet(),
|
|
67
|
-
context,
|
|
94
|
+
context as any,
|
|
68
95
|
),
|
|
69
96
|
})),
|
|
70
97
|
);
|
|
@@ -1,8 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export interface PricingCalculation {
|
|
2
|
+
category: string;
|
|
3
|
+
amount: number;
|
|
4
|
+
baseCategory?: string;
|
|
5
|
+
meta?: any;
|
|
6
|
+
}
|
|
7
|
+
export interface PricingDiscount {
|
|
8
|
+
discountId: string;
|
|
9
|
+
amount: number;
|
|
10
|
+
currency: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IPricingSheet<Calculation extends PricingCalculation>
|
|
14
|
+
extends IBasePricingSheet<Calculation> {
|
|
15
|
+
discountPrices: (discountId?: string) => Array<PricingDiscount>;
|
|
16
|
+
addDiscount: (params: { amount: number; discountId: string; meta?: any }) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IBasePricingSheet<Calculation extends PricingCalculation> {
|
|
20
|
+
calculation: Array<Calculation>;
|
|
21
|
+
currency?: string;
|
|
22
|
+
quantity?: number;
|
|
23
|
+
|
|
24
|
+
getRawPricingSheet: () => Array<Calculation>;
|
|
25
|
+
filterBy: (filter?: Partial<Calculation>) => Array<Calculation>;
|
|
26
|
+
isValid: () => boolean;
|
|
27
|
+
|
|
28
|
+
gross: () => number;
|
|
29
|
+
net: () => number;
|
|
30
|
+
sum: (filter?: Partial<Calculation>) => number;
|
|
31
|
+
total: (params?: { category?: string; discountId?: string; useNetPrice?: boolean }) => {
|
|
32
|
+
amount: number;
|
|
33
|
+
currency: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
taxSum: (filter?: Partial<Calculation>) => number;
|
|
37
|
+
|
|
38
|
+
resetCalculation: (sheetToInvert: IBasePricingSheet<Calculation>) => Array<Calculation>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface PricingSheetParams<Calculation extends PricingCalculation> {
|
|
42
|
+
calculation?: Array<Calculation>;
|
|
43
|
+
currency?: string;
|
|
44
|
+
quantity?: number;
|
|
45
|
+
}
|
|
6
46
|
|
|
7
47
|
export const BasePricingSheet = <Calculation extends PricingCalculation>(
|
|
8
48
|
params: PricingSheetParams<Calculation>,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseDirector } from
|
|
1
|
+
import { BaseDirector } from './BaseDirector.js';
|
|
2
2
|
|
|
3
3
|
const adapter1 = {
|
|
4
4
|
key: 'adapter1',
|
|
@@ -31,99 +31,88 @@ describe('BaseDirector', () => {
|
|
|
31
31
|
director.registerAdapter(adapter2);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
describe('registerAdapter',
|
|
35
|
-
|
|
34
|
+
describe('registerAdapter', () => {
|
|
36
35
|
it('should add the adapter to the director', () => {
|
|
37
36
|
director.registerAdapter(adapter1);
|
|
38
37
|
expect(director.getAdapter(adapter1.key)).toBe(adapter1);
|
|
39
38
|
});
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
xit('should generate a log message when an adapter is registered', () => {
|
|
42
|
-
const spy = import.meta.jest.spyOn(console, 'log')
|
|
43
|
-
|
|
44
|
-
'TestDirector -> Registered adapter1 1.0.0 (Adapter 1)',
|
|
45
|
-
);
|
|
41
|
+
const spy = import.meta.jest.spyOn(console, 'log');
|
|
42
|
+
expect(spy).toHaveBeenCalledWith('TestDirector -> Registered adapter1 1.0.0 (Adapter 1)');
|
|
46
43
|
});
|
|
47
|
-
|
|
44
|
+
|
|
48
45
|
it('should allow multiple adapters to be registered', () => {
|
|
49
46
|
expect(director.getAdapter(adapter1.key)).toBe(adapter1);
|
|
50
47
|
expect(director.getAdapter(adapter2.key)).toBe(adapter2);
|
|
51
48
|
});
|
|
52
|
-
|
|
49
|
+
|
|
53
50
|
it('should register an adapter and add it to the internal map', () => {
|
|
54
51
|
expect(director.getAdapter(adapter1.key)).toEqual(adapter1);
|
|
55
52
|
});
|
|
56
|
-
|
|
53
|
+
|
|
57
54
|
it('should use the adapterKeyField specified in the options to store the adapter in the internal map', () => {
|
|
58
|
-
|
|
59
55
|
director = BaseDirector('TestDirector', {
|
|
60
56
|
adapterKeyField: 'label',
|
|
61
57
|
});
|
|
62
58
|
director.registerAdapter(adapter1);
|
|
63
59
|
expect(director.getAdapter(adapter1.label)).toEqual(adapter1);
|
|
64
60
|
});
|
|
65
|
-
|
|
66
|
-
it('should return the correct Adapter object', () => {
|
|
67
|
-
const directorName = 'TestDirector';
|
|
68
|
-
const adapterKeyField = 'key';
|
|
69
|
-
director = BaseDirector(directorName, { adapterKeyField });
|
|
70
|
-
director.registerAdapter(adapter1);
|
|
71
|
-
|
|
72
|
-
expect(director.getAdapter(adapter1.key)).toEqual(adapter1);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should return undefined if the Adapter does not exist', () => {
|
|
76
|
-
const directorName = 'TestDirector';
|
|
77
|
-
const adapterKeyField = 'key';
|
|
78
|
-
director = BaseDirector(directorName, { adapterKeyField });
|
|
79
|
-
expect(director.getAdapter(adapter1.key)).toBeUndefined();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
})
|
|
83
61
|
|
|
62
|
+
it('should return the correct Adapter object', () => {
|
|
63
|
+
const directorName = 'TestDirector';
|
|
64
|
+
const adapterKeyField = 'key';
|
|
65
|
+
director = BaseDirector(directorName, { adapterKeyField });
|
|
66
|
+
director.registerAdapter(adapter1);
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// Expect that calling getAdapter with the key 'foo' returns the fooAdapter
|
|
88
|
-
expect(director.getAdapter(adapter1.key)).toBe(adapter1);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should return undefined if no Adapter is registered with the given key', () => {
|
|
92
|
-
// Expect that calling getAdapter with a key that has not been registered returns undefined
|
|
93
|
-
expect(director.getAdapter('invalid-key')).toBeUndefined();
|
|
94
|
-
});
|
|
68
|
+
expect(director.getAdapter(adapter1.key)).toEqual(adapter1);
|
|
69
|
+
});
|
|
95
70
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
it('should return undefined if the Adapter does not exist', () => {
|
|
72
|
+
const directorName = 'TestDirector';
|
|
73
|
+
const adapterKeyField = 'key';
|
|
74
|
+
director = BaseDirector(directorName, { adapterKeyField });
|
|
75
|
+
expect(director.getAdapter(adapter1.key)).toBeUndefined();
|
|
76
|
+
});
|
|
101
77
|
});
|
|
102
|
-
});
|
|
103
78
|
|
|
104
|
-
describe('
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
79
|
+
describe('getAdapter', () => {
|
|
80
|
+
it('should return the correct Adapter object for a given key', () => {
|
|
81
|
+
// Expect that calling getAdapter with the key 'foo' returns the fooAdapter
|
|
82
|
+
expect(director.getAdapter(adapter1.key)).toBe(adapter1);
|
|
83
|
+
});
|
|
108
84
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(baseDirector.getAdapters()).toEqual([]);
|
|
114
|
-
});
|
|
85
|
+
it('should return undefined if no Adapter is registered with the given key', () => {
|
|
86
|
+
// Expect that calling getAdapter with a key that has not been registered returns undefined
|
|
87
|
+
expect(director.getAdapter('invalid-key')).toBeUndefined();
|
|
88
|
+
});
|
|
115
89
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
90
|
+
it('should return the correct Adapter object when the keyField is set to a property other than "key"', () => {
|
|
91
|
+
// Create a director with the keyField option set to 'label'
|
|
92
|
+
director = BaseDirector('TestDirector', { adapterKeyField: 'label' });
|
|
93
|
+
director.registerAdapter(adapter1);
|
|
94
|
+
expect(director.getAdapter(adapter1.label)).toBe(adapter1);
|
|
95
|
+
});
|
|
122
96
|
});
|
|
123
97
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
})
|
|
98
|
+
describe('getAdapters', () => {
|
|
99
|
+
it('should return an array of registered Adapter objects', () => {
|
|
100
|
+
expect(director.getAdapters()).toEqual([adapter1, adapter3, adapter2]);
|
|
101
|
+
});
|
|
128
102
|
|
|
103
|
+
it('should return an empty array if no Adapter objects have been registered', () => {
|
|
104
|
+
const directorName = 'TestDirector';
|
|
105
|
+
const adapterKeyField = 'key';
|
|
106
|
+
const baseDirector = BaseDirector(directorName, { adapterKeyField });
|
|
107
|
+
expect(baseDirector.getAdapters()).toEqual([]);
|
|
108
|
+
});
|
|
129
109
|
|
|
110
|
+
it('should return an array of registered Adapter objects sorted by the adapterSortKey provided', () => {
|
|
111
|
+
director = BaseDirector('TestDirector', { adapterSortKey: 'version' });
|
|
112
|
+
director.registerAdapter(adapter1);
|
|
113
|
+
director.registerAdapter(adapter3);
|
|
114
|
+
director.registerAdapter(adapter2);
|
|
115
|
+
expect(director.getAdapters()).toEqual([adapter1, adapter2, adapter3]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|