UNPKG

@devx-commerce/plugin-loyalty

Version:

A starter for Medusa plugins.

613 lines 51.1 kB
"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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