@timardex/cluemart-server-shared 1.0.281 → 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/Affiliate-C_g6TdeG.d.ts +396 -0
- package/dist/Affiliate-CmS0A1gU.d.mts +396 -0
- package/dist/{Chat-DYkhie3G.d.mts → Chat-Bnqec74U.d.mts} +4 -4
- package/dist/{Chat-DYkhie3G.d.ts → Chat-Bnqec74U.d.ts} +4 -4
- package/dist/{chunk-6IK6D2KS.mjs → chunk-6EDO734D.mjs} +6 -1
- package/dist/chunk-6EDO734D.mjs.map +1 -0
- package/dist/index.cjs +334 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +273 -149
- package/dist/index.d.ts +273 -149
- package/dist/index.mjs +318 -31
- package/dist/index.mjs.map +1 -1
- package/dist/mongoose/index.cjs +2 -0
- package/dist/mongoose/index.cjs.map +1 -1
- package/dist/mongoose/index.d.mts +40 -387
- package/dist/mongoose/index.d.ts +40 -387
- package/dist/mongoose/index.mjs +1 -1
- package/dist/service/index.cjs +677 -382
- package/dist/service/index.cjs.map +1 -1
- package/dist/service/index.d.mts +129 -5
- package/dist/service/index.d.ts +129 -5
- package/dist/service/index.mjs +313 -24
- package/dist/service/index.mjs.map +1 -1
- package/dist/types/index.d.mts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -2
- package/dist/Relation-6t3Gn4pM.d.ts +0 -47
- package/dist/Relation-BjYghDE9.d.mts +0 -47
- package/dist/chunk-6IK6D2KS.mjs.map +0 -1
package/dist/service/index.d.mts
CHANGED
|
@@ -1,11 +1,127 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
|
|
1
|
+
import * as _timardex_cluemart_shared from '@timardex/cluemart-shared';
|
|
2
|
+
import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, UserLicenceType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
|
|
3
|
+
import * as mongoose from 'mongoose';
|
|
4
|
+
import { o as SchemaAffiliateResourceType, S as SchemaOwnerType, j as SchemaRelationType } from '../Affiliate-CmS0A1gU.mjs';
|
|
5
|
+
import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Bnqec74U.mjs';
|
|
3
6
|
import dayjs from 'dayjs';
|
|
4
7
|
import { DateTimeType as DateTimeType$1 } from '@timardex/cluemart-shared/types';
|
|
5
|
-
import { S as SchemaRelationType } from '../Relation-BjYghDE9.mjs';
|
|
6
|
-
import 'mongoose';
|
|
7
8
|
import 'express';
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether an affiliate code is already taken by an active, non-deleted affiliate.
|
|
12
|
+
*
|
|
13
|
+
* Used when generating new affiliate codes so collisions are avoided before insert.
|
|
14
|
+
* Matches the partial unique index on `affiliateCode` for active documents.
|
|
15
|
+
*
|
|
16
|
+
* @param affiliateCode - Candidate affiliate promo code to check.
|
|
17
|
+
* @returns `true` when an active, non-deleted affiliate already uses this code.
|
|
18
|
+
*/
|
|
19
|
+
declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
|
|
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
|
+
|
|
46
|
+
declare const AFFILIATE_REWARDS: {
|
|
47
|
+
NEW_EVENT_REGISTRATION: {
|
|
48
|
+
description: string;
|
|
49
|
+
value: number;
|
|
50
|
+
};
|
|
51
|
+
NEW_VENDOR_REGISTRATION: {
|
|
52
|
+
description: string;
|
|
53
|
+
value: number;
|
|
54
|
+
};
|
|
55
|
+
ACTIVE_VENDOR_PRO_SUBSCRIPTION: {
|
|
56
|
+
description: string;
|
|
57
|
+
value: number;
|
|
58
|
+
};
|
|
59
|
+
ACTIVE_VENDOR_STANDARD_SUBSCRIPTION: {
|
|
60
|
+
description: string;
|
|
61
|
+
value: number;
|
|
62
|
+
};
|
|
63
|
+
ACTIVE_VENDOR_BONUS_REWARD: {
|
|
64
|
+
description: string;
|
|
65
|
+
value: number;
|
|
66
|
+
};
|
|
67
|
+
ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS: {
|
|
68
|
+
description: string;
|
|
69
|
+
value: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Create an affiliate reward
|
|
74
|
+
* @param rewardType - The type of reward
|
|
75
|
+
* @param createdAt - The date the reward was received
|
|
76
|
+
* @returns The affiliate reward
|
|
77
|
+
* @example
|
|
78
|
+
* const reward = createAffiliateReward(EnumAffiliateRewardType.NEW_EVENT_REGISTRATION, new Date());
|
|
79
|
+
* console.log(reward);
|
|
80
|
+
*/
|
|
81
|
+
declare function createAffiliateReward(rewardType: EnumAffiliateRewardType, createdAt: Date): AffiliateRewardType;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Loads the affiliate whose `affiliateCode` matches the given promo code.
|
|
85
|
+
*
|
|
86
|
+
* Includes inactive affiliates (`active: false`) so resource referrals work before
|
|
87
|
+
* admin approval. Soft-deleted affiliates (`deletedAt` set) are excluded.
|
|
88
|
+
*
|
|
89
|
+
* @param promoCode - Normalized affiliate promo code to resolve.
|
|
90
|
+
* @returns The matching affiliate document, or `null` when not found.
|
|
91
|
+
*/
|
|
92
|
+
declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mongoose.Document<unknown, {}, Omit<_timardex_cluemart_shared.AffiliateType, "_id" | "owner" | "affiliateResources"> & {
|
|
93
|
+
affiliateResources: SchemaAffiliateResourceType[];
|
|
94
|
+
owner: SchemaOwnerType;
|
|
95
|
+
}, {}, {}> & Omit<_timardex_cluemart_shared.AffiliateType, "_id" | "owner" | "affiliateResources"> & {
|
|
96
|
+
affiliateResources: SchemaAffiliateResourceType[];
|
|
97
|
+
owner: SchemaOwnerType;
|
|
98
|
+
} & {
|
|
99
|
+
_id: mongoose.Types.ObjectId;
|
|
100
|
+
} & {
|
|
101
|
+
__v: number;
|
|
102
|
+
}) | null>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalizes, trims, uppercases, and deduplicates resource promo codes.
|
|
106
|
+
*
|
|
107
|
+
* @param promoCodes - Raw promo codes from a create-resource mutation input.
|
|
108
|
+
* @returns Unique normalized codes; empty strings and whitespace-only values are removed.
|
|
109
|
+
*/
|
|
110
|
+
declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
|
|
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
|
+
|
|
9
125
|
/**
|
|
10
126
|
* Connect to MongoDB using Mongoose.
|
|
11
127
|
* Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
|
|
@@ -18,6 +134,14 @@ declare const connectToDatabase: ({ appName, dbName, dbPassword, dbUser, mongodb
|
|
|
18
134
|
mongodbUri: string;
|
|
19
135
|
}) => Promise<void>;
|
|
20
136
|
|
|
137
|
+
/** Matches partial unique indexes for promo/affiliate codes on active documents. */
|
|
138
|
+
declare const ACTIVE_NOT_DELETED_FILTER: {
|
|
139
|
+
readonly active: true;
|
|
140
|
+
readonly deletedAt: null;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
declare function normalizePromoCode(decoratedPromoCode: string | null | undefined): string | null;
|
|
144
|
+
|
|
21
145
|
/**
|
|
22
146
|
* Create notifications in the database for multiple users
|
|
23
147
|
* This is typically called when sending push notifications
|
|
@@ -73,4 +197,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
|
|
|
73
197
|
*/
|
|
74
198
|
declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
|
|
75
199
|
|
|
76
|
-
export { connectToDatabase, convertObjectIdsToStrings, didRemoveAnyEventDates, findEventOrImportedMarketById, 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 };
|
package/dist/service/index.d.ts
CHANGED
|
@@ -1,11 +1,127 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
|
|
1
|
+
import * as _timardex_cluemart_shared from '@timardex/cluemart-shared';
|
|
2
|
+
import { EnumAffiliateRewardType, AffiliateRewardType, PromoCodeType, UserLicenceType, EnumUserLicence, DateTimeType, EventListItemType, DateTimeWithPriceType } from '@timardex/cluemart-shared';
|
|
3
|
+
import * as mongoose from 'mongoose';
|
|
4
|
+
import { o as SchemaAffiliateResourceType, S as SchemaOwnerType, j as SchemaRelationType } from '../Affiliate-C_g6TdeG.js';
|
|
5
|
+
import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Bnqec74U.js';
|
|
3
6
|
import dayjs from 'dayjs';
|
|
4
7
|
import { DateTimeType as DateTimeType$1 } from '@timardex/cluemart-shared/types';
|
|
5
|
-
import { S as SchemaRelationType } from '../Relation-6t3Gn4pM.js';
|
|
6
|
-
import 'mongoose';
|
|
7
8
|
import 'express';
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether an affiliate code is already taken by an active, non-deleted affiliate.
|
|
12
|
+
*
|
|
13
|
+
* Used when generating new affiliate codes so collisions are avoided before insert.
|
|
14
|
+
* Matches the partial unique index on `affiliateCode` for active documents.
|
|
15
|
+
*
|
|
16
|
+
* @param affiliateCode - Candidate affiliate promo code to check.
|
|
17
|
+
* @returns `true` when an active, non-deleted affiliate already uses this code.
|
|
18
|
+
*/
|
|
19
|
+
declare function activeAffiliateCodeExists(affiliateCode: string): Promise<boolean>;
|
|
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
|
+
|
|
46
|
+
declare const AFFILIATE_REWARDS: {
|
|
47
|
+
NEW_EVENT_REGISTRATION: {
|
|
48
|
+
description: string;
|
|
49
|
+
value: number;
|
|
50
|
+
};
|
|
51
|
+
NEW_VENDOR_REGISTRATION: {
|
|
52
|
+
description: string;
|
|
53
|
+
value: number;
|
|
54
|
+
};
|
|
55
|
+
ACTIVE_VENDOR_PRO_SUBSCRIPTION: {
|
|
56
|
+
description: string;
|
|
57
|
+
value: number;
|
|
58
|
+
};
|
|
59
|
+
ACTIVE_VENDOR_STANDARD_SUBSCRIPTION: {
|
|
60
|
+
description: string;
|
|
61
|
+
value: number;
|
|
62
|
+
};
|
|
63
|
+
ACTIVE_VENDOR_BONUS_REWARD: {
|
|
64
|
+
description: string;
|
|
65
|
+
value: number;
|
|
66
|
+
};
|
|
67
|
+
ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS: {
|
|
68
|
+
description: string;
|
|
69
|
+
value: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Create an affiliate reward
|
|
74
|
+
* @param rewardType - The type of reward
|
|
75
|
+
* @param createdAt - The date the reward was received
|
|
76
|
+
* @returns The affiliate reward
|
|
77
|
+
* @example
|
|
78
|
+
* const reward = createAffiliateReward(EnumAffiliateRewardType.NEW_EVENT_REGISTRATION, new Date());
|
|
79
|
+
* console.log(reward);
|
|
80
|
+
*/
|
|
81
|
+
declare function createAffiliateReward(rewardType: EnumAffiliateRewardType, createdAt: Date): AffiliateRewardType;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Loads the affiliate whose `affiliateCode` matches the given promo code.
|
|
85
|
+
*
|
|
86
|
+
* Includes inactive affiliates (`active: false`) so resource referrals work before
|
|
87
|
+
* admin approval. Soft-deleted affiliates (`deletedAt` set) are excluded.
|
|
88
|
+
*
|
|
89
|
+
* @param promoCode - Normalized affiliate promo code to resolve.
|
|
90
|
+
* @returns The matching affiliate document, or `null` when not found.
|
|
91
|
+
*/
|
|
92
|
+
declare function findAffiliateByPromoCode(promoCode: PromoCodeType): Promise<(mongoose.Document<unknown, {}, Omit<_timardex_cluemart_shared.AffiliateType, "_id" | "owner" | "affiliateResources"> & {
|
|
93
|
+
affiliateResources: SchemaAffiliateResourceType[];
|
|
94
|
+
owner: SchemaOwnerType;
|
|
95
|
+
}, {}, {}> & Omit<_timardex_cluemart_shared.AffiliateType, "_id" | "owner" | "affiliateResources"> & {
|
|
96
|
+
affiliateResources: SchemaAffiliateResourceType[];
|
|
97
|
+
owner: SchemaOwnerType;
|
|
98
|
+
} & {
|
|
99
|
+
_id: mongoose.Types.ObjectId;
|
|
100
|
+
} & {
|
|
101
|
+
__v: number;
|
|
102
|
+
}) | null>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalizes, trims, uppercases, and deduplicates resource promo codes.
|
|
106
|
+
*
|
|
107
|
+
* @param promoCodes - Raw promo codes from a create-resource mutation input.
|
|
108
|
+
* @returns Unique normalized codes; empty strings and whitespace-only values are removed.
|
|
109
|
+
*/
|
|
110
|
+
declare function normalizeAffiliatePromoCodes(promoCodes: PromoCodeType[] | null | undefined): PromoCodeType[];
|
|
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
|
+
|
|
9
125
|
/**
|
|
10
126
|
* Connect to MongoDB using Mongoose.
|
|
11
127
|
* Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).
|
|
@@ -18,6 +134,14 @@ declare const connectToDatabase: ({ appName, dbName, dbPassword, dbUser, mongodb
|
|
|
18
134
|
mongodbUri: string;
|
|
19
135
|
}) => Promise<void>;
|
|
20
136
|
|
|
137
|
+
/** Matches partial unique indexes for promo/affiliate codes on active documents. */
|
|
138
|
+
declare const ACTIVE_NOT_DELETED_FILTER: {
|
|
139
|
+
readonly active: true;
|
|
140
|
+
readonly deletedAt: null;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
declare function normalizePromoCode(decoratedPromoCode: string | null | undefined): string | null;
|
|
144
|
+
|
|
21
145
|
/**
|
|
22
146
|
* Create notifications in the database for multiple users
|
|
23
147
|
* This is typically called when sending push notifications
|
|
@@ -73,4 +197,4 @@ declare function didRemoveAnyEventDates(previousDateTime: EventDateSlot[] | unde
|
|
|
73
197
|
*/
|
|
74
198
|
declare function updateRelationDatesToUnavailable(relationDates: SchemaRelationType["relationDates"], eventDateTime: DateTimeWithPriceType[] | undefined): SchemaRelationType["relationDates"];
|
|
75
199
|
|
|
76
|
-
export { connectToDatabase, convertObjectIdsToStrings, didRemoveAnyEventDates, findEventOrImportedMarketById, 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 };
|
package/dist/service/index.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdModel,
|
|
3
|
+
AffiliateModel,
|
|
3
4
|
ChatModel,
|
|
4
5
|
EnumAdStatus,
|
|
6
|
+
EnumAffiliateRewardType,
|
|
5
7
|
EnumChatType,
|
|
6
8
|
EnumEventDateStatus,
|
|
7
9
|
EnumInviteStatus,
|
|
10
|
+
EnumNotificationResourceType,
|
|
11
|
+
EnumNotificationType,
|
|
8
12
|
EnumResourceType,
|
|
9
13
|
EnumUserLicence,
|
|
10
14
|
EventModel,
|
|
@@ -15,33 +19,29 @@ import {
|
|
|
15
19
|
VendorModel,
|
|
16
20
|
dateFormat,
|
|
17
21
|
timeFormat
|
|
18
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-6EDO734D.mjs";
|
|
19
23
|
|
|
20
|
-
// src/service/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
dbName,
|
|
25
|
-
dbPassword,
|
|
26
|
-
dbUser,
|
|
27
|
-
mongodbUri
|
|
28
|
-
}) => {
|
|
29
|
-
try {
|
|
30
|
-
const mongoUri = mongodbUri ? mongodbUri : (
|
|
31
|
-
// Fallback to MongoDB Atlas connection string
|
|
32
|
-
`mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`
|
|
33
|
-
);
|
|
34
|
-
await mongoose.connect(mongoUri);
|
|
35
|
-
const connectionType = mongodbUri ? "Local MongoDB" : "MongoDB Atlas";
|
|
36
|
-
console.log(
|
|
37
|
-
`${connectionType} connected from server/src/service/database.ts`
|
|
38
|
-
);
|
|
39
|
-
} catch (err) {
|
|
40
|
-
console.error("Error connecting to MongoDB:", err);
|
|
41
|
-
throw err;
|
|
42
|
-
}
|
|
24
|
+
// src/service/promoCode/constants.ts
|
|
25
|
+
var ACTIVE_NOT_DELETED_FILTER = {
|
|
26
|
+
active: true,
|
|
27
|
+
deletedAt: null
|
|
43
28
|
};
|
|
44
29
|
|
|
30
|
+
// src/service/affiliate/activeAffiliateCodeExists.ts
|
|
31
|
+
async function activeAffiliateCodeExists(affiliateCode) {
|
|
32
|
+
const existing = await AffiliateModel.exists({
|
|
33
|
+
...ACTIVE_NOT_DELETED_FILTER,
|
|
34
|
+
affiliateCode
|
|
35
|
+
}).exec();
|
|
36
|
+
return existing !== null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/service/promoCode/normalizePromoCode.ts
|
|
40
|
+
function normalizePromoCode(decoratedPromoCode) {
|
|
41
|
+
const normalized = decoratedPromoCode?.trim().toUpperCase();
|
|
42
|
+
return normalized || null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
45
|
// src/service/saveNotificationsInDb.ts
|
|
46
46
|
async function saveNotificationsInDb(payload) {
|
|
47
47
|
const { data, message, title, type, userIds } = payload;
|
|
@@ -175,6 +175,285 @@ async function sendPushNotifications({
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
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
|
+
|
|
178
457
|
// src/service/updateAdStatus.ts
|
|
179
458
|
async function updateAdStatuses() {
|
|
180
459
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -552,10 +831,20 @@ function updateRelationDatesToUnavailable(relationDates, eventDateTime) {
|
|
|
552
831
|
});
|
|
553
832
|
}
|
|
554
833
|
export {
|
|
834
|
+
ACTIVE_NOT_DELETED_FILTER,
|
|
835
|
+
AFFILIATE_REWARDS,
|
|
836
|
+
activeAffiliateCodeExists,
|
|
837
|
+
awardAffiliateVendorSubscriptionRewards,
|
|
555
838
|
connectToDatabase,
|
|
556
839
|
convertObjectIdsToStrings,
|
|
840
|
+
createAffiliateReward,
|
|
557
841
|
didRemoveAnyEventDates,
|
|
842
|
+
findAffiliateByPromoCode,
|
|
558
843
|
findEventOrImportedMarketById,
|
|
844
|
+
getSubscriptionRewardPeriodBounds,
|
|
845
|
+
mapVendorLicenceToSubscriptionRewardType,
|
|
846
|
+
normalizeAffiliatePromoCodes,
|
|
847
|
+
normalizePromoCode,
|
|
559
848
|
saveNotificationsInDb,
|
|
560
849
|
sendPushNotifications,
|
|
561
850
|
updateAdStatuses,
|