@zoxllc/shopify-checkout-extensions 0.0.8 → 0.1.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/CLAUDE.md +136 -0
- package/EXAMPLES.md +384 -0
- package/INTEGRATION.md +482 -0
- package/README.md +5 -1
- package/dist/common/AndSelector.d.ts +11 -0
- package/dist/common/AndSelector.d.ts.map +1 -0
- package/dist/common/AndSelector.js +30 -0
- package/dist/common/Campaign.d.ts +24 -0
- package/dist/common/Campaign.d.ts.map +1 -0
- package/dist/common/Campaign.js +41 -0
- package/dist/common/CampaignConfiguration.d.ts +10 -0
- package/dist/common/CampaignConfiguration.d.ts.map +1 -0
- package/dist/common/CampaignConfiguration.js +7 -0
- package/dist/common/CampaignFactory.d.ts +22 -0
- package/dist/common/CampaignFactory.d.ts.map +1 -0
- package/dist/common/CampaignFactory.js +95 -0
- package/dist/common/CartAmountQualifier.d.ts +22 -0
- package/dist/common/CartAmountQualifier.d.ts.map +1 -0
- package/dist/common/CartAmountQualifier.js +46 -0
- package/dist/common/CartHasItemQualifier.d.ts +21 -0
- package/dist/common/CartHasItemQualifier.d.ts.map +1 -0
- package/dist/common/CartHasItemQualifier.js +54 -0
- package/dist/common/CartQuantityQualifier.d.ts +22 -0
- package/dist/common/CartQuantityQualifier.d.ts.map +1 -0
- package/dist/common/CartQuantityQualifier.js +62 -0
- package/dist/common/ConfigSchema.d.ts +71 -0
- package/dist/common/ConfigSchema.d.ts.map +1 -0
- package/dist/common/ConfigSchema.js +69 -0
- package/dist/common/ConfigValidator.d.ts +29 -0
- package/dist/common/ConfigValidator.d.ts.map +1 -0
- package/dist/common/ConfigValidator.js +84 -0
- package/dist/common/CountryCodeQualifier.d.ts +15 -0
- package/dist/common/CountryCodeQualifier.d.ts.map +1 -0
- package/dist/common/CountryCodeQualifier.js +30 -0
- package/dist/common/CustomerEmailQualifier.d.ts +16 -0
- package/dist/common/CustomerEmailQualifier.d.ts.map +1 -0
- package/dist/common/CustomerEmailQualifier.js +52 -0
- package/dist/common/CustomerSubscriberQualifier.d.ts +18 -0
- package/dist/common/CustomerSubscriberQualifier.d.ts.map +1 -0
- package/dist/common/CustomerSubscriberQualifier.js +28 -0
- package/dist/common/CustomerTagQualifier.d.ts +16 -0
- package/dist/common/CustomerTagQualifier.d.ts.map +1 -0
- package/dist/common/CustomerTagQualifier.js +45 -0
- package/dist/common/DiscountCart.d.ts +23 -0
- package/dist/common/DiscountCart.d.ts.map +1 -0
- package/dist/common/DiscountCart.js +103 -0
- package/dist/common/DiscountInterface.d.ts +17 -0
- package/dist/common/DiscountInterface.d.ts.map +1 -0
- package/dist/common/DiscountInterface.js +2 -0
- package/dist/common/OrSelector.d.ts +11 -0
- package/dist/common/OrSelector.d.ts.map +1 -0
- package/dist/common/OrSelector.js +30 -0
- package/dist/common/PostCartAmountQualifier.d.ts +9 -0
- package/dist/common/PostCartAmountQualifier.d.ts.map +1 -0
- package/dist/common/PostCartAmountQualifier.js +17 -0
- package/dist/common/ProductHandleSelector.d.ts +8 -0
- package/dist/common/ProductHandleSelector.d.ts.map +1 -0
- package/dist/common/ProductHandleSelector.js +22 -0
- package/dist/common/ProductIdSelector.d.ts +8 -0
- package/dist/common/ProductIdSelector.d.ts.map +1 -0
- package/dist/common/ProductIdSelector.js +22 -0
- package/dist/common/ProductTagSelector.d.ts +10 -0
- package/dist/common/ProductTagSelector.d.ts.map +1 -0
- package/dist/common/ProductTagSelector.js +39 -0
- package/dist/common/ProductTypeSelector.d.ts +8 -0
- package/dist/common/ProductTypeSelector.d.ts.map +1 -0
- package/dist/common/ProductTypeSelector.js +25 -0
- package/dist/common/Qualifier.d.ts +30 -0
- package/dist/common/Qualifier.d.ts.map +1 -0
- package/dist/common/Qualifier.js +61 -0
- package/dist/common/SaleItemSelector.d.ts +11 -0
- package/dist/common/SaleItemSelector.d.ts.map +1 -0
- package/dist/common/SaleItemSelector.js +30 -0
- package/dist/common/Selector.d.ts +27 -0
- package/dist/common/Selector.d.ts.map +1 -0
- package/dist/common/Selector.js +51 -0
- package/dist/common/SubscriptionItemSelector.d.ts +7 -0
- package/dist/common/SubscriptionItemSelector.d.ts.map +1 -0
- package/dist/common/SubscriptionItemSelector.js +19 -0
- package/dist/form/CampaignForm.d.ts +1 -0
- package/dist/form/CampaignForm.d.ts.map +1 -0
- package/dist/form/CampaignForm.js +1 -0
- package/{src/index.ts → dist/index.d.ts} +22 -27
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/lineItem/BuyXGetY.d.ts +18 -0
- package/dist/lineItem/BuyXGetY.d.ts.map +1 -0
- package/dist/lineItem/BuyXGetY.js +85 -0
- package/dist/lineItem/ConditionalDiscount.d.ts +14 -0
- package/dist/lineItem/ConditionalDiscount.d.ts.map +1 -0
- package/dist/lineItem/ConditionalDiscount.js +81 -0
- package/dist/lineItem/FixedItemDiscount.d.ts +9 -0
- package/dist/lineItem/FixedItemDiscount.d.ts.map +1 -0
- package/dist/lineItem/FixedItemDiscount.js +35 -0
- package/dist/lineItem/PercentageDiscount.d.ts +8 -0
- package/dist/lineItem/PercentageDiscount.d.ts.map +1 -0
- package/dist/lineItem/PercentageDiscount.js +32 -0
- package/dist/lineItem/api.d.ts +2026 -0
- package/dist/lineItem/api.d.ts.map +1 -0
- package/dist/lineItem/api.js +1157 -0
- package/dist/shipping/FixedDiscount.d.ts +8 -0
- package/dist/shipping/FixedDiscount.d.ts.map +1 -0
- package/dist/shipping/FixedDiscount.js +30 -0
- package/dist/shipping/RateNameSelector.d.ts +10 -0
- package/dist/shipping/RateNameSelector.d.ts.map +1 -0
- package/dist/shipping/RateNameSelector.js +45 -0
- package/dist/shipping/ShippingDiscount.d.ts +14 -0
- package/dist/shipping/ShippingDiscount.d.ts.map +1 -0
- package/dist/shipping/ShippingDiscount.js +48 -0
- package/dist/shipping/api.d.ts +2019 -0
- package/dist/shipping/api.d.ts.map +1 -0
- package/dist/shipping/api.js +1147 -0
- package/package.json +26 -7
- package/migrate.ts +0 -64
- package/src/common/AndSelector.ts +0 -42
- package/src/common/Campaign.ts +0 -70
- package/src/common/CampaignConfiguration.ts +0 -10
- package/src/common/CampaignFactory.ts +0 -233
- package/src/common/CartAmountQualifier.ts +0 -64
- package/src/common/CartHasItemQualifier.ts +0 -80
- package/src/common/CartQuantityQualifier.ts +0 -94
- package/src/common/CountryCodeQualifier.ts +0 -49
- package/src/common/CustomerEmailQualifier.ts +0 -67
- package/src/common/CustomerSubscriberQualifier.ts +0 -32
- package/src/common/CustomerTagQualifier.ts +0 -69
- package/src/common/DiscountCart.ts +0 -161
- package/src/common/DiscountInterface.ts +0 -26
- package/src/common/OrSelector.ts +0 -40
- package/src/common/PostCartAmountQualifier.ts +0 -21
- package/src/common/ProductHandleSelector.ts +0 -32
- package/src/common/ProductIdSelector.ts +0 -34
- package/src/common/ProductTagSelector.ts +0 -63
- package/src/common/ProductTypeSelector.ts +0 -40
- package/src/common/Qualifier.ts +0 -67
- package/src/common/SaleItemSelector.ts +0 -36
- package/src/common/Selector.ts +0 -66
- package/src/common/SubscriptionItemSelector.ts +0 -23
- package/src/lineItem/BuyXGetY.ts +0 -131
- package/src/lineItem/ConditionalDiscount.ts +0 -102
- package/src/lineItem/FixedItemDiscount.ts +0 -51
- package/src/lineItem/PercentageDiscount.ts +0 -44
- package/src/lineItem/api.ts +0 -2103
- package/src/shipping/FixedDiscount.ts +0 -37
- package/src/shipping/RateNameSelector.ts +0 -54
- package/src/shipping/ShippingDiscount.ts +0 -75
- package/src/shipping/api.ts +0 -2014
- package/tests/AndSelector.test.ts +0 -27
- package/tests/CartQuantityQualifier.test.ts +0 -381
- package/tests/CountryCodeQualifier.test.ts +0 -55
- package/tests/CustomerSubscriberQualifier.test.ts +0 -101
- package/tests/CustomerTagQualifier.test.ts +0 -71
- package/tests/DiscountCart.test.ts +0 -115
- package/tests/OrSelector.test.ts +0 -27
- package/tests/ProductIdSelector.test.ts +0 -83
- package/tests/ProductTagSelector.test.ts +0 -75
- package/tests/Qualifier.test.ts +0 -193
- package/tests/RateNameSelector.test.ts +0 -107
- package/tests/SaleItemSelector.test.ts +0 -113
- package/tests/Selector.test.ts +0 -83
- package/tests/ShippingDiscount.test.ts +0 -147
- package/tsconfig.json +0 -25
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CartLine,
|
|
3
|
-
ProductVariant,
|
|
4
|
-
} from '../lineItem/api';
|
|
5
|
-
import { MatchType, Selector } from './Selector';
|
|
6
|
-
|
|
7
|
-
export class ProductTypeSelector extends Selector {
|
|
8
|
-
invert: boolean;
|
|
9
|
-
productTypes: string[];
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
matchType: MatchType,
|
|
13
|
-
productTypes: string[]
|
|
14
|
-
) {
|
|
15
|
-
super();
|
|
16
|
-
this.invert = matchType == MatchType.NOT_ONE;
|
|
17
|
-
this.productTypes = productTypes.map((item) =>
|
|
18
|
-
item.toLowerCase()
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
match(subject: unknown): boolean;
|
|
23
|
-
match(lineItem: CartLine) {
|
|
24
|
-
const variant = lineItem.merchandise as ProductVariant;
|
|
25
|
-
|
|
26
|
-
if (variant.product.productType) {
|
|
27
|
-
const matchResult = this.productTypes.includes(
|
|
28
|
-
variant.product.productType.toLowerCase()
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
if (this.invert) {
|
|
32
|
-
return !matchResult;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return matchResult;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
package/src/common/Qualifier.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { DiscountCart } from './DiscountCart';
|
|
2
|
-
import type { Selector } from './Selector';
|
|
3
|
-
|
|
4
|
-
export enum QualifierMatchType {
|
|
5
|
-
DOES = ':does',
|
|
6
|
-
DOES_NOT = ':does_not',
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export enum StringComparisonType {
|
|
10
|
-
MATCH = ':match',
|
|
11
|
-
CONTAINS = ':contains',
|
|
12
|
-
START_WITH = ':start_with',
|
|
13
|
-
END_WITH = ':end_with',
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export enum NumericalComparisonType {
|
|
17
|
-
GREATER_THAN = ':greater_than',
|
|
18
|
-
GREATER_THAN_OR_EQUAL = ':greater_than_or_equal',
|
|
19
|
-
LESS_THAN = ':less_than',
|
|
20
|
-
LESS_THAN_OR_EQUAL = ':less_than_or_equal',
|
|
21
|
-
EQUAL_TO = ':equal_to',
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// export enum ComparisonType {
|
|
25
|
-
// GREATER_THAN = ":greater_than",
|
|
26
|
-
// GREATER_THAN_OR_EQUAL = ":greater_than_or_equal",
|
|
27
|
-
// LESS_THAN = ":less_than",
|
|
28
|
-
// LESS_THAN_OR_EQUAL = ":less_than_or_equal",
|
|
29
|
-
// EQUAL_TO = ":equal_to",
|
|
30
|
-
// }
|
|
31
|
-
|
|
32
|
-
export enum QualifierBehavior {
|
|
33
|
-
ALL = ':all',
|
|
34
|
-
ANY = ':any',
|
|
35
|
-
}
|
|
36
|
-
export class Qualifier {
|
|
37
|
-
is_a: 'Qualifier';
|
|
38
|
-
|
|
39
|
-
constructor() {
|
|
40
|
-
this.is_a = 'Qualifier';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
compareAmounts(
|
|
44
|
-
compare: number,
|
|
45
|
-
comparisonType: NumericalComparisonType,
|
|
46
|
-
compareTo: number
|
|
47
|
-
) {
|
|
48
|
-
switch (comparisonType) {
|
|
49
|
-
case NumericalComparisonType.GREATER_THAN:
|
|
50
|
-
return compare > compareTo;
|
|
51
|
-
case NumericalComparisonType.GREATER_THAN_OR_EQUAL:
|
|
52
|
-
return compare >= compareTo;
|
|
53
|
-
case NumericalComparisonType.LESS_THAN:
|
|
54
|
-
return compare < compareTo;
|
|
55
|
-
case NumericalComparisonType.LESS_THAN_OR_EQUAL:
|
|
56
|
-
return compare <= compareTo;
|
|
57
|
-
case NumericalComparisonType.EQUAL_TO:
|
|
58
|
-
return compare == compareTo;
|
|
59
|
-
default:
|
|
60
|
-
throw new Error('Invalid comparison type');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
match(discountCart: DiscountCart, selector?: Selector) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { CartLine } from '../lineItem/api';
|
|
2
|
-
import { Selector } from './Selector';
|
|
3
|
-
|
|
4
|
-
export enum SaleItemSelectorMatchType {
|
|
5
|
-
IS = ':is',
|
|
6
|
-
NOT = ':not',
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class SaleItemSelector extends Selector {
|
|
10
|
-
invert: boolean;
|
|
11
|
-
|
|
12
|
-
constructor(matchType: SaleItemSelectorMatchType) {
|
|
13
|
-
super();
|
|
14
|
-
// Unlike other common scenarios, we invert if we pass an :IS
|
|
15
|
-
// because it is easier to figure out of the item is NOT on sale.
|
|
16
|
-
this.invert = matchType == SaleItemSelectorMatchType.IS;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
match(subject: unknown): boolean;
|
|
20
|
-
match(lineItem: CartLine) {
|
|
21
|
-
// Check to see if the item is NOT on sale
|
|
22
|
-
const matchResult =
|
|
23
|
-
!lineItem.cost.compareAtAmountPerQuantity ||
|
|
24
|
-
parseFloat(
|
|
25
|
-
lineItem.cost.compareAtAmountPerQuantity.amount
|
|
26
|
-
) <=
|
|
27
|
-
parseFloat(lineItem.cost.amountPerQuantity.amount);
|
|
28
|
-
|
|
29
|
-
// If we want to check that the item IS on sale, we'll invert the result
|
|
30
|
-
if (this.invert) {
|
|
31
|
-
return !matchResult;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return matchResult;
|
|
35
|
-
}
|
|
36
|
-
}
|
package/src/common/Selector.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { CartDeliveryOption } from '../shipping/api';
|
|
2
|
-
import type { CartLine } from '../lineItem/api';
|
|
3
|
-
import { StringComparisonType } from './Qualifier';
|
|
4
|
-
|
|
5
|
-
export enum MatchType {
|
|
6
|
-
'ALL' = ':all',
|
|
7
|
-
'IS_ONE' = ':is_one',
|
|
8
|
-
'NOT_ONE' = ':not_one',
|
|
9
|
-
'DOES' = ':does',
|
|
10
|
-
'DOES_NOT' = ':does_not',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export type SelectorInterface = {
|
|
14
|
-
match(subject: CartLine): boolean;
|
|
15
|
-
match(subject: CartDeliveryOption): boolean;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export class Selector implements SelectorInterface {
|
|
19
|
-
match(subject: CartLine): boolean;
|
|
20
|
-
match(subject: CartDeliveryOption): boolean;
|
|
21
|
-
match(subject: unknown): boolean {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Checks each member of the subject array for possible matches. Returns true when any possible match is made.
|
|
27
|
-
* @param matchType StringComparisonType
|
|
28
|
-
* @param itemInfo string | string[]
|
|
29
|
-
* @param possibleMatches string[]
|
|
30
|
-
* @returns boolean
|
|
31
|
-
*/
|
|
32
|
-
partialMatch(
|
|
33
|
-
matchType:
|
|
34
|
-
| StringComparisonType.CONTAINS
|
|
35
|
-
| StringComparisonType.START_WITH
|
|
36
|
-
| StringComparisonType.END_WITH,
|
|
37
|
-
itemInfo: string | string[],
|
|
38
|
-
possibleMatches: string[]
|
|
39
|
-
) {
|
|
40
|
-
// Convert to an array if it isn't one
|
|
41
|
-
const subject = Array.isArray(itemInfo)
|
|
42
|
-
? itemInfo
|
|
43
|
-
: [itemInfo];
|
|
44
|
-
let foundMatch = false;
|
|
45
|
-
possibleMatches.forEach((possibility) => {
|
|
46
|
-
subject.forEach((search) => {
|
|
47
|
-
switch (matchType) {
|
|
48
|
-
case StringComparisonType.CONTAINS:
|
|
49
|
-
if (search.search(possibility) > -1)
|
|
50
|
-
foundMatch = true;
|
|
51
|
-
break;
|
|
52
|
-
case StringComparisonType.START_WITH:
|
|
53
|
-
if (search.startsWith(possibility))
|
|
54
|
-
foundMatch = true;
|
|
55
|
-
break;
|
|
56
|
-
case StringComparisonType.END_WITH:
|
|
57
|
-
if (search.endsWith(possibility))
|
|
58
|
-
foundMatch = true;
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return foundMatch;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { CartLine } from '../lineItem/api';
|
|
2
|
-
import { MatchType, Selector } from './Selector';
|
|
3
|
-
|
|
4
|
-
export class SubscriptionItemSelector extends Selector {
|
|
5
|
-
invert: boolean;
|
|
6
|
-
|
|
7
|
-
constructor(matchType: MatchType) {
|
|
8
|
-
super();
|
|
9
|
-
this.invert = matchType == MatchType.NOT_ONE;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
match(subject: unknown): boolean;
|
|
13
|
-
match(lineItem: CartLine) {
|
|
14
|
-
const matchResult =
|
|
15
|
-
lineItem.sellingPlanAllocation !== undefined;
|
|
16
|
-
|
|
17
|
-
if (this.invert) {
|
|
18
|
-
return !matchResult;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
package/src/lineItem/BuyXGetY.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { Campaign } from '../common/Campaign';
|
|
2
|
-
import type { AndSelector } from '../common/AndSelector';
|
|
3
|
-
import type { DiscountCart } from '../common/DiscountCart';
|
|
4
|
-
import type {
|
|
5
|
-
ApplyDiscount,
|
|
6
|
-
DiscountInterface,
|
|
7
|
-
} from '../common/DiscountInterface';
|
|
8
|
-
import type { OrSelector } from '../common/OrSelector';
|
|
9
|
-
import type {
|
|
10
|
-
Qualifier,
|
|
11
|
-
QualifierBehavior,
|
|
12
|
-
} from '../common/Qualifier';
|
|
13
|
-
import type { Selector } from '../common/Selector';
|
|
14
|
-
|
|
15
|
-
export class BuyXGetY extends Campaign {
|
|
16
|
-
lineItemSelector?: Selector;
|
|
17
|
-
getItemSelector?: Selector;
|
|
18
|
-
discount: DiscountInterface;
|
|
19
|
-
buyX: number;
|
|
20
|
-
getY: number;
|
|
21
|
-
maxSets?: number;
|
|
22
|
-
|
|
23
|
-
constructor(
|
|
24
|
-
behavior: QualifierBehavior,
|
|
25
|
-
conditions: [AndSelector | OrSelector | Qualifier],
|
|
26
|
-
discount: DiscountInterface,
|
|
27
|
-
buyItemSelector: Selector,
|
|
28
|
-
getItemSelector: Selector,
|
|
29
|
-
buyX: number,
|
|
30
|
-
getY: number,
|
|
31
|
-
maxSets?: number
|
|
32
|
-
) {
|
|
33
|
-
super(behavior, conditions);
|
|
34
|
-
this.lineItemSelector = buyItemSelector;
|
|
35
|
-
this.getItemSelector = getItemSelector;
|
|
36
|
-
this.discount = discount;
|
|
37
|
-
this.buyX = buyX;
|
|
38
|
-
this.getY = getY;
|
|
39
|
-
this.maxSets = maxSets;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
run(discountCart: DiscountCart) {
|
|
43
|
-
if (this.qualifies(discountCart)) {
|
|
44
|
-
// Find the items that qualify for buyX
|
|
45
|
-
let applicableBuyItems = discountCart.cart.lines;
|
|
46
|
-
if (this.lineItemSelector) {
|
|
47
|
-
applicableBuyItems = discountCart.cart.lines.filter(
|
|
48
|
-
(lineItem) => {
|
|
49
|
-
// @ts-ignore
|
|
50
|
-
return this.lineItemSelector?.match(lineItem);
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
applicableBuyItems.reduce(
|
|
57
|
-
(qty, item) => (qty += item.quantity),
|
|
58
|
-
0
|
|
59
|
-
) < this.buyX
|
|
60
|
-
) {
|
|
61
|
-
return discountCart;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Find the items that qualify for getX
|
|
65
|
-
let eligibleGetItems = discountCart.cart.lines;
|
|
66
|
-
if (this.getItemSelector) {
|
|
67
|
-
eligibleGetItems = discountCart.cart.lines.filter(
|
|
68
|
-
(lineItem) => {
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
return this.getItemSelector?.match(lineItem);
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check if cart qualifies for discounts and limit the discount sets
|
|
76
|
-
const purchasedQuantity = applicableBuyItems.reduce(
|
|
77
|
-
(total, item) => (total += item.quantity),
|
|
78
|
-
0
|
|
79
|
-
);
|
|
80
|
-
const discountableSets = this.maxSets
|
|
81
|
-
? [
|
|
82
|
-
purchasedQuantity / this.buyX,
|
|
83
|
-
this.maxSets,
|
|
84
|
-
].sort((a, b) => a - b)[0]
|
|
85
|
-
: Math.floor(purchasedQuantity / this.buyX);
|
|
86
|
-
|
|
87
|
-
if (discountableSets > 0) {
|
|
88
|
-
let discountableQuantity =
|
|
89
|
-
discountableSets *
|
|
90
|
-
(this.getY
|
|
91
|
-
? this.getY
|
|
92
|
-
: discountCart.totalLineItemQuantity());
|
|
93
|
-
|
|
94
|
-
// Apply the discounts (sort to discount lower priced items first)
|
|
95
|
-
eligibleGetItems = eligibleGetItems.sort(
|
|
96
|
-
(item) => item.cost.amountPerQuantity.amount
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const itemsToApplyDiscounts: ApplyDiscount[] = [];
|
|
100
|
-
|
|
101
|
-
eligibleGetItems.forEach((lineItem) => {
|
|
102
|
-
if (discountableQuantity == 0) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (lineItem.quantity <= discountableQuantity) {
|
|
107
|
-
itemsToApplyDiscounts.push({
|
|
108
|
-
lineItem,
|
|
109
|
-
} as ApplyDiscount);
|
|
110
|
-
discountableQuantity -= lineItem.quantity;
|
|
111
|
-
} else {
|
|
112
|
-
itemsToApplyDiscounts.push({
|
|
113
|
-
lineItem,
|
|
114
|
-
maxDiscounts: discountableQuantity,
|
|
115
|
-
} as ApplyDiscount);
|
|
116
|
-
discountableQuantity = 0;
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
if (itemsToApplyDiscounts.length) {
|
|
121
|
-
const discount = this.discount.apply(
|
|
122
|
-
itemsToApplyDiscounts
|
|
123
|
-
);
|
|
124
|
-
discountCart.addDiscount(discount);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return discountCart;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { CartLine } from './api';
|
|
2
|
-
import { Campaign } from '../common/Campaign';
|
|
3
|
-
import type { DiscountCart } from '../common/DiscountCart';
|
|
4
|
-
import type {
|
|
5
|
-
ApplyDiscount,
|
|
6
|
-
DiscountInterface,
|
|
7
|
-
} from '../common/DiscountInterface';
|
|
8
|
-
import type {
|
|
9
|
-
QualifierBehavior,
|
|
10
|
-
Qualifier,
|
|
11
|
-
} from '../common/Qualifier';
|
|
12
|
-
import type { Selector } from '../common/Selector';
|
|
13
|
-
import type { AndSelector } from '../common/AndSelector';
|
|
14
|
-
import type { OrSelector } from '../common/OrSelector';
|
|
15
|
-
|
|
16
|
-
export class ConditionalDiscount extends Campaign {
|
|
17
|
-
discount: DiscountInterface;
|
|
18
|
-
itemsToDiscount?: number;
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
behavior: QualifierBehavior,
|
|
22
|
-
qualifiers: [Qualifier | AndSelector | OrSelector],
|
|
23
|
-
discount: DiscountInterface,
|
|
24
|
-
lineItemSelector?: Selector,
|
|
25
|
-
maxDiscounts?: number
|
|
26
|
-
) {
|
|
27
|
-
super(behavior, qualifiers);
|
|
28
|
-
this.discount = discount;
|
|
29
|
-
this.lineItemSelector = lineItemSelector;
|
|
30
|
-
this.itemsToDiscount =
|
|
31
|
-
maxDiscounts == 0 ? undefined : maxDiscounts;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
run(discountCart: DiscountCart) {
|
|
35
|
-
// First we see if this cart qualifies for this discount
|
|
36
|
-
if (this.qualifies(discountCart)) {
|
|
37
|
-
// Now that it qualifies, we can determine the applicableLineItems
|
|
38
|
-
// to apply discounts to
|
|
39
|
-
|
|
40
|
-
// figure out applicable lineItems...
|
|
41
|
-
const applicableLineItems = discountCart.cart.lines
|
|
42
|
-
.filter((lineItem) => {
|
|
43
|
-
if (this.lineItemSelector === undefined) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
return this.lineItemSelector.match(
|
|
47
|
-
lineItem as CartLine
|
|
48
|
-
);
|
|
49
|
-
})
|
|
50
|
-
.sort((a, b) => {
|
|
51
|
-
const priceA = a.cost.amountPerQuantity
|
|
52
|
-
.amount as number;
|
|
53
|
-
const priceB = b.cost.amountPerQuantity
|
|
54
|
-
.amount as number;
|
|
55
|
-
|
|
56
|
-
if (priceA < priceB) return -1;
|
|
57
|
-
if (priceA == priceB) return 0;
|
|
58
|
-
return 1;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
const itemsToApplyDiscounts: ApplyDiscount[] = [];
|
|
62
|
-
|
|
63
|
-
// Loop over the applicable line items and deduct the itemsToDiscount count
|
|
64
|
-
// as they are applied to not exceed a maximum.
|
|
65
|
-
applicableLineItems.forEach((lineItem) => {
|
|
66
|
-
// If there are no more items to discount, return/skip
|
|
67
|
-
if (this.itemsToDiscount === 0) return;
|
|
68
|
-
|
|
69
|
-
// If items to discount is not undefined, apply as many as possible
|
|
70
|
-
if (this.itemsToDiscount !== undefined) {
|
|
71
|
-
// There are more than enough qty to apply, apply the maximum
|
|
72
|
-
if (lineItem.quantity >= this.itemsToDiscount) {
|
|
73
|
-
itemsToApplyDiscounts.push({
|
|
74
|
-
lineItem,
|
|
75
|
-
maxDiscounts: this.itemsToDiscount,
|
|
76
|
-
} as ApplyDiscount);
|
|
77
|
-
this.itemsToDiscount = 0;
|
|
78
|
-
} else {
|
|
79
|
-
itemsToApplyDiscounts.push({
|
|
80
|
-
lineItem,
|
|
81
|
-
} as ApplyDiscount);
|
|
82
|
-
this.itemsToDiscount -= lineItem.quantity;
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
// items to discount is undefined, so apply to entire item count
|
|
86
|
-
itemsToApplyDiscounts.push({
|
|
87
|
-
lineItem,
|
|
88
|
-
} as ApplyDiscount);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (itemsToApplyDiscounts.length) {
|
|
92
|
-
// append the discount to the discountCart singleton
|
|
93
|
-
const discount = this.discount.apply(
|
|
94
|
-
itemsToApplyDiscounts
|
|
95
|
-
);
|
|
96
|
-
discountCart.addDiscount(discount);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return discountCart;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { ProductVariant } from './api';
|
|
2
|
-
import type {
|
|
3
|
-
ApplyDiscount,
|
|
4
|
-
Discount,
|
|
5
|
-
DiscountInterface,
|
|
6
|
-
} from '../common/DiscountInterface';
|
|
7
|
-
|
|
8
|
-
export class FixedItemDiscount
|
|
9
|
-
implements DiscountInterface
|
|
10
|
-
{
|
|
11
|
-
amount: number;
|
|
12
|
-
appliesToEachItem: boolean;
|
|
13
|
-
message: string;
|
|
14
|
-
|
|
15
|
-
constructor(
|
|
16
|
-
amount: number,
|
|
17
|
-
appliesToEachItem: boolean,
|
|
18
|
-
message: string
|
|
19
|
-
) {
|
|
20
|
-
this.amount = amount;
|
|
21
|
-
this.appliesToEachItem = appliesToEachItem;
|
|
22
|
-
this.message = message;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
apply(items: ApplyDiscount[]): Discount {
|
|
26
|
-
const targets = items.map(
|
|
27
|
-
({ lineItem, maxDiscounts }) => {
|
|
28
|
-
const variant =
|
|
29
|
-
lineItem.merchandise as ProductVariant;
|
|
30
|
-
return {
|
|
31
|
-
productVariant: {
|
|
32
|
-
id: variant.id,
|
|
33
|
-
quantity:
|
|
34
|
-
maxDiscounts == 0 ? undefined : maxDiscounts,
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
targets,
|
|
42
|
-
value: {
|
|
43
|
-
fixedAmount: {
|
|
44
|
-
amount: this.amount,
|
|
45
|
-
appliesToEachItem: this.appliesToEachItem,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
message: this.message,
|
|
49
|
-
} as Discount;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { ProductVariant } from './api';
|
|
2
|
-
import type {
|
|
3
|
-
Discount,
|
|
4
|
-
ApplyDiscount,
|
|
5
|
-
DiscountInterface,
|
|
6
|
-
} from '../common/DiscountInterface';
|
|
7
|
-
|
|
8
|
-
export class PercentageDiscount
|
|
9
|
-
implements DiscountInterface
|
|
10
|
-
{
|
|
11
|
-
discount: number;
|
|
12
|
-
message?: string;
|
|
13
|
-
|
|
14
|
-
constructor(percentage: number, message?: string) {
|
|
15
|
-
this.discount = percentage;
|
|
16
|
-
this.message = message;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
apply(items: ApplyDiscount[]): Discount {
|
|
20
|
-
const targets = items.map(
|
|
21
|
-
({ lineItem, maxDiscounts }) => {
|
|
22
|
-
const variant =
|
|
23
|
-
lineItem.merchandise as ProductVariant;
|
|
24
|
-
return {
|
|
25
|
-
productVariant: {
|
|
26
|
-
id: variant.id,
|
|
27
|
-
quantity:
|
|
28
|
-
maxDiscounts == 0 ? undefined : maxDiscounts,
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
targets,
|
|
36
|
-
value: {
|
|
37
|
-
percentage: {
|
|
38
|
-
value: this.discount,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
message: this.message,
|
|
42
|
-
} as Discount;
|
|
43
|
-
}
|
|
44
|
-
}
|