@openlup/platform 0.18.0 → 0.19.0
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 +47 -0
- package/README.md +24 -9
- package/dist/catalog/adminBundleReadHandler.d.ts +2 -1
- package/dist/catalog/adminBundleReadHandler.js +4 -4
- package/dist/catalog/bundleCatalogReadPort.d.ts +8 -1
- package/dist/catalog/bundleCatalogStore.d.ts +3 -1
- package/dist/catalog/bundleCatalogStore.js +5 -1
- package/dist/catalog/bundlePriceSelection.d.ts +13 -0
- package/dist/catalog/bundlePriceSelection.js +26 -0
- package/dist/catalog/contracts/bundleRead.d.ts +18 -0
- package/dist/catalog/contracts/draft.js +1 -1
- package/dist/catalog/effectiveBundleCatalogQueries.d.ts +8 -0
- package/dist/catalog/effectiveBundleCatalogQueries.js +79 -0
- package/dist/catalog/effectiveBundleCatalogStore.d.ts +4 -0
- package/dist/catalog/effectiveBundleCatalogStore.js +156 -0
- package/dist/catalog/index.d.ts +1 -0
- package/dist/catalog/index.js +1 -0
- package/dist/composition/index.d.ts +2 -0
- package/dist/composition/index.js +2 -0
- package/dist/composition/inventoryContribution.d.ts +16 -0
- package/dist/composition/inventoryContribution.js +17 -0
- package/dist/composition/shippingContribution.d.ts +16 -0
- package/dist/composition/shippingContribution.js +17 -0
- package/dist/inventory/bindings.d.ts +35 -0
- package/dist/inventory/bindings.js +45 -0
- package/dist/inventory/index.d.ts +1 -0
- package/dist/inventory/index.js +1 -0
- package/dist/pricing/commercePriceAuthority.d.ts +2 -0
- package/dist/pricing/commercePriceAuthority.js +61 -0
- package/dist/pricing/commerceQuoteHandler.d.ts +7 -3
- package/dist/pricing/commerceQuoteHandler.js +16 -4
- package/dist/pricing/dbBackedCommerceQuotePort.d.ts +8 -0
- package/dist/pricing/dbBackedCommerceQuotePort.js +46 -29
- package/dist/pricing/index.d.ts +2 -1
- package/dist/pricing/index.js +1 -1
- package/dist/pricing/offerPolicySchemas.d.ts +2 -2
- package/dist/pricing/offerPricingSchemas.d.ts +2 -2
- package/dist/pricing/pricingPolicyToken.d.ts +2 -2
- package/dist/pricing/quoteSchemas.d.ts +18 -18
- package/dist/pricing/quoteShippingPromotions.d.ts +20 -0
- package/dist/pricing/quoteShippingPromotions.js +1 -0
- package/dist/promotions/adminPromotionCodesContracts.d.ts +7 -7
- package/dist/promotions/commerceQuoteReadPreparation.d.ts +3 -0
- package/dist/promotions/commerceQuoteReadPreparation.js +27 -0
- package/dist/promotions/index.d.ts +3 -0
- package/dist/promotions/index.js +1 -0
- package/dist/promotions/promotionQuoteAcceptance.d.ts +5 -0
- package/dist/promotions/promotionQuoteAcceptance.js +12 -12
- package/dist/promotions/promotionQuoteBinding.d.ts +4 -2
- package/dist/promotions/promotionQuoteBinding.js +5 -3
- package/dist/promotions/quoteAcquisitionBinding.d.ts +6 -0
- package/dist/promotions/quoteAcquisitionBinding.js +1 -0
- package/dist/promotions/quoteAutomaticPromotions.d.ts +3 -1
- package/dist/promotions/quoteAutomaticPromotions.js +28 -4
- package/dist/promotions/quotePromoDiscounts.d.ts +5 -1
- package/dist/promotions/quotePromoDiscounts.js +8 -2
- package/dist/promotions/quotePromotionV2.d.ts +7 -6
- package/dist/promotions/quotePromotionV2.js +15 -3
- package/dist/promotions/quoteShippingPolicy.d.ts +23 -0
- package/dist/promotions/quoteShippingPolicy.js +47 -0
- package/dist/shipping/bindings.d.ts +57 -0
- package/dist/shipping/bindings.js +46 -0
- package/dist/shipping/contracts/index.d.ts +1 -0
- package/dist/shipping/contracts/threshold.d.ts +22 -0
- package/dist/shipping/contracts/threshold.js +1 -0
- package/dist/shipping/index.d.ts +3 -0
- package/dist/shipping/index.js +2 -0
- package/dist/shipping/thresholdPolicy.d.ts +42 -0
- package/dist/shipping/thresholdPolicy.js +60 -0
- package/package.json +2 -2
- package/release-gates.json +136 -134
- package/src/catalog/adminBundleReadHandler.ts +6 -4
- package/src/catalog/bundleCatalogReadPort.ts +6 -2
- package/src/catalog/bundleCatalogStore.ts +5 -0
- package/src/catalog/bundlePriceSelection.ts +25 -0
- package/src/catalog/contracts/bundleRead.ts +2 -2
- package/src/catalog/contracts/draft.ts +1 -1
- package/src/catalog/effectiveBundleCatalogQueries.ts +80 -0
- package/src/catalog/effectiveBundleCatalogStore.ts +208 -0
- package/src/catalog/index.ts +1 -0
- package/src/composition/index.ts +2 -0
- package/src/composition/inventoryContribution.ts +24 -0
- package/src/composition/shippingContribution.ts +24 -0
- package/src/inventory/bindings.ts +61 -0
- package/src/inventory/index.ts +1 -0
- package/src/pricing/commercePriceAuthority.ts +65 -0
- package/src/pricing/commerceQuoteHandler.ts +20 -5
- package/src/pricing/dbBackedCommerceQuotePort.ts +50 -30
- package/src/pricing/index.ts +2 -1
- package/src/pricing/quoteShippingPromotions.ts +21 -0
- package/src/promotions/commerceQuoteReadPreparation.ts +29 -0
- package/src/promotions/index.ts +3 -0
- package/src/promotions/promotionQuoteAcceptance.ts +17 -10
- package/src/promotions/promotionQuoteBinding.ts +7 -2
- package/src/promotions/quoteAcquisitionBinding.ts +7 -0
- package/src/promotions/quoteAutomaticPromotions.ts +30 -4
- package/src/promotions/quotePromoDiscounts.ts +11 -2
- package/src/promotions/quotePromotionV2.ts +20 -4
- package/src/promotions/quoteShippingPolicy.ts +61 -0
- package/src/shipping/bindings.ts +74 -0
- package/src/shipping/contracts/index.ts +1 -0
- package/src/shipping/contracts/threshold.ts +22 -0
- package/src/shipping/index.ts +3 -0
- package/src/shipping/thresholdPolicy.ts +65 -0
|
@@ -33,8 +33,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
33
33
|
}, z.core.$strip>;
|
|
34
34
|
quotePricingComponentSchema: z.ZodObject<{
|
|
35
35
|
scope: z.ZodEnum<{
|
|
36
|
-
order: "order";
|
|
37
36
|
line: "line";
|
|
37
|
+
order: "order";
|
|
38
38
|
}>;
|
|
39
39
|
componentType: z.ZodEnum<{
|
|
40
40
|
bundle: "bundle";
|
|
@@ -111,8 +111,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
111
111
|
}, z.core.$strip>;
|
|
112
112
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
113
113
|
scope: z.ZodEnum<{
|
|
114
|
-
order: "order";
|
|
115
114
|
line: "line";
|
|
115
|
+
order: "order";
|
|
116
116
|
}>;
|
|
117
117
|
componentType: z.ZodEnum<{
|
|
118
118
|
bundle: "bundle";
|
|
@@ -162,11 +162,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
162
162
|
quoteCodeRejectionSchema: z.ZodObject<{
|
|
163
163
|
code: z.ZodString;
|
|
164
164
|
reason: z.ZodEnum<{
|
|
165
|
-
expired: "expired";
|
|
166
165
|
scope_not_applicable: "scope_not_applicable";
|
|
167
166
|
not_recognized: "not_recognized";
|
|
168
167
|
already_used: "already_used";
|
|
169
168
|
not_eligible: "not_eligible";
|
|
169
|
+
expired: "expired";
|
|
170
170
|
better_price_exists: "better_price_exists";
|
|
171
171
|
}>;
|
|
172
172
|
}, z.core.$strict>;
|
|
@@ -206,8 +206,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
206
206
|
}, z.core.$strip>;
|
|
207
207
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
208
208
|
scope: z.ZodEnum<{
|
|
209
|
-
order: "order";
|
|
210
209
|
line: "line";
|
|
210
|
+
order: "order";
|
|
211
211
|
}>;
|
|
212
212
|
componentType: z.ZodEnum<{
|
|
213
213
|
bundle: "bundle";
|
|
@@ -293,11 +293,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
293
293
|
codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
294
294
|
code: z.ZodString;
|
|
295
295
|
reason: z.ZodEnum<{
|
|
296
|
-
expired: "expired";
|
|
297
296
|
scope_not_applicable: "scope_not_applicable";
|
|
298
297
|
not_recognized: "not_recognized";
|
|
299
298
|
already_used: "already_used";
|
|
300
299
|
not_eligible: "not_eligible";
|
|
300
|
+
expired: "expired";
|
|
301
301
|
better_price_exists: "better_price_exists";
|
|
302
302
|
}>;
|
|
303
303
|
}, z.core.$strict>>>;
|
|
@@ -322,8 +322,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
322
322
|
}, z.core.$strict>], "reason">>>;
|
|
323
323
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
324
324
|
scope: z.ZodEnum<{
|
|
325
|
-
order: "order";
|
|
326
325
|
line: "line";
|
|
326
|
+
order: "order";
|
|
327
327
|
}>;
|
|
328
328
|
componentType: z.ZodEnum<{
|
|
329
329
|
bundle: "bundle";
|
|
@@ -406,8 +406,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
406
406
|
}, z.core.$strip>;
|
|
407
407
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
408
408
|
scope: z.ZodEnum<{
|
|
409
|
-
order: "order";
|
|
410
409
|
line: "line";
|
|
410
|
+
order: "order";
|
|
411
411
|
}>;
|
|
412
412
|
componentType: z.ZodEnum<{
|
|
413
413
|
bundle: "bundle";
|
|
@@ -493,11 +493,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
493
493
|
codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
494
494
|
code: z.ZodString;
|
|
495
495
|
reason: z.ZodEnum<{
|
|
496
|
-
expired: "expired";
|
|
497
496
|
scope_not_applicable: "scope_not_applicable";
|
|
498
497
|
not_recognized: "not_recognized";
|
|
499
498
|
already_used: "already_used";
|
|
500
499
|
not_eligible: "not_eligible";
|
|
500
|
+
expired: "expired";
|
|
501
501
|
better_price_exists: "better_price_exists";
|
|
502
502
|
}>;
|
|
503
503
|
}, z.core.$strict>>>;
|
|
@@ -522,8 +522,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
522
522
|
}, z.core.$strict>], "reason">>>;
|
|
523
523
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
524
524
|
scope: z.ZodEnum<{
|
|
525
|
-
order: "order";
|
|
526
525
|
line: "line";
|
|
526
|
+
order: "order";
|
|
527
527
|
}>;
|
|
528
528
|
componentType: z.ZodEnum<{
|
|
529
529
|
bundle: "bundle";
|
|
@@ -607,8 +607,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
607
607
|
}, z.core.$strip>;
|
|
608
608
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
609
609
|
scope: z.ZodEnum<{
|
|
610
|
-
order: "order";
|
|
611
610
|
line: "line";
|
|
611
|
+
order: "order";
|
|
612
612
|
}>;
|
|
613
613
|
componentType: z.ZodEnum<{
|
|
614
614
|
bundle: "bundle";
|
|
@@ -694,11 +694,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
694
694
|
codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
695
695
|
code: z.ZodString;
|
|
696
696
|
reason: z.ZodEnum<{
|
|
697
|
-
expired: "expired";
|
|
698
697
|
scope_not_applicable: "scope_not_applicable";
|
|
699
698
|
not_recognized: "not_recognized";
|
|
700
699
|
already_used: "already_used";
|
|
701
700
|
not_eligible: "not_eligible";
|
|
701
|
+
expired: "expired";
|
|
702
702
|
better_price_exists: "better_price_exists";
|
|
703
703
|
}>;
|
|
704
704
|
}, z.core.$strict>>>;
|
|
@@ -723,8 +723,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
723
723
|
}, z.core.$strict>], "reason">>>;
|
|
724
724
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
725
725
|
scope: z.ZodEnum<{
|
|
726
|
-
order: "order";
|
|
727
726
|
line: "line";
|
|
727
|
+
order: "order";
|
|
728
728
|
}>;
|
|
729
729
|
componentType: z.ZodEnum<{
|
|
730
730
|
bundle: "bundle";
|
|
@@ -809,8 +809,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
809
809
|
}, z.core.$strip>;
|
|
810
810
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
811
811
|
scope: z.ZodEnum<{
|
|
812
|
-
order: "order";
|
|
813
812
|
line: "line";
|
|
813
|
+
order: "order";
|
|
814
814
|
}>;
|
|
815
815
|
componentType: z.ZodEnum<{
|
|
816
816
|
bundle: "bundle";
|
|
@@ -896,11 +896,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
896
896
|
codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
897
897
|
code: z.ZodString;
|
|
898
898
|
reason: z.ZodEnum<{
|
|
899
|
-
expired: "expired";
|
|
900
899
|
scope_not_applicable: "scope_not_applicable";
|
|
901
900
|
not_recognized: "not_recognized";
|
|
902
901
|
already_used: "already_used";
|
|
903
902
|
not_eligible: "not_eligible";
|
|
903
|
+
expired: "expired";
|
|
904
904
|
better_price_exists: "better_price_exists";
|
|
905
905
|
}>;
|
|
906
906
|
}, z.core.$strict>>>;
|
|
@@ -925,8 +925,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
925
925
|
}, z.core.$strict>], "reason">>>;
|
|
926
926
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
927
927
|
scope: z.ZodEnum<{
|
|
928
|
-
order: "order";
|
|
929
928
|
line: "line";
|
|
929
|
+
order: "order";
|
|
930
930
|
}>;
|
|
931
931
|
componentType: z.ZodEnum<{
|
|
932
932
|
bundle: "bundle";
|
|
@@ -1010,8 +1010,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
1010
1010
|
}, z.core.$strip>;
|
|
1011
1011
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1012
1012
|
scope: z.ZodEnum<{
|
|
1013
|
-
order: "order";
|
|
1014
1013
|
line: "line";
|
|
1014
|
+
order: "order";
|
|
1015
1015
|
}>;
|
|
1016
1016
|
componentType: z.ZodEnum<{
|
|
1017
1017
|
bundle: "bundle";
|
|
@@ -1097,11 +1097,11 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
1097
1097
|
codeRejections: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1098
1098
|
code: z.ZodString;
|
|
1099
1099
|
reason: z.ZodEnum<{
|
|
1100
|
-
expired: "expired";
|
|
1101
1100
|
scope_not_applicable: "scope_not_applicable";
|
|
1102
1101
|
not_recognized: "not_recognized";
|
|
1103
1102
|
already_used: "already_used";
|
|
1104
1103
|
not_eligible: "not_eligible";
|
|
1104
|
+
expired: "expired";
|
|
1105
1105
|
better_price_exists: "better_price_exists";
|
|
1106
1106
|
}>;
|
|
1107
1107
|
}, z.core.$strict>>>;
|
|
@@ -1126,8 +1126,8 @@ export declare function createCommerceQuoteSchemas<Context extends z.ZodType>(in
|
|
|
1126
1126
|
}, z.core.$strict>], "reason">>>;
|
|
1127
1127
|
pricingComponents: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1128
1128
|
scope: z.ZodEnum<{
|
|
1129
|
-
order: "order";
|
|
1130
1129
|
line: "line";
|
|
1130
|
+
order: "order";
|
|
1131
1131
|
}>;
|
|
1132
1132
|
componentType: z.ZodEnum<{
|
|
1133
1133
|
bundle: "bundle";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ShippingTerms } from "../shipping/index.js";
|
|
2
|
+
import type { ResolvedAcquisitionTerms } from "./contracts/acquisitionTerms.js";
|
|
3
|
+
import type { CreateQuoteRequest } from "./contracts/quote.js";
|
|
4
|
+
import type { CreateQuoteOptions } from "./quotePorts.js";
|
|
5
|
+
import type { QuotePromotionResult } from "./dbBackedCommerceQuotePort.js";
|
|
6
|
+
export interface QuoteShippingPromotionInput<Request extends CreateQuoteRequest> {
|
|
7
|
+
request: Request;
|
|
8
|
+
options?: CreateQuoteOptions;
|
|
9
|
+
regionCode: string;
|
|
10
|
+
currency: string;
|
|
11
|
+
referenceProductMinor: number;
|
|
12
|
+
subtotalGrossMinor: number;
|
|
13
|
+
atTime: string;
|
|
14
|
+
}
|
|
15
|
+
export interface QuoteShippingPromotionResult extends QuotePromotionResult {
|
|
16
|
+
shippingGrossMinor: number;
|
|
17
|
+
hasShipping: boolean;
|
|
18
|
+
shippingTerms?: ShippingTerms;
|
|
19
|
+
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -107,8 +107,8 @@ export declare const promotionCodePreviewRequestSchema: z.ZodObject<{
|
|
|
107
107
|
subscription_initial: "subscription_initial";
|
|
108
108
|
}>>;
|
|
109
109
|
promotionEngineVersion: z.ZodDefault<z.ZodEnum<{
|
|
110
|
-
"promotion-engine.v2": "promotion-engine.v2";
|
|
111
110
|
"promotion-engine.v1": "promotion-engine.v1";
|
|
111
|
+
"promotion-engine.v2": "promotion-engine.v2";
|
|
112
112
|
}>>;
|
|
113
113
|
minimumReferenceMinor: z.ZodDefault<z.ZodNumber>;
|
|
114
114
|
context: z.ZodObject<{
|
|
@@ -150,8 +150,8 @@ export declare const promotionCodePreviewResultSchema: z.ZodObject<{
|
|
|
150
150
|
export declare const promotionCodePreviewResponseSchema: z.ZodObject<{
|
|
151
151
|
contractVersion: z.ZodLiteral<"commerce.v0">;
|
|
152
152
|
promotionEngineVersion: z.ZodEnum<{
|
|
153
|
-
"promotion-engine.v2": "promotion-engine.v2";
|
|
154
153
|
"promotion-engine.v1": "promotion-engine.v1";
|
|
154
|
+
"promotion-engine.v2": "promotion-engine.v2";
|
|
155
155
|
}>;
|
|
156
156
|
previewProof: z.ZodString;
|
|
157
157
|
results: z.ZodArray<z.ZodObject<{
|
|
@@ -292,8 +292,8 @@ export declare const promotionCodesListRequestSchema: z.ZodObject<{
|
|
|
292
292
|
archived: "archived";
|
|
293
293
|
all: "all";
|
|
294
294
|
expired: "expired";
|
|
295
|
-
paused: "paused";
|
|
296
295
|
scheduled: "scheduled";
|
|
296
|
+
paused: "paused";
|
|
297
297
|
expired_or_archived: "expired_or_archived";
|
|
298
298
|
}>>;
|
|
299
299
|
scope: z.ZodDefault<z.ZodEnum<{
|
|
@@ -358,8 +358,8 @@ export declare const promotionCodeSummarySchema: z.ZodObject<{
|
|
|
358
358
|
validationState: z.ZodLiteral<"unsupported_legacy_value">;
|
|
359
359
|
}, z.core.$strict>]>>;
|
|
360
360
|
promotionEngineVersion: z.ZodEnum<{
|
|
361
|
-
"promotion-engine.v2": "promotion-engine.v2";
|
|
362
361
|
"promotion-engine.v1": "promotion-engine.v1";
|
|
362
|
+
"promotion-engine.v2": "promotion-engine.v2";
|
|
363
363
|
}>;
|
|
364
364
|
scopes: z.ZodArray<z.ZodEnum<{
|
|
365
365
|
one_time: "one_time";
|
|
@@ -376,8 +376,8 @@ export declare const promotionCodeSummarySchema: z.ZodObject<{
|
|
|
376
376
|
active: "active";
|
|
377
377
|
archived: "archived";
|
|
378
378
|
expired: "expired";
|
|
379
|
-
paused: "paused";
|
|
380
379
|
scheduled: "scheduled";
|
|
380
|
+
paused: "paused";
|
|
381
381
|
}>;
|
|
382
382
|
validFrom: z.ZodString;
|
|
383
383
|
validTo: z.ZodNullable<z.ZodString>;
|
|
@@ -444,8 +444,8 @@ export declare const promotionCodesListResponseSchema: z.ZodObject<{
|
|
|
444
444
|
validationState: z.ZodLiteral<"unsupported_legacy_value">;
|
|
445
445
|
}, z.core.$strict>]>>;
|
|
446
446
|
promotionEngineVersion: z.ZodEnum<{
|
|
447
|
-
"promotion-engine.v2": "promotion-engine.v2";
|
|
448
447
|
"promotion-engine.v1": "promotion-engine.v1";
|
|
448
|
+
"promotion-engine.v2": "promotion-engine.v2";
|
|
449
449
|
}>;
|
|
450
450
|
scopes: z.ZodArray<z.ZodEnum<{
|
|
451
451
|
one_time: "one_time";
|
|
@@ -462,8 +462,8 @@ export declare const promotionCodesListResponseSchema: z.ZodObject<{
|
|
|
462
462
|
active: "active";
|
|
463
463
|
archived: "archived";
|
|
464
464
|
expired: "expired";
|
|
465
|
-
paused: "paused";
|
|
466
465
|
scheduled: "scheduled";
|
|
466
|
+
paused: "paused";
|
|
467
467
|
}>;
|
|
468
468
|
validFrom: z.ZodString;
|
|
469
469
|
validTo: z.ZodNullable<z.ZodString>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Allocate inside one quote resolution; clone each evaluator's mutable raw view. */
|
|
2
|
+
export function memoizeCommercePromoReads(inner) {
|
|
3
|
+
const definitions = memoize(() => inner.listActivePromotions());
|
|
4
|
+
const paid = memoize((id) => inner.countPaidOrders(id));
|
|
5
|
+
const modes = memoize((id) => inner.countPaidOrdersByMode(id));
|
|
6
|
+
const redemptions = memoize((id) => inner.redemptionCounts(id));
|
|
7
|
+
const device = memoize((id) => inner.deviceFirstOrderRedeemed(id));
|
|
8
|
+
return {
|
|
9
|
+
listActivePromotions: async () => structuredClone(await definitions()),
|
|
10
|
+
countPaidOrders: paid,
|
|
11
|
+
countPaidOrdersByMode: async (id) => ({ ...await modes(id) }),
|
|
12
|
+
redemptionCounts: async (id) => structuredClone(await redemptions(id)),
|
|
13
|
+
deviceFirstOrderRedeemed: device,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function memoize(read) {
|
|
17
|
+
const values = new Map();
|
|
18
|
+
return (...args) => {
|
|
19
|
+
const key = JSON.stringify(args);
|
|
20
|
+
let value = values.get(key);
|
|
21
|
+
if (!value) {
|
|
22
|
+
value = Promise.resolve().then(() => read(...args));
|
|
23
|
+
values.set(key, value);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -20,3 +20,6 @@ export * from "./promotionClaimSweepPort.js";
|
|
|
20
20
|
export * from "./promotionClaims.js";
|
|
21
21
|
export * from "./sql.js";
|
|
22
22
|
export type { PromotionStatus, AdminPromotion, AdminPromotionV2Mirror, AdminPromotionSemanticBenefit, AdminPromotionsListResponse, AdminPromotionUpdatePayload, AdminPromotionUpdateRequest, AdminPromotionUpdateResponse } from "./contracts/index.js";
|
|
23
|
+
export type { QuoteAcquisitionBinding } from "./quoteAcquisitionBinding.js";
|
|
24
|
+
export { createQuoteShippingPromotions } from "./quoteShippingPolicy.js";
|
|
25
|
+
export type { QuoteShippingPromotionsDeps } from "./quoteShippingPolicy.js";
|
package/dist/promotions/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ShippingTerms } from "../shipping/index.js";
|
|
1
2
|
import type { ResolvedAcquisitionTerms } from "../pricing/index.js";
|
|
2
3
|
import type { CommerceQuote } from "../pricing/index.js";
|
|
3
4
|
import type { PromotionAcceptanceTelemetry } from "./promotionAcceptanceTelemetry.js";
|
|
@@ -25,6 +26,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
|
|
|
25
26
|
request: Request;
|
|
26
27
|
quote: CommerceQuote;
|
|
27
28
|
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
29
|
+
shippingTerms?: ShippingTerms;
|
|
28
30
|
keyring: PromotionAcceptanceKeyring;
|
|
29
31
|
now?: Date;
|
|
30
32
|
}) => string | null;
|
|
@@ -32,6 +34,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
|
|
|
32
34
|
request: Request;
|
|
33
35
|
quote: CommerceQuote;
|
|
34
36
|
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
37
|
+
shippingTerms?: ShippingTerms;
|
|
35
38
|
keyring: PromotionAcceptanceKeyring;
|
|
36
39
|
now?: Date;
|
|
37
40
|
}) => {
|
|
@@ -43,6 +46,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
|
|
|
43
46
|
request: Request;
|
|
44
47
|
quote: CommerceQuote;
|
|
45
48
|
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
49
|
+
shippingTerms?: ShippingTerms;
|
|
46
50
|
expectedTotal: {
|
|
47
51
|
amountMinor: number;
|
|
48
52
|
currency: string;
|
|
@@ -55,6 +59,7 @@ export declare function createPromotionQuoteAcceptance<Request extends {
|
|
|
55
59
|
request: Request;
|
|
56
60
|
quote: CommerceQuote;
|
|
57
61
|
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
62
|
+
shippingTerms?: ShippingTerms;
|
|
58
63
|
expectedTotal: {
|
|
59
64
|
amountMinor: number;
|
|
60
65
|
currency: string;
|
|
@@ -25,7 +25,7 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
25
25
|
}
|
|
26
26
|
function issuePromotionQuoteAcceptanceWithOutcome(input) {
|
|
27
27
|
const evidence = v2Discounts(input.quote);
|
|
28
|
-
if (evidence.length === 0 && !input.acquisitionTerms)
|
|
28
|
+
if (evidence.length === 0 && !input.acquisitionTerms && !input.shippingTerms)
|
|
29
29
|
return {
|
|
30
30
|
token: null,
|
|
31
31
|
diagnostic: rejected("no_v2_adjustment"),
|
|
@@ -47,8 +47,8 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
47
47
|
engine: "promotion-engine.v2",
|
|
48
48
|
requestBinding: requestBinding(input.request, input.keyring.current),
|
|
49
49
|
codeBinding: codeBinding(input.request.promoCodes, input.keyring.current),
|
|
50
|
-
quoteBinding: quoteBinding(input.quote, input.keyring.current, input.acquisitionTerms),
|
|
51
|
-
sectionBindings: sectionBindings(input.quote, input.keyring.current, input.acquisitionTerms),
|
|
50
|
+
quoteBinding: quoteBinding(input.quote, input.keyring.current, input.acquisitionTerms, input.shippingTerms),
|
|
51
|
+
sectionBindings: sectionBindings(input.quote, input.keyring.current, input.acquisitionTerms, input.shippingTerms),
|
|
52
52
|
totalMinor: input.quote.totalGross.amountMinor,
|
|
53
53
|
currency: input.quote.totalGross.currency,
|
|
54
54
|
});
|
|
@@ -78,8 +78,8 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
78
78
|
if (payload.codeBinding !== codeBinding(input.request.promoCodes, secret)) {
|
|
79
79
|
return { accepted: false, reason: "code_mismatch", ...base };
|
|
80
80
|
}
|
|
81
|
-
if (payload.quoteBinding !== quoteBinding(input.quote, secret, input.acquisitionTerms)) {
|
|
82
|
-
const mismatchField = namedMismatchField(payload.sectionBindings, input.quote, secret, input.acquisitionTerms);
|
|
81
|
+
if (payload.quoteBinding !== quoteBinding(input.quote, secret, input.acquisitionTerms, input.shippingTerms)) {
|
|
82
|
+
const mismatchField = namedMismatchField(payload.sectionBindings, input.quote, secret, input.acquisitionTerms, input.shippingTerms);
|
|
83
83
|
return { accepted: false, reason: "quote_mismatch", mismatchField, ...base };
|
|
84
84
|
}
|
|
85
85
|
if (payload.totalMinor !== input.expectedTotal.amountMinor) {
|
|
@@ -88,7 +88,7 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
88
88
|
if (payload.currency !== input.expectedTotal.currency) {
|
|
89
89
|
return { accepted: false, reason: "currency_mismatch", ...base };
|
|
90
90
|
}
|
|
91
|
-
if (v2Discounts(input.quote).length === 0 && !input.acquisitionTerms) {
|
|
91
|
+
if (v2Discounts(input.quote).length === 0 && !input.acquisitionTerms && !input.shippingTerms) {
|
|
92
92
|
return { accepted: false, reason: "no_v2_adjustment_in_quote", ...base };
|
|
93
93
|
}
|
|
94
94
|
return { accepted: true, reason: "accepted", ...base };
|
|
@@ -161,11 +161,11 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
161
161
|
function codeBinding(codes, secret) {
|
|
162
162
|
return hmac(secret, stableJson([...new Set(codes.map((code) => code.trim().toUpperCase()))].sort()));
|
|
163
163
|
}
|
|
164
|
-
function quoteBinding(quote, secret, acquisitionTerms) {
|
|
165
|
-
return hmac(secret, stableJson(projectPromotionQuoteMoney(quote, acquisitionTerms)));
|
|
164
|
+
function quoteBinding(quote, secret, acquisitionTerms, shippingTerms) {
|
|
165
|
+
return hmac(secret, stableJson(projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms)));
|
|
166
166
|
}
|
|
167
|
-
function sectionBindings(quote, secret, acquisitionTerms) {
|
|
168
|
-
const sections = promotionQuoteMoneySections(quote, acquisitionTerms);
|
|
167
|
+
function sectionBindings(quote, secret, acquisitionTerms, shippingTerms) {
|
|
168
|
+
const sections = promotionQuoteMoneySections(quote, acquisitionTerms, shippingTerms);
|
|
169
169
|
return Object.fromEntries(PROMOTION_QUOTE_MONEY_SECTIONS
|
|
170
170
|
.map((section) => [section, hmac(secret, stableJson(sections[section]))]));
|
|
171
171
|
}
|
|
@@ -173,10 +173,10 @@ export function createPromotionQuoteAcceptance({ projectRequest }) {
|
|
|
173
173
|
* The verifier holds only the issuer's HMACs, never the issued quote, so the
|
|
174
174
|
* differing section is found by comparing per-section hashes in a fixed order.
|
|
175
175
|
*/
|
|
176
|
-
function namedMismatchField(issued, quote, secret, acquisitionTerms) {
|
|
176
|
+
function namedMismatchField(issued, quote, secret, acquisitionTerms, shippingTerms) {
|
|
177
177
|
if (!issued)
|
|
178
178
|
return "other";
|
|
179
|
-
const verified = sectionBindings(quote, secret, acquisitionTerms);
|
|
179
|
+
const verified = sectionBindings(quote, secret, acquisitionTerms, shippingTerms);
|
|
180
180
|
return PROMOTION_QUOTE_MONEY_SECTIONS
|
|
181
181
|
.find((section) => issued[section] !== verified[section]) ?? "other";
|
|
182
182
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ShippingTerms } from "../shipping/index.js";
|
|
1
2
|
import { type ResolvedAcquisitionTerms } from "../pricing/index.js";
|
|
2
3
|
import type { CommerceMoney, CommerceQuote, CommerceQuoteDiscount } from "../pricing/index.js";
|
|
3
4
|
/**
|
|
@@ -43,6 +44,7 @@ export interface PromotionQuoteMoneyDiscount {
|
|
|
43
44
|
}
|
|
44
45
|
export interface PromotionQuoteMoneyProjection {
|
|
45
46
|
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
47
|
+
shippingTerms?: ShippingTerms;
|
|
46
48
|
currency: string;
|
|
47
49
|
totalGross: CommerceMoney;
|
|
48
50
|
netTotal: CommerceMoney;
|
|
@@ -64,8 +66,8 @@ export type PromotionQuoteMismatchField = (typeof PROMOTION_QUOTE_MISMATCH_FIELD
|
|
|
64
66
|
* Discount order is preserved: the promotion engine emits its lanes
|
|
65
67
|
* deterministically, so a reordering is a real evaluation difference.
|
|
66
68
|
*/
|
|
67
|
-
export declare function projectPromotionQuoteMoney(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms): PromotionQuoteMoneyProjection;
|
|
69
|
+
export declare function projectPromotionQuoteMoney(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms, shippingTerms?: ShippingTerms): PromotionQuoteMoneyProjection;
|
|
68
70
|
/** Same projection, partitioned. The union of these five sections is the whole projection. */
|
|
69
|
-
export declare function promotionQuoteMoneySections(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms): Record<PromotionQuoteMoneySection, unknown>;
|
|
71
|
+
export declare function promotionQuoteMoneySections(quote: CommerceQuote, acquisitionTerms?: ResolvedAcquisitionTerms, shippingTerms?: ShippingTerms): Record<PromotionQuoteMoneySection, unknown>;
|
|
70
72
|
/** Key-sorted, `undefined`-dropping JSON. Every acceptance binding hashes this shape. */
|
|
71
73
|
export declare function stableJson(value: unknown): string;
|
|
@@ -16,7 +16,7 @@ export const PROMOTION_QUOTE_MISMATCH_FIELDS = [
|
|
|
16
16
|
* Discount order is preserved: the promotion engine emits its lanes
|
|
17
17
|
* deterministically, so a reordering is a real evaluation difference.
|
|
18
18
|
*/
|
|
19
|
-
export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
|
|
19
|
+
export function projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms) {
|
|
20
20
|
return {
|
|
21
21
|
...acquisitionQuoteBinding(acquisitionTerms),
|
|
22
22
|
currency: quote.currency,
|
|
@@ -27,6 +27,7 @@ export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
|
|
|
27
27
|
discountTotalGross: quote.discountTotalGross,
|
|
28
28
|
shippingGross: quote.shippingGross ?? null,
|
|
29
29
|
shippingDiscountGross: quote.shippingDiscountGross ?? null,
|
|
30
|
+
...(shippingTerms ? { shippingTerms } : {}),
|
|
30
31
|
lines: quote.lines.map((line) => ({
|
|
31
32
|
sku: line.sku,
|
|
32
33
|
quantity: line.quantity,
|
|
@@ -52,14 +53,15 @@ export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
/** Same projection, partitioned. The union of these five sections is the whole projection. */
|
|
55
|
-
export function promotionQuoteMoneySections(quote, acquisitionTerms) {
|
|
56
|
-
const money = projectPromotionQuoteMoney(quote, acquisitionTerms);
|
|
56
|
+
export function promotionQuoteMoneySections(quote, acquisitionTerms, shippingTerms) {
|
|
57
|
+
const money = projectPromotionQuoteMoney(quote, acquisitionTerms, shippingTerms);
|
|
57
58
|
return {
|
|
58
59
|
lines: money.lines,
|
|
59
60
|
discounts: money.discounts,
|
|
60
61
|
shipping: {
|
|
61
62
|
shippingGross: money.shippingGross,
|
|
62
63
|
shippingDiscountGross: money.shippingDiscountGross,
|
|
64
|
+
...(money.shippingTerms ? { shippingTerms: money.shippingTerms } : {}),
|
|
63
65
|
},
|
|
64
66
|
totals: {
|
|
65
67
|
...acquisitionQuoteBinding(money.acquisitionTerms),
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PromotionRow } from "@openlup/core/promo";
|
|
2
|
+
import type { AcquisitionFinancialPolicy } from "../pricing/index.js";
|
|
3
|
+
export interface QuoteAcquisitionBinding<Request = unknown> {
|
|
4
|
+
financialPolicy: AcquisitionFinancialPolicy<PromotionRow>;
|
|
5
|
+
capability(request: Request): unknown;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QuoteAcquisitionBinding } from "./quoteAcquisitionBinding.js";
|
|
1
2
|
import type { CreateQuoteRequest } from "../pricing/index.js";
|
|
2
3
|
import type { CreateQuoteOptions } from "../pricing/index.js";
|
|
3
4
|
import type { CommercePromoDataPort } from "./promoDataPort.js";
|
|
@@ -5,7 +6,8 @@ import type { createQuotePromoDiscounts, QuoteDiscountResult } from "./quoteProm
|
|
|
5
6
|
import type { createQuotePromotionV2 } from "./quotePromotionV2.js";
|
|
6
7
|
export declare function createQuoteAutomaticPromotions<Request extends CreateQuoteRequest & {
|
|
7
8
|
visitorId?: string;
|
|
8
|
-
}>({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort }: {
|
|
9
|
+
}>({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort, acquisition }: {
|
|
10
|
+
acquisition?: QuoteAcquisitionBinding<Request>;
|
|
9
11
|
resolveEmailEligibility(request: Request): boolean;
|
|
10
12
|
evaluateOrderTotalDiscounts: ReturnType<typeof createQuotePromoDiscounts>["evaluateOrderTotalDiscounts"];
|
|
11
13
|
evaluateAutomaticPromotionV2: ReturnType<typeof createQuotePromotionV2>["evaluateAutomaticPromotionV2"];
|
|
@@ -1,12 +1,30 @@
|
|
|
1
|
+
import { selectAcquisitionTerms, assertAcquisitionTermsCapability, ACQUISITION_TERMS_CAPABILITY } from "../pricing/index.js";
|
|
1
2
|
import { PROMOTION_ENGINE_V2 } from "../pricing/index.js";
|
|
2
|
-
export function createQuoteAutomaticPromotions({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort }) {
|
|
3
|
+
export function createQuoteAutomaticPromotions({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort, acquisition }) {
|
|
3
4
|
async function resolveAutomaticQuotePromotions(input) {
|
|
4
5
|
if (!input.promoDataPort)
|
|
5
6
|
return { discounts: [], codeRejections: [] };
|
|
7
|
+
let promoDataPort = input.promoDataPort;
|
|
8
|
+
let selection = null;
|
|
9
|
+
let acquisitionCapability;
|
|
10
|
+
if (acquisition) {
|
|
11
|
+
const clientId = input.options?.clientId ?? null;
|
|
12
|
+
const [paid, allRows] = await Promise.all([
|
|
13
|
+
clientId ? promoDataPort.countPaidOrdersByMode(clientId) : Promise.resolve({ oneTime: 0, subscription: 0 }),
|
|
14
|
+
promoDataPort.listActivePromotions(),
|
|
15
|
+
]);
|
|
16
|
+
const existingSubscriber = input.request.mode === "subscription" && paid.subscription > 0;
|
|
17
|
+
const rows = existingSubscriber ? allRows.filter((row) => !acquisition.financialPolicy.facts(row).metadataPresent && row.eligibility.first_subscription_purchase !== true) : allRows;
|
|
18
|
+
promoDataPort = { ...promoDataPort, listActivePromotions: async () => rows, countPaidOrdersByMode: async () => paid };
|
|
19
|
+
selection = input.request.mode === "subscription" && !existingSubscriber
|
|
20
|
+
? selectAcquisitionTerms(rows, input.atTime, input.regionCode, acquisition.financialPolicy) : null;
|
|
21
|
+
acquisitionCapability = acquisition.capability(input.request);
|
|
22
|
+
assertAcquisitionTermsCapability(selection, acquisitionCapability, input.options?.pricingPolicy?.promotionEngineVersion);
|
|
23
|
+
}
|
|
6
24
|
const emailEligibilityConfirmed = resolveEmailEligibility(input.request);
|
|
7
25
|
if (input.options?.pricingPolicy?.promotionEngineVersion === PROMOTION_ENGINE_V2) {
|
|
8
26
|
return evaluateAutomaticPromotionV2({
|
|
9
|
-
promoDataPort
|
|
27
|
+
promoDataPort,
|
|
10
28
|
clientId: input.options.clientId ?? null,
|
|
11
29
|
visitorId: input.request.visitorId ?? null,
|
|
12
30
|
mode: input.request.mode,
|
|
@@ -17,10 +35,11 @@ export function createQuoteAutomaticPromotions({ resolveEmailEligibility, evalua
|
|
|
17
35
|
shippingMinor: input.shippingGrossMinor,
|
|
18
36
|
atTime: input.atTime,
|
|
19
37
|
emailEligibilityConfirmed,
|
|
38
|
+
...(acquisition ? { acquisitionCapability } : {}),
|
|
20
39
|
});
|
|
21
40
|
}
|
|
22
|
-
|
|
23
|
-
promoDataPort: legacyPromotionDataPort(
|
|
41
|
+
const pending = evaluateOrderTotalDiscounts({
|
|
42
|
+
promoDataPort: legacyPromotionDataPort(promoDataPort),
|
|
24
43
|
clientId: input.options?.clientId ?? null,
|
|
25
44
|
visitorId: input.request.visitorId ?? null,
|
|
26
45
|
mode: input.request.mode,
|
|
@@ -31,6 +50,11 @@ export function createQuoteAutomaticPromotions({ resolveEmailEligibility, evalua
|
|
|
31
50
|
atTime: input.atTime,
|
|
32
51
|
emailEligibilityConfirmed,
|
|
33
52
|
});
|
|
53
|
+
if (!acquisition)
|
|
54
|
+
return pending;
|
|
55
|
+
const result = await pending;
|
|
56
|
+
return selection && acquisitionCapability === ACQUISITION_TERMS_CAPABILITY
|
|
57
|
+
? { ...result, acquisitionTerms: selection.terms } : result;
|
|
34
58
|
}
|
|
35
59
|
return { resolveAutomaticQuotePromotions };
|
|
36
60
|
}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import type { ResolvedAcquisitionTerms } from "../pricing/index.js";
|
|
2
|
+
import type { QuoteAcquisitionBinding } from "./quoteAcquisitionBinding.js";
|
|
1
3
|
import type { CreateQuoteRequest } from "../pricing/index.js";
|
|
2
4
|
import type { CommerceCodeRejection, CommerceQuoteDiscount } from "../pricing/index.js";
|
|
3
5
|
import type { PromotionRow } from "@openlup/core/promo";
|
|
4
6
|
import type { CommercePromoDataPort } from "./promoDataPort.js";
|
|
5
7
|
export interface QuoteDiscountResult {
|
|
8
|
+
acquisitionTerms?: ResolvedAcquisitionTerms;
|
|
6
9
|
/** Applied discounts: order_total (clamped to subtotal) + at most one shipping discount. */
|
|
7
10
|
discounts: CommerceQuoteDiscount[];
|
|
8
11
|
/** Submitted coupon codes that produced no discount, with the reason. */
|
|
9
12
|
codeRejections: CommerceCodeRejection[];
|
|
10
13
|
}
|
|
11
|
-
export declare function createQuotePromoDiscounts({ promotionCustomerSemantic }: {
|
|
14
|
+
export declare function createQuotePromoDiscounts({ promotionCustomerSemantic, acquisition }: {
|
|
15
|
+
acquisition?: Pick<QuoteAcquisitionBinding, "financialPolicy">;
|
|
12
16
|
promotionCustomerSemantic: (row: PromotionRow) => CommerceQuoteDiscount["customerSemantic"];
|
|
13
17
|
}): {
|
|
14
18
|
evaluateOrderTotalDiscounts: (input: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { filterAcquisitionPromotions } from "../pricing/index.js";
|
|
1
2
|
import { evaluatePromos, promoEligibilityFailure } from "@openlup/core/promo";
|
|
2
|
-
export function createQuotePromoDiscounts({ promotionCustomerSemantic }) {
|
|
3
|
+
export function createQuotePromoDiscounts({ promotionCustomerSemantic, acquisition }) {
|
|
3
4
|
/**
|
|
4
5
|
* Evaluate active promotions for this cart and map them to quote discounts. Two lanes:
|
|
5
6
|
* `order_total` promos are clamped to the subtotal (so `discountTotal <= subtotal`), and
|
|
@@ -23,7 +24,12 @@ export function createQuotePromoDiscounts({ promotionCustomerSemantic }) {
|
|
|
23
24
|
input.promoDataPort.redemptionCounts(input.clientId),
|
|
24
25
|
input.promoDataPort.deviceFirstOrderRedeemed(input.visitorId ?? null),
|
|
25
26
|
]);
|
|
26
|
-
const
|
|
27
|
+
const customerCandidates = acquisition && input.mode === "subscription" && byMode.subscription > 0
|
|
28
|
+
? allCandidates.filter((row) => !acquisition.financialPolicy.facts(row).metadataPresent && row.eligibility.first_subscription_purchase !== true)
|
|
29
|
+
: allCandidates;
|
|
30
|
+
const effectiveCandidates = acquisition && input.mode === "subscription"
|
|
31
|
+
? filterAcquisitionPromotions(customerCandidates, input.atTime, input.regionCode, acquisition.financialPolicy) : customerCandidates;
|
|
32
|
+
const candidates = effectiveCandidates.filter((promo) => !capReached(promo, redemptionCounts.get(promo.id), input.clientId));
|
|
27
33
|
const cart = {
|
|
28
34
|
region_code: input.regionCode,
|
|
29
35
|
cart_mode: input.mode === "subscription" ? "subscription" : "one_time",
|