@lyxa.ai/marketing 1.0.44 → 1.0.46

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.
Files changed (105) hide show
  1. package/dist/lib/index.d.ts +102 -22
  2. package/dist/lib/index.d.ts.map +1 -1
  3. package/dist/lib/modules/coupon/routers/coupon.router.d.ts +101 -21
  4. package/dist/lib/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  5. package/dist/lib/modules/coupon/routers/coupon.router.js +1 -1
  6. package/dist/lib/modules/coupon/routers/coupon.router.js.map +1 -1
  7. package/dist/lib/modules/coupon/services/coupon.service.d.ts +3 -1
  8. package/dist/lib/modules/coupon/services/coupon.service.d.ts.map +1 -1
  9. package/dist/lib/modules/coupon/services/coupon.service.js +33 -12
  10. package/dist/lib/modules/coupon/services/coupon.service.js.map +1 -1
  11. package/dist/lib/modules/coupon/services/custom-coupon.service.d.ts +208 -3
  12. package/dist/lib/modules/coupon/services/custom-coupon.service.d.ts.map +1 -1
  13. package/dist/lib/modules/coupon/services/global-coupon.service.d.ts.map +1 -1
  14. package/dist/lib/modules/coupon/services/global-coupon.service.js +2 -0
  15. package/dist/lib/modules/coupon/services/global-coupon.service.js.map +1 -1
  16. package/dist/lib/modules/coupon/services/helper.service.d.ts +214 -4
  17. package/dist/lib/modules/coupon/services/helper.service.d.ts.map +1 -1
  18. package/dist/lib/modules/coupon/services/helper.service.js +114 -16
  19. package/dist/lib/modules/coupon/services/helper.service.js.map +1 -1
  20. package/dist/lib/modules/coupon/services/individual-store-coupon.service.d.ts +208 -3
  21. package/dist/lib/modules/coupon/services/individual-store-coupon.service.d.ts.map +1 -1
  22. package/dist/lib/modules/coupon/services/individual-user-coupon.service.d.ts +1 -1
  23. package/dist/lib/modules/coupon/services/individual-user-coupon.service.d.ts.map +1 -1
  24. package/dist/lib/modules/coupon/services/individual-user-coupon.service.js +20 -11
  25. package/dist/lib/modules/coupon/services/individual-user-coupon.service.js.map +1 -1
  26. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -1
  27. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js +2 -5
  28. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js.map +1 -1
  29. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.d.ts +2 -2
  30. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
  31. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js +151 -45
  32. package/dist/lib/modules/coupon/services/patterns/repository/CouponRepository.js.map +1 -1
  33. package/dist/lib/modules/coupon/services/referral-reward-coupon.service.d.ts.map +1 -1
  34. package/dist/lib/modules/coupon/services/referral-reward-coupon.service.js +2 -0
  35. package/dist/lib/modules/coupon/services/referral-reward-coupon.service.js.map +1 -1
  36. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.d.ts +208 -3
  37. package/dist/lib/modules/coupon/services/shops-categories-coupon.service.d.ts.map +1 -1
  38. package/dist/lib/modules/coupon/transformers/coupon.transformer.d.ts +5 -1
  39. package/dist/lib/modules/coupon/transformers/coupon.transformer.d.ts.map +1 -1
  40. package/dist/lib/modules/coupon/validations/all-coupons.validation.d.ts +243 -23
  41. package/dist/lib/modules/coupon/validations/all-coupons.validation.d.ts.map +1 -1
  42. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts +69 -17
  43. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  44. package/dist/lib/modules/coupon/validations/coupon.validation.js +24 -3
  45. package/dist/lib/modules/coupon/validations/coupon.validation.js.map +1 -1
  46. package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts +94 -6
  47. package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -1
  48. package/dist/lib/modules/coupon/validations/custom-coupon.validation.d.ts +9 -0
  49. package/dist/lib/modules/coupon/validations/custom-coupon.validation.d.ts.map +1 -1
  50. package/dist/lib/modules/coupon/validations/custom-coupon.validation.js +1 -0
  51. package/dist/lib/modules/coupon/validations/custom-coupon.validation.js.map +1 -1
  52. package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.d.ts +200 -122
  53. package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.d.ts.map +1 -1
  54. package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.js +20 -3
  55. package/dist/lib/modules/coupon/validations/individual-store-coupon.validation.js.map +1 -1
  56. package/dist/lib/modules/coupon/validations/individual-user-coupon.validation.d.ts +9 -0
  57. package/dist/lib/modules/coupon/validations/individual-user-coupon.validation.d.ts.map +1 -1
  58. package/dist/lib/modules/coupon/validations/individual-user-coupon.validation.js +1 -0
  59. package/dist/lib/modules/coupon/validations/individual-user-coupon.validation.js.map +1 -1
  60. package/dist/lib/modules/coupon/validations/shops-categories-coupon.validation.d.ts +9 -0
  61. package/dist/lib/modules/coupon/validations/shops-categories-coupon.validation.d.ts.map +1 -1
  62. package/dist/lib/modules/coupon/validations/shops-categories-coupon.validation.js +1 -0
  63. package/dist/lib/modules/coupon/validations/shops-categories-coupon.validation.js.map +1 -1
  64. package/dist/lib/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +1 -1
  65. package/dist/lib/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
  66. package/dist/types/index.d.ts +102 -22
  67. package/dist/types/index.d.ts.map +1 -1
  68. package/dist/types/modules/coupon/routers/coupon.router.d.ts +101 -21
  69. package/dist/types/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  70. package/dist/types/modules/coupon/services/coupon.service.d.ts +3 -1
  71. package/dist/types/modules/coupon/services/coupon.service.d.ts.map +1 -1
  72. package/dist/types/modules/coupon/services/custom-coupon.service.d.ts +208 -3
  73. package/dist/types/modules/coupon/services/custom-coupon.service.d.ts.map +1 -1
  74. package/dist/types/modules/coupon/services/global-coupon.service.d.ts.map +1 -1
  75. package/dist/types/modules/coupon/services/helper.service.d.ts +214 -4
  76. package/dist/types/modules/coupon/services/helper.service.d.ts.map +1 -1
  77. package/dist/types/modules/coupon/services/individual-store-coupon.service.d.ts +208 -3
  78. package/dist/types/modules/coupon/services/individual-store-coupon.service.d.ts.map +1 -1
  79. package/dist/types/modules/coupon/services/individual-user-coupon.service.d.ts +1 -1
  80. package/dist/types/modules/coupon/services/individual-user-coupon.service.d.ts.map +1 -1
  81. package/dist/types/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -1
  82. package/dist/types/modules/coupon/services/patterns/repository/CouponRepository.d.ts +2 -2
  83. package/dist/types/modules/coupon/services/patterns/repository/CouponRepository.d.ts.map +1 -1
  84. package/dist/types/modules/coupon/services/referral-reward-coupon.service.d.ts.map +1 -1
  85. package/dist/types/modules/coupon/services/shops-categories-coupon.service.d.ts +208 -3
  86. package/dist/types/modules/coupon/services/shops-categories-coupon.service.d.ts.map +1 -1
  87. package/dist/types/modules/coupon/transformers/coupon.transformer.d.ts +5 -1
  88. package/dist/types/modules/coupon/transformers/coupon.transformer.d.ts.map +1 -1
  89. package/dist/types/modules/coupon/validations/all-coupons.validation.d.ts +243 -23
  90. package/dist/types/modules/coupon/validations/all-coupons.validation.d.ts.map +1 -1
  91. package/dist/types/modules/coupon/validations/coupon.validation.d.ts +69 -17
  92. package/dist/types/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  93. package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts +94 -6
  94. package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -1
  95. package/dist/types/modules/coupon/validations/custom-coupon.validation.d.ts +9 -0
  96. package/dist/types/modules/coupon/validations/custom-coupon.validation.d.ts.map +1 -1
  97. package/dist/types/modules/coupon/validations/individual-store-coupon.validation.d.ts +200 -122
  98. package/dist/types/modules/coupon/validations/individual-store-coupon.validation.d.ts.map +1 -1
  99. package/dist/types/modules/coupon/validations/individual-user-coupon.validation.d.ts +9 -0
  100. package/dist/types/modules/coupon/validations/individual-user-coupon.validation.d.ts.map +1 -1
  101. package/dist/types/modules/coupon/validations/shops-categories-coupon.validation.d.ts +9 -0
  102. package/dist/types/modules/coupon/validations/shops-categories-coupon.validation.d.ts.map +1 -1
  103. package/dist/types/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +1 -1
  104. package/dist/types/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
  105. package/package.json +2 -2
