@timardex/cluemart-server-shared 1.0.284 → 1.0.289

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.
@@ -1,5 +1,5 @@
1
1
  import * as _timardex_cluemart_shared from '@timardex/cluemart-shared';
2
- import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
2
+ import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, UserLicenceType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
3
3
  import * as mongoose from 'mongoose';
4
4
  import { o as SchemaAffiliateResourceType, S as SchemaOwnerType, j as SchemaRelationType } from '../Affiliate-CmS0A1gU.mjs';
5
5
  import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Bnqec74U.mjs';
@@ -18,6 +18,31 @@ import 'express';
18
18
  */
19
19
  declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
20
20
 
21
+ type AwardAffiliateVendorSubscriptionRewardsResult = {
22
+ awarded: number;
23
+ skipped: number;
24
+ };
25
+ /**
26
+ * Awards monthly vendor subscription rewards to affiliates with active referred vendors.
27
+ *
28
+ * Eligibility per affiliate resource:
29
+ * - Affiliate is not soft-deleted
30
+ * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted
31
+ * - Vendor is active, not deleted, and `promoCodes` includes the affiliate code
32
+ * - Vendor owner has a non-expired Standard / Pro / Pro+ vendor licence
33
+ *
34
+ * Reward type follows the owner's highest valid licence:
35
+ * - Pro / Pro+ → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)
36
+ * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)
37
+ *
38
+ * Idempotent per affiliate + vendor + reward type + calendar month.
39
+ * On successful award, notifies the affiliate owner (best-effort).
40
+ *
41
+ * @param now - Reference time for month bounds and licence expiry (defaults to now).
42
+ * @returns Counts of awarded and skipped candidates.
43
+ */
44
+ declare function awardAffiliateVendorSubscriptionRewards(now?: Date): Promise<AwardAffiliateVendorSubscriptionRewardsResult>;
45
+
21
46
  declare const AFFILIATE_REWARDS: {
22
47
  NEW_EVENT_REGISTRATION: {
23
48
  description: string;
@@ -84,6 +109,19 @@ declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mo
84
109
  */
85
110
  declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
86
111
 
112
+ /**
113
+ * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.
114
+ *
115
+ * Prefer Pro / Pro+ over Standard when both are valid.
116
+ * Returns `null` when no non-expired vendor licence applies.
117
+ */
118
+ declare function mapVendorLicenceToSubscriptionRewardType(licences: UserLicenceType[] | null | undefined, now?: Date): EnumAffiliateRewardType | null;
119
+ /** Inclusive start / exclusive end of the UTC calendar month containing `now`. */
120
+ declare function getSubscriptionRewardPeriodBounds(now?: Date): {
121
+ periodEnd: Date;
122
+ periodStart: Date;
123
+ };
124
+
87
125
  /**
88
126
  * Connect to MongoDB using Mongoose.
89
127
  * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
@@ -159,4 +197,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
159
197
  */
160
198
  declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
161
199
 
162
- export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, activeAffiliateCodeExists, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, normalizeAffiliatePromoCodes, normalizePromoCode, saveNotificationsInDb, sendPushNotifications, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense };
200
+ export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, type AwardAffiliateVendorSubscriptionRewardsResult, activeAffiliateCodeExists, awardAffiliateVendorSubscriptionRewards, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, getSubscriptionRewardPeriodBounds, mapVendorLicenceToSubscriptionRewardType, normalizeAffiliatePromoCodes, normalizePromoCode, saveNotificationsInDb, sendPushNotifications, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense };
@@ -1,5 +1,5 @@
1
1
  import * as _timardex_cluemart_shared from '@timardex/cluemart-shared';
2
- import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
2
+ import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, UserLicenceType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
3
3
  import * as mongoose from 'mongoose';
4
4
  import { o as SchemaAffiliateResourceType, S as SchemaOwnerType, j as SchemaRelationType } from '../Affiliate-C_g6TdeG.js';
