@lyxa.ai/marketing 1.0.68 → 1.0.69
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/dist/lib/index.js +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/modules/coupon/routers/coupon.router.d.ts +1194 -8087
- package/dist/lib/modules/coupon/routers/coupon.router.d.ts.map +1 -1
- package/dist/lib/modules/coupon/routers/coupon.router.js +9 -22
- package/dist/lib/modules/coupon/routers/coupon.router.js.map +1 -1
- package/dist/lib/modules/coupon/services/coupon.service.d.ts +12 -2
- package/dist/lib/modules/coupon/services/coupon.service.d.ts.map +1 -1
- package/dist/lib/modules/coupon/services/coupon.service.js +271 -90
- package/dist/lib/modules/coupon/services/coupon.service.js.map +1 -1
- package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -1
- package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js +1 -2
- package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js.map +1 -1
- package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
- package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js +82 -52
- package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js.map +1 -1
- package/dist/lib/modules/coupon/transformers/coupon.transformer.d.ts +4 -2
- package/dist/lib/modules/coupon/transformers/coupon.transformer.d.ts.map +1 -1
- package/dist/lib/modules/coupon/validations/all-coupons.validation.d.ts +42 -3693
- package/dist/lib/modules/coupon/validations/all-coupons.validation.d.ts.map +1 -1
- package/dist/lib/modules/coupon/validations/all-coupons.validation.js +1 -2
- package/dist/lib/modules/coupon/validations/all-coupons.validation.js.map +1 -1
- package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts +18 -8
- package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -1
- package/dist/lib/modules/coupon/validations/global-coupon.validation.d.ts +18 -0
- package/dist/lib/modules/coupon/validations/global-coupon.validation.d.ts.map +1 -1
- package/dist/lib/modules/coupon/validations/global-coupon.validation.js +2 -0
- package/dist/lib/modules/coupon/validations/global-coupon.validation.js.map +1 -1
- package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.d.ts +18 -18
- package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.js +2 -2
- package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.js.map +1 -1
- package/dist/lib/modules/deal-setting/routers/deal-setting.router.d.ts +5 -5
- package/dist/lib/modules/deal-setting/routers/deal-setting.router.d.ts.map +1 -1
- package/dist/lib/modules/featured-setting/routers/featured-setting.router.d.ts +5 -5
- package/dist/lib/modules/featured-setting/routers/featured-setting.router.d.ts.map +1 -1
- package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts +1 -0
- package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts.map +1 -1
- package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.js +5 -0
- package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.js.map +1 -1
- package/dist/lib/modules/marketing/routers/marketing.router.d.ts +5 -5
- package/dist/lib/modules/marketing/routers/marketing.router.d.ts.map +1 -1
- package/dist/lib/modules/marketing/services/marketing.service.d.ts.map +1 -1
- package/dist/lib/modules/marketing/services/marketing.service.js +37 -20
- package/dist/lib/modules/marketing/services/marketing.service.js.map +1 -1
- package/dist/lib/modules/marketing/validations/punch-marketing.validation.d.ts +4 -4
- package/dist/lib/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +6 -6
- package/dist/lib/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts.map +1 -1
- package/dist/lib/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
- package/dist/lib/modules/spend-to-get-history/spend-to-get-history.router.d.ts +5 -5
- package/dist/lib/modules/spend-to-get-history/spend-to-get-history.router.d.ts.map +1 -1
- package/dist/types/modules/coupon/routers/coupon.router.d.ts +1194 -8087
- package/dist/types/modules/coupon/routers/coupon.router.d.ts.map +1 -1
- package/dist/types/modules/coupon/services/coupon.service.d.ts +12 -2
- package/dist/types/modules/coupon/services/coupon.service.d.ts.map +1 -1
- package/dist/types/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -1
- package/dist/types/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
- package/dist/types/modules/coupon/transformers/coupon.transformer.d.ts +4 -2
- package/dist/types/modules/coupon/transformers/coupon.transformer.d.ts.map +1 -1
- package/dist/types/modules/coupon/validations/all-coupons.validation.d.ts +42 -3693
- package/dist/types/modules/coupon/validations/all-coupons.validation.d.ts.map +1 -1
- package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts +18 -8
- package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -1
- package/dist/types/modules/coupon/validations/global-coupon.validation.d.ts +18 -0
- package/dist/types/modules/coupon/validations/global-coupon.validation.d.ts.map +1 -1
- package/dist/types/modules/coupon/validations/individual-store-coupon.validation.d.ts +18 -18
- package/dist/types/modules/deal-setting/routers/deal-setting.router.d.ts +5 -5
- package/dist/types/modules/deal-setting/routers/deal-setting.router.d.ts.map +1 -1
- package/dist/types/modules/featured-setting/routers/featured-setting.router.d.ts +5 -5
- package/dist/types/modules/featured-setting/routers/featured-setting.router.d.ts.map +1 -1
- package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts +1 -0
- package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts.map +1 -1
- package/dist/types/modules/marketing/routers/marketing.router.d.ts +5 -5
- package/dist/types/modules/marketing/routers/marketing.router.d.ts.map +1 -1
- package/dist/types/modules/marketing/services/marketing.service.d.ts.map +1 -1
- package/dist/types/modules/marketing/validations/punch-marketing.validation.d.ts +4 -4
- package/dist/types/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +6 -6
- package/dist/types/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts.map +1 -1
- package/dist/types/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
- package/dist/types/modules/spend-to-get-history/spend-to-get-history.router.d.ts +5 -5
- package/dist/types/modules/spend-to-get-history/spend-to-get-history.router.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -78,6 +78,7 @@ let CouponService = class CouponService {
|
|
|
78
78
|
_coreUserModel = null;
|
|
79
79
|
couponHelper;
|
|
80
80
|
referralSettingsModel;
|
|
81
|
+
userCouponModel;
|
|
81
82
|
vendorModel;
|
|
82
83
|
serviceBookingModel;
|
|
83
84
|
repository;
|
|
@@ -92,6 +93,7 @@ let CouponService = class CouponService {
|
|
|
92
93
|
this.punchHistoryService = services_1.punchMarketingHistoryService;
|
|
93
94
|
this.couponHelper = helper_service_1.couponHelperService;
|
|
94
95
|
this.referralSettingsModel = models_1.ReferralSettingModel;
|
|
96
|
+
this.userCouponModel = models_1.UserCouponModel;
|
|
95
97
|
this.vendorModel = models_1.VendorModel;
|
|
96
98
|
this.serviceBookingModel = models_1.ServiceBookingModel;
|
|
97
99
|
this.repository = new CouponRepository_1.CouponRepository();
|
|
@@ -527,22 +529,22 @@ let CouponService = class CouponService {
|
|
|
527
529
|
}
|
|
528
530
|
async update(id, data) {
|
|
529
531
|
let coupon;
|
|
530
|
-
const [oldCoupon, checkCouponCode,
|
|
532
|
+
const [oldCoupon, checkCouponCode, userCouponsWithOldCoupon] = await Promise.all([
|
|
531
533
|
this.baseCouponModel.findById(id),
|
|
532
534
|
this.model.findOne({
|
|
533
535
|
code: data.code,
|
|
534
536
|
_id: { $ne: id },
|
|
535
537
|
updatedVersion: { $exists: false },
|
|
536
538
|
}),
|
|
537
|
-
this.
|
|
538
|
-
.find({
|
|
539
|
-
coupons: new typegoose_1.mongoose.Types.ObjectId(id),
|
|
540
|
-
})
|
|
541
|
-
.select(['_id', 'coupons']),
|
|
539
|
+
this.userCouponModel.find({ coupon: new typegoose_1.mongoose.Types.ObjectId(id) }).select(['_id', 'user']),
|
|
542
540
|
]);
|
|
541
|
+
const oldCouponUsers = userCouponsWithOldCoupon?.map(u => u.user);
|
|
543
542
|
if (!oldCoupon) {
|
|
544
543
|
(0, error_common_1.notFoundError)('Coupon not found');
|
|
545
544
|
}
|
|
545
|
+
if (data.couponType && oldCoupon.couponType !== data.couponType) {
|
|
546
|
+
(0, error_common_1.badRequestError)('Cannot update coupon type. Please delete and create');
|
|
547
|
+
}
|
|
546
548
|
const running = this.isCouponRunning(oldCoupon);
|
|
547
549
|
if (running && data && 'duration' in data && data.duration) {
|
|
548
550
|
data.duration.start = oldCoupon.duration.start;
|
|
@@ -565,8 +567,21 @@ let CouponService = class CouponService {
|
|
|
565
567
|
coupon = await _1.globalCouponService.create(data);
|
|
566
568
|
if (coupon) {
|
|
567
569
|
const globalCoupon = coupon;
|
|
570
|
+
const typedOldCoupon = oldCoupon;
|
|
568
571
|
if (!globalCoupon.forNewUserOnly) {
|
|
569
|
-
await this.exchangeCouponForMultipleUser({ _id: { $in:
|
|
572
|
+
await this.exchangeCouponForMultipleUser({ _id: { $in: oldCouponUsers } }, oldCoupon._id, coupon._id);
|
|
573
|
+
}
|
|
574
|
+
else if (typedOldCoupon.forNewUserOnly &&
|
|
575
|
+
!typedOldCoupon.couponDurationInDays &&
|
|
576
|
+
globalCoupon.isCouponDurationInDaysEnabled &&
|
|
577
|
+
globalCoupon.couponDurationInDays) {
|
|
578
|
+
await this.userCouponModel.updateMany({ coupon: oldCoupon._id }, {
|
|
579
|
+
$set: {
|
|
580
|
+
coupon: globalCoupon._id,
|
|
581
|
+
startDate: new Date(),
|
|
582
|
+
endDate: (0, dayjs_1.dayjs)().add(globalCoupon.couponDurationInDays, 'day').endOf('day').toDate(),
|
|
583
|
+
},
|
|
584
|
+
});
|
|
570
585
|
}
|
|
571
586
|
}
|
|
572
587
|
break;
|
|
@@ -576,7 +591,7 @@ let CouponService = class CouponService {
|
|
|
576
591
|
if (coupon) {
|
|
577
592
|
const shopsCategoriesCoupon = coupon;
|
|
578
593
|
if (!shopsCategoriesCoupon.forNewUserOnly) {
|
|
579
|
-
await this.exchangeCouponForMultipleUser({ _id: { $in:
|
|
594
|
+
await this.exchangeCouponForMultipleUser({ _id: { $in: oldCouponUsers } }, oldCoupon._id, shopsCategoriesCoupon._id);
|
|
580
595
|
}
|
|
581
596
|
}
|
|
582
597
|
break;
|
|
@@ -586,7 +601,7 @@ let CouponService = class CouponService {
|
|
|
586
601
|
if (coupon) {
|
|
587
602
|
const individualStoreCoupon = coupon;
|
|
588
603
|
if (!individualStoreCoupon.forNewUserOnly) {
|
|
589
|
-
await this.exchangeCouponForMultipleUser({ _id: { $in:
|
|
604
|
+
await this.exchangeCouponForMultipleUser({ _id: { $in: oldCouponUsers } }, oldCoupon._id, individualStoreCoupon._id);
|
|
590
605
|
}
|
|
591
606
|
}
|
|
592
607
|
break;
|
|
@@ -595,7 +610,7 @@ let CouponService = class CouponService {
|
|
|
595
610
|
coupon = await _1.individualUserCouponService.create(data);
|
|
596
611
|
if (coupon) {
|
|
597
612
|
const individualUserCoupon = coupon;
|
|
598
|
-
await this.removeCouponFromMultipleUser({ _id: { $in:
|
|
613
|
+
await this.removeCouponFromMultipleUser({ _id: { $in: oldCouponUsers } }, oldCoupon._id);
|
|
599
614
|
await this.assignCouponToMultipleUser({ _id: { $in: individualUserCoupon.users } }, individualUserCoupon._id);
|
|
600
615
|
}
|
|
601
616
|
break;
|
|
@@ -605,7 +620,7 @@ let CouponService = class CouponService {
|
|
|
605
620
|
if (coupon) {
|
|
606
621
|
const customCoupon = coupon;
|
|
607
622
|
if (!customCoupon.forNewUserOnly) {
|
|
608
|
-
await this.exchangeCouponForMultipleUser({ _id: { $in:
|
|
623
|
+
await this.exchangeCouponForMultipleUser({ _id: { $in: oldCouponUsers } }, oldCoupon._id, coupon._id);
|
|
609
624
|
}
|
|
610
625
|
}
|
|
611
626
|
break;
|
|
@@ -722,16 +737,21 @@ let CouponService = class CouponService {
|
|
|
722
737
|
const user = await this.userModel.findById(data.user);
|
|
723
738
|
if (!user)
|
|
724
739
|
(0, error_common_1.notFoundError)('User not found');
|
|
740
|
+
const userCoupons = await this.userCouponModel.find({ user: user._id, isExpired: false });
|
|
741
|
+
const userCouponIds = userCoupons.map(userCoupon => userCoupon.coupon);
|
|
725
742
|
let coupon = await this.model
|
|
726
743
|
.findOne({
|
|
727
744
|
code: data.coupon,
|
|
728
|
-
_id: { $in:
|
|
745
|
+
_id: { $in: userCouponIds },
|
|
729
746
|
status: { $in: [enum_1.Status.ACTIVE, null] },
|
|
730
747
|
})
|
|
731
748
|
.populate([
|
|
732
749
|
{ path: 'shops', select: '_id name' },
|
|
733
750
|
{ path: 'vendors', select: '_id name' },
|
|
751
|
+
{ path: 'shop', select: '_id name' },
|
|
752
|
+
{ path: 'vendor', select: '_id name' },
|
|
734
753
|
]);
|
|
754
|
+
let matchedUserCoupon = userCoupons.find(userCoupon => userCoupon.coupon?.toString?.() == coupon?._id?.toString?.()) ?? null;
|
|
735
755
|
if (!coupon) {
|
|
736
756
|
coupon = await this.model
|
|
737
757
|
.findOne({
|
|
@@ -743,19 +763,22 @@ let CouponService = class CouponService {
|
|
|
743
763
|
.populate([
|
|
744
764
|
{ path: 'shops', select: '_id name' },
|
|
745
765
|
{ path: 'vendors', select: '_id name' },
|
|
766
|
+
{ path: 'shop', select: '_id name' },
|
|
767
|
+
{ path: 'vendor', select: '_id name' },
|
|
746
768
|
]);
|
|
747
769
|
if (!coupon) {
|
|
748
770
|
(0, error_common_1.notFoundError)('Coupon not found');
|
|
749
771
|
}
|
|
750
|
-
this.userModel.findByIdAndUpdate(user._id, {
|
|
751
|
-
$addToSet: { coupons: coupon._id },
|
|
752
|
-
});
|
|
753
772
|
}
|
|
754
773
|
if (coupon.couponType == enum_1.CouponType.REFERRAL_CODE) {
|
|
755
|
-
|
|
774
|
+
const validation = await _1.referralCodeCouponService.validateCoupon(data, coupon);
|
|
775
|
+
if (validation.isValid && !matchedUserCoupon) {
|
|
776
|
+
await this.assignCouponToUser(user._id, coupon._id);
|
|
777
|
+
}
|
|
778
|
+
return validation;
|
|
756
779
|
}
|
|
757
780
|
const baseCoupon = coupon;
|
|
758
|
-
const { isValid, invalidMessage } = await this.validateBaseCoupon(baseCoupon, user._id, data.amountBeforeCoupon, Boolean(data.scheduledAt) ? (0, dayjs_1.dayjs)(data.scheduledAt) : undefined);
|
|
781
|
+
const { isValid, invalidMessage } = await this.validateBaseCoupon(baseCoupon, user._id, data.amountBeforeCoupon, Boolean(data.scheduledAt) ? (0, dayjs_1.dayjs)(data.scheduledAt) : undefined, matchedUserCoupon ? matchedUserCoupon : undefined);
|
|
759
782
|
if (!isValid) {
|
|
760
783
|
return {
|
|
761
784
|
isValid,
|
|
@@ -773,8 +796,13 @@ let CouponService = class CouponService {
|
|
|
773
796
|
return await _1.individualStoreCouponService.validateCoupon(data, coupon);
|
|
774
797
|
case enum_1.CouponType.INDIVIDUAL_USER:
|
|
775
798
|
return await _1.individualUserCouponService.validateCoupon(data, coupon);
|
|
776
|
-
case enum_1.CouponType.CUSTOM_COUPON:
|
|
777
|
-
|
|
799
|
+
case enum_1.CouponType.CUSTOM_COUPON: {
|
|
800
|
+
const validation = await _1.customCouponService.validateCoupon(data, coupon);
|
|
801
|
+
if (validation.isValid && !matchedUserCoupon) {
|
|
802
|
+
await this.assignCouponToUser(user._id, coupon._id);
|
|
803
|
+
}
|
|
804
|
+
return validation;
|
|
805
|
+
}
|
|
778
806
|
default:
|
|
779
807
|
(0, error_common_1.notImplementedError)(`Validation not implemented for ${coupon.couponType} coupons`);
|
|
780
808
|
}
|
|
@@ -788,16 +816,27 @@ let CouponService = class CouponService {
|
|
|
788
816
|
}
|
|
789
817
|
return false;
|
|
790
818
|
}
|
|
791
|
-
async validateBaseCoupon(coupon, user, orderAmount, scheduledAt = undefined) {
|
|
819
|
+
async validateBaseCoupon(coupon, user, orderAmount, scheduledAt = undefined, userCoupon) {
|
|
820
|
+
const now = (0, dayjs_1.dayjs)();
|
|
821
|
+
const durationStart = coupon.forNewUserOnly
|
|
822
|
+
? (0, dayjs_1.dayjs)(userCoupon?.startDate ?? coupon.duration.start)
|
|
823
|
+
: (0, dayjs_1.dayjs)(coupon.duration.start);
|
|
824
|
+
const durationEnd = coupon.forNewUserOnly
|
|
825
|
+
? (0, dayjs_1.dayjs)(userCoupon?.endDate ?? coupon.duration.end)
|
|
826
|
+
: (0, dayjs_1.dayjs)(coupon.duration.end);
|
|
792
827
|
const invalidConditions = [
|
|
793
|
-
|
|
794
|
-
|
|
828
|
+
coupon.forNewUserOnly
|
|
829
|
+
? Boolean(userCoupon?.isExpired) || (durationEnd?.isValid?.() ? durationEnd.isBefore(now) : false)
|
|
830
|
+
: this.isCouponExpired(coupon),
|
|
831
|
+
durationStart?.isValid?.() ? durationStart.isAfter(now) : false,
|
|
795
832
|
coupon.isMinOrderValueEnabled && coupon.minOrderValue && orderAmount < coupon.minOrderValue,
|
|
796
833
|
coupon.forNewUserOnly && !(await this.verifyNewUserConditions(coupon, user)),
|
|
797
834
|
coupon.isOrderLimitPerUserEnabled &&
|
|
798
835
|
coupon.orderLimitPerUser &&
|
|
799
836
|
(await this.getCouponOrdersForUser(user, coupon.code)) >= coupon.orderLimitPerUser,
|
|
800
|
-
scheduledAt &&
|
|
837
|
+
scheduledAt &&
|
|
838
|
+
Boolean(coupon.forNewUserOnly ? (userCoupon?.endDate ?? coupon.duration.end) : coupon.duration.end) &&
|
|
839
|
+
(0, dayjs_1.dayjs)(coupon.forNewUserOnly ? (userCoupon?.endDate ?? coupon.duration.end) : coupon.duration.end).isBefore(scheduledAt),
|
|
801
840
|
];
|
|
802
841
|
const invalidMessages = [
|
|
803
842
|
'This coupon is expired',
|
|
@@ -975,49 +1014,81 @@ let CouponService = class CouponService {
|
|
|
975
1014
|
totalSecondaryCouponDiscount: result[0]?.totalSecondaryCouponDiscount ?? 0,
|
|
976
1015
|
};
|
|
977
1016
|
}
|
|
978
|
-
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
1017
|
+
getNewUserCouponDates(coupon, startDate = new Date()) {
|
|
1018
|
+
const typedCoupon = coupon;
|
|
1019
|
+
const baseStartDate = typedCoupon.forNewUserOnly ? startDate : (typedCoupon.duration?.start ?? startDate);
|
|
1020
|
+
if (typedCoupon.forNewUserOnly &&
|
|
1021
|
+
typedCoupon.isCouponDurationInDaysEnabled &&
|
|
1022
|
+
typedCoupon.couponDurationInDays) {
|
|
1023
|
+
return {
|
|
1024
|
+
startDate: baseStartDate,
|
|
1025
|
+
endDate: (0, dayjs_1.dayjs)(baseStartDate).add(typedCoupon.couponDurationInDays, 'day').endOf('day').toDate(),
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
startDate: baseStartDate,
|
|
1030
|
+
endDate: typedCoupon.forNewUserOnly ? (typedCoupon.duration?.end ?? null) : null,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
async assignCouponToUser(user, coupon, options = {}) {
|
|
1034
|
+
await this.userCouponModel.updateOne({ user, coupon }, {
|
|
1035
|
+
$setOnInsert: {
|
|
1036
|
+
user,
|
|
1037
|
+
coupon,
|
|
982
1038
|
},
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1039
|
+
$set: {
|
|
1040
|
+
startDate: options.startDate ?? new Date(),
|
|
1041
|
+
...(options.endDate !== undefined ? { endDate: options.endDate } : {}),
|
|
1042
|
+
isExpired: false,
|
|
1043
|
+
},
|
|
1044
|
+
}, { upsert: true });
|
|
987
1045
|
}
|
|
988
|
-
async
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1046
|
+
async assignCouponToUsersBulk(userIds, coupon, options = {}) {
|
|
1047
|
+
const uniqueUserIds = [...new Map(userIds.map(userId => [userId.toString(), userId])).values()];
|
|
1048
|
+
if (!uniqueUserIds.length) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const update = {
|
|
1052
|
+
startDate: options.startDate ?? new Date(),
|
|
1053
|
+
isExpired: false,
|
|
1054
|
+
};
|
|
1055
|
+
if (options.endDate !== undefined) {
|
|
1056
|
+
update.endDate = options.endDate;
|
|
1057
|
+
}
|
|
1058
|
+
await this.userCouponModel.bulkWrite(uniqueUserIds.map(userId => ({
|
|
1059
|
+
updateOne: {
|
|
1060
|
+
filter: { user: userId, coupon },
|
|
1061
|
+
update: {
|
|
1062
|
+
$setOnInsert: {
|
|
1063
|
+
user: userId,
|
|
1064
|
+
coupon,
|
|
1065
|
+
},
|
|
1066
|
+
$set: update,
|
|
1067
|
+
},
|
|
1068
|
+
upsert: true,
|
|
993
1069
|
},
|
|
994
|
-
});
|
|
1070
|
+
})), { ordered: false });
|
|
1071
|
+
}
|
|
1072
|
+
async assignCouponToMultipleUser(query, coupon, options = {}) {
|
|
1073
|
+
console.log('Assigning coupon to multiple users with coupon:', coupon);
|
|
1074
|
+
const users = await this.userModel.find(query).select('_id');
|
|
1075
|
+
await this.assignCouponToUsersBulk(users.map(user => user._id), coupon, options);
|
|
995
1076
|
}
|
|
996
1077
|
async assignCouponToNewUsers(coupon) {
|
|
997
1078
|
console.log('Assigning coupon to new users', coupon);
|
|
998
1079
|
const newUsers = await helper_service_1.couponHelperService.getNewUsers();
|
|
999
|
-
const
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
});
|
|
1080
|
+
const couponDocument = await this.model.findById(coupon);
|
|
1081
|
+
if (!couponDocument)
|
|
1082
|
+
return;
|
|
1083
|
+
const { startDate, endDate } = this.getNewUserCouponDates(couponDocument);
|
|
1084
|
+
await this.assignCouponToUsersBulk(newUsers.map(user => user._id), coupon, { startDate, endDate });
|
|
1004
1085
|
}
|
|
1005
1086
|
async removeCouponFromUser(user, coupon) {
|
|
1006
|
-
|
|
1007
|
-
$pull: {
|
|
1008
|
-
coupons: coupon,
|
|
1009
|
-
},
|
|
1010
|
-
}, {
|
|
1011
|
-
new: true,
|
|
1012
|
-
runValidators: true,
|
|
1013
|
-
});
|
|
1087
|
+
await this.userCouponModel.deleteOne({ user, coupon });
|
|
1014
1088
|
}
|
|
1015
1089
|
async removeCouponFromMultipleUser(query, coupon) {
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
coupons: coupon,
|
|
1019
|
-
},
|
|
1020
|
-
});
|
|
1090
|
+
const users = await this.userModel.find(query).select('_id');
|
|
1091
|
+
await this.userCouponModel.deleteMany({ user: { $in: users.map(user => user._id) }, coupon });
|
|
1021
1092
|
}
|
|
1022
1093
|
throwBadRequest(message) {
|
|
1023
1094
|
throw new server_1.TRPCError({
|
|
@@ -1046,7 +1117,7 @@ let CouponService = class CouponService {
|
|
|
1046
1117
|
if (typedCoupon.forNewUserOnly && !isNewUser) {
|
|
1047
1118
|
this.throwBadRequest('This coupon is valid for new users only');
|
|
1048
1119
|
}
|
|
1049
|
-
const alreadyApplied = await this.
|
|
1120
|
+
const alreadyApplied = await this.hasUserAlreadyAddedCoupon(user, typedCoupon);
|
|
1050
1121
|
if (alreadyApplied) {
|
|
1051
1122
|
this.throwBadRequest('Coupon already applied to user');
|
|
1052
1123
|
}
|
|
@@ -1055,20 +1126,23 @@ let CouponService = class CouponService {
|
|
|
1055
1126
|
this.throwBadRequest('This coupon is not assigned to this user');
|
|
1056
1127
|
}
|
|
1057
1128
|
}
|
|
1058
|
-
|
|
1129
|
+
const dates = this.getNewUserCouponDates(typedCoupon);
|
|
1130
|
+
await this.assignCouponToUser(user, typedCoupon._id, dates);
|
|
1059
1131
|
return coupon;
|
|
1060
1132
|
}
|
|
1061
|
-
async
|
|
1133
|
+
async hasUserAlreadyAddedCoupon(userId, coupon) {
|
|
1062
1134
|
const couponIds = [coupon._id, ...(coupon.previousVersions ?? []).map(v => v._id)];
|
|
1063
|
-
const user = await this.
|
|
1064
|
-
|
|
1065
|
-
|
|
1135
|
+
const user = await this.userCouponModel.exists({
|
|
1136
|
+
user: userId,
|
|
1137
|
+
coupon: { $in: couponIds },
|
|
1066
1138
|
});
|
|
1067
1139
|
return !!user;
|
|
1068
1140
|
}
|
|
1069
|
-
async exchangeCouponForMultipleUser(query, oldCoupon, newCoupon) {
|
|
1070
|
-
await this.
|
|
1071
|
-
|
|
1141
|
+
async exchangeCouponForMultipleUser(query, oldCoupon, newCoupon, options = {}) {
|
|
1142
|
+
const users = await this.userModel.find(query).select('_id');
|
|
1143
|
+
const userIds = users.map(user => user._id);
|
|
1144
|
+
await this.userCouponModel.deleteMany({ user: { $in: userIds }, coupon: oldCoupon });
|
|
1145
|
+
await this.assignCouponToUsersBulk(userIds, newCoupon, options);
|
|
1072
1146
|
}
|
|
1073
1147
|
getShareableLink = async (code) => {
|
|
1074
1148
|
const secretsService = (0, core_1.getLibraries)().getSecretsService();
|
|
@@ -1115,15 +1189,24 @@ let CouponService = class CouponService {
|
|
|
1115
1189
|
query.createdAt.$lt = new Date(query.createdAt.$lt);
|
|
1116
1190
|
}
|
|
1117
1191
|
}
|
|
1118
|
-
const
|
|
1119
|
-
query._id = { $in: user?.coupons ?? [] };
|
|
1192
|
+
const userId = new typegoose_1.mongoose.Types.ObjectId(query.user);
|
|
1120
1193
|
delete query.user;
|
|
1121
1194
|
const pipeline = [
|
|
1122
|
-
{ $match: {
|
|
1195
|
+
{ $match: { user: userId } },
|
|
1196
|
+
{
|
|
1197
|
+
$lookup: {
|
|
1198
|
+
from: 'coupons',
|
|
1199
|
+
localField: 'coupon',
|
|
1200
|
+
foreignField: '_id',
|
|
1201
|
+
as: 'coupon',
|
|
1202
|
+
pipeline: [{ $match: { deletedAt: null } }],
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
{ $unwind: '$coupon' },
|
|
1123
1206
|
{
|
|
1124
1207
|
$lookup: {
|
|
1125
1208
|
from: 'admins',
|
|
1126
|
-
localField: 'createdBy',
|
|
1209
|
+
localField: 'coupon.createdBy',
|
|
1127
1210
|
foreignField: '_id',
|
|
1128
1211
|
as: 'createdBy',
|
|
1129
1212
|
},
|
|
@@ -1135,13 +1218,21 @@ let CouponService = class CouponService {
|
|
|
1135
1218
|
$cond: {
|
|
1136
1219
|
if: {
|
|
1137
1220
|
$or: [
|
|
1221
|
+
{ $ifNull: ['$isExpired', false] },
|
|
1138
1222
|
{
|
|
1139
1223
|
$and: [
|
|
1140
|
-
{ $ifNull: ['$duration.end', false] },
|
|
1141
|
-
{ $lt: ['$duration.end', (0, dayjs_1.dayjs)().toDate()] },
|
|
1224
|
+
{ $ifNull: ['$coupon.duration.end', false] },
|
|
1225
|
+
{ $lt: ['$coupon.duration.end', (0, dayjs_1.dayjs)().toDate()] },
|
|
1226
|
+
],
|
|
1227
|
+
},
|
|
1228
|
+
{ $ifNull: ['$coupon.expirationReason', false] },
|
|
1229
|
+
{
|
|
1230
|
+
$and: [
|
|
1231
|
+
{ $eq: ['$coupon.forNewUserOnly', true] },
|
|
1232
|
+
{ $ifNull: ['$endDate', false] },
|
|
1233
|
+
{ $lt: ['$endDate', (0, dayjs_1.dayjs)().toDate()] },
|
|
1142
1234
|
],
|
|
1143
1235
|
},
|
|
1144
|
-
{ $ifNull: ['$expirationReason', false] },
|
|
1145
1236
|
],
|
|
1146
1237
|
},
|
|
1147
1238
|
then: true,
|
|
@@ -1150,6 +1241,17 @@ let CouponService = class CouponService {
|
|
|
1150
1241
|
},
|
|
1151
1242
|
},
|
|
1152
1243
|
},
|
|
1244
|
+
{
|
|
1245
|
+
$addFields: {
|
|
1246
|
+
'coupon.isExpired': '$isExpired',
|
|
1247
|
+
'coupon.duration.start': {
|
|
1248
|
+
$cond: [{ $eq: ['$coupon.forNewUserOnly', true] }, '$startDate', '$coupon.duration.start'],
|
|
1249
|
+
},
|
|
1250
|
+
'coupon.duration.end': {
|
|
1251
|
+
$cond: [{ $eq: ['$coupon.forNewUserOnly', true] }, '$endDate', '$coupon.duration.end'],
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1254
|
+
},
|
|
1153
1255
|
{
|
|
1154
1256
|
$match: isExpiredField
|
|
1155
1257
|
? { isExpired: true }
|
|
@@ -1158,15 +1260,10 @@ let CouponService = class CouponService {
|
|
|
1158
1260
|
: {},
|
|
1159
1261
|
},
|
|
1160
1262
|
{
|
|
1161
|
-
$
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
then: 'expired',
|
|
1166
|
-
else: '$status',
|
|
1167
|
-
},
|
|
1168
|
-
},
|
|
1169
|
-
},
|
|
1263
|
+
$replaceRoot: { newRoot: '$coupon' },
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
$match: { ...query },
|
|
1170
1267
|
},
|
|
1171
1268
|
{
|
|
1172
1269
|
$project: select,
|
|
@@ -1182,7 +1279,7 @@ let CouponService = class CouponService {
|
|
|
1182
1279
|
},
|
|
1183
1280
|
});
|
|
1184
1281
|
}
|
|
1185
|
-
const results = await (0, pagination_1.paginateAggregation)(this.
|
|
1282
|
+
const results = await (0, pagination_1.paginateAggregation)(this.userCouponModel, pipeline, data?.page, data?.size, data?.sort);
|
|
1186
1283
|
return results;
|
|
1187
1284
|
}
|
|
1188
1285
|
catch (err) {
|
|
@@ -1209,15 +1306,27 @@ let CouponService = class CouponService {
|
|
|
1209
1306
|
query.forNewUserOnly = { $ne: true };
|
|
1210
1307
|
}
|
|
1211
1308
|
const coupons = await this.baseCouponModel.find(query);
|
|
1212
|
-
const
|
|
1213
|
-
await this.
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1309
|
+
const now = new Date();
|
|
1310
|
+
await this.userCouponModel.bulkWrite(coupons.map(coupon => {
|
|
1311
|
+
const dates = this.getNewUserCouponDates(coupon, now);
|
|
1312
|
+
return {
|
|
1313
|
+
updateOne: {
|
|
1314
|
+
filter: { user: userId, coupon: coupon._id },
|
|
1315
|
+
update: {
|
|
1316
|
+
$setOnInsert: {
|
|
1317
|
+
user: userId,
|
|
1318
|
+
coupon: coupon._id,
|
|
1319
|
+
},
|
|
1320
|
+
$set: {
|
|
1321
|
+
startDate: dates.startDate,
|
|
1322
|
+
...(dates.endDate !== undefined ? { endDate: dates.endDate } : {}),
|
|
1323
|
+
isExpired: false,
|
|
1324
|
+
},
|
|
1325
|
+
},
|
|
1326
|
+
upsert: true,
|
|
1327
|
+
},
|
|
1328
|
+
};
|
|
1329
|
+
}), { ordered: false });
|
|
1221
1330
|
}
|
|
1222
1331
|
async getActiveNewUserCoupon() {
|
|
1223
1332
|
const currentDate = (0, dayjs_1.dayjs)().toDate();
|
|
@@ -1518,6 +1627,78 @@ let CouponService = class CouponService {
|
|
|
1518
1627
|
const result = await this.repository.getTotalProfitGraphData(input);
|
|
1519
1628
|
return result;
|
|
1520
1629
|
}
|
|
1630
|
+
async migrateOldUserCoupons() {
|
|
1631
|
+
const users = (await this.userModel
|
|
1632
|
+
.find({
|
|
1633
|
+
coupons: { $exists: true, $ne: [] },
|
|
1634
|
+
})
|
|
1635
|
+
.select('_id coupons')
|
|
1636
|
+
.lean());
|
|
1637
|
+
if (!users.length) {
|
|
1638
|
+
return {
|
|
1639
|
+
usersProcessed: 0,
|
|
1640
|
+
couponsMigrated: 0,
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
const couponIds = [
|
|
1644
|
+
...new Set(users.flatMap(user => (user.coupons ?? []).map((coupon) => coupon.toString()))),
|
|
1645
|
+
];
|
|
1646
|
+
const coupons = (await this.model
|
|
1647
|
+
.findWithDeleted({
|
|
1648
|
+
_id: { $in: couponIds },
|
|
1649
|
+
})
|
|
1650
|
+
.select('_id duration')
|
|
1651
|
+
.lean());
|
|
1652
|
+
const couponMap = new Map(coupons.map(coupon => [coupon._id.toString(), coupon]));
|
|
1653
|
+
const existingUserCoupons = (await this.userCouponModel
|
|
1654
|
+
.find({
|
|
1655
|
+
user: { $in: users.map(user => user._id) },
|
|
1656
|
+
})
|
|
1657
|
+
.select('user coupon')
|
|
1658
|
+
.lean());
|
|
1659
|
+
const existingUserCouponKeys = new Set(existingUserCoupons.map(userCoupon => `${userCoupon.user.toString()}:${userCoupon.coupon.toString()}`));
|
|
1660
|
+
const bulkOps = [];
|
|
1661
|
+
for (const user of users) {
|
|
1662
|
+
const uniqueCouponIds = [...new Set((user.coupons ?? []).map((coupon) => coupon.toString()))];
|
|
1663
|
+
for (const couponId of uniqueCouponIds) {
|
|
1664
|
+
const coupon = couponMap.get(couponId);
|
|
1665
|
+
if (!coupon)
|
|
1666
|
+
continue;
|
|
1667
|
+
if (existingUserCouponKeys.has(`${user._id.toString()}:${coupon._id.toString()}`))
|
|
1668
|
+
continue;
|
|
1669
|
+
const startDate = coupon.duration?.start;
|
|
1670
|
+
const endDate = coupon.duration?.end;
|
|
1671
|
+
bulkOps.push({
|
|
1672
|
+
updateOne: {
|
|
1673
|
+
filter: { user: user._id, coupon: coupon._id },
|
|
1674
|
+
update: {
|
|
1675
|
+
$setOnInsert: {
|
|
1676
|
+
user: user._id,
|
|
1677
|
+
coupon: coupon._id,
|
|
1678
|
+
},
|
|
1679
|
+
$set: {
|
|
1680
|
+
startDate,
|
|
1681
|
+
endDate,
|
|
1682
|
+
isExpired: false,
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1685
|
+
upsert: true,
|
|
1686
|
+
},
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (!bulkOps.length) {
|
|
1691
|
+
return {
|
|
1692
|
+
usersProcessed: users.length,
|
|
1693
|
+
couponsMigrated: 0,
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
this.userCouponModel.bulkWrite(bulkOps, { ordered: false });
|
|
1697
|
+
return {
|
|
1698
|
+
usersProcessed: users.length,
|
|
1699
|
+
couponsMigrated: bulkOps.length,
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1521
1702
|
};
|
|
1522
1703
|
exports.CouponService = CouponService;
|
|
1523
1704
|
exports.CouponService = CouponService = __decorate([
|