@timardex/cluemart-server-shared 1.0.284 → 1.0.287

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/index.d.mts CHANGED
@@ -754,6 +754,31 @@ declare const AffiliateModel: mongoose__default.Model<AffiliateDocument, {}, {},
754
754
  */
755
755
  declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
756
756
 
757
+ type AwardAffiliateVendorSubscriptionRewardsResult = {
758
+ awarded: number;
759
+ skipped: number;
760
+ };
761
+ /**
762
+ * Awards monthly vendor subscription rewards to affiliates with active referred vendors.
763
+ *
764
+ * Eligibility per affiliate resource:
765
+ * - Affiliate is not soft-deleted
766
+ * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted
767
+ * - Vendor is active, not deleted, and `promoCodes` includes the affiliate code
768
+ * - Vendor owner has a non-expired Standard / Pro / Pro+ vendor licence
769
+ *
770
+ * Reward type follows the owner's highest valid licence:
771
+ * - Pro / Pro+ → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)
772
+ * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)
773
+ *
774
+ * Idempotent per affiliate + vendor + reward type + calendar month.
775
+ * On successful award, notifies the affiliate owner (best-effort).
776
+ *
777
+ * @param now - Reference time for month bounds and licence expiry (defaults to now).
778
+ * @returns Counts of awarded and skipped candidates.
779
+ */
780
+ declare function awardAffiliateVendorSubscriptionRewards(now?: Date): Promise<AwardAffiliateVendorSubscriptionRewardsResult>;
781
+
757
782
  declare const AFFILIATE_REWARDS: {
758
783
  NEW_EVENT_REGISTRATION: {
759
784
  description: string;
@@ -820,6 +845,19 @@ declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mo
820
845
  */
821
846
  declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
822
847
 
848
+ /**
849
+ * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.
850
+ *
851
+ * Prefer Pro / Pro+ over Standard when both are valid.
852
+ * Returns `null` when no non-expired vendor licence applies.
853
+ */
854
+ declare function mapVendorLicenceToSubscriptionRewardType(licences: UserLicenceType[] | null | undefined, now?: Date): EnumAffiliateRewardType | null;
855
+ /** Inclusive start / exclusive end of the UTC calendar month containing `now`. */
856
+ declare function getSubscriptionRewardPeriodBounds(now?: Date): {
857
+ periodEnd: Date;
858
+ periodStart: Date;
859
+ };
860
+
823
861
  /**
824
862
  * Connect to MongoDB using Mongoose.
825
863
  * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
@@ -895,4 +933,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
895
933
  */
896
934
  declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
897
935
 
898
- export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, APP_SETTINGS_ID, AdModel, AffiliateModel, AppSettingModel, type AuthUser, CategorySchema, ChatModel, ChatReportModel, ContactDetailsSchema, EnumPubSubEvents, EventInfoModel, EventModel, GameModel, GoogleImportedMarketModel, type GraphQLContext, NotificationModel, type ObjectId, OwnerTypeSchema, PartnerModel, PostModel, PushTokenModel, RelationModel, RelationTypeSchema, ResourceActivityModel, ResourceImageTypeSchema, type SchemaAffiliateResourceType, type SchemaAffiliateReward, type SchemaChatMessageReactionType, type SchemaChatMessageReplyPreviewType, type SchemaChatMessageSeenType, type SchemaChatMessageType, type SchemaChatReportType, type SchemaChatType, type SchemaCreateBulkNotificationInput, type SchemaEventInfoType, type SchemaEventType, type SchemaGameType, type SchemaGoogleImportedMarket, type SchemaNotificationType, type SchemaOwnerType, type SchemaParticipantType, type SchemaPartnerType, type SchemaPostType, type SchemaPushTokenType, type SchemaRelatedPostType, type SchemaRelationType, type SchemaUnregisteredVendorInvitationType, type SchemaUnregisteredVendorType, type SchemaUserActivityEvent, type SchemaUserType, type SchemaVendorInfoType, type SchemaVendorType, SchoolModel, SocialMediaTypeSchema, StallTypeSchema, type SubscriptionPayload, UnregisteredVendorModel, UserModel, VendorInfoModel, VendorModel, VerificationTokenModel, type VerificationTokenType, activeAffiliateCodeExists, associatesSchema, baseResourceFields, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, dateTimeSchema, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, locationGeoSchema, locationsSchema, normalizeAffiliatePromoCodes, normalizePromoCode, refundPolicySchema, relatedPostSchema, relationDatesSchema, resourceRelationsSchema, saveNotificationsInDb, sendPushNotifications, socialShareResourceSchema, termsAgreementSchema, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense, userLicenseSchema };
936
+ export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, APP_SETTINGS_ID, AdModel, AffiliateModel, AppSettingModel, type AuthUser, type AwardAffiliateVendorSubscriptionRewardsResult, CategorySchema, ChatModel, ChatReportModel, ContactDetailsSchema, EnumPubSubEvents, EventInfoModel, EventModel, GameModel, GoogleImportedMarketModel, type GraphQLContext, NotificationModel, type ObjectId, OwnerTypeSchema, PartnerModel, PostModel, PushTokenModel, RelationModel, RelationTypeSchema, ResourceActivityModel, ResourceImageTypeSchema, type SchemaAffiliateResourceType, type SchemaAffiliateReward, type SchemaChatMessageReactionType, type SchemaChatMessageReplyPreviewType, type SchemaChatMessageSeenType, type SchemaChatMessageType, type SchemaChatReportType, type SchemaChatType, type SchemaCreateBulkNotificationInput, type SchemaEventInfoType, type SchemaEventType, type SchemaGameType, type SchemaGoogleImportedMarket, type SchemaNotificationType, type SchemaOwnerType, type SchemaParticipantType, type SchemaPartnerType, type SchemaPostType, type SchemaPushTokenType, type SchemaRelatedPostType, type SchemaRelationType, type SchemaUnregisteredVendorInvitationType, type SchemaUnregisteredVendorType, type SchemaUserActivityEvent, type SchemaUserType, type SchemaVendorInfoType, type SchemaVendorType, SchoolModel, SocialMediaTypeSchema, StallTypeSchema, type SubscriptionPayload, UnregisteredVendorModel, UserModel, VendorInfoModel, VendorModel, VerificationTokenModel, type VerificationTokenType, activeAffiliateCodeExists, associatesSchema, awardAffiliateVendorSubscriptionRewards, baseResourceFields, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, dateTimeSchema, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, getSubscriptionRewardPeriodBounds, locationGeoSchema, locationsSchema, mapVendorLicenceToSubscriptionRewardType, normalizeAffiliatePromoCodes, normalizePromoCode, refundPolicySchema, relatedPostSchema, relationDatesSchema, resourceRelationsSchema, saveNotificationsInDb, sendPushNotifications, socialShareResourceSchema, termsAgreementSchema, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense, userLicenseSchema };
package/dist/index.d.ts CHANGED
@@ -754,6 +754,31 @@ declare const AffiliateModel: mongoose__default.Model<AffiliateDocument, {}, {},
754
754
  */