5
5
  import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Bnqec74U.js';
@@ -18,6 +18,31 @@ import 'express';
18
18
  */
19
19
  declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
20
20
 
21
+ type AwardAffiliateVendorSubscriptionRewardsResult = {
22
+ awarded: number;
23
+ skipped: number;
24
+ };
25
+ /**
26
+ * Awards monthly vendor subscription rewards to affiliates with active referred vendors.
27
+ *
28
+ * Eligibility per affiliate resource:
29
+ * - Affiliate is not soft-deleted
30
+ * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted
31
+ * - Vendor is active, not deleted, and `promoCodes` includes the affiliate code
32
+ * - Vendor owner has a non-expired Standard / Pro / Pro+ vendor licence
33
+ *
34
+ * Reward type follows the owner's highest valid licence:
35
+ * - Pro / Pro+ → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)
36
+ * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)
37
+ *
38
+ * Idempotent per affiliate + vendor + reward type + calendar month.
39
+ * On successful award, notifies the affiliate owner (best-effort).
40
+ *
41
+ * @param now - Reference time for month bounds and licence expiry (defaults to now).
42
+ * @returns Counts of awarded and skipped candidates.
43
+ */
44
+ declare function awardAffiliateVendorSubscriptionRewards(now?: Date): Promise<AwardAffiliateVendorSubscriptionRewardsResult>;
45
+
21
46
  declare const AFFILIATE_REWARDS: {
22
47
  NEW_EVENT_REGISTRATION: {
23
48
  description: string;
@@ -84,6 +109,19 @@ declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mo
84
109
  */
85
110
  declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
86
111
 
112
+ /**
113
+ * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.
114
+ *
115
+ * Prefer Pro / Pro+ over Standard when both are valid.
116
+ * Returns `null` when no non-expired vendor licence applies.
117
+ */
118
+ declare function mapVendorLicenceToSubscriptionRewardType(licences: UserLicenceType[] | null | undefined, now?: Date): EnumAffiliateRewardType | null;
119
+ /** Inclusive start / exclusive end of the UTC calendar month containing `now`. */
120
+ declare function getSubscriptionRewardPeriodBounds(now?: Date): {
121
+ periodEnd: Date;
122
+ periodStart: Date;
123
+ };
124
+
87
125
  /**
88
126
  * Connect to MongoDB using Mongoose.
89
127
  * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
@@ -159,4 +197,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
159
197
  */
160
198
  declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
161
199
 
162
- export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, activeAffiliateCodeExists, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, normalizeAffiliatePromoCodes, normalizePromoCode, saveNotificationsInDb, sendPushNotifications, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense };
200
+ export { ACTIVE_NOT_DELETED_FILTER, AFFILIATE_REWARDS, type AwardAffiliateVendorSubscriptionRewardsResult, activeAffiliateCodeExists, awardAffiliateVendorSubscriptionRewards, connectToDatabase, convertObjectIdsToStrings, createAffiliateReward, didRemoveAnyEventDates, findAffiliateByPromoCode, findEventOrImportedMarketById, getSubscriptionRewardPeriodBounds, mapVendorLicenceToSubscriptionRewardType, normalizeAffiliatePromoCodes, normalizePromoCode, saveNotificationsInDb, sendPushNotifications, updateAdStatuses, updateAllEventDateTimeStatuses, updateRelationDatesToUnavailable, updateSingleDateTimeStatus, updateVendorBasedOnUserLicense };
@@ -7,6 +7,8 @@ import {
7
7
  EnumChatType,
8
8
  EnumEventDateStatus,
9
9
  EnumInviteStatus,
10
+ EnumNotificationResourceType,
11
+ EnumNotificationType,
10
12
  EnumResourceType,
11
13
  EnumUserLicence,
12
14
  EventModel,
@@ -17,7 +19,7 @@ import {
17
19
  VendorModel,
18
20
  dateFormat,
19
21
  timeFormat
20
- } from "../chunk-4FWW5CDC.mjs";
22
+ } from "../chunk-KRG2CATM.mjs";
21
23
 
22
24
  // src/service/promoCode/constants.ts
23
25
  var ACTIVE_NOT_DELETED_FILTER = {
@@ -34,89 +36,12 @@ async function activeAffiliateCodeExists(affiliateCode) {
34
36
  return existing !== null;
35
37
  }
36
38
 
37
- // src/service/affiliate/createAffiliateReward.ts
38
- var AFFILIATE_REWARDS = {
39
- [EnumAffiliateRewardType.NEW_EVENT_REGISTRATION]: {
40
- description: "10 points for new event registration (one-time reward)",
41
- value: 10
42
- },
43
- [EnumAffiliateRewardType.NEW_VENDOR_REGISTRATION]: {
44
- description: "5 points for new vendor registration (one-time reward)",
45
- value: 5
46
- },
47
- [EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION]: {
48
- description: "3 points for active vendor pro subscription (monthly reward)",
49
- value: 3
50
- },
51
- [EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION]: {
52
- description: "1 point for active vendor standard subscription (monthly reward)",
53
- value: 1
54
- },
55
- [EnumAffiliateRewardType.ACTIVE_VENDOR_BONUS_REWARD]: {
56
- description: "5 bonus points for every 10th active vendor (one-time reward)",
57
- value: 5
58
- },
59
- [EnumAffiliateRewardType.ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS]: {
60
- description: "50 points for active event with vendor registrations (one-time reward)",
61
- value: 50
62
- }
63
- };
64
- function createAffiliateReward(rewardType, createdAt) {
65
- const reward = AFFILIATE_REWARDS[rewardType];
66
- return {
67
- createdAt,
68
- redeemedAt: null,
69
- rewardDescription: reward.description,
70
- rewardType,
71
- rewardValue: reward.value
72
- };
73
- }
74
-
75
- // src/service/affiliate/findAffiliateByPromoCode.ts
76
- async function findAffiliateByPromoCode(promoCode) {
77
- return AffiliateModel.findOne({
78
- affiliateCode: promoCode,
79
- deletedAt: null
80
- }).exec();
81
- }
82
-
83
39
  // src/service/promoCode/normalizePromoCode.ts
84
40
  function normalizePromoCode(decoratedPromoCode) {
85
41
  const normalized = decoratedPromoCode?.trim().toUpperCase();
86
42
  return normalized || null;
87
43
  }
88
44
 
89
- // src/service/affiliate/normalizeAffiliatePromoCodes.ts
90
- function normalizeAffiliatePromoCodes(promoCodes) {
91
- const normalized = (promoCodes ?? []).map((code) => normalizePromoCode(code)).filter((code) => code !== null);
92
- return [...new Set(normalized)];
93
- }
94
-
95
- // src/service/database.ts
96
- import mongoose from "mongoose";
97
- var connectToDatabase = async ({
98
- appName,
99
- dbName,
100
- dbPassword,
101
- dbUser,
102
- mongodbUri
103
- }) => {
104
- try {
105
- const mongoUri = mongodbUri ? mongodbUri : (
106
- // Fallback to MongoDB Atlas connection string
107
- `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`
108
- );
109
- await mongoose.connect(mongoUri);
110
- const connectionType = mongodbUri ? "Local MongoDB" : "MongoDB Atlas";
111
- console.log(
112
- `${connectionType} connected from server/src/service/database.ts`
113
- );
114
- } catch (err) {
115
- console.error("Error connecting to MongoDB:", err);
116
- throw err;
117
- }
118
- };
119
-
120
45
  // src/service/saveNotificationsInDb.ts
121
46
  async function saveNotificationsInDb(payload) {
122
47
  const { data, message, title, type, userIds } = payload;
@@ -250,6 +175,285 @@ async function sendPushNotifications({
250
175
  }
251
176
  }
252
177
 
178
+ // src/service/affiliate/createAffiliateReward.ts
179
+ var AFFILIATE_REWARDS = {
180
+ [EnumAffiliateRewardType.NEW_EVENT_REGISTRATION]: {
181
+ description: "10 points for new event registration (one-time reward)",
182
+ value: 10
183
+ },
184
+ [EnumAffiliateRewardType.NEW_VENDOR_REGISTRATION]: {
185
+ description: "5 points for new vendor registration (one-time reward)",
186
+ value: 5
187
+ },
188
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION]: {
189
+ description: "3 points for active vendor pro subscription (monthly reward)",
190
+ value: 3
191
+ },
192
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION]: {
193
+ description: "1 point for active vendor standard subscription (monthly reward)",
194
+ value: 1
195
+ },
196
+ [EnumAffiliateRewardType.ACTIVE_VENDOR_BONUS_REWARD]: {
197
+ description: "5 bonus points for every 10th active vendor (one-time reward)",
198
+ value: 5
199
+ },
200
+ [EnumAffiliateRewardType.ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS]: {
201
+ description: "50 points for active event with vendor registrations (one-time reward)",
202
+ value: 50
203
+ }
204
+ };
205
+ function createAffiliateReward(rewardType, createdAt) {
206
+ const reward = AFFILIATE_REWARDS[rewardType];
207
+ return {
208
+ createdAt,
209
+ redeemedAt: null,
210
+ rewardDescription: reward.description,
211
+ rewardType,
212
+ rewardValue: reward.value
213
+ };
214
+ }
215
+
216
+ // src/service/affiliate/vendorSubscriptionRewards.ts
217
+ function mapVendorLicenceToSubscriptionRewardType(licences, now = /* @__PURE__ */ new Date()) {
218
+ const validTypes = new Set(
219
+ (licences ?? []).filter(
220
+ (licence) => new Date(licence.expiryDate).getTime() > now.getTime()
221
+ ).map((licence) => licence.licenceType)
222
+ );
223
+ if (validTypes.has(EnumUserLicence.PRO_VENDOR) || validTypes.has(EnumUserLicence.PRO_PLUS_VENDOR)) {
224
+ return EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION;
225
+ }
226
+ if (validTypes.has(EnumUserLicence.STANDARD_VENDOR)) {
227
+ return EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION;
228
+ }
229
+ return null;
230
+ }
231
+ function getSubscriptionRewardPeriodBounds(now = /* @__PURE__ */ new Date()) {
232
+ const periodStart = new Date(
233
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)
234
+ );
235
+ const periodEnd = new Date(
236
+ Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)
237
+ );
238
+ return { periodEnd, periodStart };
239
+ }
240
+
241
+ // src/service/affiliate/awardAffiliateVendorSubscriptionRewards.ts
242
+ function isEligibleVendorResource(resource) {
243
+ return resource.resourceType === EnumResourceType.VENDOR && resource.resourceActive === true && resource.resourceDeletedAt == null;
244
+ }
245
+ async function findAffiliatesWithActiveVendorReferrals() {
246
+ return await AffiliateModel.find({
247
+ affiliateResources: {
248
+ $elemMatch: {
249
+ resourceActive: true,
250
+ resourceDeletedAt: null,
251
+ resourceType: EnumResourceType.VENDOR
252
+ }
253
+ },
254
+ deletedAt: null
255
+ }).select("_id affiliateCode affiliateResources owner").lean().exec();
256
+ }
257
+ async function findEligibleReferredVendor(resourceId, affiliateCode) {
258
+ return await VendorModel.findOne({
259
+ _id: resourceId,
260
+ active: true,
261
+ deletedAt: null,
262
+ promoCodes: affiliateCode
263
+ }).select("_id name owner.userId").lean().exec();
264
+ }
265
+ async function resolveSubscriptionRewardTypeForVendorOwner(vendorOwnerUserId, now) {
266
+ const vendorOwner = await UserModel.findById(vendorOwnerUserId).select("licences").lean().exec();
267
+ return mapVendorLicenceToSubscriptionRewardType(vendorOwner?.licences, now);
268
+ }
269
+ async function notifyAffiliateOwnerOfSubscriptionReward({
270
+ affiliateId,
271
+ affiliateOwnerUserId,
272
+ resourceName,
273
+ rewardValue
274
+ }) {
275
+ const payload = {
276
+ data: {
277
+ resourceId: String(affiliateId),
278
+ resourceName,
279
+ resourceType: EnumNotificationResourceType.AFFILIATE_REWARD_RECEIVED
280
+ },
281
+ message: `You earned ${rewardValue} points! Your referred vendor "${resourceName}" has an active subscription.`,
282
+ title: "Affiliate points earned",
283
+ type: EnumNotificationType.SYSTEM,
284
+ userIds: [affiliateOwnerUserId]
285
+ };
286
+ try {
287
+ await saveNotificationsInDb(payload);
288
+ await sendPushNotifications(payload);
289
+ } catch (error) {
290
+ console.error(
291
+ `[affiliate] subscription reward notification failed for affiliate ${String(affiliateId)}:`,
292
+ error instanceof Error ? error.message : error
293
+ );
294
+ }
295
+ }
296
+ async function tryAwardSubscriptionReward({
297
+ affiliate,
298
+ createdAt,
299
+ period,
300
+ resource,
301
+ rewardType,
302
+ vendor
303
+ }) {
304
+ const reward = createAffiliateReward(rewardType, createdAt);
305
+ const updateResult = await AffiliateModel.updateOne(
306
+ {
307
+ _id: affiliate._id,
308
+ affiliateResources: {
309
+ $elemMatch: {
310
+ resourceActive: true,
311
+ resourceDeletedAt: null,
312
+ resourceId: vendor._id,
313
+ resourceType: EnumResourceType.VENDOR,
314
+ rewards: {
315
+ $not: {
316
+ $elemMatch: {
317
+ createdAt: {
318
+ $gte: period.periodStart,
319
+ $lt: period.periodEnd
320
+ },
321
+ rewardType
322
+ }
323
+ }
324
+ }
325
+ }
326
+ },
327
+ deletedAt: null
328
+ },
329
+ {
330
+ $inc: { overallPoints: reward.rewardValue },
331
+ $push: { "affiliateResources.$.rewards": reward }
332
+ }
333
+ ).exec();
334
+ if (updateResult.modifiedCount === 0) {
335
+ return "skipped";
336
+ }
337
+ const affiliateOwnerUserId = affiliate.owner?.userId;
338
+ if (affiliateOwnerUserId) {
339
+ await notifyAffiliateOwnerOfSubscriptionReward({
340
+ affiliateId: affiliate._id,
341
+ affiliateOwnerUserId,
342
+ resourceName: resource.resourceName || vendor.name || "vendor",
343
+ rewardValue: reward.rewardValue
344
+ });
345
+ }
346
+ return "awarded";
347
+ }
348
+ async function processAffiliateVendorReferral({
349
+ affiliate,
350
+ affiliateCode,
351
+ createdAt,
352
+ now,
353
+ period,
354
+ resource
355
+ }) {
356
+ const vendor = await findEligibleReferredVendor(
357
+ resource.resourceId,
358
+ affiliateCode
359
+ );
360
+ if (!vendor?.owner?.userId) {
361
+ return "skipped";
362
+ }
363
+ const rewardType = await resolveSubscriptionRewardTypeForVendorOwner(
364
+ vendor.owner.userId,
365
+ now
366
+ );
367
+ if (!rewardType) {
368
+ return "skipped";
369
+ }
370
+ return tryAwardSubscriptionReward({
371
+ affiliate,
372
+ createdAt,
373
+ period,
374
+ resource,
375
+ rewardType,
376
+ vendor
377
+ });
378
+ }
379
+ async function awardAffiliateVendorSubscriptionRewards(now = /* @__PURE__ */ new Date()) {
380
+ const period = getSubscriptionRewardPeriodBounds(now);
381
+ const createdAt = now;
382
+ const affiliates = await findAffiliatesWithActiveVendorReferrals();
383
+ let awarded = 0;
384
+ let skipped = 0;
385
+ const seenPairs = /* @__PURE__ */ new Set();
386
+ for (const affiliate of affiliates) {
387
+ const affiliateCode = normalizePromoCode(affiliate.affiliateCode);
388
+ if (!affiliateCode) {
389
+ continue;
390
+ }
391
+ for (const resource of affiliate.affiliateResources) {
392
+ if (!isEligibleVendorResource(resource)) {
393
+ continue;
394
+ }
395
+ const pairKey = `${String(affiliate._id)}:${String(resource.resourceId)}`;
396
+ if (seenPairs.has(pairKey)) {
397
+ continue;
398
+ }
399
+ seenPairs.add(pairKey);
400
+ const outcome = await processAffiliateVendorReferral({
401
+ affiliate,
402
+ affiliateCode,
403
+ createdAt,
404
+ now,
405
+ period,
406
+ resource
407
+ });
408
+ if (outcome === "awarded") {
409
+ awarded += 1;
410
+ } else {
411
+ skipped += 1;
412
+ }
413
+ }
414
+ }
415
+ return { awarded, skipped };
416
+ }
417
+
418
+ // src/service/affiliate/findAffiliateByPromoCode.ts
419
+ async function findAffiliateByPromoCode(promoCode) {
420
+ return AffiliateModel.findOne({
421
+ affiliateCode: promoCode,
422
+ deletedAt: null
423
+ }).exec();
424
+ }
425
+
426
+ // src/service/affiliate/normalizeAffiliatePromoCodes.ts
427
+ function normalizeAffiliatePromoCodes(promoCodes) {
428
+ const normalized = (promoCodes ?? []).map((code) => normalizePromoCode(code)).filter((code) => code !== null);
429
+ return [...new Set(normalized)];
430
+ }
431
+
432
+ // src/service/database.ts
433
+ import mongoose from "mongoose";
434
+ var connectToDatabase = async ({
435
+ appName,
436
+ dbName,
437
+ dbPassword,
438
+ dbUser,
439
+ mongodbUri
440
+ }) => {
441
+ try {
442
+ const mongoUri = mongodbUri ? mongodbUri : (
443
+ // Fallback to MongoDB Atlas connection string
444
+ `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`
445
+ );
446
+ await mongoose.connect(mongoUri);
447
+ const connectionType = mongodbUri ? "Local MongoDB" : "MongoDB Atlas";
448
+ console.log(
449
+ `${connectionType} connected from server/src/service/database.ts`
450
+ );
451
+ } catch (err) {
452
+ console.error("Error connecting to MongoDB:", err);
453
+ throw err;
454
+ }
455
+ };
456
+
253
457
  // src/service/updateAdStatus.ts
254
458
  async function updateAdStatuses() {
255
459
  const now = /* @__PURE__ */ new Date();
@@ -630,12 +834,15 @@ export {
630
834
  ACTIVE_NOT_DELETED_FILTER,
631
835
  AFFILIATE_REWARDS,
632
836
  activeAffiliateCodeExists,
837
+ awardAffiliateVendorSubscriptionRewards,
633
838
  connectToDatabase,
634
839
  convertObjectIdsToStrings,
635
840
  createAffiliateReward,
636
841
  didRemoveAnyEventDates,
637
842
  findAffiliateByPromoCode,
638
843
  findEventOrImportedMarketById,
844
+ getSubscriptionRewardPeriodBounds,
845
+ mapVendorLicenceToSubscriptionRewardType,
639
846
  normalizeAffiliatePromoCodes,
640
847
  normalizePromoCode,
641
848
  saveNotificationsInDb,