shufflecom-calculations 1.12.19 → 1.12.21
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/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/derive-vip-bonus.js +6 -8
- package/lib/utils/derive-vip-bonus.js.map +1 -1
- package/lib/utils/validate-cashout-avaliable.d.ts +3 -7
- package/lib/utils/validate-cashout-avaliable.js +36 -37
- package/lib/utils/validate-cashout-avaliable.js.map +1 -1
- package/lib/utils/vip-bonus.type.d.ts +2 -1
- package/lib/utils/vip-bonus.type.js.map +1 -1
- package/package.json +2 -2
- package/src/utils/derive-vip-bonus.spec.ts +214 -127
- package/src/utils/derive-vip-bonus.ts +26 -14
- package/src/utils/validate-cashout-avaliable.ts +50 -46
- package/src/utils/vip-bonus.type.ts +2 -1
|
@@ -21,19 +21,26 @@ export function getReloadAvailableDate(activationTime: Dayjs | null, cadence: nu
|
|
|
21
21
|
return activationTime.add(cadence * (index - 1), 'second').toDate();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function getReloadExpiryDate(
|
|
24
|
+
export function getReloadExpiryDate(
|
|
25
|
+
activationTime: Dayjs | null,
|
|
26
|
+
cadence: number,
|
|
27
|
+
expiryPerOccurrence: number,
|
|
28
|
+
index: number,
|
|
29
|
+
createdAt: Dayjs,
|
|
30
|
+
firstOccurrenceActivationPeriod: number,
|
|
31
|
+
) {
|
|
25
32
|
const availableDate = getReloadAvailableDate(activationTime, cadence, index, createdAt);
|
|
26
33
|
|
|
27
34
|
if (index === 1) {
|
|
28
35
|
return dayjs(createdAt).add(firstOccurrenceActivationPeriod, 'hours').add(expiryPerOccurrence, 'second').toDate();
|
|
29
36
|
}
|
|
30
|
-
|
|
37
|
+
|
|
31
38
|
if (!availableDate) {
|
|
32
|
-
return null
|
|
39
|
+
return null;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
const result = dayjs(availableDate).add(expiryPerOccurrence, 'second').toDate();
|
|
36
|
-
return result
|
|
43
|
+
return result;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
export function deriveBonusIssuances(vipBonus: VipBonusPartial) {
|
|
@@ -42,7 +49,7 @@ export function deriveBonusIssuances(vipBonus: VipBonusPartial) {
|
|
|
42
49
|
const bonusCreatedAt = dayjs(vipBonus.createdAt);
|
|
43
50
|
const now = dayjs();
|
|
44
51
|
|
|
45
|
-
const firstClaim = claims?.find(
|
|
52
|
+
const firstClaim = claims?.find(claim => claim.occurrenceIndex == 1);
|
|
46
53
|
let activationTime: Dayjs | null = null;
|
|
47
54
|
const maxBonusActivationTime = bonusCreatedAt.add(vipBonus.firstOccurrenceActivationPeriod ?? 0, 'hours');
|
|
48
55
|
|
|
@@ -52,11 +59,18 @@ export function deriveBonusIssuances(vipBonus: VipBonusPartial) {
|
|
|
52
59
|
activationTime = maxBonusActivationTime;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
return range(1, vipBonus.occurrence + 1).map(
|
|
56
|
-
const claim = claims?.find(
|
|
62
|
+
return range(1, vipBonus.occurrence + 1).map(occurrenceIndex => {
|
|
63
|
+
const claim = claims?.find(claim => claim.occurrenceIndex == occurrenceIndex);
|
|
57
64
|
const expiryDate =
|
|
58
65
|
vipBonus.type === VipBonusType.RELOADED
|
|
59
|
-
? getReloadExpiryDate(
|
|
66
|
+
? getReloadExpiryDate(
|
|
67
|
+
activationTime,
|
|
68
|
+
vipBonus.cadence!,
|
|
69
|
+
vipBonus.expiryPerOccurrence ?? 0,
|
|
70
|
+
occurrenceIndex,
|
|
71
|
+
bonusCreatedAt,
|
|
72
|
+
vipBonus.firstOccurrenceActivationPeriod ?? 0,
|
|
73
|
+
)?.toISOString()
|
|
60
74
|
: vipBonus.endAt;
|
|
61
75
|
const availableDate =
|
|
62
76
|
vipBonus.type === VipBonusType.RELOADED
|
|
@@ -65,18 +79,16 @@ export function deriveBonusIssuances(vipBonus: VipBonusPartial) {
|
|
|
65
79
|
return updateBonusIssuanceStatus(
|
|
66
80
|
{
|
|
67
81
|
currency,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.decimalPlaces(8, BigNumber.ROUND_DOWN)
|
|
71
|
-
.toFixed(),
|
|
82
|
+
amount: claim?.amount,
|
|
83
|
+
bonusAmountUsd: new BigNumber(vipBonus.bonusAmountUsd).dividedBy(vipBonus.occurrence).decimalPlaces(8, BigNumber.ROUND_DOWN).toFixed(),
|
|
72
84
|
occurrenceIndex,
|
|
73
85
|
expiryDate,
|
|
74
86
|
availableDate,
|
|
75
87
|
status: claim ? VipBonusIssuanceStatus.CLAIMED : null,
|
|
76
|
-
createdAt: claim?.createdAt
|
|
88
|
+
createdAt: claim?.createdAt,
|
|
77
89
|
},
|
|
78
90
|
vipBonus.cancelledAt ? new Date(vipBonus.cancelledAt) : null,
|
|
79
|
-
now
|
|
91
|
+
now,
|
|
80
92
|
);
|
|
81
93
|
});
|
|
82
94
|
}
|
|
@@ -22,49 +22,57 @@ export enum SportsCashoutFailedReason {
|
|
|
22
22
|
SPORTS_MARKET_CASHOUT_NOT_ENABLED = 'SPORTS_MARKET_CASHOUT_NOT_ENABLED',
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
class SportsCashoutError extends Error {
|
|
26
|
+
sportsBetSelection?: SportsBetSelectionInterface;
|
|
27
|
+
market?: SportsMarketInterface;
|
|
28
|
+
marketSelection?: SportsMarketSelectionInterface;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
reason: SportsCashoutFailedReason,
|
|
32
|
+
{
|
|
33
|
+
sportsBetSelection,
|
|
34
|
+
market,
|
|
35
|
+
marketSelection,
|
|
36
|
+
}: {
|
|
33
37
|
sportsBetSelection?: SportsBetSelectionInterface;
|
|
34
38
|
market?: SportsMarketInterface;
|
|
35
39
|
marketSelection?: SportsMarketSelectionInterface;
|
|
36
|
-
}
|
|
37
|
-
|
|
40
|
+
} = {},
|
|
41
|
+
) {
|
|
42
|
+
super(reason);
|
|
43
|
+
|
|
44
|
+
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example
|
|
45
|
+
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
|
|
46
|
+
|
|
47
|
+
this.sportsBetSelection = sportsBetSelection;
|
|
48
|
+
this.market = market;
|
|
49
|
+
this.marketSelection = marketSelection;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function validateSportsBetCashoutAvailable(sportsBet: SportsBetInterface, sportsBetSelections: SportsBetSelectionInterface[]) {
|
|
38
54
|
if (SportsBetStatus.PENDING !== sportsBet.status) {
|
|
39
|
-
|
|
40
|
-
reason: SportsCashoutFailedReason.SPORTS_BET_IS_CLOSED,
|
|
41
|
-
};
|
|
55
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_BET_IS_CLOSED);
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
const containsLostBetSelection = sportsBetSelections.find(({ status }) => status === SportsBetSelectionStatus.LOST);
|
|
45
59
|
|
|
46
60
|
if (containsLostBetSelection) {
|
|
47
|
-
|
|
48
|
-
reason: SportsCashoutFailedReason.SPORTS_BET_IS_CLOSED,
|
|
49
|
-
sportsBetSelection: containsLostBetSelection,
|
|
50
|
-
};
|
|
61
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_BET_IS_CLOSED, { sportsBetSelection: containsLostBetSelection });
|
|
51
62
|
}
|
|
63
|
+
}
|
|
52
64
|
|
|
65
|
+
export function validateSportsMarketsCashoutAvailable(markets: SportsMarketInterface[], marketSelections: SportsMarketSelectionInterface[]) {
|
|
53
66
|
const containsManualMarketSelection = marketSelections.find(({ provider }) => provider === SportsMarketProvider.SHUFFLE);
|
|
54
67
|
|
|
55
68
|
if (containsManualMarketSelection) {
|
|
56
|
-
|
|
57
|
-
reason: SportsCashoutFailedReason.MANUAL_MARKET_NOT_ALLOWED_CASHOUT,
|
|
58
|
-
marketSelection: containsManualMarketSelection,
|
|
59
|
-
};
|
|
69
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.MANUAL_MARKET_NOT_ALLOWED_CASHOUT, { marketSelection: containsManualMarketSelection });
|
|
60
70
|
}
|
|
61
71
|
|
|
62
72
|
const atLeastOneTrading = marketSelections.some(({ status }) => status === SportsMarketSelectionStatus.TRADING);
|
|
63
73
|
|
|
64
74
|
if (!atLeastOneTrading) {
|
|
65
|
-
|
|
66
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN,
|
|
67
|
-
};
|
|
75
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN);
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
const containsUnavailableMarketSelection = marketSelections.find(marketSelection => {
|
|
@@ -78,10 +86,10 @@ function checkCashoutAvailable(
|
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
if (containsUnavailableMarketSelection) {
|
|
81
|
-
|
|
82
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN,
|
|
89
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN, {
|
|
83
90
|
marketSelection: containsUnavailableMarketSelection,
|
|
84
|
-
|
|
91
|
+
market: markets.find(market => market.id === containsUnavailableMarketSelection.marketId),
|
|
92
|
+
});
|
|
85
93
|
}
|
|
86
94
|
|
|
87
95
|
for (const marketSelection of marketSelections) {
|
|
@@ -90,32 +98,20 @@ function checkCashoutAvailable(
|
|
|
90
98
|
const market = markets.find(market => market.id === marketSelection.marketId);
|
|
91
99
|
|
|
92
100
|
if (!market) {
|
|
93
|
-
|
|
94
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_NOT_FOUND,
|
|
95
|
-
market,
|
|
96
|
-
};
|
|
101
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_NOT_FOUND, { market });
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
if (!market.inPlay && dayjs().isAfter(market.expiryTime)) {
|
|
100
|
-
|
|
101
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_CLOSED,
|
|
102
|
-
market,
|
|
103
|
-
};
|
|
105
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_CLOSED, { market });
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
if (market.inPlay) {
|
|
107
109
|
if (!market.cashoutEnabled) {
|
|
108
|
-
|
|
109
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_NOT_ENABLED,
|
|
110
|
-
market,
|
|
111
|
-
};
|
|
110
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_NOT_ENABLED, { market });
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
if (market.cashoutStatus !== SportsMarketCashoutStatus.OPEN) {
|
|
115
|
-
|
|
116
|
-
reason: SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN,
|
|
117
|
-
market,
|
|
118
|
-
};
|
|
114
|
+
throw new SportsCashoutError(SportsCashoutFailedReason.SPORTS_MARKET_CASHOUT_IS_NOT_OPEN, { market });
|
|
119
115
|
}
|
|
120
116
|
}
|
|
121
117
|
}
|
|
@@ -134,10 +130,18 @@ export function isCashoutAvailable({
|
|
|
134
130
|
markets: SportsMarketInterface[];
|
|
135
131
|
marketSelections: SportsMarketSelectionInterface[];
|
|
136
132
|
}) {
|
|
137
|
-
|
|
133
|
+
try {
|
|
134
|
+
validateSportsBetCashoutAvailable(sportsBet, sportsBetSelections);
|
|
135
|
+
validateSportsMarketsCashoutAvailable(markets, marketSelections);
|
|
136
|
+
} catch (error: any) {
|
|
137
|
+
return {
|
|
138
|
+
canCashout: false,
|
|
139
|
+
reason: error.message,
|
|
140
|
+
...error,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
138
143
|
|
|
139
144
|
return {
|
|
140
|
-
canCashout:
|
|
141
|
-
...cashoutAvailableState,
|
|
145
|
+
canCashout: true,
|
|
142
146
|
};
|
|
143
147
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export interface VipBonusIssuanceModel {
|
|
2
2
|
currency?: string | null;
|
|
3
|
-
bonusAmountUsd: string;
|
|
4
3
|
occurrenceIndex: number;
|
|
4
|
+
amount?: string | null;
|
|
5
|
+
bonusAmountUsd?: string | null; // derived field
|
|
5
6
|
createdAt?: string; // derived field
|
|
6
7
|
expiryDate?: string; // derived field
|
|
7
8
|
availableDate?: string; // derived field
|