755
755
  declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
756
756
 
757
+ type AwardAffiliateVendorSubscriptionRewardsResult = {
758
+ awarded: number;
759
+ skipped: number;
760
+ };
761
+ /**
762
+ * Awards monthly vendor subscription rewards to affiliates with active referred vendors.
763
+ *
764
+ * Eligibility per affiliate resource:
765
+ * - Affiliate is not soft-deleted
766
+ * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted
767
+ * - Vendor is active, not deleted, and `promoCodes` includes the affiliate code
768
+ * - Vendor owner has a non-expired Standard / Pro / Pro+ vendor licence
769
+ *
770
+ * Reward type follows the owner's highest valid licence:
771
+ * - Pro / Pro+ → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)
772
+ * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)
773
+ *
774
+ * Idempotent per affiliate + vendor + reward type + calendar month.
775
+ * On successful award, notifies the affiliate owner (best-effort).
776
+ *
777
+ * @param now - Reference time for month bounds and licence expiry (defaults to now).
778
+ * @returns Counts of awarded and skipped candidates.
779
+ */
780
+ declare function awardAffiliateVendorSubscriptionRewards(now?: Date): Promise<AwardAffiliateVendorSubscriptionRewardsResult>;
781
+
757
782
  declare const AFFILIATE_REWARDS: {
758
783
  NEW_EVENT_REGISTRATION: {
759
784
  description: string;
@@ -820,6 +845,19 @@ declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mo
820
845
  */
821
846
  declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
822
847
 
848
+ /**
849
+ * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.
850
+ *
851
+ * Prefer Pro / Pro+ over Standard when both are valid.
852
+ * Returns `null` when no non-expired vendor licence applies.
853
+ */
854
+ declare function mapVendorLicenceToSubscriptionRewardType(licences: UserLicenceType[] | null | undefined, now?: Date): EnumAffiliateRewardType | null;
855
+ /** Inclusive start / exclusive end of the UTC calendar month containing `now`. */
856
+ declare function getSubscriptionRewardPeriodBounds(now?: Date): {
857
+ periodEnd: Date;
858
+ periodStart: Date;
859
+ };
860
+
823
861
  /**
824
862
  * Connect to MongoDB using Mongoose.
825
863
  * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
@@ -895,4 +933,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
895
933
  */
896
934
  declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
897
935
 
898
- export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, APP_SETTINGS_ID, AdModel, AffiliateModel, AppSettingModel, type AuthUser, CategorySchema, ChatModel, ChatReportModel, ContactDetailsSchema, EnumPubSubEvents, EventInfoModel, EventModel, GameModel, GoogleImportedMarketModel, type GraphQLContext, NotificationModel, type ObjectId, OwnerTypeSchema, PartnerModel, PostModel, PushTokenModel, RelationModel, RelationTypeSchema, ResourceActivityModel, ResourceImageTypeSchema, type SchemaAffiliateResourceType, type SchemaAffiliateReward, type SchemaChatMessageReactionType, type SchemaChatMessageReplyPreviewType, type SchemaChatMessageSeenType, type SchemaChatMessageType, type SchemaChatReportType, type SchemaChatType, type SchemaCreateBulkNotificationInput, type SchemaEventInfoType, type SchemaEventType, type SchemaGameType, type SchemaGoogleImportedMarket, type SchemaNotificationType, type SchemaOwnerType, type SchemaParticipantType, type SchemaPartnerType, type SchemaPostType, type SchemaPushTokenType, type SchemaRelatedPostType, type SchemaRelationType, type SchemaUnregisteredVendorInvitationType, type SchemaUnregisteredVendorType, type SchemaUserActivityEvent, type SchemaUserType, type SchemaVendorInfoType, type SchemaVendorType, SchoolModel, SocialMediaTypeSchema, StallTypeSchema, type SubscriptionPayload, UnregisteredVendorModel, UserModel, VendorInfoModel, VendorModel, VerificationTokenModel, type VerificationTokenType, activeAffiliateCodeExists, associatesSchema, baseResourceFields, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, dateTimeSchema, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, locationGeoSchema, locationsSchema, normalizeAffiliatePromoCodes, normalizePromoCode, refundPolicySchema, relatedPostSchema, relationDatesSchema, resourceRelationsSchema, saveNotificationsInDb, sendPushNotifications, socialShareResourceSchema, termsAgreementSchema, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense, userLicenseSchema };
936
+ export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, APP_SETTINGS_ID, AdModel, AffiliateModel, AppSettingModel, type AuthUser, type AwardAffiliateVendorSubscriptionRewardsResult, CategorySchema, ChatModel, ChatReportModel, ContactDetailsSchema, EnumPubSubEvents, EventInfoModel, EventModel, GameModel, GoogleImportedMarketModel, type GraphQLContext, NotificationModel, type ObjectId, OwnerTypeSchema, PartnerModel, PostModel, PushTokenModel, RelationModel, RelationTypeSchema, ResourceActivityModel, ResourceImageTypeSchema, type SchemaAffiliateResourceType, type SchemaAffiliateReward, type SchemaChatMessageReactionType, type SchemaChatMessageReplyPreviewType, type SchemaChatMessageSeenType, type SchemaChatMessageType, type SchemaChatReportType, type SchemaChatType, type SchemaCreateBulkNotificationInput, type SchemaEventInfoType, type SchemaEventType, type SchemaGameType, type SchemaGoogleImportedMarket, type SchemaNotificationType, type SchemaOwnerType, type SchemaParticipantType, type SchemaPartnerType, type SchemaPostType, type SchemaPushTokenType, type SchemaRelatedPostType, type SchemaRelationType, type SchemaUnregisteredVendorInvitationType, type SchemaUnregisteredVendorType, type SchemaUserActivityEvent, type SchemaUserType, type SchemaVendorInfoType, type SchemaVendorType, SchoolModel, SocialMediaTypeSchema, StallTypeSchema, type SubscriptionPayload, UnregisteredVendorModel, UserModel, VendorInfoModel, VendorModel, VerificationTokenModel, type VerificationTokenType, activeAffiliateCodeExists, associatesSchema, awardAffiliateVendorSubscriptionRewards, baseResourceFields, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, dateTimeSchema, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, getSubscriptionRewardPeriodBounds, locationGeoSchema, locationsSchema, mapVendorLicenceToSubscriptionRewardType, normalizeAffiliatePromoCodes, normalizePromoCode, refundPolicySchema, relatedPostSchema, relationDatesSchema, resourceRelationsSchema, saveNotificationsInDb, sendPushNotifications, socialShareResourceSchema, termsAgreementSchema, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense, userLicenseSchema };
package/dist/index.mjs CHANGED
@@ -3608,14 +3608,14 @@ var EnumGameType = /* @__PURE__ */ ((EnumGameType2) => {
3608
3608
  EnumGameType2["ODD_ONE_OUT"] = "oddOneOut";
3609
3609
  return EnumGameType2;
3610
3610
  })(EnumGameType || {});