@@ -56,12 +56,16 @@ const shared_1 = require("@lyxa.ai/core/dist/utilities/shared");
56
56
  let CouponHelperService = class CouponHelperService {
57
57
  userModel;
58
58
  orderModel;
59
+ serviceBookingModel;
59
60
  shopModel;
61
+ vendorModel;
60
62
  _coreUserModel = null;
61
63
  constructor() {
62
64
  this.userModel = models_1.UserModel;
63
65
  this.orderModel = models_1.RegularOrderModel;
66
+ this.serviceBookingModel = models_1.ServiceBookingModel;
64
67
  this.shopModel = models_1.ShopModel;
68
+ this.vendorModel = models_1.VendorModel;
65
69
  }
66
70
  get coreUserModel() {
67
71
  if (!this._coreUserModel) {
@@ -82,11 +86,17 @@ let CouponHelperService = class CouponHelperService {
82
86
  users.push(u._id.toString());
83
87
  }
84
88
  });
85
- const orderCount = await this.orderModel.countDocuments({
86
- user: { $in: users },
87
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
88
- });
89
- return orderCount < 1;
89
+ const [orderCount, bookingCount] = await Promise.all([
90
+ this.orderModel.countDocuments({
91
+ user: { $in: users },
92
+ status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
93
+ }),
94
+ this.serviceBookingModel.countDocuments({
95
+ user: { $in: users },
96
+ status: { $ne: enum_1.ServiceBookingStatus.CANCELLED },
97
+ }),
98
+ ]);
99
+ return orderCount + bookingCount < 1;
90
100
  }
91
101
  async getSameDeviceOrdersWithCoupon(user, coupon) {
92
102
  let users = [user._id.toString()];
@@ -101,12 +111,19 @@ let CouponHelperService = class CouponHelperService {
101
111
  users.push(u._id.toString());
102
112
  }
103
113
  });
104
- const orderCount = await this.orderModel.countDocuments({
105
- user: { $in: users },
106
- status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
107
- 'adjustedFinance.coupon.coupon': coupon._id,
108
- });
109
- return orderCount;
114
+ const [orderCount, bookingCount] = await Promise.all([
115
+ this.orderModel.countDocuments({
116
+ user: { $in: users },
117
+ status: { $ne: enum_1.RegularOrderStatus.CANCELLED },
118
+ 'adjustedFinance.coupon.coupon': coupon._id,
119
+ }),
120
+ this.serviceBookingModel.countDocuments({
121
+ user: { $in: users },
122
+ status: { $ne: enum_1.ServiceBookingStatus.CANCELLED },
123
+ 'adjustedFinance.coupon.coupon': coupon._id,
124
+ }),
125
+ ]);
126
+ return orderCount + bookingCount;
110
127
  }
