@devx-commerce/plugin-loyalty
Version:
A starter for Medusa plugins.
613 lines • 51.1 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@medusajs/framework/utils");
const helper_1 = require("../../utils/helper");
const models_1 = require("./models");
class LoyaltyModuleService extends (0, utils_1.MedusaService)({
Event: models_1.EventModel,
Wallet: models_1.WalletModel,
// Transaction: LoyaltyTransaction,
LoyaltyTransaction: models_1.LoyaltyTransaction,
Referral: models_1.ReferralModel,
TransactionBucket: models_1.TransactionBucketModel,
TransactionHistory: models_1.TransactionHistoryModel,
Config: models_1.ConfigModel,
}) {
constructor(container) {
super(container);
this.logger_ = container.logger;
this.logger_.info("[LoyaltyModuleService]: Service initialized");
}
async addPoints_({ customer_id, points, referenceType = "manual", description, expires_at, order_id, referrer, referee, }, sharedContext) {
if (points <= 0)
return null;
this.logger_.info(`[LoyaltyModuleService]: Adding ${points} points for customer ${customer_id}`);
const wallet = await this.createOrRetrieveWallet(customer_id, sharedContext);
const transaction = await this.createLoyaltyTransactions({
customer_id: customer_id,
points,
type: "credit",
loyalty_wallet_id: wallet.id,
reference_type: referenceType,
description: description || "Points added",
expires_at,
order_id,
referrer,
referee,
}, sharedContext);
await this.createTransactionBuckets({
loyalty_wallet_id: wallet.id,
loyalty_transaction_id: transaction.id,
customer_id: customer_id,
original_points: points,
remaining_points: points,
status: "active",
reference_type: referenceType,
description: description || "Points added",
expires_at,
}, sharedContext);
const isPerpetual = !expires_at;
const updatedWallet = await this.updateWallets({
id: wallet.id,
points: wallet.points + points,
perpetual_points: isPerpetual
? wallet.perpetual_points + points
: wallet.perpetual_points,
total_earned_points: wallet.total_earned_points + points,
}, sharedContext);
this.logger_.info(`[LoyaltyModuleService]: Successfully added ${points} points for customer ${customer_id}: ${description ? description : ""} `);
return updatedWallet;
}
async addPoints(params, sharedContext) {
return await this.addPoints_(params, sharedContext);
}
async deductPoints_(params, sharedContext) {
const { customerId, points, referenceType, description, expires_at, order_id, } = params;
const wallet = await this.createOrRetrieveWallet(customerId, sharedContext);
if (wallet.points < points) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Insufficient points");
}
const transaction = await this.createLoyaltyTransactions({
customer_id: customerId,
points,
type: "debit",
loyalty_wallet_id: wallet.id,
reference_type: referenceType,
description: description || "Points added",
expires_at,
order_id,
}, sharedContext);
const buckets = await this.listTransactionBuckets({
status: "active",
loyalty_wallet_id: wallet.id,
}, {
order: {
expires_at: "ASC",
},
});
if (buckets.length === 0) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "No active transaction buckets found");
}
const { deductedPerpetualPoints } = await this.updateRemainingPointsInBucketsAndHistory(buckets, points, transaction.id, order_id, sharedContext);
const updatedWallet = await this.updateWallets({
id: wallet.id,
points: wallet.points - points,
perpetual_points: wallet.perpetual_points - deductedPerpetualPoints,
total_used_points: referenceType === "order" ||
referenceType === "order_return" ||
referenceType === "manual"
? wallet.total_used_points + points
: wallet.total_used_points,
total_expired_points: referenceType === "expired"
? wallet.total_expired_points + points
: wallet.total_expired_points,
}, sharedContext);
this.logger_.info(`[LoyaltyModuleService]: Successfully added ${points} points for customer ${customerId}: ${description} `);
return updatedWallet;
}
async deductPoints(params, sharedContext) {
return await this.deductPoints_(params, sharedContext);
}
async updateRemainingPointsInBucketsAndHistory(buckets, pointsToDeduct, transaction_id, order_id, sharedContext) {
try {
const updatedBuckets = [];
const history = [];
let deductedPerpetualPoints = 0;
for (const bucket of buckets) {
if (pointsToDeduct <= 0)
break;
const availablePoints = bucket.remaining_points;
if (availablePoints <= 0) {
continue; // Skip depleted buckets
}
const deduction = Math.min(pointsToDeduct, availablePoints);
// Track perpetual points being deducted
const isPerpetualBucket = !bucket.expires_at;
if (isPerpetualBucket) {
deductedPerpetualPoints += deduction;
}
bucket.remaining_points = availablePoints - deduction;
pointsToDeduct -= deduction;
if (bucket.remaining_points === 0) {
bucket.status = "depleted";
}
updatedBuckets.push(bucket);
const historyItem = {
loyalty_transaction_bucket_id: bucket.id,
loyalty_wallet_id: bucket.loyalty_wallet_id,
points_used: deduction,
type: "debit",
status: "active",
reference_type: bucket.reference_type,
customer_id: bucket.customer_id,
order_id: order_id || null,
loyalty_transaction_id: transaction_id,
};
history.push(historyItem);
}
await this.createTransactionHistories(history, sharedContext);
await this.updateTransactionBuckets(updatedBuckets, sharedContext);
return { deductedPerpetualPoints };
}
catch (error) {
throw error;
}
}
async calculateAndUpdateWalletPoints(walletId, sharedContext) {
const wallet = await this.retrieveWallet(walletId, {
relations: [
"loyalty_transactions",
"loyalty_transactions.loyalty_transaction_buckets",
"loyalty_transactions.loyalty_transaction_buckets.transaction_histories",
],
}, sharedContext);
return wallet;
}
async getPoints(customerId) {
const points = await this.listWallets({
customer_id: customerId,
});
return points[0]?.points || 0;
}
async getTransactionHistory(customerId, options = {}) {
const { take = 10, skip = 0, type, referenceType } = options;
const query = {
customer_id: customerId,
};
if (type) {
query.type = type;
}
if (referenceType) {
query.reference_type = referenceType;
}
return await this.listLoyaltyTransactions(query, {
take,
skip,
order: { created_at: "DESC" },
});
}
async calculatePointsFromAmount(amount) {
const config = await this.listConfigs();
const { points_per_rupee } = config[0];
const points = Math.floor(amount * (points_per_rupee || 1));
if (points < 0) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Amount cannot be negative");
}
return points;
}
async calculatePointsToAddFromOrderEvent(event, orderTotal) {
const { earn_per_order, earn_per_order_type, max_earn_per_order, min_order_value, expiry_days, } = event;
if (min_order_value && orderTotal < min_order_value) {
return { pointsToAdd: 0, expires_at: undefined };
}
if (!earn_per_order) {
return { pointsToAdd: 0, expires_at: undefined };
}
// Calculate expiry date if applicable
const expires_at = expiry_days
? new Date(Date.now() + expiry_days * 24 * 60 * 60 * 1000)
: undefined;
let rawPoints = earn_per_order_type === "percentage"
? orderTotal * (earn_per_order / 100)
: earn_per_order;
if (max_earn_per_order) {
rawPoints = Math.min(rawPoints, max_earn_per_order);
}
const pointsToAdd = Math.ceil(rawPoints);
return { pointsToAdd, expires_at };
}
async calculatePointsOnCart(cartTotal, availablePoints, orderValue) {
const [config] = await this.listConfigs();
if (!config) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Loyalty configuration not found");
}
const { max_spend_per_order, min_order_value, spend_per_order, spend_per_order_type, points_per_rupee, } = config;
if (orderValue < min_order_value) {
return 0;
}
let applicableAmount = spend_per_order
? spend_per_order_type === "percentage"
? cartTotal * (spend_per_order / 100)
: spend_per_order
: 0;
if (max_spend_per_order) {
applicableAmount = Math.min(applicableAmount, max_spend_per_order);
}
const points = await this.calculatePointsFromAmount(applicableAmount);
if (availablePoints < points) {
const amount = (0, helper_1.calculateAmountFromPoints)(availablePoints, points_per_rupee);
if (amount <= 0) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Insufficient points");
}
return amount;
}
return Math.floor(applicableAmount);
}
async createReferral(customerId) {
let customer = await this.createOrRetrieveWallet(customerId);
const referral = await this.createReferrals({
customer_id: customer.id,
referral_code: `${Date.now()}`,
});
return referral;
}
async createOrRetrieveWallet(customerId, sharedContext) {
const wallet = await this.listWallets({
customer_id: customerId,
}, undefined, sharedContext);
if (wallet.length === 0) {
return await this.createWallets({
customer_id: customerId,
points: 0,
}, undefined, sharedContext);
}
return wallet[0];
}
async calculatePointForReferralOrder(event, referral, amount) {
const { referee_bonus_per_order, referrer_bonus_per_order, referee_bonus_per_order_type, referrer_bonus_per_order_type, expiry_days, } = event;
let referee_bonus = referee_bonus_per_order_type === "percentage" && referee_bonus_per_order
? amount * (referee_bonus_per_order / 100)
: referee_bonus_per_order;
let referrer_bonus = referrer_bonus_per_order_type === "percentage" && referrer_bonus_per_order
? amount * (referrer_bonus_per_order / 100)
: referrer_bonus_per_order;
return {
referee: {
bonus: referee_bonus ? Math.round(referee_bonus) : 0,
expires_at: expiry_days
? new Date(Date.now() + expiry_days * 24 * 60 * 60 * 1000)
: null,
},
referrer: {
bonus: referrer_bonus ? Math.round(referrer_bonus) : 0,
customer_id: referral.customer_id,
expires_at: expiry_days
? new Date(Date.now() + expiry_days * 24 * 60 * 60 * 1000)
: null,
},
};
}
async addReferralCodeInWallet(customerId, referralCode) {
const wallet = await this.createOrRetrieveWallet(customerId);
const usedReferralCode = wallet?.used_referrals || [];
const codes = [...new Set([...usedReferralCode, referralCode])].filter(Boolean);
await this.updateWallets({
id: wallet.id,
used_referrals: codes,
});
}
async expireTransaction_(bucket, sharedContext) {
try {
const { loyalty_transaction_id, remaining_points } = bucket;
const transaction = await this.retrieveLoyaltyTransaction(loyalty_transaction_id, undefined, sharedContext);
const reverseTransaction = {
type: "debit",
description: `Points expired`,
reference_type: "expired",
points: remaining_points,
loyalty_wallet_id: transaction.loyalty_wallet_id,
customer_id: transaction.customer_id,
};
await this.createLoyaltyTransactions(reverseTransaction, sharedContext);
await this.updateTransactionBuckets({
...bucket,
status: "expired",
remaining_points: 0,
}, sharedContext);
await this.createTransactionHistories({
loyalty_transaction_bucket_id: bucket.id,
loyalty_wallet_id: bucket.loyalty_wallet_id,
points_used: remaining_points,
type: "debit",
status: "active",
reference_type: "expired",
customer_id: transaction.customer_id,
order_id: transaction.order_id,
}, sharedContext);
}
catch (error) {
this.logger_.error("Error in expire transaction", error);
throw error;
}
}
async expireTransactionsByBucket(buckets) {
if (buckets.length === 0)
return;
await Promise.all(buckets.map((bucket) => this.expireTransaction_(bucket)));
const uniqueWalletIds = [
...new Set(buckets.map((bucket) => bucket.loyalty_wallet_id)),
];
await Promise.all(uniqueWalletIds.map((walletId) => this.calculateWalletPoints(walletId)));
}
async reverseDebitTransaction(transaction, sharedContext) {
const histories = await this.listTransactionHistories({
loyalty_transaction_id: transaction.id,
}, undefined, sharedContext);
if (histories.length === 0)
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "No history found");
await Promise.all(histories.map(async (h) => {
await this.updateTransactionHistories({
id: h.id,
status: "reversed",
}, sharedContext);
//find bucket
const bucket = await this.retrieveTransactionBucket(h.loyalty_transaction_bucket_id, undefined, sharedContext);
if (!bucket)
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "No bucket found");
await this.updateTransactionBuckets({
id: bucket.id,
remaining_points: bucket.remaining_points + h.points_used,
status: "active",
}, sharedContext);
}));
}
async reverseCreditTransaction(transaction, sharedContext) {
const [bucket] = await this.listTransactionBuckets({
loyalty_transaction_id: transaction.id,
status: "active",
}, undefined, sharedContext);
//find remaining points in bucket
const remainingPoints = bucket.remaining_points;
if (remainingPoints === 0)
return;
//create history
await this.createTransactionHistories({
loyalty_transaction_bucket_id: bucket.id,
points_used: remainingPoints,
type: "debit",
status: "active",
reference_type: "order_return",
customer_id: transaction.customer_id,
order_id: transaction.order_id,
loyalty_wallet_id: transaction.loyalty_wallet_id,
}, sharedContext);
//update bucket status and remaining points
await this.updateTransactionBuckets({
id: bucket.id,
status: "depleted",
remaining_points: 0,
}, sharedContext);
}
async reverseTransaction(transaction, sharedContext) {
//create reverse transaction
const reversed = {
type: transaction.type === "debit" ? "credit" : "debit",
description: `Points reversed`,
reference_type: "order_return",
points: transaction.points,
loyalty_wallet_id: transaction.loyalty_wallet_id,
customer_id: transaction.customer_id,
order_id: transaction.order_id,
};
await this.createLoyaltyTransactions(reversed, sharedContext);
//update transaction status
await this.updateLoyaltyTransactions({ id: transaction.id, status: "reversed" }, sharedContext);
}
async reverseOrderTransactions(transactions, sharedContext) {
await Promise.all(transactions.map(async (transaction) => {
if (transaction.type === "debit") {
await this.reverseDebitTransaction(transaction, sharedContext);
}
else {
await this.reverseCreditTransaction(transaction, sharedContext);
}
await this.reverseTransaction(transaction, sharedContext);
}));
}
async calculateWalletPoints(walletId) {
const buckets = await this.listTransactionBuckets({
loyalty_wallet_id: walletId,
status: "active",
}, undefined);
const totalPoints = buckets.reduce((acc, bucket) => acc + bucket.remaining_points, 0);
const perpetualPoints = buckets.reduce((acc, bucket) => {
const points = !bucket.expires_at ? bucket.remaining_points : 0;
return acc + points;
}, 0);
await this.updateWallets({
id: walletId,
points: totalPoints,
perpetual_points: perpetualPoints,
});
}
async expireReferrals(referrals) {
await Promise.all(referrals.map(async (referral) => {
await this.updateReferrals({
id: referral.id,
status: "expired",
});
}));
}
async applyReferralBonus({ referee, referrer, referee_bonus, referrer_bonus, referral_code, expires_at, order_id, }) {
const referrerWallet = await this.createOrRetrieveWallet(referrer);
const refereeWallet = await this.createOrRetrieveWallet(referee);
if (referral_code) {
if (referee_bonus && referee_bonus > 0) {
await this.addPoints({
customer_id: referee,
points: referee_bonus,
description: order_id
? `Referral bonus for order ${order_id}`
: "Referral bonus",
expires_at,
order_id,
referrer: referrerWallet.id,
referenceType: order_id ? "referral_order" : "referral",
});
await this.addReferralCodeInWallet(referee, referral_code);
}
if (referrer_bonus && referrer_bonus > 0) {
await this.addPoints({
customer_id: referrer,
points: referrer_bonus,
description: order_id
? `Referrer bonus for order ${order_id}`
: "Referrer bonus",
expires_at,
order_id,
referee: refereeWallet.id,
referenceType: order_id ? "referral_order" : "referral",
});
}
}
return {
referee: {
bonus: referee_bonus,
expires_at,
},
referrer: {
bonus: referrer_bonus,
expires_at,
},
};
}
async redeemReferralLink(customerId, referralCode) {
const [referral] = await this.listReferrals({
referral_code: referralCode,
});
if (!referral) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Referral not found");
}
const referrer = referral.customer_id;
if (referrer === customerId) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Referral cannot be redeemed by the referrer");
}
const [event] = await this.listEvents({
trigger_event: "referral_link_redeemed",
status: "active",
});
if (!event) {
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "referral_link_redeemed event not found");
}
const { referrer_bonus, referee_bonus } = event;
const res = await this.applyReferralBonus({
referee: customerId,
referrer: referrer,
referee_bonus: referee_bonus,
referrer_bonus: referrer_bonus,
referral_code: referralCode,
expires_at: event.expiry_days
? new Date(Date.now() + event.expiry_days * 24 * 60 * 60 * 1000)
: undefined,
});
return res;
}
}
LoyaltyModuleService.linkable = {
loyaltyWallet: models_1.WalletModel,
};
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "addPoints_", null);
__decorate([
(0, utils_1.InjectManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "addPoints", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "deductPoints_", null);
__decorate([
(0, utils_1.InjectManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "deductPoints", null);
__decorate([
__param(4, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Number, String, String, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "updateRemainingPointsInBucketsAndHistory", null);
__decorate([
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "calculateAndUpdateWalletPoints", null);
__decorate([
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "createOrRetrieveWallet", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "expireTransaction_", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "reverseDebitTransaction", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "reverseCreditTransaction", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "reverseTransaction", null);
__decorate([
(0, utils_1.InjectTransactionManager)(),
__param(1, (0, utils_1.MedusaContext)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Object]),
__metadata("design:returntype", Promise)
], LoyaltyModuleService.prototype, "reverseOrderTransactions", null);
exports.default = LoyaltyModuleService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2xveWFsdHkvc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7OztBQUNBLHFEQU1tQztBQUluQywrQ0FBK0Q7QUFDL0QscUNBUWtCO0FBb0JsQixNQUFNLG9CQUFxQixTQUFRLElBQUEscUJBQWEsRUFBQztJQUMvQyxLQUFLLEVBQUUsbUJBQVU7SUFDakIsTUFBTSxFQUFFLG9CQUFXO0lBQ25CLHFDQUFxQztJQUNyQyxrQkFBa0IsRUFBbEIsMkJBQWtCO0lBQ2xCLFFBQVEsRUFBRSxzQkFBYTtJQUN2QixpQkFBaUIsRUFBRSwrQkFBc0I7SUFDekMsa0JBQWtCLEVBQUUsZ0NBQXVCO0lBQzNDLE1BQU0sRUFBRSxvQkFBVztDQUNwQixDQUFDO0lBR0EsWUFBWSxTQUFTO1FBQ25CLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNqQixJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUM7UUFDaEMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsNkNBQTZDLENBQUMsQ0FBQztJQUNuRSxDQUFDO0lBT2UsQUFBTixLQUFLLENBQUMsVUFBVSxDQUN4QixFQUNFLFdBQVcsRUFDWCxNQUFNLEVBQ04sYUFBYSxHQUFHLFFBQVEsRUFDeEIsV0FBVyxFQUNYLFVBQVUsRUFDVixRQUFRLEVBQ1IsUUFBUSxFQUNSLE9BQU8sR0FnQlIsRUFDZ0IsYUFBc0M7UUFFdkQsSUFBSSxNQUFNLElBQUksQ0FBQztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBRTdCLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUNmLGtDQUFrQyxNQUFNLHdCQUF3QixXQUFXLEVBQUUsQ0FDOUUsQ0FBQztRQUVGLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUM5QyxXQUFXLEVBQ1gsYUFBYSxDQUNkLENB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