3611
- var EnumAffiliateRewardType = /* @__PURE__ */ ((EnumAffiliateRewardType2) => {
3612
- EnumAffiliateRewardType2["ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS"] = "ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS";
3613
- EnumAffiliateRewardType2["ACTIVE_VENDOR_BONUS_REWARD"] = "ACTIVE_VENDOR_BONUS_REWARD";
3614
- EnumAffiliateRewardType2["ACTIVE_VENDOR_PRO_SUBSCRIPTION"] = "ACTIVE_VENDOR_PRO_SUBSCRIPTION";
3615
- EnumAffiliateRewardType2["ACTIVE_VENDOR_STANDARD_SUBSCRIPTION"] = "ACTIVE_VENDOR_STANDARD_SUBSCRIPTION";
3616
- EnumAffiliateRewardType2["NEW_EVENT_REGISTRATION"] = "NEW_EVENT_REGISTRATION";
3617
- EnumAffiliateRewardType2["NEW_VENDOR_REGISTRATION"] = "NEW_VENDOR_REGISTRATION";
3618
- return EnumAffiliateRewardType2;
3611
+ var EnumAffiliateRewardType = /* @__PURE__ */ ((EnumAffiliateRewardType22) => {
3612
+ EnumAffiliateRewardType22["ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS"] = "ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS";
3613
+ EnumAffiliateRewardType22["ACTIVE_VENDOR_BONUS_REWARD"] = "ACTIVE_VENDOR_BONUS_REWARD";
3614
+ EnumAffiliateRewardType22["ACTIVE_VENDOR_PRO_SUBSCRIPTION"] = "ACTIVE_VENDOR_PRO_SUBSCRIPTION";
3615
+ EnumAffiliateRewardType22["ACTIVE_VENDOR_STANDARD_SUBSCRIPTION"] = "ACTIVE_VENDOR_STANDARD_SUBSCRIPTION";
3616
+ EnumAffiliateRewardType22["NEW_EVENT_REGISTRATION"] = "NEW_EVENT_REGISTRATION";
3617
+ EnumAffiliateRewardType22["NEW_VENDOR_REGISTRATION"] = "NEW_VENDOR_REGISTRATION";
3618
+ return EnumAffiliateRewardType22;
3619
3619
  })(EnumAffiliateRewardType || {});
3620
3620
  var statusOptions = [
3621
3621
  ...Object.values(EnumInviteStatus).map((status) => ({
@@ -9313,89 +9313,12 @@ async function activeAffiliateCodeExists(affiliateCode) {
9313
9313
  return existing !== null;
9314
9314
  }
9315
9315
 
9316
- // src/service/affiliate/createAffiliateReward.ts
9317
- var AFFILIATE_REWARDS = {
9318
- [EnumAffiliateRewardType.NEW_EVENT_REGISTRATION]: {
9319
- description: "10 points for new event registration (one-time reward)",
9320
- value: 10
9321
- },
9322
- [EnumAffiliateRewardType.NEW_VENDOR_REGISTRATION]: {
9323
- description: "5 points for new vendor registration (one-time reward)",
9324
- value: 5
9325
- },
9326
- [EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION]: {
9327
- description: "3 points for active vendor pro subscription (monthly reward)",
9328
- value: 3
9329
- },
9330
- [EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION]: {
9331
- description: "1 point for active vendor standard subscription (monthly reward)",
9332
- value: 1
9333
- },
9334
- [EnumAffiliateRewardType.ACTIVE_VENDOR_BONUS_REWARD]: {
9335
- description: "5 bonus points for every 10th active vendor (one-time reward)",
9336
- value: 5
9337
- },
9338
- [EnumAffiliateRewardType.ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS]: {
9339
- description: "50 points for active event with vendor registrations (one-time reward)",
9340
- value: 50
9341
- }
9342
- };
9343
- function createAffiliateReward(rewardType, createdAt) {
9344
- const reward = AFFILIATE_REWARDS[rewardType];
9345
- return {
9346
- createdAt,
9347
- redeemedAt: null,
9348
- rewardDescription: reward.description,
9349
- rewardType,
9350
- rewardValue: reward.value
9351
- };
9352
- }
9353
-
9354
- // src/service/affiliate/findAffiliateByPromoCode.ts
9355
- async function findAffiliateByPromoCode(promoCode) {
9356
- return AffiliateModel.findOne({
9357
- affiliateCode: promoCode,
9358
- deletedAt: null
9359
- }).exec();
9360
- }
9361
-
9362
9316
  // src/service/promoCode/normalizePromoCode.ts
9363
9317
  function normalizePromoCode(decoratedPromoCode) {
9364
9318
  const normalized = decoratedPromoCode?.trim().toUpperCase();
9365
9319
  return normalized || null;
9366
9320
  }
9367
9321
 
9368
- // src/service/affiliate/normalizeAffiliatePromoCodes.ts
9369
- function normalizeAffiliatePromoCodes(promoCodes) {
9370
- const normalized = (promoCodes ?? []).map((code) => normalizePromoCode(code)).filter((code) => code !== null);
9371
- return [...new Set(normalized)];
9372
- }
9373
-
9374
- // src/service/database.ts
9375
- import mongoose26 from "mongoose";
9376
- var connectToDatabase = async ({
9377
- appName,
9378
- dbName,
9379
- dbPassword,
9380
- dbUser,
9381
- mongodbUri
9382
- }) => {
9383
- try {
9384
- const mongoUri = mongodbUri ? mongodbUri : (
9385
- // Fallback to MongoDB Atlas connection string
9386
- `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`
9387
- );
9388
- await mongoose26.connect(mongoUri);
9389
- const connectionType = mongodbUri ? "Local MongoDB" : "MongoDB Atlas";
9390
- console.log(
9391
- `${connectionType} connected from server/src/service/database.ts`
9392
- );
9393
- } catch (err) {
9394
- console.error("Error connecting to MongoDB:", err);
9395
- throw err;
9396
- }
9397
- };
9398
-
9399
9322
  // src/service/saveNotificationsInDb.ts
9400
9323
  async function saveNotificationsInDb(payload) {
9401
9324
  const { data, message, title, type, userIds } = payload;
@@ -9529,6 +9452,285 @@ async function sendPushNotifications({
9529
9452
  }
9530
9453
  }
9531
9454
 
9455
+ // src/service/affiliate/createAffiliateReward.ts
9456
+ var AFFILIATE_REWARDS = {
9457
+ [EnumAffiliateRewardType.NEW_EVENT_REGISTRATION]: {
9458
+ description: "10 points for new event registration (one-time reward)",
9459
+ value: 10
9460
+ },
9461
+ [EnumAffiliateRewardType.NEW_VENDOR_REGISTRATION]: {
9462
+ description: "5 points for new vendor registration (one-time reward)",
9463
+ value: 5
9464
+ },
9465
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION]: {
9466
+ description: "3 points for active vendor pro subscription (monthly reward)",
9467
+ value: 3
9468
+ },
9469
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION]: {
9470
+ description: "1 point for active vendor standard subscription (monthly reward)",
9471
+ value: 1
9472
+ },
9473
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_BONUS_REWARD]: {
9474
+ description: "5 bonus points for every 10th active vendor (one-time reward)",
9475
+ value: 5
9476
+ },
9477
+ [EnumAffiliateRewardType.ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS]: {
9478
+ description: "50 points for active event with vendor registrations (one-time reward)",
9479
+ value: 50
9480
+ }
9481
+ };
9482
+ function createAffiliateReward(rewardType, createdAt) {
9483
+ const reward = AFFILIATE_REWARDS[rewardType];
9484
+ return {
9485
+ createdAt,
9486
+ redeemedAt: null,
9487
+ rewardDescription: reward.description,
9488
+ rewardType,
9489
+ rewardValue: reward.value
9490
+ };
9491
+ }
9492
+
9493
+ // src/service/affiliate/vendorSubscriptionRewards.ts
9494
+ function mapVendorLicenceToSubscriptionRewardType(licences, now = /* @__PURE__ */ new Date()) {
9495
+ const validTypes = new Set(
9496
+ (licences ?? []).filter(
9497
+ (licence) => new Date(licence.expiryDate).getTime() > now.getTime()
9498
+ ).map((licence) => licence.licenceType)
9499
+ );
9500
+ if (validTypes.has(EnumUserLicence.PRO_VENDOR) || validTypes.has(EnumUserLicence.PRO_PLUS_VENDOR)) {
9501
+ return EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION;
9502
+ }
9503
+ if (validTypes.has(EnumUserLicence.STANDARD_VENDOR)) {
9504
+ return EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION;
9505
+ }
9506
+ return null;
9507
+ }
9508
+ function getSubscriptionRewardPeriodBounds(now = /* @__PURE__ */ new Date()) {
9509
+ const periodStart = new Date(
9510
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)
9511
+ );
9512
+ const periodEnd = new Date(
9513
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)
9514
+ );
9515
+ return { periodEnd, periodStart };
9516
+ }
9517
+
9518
+ // src/service/affiliate/awardAffiliateVendorSubscriptionRewards.ts
9519
+ function isEligibleVendorResource(resource) {
9520
+ return resource.resourceType === EnumResourceType.VENDOR && resource.resourceActive === true && resource.resourceDeletedAt == null;
9521
+ }
9522
+ async function findAffiliatesWithActiveVendorReferrals() {
9523
+ return await AffiliateModel.find({
9524
+ affiliateResources: {
9525
+ $elemMatch: {
9526
+ resourceActive: true,
9527
+ resourceDeletedAt: null,
9528
+ resourceType: EnumResourceType.VENDOR
9529
+ }
9530
+ },
9531
+ deletedAt: null
9532
+ }).select("_id affiliateCode affiliateResources owner").lean().exec();
9533
+ }
9534
+ async function findEligibleReferredVendor(resourceId, affiliateCode) {
9535
+ return await VendorModel.findOne({
9536
+ _id: resourceId,
9537
+ active: true,
9538
+ deletedAt: null,
9539
+ promoCodes: affiliateCode
9540
+ }).select("_id name owner.userId").lean().exec();
9541
+ }
9542
+ async function resolveSubscriptionRewardTypeForVendorOwner(vendorOwnerUserId, now) {
9543
+ const vendorOwner = await UserModel.findById(vendorOwnerUserId).select("licences").lean().exec();
9544
+ return mapVendorLicenceToSubscriptionRewardType(vendorOwner?.licences, now);
9545
+ }
9546
+ async function notifyAffiliateOwnerOfSubscriptionReward({
9547
+ affiliateId,
9548
+ affiliateOwnerUserId,
9549
+ resourceName,
9550
+ rewardValue
9551
+ }) {
9552
+ const payload = {
9553
+ data: {
9554
+ resourceId: String(affiliateId),
9555
+ resourceName,
9556
+ resourceType: EnumNotificationResourceType.AFFILIATE_REWARD_RECEIVED
9557
+ },
9558
+ message: `You earned ${rewardValue} points! Your referred vendor "${resourceName}" has an active subscription.`,
9559
+ title: "Affiliate points earned",
9560
+ type: EnumNotificationType.SYSTEM,
9561
+ userIds: [affiliateOwnerUserId]
9562
+ };
9563
+ try {
9564
+ await saveNotificationsInDb(payload);
9565
+ await sendPushNotifications(payload);
9566
+ } catch (error) {
9567
+ console.error(
9568
+ `[affiliate] subscription reward notification failed for affiliate ${String(affiliateId)}:`,
9569
+ error instanceof Error ? error.message : error
9570
+ );
9571
+ }
9572
+ }
9573
+ async function tryAwardSubscriptionReward({
9574
+ affiliate,
9575
+ createdAt,
9576
+ period,
9577
+ resource,
9578
+ rewardType,
9579
+ vendor
9580
+ }) {
9581
+ const reward = createAffiliateReward(rewardType, createdAt);
9582
+ const updateResult = await AffiliateModel.updateOne(
9583
+ {
9584
+ _id: affiliate._id,
9585
+ affiliateResources: {
9586
+ $elemMatch: {
9587
+ resourceActive: true,
9588
+ resourceDeletedAt: null,
9589
+ resourceId: vendor._id,
9590
+ resourceType: EnumResourceType.VENDOR,
9591
+ rewards: {
9592
+ $not: {
9593
+ $elemMatch: {
9594
+ createdAt: {
9595
+ $gte: period.periodStart,
9596
+ $lt: period.periodEnd
9597
+ },
9598
+ rewardType
9599
+ }
9600
+ }
9601
+ }
9602
+ }
9603
+ },
9604
+ deletedAt: null
9605
+ },
9606
+ {
9607
+ $inc: { overallPoints: reward.rewardValue },
9608
+ $push: { "affiliateResources.$.rewards": reward }
9609
+ }
9610
+ ).exec();
9611
+ if (updateResult.modifiedCount === 0) {
9612
+ return "skipped";
9613
+ }
9614
+ const affiliateOwnerUserId = affiliate.owner?.userId;
9615
+ if (affiliateOwnerUserId) {
9616
+ await notifyAffiliateOwnerOfSubscriptionReward({
9617
+ affiliateId: affiliate._id,
9618
+ affiliateOwnerUserId,
9619
+ resourceName: resource.resourceName || vendor.name || "vendor",
9620
+ rewardValue: reward.rewardValue
9621
+ });
9622
+ }
9623
+ return "awarded";
9624
+ }
9625
+ async function processAffiliateVendorReferral({
9626
+ affiliate,
9627
+ affiliateCode,
9628
+ createdAt,
9629
+ now,
9630
+ period,
9631
+ resource
9632
+ }) {
9633
+ const vendor = await findEligibleReferredVendor(
9634
+ resource.resourceId,
9635
+ affiliateCode
9636
+ );
9637
+ if (!vendor?.owner?.userId) {
9638
+ return "skipped";
9639
+ }
9640
+ const rewardType = await resolveSubscriptionRewardTypeForVendorOwner(
9641
+ vendor.owner.userId,
9642
+ now
9643
+ );
9644
+ if (!rewardType) {
9645
+ return "skipped";
9646
+ }
9647
+ return tryAwardSubscriptionReward({
9648
+ affiliate,
9649
+ createdAt,
9650
+ period,
9651
+ resource,
9652
+ rewardType,
9653
+ vendor
9654
+ });
9655
+ }
9656
+ async function awardAffiliateVendorSubscriptionRewards(now = /* @__PURE__ */ new Date()) {
9657
+ const period = getSubscriptionRewardPeriodBounds(now);
9658
+ const createdAt = now;
9659
+ const affiliates = await findAffiliatesWithActiveVendorReferrals();
9660
+ let awarded = 0;
9661
+ let skipped = 0;
9662
+ const seenPairs = /* @__PURE__ */ new Set();
9663
+ for (const affiliate of affiliates) {
9664
+ const affiliateCode = normalizePromoCode(affiliate.affiliateCode);
9665
+ if (!affiliateCode) {
9666
+ continue;
9667
+ }
9668
+ for (const resource of affiliate.affiliateResources) {
9669
+ if (!isEligibleVendorResource(resource)) {
9670
+ continue;
9671
+ }
9672
+ const pairKey = `${String(affiliate._id)}:${String(resource.resourceId)}`;
9673
+ if (seenPairs.has(pairKey)) {
9674
+ continue;
9675
+ }
9676
+ seenPairs.add(pairKey);
9677
+ const outcome = await processAffiliateVendorReferral({
9678
+ affiliate,
9679
+ affiliateCode,
9680
+ createdAt,
9681
+ now,
9682
+ period,
9683
+ resource
9684
+ });
9685
+ if (outcome === "awarded") {
9686
+ awarded += 1;
9687
+ } else {
9688
+ skipped += 1;
9689
+ }
9690
+ }
9691
+ }
9692
+ return { awarded, skipped };
9693
+ }
9694
+
9695
+ // src/service/affiliate/findAffiliateByPromoCode.ts
9696
+ async function findAffiliateByPromoCode(promoCode) {
9697
+ return AffiliateModel.findOne({
9698
+ affiliateCode: promoCode,
9699
+ deletedAt: null
9700
+ }).exec();
9701
+ }
9702
+
9703
+ // src/service/affiliate/normalizeAffiliatePromoCodes.ts
9704
+ function normalizeAffiliatePromoCodes(promoCodes) {
9705
+ const normalized = (promoCodes ?? []).map((code) => normalizePromoCode(code)).filter((code) => code !== null);
9706
+ return [...new Set(normalized)];
9707
+ }
9708
+
9709
+ // src/service/database.ts
9710
+ import mongoose26 from "mongoose";
9711
+ var connectToDatabase = async ({
9712
+ appName,
9713
+ dbName,
9714
+ dbPassword,
9715
+ dbUser,
9716
+ mongodbUri
9717
+ }) => {
9718
+ try {
9719
+ const mongoUri = mongodbUri ? mongodbUri : (
9720
+ // Fallback to MongoDB Atlas connection string
9721
+ `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`
9722
+ );
9723
+ await mongoose26.connect(mongoUri);
9724
+ const connectionType = mongodbUri ? "Local MongoDB" : "MongoDB Atlas";
9725
+ console.log(
9726
+ `${connectionType} connected from server/src/service/database.ts`
9727
+ );
9728
+ } catch (err) {
9729
+ console.error("Error connecting to MongoDB:", err);
9730
+ throw err;
9731
+ }
9732
+ };
9733
+
9532
9734
  // src/service/updateAdStatus.ts
9533
9735
  async function updateAdStatuses() {
9534
9736
  const now = /* @__PURE__ */ new Date();
@@ -9951,6 +10153,7 @@ export {
9951
10153
  VerificationTokenModel,
9952
10154
  activeAffiliateCodeExists,
9953
10155
  associatesSchema,
10156
+ awardAffiliateVendorSubscriptionRewards,
9954
10157
  baseResourceFields,
9955
10158
  connectToDatabase,
9956
10159
  convertObjectIdsToStrings,
@@ -9960,8 +10163,10 @@ export {
9960
10163
  express,
9961
10164
  findAffiliateByPromoCode,
9962
10165
  findEventOrImportedMarketById,
10166
+ getSubscriptionRewardPeriodBounds,
9963
10167
  locationGeoSchema,
9964
10168
  locationsSchema,
10169
+ mapVendorLicenceToSubscriptionRewardType,
9965
10170
  mongoose29 as mongoose,
9966
10171
  normalizeAffiliatePromoCodes,
9967
10172
  normalizePromoCode,