111
128
  async getNewUsers() {
112
129
  const pipeline = [
@@ -199,17 +216,24 @@ let CouponHelperService = class CouponHelperService {
199
216
  discountCut: {
200
217
  companyCut: isShopCoverExpenses ? 0 : discountAmount,
201
218
  secondaryCompanyCut: isShopCoverExpenses ? 0 : secondaryDiscountAmount,
202
- shopCut: isShopCoverExpenses ? discountAmount : 0,
203
- secondaryShopCut: isShopCoverExpenses ? secondaryDiscountAmount : 0,
219
+ vendorCut: isShopCoverExpenses && data.vendor ? discountAmount : 0,
220
+ secondaryVendorCut: isShopCoverExpenses && data.vendor ? secondaryDiscountAmount : 0,
221
+ shopCut: isShopCoverExpenses && !data.vendor ? discountAmount : 0,
222
+ secondaryShopCut: isShopCoverExpenses && !data.vendor ? secondaryDiscountAmount : 0,
204
223
  },
205
224
  couponDetails,
206
225
  };
207
226
  }
208
227
  async validateShopEligibility({ data, coupon }) {
209
- const shop = await this.shopModel.findOne({
228
+ if (data.vendor) {
229
+ return this.validateVendorEligibility({ data, coupon });
230
+ }
231
+ const shop = await this.shopModel
232
+ .findOne({
210
233
  _id: new typegoose_1.mongoose.Types.ObjectId(data.shop),
211
234
  deletedAt: null,
212
- });
235
+ })
236
+ .lean();
213
237
  if (!shop)
214
238
  return { isValid: false, message: 'Shop not found' };
215
239
  const singleShopError = await this.validateSingleShopRestriction(shop, coupon);
@@ -223,6 +247,25 @@ let CouponHelperService = class CouponHelperService {
223
247
  return firstOrderError;
224
248
  return { isValid: true, shop };
225
249
  }
250
+ async validateVendorEligibility({ data, coupon }) {
251
+ const vendor = await this.vendorModel
252
+ .findOne({
253
+ _id: new typegoose_1.mongoose.Types.ObjectId(data.vendor),
254
+ })
255
+ .lean();
256
+ if (!vendor)
257
+ return { isValid: false, message: 'Vendor not found' };
258
+ const singleVendorError = await this.validateSingleVendorRestriction(vendor, coupon);
259
+ if (singleVendorError)
260
+ return singleVendorError;
261
+ const restrictionError = this.validateVendorMultiRestrictions(vendor, coupon);
262
+ if (restrictionError)
263
+ return restrictionError;
264
+ const firstOrderError = await this.validateVendorFirstOrderOnly(data, vendor, coupon);
265
+ if (firstOrderError)
266
+ return firstOrderError;
267
+ return { isValid: true, vendor };
268
+ }
226
269
  async validateSingleShopRestriction(shop, coupon) {
227
270
  if (!coupon.shop)
228
271
  return null;
@@ -230,7 +273,10 @@ let CouponHelperService = class CouponHelperService {
230
273
  const currentShopId = shop._id.toString();
231
274
  if (couponShopId === currentShopId)
232
275
  return null;
233
- const validShop = await this.shopModel.findOne({ _id: coupon.shop, deletedAt: null }).select('name');
276
+ const validShop = await this.shopModel
277
+ .findOne({ _id: coupon.shop, deletedAt: null })
278
+ .select('name')
279
+ .lean();
234
280
  if (!validShop) {
235
281
  return {
236
282
  isValid: false,
@@ -259,6 +305,42 @@ let CouponHelperService = class CouponHelperService {
259
305
  }
260
306
  return null;
261
307
  }
308
+ async validateSingleVendorRestriction(vendor, coupon) {
309
+ if (!coupon.vendor)
310
+ return null;
311
+ const couponVendorId = coupon.vendor.toString();
312
+ const currentVendorId = vendor._id.toString();
313
+ if (couponVendorId === currentVendorId)
314
+ return null;
315
+ const validVendor = await this.vendorModel.findOne({ _id: coupon.vendor }).select('name').lean();
316
+ if (!validVendor) {
317
+ return {
318
+ isValid: false,
319
+ message: 'Coupon is associated with an invalid vendor',
320
+ };
321
+ }
322
+ return {
323
+ isValid: false,
324
+ message: `This coupon can be used at ${validVendor.name} only`,
325
+ };
326
+ }
327
+ validateVendorMultiRestrictions(vendor, coupon) {
328
+ const hasVendorRestriction = Array.isArray(coupon.vendors) && coupon.vendors.length > 0;
329
+ const hasItemTypeRestriction = Array.isArray(coupon.itemTypes) && coupon.itemTypes.length > 0;
330
+ if (!hasVendorRestriction && !hasItemTypeRestriction)
331
+ return null;
332
+ const vendorMatches = hasVendorRestriction &&
333
+ coupon.vendors.some((couponVendor) => couponVendor._id.toString() === vendor._id.toString());
334
+ const itemTypeMatches = hasItemTypeRestriction && coupon.itemTypes.includes(vendor.serviceType);
335
+ if (!vendorMatches && !itemTypeMatches) {
336
+ const vendorNames = coupon.vendors.map((vendor) => vendor.name).join(', ');
337
+ return {
338
+ isValid: false,
339
+ message: `This coupon can be used at ${vendorNames}`,
340
+ };
341
+ }
342
+ return null;
343
+ }
262
344
  async validateFirstOrderOnly(data, shop, coupon) {
263
345
  if (!coupon.firstOrderOnly)
264
346
  return null;
@@ -275,6 +357,22 @@ let CouponHelperService = class CouponHelperService {
275
357
  }
276
358
  return null;
277
359
  }
360
+ async validateVendorFirstOrderOnly(data, vendor, coupon) {
361
+ if (!coupon.firstOrderOnly)
362
+ return null;
363
+ const existingBooking = await this.serviceBookingModel.exists({
364
+ user: new typegoose_1.mongoose.Types.ObjectId(data.user),
365
+ vendor: vendor._id,
366
+ status: { $ne: enum_1.ServiceBookingStatus.CANCELLED },
367
+ });
368
+ if (existingBooking) {
369
+ return {
370
+ isValid: false,
371
+ message: 'This coupon is only valid for the first order in the vendor',
372
+ };
373
+ }
374
+ return null;
375
+ }
278
376
  async fetchUserName(userId) {
279
377
  const user = await this.userModel.findById(userId).select('coreUser').lean();
280
378
  if (!user)
@@ -1 +1 @@
1
- {"version":3,"file":"helper.service.js","sourceRoot":"/","sources":["modules/coupon/services/helper.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport {\n\tBaseCoupon,\n\tgetCoreUserModel,\n\tRegularOrder,\n\tRegularOrderModel,\n\tShop,\n\tShopModel,\n\tUser,\n\tUserModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { RegularOrderStatus, ValueType } from '@lyxa.ai/core/dist/utilities/enum';\nimport dayjs, { Dayjs } from '@lyxa.ai/core/dist/utilities/dayjs/dayjs';\nimport { CouponValidationOutputDTO, ValidateCouponDTO } from '../validations';\nimport { convertToSecondary } from '@lyxa.ai/core/dist/utilities/currency';\nimport { roundBaseCurrency, roundSecondaryCurrency } from '@lyxa.ai/core/dist/utilities/shared';\n\n@Service()\nexport class CouponHelperService {\n\tprivate userModel: typeof UserModel & SoftDeleteModel<User>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate _coreUserModel: ReturnType<typeof getCoreUserModel> | null = null;\n\n\tconstructor() {\n\t\tthis.userModel = UserModel as typeof UserModel & SoftDeleteModel<User>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t}\n\n\tprivate get coreUserModel() {\n\t\tif (!this._coreUserModel) {\n\t\t\tthis._coreUserModel = getCoreUserModel();\n\t\t}\n\t\treturn this._coreUserModel;\n\t}\n\n\tpublic async isNewUser(user: mongoose.Types.ObjectId): Promise<boolean> {\n\t\tlet users = [user._id.toString()];\n\n\t\tconst existingUser = await this.userModel.findOne({\n\t\t\t_id: user,\n\t\t});\n\n\t\tconst sameDeviceUsers = await this.userModel.findWithDeleted({\n\t\t\tdeviceId: existingUser?.deviceId,\n\t\t});\n\n\t\tsameDeviceUsers.forEach((u: any) => {\n\t\t\tif (!users.includes(u._id.toString())) {\n\t\t\t\tusers.push(u._id.toString());\n\t\t\t}\n\t\t});\n\n\t\tconst orderCount = await this.orderModel.countDocuments({\n\t\t\tuser: { $in: users },\n\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t});\n\n\t\treturn orderCount < 1;\n\t}\n\n\tpublic async getSameDeviceOrdersWithCoupon(\n\t\tuser: mongoose.Types.ObjectId,\n\t\tcoupon: DocumentType<BaseCoupon>\n\t): Promise<number> {\n\t\tlet users = [user._id.toString()];\n\n\t\tconst existingUser = await this.userModel.findOne({\n\t\t\t_id: user,\n\t\t});\n\n\t\tconst sameDeviceUsers = await this.userModel.findWithDeleted({\n\t\t\tdeviceId: existingUser?.deviceId,\n\t\t});\n\n\t\tsameDeviceUsers.forEach((u: any) => {\n\t\t\tif (!users.includes(u._id.toString())) {\n\t\t\t\tusers.push(u._id.toString());\n\t\t\t}\n\t\t});\n\n\t\tconst orderCount = await this.orderModel.countDocuments({\n\t\t\tuser: { $in: users },\n\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t'adjustedFinance.coupon.coupon': coupon._id,\n\t\t});\n\n\t\treturn orderCount;\n\t}\n\n\tpublic async getNewUsers(): Promise<mongoose.Types.ObjectId[]> {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t// Step 1: Lookup all users who share the same deviceId\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'users',\n\t\t\t\t\tlocalField: 'deviceId',\n\t\t\t\t\tforeignField: 'deviceId',\n\t\t\t\t\tas: 'sameDeviceUsers',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 2: Collect the list of userIds belonging to this device\n\t\t\t{\n\t\t\t\t$set: {\n\t\t\t\t\tallUserIds: {\n\t\t\t\t\t\t$map: {\n\t\t\t\t\t\t\tinput: '$sameDeviceUsers',\n\t\t\t\t\t\t\tas: 'u',\n\t\t\t\t\t\t\tin: '$$u._id',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 3: Lookup orders attached to any of these users\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'orders',\n\t\t\t\t\tlet: { ids: '$allUserIds' },\n\t\t\t\t\tpipeline: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$match: {\n\t\t\t\t\t\t\t\t$expr: {\n\t\t\t\t\t\t\t\t\t$and: [{ $in: ['$user', '$$ids'] }, { $ne: ['$status', 'CANCELLED'] }],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tas: 'orders',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 4: Count orders\n\t\t\t{\n\t\t\t\t$set: {\n\t\t\t\t\torderCount: { $size: '$orders' },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 5: New user = zero or one non-cancelled order\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\torderCount: { $lte: 1 },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Optional: Remove temporary fields\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\tsameDeviceUsers: 0,\n\t\t\t\t\tallUserIds: 0,\n\t\t\t\t\torders: 0,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst newUsers = await this.userModel.aggregate(pipeline).exec();\n\n\t\treturn newUsers.map(u => u._id);\n\t}\n\n\tpublic isDurationValid(start: Dayjs, end: Dayjs): boolean {\n\t\tconst getRoundedHourBack = (hoursBack = 0) => {\n\t\t\treturn dayjs().subtract(hoursBack, 'hour').startOf('hour');\n\t\t};\n\n\t\tconst cutoffTime = getRoundedHourBack();\n\n\t\treturn start >= cutoffTime && end >= cutoffTime;\n\t}\n\n\tpublic async calculateCouponDiscount(data: ValidateCouponDTO, coupon: DocumentType<BaseCoupon>) {\n\t\tlet discountAmount =\n\t\t\tcoupon.valueType === ValueType.FIXED\n\t\t\t\t? coupon.value\n\t\t\t\t: roundBaseCurrency((data.amountBeforeCoupon * coupon.value) / 100);\n\n\t\tlet secondaryDiscountAmount =\n\t\t\tcoupon.valueType === ValueType.FIXED\n\t\t\t\t? await convertToSecondary(coupon.value)\n\t\t\t\t: roundSecondaryCurrency((data.secondaryAmountBeforeCoupon * coupon.value) / 100);\n\n\t\tif (coupon.isMaxDiscountLimitEnabled && coupon.maxDiscountLimit) {\n\t\t\tconst secondaryMax = await convertToSecondary(coupon.maxDiscountLimit);\n\n\t\t\tdiscountAmount = Math.min(discountAmount, coupon.maxDiscountLimit);\n\t\t\tsecondaryDiscountAmount = Math.min(secondaryDiscountAmount, secondaryMax);\n\t\t}\n\n\t\t// Prevent negative totals\n\t\tdiscountAmount = Math.min(discountAmount, data.amountBeforeCoupon);\n\t\tsecondaryDiscountAmount = Math.min(secondaryDiscountAmount, data.secondaryAmountBeforeCoupon);\n\n\t\treturn { discountAmount, secondaryDiscountAmount };\n\t}\n\n\tpublic buildCouponResponse(\n\t\tdata: ValidateCouponDTO,\n\t\tdiscountAmount: number,\n\t\tsecondaryDiscountAmount: number,\n\t\tisShopCoverExpenses: boolean,\n\t\tcouponDetails?: Record<string, any>\n\t): CouponValidationOutputDTO {\n\t\treturn {\n\t\t\tisValid: true,\n\t\t\tdiscountAmount,\n\t\t\tsecondaryDiscountAmount,\n\t\t\tamountAfterCoupon: roundBaseCurrency(data.amountBeforeCoupon - discountAmount),\n\t\t\tsecondaryAmountAfterCoupon: roundSecondaryCurrency(\n\t\t\t\tdata.secondaryAmountBeforeCoupon - secondaryDiscountAmount\n\t\t\t),\n\t\t\tdiscountCut: {\n\t\t\t\tcompanyCut: isShopCoverExpenses ? 0 : discountAmount,\n\t\t\t\tsecondaryCompanyCut: isShopCoverExpenses ? 0 : secondaryDiscountAmount,\n\t\t\t\tshopCut: isShopCoverExpenses ? discountAmount : 0,\n\t\t\t\tsecondaryShopCut: isShopCoverExpenses ? secondaryDiscountAmount : 0,\n\t\t\t},\n\t\t\tcouponDetails,\n\t\t};\n\t}\n\n\tpublic async validateShopEligibility({ data, coupon }: { data: ValidateCouponDTO; coupon: any }) {\n\t\tconst shop = await this.shopModel.findOne({\n\t\t\t_id: new mongoose.Types.ObjectId(data.shop),\n\t\t\tdeletedAt: null,\n\t\t});\n\n\t\tif (!shop) return { isValid: false, message: 'Shop not found' };\n\n\t\tconst singleShopError = await this.validateSingleShopRestriction(shop, coupon);\n\t\tif (singleShopError) return singleShopError;\n\n\t\tconst restrictionError = this.validateMultiRestrictions(shop, coupon);\n\t\tif (restrictionError) return restrictionError;\n\n\t\tconst firstOrderError = await this.validateFirstOrderOnly(data, shop, coupon);\n\t\tif (firstOrderError) return firstOrderError;\n\n\t\treturn { isValid: true, shop };\n\t}\n\n\tprivate async validateSingleShopRestriction(shop: any, coupon: any) {\n\t\tif (!coupon.shop) return null;\n\n\t\tconst couponShopId = coupon.shop.toString();\n\t\tconst currentShopId = shop._id.toString();\n\n\t\tif (couponShopId === currentShopId) return null;\n\n\t\tconst validShop = await this.shopModel.findOne({ _id: coupon.shop, deletedAt: null }).select('name');\n\n\t\tif (!validShop) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'Coupon is associated with an invalid shop',\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\tmessage: `This coupon can be used at ${validShop.name} only`,\n\t\t};\n\t}\n\n\tprivate validateMultiRestrictions(shop: any, coupon: any) {\n\t\tconst hasShopRestriction = Array.isArray(coupon.shops) && coupon.shops.length > 0;\n\t\tconst hasItemTypeRestriction = Array.isArray(coupon.itemTypes) && coupon.itemTypes.length > 0;\n\n\t\tif (!hasShopRestriction && !hasItemTypeRestriction) return null;\n\n\t\tconst shopMatches =\n\t\t\thasShopRestriction &&\n\t\t\tcoupon.shops.some((couponShop: any) => couponShop._id.toString() === shop._id.toString());\n\n\t\tconst itemTypeMatches = hasItemTypeRestriction && coupon.itemTypes.includes(shop.itemType);\n\n\t\tif (!shopMatches && !itemTypeMatches) {\n\t\t\tconst shopNames = coupon.shops.map((shop: any) => shop.name).join(', ');\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: `This coupon can be used at ${shopNames}`,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async validateFirstOrderOnly(data: ValidateCouponDTO, shop: any, coupon: any) {\n\t\tif (!coupon.firstOrderOnly) return null;\n\n\t\tconst existingOrder = await this.orderModel.exists({\n\t\t\tuser: new mongoose.Types.ObjectId(data.user),\n\t\t\tshop: shop._id,\n\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t});\n\n\t\tif (existingOrder) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'This coupon is only valid for the first order in the shop',\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic async fetchUserName(userId: mongoose.Types.ObjectId): Promise<string> {\n\t\tconst user = await this.userModel.findById(userId).select('coreUser').lean();\n\n\t\tif (!user) return 'Unknown User';\n\n\t\tconst coreUser = await this.coreUserModel.findById(user.coreUser).select('name').lean();\n\n\t\treturn coreUser ? coreUser.name : 'Unknown User';\n\t}\n}\n\nexport const couponHelperService = Container.get(CouponHelperService);\n"]}
1
+ {"version":3,"file":"helper.service.js","sourceRoot":"/","sources":["modules/coupon/services/helper.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport {\n\tBaseCoupon,\n\tgetCoreUserModel,\n\tRegularOrder,\n\tRegularOrderModel,\n\tServiceBooking,\n\tServiceBookingModel,\n\tShop,\n\tShopModel,\n\tUser,\n\tUserModel,\n\tVendor,\n\tVendorModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { RegularOrderStatus, ServiceBookingStatus, ValueType } from '@lyxa.ai/core/dist/utilities/enum';\nimport dayjs, { Dayjs } from '@lyxa.ai/core/dist/utilities/dayjs/dayjs';\nimport { CouponValidationOutputDTO, ValidateCouponDTO } from '../validations';\nimport { convertToSecondary } from '@lyxa.ai/core/dist/utilities/currency';\nimport { roundBaseCurrency, roundSecondaryCurrency } from '@lyxa.ai/core/dist/utilities/shared';\n\n@Service()\nexport class CouponHelperService {\n\tprivate userModel: typeof UserModel & SoftDeleteModel<User>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\tprivate serviceBookingModel: typeof ServiceBookingModel;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate vendorModel: typeof VendorModel & SoftDeleteModel<Vendor>;\n\tprivate _coreUserModel: ReturnType<typeof getCoreUserModel> | null = null;\n\n\tconstructor() {\n\t\tthis.userModel = UserModel as typeof UserModel & SoftDeleteModel<User>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t\tthis.serviceBookingModel = ServiceBookingModel as typeof ServiceBookingModel &\n\t\t\tSoftDeleteModel<ServiceBooking>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t\tthis.vendorModel = VendorModel as typeof VendorModel & SoftDeleteModel<Vendor>;\n\t}\n\n\tprivate get coreUserModel() {\n\t\tif (!this._coreUserModel) {\n\t\t\tthis._coreUserModel = getCoreUserModel();\n\t\t}\n\t\treturn this._coreUserModel;\n\t}\n\n\tpublic async isNewUser(user: mongoose.Types.ObjectId): Promise<boolean> {\n\t\tlet users = [user._id.toString()];\n\n\t\tconst existingUser = await this.userModel.findOne({\n\t\t\t_id: user,\n\t\t});\n\n\t\tconst sameDeviceUsers = await this.userModel.findWithDeleted({\n\t\t\tdeviceId: existingUser?.deviceId,\n\t\t});\n\n\t\tsameDeviceUsers.forEach((u: any) => {\n\t\t\tif (!users.includes(u._id.toString())) {\n\t\t\t\tusers.push(u._id.toString());\n\t\t\t}\n\t\t});\n\n\t\tconst [orderCount, bookingCount] = await Promise.all([\n\t\t\tthis.orderModel.countDocuments({\n\t\t\t\tuser: { $in: users },\n\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t}),\n\t\t\tthis.serviceBookingModel.countDocuments({\n\t\t\t\tuser: { $in: users },\n\t\t\t\tstatus: { $ne: ServiceBookingStatus.CANCELLED },\n\t\t\t}),\n\t\t]);\n\n\t\treturn orderCount + bookingCount < 1;\n\t}\n\n\tpublic async getSameDeviceOrdersWithCoupon(\n\t\tuser: mongoose.Types.ObjectId,\n\t\tcoupon: DocumentType<BaseCoupon>\n\t): Promise<number> {\n\t\tlet users = [user._id.toString()];\n\n\t\tconst existingUser = await this.userModel.findOne({\n\t\t\t_id: user,\n\t\t});\n\n\t\tconst sameDeviceUsers = await this.userModel.findWithDeleted({\n\t\t\tdeviceId: existingUser?.deviceId,\n\t\t});\n\n\t\tsameDeviceUsers.forEach((u: any) => {\n\t\t\tif (!users.includes(u._id.toString())) {\n\t\t\t\tusers.push(u._id.toString());\n\t\t\t}\n\t\t});\n\n\t\tconst [orderCount, bookingCount] = await Promise.all([\n\t\t\tthis.orderModel.countDocuments({\n\t\t\t\tuser: { $in: users },\n\t\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t\t\t'adjustedFinance.coupon.coupon': coupon._id,\n\t\t\t}),\n\t\t\tthis.serviceBookingModel.countDocuments({\n\t\t\t\tuser: { $in: users },\n\t\t\t\tstatus: { $ne: ServiceBookingStatus.CANCELLED },\n\t\t\t\t'adjustedFinance.coupon.coupon': coupon._id,\n\t\t\t}),\n\t\t]);\n\n\t\treturn orderCount + bookingCount;\n\t}\n\n\tpublic async getNewUsers(): Promise<mongoose.Types.ObjectId[]> {\n\t\tconst pipeline: mongoose.PipelineStage[] = [\n\t\t\t// Step 1: Lookup all users who share the same deviceId\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'users',\n\t\t\t\t\tlocalField: 'deviceId',\n\t\t\t\t\tforeignField: 'deviceId',\n\t\t\t\t\tas: 'sameDeviceUsers',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 2: Collect the list of userIds belonging to this device\n\t\t\t{\n\t\t\t\t$set: {\n\t\t\t\t\tallUserIds: {\n\t\t\t\t\t\t$map: {\n\t\t\t\t\t\t\tinput: '$sameDeviceUsers',\n\t\t\t\t\t\t\tas: 'u',\n\t\t\t\t\t\t\tin: '$$u._id',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 3: Lookup orders attached to any of these users\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'orders',\n\t\t\t\t\tlet: { ids: '$allUserIds' },\n\t\t\t\t\tpipeline: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$match: {\n\t\t\t\t\t\t\t\t$expr: {\n\t\t\t\t\t\t\t\t\t$and: [{ $in: ['$user', '$$ids'] }, { $ne: ['$status', 'CANCELLED'] }],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tas: 'orders',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 4: Count orders\n\t\t\t{\n\t\t\t\t$set: {\n\t\t\t\t\torderCount: { $size: '$orders' },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Step 5: New user = zero or one non-cancelled order\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\torderCount: { $lte: 1 },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Optional: Remove temporary fields\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\tsameDeviceUsers: 0,\n\t\t\t\t\tallUserIds: 0,\n\t\t\t\t\torders: 0,\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\n\t\tconst newUsers = await this.userModel.aggregate(pipeline).exec();\n\n\t\treturn newUsers.map(u => u._id);\n\t}\n\n\tpublic isDurationValid(start: Dayjs, end: Dayjs): boolean {\n\t\tconst getRoundedHourBack = (hoursBack = 0) => {\n\t\t\treturn dayjs().subtract(hoursBack, 'hour').startOf('hour');\n\t\t};\n\n\t\tconst cutoffTime = getRoundedHourBack();\n\n\t\treturn start >= cutoffTime && end >= cutoffTime;\n\t}\n\n\tpublic async calculateCouponDiscount(data: ValidateCouponDTO, coupon: DocumentType<BaseCoupon>) {\n\t\tlet discountAmount =\n\t\t\tcoupon.valueType === ValueType.FIXED\n\t\t\t\t? coupon.value\n\t\t\t\t: roundBaseCurrency((data.amountBeforeCoupon * coupon.value) / 100);\n\n\t\tlet secondaryDiscountAmount =\n\t\t\tcoupon.valueType === ValueType.FIXED\n\t\t\t\t? await convertToSecondary(coupon.value)\n\t\t\t\t: roundSecondaryCurrency((data.secondaryAmountBeforeCoupon * coupon.value) / 100);\n\n\t\tif (coupon.isMaxDiscountLimitEnabled && coupon.maxDiscountLimit) {\n\t\t\tconst secondaryMax = await convertToSecondary(coupon.maxDiscountLimit);\n\n\t\t\tdiscountAmount = Math.min(discountAmount, coupon.maxDiscountLimit);\n\t\t\tsecondaryDiscountAmount = Math.min(secondaryDiscountAmount, secondaryMax);\n\t\t}\n\n\t\t// Prevent negative totals\n\t\tdiscountAmount = Math.min(discountAmount, data.amountBeforeCoupon);\n\t\tsecondaryDiscountAmount = Math.min(secondaryDiscountAmount, data.secondaryAmountBeforeCoupon);\n\n\t\treturn { discountAmount, secondaryDiscountAmount };\n\t}\n\n\tpublic buildCouponResponse(\n\t\tdata: ValidateCouponDTO,\n\t\tdiscountAmount: number,\n\t\tsecondaryDiscountAmount: number,\n\t\tisShopCoverExpenses: boolean,\n\t\tcouponDetails?: Record<string, any>\n\t): CouponValidationOutputDTO {\n\t\treturn {\n\t\t\tisValid: true,\n\t\t\tdiscountAmount,\n\t\t\tsecondaryDiscountAmount,\n\t\t\tamountAfterCoupon: roundBaseCurrency(data.amountBeforeCoupon - discountAmount),\n\t\t\tsecondaryAmountAfterCoupon: roundSecondaryCurrency(\n\t\t\t\tdata.secondaryAmountBeforeCoupon - secondaryDiscountAmount\n\t\t\t),\n\t\t\tdiscountCut: {\n\t\t\t\tcompanyCut: isShopCoverExpenses ? 0 : discountAmount,\n\t\t\t\tsecondaryCompanyCut: isShopCoverExpenses ? 0 : secondaryDiscountAmount,\n\t\t\t\tvendorCut: isShopCoverExpenses && data.vendor ? discountAmount : 0,\n\t\t\t\tsecondaryVendorCut: isShopCoverExpenses && data.vendor ? secondaryDiscountAmount : 0,\n\t\t\t\tshopCut: isShopCoverExpenses && !data.vendor ? discountAmount : 0,\n\t\t\t\tsecondaryShopCut: isShopCoverExpenses && !data.vendor ? secondaryDiscountAmount : 0,\n\t\t\t},\n\t\t\tcouponDetails,\n\t\t};\n\t}\n\n\tpublic async validateShopEligibility({ data, coupon }: { data: ValidateCouponDTO; coupon: any }) {\n\t\tif (data.vendor) {\n\t\t\treturn this.validateVendorEligibility({ data, coupon });\n\t\t}\n\n\t\tconst shop = await this.shopModel\n\t\t\t.findOne({\n\t\t\t\t_id: new mongoose.Types.ObjectId(data.shop),\n\t\t\t\tdeletedAt: null,\n\t\t\t})\n\t\t\t.lean();\n\n\t\tif (!shop) return { isValid: false, message: 'Shop not found' };\n\n\t\tconst singleShopError = await this.validateSingleShopRestriction(shop, coupon);\n\t\tif (singleShopError) return singleShopError;\n\n\t\tconst restrictionError = this.validateMultiRestrictions(shop, coupon);\n\t\tif (restrictionError) return restrictionError;\n\n\t\tconst firstOrderError = await this.validateFirstOrderOnly(data, shop, coupon);\n\t\tif (firstOrderError) return firstOrderError;\n\n\t\treturn { isValid: true, shop };\n\t}\n\n\tprivate async validateVendorEligibility({ data, coupon }: { data: ValidateCouponDTO; coupon: any }) {\n\t\tconst vendor = await this.vendorModel\n\t\t\t.findOne({\n\t\t\t\t_id: new mongoose.Types.ObjectId(data.vendor),\n\t\t\t})\n\t\t\t.lean();\n\n\t\tif (!vendor) return { isValid: false, message: 'Vendor not found' };\n\n\t\tconst singleVendorError = await this.validateSingleVendorRestriction(vendor, coupon);\n\t\tif (singleVendorError) return singleVendorError;\n\n\t\tconst restrictionError = this.validateVendorMultiRestrictions(vendor, coupon);\n\t\tif (restrictionError) return restrictionError;\n\n\t\tconst firstOrderError = await this.validateVendorFirstOrderOnly(data, vendor, coupon);\n\t\tif (firstOrderError) return firstOrderError;\n\n\t\treturn { isValid: true, vendor };\n\t}\n\n\tprivate async validateSingleShopRestriction(shop: any, coupon: any) {\n\t\tif (!coupon.shop) return null;\n\n\t\tconst couponShopId = coupon.shop.toString();\n\t\tconst currentShopId = shop._id.toString();\n\n\t\tif (couponShopId === currentShopId) return null;\n\n\t\tconst validShop = await this.shopModel\n\t\t\t.findOne({ _id: coupon.shop, deletedAt: null })\n\t\t\t.select('name')\n\t\t\t.lean();\n\n\t\tif (!validShop) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'Coupon is associated with an invalid shop',\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\tmessage: `This coupon can be used at ${validShop.name} only`,\n\t\t};\n\t}\n\n\tprivate validateMultiRestrictions(shop: any, coupon: any) {\n\t\tconst hasShopRestriction = Array.isArray(coupon.shops) && coupon.shops.length > 0;\n\t\tconst hasItemTypeRestriction = Array.isArray(coupon.itemTypes) && coupon.itemTypes.length > 0;\n\n\t\tif (!hasShopRestriction && !hasItemTypeRestriction) return null;\n\n\t\tconst shopMatches =\n\t\t\thasShopRestriction &&\n\t\t\tcoupon.shops.some((couponShop: any) => couponShop._id.toString() === shop._id.toString());\n\n\t\tconst itemTypeMatches = hasItemTypeRestriction && coupon.itemTypes.includes(shop.itemType);\n\n\t\tif (!shopMatches && !itemTypeMatches) {\n\t\t\tconst shopNames = coupon.shops.map((shop: any) => shop.name).join(', ');\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: `This coupon can be used at ${shopNames}`,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async validateSingleVendorRestriction(vendor: any, coupon: any) {\n\t\tif (!coupon.vendor) return null;\n\n\t\tconst couponVendorId = coupon.vendor.toString();\n\t\tconst currentVendorId = vendor._id.toString();\n\n\t\tif (couponVendorId === currentVendorId) return null;\n\n\t\tconst validVendor = await this.vendorModel.findOne({ _id: coupon.vendor }).select('name').lean();\n\n\t\tif (!validVendor) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'Coupon is associated with an invalid vendor',\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\tmessage: `This coupon can be used at ${validVendor.name} only`,\n\t\t};\n\t}\n\n\tprivate validateVendorMultiRestrictions(vendor: any, coupon: any) {\n\t\tconst hasVendorRestriction = Array.isArray(coupon.vendors) && coupon.vendors.length > 0;\n\t\tconst hasItemTypeRestriction = Array.isArray(coupon.itemTypes) && coupon.itemTypes.length > 0;\n\n\t\tif (!hasVendorRestriction && !hasItemTypeRestriction) return null;\n\n\t\tconst vendorMatches =\n\t\t\thasVendorRestriction &&\n\t\t\tcoupon.vendors.some((couponVendor: any) => couponVendor._id.toString() === vendor._id.toString());\n\n\t\tconst itemTypeMatches = hasItemTypeRestriction && coupon.itemTypes.includes(vendor.serviceType);\n\n\t\tif (!vendorMatches && !itemTypeMatches) {\n\t\t\tconst vendorNames = coupon.vendors.map((vendor: any) => vendor.name).join(', ');\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: `This coupon can be used at ${vendorNames}`,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async validateFirstOrderOnly(data: ValidateCouponDTO, shop: any, coupon: any) {\n\t\tif (!coupon.firstOrderOnly) return null;\n\n\t\tconst existingOrder = await this.orderModel.exists({\n\t\t\tuser: new mongoose.Types.ObjectId(data.user),\n\t\t\tshop: shop._id,\n\t\t\tstatus: { $ne: RegularOrderStatus.CANCELLED },\n\t\t});\n\n\t\tif (existingOrder) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'This coupon is only valid for the first order in the shop',\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate async validateVendorFirstOrderOnly(data: ValidateCouponDTO, vendor: any, coupon: any) {\n\t\tif (!coupon.firstOrderOnly) return null;\n\n\t\tconst existingBooking = await this.serviceBookingModel.exists({\n\t\t\tuser: new mongoose.Types.ObjectId(data.user),\n\t\t\tvendor: vendor._id,\n\t\t\tstatus: { $ne: ServiceBookingStatus.CANCELLED },\n\t\t});\n\n\t\tif (existingBooking) {\n\t\t\treturn {\n\t\t\t\tisValid: false,\n\t\t\t\tmessage: 'This coupon is only valid for the first order in the vendor',\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic async fetchUserName(userId: mongoose.Types.ObjectId): Promise<string> {\n\t\tconst user = await this.userModel.findById(userId).select('coreUser').lean();\n\n\t\tif (!user) return 'Unknown User';\n\n\t\tconst coreUser = await this.coreUserModel.findById(user.coreUser).select('name').lean();\n\n\t\treturn coreUser ? coreUser.name : 'Unknown User';\n\t}\n}\n\nexport const couponHelperService = Container.get(CouponHelperService);\n"]}
@@ -20,20 +20,225 @@ export declare class IndividualStoreCouponService {
20
20
  secondaryCompanyCut?: number | undefined;
21
21
  shopCut?: number | undefined;
22
22
  secondaryShopCut?: number | undefined;
23
+ vendorCut?: number | undefined;
24
+ secondaryVendorCut?: number | undefined;
23
25
  } | undefined;
24
26
  couponDetails?: Record<string, unknown> | undefined;
25
27
  } | {
26
28
  isValid: boolean;
27
29
  message: string;
28
- shop?: undefined;
30
+ vendor?: undefined;
29
31
  } | {
30
32
  isValid: boolean;
31
- shop: mongoose.Document<unknown, import("@typegoose/typegoose/lib/types").BeAnObject, import("@lyxa.ai/core/dist/libraries/mongo/models/shop.model").Shop, import("@typegoose/typegoose/lib/types").BeAnyObject, {}> & Omit<import("@lyxa.ai/core/dist/libraries/mongo/models/shop.model").Shop & {
33
+ vendor: mongoose.FlattenMaps<{
34
+ vendorId?: string | undefined;
35
+ name: string;
36
+ ownerName: string;
37
+ email: string;
38
+ vendorParent: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/vendor-parent.model").VendorParent>;
39
+ serviceType?: import("@lyxa.ai/core/dist/utilities/enum").ServiceType | undefined;
40
+ status: import("@lyxa.ai/core/dist/utilities/enum").Status;
41
+ phoneNumber: string;
42
+ userPhoneNumber: string;
43
+ additionalPhoneNumbers?: string[] | undefined;
44
+ address: {
45
+ zone?: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/zone.model").Zone> | undefined;
46
+ address: string;
47
+ description?: string | undefined;
48
+ city?: string | undefined;
49
+ state?: string | undefined;
50
+ country: string;
51
+ location: {
52
+ type?: import("@lyxa.ai/core/dist/utilities/enum").GeoLocationType | undefined;
53
+ coordinates: [number, number];
54
+ };
55
+ latitude: number;
56
+ longitude: number;
57
+ };
58
+ zones: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/zone.model").Zone>[];
59
+ logo?: string | undefined;
60
+ banner?: string | undefined;
61
+ document?: {
62
+ commercialCircularDocument?: string | undefined;
63
+ taxRegistration?: string | undefined;
64
+ contactPaper?: string | undefined;
65
+ identityDoc?: string | undefined;
66
+ } | undefined;
67
+ avgRating?: number | undefined;
68
+ isVisibleToUser: boolean;
69
+ ordersCount?: number | undefined;
70
+ bobContactName?: string | undefined;
71
+ bobContactPhoneNumber?: string | undefined;
72
+ orderSettings?: {
73
+ frequencyConfigs?: {
74
+ recurrencePattern: import("@lyxa.ai/core/dist/utilities/enum").ServiceBookingRecurrencePattern;
75
+ enabled: boolean;
76
+ discountType?: import("@lyxa.ai/core/dist/utilities/enum").ValueType | undefined;
77
+ discountValue?: number | undefined;
78
+ }[] | undefined;
79
+ slotInterval?: number | undefined;
80
+ bufferTime?: number | undefined;
81
+ servicePreparationTime?: number | undefined;
82
+ maxAdvanceOrderDay?: number | undefined;
83
+ autoUrgencyTimeLimit?: number | undefined;
84
+ } | undefined;
85
+ workHourSetting?: {
86
+ normalHours?: {
87
+ day: import("@lyxa.ai/core/dist/utilities/enum").WeekDay;
88
+ status: import("@lyxa.ai/core/dist/utilities/enum").WorkStatus;
89
+ openingHours?: {
90
+ start: string;
91
+ end: string;
92
+ }[] | undefined;
93
+ }[] | undefined;
94
+ holidayHours?: {
95
+ date?: Date | undefined;
96
+ status: import("@lyxa.ai/core/dist/utilities/enum").HolidayWorkStatus;
97
+ closingHour?: {
98
+ start: string;
99
+ end: string;
100
+ } | undefined;
101
+ }[] | undefined;
102
+ } | undefined;
103
+ createdAt?: Date | undefined;
104
+ updatedAt?: Date | undefined;
105
+ }> & {
32
106
  _id: mongoose.Types.ObjectId;
33
107
  } & {
34
108
  __v: number;
35
- }, "typegooseName"> & import("@typegoose/typegoose/lib/types").IObjectWithTypegooseFunction;
109
+ };
36
110
  message?: undefined;
111
+ } | {
112
+ isValid: boolean;
113
+ shop: mongoose.FlattenMaps<{
114
+ shopId?: string | undefined;
115
+ name: string;
116
+ ownerName: string;
117
+ email: string;
118
+ parent: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/parent.model").Parent>;
119
+ itemType: import("@lyxa.ai/core/dist/utilities/enum").ItemType;
120
+ status: import("@lyxa.ai/core/dist/utilities/enum").Status;
121
+ shopZoneStatus?: import("@lyxa.ai/core/dist/utilities/enum").ZoneStatus | undefined;
122
+ brand: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/brand.model").Brand>;
123
+ priceRange?: number | undefined;
124
+ phoneNumber: string;
125
+ userPhoneNumber: string;
126
+ additionalPhoneNumbers?: string[] | undefined;
127
+ bobContactFirstName?: string | undefined;
128
+ bobContactLastName?: string | undefined;
129
+ bobContactPhoneNumber?: string | undefined;
130
+ address: {
131
+ zone?: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/zone.model").Zone> | undefined;
132
+ address: string;
133
+ description?: string | undefined;
134
+ city?: string | undefined;
135
+ state?: string | undefined;
136
+ country: string;
137
+ location: {
138
+ type?: import("@lyxa.ai/core/dist/utilities/enum").GeoLocationType | undefined;
139
+ coordinates: [number, number];
140
+ };
141
+ latitude: number;
142
+ longitude: number;
143
+ };
144
+ logo?: string | undefined;
145
+ banner?: string | undefined;
146
+ document?: {
147
+ commercialCircularDocument?: string | undefined;
148
+ taxRegistration?: string | undefined;
149
+ contactPaper?: string | undefined;
150
+ identityDoc?: string | undefined;
151
+ } | undefined;
152
+ onlineStatus?: import("@lyxa.ai/core/dist/utilities/enum").ShopOnlineStatus | undefined;
153
+ workHourSetting?: {
154
+ normalHours?: {
155
+ day: import("@lyxa.ai/core/dist/utilities/enum").WeekDay;
156
+ status: import("@lyxa.ai/core/dist/utilities/enum").WorkStatus;
157
+ openingHours?: {
158
+ start: string;
159
+ end: string;
160
+ }[] | undefined;
161
+ }[] | undefined;
162
+ holidayHours?: {
163
+ date?: Date | undefined;
164
+ status: import("@lyxa.ai/core/dist/utilities/enum").HolidayWorkStatus;
165
+ closingHour?: {
166
+ start: string;
167
+ end: string;
168
+ } | undefined;
169
+ }[] | undefined;
170
+ } | undefined;
171
+ dietaries?: string[] | undefined;
172
+ tags?: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/tag.model").Tag>[] | undefined;
173
+ isBeirutAuthentic: boolean;
174
+ isVisibleToUser: boolean;
175
+ isShopDelivery?: boolean | undefined;
176
+ deliveryFee?: number | undefined;
177
+ minimumOrderAmount?: number | undefined;
178
+ orderCapacityEnabled?: boolean | undefined;
179
+ orderCapacity?: number | undefined;
180
+ shopActiveTimingsOfCurrentDate?: {
181
+ start: string;
182
+ end: string;
183
+ }[] | undefined;
184
+ deliveryInfo?: {
185
+ totalDeliveries?: number | undefined;
186
+ totalDeliveryTime?: number | undefined;
187
+ } | undefined;
188
+ sortingOrder?: number | undefined;
189
+ allowSpecialInstructions?: boolean | undefined;
190
+ isRiderNoteVisible?: boolean | undefined;
191
+ riderNote?: string | undefined;
192
+ defaultPreparationTime?: number | undefined;
193
+ averagePreparationTime?: number | undefined;
194
+ bestSellerCategory?: {
195
+ title?: string | undefined;
196
+ status?: import("@lyxa.ai/core/dist/utilities/enum").Status | undefined;
197
+ } | undefined;
198
+ favouritesCategory?: {
199
+ title?: string | undefined;
200
+ status?: import("@lyxa.ai/core/dist/utilities/enum").Status | undefined;
201
+ products?: import("@typegoose/typegoose").Ref<import("@lyxa.ai/core/dist/libraries/mongo/models/product.model").Product>[] | undefined;
202
+ } | undefined;
203
+ avgRating?: number | undefined;
204
+ ordersCount?: number | undefined;
205
+ customersCount?: number | undefined;
206
+ fcmTokens?: string[] | undefined;
207
+ searchFields?: {
208
+ name?: {
209
+ autocomplete?: string[] | undefined;
210
+ fulltext?: string[] | undefined;
211
+ } | undefined;
212
+ products?: {
213
+ autocomplete?: string[] | undefined;
214
+ fulltext?: string[] | undefined;
215
+ } | undefined;
216
+ tags?: {
217
+ autocomplete?: string[] | undefined;
218
+ fulltext?: string[] | undefined;
219
+ } | undefined;
220
+ parentCategories?: {
221
+ autocomplete?: string[] | undefined;
222
+ fulltext?: string[] | undefined;
223
+ } | undefined;
224
+ subCategories?: {
225
+ autocomplete?: string[] | undefined;
226
+ fulltext?: string[] | undefined;
227
+ } | undefined;
228
+ brands?: {
229
+ autocomplete?: string[] | undefined;
230
+ fulltext?: string[] | undefined;
231
+ } | undefined;
232
+ } | undefined;
233
+ isMenuDietaryFilterEnabled?: boolean | undefined;
234
+ categoryNumberingEnabled?: boolean | undefined;
235
+ createdAt?: Date | undefined;
236
+ updatedAt?: Date | undefined;
237
+ }> & {
238
+ _id: mongoose.Types.ObjectId;
239
+ } & {
240
+ __v: number;
241
+ };
37
242
  }>;
38
243
  }
39
244
  export declare const individualStoreCouponService: IndividualStoreCouponService;
@@ -1 +1 @@
1
- {"version":3,"file":"individual-store-coupon.service.d.ts","sourceRoot":"/","sources":["modules/coupon/services/individual-store-coupon.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAEN,qBAAqB,IAAI,KAAK,EAI9B,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACN,8BAA8B,IAAI,SAAS,EAC3C,8BAA8B,IAAI,SAAS,EAC3C,iBAAiB,EACjB,MAAM,6BAA6B,CAAC;AAIrC,qBACa,4BAA4B;IACxC,OAAO,CAAC,KAAK,CAAC;IACd,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IACtD,OAAO,CAAC,UAAU,CAA2D;;IAYhE,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAWrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAuBjE,cAAc,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYhF;AAED,eAAO,MAAM,4BAA4B,8BAA8C,CAAC"}
1
+ {"version":3,"file":"individual-store-coupon.service.d.ts","sourceRoot":"/","sources":["modules/coupon/services/individual-store-coupon.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAEN,qBAAqB,IAAI,KAAK,EAI9B,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACN,8BAA8B,IAAI,SAAS,EAC3C,8BAA8B,IAAI,SAAS,EAC3C,iBAAiB,EACjB,MAAM,6BAA6B,CAAC;AAIrC,qBACa,4BAA4B;IACxC,OAAO,CAAC,KAAK,CAAC;IACd,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IACtD,OAAO,CAAC,UAAU,CAA2D;;IAYhE,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAWrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAuBjE,cAAc,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYhF;AAED,eAAO,MAAM,4BAA4B,8BAA8C,CAAC"}
@@ -6,7 +6,7 @@ export declare class IndividualUserCouponService {
6
6
  constructor();
7
7
  create(data: CreateDTO): Promise<DocumentType<Model>>;
8
8
  update(id: string, data: UpdateDTO): Promise<DocumentType<Model>>;
9
- validateCoupon(data: ValidateCouponDTO, couponDocument: DocumentType<Model>): Promise<CouponValidationOutputDTO>;
9
+ validateCoupon(data: ValidateCouponDTO, coupon: DocumentType<Model>): Promise<CouponValidationOutputDTO>;
10
10
  }
11
11
  export declare const individualUserCouponService: IndividualUserCouponService;
12
12
  //# sourceMappingURL=individual-user-coupon.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"individual-user-coupon.service.d.ts","sourceRoot":"/","sources":["modules/coupon/services/individual-user-coupon.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAY,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAEN,oBAAoB,IAAI,KAAK,EAC7B,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACN,yBAAyB,EACzB,6BAA6B,IAAI,SAAS,EAC1C,6BAA6B,IAAI,SAAS,EAC1C,iBAAiB,EACjB,MAAM,6BAA6B,CAAC;AAKrC,qBACa,2BAA2B;IACvC,OAAO,CAAC,KAAK,CAAC;;IAUD,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAWrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IA2BjE,cAAc,CAC1B,IAAI,EAAE,iBAAiB,EACvB,cAAc,EAAE,YAAY,CAAC,KAAK,CAAC,GACjC,OAAO,CAAC,yBAAyB,CAAC;CAkDrC;AAED,eAAO,MAAM,2BAA2B,6BAA6C,CAAC"}
1
+ {"version":3,"file":"individual-user-coupon.service.d.ts","sourceRoot":"/","sources":["modules/coupon/services/individual-user-coupon.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAY,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAEN,oBAAoB,IAAI,KAAK,EAC7B,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACN,yBAAyB,EACzB,6BAA6B,IAAI,SAAS,EAC1C,6BAA6B,IAAI,SAAS,EAC1C,iBAAiB,EACjB,MAAM,6BAA6B,CAAC;AAMrC,qBACa,2BAA2B;IACvC,OAAO,CAAC,KAAK,CAAC;;IAUD,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAWrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IA2BjE,cAAc,CAC1B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,GACzB,OAAO,CAAC,yBAAyB,CAAC;CA0DrC;AAED,eAAO,MAAM,2BAA2B,6BAA6C,CAAC"}
@@ -50,6 +50,7 @@ const models_1 = require("@lyxa.ai/core/dist/libraries/mongo/models/");
50
50
  const currency_1 = require("@lyxa.ai/core/dist/utilities/currency");
51
51
  const shared_1 = require("@lyxa.ai/core/dist/utilities/shared");
52
52
  const enum_1 = require("@lyxa.ai/core/dist/utilities/enum");
53
+ const helper_service_1 = require("./helper.service");
53
54
  let IndividualUserCouponService = class IndividualUserCouponService {
54
55
  model;
55
56
  constructor() {
@@ -75,23 +76,29 @@ let IndividualUserCouponService = class IndividualUserCouponService {
75
76
  }
76
77
  return record;
77
78
  }
78
- async validateCoupon(data, couponDocument) {
79
+ async validateCoupon(data, coupon) {
79
80
  const userId = new typegoose_1.mongoose.Types.ObjectId(data.user);
80
- if (!couponDocument.users.some(u => u.toString() == userId.toString())) {
81
+ if (!coupon.users.some(u => u.toString() == userId.toString())) {
81
82
  return {
82
83
  isValid: false,
83
84
  message: 'This coupon is for specific users only',
84
85
  };
85
86
  }
86
- let discountAmount = couponDocument.valueType == enum_1.ValueType.FIXED
87
- ? couponDocument.value
88
- : (0, shared_1.roundBaseCurrency)((data.amountBeforeCoupon * couponDocument.value) / 100);
89
- let secondaryDiscountAmount = couponDocument.valueType == enum_1.ValueType.FIXED
90
- ? await (0, currency_1.convertToSecondary)(couponDocument.value)
91
- : (0, shared_1.roundSecondaryCurrency)((data.secondaryAmountBeforeCoupon * couponDocument.value) / 100);
92
- if (couponDocument.isMaxDiscountLimitEnabled && couponDocument.maxDiscountLimit) {
93
- discountAmount = Math.min(discountAmount, couponDocument.maxDiscountLimit);
94
- secondaryDiscountAmount = Math.min(secondaryDiscountAmount, await (0, currency_1.convertToSecondary)(couponDocument.maxDiscountLimit));
87
+ const shopValidation = await helper_service_1.couponHelperService.validateShopEligibility({
88
+ data,
89
+ coupon,
90
+ });
91
+ if (!shopValidation.isValid)
92
+ return shopValidation;
93
+ let discountAmount = coupon.valueType == enum_1.ValueType.FIXED
94
+ ? coupon.value
95
+ : (0, shared_1.roundBaseCurrency)((data.amountBeforeCoupon * coupon.value) / 100);
96
+ let secondaryDiscountAmount = coupon.valueType == enum_1.ValueType.FIXED
97
+ ? await (0, currency_1.convertToSecondary)(coupon.value)
98
+ : (0, shared_1.roundSecondaryCurrency)((data.secondaryAmountBeforeCoupon * coupon.value) / 100);
99
+ if (coupon.isMaxDiscountLimitEnabled && coupon.maxDiscountLimit) {
100
+ discountAmount = Math.min(discountAmount, coupon.maxDiscountLimit);
101
+ secondaryDiscountAmount = Math.min(secondaryDiscountAmount, await (0, currency_1.convertToSecondary)(coupon.maxDiscountLimit));
95
102
  }
96
103
  if (discountAmount > data.amountBeforeCoupon) {
97
104
  discountAmount = data.amountBeforeCoupon;
@@ -110,6 +117,8 @@ let IndividualUserCouponService = class IndividualUserCouponService {
110
117
  secondaryCompanyCut: secondaryDiscountAmount,
111
118
  shopCut: 0,
112
119
  secondaryShopCut: 0,
120
+ vendorCut: 0,
121
+ secondaryVendorCut: 0,
113
122
  },
114
123
  };
115
124
  }