@lyxa.ai/marketing 1.0.61 → 1.0.63

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 (131) hide show
  1. package/dist/lib/index.d.ts +60 -1
  2. package/dist/lib/index.d.ts.map +1 -1
  3. package/dist/lib/modules/coupon/routers/coupon.router.d.ts +59 -0
  4. package/dist/lib/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  5. package/dist/lib/modules/coupon/routers/coupon.router.js +7 -0
  6. package/dist/lib/modules/coupon/routers/coupon.router.js.map +1 -1
  7. package/dist/lib/modules/coupon/services/coupon.service.d.ts +1 -0
  8. package/dist/lib/modules/coupon/services/coupon.service.d.ts.map +1 -1
  9. package/dist/lib/modules/coupon/services/coupon.service.js +3 -0
  10. package/dist/lib/modules/coupon/services/coupon.service.js.map +1 -1
  11. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.d.ts +1 -0
  12. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.d.ts.map +1 -0
  13. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.js +2 -0
  14. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.js.map +1 -0
  15. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.validation.d.ts +1 -1
  16. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts +1 -0
  17. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts.map +1 -1
  18. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.js +8 -2
  19. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.js.map +1 -1
  20. package/dist/lib/modules/marketing/services/marketing-conflict.service.d.ts +1 -0
  21. package/dist/lib/modules/marketing/services/marketing-conflict.service.d.ts.map +1 -1
  22. package/dist/lib/modules/marketing/services/marketing-conflict.service.js +4 -0
  23. package/dist/lib/modules/marketing/services/marketing-conflict.service.js.map +1 -1
  24. package/dist/lib/modules/marketing/services/marketing.service.d.ts.map +1 -1
  25. package/dist/lib/modules/marketing/services/marketing.service.js +3 -0
  26. package/dist/lib/modules/marketing/services/marketing.service.js.map +1 -1
  27. package/dist/lib/modules/marketing/services/spend-to-get-marketing.service.d.ts.map +1 -1
  28. package/dist/lib/modules/marketing/services/spend-to-get-marketing.service.js +1 -0
  29. package/dist/lib/modules/marketing/services/spend-to-get-marketing.service.js.map +1 -1
  30. package/dist/lib/modules/marketing/validations/buy1-get1-marketing.validation.d.ts +3 -3
  31. package/dist/lib/modules/marketing/validations/discount-marketing.validation.d.ts +3 -3
  32. package/dist/lib/modules/marketing/validations/product-marketing.validation.d.ts +2 -2
  33. package/dist/lib/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +1 -1
  34. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts +1 -0
  35. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts.map +1 -1
  36. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.js +49 -2
  37. package/dist/lib/modules/punch-marketing-history/services/punch-marketing-history.service.js.map +1 -1
  38. package/dist/lib/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
  39. package/dist/types/index.d.ts +60 -1
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/modules/coupon/routers/coupon.router.d.ts +59 -0
  42. package/dist/types/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  43. package/dist/types/modules/coupon/services/coupon.service.d.ts +1 -0
  44. package/dist/types/modules/coupon/services/coupon.service.d.ts.map +1 -1
  45. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.validation.d.ts +1 -1
  46. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts +1 -0
  47. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts.map +1 -1
  48. package/dist/types/modules/marketing/services/marketing-conflict.service.d.ts +5 -0
  49. package/dist/types/modules/marketing/services/marketing-conflict.service.d.ts.map +1 -1
  50. package/dist/types/modules/marketing/services/marketing.service.d.ts.map +1 -1
  51. package/dist/types/modules/marketing/services/spend-to-get-marketing.service.d.ts.map +1 -1
  52. package/dist/types/modules/marketing/validations/buy1-get1-marketing.validation.d.ts +3 -3
  53. package/dist/types/modules/marketing/validations/discount-marketing.validation.d.ts +3 -3
  54. package/dist/types/modules/marketing/validations/product-marketing.validation.d.ts +2 -2
  55. package/dist/types/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +1 -1
  56. package/dist/types/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts +5 -0
  57. package/dist/types/modules/punch-marketing-history/services/punch-marketing-history.service.d.ts.map +1 -1
  58. package/dist/types/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +2 -2
  59. package/package.json +1 -1
  60. package/dist/lib/modules/spend-to-get-history/routers/index.d.ts +0 -2
  61. package/dist/lib/modules/spend-to-get-history/routers/index.d.ts.map +0 -1
  62. package/dist/lib/modules/spend-to-get-history/routers/index.js +0 -18
  63. package/dist/lib/modules/spend-to-get-history/routers/index.js.map +0 -1
  64. package/dist/lib/modules/spend-to-get-history/routers/spend-to-get-history.router.js +0 -62
  65. package/dist/lib/modules/spend-to-get-history/routers/spend-to-get-history.router.js.map +0 -1
  66. package/dist/lib/modules/spend-to-get-history/services/index.d.ts +0 -2
  67. package/dist/lib/modules/spend-to-get-history/services/index.d.ts.map +0 -1
  68. package/dist/lib/modules/spend-to-get-history/services/index.js +0 -18
  69. package/dist/lib/modules/spend-to-get-history/services/index.js.map +0 -1
  70. package/dist/lib/modules/spend-to-get-history/services/spend-to-get-history.service.d.ts +0 -24
  71. package/dist/lib/modules/spend-to-get-history/services/spend-to-get-history.service.d.ts.map +0 -1
  72. package/dist/lib/modules/spend-to-get-history/services/spend-to-get-history.service.js +0 -136
  73. package/dist/lib/modules/spend-to-get-history/services/spend-to-get-history.service.js.map +0 -1
  74. package/dist/lib/modules/spend-to-get-history/validations/index.d.ts +0 -2
  75. package/dist/lib/modules/spend-to-get-history/validations/index.d.ts.map +0 -1
  76. package/dist/lib/modules/spend-to-get-history/validations/index.js +0 -18
  77. package/dist/lib/modules/spend-to-get-history/validations/index.js.map +0 -1
  78. package/dist/lib/modules/spend-to-get-history/validations/spend-to-get-history.validation.d.ts +0 -232
  79. package/dist/lib/modules/spend-to-get-history/validations/spend-to-get-history.validation.d.ts.map +0 -1
  80. package/dist/lib/modules/spend-to-get-history/validations/spend-to-get-history.validation.js +0 -30
  81. package/dist/lib/modules/spend-to-get-history/validations/spend-to-get-history.validation.js.map +0 -1
  82. package/dist/lib/modules/tournament/match/events/match-ended.consumer.d.ts +0 -2
  83. package/dist/lib/modules/tournament/match/events/match-ended.consumer.d.ts.map +0 -1
  84. package/dist/lib/modules/tournament/match/events/match-ended.consumer.js +0 -15
  85. package/dist/lib/modules/tournament/match/events/match-ended.consumer.js.map +0 -1
  86. package/dist/lib/modules/tournament/match/events/match-ended.event.d.ts +0 -14
  87. package/dist/lib/modules/tournament/match/events/match-ended.event.d.ts.map +0 -1
  88. package/dist/lib/modules/tournament/match/events/match-ended.event.js +0 -12
  89. package/dist/lib/modules/tournament/match/events/match-ended.event.js.map +0 -1
  90. package/dist/lib/modules/tournament/notification/jobs/notification.producer.d.ts +0 -10
  91. package/dist/lib/modules/tournament/notification/jobs/notification.producer.d.ts.map +0 -1
  92. package/dist/lib/modules/tournament/notification/jobs/notification.producer.js +0 -23
  93. package/dist/lib/modules/tournament/notification/jobs/notification.producer.js.map +0 -1
  94. package/dist/lib/modules/tournament/notification/jobs/notification.worker.d.ts +0 -9
  95. package/dist/lib/modules/tournament/notification/jobs/notification.worker.d.ts.map +0 -1
  96. package/dist/lib/modules/tournament/notification/jobs/notification.worker.js +0 -82
  97. package/dist/lib/modules/tournament/notification/jobs/notification.worker.js.map +0 -1
  98. package/dist/lib/modules/tournament/notification/notification.schedule.config.d.ts +0 -10
  99. package/dist/lib/modules/tournament/notification/notification.schedule.config.d.ts.map +0 -1
  100. package/dist/lib/modules/tournament/notification/notification.schedule.config.js +0 -15
  101. package/dist/lib/modules/tournament/notification/notification.schedule.config.js.map +0 -1
  102. package/dist/lib/modules/tournament/notification/notification.scheduler.service.d.ts +0 -10
  103. package/dist/lib/modules/tournament/notification/notification.scheduler.service.d.ts.map +0 -1
  104. package/dist/lib/modules/tournament/notification/notification.scheduler.service.js +0 -73
  105. package/dist/lib/modules/tournament/notification/notification.scheduler.service.js.map +0 -1
  106. package/dist/types/events/event-initializations.d.ts +0 -2
  107. package/dist/types/events/event-initializations.d.ts.map +0 -1
  108. package/dist/types/modules/spend-to-get-history/routers/index.d.ts +0 -2
  109. package/dist/types/modules/spend-to-get-history/routers/index.d.ts.map +0 -1
  110. package/dist/types/modules/spend-to-get-history/services/index.d.ts +0 -2
  111. package/dist/types/modules/spend-to-get-history/services/index.d.ts.map +0 -1
  112. package/dist/types/modules/spend-to-get-history/services/spend-to-get-history.service.d.ts +0 -56
  113. package/dist/types/modules/spend-to-get-history/services/spend-to-get-history.service.d.ts.map +0 -1
  114. package/dist/types/modules/spend-to-get-history/validations/index.d.ts +0 -2
  115. package/dist/types/modules/spend-to-get-history/validations/index.d.ts.map +0 -1
  116. package/dist/types/modules/spend-to-get-history/validations/spend-to-get-history.validation.d.ts +0 -241
  117. package/dist/types/modules/spend-to-get-history/validations/spend-to-get-history.validation.d.ts.map +0 -1
  118. package/dist/types/modules/tournament/config/tournament-config.validation.d.ts +0 -201
  119. package/dist/types/modules/tournament/config/tournament-config.validation.d.ts.map +0 -1
  120. package/dist/types/modules/tournament/match/events/match-ended.consumer.d.ts +0 -2
  121. package/dist/types/modules/tournament/match/events/match-ended.consumer.d.ts.map +0 -1
  122. package/dist/types/modules/tournament/match/events/match-ended.event.d.ts +0 -14
  123. package/dist/types/modules/tournament/match/events/match-ended.event.d.ts.map +0 -1
  124. package/dist/types/modules/tournament/notification/jobs/notification.producer.d.ts +0 -10
  125. package/dist/types/modules/tournament/notification/jobs/notification.producer.d.ts.map +0 -1
  126. package/dist/types/modules/tournament/notification/jobs/notification.worker.d.ts +0 -9
  127. package/dist/types/modules/tournament/notification/jobs/notification.worker.d.ts.map +0 -1
  128. package/dist/types/modules/tournament/notification/notification.schedule.config.d.ts +0 -10
  129. package/dist/types/modules/tournament/notification/notification.schedule.config.d.ts.map +0 -1
  130. package/dist/types/modules/tournament/notification/notification.scheduler.service.d.ts +0 -10
  131. package/dist/types/modules/tournament/notification/notification.scheduler.service.d.ts.map +0 -1
@@ -89,9 +89,12 @@ let LimitedTimeOfferParticipantService = class LimitedTimeOfferParticipantServic
89
89
  dto: resolvedDto,
90
90
  pausedMarketings: allPausedMarketings,
91
91
  }));
92
- await this.repository.updateShopOrderCapacity(shopId, resolvedDto.isOrderCapacityEnabled, resolvedDto.maxOrderCapacity);
92
+ await this.syncShopOrderCapacity(shopId, resolvedDto);
93
93
  return participant;
94
94
  }
95
+ syncShopOrderCapacity(shopId, dto) {
96
+ return this.repository.updateShopOrderCapacity(shopId, dto.isOrderCapacityEnabled, dto.maxOrderCapacity);
97
+ }
95
98
  async pauseIfLive(campaign, shopId) {
96
99
  if (campaign.status !== enum_1.LimitedTimeOfferCampaignStatus.ACTIVE)
97
100
  return [];
@@ -144,7 +147,7 @@ let LimitedTimeOfferParticipantService = class LimitedTimeOfferParticipantServic
144
147
  await this.marketingService.removeMarketing(participant.marketingId);
145
148
  }
146
149
  const { marketingId: newMarketingId } = await this.marketingService.applyMarketing(shopId, campaign, resolvedDto);
147
- return this.repository.updateParticipant(campaign._id, shopId, {
150
+ const updatedParticipant = await this.repository.updateParticipant(campaign._id, shopId, {
148
151
  products: resolvedDto.products ?? [],
149
152
  isEntireMenu: resolvedDto.isEntireMenu,
150
153
  discountPercentage: resolvedDto.discountPercentage,
@@ -153,6 +156,8 @@ let LimitedTimeOfferParticipantService = class LimitedTimeOfferParticipantServic
153
156
  maxOrderCapacity: resolvedDto.maxOrderCapacity,
154
157
  marketing: newMarketingId,
155
158
  });
159
+ await this.syncShopOrderCapacity(shopId, resolvedDto);
160
+ return updatedParticipant;
156
161
  }
157
162
  async getParticipants(dto) {
158
163
  const { documents, total } = await this.repository.findByCampaign(dto);
@@ -186,6 +191,7 @@ let LimitedTimeOfferParticipantService = class LimitedTimeOfferParticipantServic
186
191
  }
187
192
  const { marketingId } = await this.marketingService.applyMarketing(participant.shop, campaign, dto);
188
193
  await this.repository.updateParticipant(campaign._id, participant.shop, { ...dto, marketing: marketingId });
194
+ await this.syncShopOrderCapacity(participant.shop, dto);
189
195
  }
190
196
  }
191
197
  async applyMarketingWithRollback(input) {
@@ -1 +1 @@
1
- {"version":3,"file":"limited-time-offer-participant.service.js","sourceRoot":"/","sources":["modules/limited-time-offer/participant/services/limited-time-offer-participant.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, {Service} from 'typedi';\nimport {DocumentType, mongoose} from '@typegoose/typegoose';\nimport {LimitedTimeOfferCampaign, Setting} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {LimitedTimeOfferCampaignStatus, MarketingType} from '@lyxa.ai/core/dist/utilities/enum';\nimport {badRequestError, conflictError, notFoundError} from '@lyxa.ai/core/dist/utilities/error-common';\nimport {LtoCampaignService} from '@modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service';\nimport {LtoParticipantMarketingService} from './limited-time-offer-participant-marketing.service';\nimport {\n\tAdminEnrollShopEntryDTO,\n\tJoinLtoCampaignDTO,\n\tLtoCardState,\n\tParticipantStatusDTO,\n\tParticipantSummaryDTO,\n} from '../limited-time-offer-participant.validation';\nimport {LtoParticipantQueryBuilder} from '../limited-time-offer-participant.builder';\nimport {LtoParticipantRepository} from '../limited-time-offer-participant.repository';\nimport {GetLtoCampaignParticipantsDTO} from '../../campaign/limited-time-offer-campaign.validation';\nimport {getLibraries} from '@lyxa.ai/core';\n\n@Service()\nexport class LimitedTimeOfferParticipantService {\n\tprivate repository: LtoParticipantRepository;\n\tprivate campaignService: LtoCampaignService;\n\tprivate marketingService: LtoParticipantMarketingService;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(LtoParticipantRepository);\n\t\tthis.campaignService = Container.get(LtoCampaignService);\n\t\tthis.marketingService = Container.get(LtoParticipantMarketingService);\n\t}\n\n\tpublic async join(shopId: mongoose.Types.ObjectId, dto: JoinLtoCampaignDTO) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\t\treturn this.joinShop(campaign, shopId, dto);\n\t}\n\n\tpublic async enrollShops(entries: AdminEnrollShopEntryDTO[]) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\t\treturn Promise.all(entries.map(({shopId, ...dto}) => this.joinShop(campaign, shopId, dto)));\n\t}\n\n\tprivate async joinShop(\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>,\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t) {\n\t\tawait this.assertNotAlreadyJoined(campaign._id, shopId);\n\t\tthis.validateDiscountSelections({campaign, discountPercentage: dto.discountPercentage, maxDiscountCap: dto.maxDiscountCap});\n\t\tawait this.assertProductsBelongToShop(shopId, dto);\n\n\t\tconst resolvedDto = await this.resolveCategorySelection(shopId, dto);\n\t\tconst pausedMarketings = await this.pauseIfLive(campaign, shopId);\n\n\t\tconst {marketingId, pausedMarketingIds} = await this.applyMarketingWithRollback({\n\t\t\tshopId,\n\t\t\tcampaign,\n\t\t\tdto: resolvedDto,\n\t\t\tpausedMarketings,\n\t\t});\n\t\tconst allPausedMarketings = this.uniqueObjectIds([...pausedMarketings, ...pausedMarketingIds]);\n\n\t\tconst participant = await this.repository.create(\n\t\t\tLtoParticipantQueryBuilder.buildCreateInput({\n\t\t\t\tcampaignId: campaign._id,\n\t\t\t\tshopId,\n\t\t\t\tmarketingId,\n\t\t\t\tdto: resolvedDto,\n\t\t\t\tpausedMarketings: allPausedMarketings,\n\t\t\t})\n\t\t);\n\t\tawait this.repository.updateShopOrderCapacity(\n\t\t\tshopId,\n\t\t\tresolvedDto.isOrderCapacityEnabled,\n\t\t\tresolvedDto.maxOrderCapacity\n\t\t);\n\t\treturn participant;\n\t}\n\n\tprivate async pauseIfLive(\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>,\n\t\tshopId: mongoose.Types.ObjectId\n\t): Promise<mongoose.Types.ObjectId[]> {\n\t\tif (campaign.status !== LimitedTimeOfferCampaignStatus.ACTIVE) return [];\n\t\treturn this.marketingService.pauseActiveShopMarketings(shopId, MarketingType.DISCOUNT);\n\t}\n\n\tpublic async withdraw(shopId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\n\t\tconst participant = await this.repository.withdraw(campaign._id, shopId);\n\t\tif (!participant) throw notFoundError('You have not joined this campaign.');\n\n\t\tif (participant.marketing) {\n\t\t\tawait this.marketingService.removeMarketing(participant.marketing as mongoose.Types.ObjectId);\n\t\t}\n\t\tawait this.marketingService.resumeMarketings(\n\t\t\t(participant.pausedMarketings ?? []) as mongoose.Types.ObjectId[]\n\t\t);\n\t}\n\n\tpublic async cleanupCampaignMarketings(campaignId: mongoose.Types.ObjectId): Promise<void> {\n\t\tawait this.marketingService.removeAllCampaignMarketings(campaignId);\n\t\tconst pausedMarketingIds = await this.repository.findPausedMarketingIdsByCampaign(campaignId);\n\t\tawait this.marketingService.resumeMarketings(pausedMarketingIds);\n\t}\n\n\tpublic async isActiveParticipant(shopId: mongoose.Types.ObjectId): Promise<boolean> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\t\tif (!campaign) return false;\n\t\treturn this.repository.existsActiveByCampaignAndShop(campaign._id, shopId);\n\t}\n\n\tpublic async getStatus(shopId: mongoose.Types.ObjectId): Promise<ParticipantStatusDTO> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\n\t\tif (!campaign) return { campaignState: LtoCardState.NONE };\n\n\t\tconst participant = await this.repository.findActiveByCampaignAndShop(campaign._id, shopId);\n\n\t\tconst campaignState =\n\t\t\tcampaign.status === LimitedTimeOfferCampaignStatus.SCHEDULED\n\t\t\t\t? LtoCardState.SCHEDULED\n\t\t\t\t: campaign.status === LimitedTimeOfferCampaignStatus.ACTIVE\n\t\t\t\t\t? LtoCardState.LIVE\n\t\t\t\t\t: LtoCardState.NONE;\n\n\t\treturn participant\n\t\t\t? { campaignState, campaign: campaign as any, participant }\n\t\t\t: { campaignState, campaign: campaign as any };\n\t}\n\n\tpublic async update(shopId: mongoose.Types.ObjectId, dto: JoinLtoCampaignDTO) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\n\t\tconst participant = await this.repository.findActiveByCampaignAndShop(campaign._id, shopId);\n\t\tif (!participant) throw notFoundError('You have not joined this campaign.');\n\n\t\tthis.validateDiscountSelections({campaign, discountPercentage: dto.discountPercentage, maxDiscountCap: dto.maxDiscountCap, participant});\n\t\tawait this.assertProductsBelongToShop(shopId, dto);\n\n\t\tconst resolvedDto = await this.resolveCategorySelection(shopId, dto);\n\n\t\tif (participant.marketingId) {\n\t\t\tawait this.marketingService.removeMarketing(participant.marketingId);\n\t\t}\n\t\tconst {marketingId: newMarketingId} = await this.marketingService.applyMarketing(shopId, campaign, resolvedDto);\n\n\t\treturn this.repository.updateParticipant(campaign._id, shopId, {\n\t\t\tproducts: resolvedDto.products ?? [],\n\t\t\tisEntireMenu: resolvedDto.isEntireMenu,\n\t\t\tdiscountPercentage: resolvedDto.discountPercentage,\n\t\t\tmaxDiscountCap: resolvedDto.maxDiscountCap,\n\t\t\tisOrderCapacityEnabled: resolvedDto.isOrderCapacityEnabled,\n\t\t\tmaxOrderCapacity: resolvedDto.maxOrderCapacity,\n\t\t\tmarketing: newMarketingId,\n\t\t});\n\t}\n\n\tpublic async getParticipants(dto: GetLtoCampaignParticipantsDTO) {\n\t\tconst {documents, total} = await this.repository.findByCampaign(dto);\n\t\treturn {\n\t\t\tdocuments,\n\t\t\tmetadata: {page: dto.page, size: dto.size, totalElements: total, totalPages: Math.ceil(total / dto.size)},\n\t\t};\n\t}\n\n\tpublic async getParticipantIdsByCampaignId(campaignId: mongoose.Types.ObjectId) {\n\t\treturn await this.repository.findParticipantIdsByCampaignId(campaignId);\n\t}\n\n\tpublic async getNearby(\n\t\tcampaignId: mongoose.Types.ObjectId,\n\t\tinput: { latitude: number; longitude: number }\n\t) {\n\t\tconst settings = (await getLibraries().getCachedSettingsService().getSettings()) as Setting;\n\t\tconst nearByShopDistance = settings.nearByShopDistance || 10000;\n\t\treturn this.repository.findNearby(campaignId, input, nearByShopDistance);\n\t}\n\n\tpublic async reactivateMarketings(campaign: DocumentType<LimitedTimeOfferCampaign>): Promise<void> {\n\t\tconst participants = await this.repository.findAllActiveByCampaign(campaign._id);\n\n\t\tfor (const participant of participants) {\n\t\t\tconst dto: JoinLtoCampaignDTO = {\n\t\t\t\tisEntireMenu: participant.isEntireMenu,\n\t\t\t\tproducts: (participant.products ?? []) as mongoose.Types.ObjectId[],\n\t\t\t\tshopCategories: [],\n\t\t\t\tdiscountPercentage: participant.discountPercentage ?? 0,\n\t\t\t\tmaxDiscountCap: participant.maxDiscountCap ?? 0,\n\t\t\t\tisOrderCapacityEnabled: participant.isOrderCapacityEnabled,\n\t\t\t\tmaxOrderCapacity: participant.maxOrderCapacity,\n\t\t\t};\n\t\t\t// Drop any prior marketing (e.g. created when the shop joined between recurrences) so we\n\t\t\t// don't orphan it — it carries no campaign ref and would leak past cleanup.\n\t\t\tif (participant.marketing) {\n\t\t\t\tawait this.marketingService.removeMarketing(participant.marketing as mongoose.Types.ObjectId);\n\t\t\t}\n\t\t\tconst {marketingId} = await this.marketingService.applyMarketing(\n\t\t\t\tparticipant.shop as mongoose.Types.ObjectId,\n\t\t\t\tcampaign,\n\t\t\t\tdto,\n\t\t\t);\n\t\t\tawait this.repository.updateParticipant(\n\t\t\t\tcampaign._id,\n\t\t\t\tparticipant.shop as mongoose.Types.ObjectId,\n\t\t\t\t{...dto, marketing: marketingId},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async applyMarketingWithRollback(input: {\n\t\tshopId: mongoose.Types.ObjectId;\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>;\n\t\tdto: JoinLtoCampaignDTO;\n\t\tpausedMarketings: mongoose.Types.ObjectId[];\n\t}): Promise<{marketingId: mongoose.Types.ObjectId; pausedMarketingIds: mongoose.Types.ObjectId[]}> {\n\t\tconst {shopId, campaign, dto, pausedMarketings} = input;\n\t\ttry {\n\t\t\treturn await this.marketingService.applyMarketing(shopId, campaign, dto);\n\t\t} catch (err) {\n\t\t\tawait this.marketingService.resumeMarketings(pausedMarketings);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate async fetchCurrentCampaignOrThrow(): Promise<DocumentType<LimitedTimeOfferCampaign>> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\t\tif (!campaign) {\n\t\t\tthrow badRequestError('No campaign active or scheduled.');\n\t\t}\n\t\treturn campaign;\n\t}\n\n\tprivate async assertNotAlreadyJoined(\n\t\tcampaignId: mongoose.Types.ObjectId,\n\t\tshopId: mongoose.Types.ObjectId\n\t): Promise<void> {\n\t\tif (await this.repository.existsActiveByCampaignAndShop(campaignId, shopId)) {\n\t\t\tthrow conflictError('Your shop has already joined this campaign.');\n\t\t}\n\t}\n\n\t/**\n\t * Expands the selected shop categories into their product ids and merges them with any explicitly\n\t * selected products. The marketing layer only ever receives a flat product list, so category\n\t * selection is purely an LTO-side convenience that is resolved here before anything is persisted.\n\t */\n\tprivate async resolveCategorySelection(\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t): Promise<JoinLtoCampaignDTO> {\n\t\tif (dto.isEntireMenu || !dto.shopCategories?.length) return dto;\n\n\t\tconst categoryProductIds = await this.repository.findProductIdsByCategories(shopId, dto.shopCategories);\n\t\tconst products = this.uniqueObjectIds([...(dto.products ?? []), ...categoryProductIds]);\n\n\t\tif (products.length === 0) {\n\t\t\tthrow badRequestError('The selected categories do not contain any products.');\n\t\t}\n\n\t\treturn { ...dto, products };\n\t}\n\n\t/**\n\t * Validates that explicitly selected products belong to the shop. Categories are not checked here:\n\t * {@link resolveCategorySelection} fetches their products with a shop-scoped query, so any category\n\t * that does not belong to the shop simply contributes no products.\n\t */\n\tprivate async assertProductsBelongToShop(\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t): Promise<void> {\n\t\tif (dto.isEntireMenu) return;\n\n\t\tconst productIds = this.uniqueObjectIds(dto.products ?? []);\n\t\tif (!productIds.length) return;\n\n\t\tconst owned = await this.repository.countShopProducts(shopId, productIds);\n\t\tif (owned !== productIds.length) {\n\t\t\tthrow badRequestError('One or more selected products do not belong to your shop.');\n\t\t}\n\t}\n\n\tprivate uniqueObjectIds(ids: mongoose.Types.ObjectId[]): mongoose.Types.ObjectId[] {\n\t\treturn [...new Map(ids.map(id => [id.toString(), id])).values()];\n\t}\n\n\tprivate validateDiscountSelections(input: {\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>;\n\t\tdiscountPercentage: number;\n\t\tmaxDiscountCap: number;\n\t\tparticipant?: ParticipantSummaryDTO;\n\t}): void {\n\t\tconst {campaign, discountPercentage, maxDiscountCap, participant} = input;\n\t\tif (!campaign.discountOptions.includes(discountPercentage)) {\n\t\t\tif (!participant || discountPercentage != participant.discountPercentage) {\n\t\t\t\tthrow badRequestError('Selected discount is not an available option for this campaign.');\n\t\t\t}\n\t\t}\n\t\tif (!campaign.maxDiscountCaps.includes(maxDiscountCap)) {\n\t\t\tif (!participant || maxDiscountCap != participant.maxDiscountCap) {\n\t\t\t\tthrow badRequestError('Selected max discount cap is not an available option for this campaign.');\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const ltoParticipantService = Container.get(LimitedTimeOfferParticipantService);\n"]}
1
+ {"version":3,"file":"limited-time-offer-participant.service.js","sourceRoot":"/","sources":["modules/limited-time-offer/participant/services/limited-time-offer-participant.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, {Service} from 'typedi';\nimport {DocumentType, mongoose} from '@typegoose/typegoose';\nimport {LimitedTimeOfferCampaign, Setting} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {LimitedTimeOfferCampaignStatus, MarketingType} from '@lyxa.ai/core/dist/utilities/enum';\nimport {badRequestError, conflictError, notFoundError} from '@lyxa.ai/core/dist/utilities/error-common';\nimport {LtoCampaignService} from '@modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service';\nimport {LtoParticipantMarketingService} from './limited-time-offer-participant-marketing.service';\nimport {\n\tAdminEnrollShopEntryDTO,\n\tJoinLtoCampaignDTO,\n\tLtoCardState,\n\tParticipantStatusDTO,\n\tParticipantSummaryDTO,\n} from '../limited-time-offer-participant.validation';\nimport {LtoParticipantQueryBuilder} from '../limited-time-offer-participant.builder';\nimport {LtoParticipantRepository} from '../limited-time-offer-participant.repository';\nimport {GetLtoCampaignParticipantsDTO} from '../../campaign/limited-time-offer-campaign.validation';\nimport {getLibraries} from '@lyxa.ai/core';\n\n@Service()\nexport class LimitedTimeOfferParticipantService {\n\tprivate repository: LtoParticipantRepository;\n\tprivate campaignService: LtoCampaignService;\n\tprivate marketingService: LtoParticipantMarketingService;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(LtoParticipantRepository);\n\t\tthis.campaignService = Container.get(LtoCampaignService);\n\t\tthis.marketingService = Container.get(LtoParticipantMarketingService);\n\t}\n\n\tpublic async join(shopId: mongoose.Types.ObjectId, dto: JoinLtoCampaignDTO) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\t\treturn this.joinShop(campaign, shopId, dto);\n\t}\n\n\tpublic async enrollShops(entries: AdminEnrollShopEntryDTO[]) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\t\treturn Promise.all(entries.map(({shopId, ...dto}) => this.joinShop(campaign, shopId, dto)));\n\t}\n\n\tprivate async joinShop(\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>,\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t) {\n\t\tawait this.assertNotAlreadyJoined(campaign._id, shopId);\n\t\tthis.validateDiscountSelections({campaign, discountPercentage: dto.discountPercentage, maxDiscountCap: dto.maxDiscountCap});\n\t\tawait this.assertProductsBelongToShop(shopId, dto);\n\n\t\tconst resolvedDto = await this.resolveCategorySelection(shopId, dto);\n\t\tconst pausedMarketings = await this.pauseIfLive(campaign, shopId);\n\n\t\tconst {marketingId, pausedMarketingIds} = await this.applyMarketingWithRollback({\n\t\t\tshopId,\n\t\t\tcampaign,\n\t\t\tdto: resolvedDto,\n\t\t\tpausedMarketings,\n\t\t});\n\t\tconst allPausedMarketings = this.uniqueObjectIds([...pausedMarketings, ...pausedMarketingIds]);\n\n\t\tconst participant = await this.repository.create(\n\t\t\tLtoParticipantQueryBuilder.buildCreateInput({\n\t\t\t\tcampaignId: campaign._id,\n\t\t\t\tshopId,\n\t\t\t\tmarketingId,\n\t\t\t\tdto: resolvedDto,\n\t\t\t\tpausedMarketings: allPausedMarketings,\n\t\t\t})\n\t\t);\n\t\tawait this.syncShopOrderCapacity(shopId, resolvedDto);\n\t\treturn participant;\n\t}\n\n\tprivate syncShopOrderCapacity(shopId: mongoose.Types.ObjectId, dto: JoinLtoCampaignDTO) {\n\t\treturn this.repository.updateShopOrderCapacity(shopId, dto.isOrderCapacityEnabled, dto.maxOrderCapacity);\n\t}\n\n\tprivate async pauseIfLive(\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>,\n\t\tshopId: mongoose.Types.ObjectId\n\t): Promise<mongoose.Types.ObjectId[]> {\n\t\tif (campaign.status !== LimitedTimeOfferCampaignStatus.ACTIVE) return [];\n\t\treturn this.marketingService.pauseActiveShopMarketings(shopId, MarketingType.DISCOUNT);\n\t}\n\n\tpublic async withdraw(shopId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\n\t\tconst participant = await this.repository.withdraw(campaign._id, shopId);\n\t\tif (!participant) throw notFoundError('You have not joined this campaign.');\n\n\t\tif (participant.marketing) {\n\t\t\tawait this.marketingService.removeMarketing(participant.marketing as mongoose.Types.ObjectId);\n\t\t}\n\t\tawait this.marketingService.resumeMarketings(\n\t\t\t(participant.pausedMarketings ?? []) as mongoose.Types.ObjectId[]\n\t\t);\n\t}\n\n\tpublic async cleanupCampaignMarketings(campaignId: mongoose.Types.ObjectId): Promise<void> {\n\t\tawait this.marketingService.removeAllCampaignMarketings(campaignId);\n\t\tconst pausedMarketingIds = await this.repository.findPausedMarketingIdsByCampaign(campaignId);\n\t\tawait this.marketingService.resumeMarketings(pausedMarketingIds);\n\t}\n\n\tpublic async isActiveParticipant(shopId: mongoose.Types.ObjectId): Promise<boolean> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\t\tif (!campaign) return false;\n\t\treturn this.repository.existsActiveByCampaignAndShop(campaign._id, shopId);\n\t}\n\n\tpublic async getStatus(shopId: mongoose.Types.ObjectId): Promise<ParticipantStatusDTO> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\n\t\tif (!campaign) return { campaignState: LtoCardState.NONE };\n\n\t\tconst participant = await this.repository.findActiveByCampaignAndShop(campaign._id, shopId);\n\n\t\tconst campaignState =\n\t\t\tcampaign.status === LimitedTimeOfferCampaignStatus.SCHEDULED\n\t\t\t\t? LtoCardState.SCHEDULED\n\t\t\t\t: campaign.status === LimitedTimeOfferCampaignStatus.ACTIVE\n\t\t\t\t\t? LtoCardState.LIVE\n\t\t\t\t\t: LtoCardState.NONE;\n\n\t\treturn participant\n\t\t\t? { campaignState, campaign: campaign as any, participant }\n\t\t\t: { campaignState, campaign: campaign as any };\n\t}\n\n\tpublic async update(shopId: mongoose.Types.ObjectId, dto: JoinLtoCampaignDTO) {\n\t\tconst campaign = await this.fetchCurrentCampaignOrThrow();\n\n\t\tconst participant = await this.repository.findActiveByCampaignAndShop(campaign._id, shopId);\n\t\tif (!participant) throw notFoundError('You have not joined this campaign.');\n\n\t\tthis.validateDiscountSelections({campaign, discountPercentage: dto.discountPercentage, maxDiscountCap: dto.maxDiscountCap, participant});\n\t\tawait this.assertProductsBelongToShop(shopId, dto);\n\n\t\tconst resolvedDto = await this.resolveCategorySelection(shopId, dto);\n\n\t\tif (participant.marketingId) {\n\t\t\tawait this.marketingService.removeMarketing(participant.marketingId);\n\t\t}\n\t\tconst {marketingId: newMarketingId} = await this.marketingService.applyMarketing(shopId, campaign, resolvedDto);\n\n\t\tconst updatedParticipant = await this.repository.updateParticipant(campaign._id, shopId, {\n\t\t\tproducts: resolvedDto.products ?? [],\n\t\t\tisEntireMenu: resolvedDto.isEntireMenu,\n\t\t\tdiscountPercentage: resolvedDto.discountPercentage,\n\t\t\tmaxDiscountCap: resolvedDto.maxDiscountCap,\n\t\t\tisOrderCapacityEnabled: resolvedDto.isOrderCapacityEnabled,\n\t\t\tmaxOrderCapacity: resolvedDto.maxOrderCapacity,\n\t\t\tmarketing: newMarketingId,\n\t\t});\n\t\tawait this.syncShopOrderCapacity(shopId, resolvedDto);\n\t\treturn updatedParticipant;\n\t}\n\n\tpublic async getParticipants(dto: GetLtoCampaignParticipantsDTO) {\n\t\tconst {documents, total} = await this.repository.findByCampaign(dto);\n\t\treturn {\n\t\t\tdocuments,\n\t\t\tmetadata: {page: dto.page, size: dto.size, totalElements: total, totalPages: Math.ceil(total / dto.size)},\n\t\t};\n\t}\n\n\tpublic async getParticipantIdsByCampaignId(campaignId: mongoose.Types.ObjectId) {\n\t\treturn await this.repository.findParticipantIdsByCampaignId(campaignId);\n\t}\n\n\tpublic async getNearby(\n\t\tcampaignId: mongoose.Types.ObjectId,\n\t\tinput: { latitude: number; longitude: number }\n\t) {\n\t\tconst settings = (await getLibraries().getCachedSettingsService().getSettings()) as Setting;\n\t\tconst nearByShopDistance = settings.nearByShopDistance || 10000;\n\t\treturn this.repository.findNearby(campaignId, input, nearByShopDistance);\n\t}\n\n\tpublic async reactivateMarketings(campaign: DocumentType<LimitedTimeOfferCampaign>): Promise<void> {\n\t\tconst participants = await this.repository.findAllActiveByCampaign(campaign._id);\n\n\t\tfor (const participant of participants) {\n\t\t\tconst dto: JoinLtoCampaignDTO = {\n\t\t\t\tisEntireMenu: participant.isEntireMenu,\n\t\t\t\tproducts: (participant.products ?? []) as mongoose.Types.ObjectId[],\n\t\t\t\tshopCategories: [],\n\t\t\t\tdiscountPercentage: participant.discountPercentage ?? 0,\n\t\t\t\tmaxDiscountCap: participant.maxDiscountCap ?? 0,\n\t\t\t\tisOrderCapacityEnabled: participant.isOrderCapacityEnabled,\n\t\t\t\tmaxOrderCapacity: participant.maxOrderCapacity,\n\t\t\t};\n\t\t\t// Drop any prior marketing (e.g. created when the shop joined between recurrences) so we\n\t\t\t// don't orphan it — it carries no campaign ref and would leak past cleanup.\n\t\t\tif (participant.marketing) {\n\t\t\t\tawait this.marketingService.removeMarketing(participant.marketing as mongoose.Types.ObjectId);\n\t\t\t}\n\t\t\tconst {marketingId} = await this.marketingService.applyMarketing(\n\t\t\t\tparticipant.shop as mongoose.Types.ObjectId,\n\t\t\t\tcampaign,\n\t\t\t\tdto,\n\t\t\t);\n\t\t\tawait this.repository.updateParticipant(\n\t\t\t\tcampaign._id,\n\t\t\t\tparticipant.shop as mongoose.Types.ObjectId,\n\t\t\t\t{...dto, marketing: marketingId},\n\t\t\t);\n\t\t\tawait this.syncShopOrderCapacity(participant.shop as mongoose.Types.ObjectId, dto);\n\t\t}\n\t}\n\n\tprivate async applyMarketingWithRollback(input: {\n\t\tshopId: mongoose.Types.ObjectId;\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>;\n\t\tdto: JoinLtoCampaignDTO;\n\t\tpausedMarketings: mongoose.Types.ObjectId[];\n\t}): Promise<{marketingId: mongoose.Types.ObjectId; pausedMarketingIds: mongoose.Types.ObjectId[]}> {\n\t\tconst {shopId, campaign, dto, pausedMarketings} = input;\n\t\ttry {\n\t\t\treturn await this.marketingService.applyMarketing(shopId, campaign, dto);\n\t\t} catch (err) {\n\t\t\tawait this.marketingService.resumeMarketings(pausedMarketings);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tprivate async fetchCurrentCampaignOrThrow(): Promise<DocumentType<LimitedTimeOfferCampaign>> {\n\t\tconst campaign = await this.campaignService.getCurrent();\n\t\tif (!campaign) {\n\t\t\tthrow badRequestError('No campaign active or scheduled.');\n\t\t}\n\t\treturn campaign;\n\t}\n\n\tprivate async assertNotAlreadyJoined(\n\t\tcampaignId: mongoose.Types.ObjectId,\n\t\tshopId: mongoose.Types.ObjectId\n\t): Promise<void> {\n\t\tif (await this.repository.existsActiveByCampaignAndShop(campaignId, shopId)) {\n\t\t\tthrow conflictError('Your shop has already joined this campaign.');\n\t\t}\n\t}\n\n\t/**\n\t * Expands the selected shop categories into their product ids and merges them with any explicitly\n\t * selected products. The marketing layer only ever receives a flat product list, so category\n\t * selection is purely an LTO-side convenience that is resolved here before anything is persisted.\n\t */\n\tprivate async resolveCategorySelection(\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t): Promise<JoinLtoCampaignDTO> {\n\t\tif (dto.isEntireMenu || !dto.shopCategories?.length) return dto;\n\n\t\tconst categoryProductIds = await this.repository.findProductIdsByCategories(shopId, dto.shopCategories);\n\t\tconst products = this.uniqueObjectIds([...(dto.products ?? []), ...categoryProductIds]);\n\n\t\tif (products.length === 0) {\n\t\t\tthrow badRequestError('The selected categories do not contain any products.');\n\t\t}\n\n\t\treturn { ...dto, products };\n\t}\n\n\t/**\n\t * Validates that explicitly selected products belong to the shop. Categories are not checked here:\n\t * {@link resolveCategorySelection} fetches their products with a shop-scoped query, so any category\n\t * that does not belong to the shop simply contributes no products.\n\t */\n\tprivate async assertProductsBelongToShop(\n\t\tshopId: mongoose.Types.ObjectId,\n\t\tdto: JoinLtoCampaignDTO\n\t): Promise<void> {\n\t\tif (dto.isEntireMenu) return;\n\n\t\tconst productIds = this.uniqueObjectIds(dto.products ?? []);\n\t\tif (!productIds.length) return;\n\n\t\tconst owned = await this.repository.countShopProducts(shopId, productIds);\n\t\tif (owned !== productIds.length) {\n\t\t\tthrow badRequestError('One or more selected products do not belong to your shop.');\n\t\t}\n\t}\n\n\tprivate uniqueObjectIds(ids: mongoose.Types.ObjectId[]): mongoose.Types.ObjectId[] {\n\t\treturn [...new Map(ids.map(id => [id.toString(), id])).values()];\n\t}\n\n\tprivate validateDiscountSelections(input: {\n\t\tcampaign: DocumentType<LimitedTimeOfferCampaign>;\n\t\tdiscountPercentage: number;\n\t\tmaxDiscountCap: number;\n\t\tparticipant?: ParticipantSummaryDTO;\n\t}): void {\n\t\tconst {campaign, discountPercentage, maxDiscountCap, participant} = input;\n\t\tif (!campaign.discountOptions.includes(discountPercentage)) {\n\t\t\tif (!participant || discountPercentage != participant.discountPercentage) {\n\t\t\t\tthrow badRequestError('Selected discount is not an available option for this campaign.');\n\t\t\t}\n\t\t}\n\t\tif (!campaign.maxDiscountCaps.includes(maxDiscountCap)) {\n\t\t\tif (!participant || maxDiscountCap != participant.maxDiscountCap) {\n\t\t\t\tthrow badRequestError('Selected max discount cap is not an available option for this campaign.');\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const ltoParticipantService = Container.get(LimitedTimeOfferParticipantService);\n"]}
@@ -9,6 +9,7 @@ export declare enum RewardTypeGroup {
9
9
  export declare const CONFLICT_CHECK_MARKETING_TYPES: MarketingType[];
10
10
  export interface ConflictCheckParams {
11
11
  shop: mongoose.Types.ObjectId;
12
+ marketingType: MarketingType;
12
13
  duration: Pick<Duration, 'start' | 'end'>;
13
14
  rewardTypeGroups: RewardTypeGroup[];
14
15
  excludeMarketingId?: mongoose.Types.ObjectId;
@@ -1 +1 @@
1
- {"version":3,"file":"marketing-conflict.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/marketing-conflict.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC;AACtE,OAAO,EAAmB,aAAa,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,iEAAiE,CAAC;AAI3F,oBAAY,eAAe;IAC1B,QAAQ,aAAa;IACrB,aAAa,kBAAkB;CAC/B;AAED,eAAO,MAAM,8BAA8B,iBAK1C,CAAC;AAKF,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC9B,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,CAAC;IAC1C,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;CAC7C;AAED,qBACa,wBAAwB;IACpC,OAAO,CAAC,UAAU,CAA8B;;IAUzC,mBAAmB,CACzB,aAAa,EAAE,aAAa,EAC5B,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,oBAAoB,CAAA;KAAE,EAAE,GAC5C,eAAe,EAAE;IAgCP,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;IA0BhF,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzE,OAAO,CAAC,gBAAgB;CAaxB;AAED,eAAO,MAAM,wBAAwB,0BAA0C,CAAC"}
1
+ {"version":3,"file":"marketing-conflict.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/marketing-conflict.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAC;AACtE,OAAO,EAAmB,aAAa,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzG,OAAO,EAAE,QAAQ,EAAE,MAAM,iEAAiE,CAAC;AAI3F,oBAAY,eAAe;IAC1B,QAAQ,aAAa;IACrB,aAAa,kBAAkB;CAC/B;AAED,eAAO,MAAM,8BAA8B,iBAK1C,CAAC;AAKF,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,CAAC;IAC1C,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC;CAC7C;AAED,qBACa,wBAAwB;IACpC,OAAO,CAAC,UAAU,CAA8B;;IAUzC,mBAAmB,CACzB,aAAa,EAAE,aAAa,EAC5B,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,oBAAoB,CAAA;KAAE,EAAE,GAC5C,eAAe,EAAE;IAoCP,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;IA8BhF,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzE,OAAO,CAAC,gBAAgB;CAaxB;AAED,eAAO,MAAM,wBAAwB,0BAA0C,CAAC"}
@@ -87,6 +87,7 @@ let MarketingConflictService = class MarketingConflictService {
87
87
  return [];
88
88
  const newStart = params.duration.start;
89
89
  const newEnd = params.duration.end ?? MAX_DATE;
90
+ const newIsSpendToGet = params.marketingType === enum_1.MarketingType.SPEND_TO_GET;
90
91
  const candidates = await this.repository.find({
91
92
  shop: params.shop,
92
93
  status: enum_1.MarketingStatus.ACTIVE,
@@ -96,6 +97,9 @@ let MarketingConflictService = class MarketingConflictService {
96
97
  $or: [{ 'duration.end': { $exists: false } }, { 'duration.end': { $gt: newStart } }],
97
98
  });
98
99
  return candidates.filter(candidate => {
100
+ const candidateIsSpendToGet = candidate.marketingType === enum_1.MarketingType.SPEND_TO_GET;
101
+ if (!newIsSpendToGet && !candidateIsSpendToGet)
102
+ return false;
99
103
  const candidateGroups = this.getRewardTypeGroups(candidate.marketingType, candidate.tiers);
100
104
  return candidateGroups.some(group => params.rewardTypeGroups.includes(group));
101
105
  });
@@ -1 +1 @@
1
- {"version":3,"file":"marketing-conflict.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing-conflict.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport { Marketing } from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport { MarketingStatus, MarketingType, SpendToGetRewardType } from '@lyxa.ai/core/dist/utilities/enum';\nimport { Duration } from '@lyxa.ai/core/dist/libraries/mongo/models/shared/duration.model';\nimport { conflictError } from '@lyxa.ai/core/dist/utilities/error-common';\nimport { MarketingConflictRepository } from './marketing-conflict.repository';\n\nexport enum RewardTypeGroup {\n\tDISCOUNT = 'discount',\n\tFREE_DELIVERY = 'free_delivery',\n}\n\nexport const CONFLICT_CHECK_MARKETING_TYPES = [\n\tMarketingType.DISCOUNT,\n\tMarketingType.BUY1GET1,\n\tMarketingType.FREE_DELIVERY,\n\tMarketingType.SPEND_TO_GET,\n];\n\n// Open-ended promotions (no duration.end) are treated as running indefinitely for overlap purposes.\nconst MAX_DATE = new Date('9999-12-31T23:59:59.999Z');\n\nexport interface ConflictCheckParams {\n\tshop: mongoose.Types.ObjectId;\n\tduration: Pick<Duration, 'start' | 'end'>;\n\trewardTypeGroups: RewardTypeGroup[];\n\texcludeMarketingId?: mongoose.Types.ObjectId;\n}\n\n@Service()\nexport class MarketingConflictService {\n\tprivate repository: MarketingConflictRepository;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(MarketingConflictRepository);\n\t}\n\n\t/**\n\t * A Spend-to-Get promotion's reward-type-group membership is the union of all its tiers'\n\t * reward types (a promotion with both a discount and a free-delivery tier belongs to both groups).\n\t */\n\tpublic getRewardTypeGroups(\n\t\tmarketingType: MarketingType,\n\t\ttiers?: { rewardType: SpendToGetRewardType }[]\n\t): RewardTypeGroup[] {\n\t\tif (marketingType === MarketingType.DISCOUNT || marketingType === MarketingType.BUY1GET1) {\n\t\t\treturn [RewardTypeGroup.DISCOUNT];\n\t\t}\n\n\t\tif (marketingType === MarketingType.FREE_DELIVERY) {\n\t\t\treturn [RewardTypeGroup.FREE_DELIVERY];\n\t\t}\n\n\t\tif (marketingType === MarketingType.SPEND_TO_GET) {\n\t\t\tconst groups = new Set<RewardTypeGroup>();\n\t\t\tfor (const tier of tiers ?? []) {\n\t\t\t\tgroups.add(\n\t\t\t\t\ttier.rewardType === SpendToGetRewardType.FREE_DELIVERY\n\t\t\t\t\t\t? RewardTypeGroup.FREE_DELIVERY\n\t\t\t\t\t\t: RewardTypeGroup.DISCOUNT\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Array.from(groups);\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Finds other active promotions on the same shop whose date range overlaps `duration` and whose\n\t * reward-type-group membership intersects `rewardTypeGroups`. Strict `<`/`>` comparisons mean\n\t * back-to-back dates (one ends 23:59, the next starts 00:00) are not treated as overlapping.\n\t */\n\t// ponytail: check-then-write, not transactional — two same-shop activations racing within the\n\t// same millisecond could both pass. Upgrade to a per-shop lock document + mongoose session if\n\t// concurrent admin activation ever becomes frequent enough to matter.\n\tpublic async findConflicting(params: ConflictCheckParams): Promise<DocumentType<Marketing>[]> {\n\t\tif (params.rewardTypeGroups.length === 0) return [];\n\n\t\tconst newStart = params.duration.start;\n\t\tconst newEnd = params.duration.end ?? MAX_DATE;\n\n\t\tconst candidates = await this.repository.find({\n\t\t\tshop: params.shop,\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tmarketingType: { $in: CONFLICT_CHECK_MARKETING_TYPES },\n\t\t\t...(params.excludeMarketingId && { _id: { $ne: params.excludeMarketingId } }),\n\t\t\t'duration.start': { $lt: newEnd },\n\t\t\t$or: [{ 'duration.end': { $exists: false } }, { 'duration.end': { $gt: newStart } }],\n\t\t});\n\n\t\treturn candidates.filter(candidate => {\n\t\t\tconst candidateGroups = this.getRewardTypeGroups(candidate.marketingType, (candidate as any).tiers);\n\t\t\treturn candidateGroups.some(group => params.rewardTypeGroups.includes(group));\n\t\t});\n\t}\n\n\t/**\n\t * Throws a CONFLICT error describing the existing blocking promotion. The message identifies\n\t * whether the blocker is a Spend-to-Get promotion or a standalone one and which reward type\n\t * (Free Delivery vs discount) actually overlaps.\n\t */\n\tpublic async assertNoConflict(params: ConflictCheckParams): Promise<void> {\n\t\tconst conflicts = await this.findConflicting(params);\n\t\tif (conflicts.length === 0) return;\n\n\t\tconst messages = new Set<string>();\n\t\tfor (const conflict of conflicts) {\n\t\t\tfor (const group of this.getRewardTypeGroups(conflict.marketingType, (conflict as any).tiers)) {\n\t\t\t\tif (params.rewardTypeGroups.includes(group)) {\n\t\t\t\t\tmessages.add(this.describeConflict(conflict.marketingType, group));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconflictError(Array.from(messages).join(' '));\n\t}\n\n\tprivate describeConflict(existingType: MarketingType, group: RewardTypeGroup): string {\n\t\tconst isFreeDelivery = group === RewardTypeGroup.FREE_DELIVERY;\n\n\t\tif (existingType === MarketingType.SPEND_TO_GET) {\n\t\t\treturn isFreeDelivery\n\t\t\t\t? 'This shop already has an active Spend to Get promotion offering Free Delivery for these dates.'\n\t\t\t\t: 'This shop already has an active Spend to Get promotion offering a discount for these dates.';\n\t\t}\n\n\t\treturn isFreeDelivery\n\t\t\t? 'This shop already has an active Free Delivery promotion for these dates.'\n\t\t\t: 'This shop already has an active Discount/B1G1 promotion for these dates.';\n\t}\n}\n\nexport const marketingConflictService = Container.get(MarketingConflictService);\n"]}
1
+ {"version":3,"file":"marketing-conflict.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing-conflict.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport { Marketing } from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport { MarketingStatus, MarketingType, SpendToGetRewardType } from '@lyxa.ai/core/dist/utilities/enum';\nimport { Duration } from '@lyxa.ai/core/dist/libraries/mongo/models/shared/duration.model';\nimport { conflictError } from '@lyxa.ai/core/dist/utilities/error-common';\nimport { MarketingConflictRepository } from './marketing-conflict.repository';\n\nexport enum RewardTypeGroup {\n\tDISCOUNT = 'discount',\n\tFREE_DELIVERY = 'free_delivery',\n}\n\nexport const CONFLICT_CHECK_MARKETING_TYPES = [\n\tMarketingType.DISCOUNT,\n\tMarketingType.BUY1GET1,\n\tMarketingType.FREE_DELIVERY,\n\tMarketingType.SPEND_TO_GET,\n];\n\n// Open-ended promotions (no duration.end) are treated as running indefinitely for overlap purposes.\nconst MAX_DATE = new Date('9999-12-31T23:59:59.999Z');\n\nexport interface ConflictCheckParams {\n\tshop: mongoose.Types.ObjectId;\n\tmarketingType: MarketingType;\n\tduration: Pick<Duration, 'start' | 'end'>;\n\trewardTypeGroups: RewardTypeGroup[];\n\texcludeMarketingId?: mongoose.Types.ObjectId;\n}\n\n@Service()\nexport class MarketingConflictService {\n\tprivate repository: MarketingConflictRepository;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(MarketingConflictRepository);\n\t}\n\n\t/**\n\t * A Spend-to-Get promotion's reward-type-group membership is the union of all its tiers'\n\t * reward types (a promotion with both a discount and a free-delivery tier belongs to both groups).\n\t */\n\tpublic getRewardTypeGroups(\n\t\tmarketingType: MarketingType,\n\t\ttiers?: { rewardType: SpendToGetRewardType }[]\n\t): RewardTypeGroup[] {\n\t\tif (marketingType === MarketingType.DISCOUNT || marketingType === MarketingType.BUY1GET1) {\n\t\t\treturn [RewardTypeGroup.DISCOUNT];\n\t\t}\n\n\t\tif (marketingType === MarketingType.FREE_DELIVERY) {\n\t\t\treturn [RewardTypeGroup.FREE_DELIVERY];\n\t\t}\n\n\t\tif (marketingType === MarketingType.SPEND_TO_GET) {\n\t\t\tconst groups = new Set<RewardTypeGroup>();\n\t\t\tfor (const tier of tiers ?? []) {\n\t\t\t\tgroups.add(\n\t\t\t\t\ttier.rewardType === SpendToGetRewardType.FREE_DELIVERY\n\t\t\t\t\t\t? RewardTypeGroup.FREE_DELIVERY\n\t\t\t\t\t\t: RewardTypeGroup.DISCOUNT\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Array.from(groups);\n\t\t}\n\n\t\treturn [];\n\t}\n\n\t/**\n\t * Finds other active promotions on the same shop whose date range overlaps `duration` and whose\n\t * reward-type-group membership intersects `rewardTypeGroups`. Strict `<`/`>` comparisons mean\n\t * back-to-back dates (one ends 23:59, the next starts 00:00) are not treated as overlapping.\n\t *\n\t * The exclusivity rule applies only to pairings that involve a Spend-to-Get promotion. Two\n\t * standalone promotions (e.g. an admin Discount and a shop Discount) are allowed to run\n\t * concurrently and are intentionally never flagged here.\n\t */\n\t// ponytail: check-then-write, not transactional — two same-shop activations racing within the\n\t// same millisecond could both pass. Upgrade to a per-shop lock document + mongoose session if\n\t// concurrent admin activation ever becomes frequent enough to matter.\n\tpublic async findConflicting(params: ConflictCheckParams): Promise<DocumentType<Marketing>[]> {\n\t\tif (params.rewardTypeGroups.length === 0) return [];\n\n\t\tconst newStart = params.duration.start;\n\t\tconst newEnd = params.duration.end ?? MAX_DATE;\n\t\tconst newIsSpendToGet = params.marketingType === MarketingType.SPEND_TO_GET;\n\n\t\tconst candidates = await this.repository.find({\n\t\t\tshop: params.shop,\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tmarketingType: { $in: CONFLICT_CHECK_MARKETING_TYPES },\n\t\t\t...(params.excludeMarketingId && { _id: { $ne: params.excludeMarketingId } }),\n\t\t\t'duration.start': { $lt: newEnd },\n\t\t\t$or: [{ 'duration.end': { $exists: false } }, { 'duration.end': { $gt: newStart } }],\n\t\t});\n\n\t\treturn candidates.filter(candidate => {\n\t\t\tconst candidateIsSpendToGet = candidate.marketingType === MarketingType.SPEND_TO_GET;\n\t\t\tif (!newIsSpendToGet && !candidateIsSpendToGet) return false;\n\n\t\t\tconst candidateGroups = this.getRewardTypeGroups(candidate.marketingType, (candidate as any).tiers);\n\t\t\treturn candidateGroups.some(group => params.rewardTypeGroups.includes(group));\n\t\t});\n\t}\n\n\t/**\n\t * Throws a CONFLICT error describing the existing blocking promotion. The message identifies\n\t * whether the blocker is a Spend-to-Get promotion or a standalone one and which reward type\n\t * (Free Delivery vs discount) actually overlaps.\n\t */\n\tpublic async assertNoConflict(params: ConflictCheckParams): Promise<void> {\n\t\tconst conflicts = await this.findConflicting(params);\n\t\tif (conflicts.length === 0) return;\n\n\t\tconst messages = new Set<string>();\n\t\tfor (const conflict of conflicts) {\n\t\t\tfor (const group of this.getRewardTypeGroups(conflict.marketingType, (conflict as any).tiers)) {\n\t\t\t\tif (params.rewardTypeGroups.includes(group)) {\n\t\t\t\t\tmessages.add(this.describeConflict(conflict.marketingType, group));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconflictError(Array.from(messages).join(' '));\n\t}\n\n\tprivate describeConflict(existingType: MarketingType, group: RewardTypeGroup): string {\n\t\tconst isFreeDelivery = group === RewardTypeGroup.FREE_DELIVERY;\n\n\t\tif (existingType === MarketingType.SPEND_TO_GET) {\n\t\t\treturn isFreeDelivery\n\t\t\t\t? 'This shop already has an active Spend to Get promotion offering Free Delivery for these dates.'\n\t\t\t\t: 'This shop already has an active Spend to Get promotion offering a discount for these dates.';\n\t\t}\n\n\t\treturn isFreeDelivery\n\t\t\t? 'This shop already has an active Free Delivery promotion for these dates.'\n\t\t\t: 'This shop already has an active Discount/B1G1 promotion for these dates.';\n\t}\n}\n\nexport const marketingConflictService = Container.get(MarketingConflictService);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"marketing.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAmB,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EAMjB,SAAS,IAAI,KAAK,EAOlB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEN,0BAA0B,EAE1B,oCAAoC,EACpC,kBAAkB,IAAI,SAAS,EAC/B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACN,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AA+C3D,qBACa,gBAAgB;IAC5B,OAAO,CAAC,KAAK,CAAiD;IAC9D,OAAO,CAAC,SAAS,CAA2C;IAC5D,OAAO,CAAC,UAAU,CAA2D;IAC7E,OAAO,CAAC,0BAA0B,CAA6B;IAC/D,OAAO,CAAC,gCAAgC,CACM;;IAgBjC,MAAM,CAClB,IAAI,EAAE,0BAA0B,EAChC,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC/B,OAAO,CAAC,EAAE;QAAE,yBAAyB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IA6JlB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAsB7D,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IA0B3F,MAAM,CAClB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC5B,IAAI,EAAE,SAAS,EACf,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAC7B,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAqIjB,uBAAuB;YA8BvB,uBAAuB;IAmBxB,UAAU,CACtB,IAAI,EAAE,SAAS,EACf,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC/B,OAAO,CAAC,EAAE;QAAE,yBAAyB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,MAAM,CAAC;IAkDL,eAAe,CAAC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,UAAU,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC1F,sCAAsC,CAAC,IAAI,EAAE,oCAAoC;IA8BjF,gCAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAiChF,gCAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;YAiC/E,oCAAoC;YAgBpC,kCAAkC;IAgBnC,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC;YAqCjD,2BAA2B;IA2B5B,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9E,yBAAyB,CAAC,SAAS,EAAE,YAAY,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;CAqCrG;AAED,eAAO,MAAM,gBAAgB,kBAAkC,CAAC"}
1
+ {"version":3,"file":"marketing.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAmB,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EAMjB,SAAS,IAAI,KAAK,EAOlB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEN,0BAA0B,EAE1B,oCAAoC,EACpC,kBAAkB,IAAI,SAAS,EAC/B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACN,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AA+C3D,qBACa,gBAAgB;IAC5B,OAAO,CAAC,KAAK,CAAiD;IAC9D,OAAO,CAAC,SAAS,CAA2C;IAC5D,OAAO,CAAC,UAAU,CAA2D;IAC7E,OAAO,CAAC,0BAA0B,CAA6B;IAC/D,OAAO,CAAC,gCAAgC,CACM;;IAgBjC,MAAM,CAClB,IAAI,EAAE,0BAA0B,EAChC,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC/B,OAAO,CAAC,EAAE;QAAE,yBAAyB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IA+JlB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAsB7D,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IA0B3F,MAAM,CAClB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC5B,IAAI,EAAE,SAAS,EACf,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAC7B,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YAqIjB,uBAAuB;YA+BvB,uBAAuB;IAmBxB,UAAU,CACtB,IAAI,EAAE,SAAS,EACf,KAAK,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAC/B,OAAO,CAAC,EAAE;QAAE,yBAAyB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/C,OAAO,CAAC,MAAM,CAAC;IAkDL,eAAe,CAAC,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,UAAU,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC1F,sCAAsC,CAAC,IAAI,EAAE,oCAAoC;IA8BjF,gCAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAiChF,gCAAgC,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;YAiC/E,oCAAoC;YAgBpC,kCAAkC;IAgBnC,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC;YAqCjD,2BAA2B;IA2B5B,yBAAyB,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9E,yBAAyB,CAAC,SAAS,EAAE,YAAY,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;CAqCrG;AAED,eAAO,MAAM,gBAAgB,kBAAkC,CAAC"}
@@ -91,6 +91,7 @@ let MarketingService = class MarketingService {
91
91
  status: enum_1.MarketingStatus.ACTIVE,
92
92
  isAppliedByCompany: data.isAppliedByCompany,
93
93
  campaignType: data.campaignType,
94
+ admin: admin ? { $ne: null } : null,
94
95
  });
95
96
  if (existingRecord) {
96
97
  throw new server_1.TRPCError({
@@ -101,6 +102,7 @@ let MarketingService = class MarketingService {
101
102
  if (_1.CONFLICT_CHECK_MARKETING_TYPES.includes(data.marketingType)) {
102
103
  await _1.marketingConflictService.assertNoConflict({
103
104
  shop: data.shop,
105
+ marketingType: data.marketingType,
104
106
  duration: data.duration,
105
107
  rewardTypeGroups: _1.marketingConflictService.getRewardTypeGroups(data.marketingType, data.tiers),
106
108
  });
@@ -339,6 +341,7 @@ let MarketingService = class MarketingService {
339
341
  if (_1.CONFLICT_CHECK_MARKETING_TYPES.includes(marketing.marketingType)) {
340
342
  await _1.marketingConflictService.assertNoConflict({
341
343
  shop: marketing.shop,
344
+ marketingType: marketing.marketingType,
342
345
  duration: marketing.duration,
343
346
  rewardTypeGroups: _1.marketingConflictService.getRewardTypeGroups(marketing.marketingType, marketing.tiers),
344
347
  excludeMarketingId: marketingId,
@@ -1 +1 @@
1
- {"version":3,"file":"marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { dayjs } from '@lyxa.ai/core/dist/utilities/dayjs';\nimport { getLibraries } from '@lyxa.ai/core';\nimport { DocumentType, mongoose, ReturnModelType } from '@typegoose/typegoose';\nimport {\n\tBuy1Get1Marketing,\n\tDiscountMarketing,\n\tLimitedTimeOfferParticipant,\n\tLimitedTimeOfferParticipantModel,\n\tLineItem,\n\tMarketing,\n\tMarketingModel,\n\tMarketing as Model,\n\tProductModel,\n\tProductMarketingModel,\n\tRegularOrder,\n\tRegularOrderModel,\n\tShop,\n\tShopModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {\n\tAllMarketingsDTO,\n\tAllMarketingsValidationDTO,\n\tCreateProductMarketingDTO,\n\tProductEntireMenuMarketingHandlerDTO,\n\tUpdateMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport {\n\tFilterDTO,\n\tPaginatedResponse,\n\tDeleteDTO,\n\tGetByIdInputDTO,\n} from '@lyxa.ai/core/dist/utilities/validation';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { createRawDocumentPaginatorObject, PaginatorOptions } from '@lyxa.ai/core/dist/utilities/pagination';\nimport { ModelType } from '@typegoose/typegoose/lib/types';\nimport {\n\tfilterRelevantChanges,\n\tgetChangedFields,\n\tpublishSyncSingleShopSectionsInCacheProcessEvent,\n} from '@lyxa.ai/core/dist/utilities/events-common-methods';\nimport { AdminActivityLogsEvent } from '@lyxa.ai/core/dist/libraries/event/events/admin-logs-event';\nimport {\n\tAdminLogsUpdatedFieldsType, MarketingCampaignType,\n\tMarketingStatus,\n\tRegularOrderStatus,\n} from '@lyxa.ai/core/dist/utilities/enum';\nimport { publishEvent } from '@lyxa.ai/core/dist/libraries/event';\nimport { BaseEvent } from '@lyxa.ai/core/dist/libraries/event/BaseEvent';\nimport {\n\tMarketingEntireMenuSeedEvent,\n\tMarketingProductSeedPayload,\n} from '@events/events/marketing-product-seed.event';\nimport { MarketingProductSyncEvent } from '@events/events/marketing-product-sync.event';\nimport {\n\tbuy1Get1MarketingService,\n\tdiscountMarketingService,\n\tfeaturedMarketingService,\n\tfreeDeliveryMarketingService,\n\tproductMarketingService,\n\tpunchMarketingService,\n\tspendToGetMarketingService,\n\tsynchronizeProductMarketingService,\n\tmarketingConflictService,\n\tCONFLICT_CHECK_MARKETING_TYPES,\n} from '.';\nimport { MarketingType } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tconflictError,\n\tforbiddenError,\n\tinternalServerError,\n\tnotFoundError,\n\tunauthorizedError,\n} from '@lyxa.ai/core/dist/utilities/error-common';\nimport { ChangeStreamOperationTypes } from '@lyxa.ai/core/dist/libraries/event/events/mongo-stream-changes-batch-process-shops-event';\nimport {\n\tMarketingMetricsRepository,\n\tmarketingMetricsRepository,\n} from '../patterns/repository/MarketingMetricsRepository';\nimport { scheduleMarketingProductSync } from '../queues/marketing-product-sync.queue';\nimport { ltoParticipantService } from '@modules/limited-time-offer/participant/services/limited-time-offer-participant.service';\n\n@Service()\nexport class MarketingService {\n\tprivate model: typeof MarketingModel & SoftDeleteModel<Model>;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\tprivate marketingMetricsRepository: MarketingMetricsRepository;\n\tprivate limitedTimeOfferParticipantModel: typeof LimitedTimeOfferParticipantModel &\n\t\tSoftDeleteModel<LimitedTimeOfferParticipant>;\n\n\tconstructor() {\n\t\tthis.model = MarketingModel as typeof MarketingModel & SoftDeleteModel<Model>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t\tthis.marketingMetricsRepository = marketingMetricsRepository;\n\t\tthis.limitedTimeOfferParticipantModel =\n\t\t\tLimitedTimeOfferParticipantModel as typeof LimitedTimeOfferParticipantModel &\n\t\t\t\tSoftDeleteModel<LimitedTimeOfferParticipant>;\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(\n\t\tdata: AllMarketingsValidationDTO,\n\t\tadmin?: mongoose.Types.ObjectId,\n\t\toptions?: { skipLimitedTimeOfferGuard?: boolean }\n\t): Promise<DocumentType<Model>> {\n\t\tconst now = dayjs().toDate();\n\n\t\tif (\n\t\t\t!options?.skipLimitedTimeOfferGuard &&\n\t\t\tdata.marketingType === MarketingType.DISCOUNT &&\n\t\t\t(await ltoParticipantService.isActiveParticipant(data.shop))\n\t\t) {\n\t\t\tforbiddenError(\n\t\t\t\t'You are currently participating in a Limited Time Offer. Please manage your discount from the Limited Time Offer section or withdraw first.'\n\t\t\t);\n\t\t}\n\n\t\tconst existingRecord = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: data.marketingType,\n\t\t\t'duration.end': { $gt: now },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisAppliedByCompany: data.isAppliedByCompany,\n\t\t\tcampaignType: data.campaignType,\n\t\t});\n\n\t\tif (existingRecord) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'CONFLICT',\n\t\t\t\tmessage: `This shop already has a ${data.marketingType} marketing`,\n\t\t\t});\n\t\t}\n\n\t\tif (CONFLICT_CHECK_MARKETING_TYPES.includes(data.marketingType)) {\n\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\tshop: data.shop,\n\t\t\t\tduration: data.duration,\n\t\t\t\trewardTypeGroups: marketingConflictService.getRewardTypeGroups(data.marketingType, data.tiers),\n\t\t\t});\n\t\t}\n\n\t\tif (data.isEntireMenu) {\n\t\t\tconst existingMarketing = await this.model.findOne({\n\t\t\t\tshop: data.shop,\n\t\t\t\t'duration.start': { $lt: now },\n\t\t\t\t'duration.end': { $gt: now },\n\t\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT], $ne: data.marketingType },\n\t\t\t});\n\n\t\t\tif (existingMarketing) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Cannot create marketing for entire menu while another product based marketing exists for the shop`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (data.products && data.products.length > 0) {\n\t\t\tconst productIds = data.products.map((product: CreateProductMarketingDTO) => product.product);\n\n\t\t\tconst hasDuplicates = new Set(productIds.map(String)).size !== productIds.length;\n\n\t\t\tif (hasDuplicates) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t\tmessage: `Product list contains duplicate products`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (data.campaignType === MarketingCampaignType.LIMITED_TIME_OFFER) {\n\t\t\t\tawait productMarketingService.pauseConflictingMarketings(productIds);\n\t\t\t} else {\n\t\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(\n\t\t\t\t\tproductIds,\n\t\t\t\t\tdata.isAppliedByCompany\n\t\t\t\t);\n\n\t\t\t\tif (!isAvailable) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\t\tmessage: `Product list contains products which already have marketing applied`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (admin) {\n\t\t\tdata.admin = admin;\n\t\t}\n\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tswitch (data.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'UNAUTHORIZED',\n\t\t\t\t\t\tmessage: `Only admin is allowed to create ${data.marketingType} marketing`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.SPEND_TO_GET: {\n\t\t\t\tmarketing = await spendToGetMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model.create(data, admin);\n\t\t\t}\n\t\t}\n\n\t\tawait this.assignMarketingToProducts(marketing);\n\n\t\tawait this.model.populate(marketing, {\n\t\t\tpath: 'shop',\n\t\t\tmodel: this.shopModel,\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.ACTIVATED_MARKETING_PROMOTIONS,\n\t\t\t\t'',\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\t\tif (marketing && !data.isEntireMenu)\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId: data.shop,\n\t\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t\t});\n\t\treturn marketing;\n\t}\n\n\t/**\n\t * Get record by ID\n\t * @param id ID of the record\n\t */\n\tpublic async findById(data: GetByIdInputDTO): Promise<DocumentType<Model>> {\n\t\tconst select = data.select || '';\n\t\tconst populate = data.populate || [];\n\t\tconst result = await this.model\n\t\t\t.findOne({\n\t\t\t\t_id: data._id,\n\t\t\t})\n\t\t\t.select(select)\n\t\t\t.populate(populate);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get records\n\t * @param query Query to find records\n\t */\n\tpublic async find(data?: Partial<FilterDTO>): Promise<PaginatedResponse<DocumentType<ModelType<Model>>>> {\n\t\ttry {\n\t\t\tconst paginate = createRawDocumentPaginatorObject(this.model as ReturnModelType<typeof this.model>);\n\t\t\tconst options: PaginatorOptions<typeof Model> = {\n\t\t\t\tpage: data?.page,\n\t\t\t\tsize: data?.size,\n\t\t\t\tsort: data?.sort,\n\t\t\t\tquery: data?.query,\n\t\t\t\tselect: data?.select,\n\t\t\t\tpopulate: data?.populate,\n\t\t\t};\n\t\t\tconst paginatedResult = await paginate(options);\n\t\t\treturn paginatedResult;\n\t\t} catch (err) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: `err: ${err}`,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(\n\t\t_id: mongoose.Types.ObjectId,\n\t\tdata: UpdateDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tconst partOfLimitedTimeOffer = await this.limitedTimeOfferParticipantModel.exists({\n\t\t\tmarketing: _id,\n\t\t});\n\n\t\tif (partOfLimitedTimeOffer) {\n\t\t\tforbiddenError(\n\t\t\t\t'You are currently participating in a Limited Time Offer. Please make your edits from the Limited Time Offer section.'\n\t\t\t);\n\t\t}\n\n\t\tconst oldMarketing = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!oldMarketing) {\n\t\t\tnotFoundError('Could not find marketing');\n\t\t}\n\n\t\tif (data.status && data.status === MarketingStatus.ACTIVE && oldMarketing.status !== MarketingStatus.ACTIVE) {\n\t\t\tif (\n\t\t\t\toldMarketing.status === MarketingStatus.PAUSED &&\n\t\t\t\toldMarketing.marketingType === MarketingType.DISCOUNT &&\n\t\t\t\t(await ltoParticipantService.isActiveParticipant(oldMarketing.shop._id))\n\t\t\t) {\n\t\t\t\tforbiddenError(\n\t\t\t\t\t'You are currently participating in a Limited Time Offer. Please manage your discount from the Limited Time Offer section or withdraw first.'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait this.verifyUnpauseConditions(_id);\n\t\t}\n\n\t\tswitch (oldMarketing.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tunauthorizedError(`Only admin is allowed to update ${data.marketingType} marketing`);\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.SPEND_TO_GET: {\n\t\t\t\tmarketing = await spendToGetMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model\n\t\t\t\t\t.findOneAndUpdate(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_id: _id,\n\t\t\t\t\t\t\tdeletedAt: null,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnew: true,\n\t\t\t\t\t\t\trunValidators: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t\t.populate({\n\t\t\t\t\t\tpath: 'shop',\n\t\t\t\t\t\tselect: '_id name shopId',\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (!marketing) {\n\t\t\tinternalServerError('Could not update marketing');\n\t\t}\n\n\t\tif (admin) {\n\t\t\tconst allChanges = getChangedFields(oldMarketing, marketing);\n\t\t\tconst relevantChanges = filterRelevantChanges(allChanges, data);\n\n\t\t\tif (Object.keys(relevantChanges).includes('status')) {\n\t\t\t\tif (data.status == MarketingStatus.ACTIVE) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.CONTINUED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t} else if (data.status == MarketingStatus.PAUSED) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.PAUSED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.EDITED_MARKETING_PROMOTIONS,\n\t\t\t\toldMarketing,\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\n\t\tawait this.assignMarketingToProducts(marketing);\n\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: marketing.shop,\n\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t});\n\n\t\treturn marketing;\n\t}\n\n\tprivate async verifyUnpauseConditions(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null }).populate('products');\n\t\tif (!marketing) return;\n\n\t\tif ([MarketingType.BUY1GET1, MarketingType.DISCOUNT].includes(marketing.marketingType)) {\n\t\t\tconst typedMarketing = marketing as DocumentType<DiscountMarketing | Buy1Get1Marketing>;\n\t\t\tconst productIds = typedMarketing.products.map((product: any) => product.product);\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(\n\t\t\t\tproductIds,\n\t\t\t\tmarketing.isAppliedByCompany\n\t\t\t);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tconflictError(\"You can't activate discount and B1G1 on the same items at the same time\");\n\t\t\t}\n\t\t}\n\n\t\tif (CONFLICT_CHECK_MARKETING_TYPES.includes(marketing.marketingType)) {\n\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\tshop: marketing.shop as mongoose.Types.ObjectId,\n\t\t\t\tduration: marketing.duration,\n\t\t\t\trewardTypeGroups: marketingConflictService.getRewardTypeGroups(\n\t\t\t\t\tmarketing.marketingType,\n\t\t\t\t\t(marketing as any).tiers\n\t\t\t\t),\n\t\t\t\texcludeMarketingId: marketingId,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async logMarketingAdminAction(\n\t\tadmin: mongoose.Types.ObjectId,\n\t\tactionType: AdminLogsUpdatedFieldsType,\n\t\toldDocument?: any,\n\t\tnewDocument?: any\n\t) {\n\t\tconst adminActivityLogsEvent: BaseEvent<Record<string, unknown>> = new AdminActivityLogsEvent({\n\t\t\tadmin,\n\t\t\tupdatedField: actionType,\n\t\t\toldDocument: oldDocument ?? '',\n\t\t\tnewDocument: newDocument ?? '',\n\t\t});\n\t\tawait publishEvent(adminActivityLogsEvent);\n\t}\n\n\t/**\n\t * Delete record\n\t * @param data Data for delete\n\t */\n\tpublic async deleteById(\n\t\tdata: DeleteDTO,\n\t\tadmin?: mongoose.Types.ObjectId,\n\t\toptions?: { skipLimitedTimeOfferGuard?: boolean }\n\t): Promise<string> {\n\t\tconst { _id, softDelete } = data;\n\n\t\tif (!options?.skipLimitedTimeOfferGuard) {\n\t\t\tconst partOfLimitedTimeOffer = await this.limitedTimeOfferParticipantModel.exists({\n\t\t\t\tmarketing: _id,\n\t\t\t});\n\n\t\t\tif (partOfLimitedTimeOffer) {\n\t\t\t\tforbiddenError(\n\t\t\t\t\t'You are currently participating in a Limited Time Offer. Please make your edits from the Limited Time Offer section.'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst record = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\n\t\trecord.status = MarketingStatus.DELETED;\n\t\tawait record.save();\n\n\t\tconst result = await this.model.deleteRecord({ _id, deletedAt: null }, softDelete);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: 'Unable to delete marketing',\n\t\t\t});\n\t\t}\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(admin, AdminLogsUpdatedFieldsType.DELETED_MARKETING_PROMOTIONS, record);\n\t\t}\n\n\t\tproductMarketingService.deleteMany({ marketing: data._id }, data.softDelete);\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: record.shop._id,\n\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t});\n\t\treturn 'Record deleted successfully';\n\t}\n\n\tpublic async deleteManyByIds(marketingIds: mongoose.Types.ObjectId[], softDelete = true): Promise<void> {\n\t\tif (marketingIds.length === 0) return;\n\n\t\tconst marketings = await this.model\n\t\t\t.find({ _id: { $in: marketingIds } })\n\t\t\t.select('shop')\n\t\t\t.lean();\n\n\t\tawait this.model.updateMany(\n\t\t\t{ _id: { $in: marketingIds } },\n\t\t\t{ $set: { status: MarketingStatus.DELETED } }\n\t\t);\n\t\tawait this.model.deleteRecords({ _id: { $in: marketingIds } }, softDelete);\n\n\t\tawait productMarketingService.deleteManyByMarketingIds(marketingIds, softDelete);\n\n\t\tconst uniqueShopIds = [\n\t\t\t...new Map(\n\t\t\t\tmarketings\n\t\t\t\t\t.map((m: any) => m.shop)\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.map((s: any) => {\n\t\t\t\t\t\tconst id = s instanceof mongoose.Types.ObjectId ? s : new mongoose.Types.ObjectId(s);\n\t\t\t\t\t\treturn [id.toString(), id] as [string, mongoose.Types.ObjectId];\n\t\t\t\t\t})\n\t\t\t).values(),\n\t\t];\n\n\t\tfor (const shopId of uniqueShopIds) {\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId,\n\t\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic async handleNewProductForEntireMenuMarketing(data: ProductEntireMenuMarketingHandlerDTO) {\n\t\tconst existingMarketing = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t'duration.end': { $gt: new Date() },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisEntireMenu: true,\n\t\t});\n\n\t\t// check if existing marketing\n\t\tif (existingMarketing) {\n\t\t\tconst productMarketingData: CreateProductMarketingDTO = {\n\t\t\t\tproduct: data.product,\n\t\t\t\tmarketing: existingMarketing._id,\n\t\t\t};\n\t\t\tif (existingMarketing.marketingType == MarketingType.BUY1GET1) {\n\t\t\t\tproductMarketingData.isBuy1Get1 = true;\n\t\t\t}\n\t\t\tif (existingMarketing.marketingType == MarketingType.DISCOUNT) {\n\t\t\t\tproductMarketingData.value = (existingMarketing as DocumentType<DiscountMarketing>).value;\n\t\t\t\tproductMarketingData.valueType = (existingMarketing as DocumentType<DiscountMarketing>).valueType;\n\t\t\t}\n\n\t\t\tawait productMarketingService.create(productMarketingData);\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByProductId(data.product);\n\t\t}\n\n\t\treturn 'Product handled successfully';\n\t}\n\n\tpublic async incrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate([\n\t\t\t\t{ path: 'lineItems' },\n\t\t\t\t{ path: 'marketings', select: 'isAppliedByCompany' },\n\t\t\t])) as DocumentType<RegularOrder> & {\n\t\t\tlineItems: DocumentType<LineItem>[];\n\t\t\tmarketings: DocumentType<Marketing>[];\n\t\t};\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent incremented successfully';\n\t}\n\n\tpublic async decrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate([\n\t\t\t\t{ path: 'lineItems' },\n\t\t\t\t{ path: 'marketings', select: 'isAppliedByCompany' },\n\t\t\t])) as DocumentType<RegularOrder> & {\n\t\t\tlineItems: DocumentType<LineItem>[];\n\t\t\tmarketings: DocumentType<Marketing>[];\n\t\t};\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent: -amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.unExpireMarketingsBelowSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent decremented successfully';\n\t}\n\n\tprivate async expireMarketingsExceedingSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tconst update = await this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: { $ne: MarketingStatus.EXPIRED },\n\t\t\t\t$expr: { $gte: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.EXPIRED, expiredAt: dayjs().toDate() } }\n\t\t);\n\n\t\tif (update.modifiedCount > 0) {\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByMarketingIds(marketingIds);\n\t\t}\n\t}\n\n\tprivate async unExpireMarketingsBelowSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tconst update = await this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t$expr: { $lt: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.ACTIVE }, $unset: { expiredAt: 1 } }\n\t\t);\n\n\t\tif (update.modifiedCount > 0) {\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByMarketingIds(marketingIds);\n\t\t}\n\t}\n\n\tpublic async recomputeAndSyncAllMarketingSpent(): Promise<void> {\n\t\tconst cursor = this.model\n\t\t\t.find({ marketingType: { $ne: MarketingType.FEATURED } }, { _id: 1 })\n\t\t\t.lean()\n\t\t\t.cursor();\n\n\t\tconst bulkOps: mongoose.AnyBulkWriteOperation[] = [];\n\t\tconst marketingIds: mongoose.Types.ObjectId[] = [];\n\n\t\tfor await (const marketing of cursor) {\n\t\t\tconst { amount } = await this.marketingMetricsRepository.calculateMarketingSpent(marketing._id);\n\n\t\t\tbulkOps.push({\n\t\t\t\tupdateOne: {\n\t\t\t\t\tfilter: { _id: marketing._id },\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t$set: { amountSpent: amount },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tmarketingIds.push(marketing._id);\n\n\t\t\tif (bulkOps.length >= 500) {\n\t\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t\t\tbulkOps.length = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(marketingIds);\n\t\tawait this.unExpireMarketingsBelowSpendLimits(marketingIds);\n\t}\n\n\tprivate async publishEntireMenuSeedEvents(\n\t\tmarketing: DocumentType<Buy1Get1Marketing | DiscountMarketing>\n\t): Promise<number> {\n\t\tconst PAGE_SIZE = 500;\n\t\tconst marketingId = marketing._id.toString();\n\t\tconst shopId = ((marketing.shop as any)?._id ?? marketing.shop).toString();\n\n\t\tconst products = await ProductModel.find({ shop: marketing.shop }).select('_id').lean();\n\t\tconst productIds = products.map((p: any) => p._id.toString());\n\n\t\tfor (let i = 0; i < productIds.length; i += PAGE_SIZE) {\n\t\t\tawait publishEvent(\n\t\t\t\tnew MarketingEntireMenuSeedEvent({\n\t\t\t\t\tmarketingId,\n\t\t\t\t\tshopId,\n\t\t\t\t\tisAppliedByCompany: marketing.isAppliedByCompany ?? false,\n\t\t\t\t\tisBuy1Get1: marketing.marketingType === MarketingType.BUY1GET1,\n\t\t\t\t\tvalue: (marketing as any).value,\n\t\t\t\t\tvalueType: (marketing as any).valueType,\n\t\t\t\t\tproductIds: productIds.slice(i, i + PAGE_SIZE),\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn productIds.length;\n\t}\n\n\tpublic async reseedEntireMenuMarketing(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst logger = getLibraries().getLoggerKit();\n\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null });\n\t\tif (!marketing) internalServerError('Marketing not found');\n\n\t\tconst productCount = await this.publishEntireMenuSeedEvents(\n\t\t\tmarketing as DocumentType<Buy1Get1Marketing | DiscountMarketing>\n\t\t);\n\n\t\tlogger.debug(`entire-menu reseed dispatched — ${productCount} products`, {\n\t\t\ttag: 'marketing.seed.entire-menu',\n\t\t\tdata: { marketingId: marketingId.toString(), productCount },\n\t\t});\n\t}\n\n\tpublic async assignMarketingToProducts(marketing: DocumentType<Buy1Get1Marketing | DiscountMarketing>) {\n\t\tconst diffStartTime = dayjs(marketing.duration.start).diff(dayjs(), 'milliseconds');\n\t\tif (diffStartTime > 0) {\n\t\t\treturn await scheduleMarketingProductSync(marketing._id.toString(), diffStartTime);\n\t\t}\n\n\t\tif (marketing.isEntireMenu) {\n\t\t\tconst logger = getLibraries().getLoggerKit();\n\n\t\t\tconst productCount = await this.publishEntireMenuSeedEvents(marketing);\n\n\t\t\tlogger.debug(`entire-menu marketing seed dispatched — ${productCount} products`, {\n\t\t\t\ttag: 'marketing.seed.entire-menu',\n\t\t\t\tdata: { marketingId: marketing._id.toString(), productCount },\n\t\t\t});\n\t\t} else {\n\t\t\t// Non-entire-menu: the productMarketings were already created synchronously. The product-doc\n\t\t\t// sync is linear in product count (thousands → seconds), so page it into background events\n\t\t\t// instead of blocking the request — same pattern as the entire-menu branch above.\n\t\t\tconst shopId = ((marketing.shop as any)?._id ?? marketing.shop).toString();\n\t\t\tconst pms = await ProductMarketingModel.find({ marketing: marketing._id, deletedAt: null })\n\t\t\t\t.select('product')\n\t\t\t\t.lean();\n\t\t\tconst productIds = pms.map((p: any) => p.product.toString());\n\n\t\t\tconst PAGE_SIZE = 500;\n\t\t\tfor (let i = 0; i < productIds.length; i += PAGE_SIZE) {\n\t\t\t\tawait publishEvent(\n\t\t\t\t\tnew MarketingProductSyncEvent({\n\t\t\t\t\t\tmarketingId: marketing._id.toString(),\n\t\t\t\t\t\tshopId,\n\t\t\t\t\t\tproductIds: productIds.slice(i, i + PAGE_SIZE),\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const marketingService = Container.get(MarketingService);\n"]}
1
+ {"version":3,"file":"marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/marketing.service.ts"],"names":[],"mappings":"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","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { dayjs } from '@lyxa.ai/core/dist/utilities/dayjs';\nimport { getLibraries } from '@lyxa.ai/core';\nimport { DocumentType, mongoose, ReturnModelType } from '@typegoose/typegoose';\nimport {\n\tBuy1Get1Marketing,\n\tDiscountMarketing,\n\tLimitedTimeOfferParticipant,\n\tLimitedTimeOfferParticipantModel,\n\tLineItem,\n\tMarketing,\n\tMarketingModel,\n\tMarketing as Model,\n\tProductModel,\n\tProductMarketingModel,\n\tRegularOrder,\n\tRegularOrderModel,\n\tShop,\n\tShopModel,\n} from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport {\n\tAllMarketingsDTO,\n\tAllMarketingsValidationDTO,\n\tCreateProductMarketingDTO,\n\tProductEntireMenuMarketingHandlerDTO,\n\tUpdateMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport {\n\tFilterDTO,\n\tPaginatedResponse,\n\tDeleteDTO,\n\tGetByIdInputDTO,\n} from '@lyxa.ai/core/dist/utilities/validation';\nimport { SoftDeleteModel } from '@lyxa.ai/core/dist/libraries/mongo/plugins/soft-delete-plugin';\nimport { createRawDocumentPaginatorObject, PaginatorOptions } from '@lyxa.ai/core/dist/utilities/pagination';\nimport { ModelType } from '@typegoose/typegoose/lib/types';\nimport {\n\tfilterRelevantChanges,\n\tgetChangedFields,\n\tpublishSyncSingleShopSectionsInCacheProcessEvent,\n} from '@lyxa.ai/core/dist/utilities/events-common-methods';\nimport { AdminActivityLogsEvent } from '@lyxa.ai/core/dist/libraries/event/events/admin-logs-event';\nimport {\n\tAdminLogsUpdatedFieldsType, MarketingCampaignType,\n\tMarketingStatus,\n\tRegularOrderStatus,\n} from '@lyxa.ai/core/dist/utilities/enum';\nimport { publishEvent } from '@lyxa.ai/core/dist/libraries/event';\nimport { BaseEvent } from '@lyxa.ai/core/dist/libraries/event/BaseEvent';\nimport {\n\tMarketingEntireMenuSeedEvent,\n\tMarketingProductSeedPayload,\n} from '@events/events/marketing-product-seed.event';\nimport { MarketingProductSyncEvent } from '@events/events/marketing-product-sync.event';\nimport {\n\tbuy1Get1MarketingService,\n\tdiscountMarketingService,\n\tfeaturedMarketingService,\n\tfreeDeliveryMarketingService,\n\tproductMarketingService,\n\tpunchMarketingService,\n\tspendToGetMarketingService,\n\tsynchronizeProductMarketingService,\n\tmarketingConflictService,\n\tCONFLICT_CHECK_MARKETING_TYPES,\n} from '.';\nimport { MarketingType } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tconflictError,\n\tforbiddenError,\n\tinternalServerError,\n\tnotFoundError,\n\tunauthorizedError,\n} from '@lyxa.ai/core/dist/utilities/error-common';\nimport { ChangeStreamOperationTypes } from '@lyxa.ai/core/dist/libraries/event/events/mongo-stream-changes-batch-process-shops-event';\nimport {\n\tMarketingMetricsRepository,\n\tmarketingMetricsRepository,\n} from '../patterns/repository/MarketingMetricsRepository';\nimport { scheduleMarketingProductSync } from '../queues/marketing-product-sync.queue';\nimport { ltoParticipantService } from '@modules/limited-time-offer/participant/services/limited-time-offer-participant.service';\n\n@Service()\nexport class MarketingService {\n\tprivate model: typeof MarketingModel & SoftDeleteModel<Model>;\n\tprivate shopModel: typeof ShopModel & SoftDeleteModel<Shop>;\n\tprivate orderModel: typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\tprivate marketingMetricsRepository: MarketingMetricsRepository;\n\tprivate limitedTimeOfferParticipantModel: typeof LimitedTimeOfferParticipantModel &\n\t\tSoftDeleteModel<LimitedTimeOfferParticipant>;\n\n\tconstructor() {\n\t\tthis.model = MarketingModel as typeof MarketingModel & SoftDeleteModel<Model>;\n\t\tthis.shopModel = ShopModel as typeof ShopModel & SoftDeleteModel<Shop>;\n\t\tthis.orderModel = RegularOrderModel as typeof RegularOrderModel & SoftDeleteModel<RegularOrder>;\n\t\tthis.marketingMetricsRepository = marketingMetricsRepository;\n\t\tthis.limitedTimeOfferParticipantModel =\n\t\t\tLimitedTimeOfferParticipantModel as typeof LimitedTimeOfferParticipantModel &\n\t\t\t\tSoftDeleteModel<LimitedTimeOfferParticipant>;\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(\n\t\tdata: AllMarketingsValidationDTO,\n\t\tadmin?: mongoose.Types.ObjectId,\n\t\toptions?: { skipLimitedTimeOfferGuard?: boolean }\n\t): Promise<DocumentType<Model>> {\n\t\tconst now = dayjs().toDate();\n\n\t\tif (\n\t\t\t!options?.skipLimitedTimeOfferGuard &&\n\t\t\tdata.marketingType === MarketingType.DISCOUNT &&\n\t\t\t(await ltoParticipantService.isActiveParticipant(data.shop))\n\t\t) {\n\t\t\tforbiddenError(\n\t\t\t\t'You are currently participating in a Limited Time Offer. Please manage your discount from the Limited Time Offer section or withdraw first.'\n\t\t\t);\n\t\t}\n\n\t\tconst existingRecord = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: data.marketingType,\n\t\t\t'duration.end': { $gt: now },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisAppliedByCompany: data.isAppliedByCompany,\n\t\t\tcampaignType: data.campaignType,\n\t\t\tadmin: admin ? { $ne: null } : null,\n\t\t});\n\n\t\tif (existingRecord) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'CONFLICT',\n\t\t\t\tmessage: `This shop already has a ${data.marketingType} marketing`,\n\t\t\t});\n\t\t}\n\n\t\tif (CONFLICT_CHECK_MARKETING_TYPES.includes(data.marketingType)) {\n\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\tshop: data.shop,\n\t\t\t\tmarketingType: data.marketingType,\n\t\t\t\tduration: data.duration,\n\t\t\t\trewardTypeGroups: marketingConflictService.getRewardTypeGroups(data.marketingType, data.tiers),\n\t\t\t});\n\t\t}\n\n\t\tif (data.isEntireMenu) {\n\t\t\tconst existingMarketing = await this.model.findOne({\n\t\t\t\tshop: data.shop,\n\t\t\t\t'duration.start': { $lt: now },\n\t\t\t\t'duration.end': { $gt: now },\n\t\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT], $ne: data.marketingType },\n\t\t\t});\n\n\t\t\tif (existingMarketing) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\tmessage: `Cannot create marketing for entire menu while another product based marketing exists for the shop`,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (data.products && data.products.length > 0) {\n\t\t\tconst productIds = data.products.map((product: CreateProductMarketingDTO) => product.product);\n\n\t\t\tconst hasDuplicates = new Set(productIds.map(String)).size !== productIds.length;\n\n\t\t\tif (hasDuplicates) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: 'BAD_REQUEST',\n\t\t\t\t\tmessage: `Product list contains duplicate products`,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (data.campaignType === MarketingCampaignType.LIMITED_TIME_OFFER) {\n\t\t\t\tawait productMarketingService.pauseConflictingMarketings(productIds);\n\t\t\t} else {\n\t\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(\n\t\t\t\t\tproductIds,\n\t\t\t\t\tdata.isAppliedByCompany\n\t\t\t\t);\n\n\t\t\t\tif (!isAvailable) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'CONFLICT',\n\t\t\t\t\t\tmessage: `Product list contains products which already have marketing applied`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (admin) {\n\t\t\tdata.admin = admin;\n\t\t}\n\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tswitch (data.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tthrow new TRPCError({\n\t\t\t\t\t\tcode: 'UNAUTHORIZED',\n\t\t\t\t\t\tmessage: `Only admin is allowed to create ${data.marketingType} marketing`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.SPEND_TO_GET: {\n\t\t\t\tmarketing = await spendToGetMarketingService.create(data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model.create(data, admin);\n\t\t\t}\n\t\t}\n\n\t\tawait this.assignMarketingToProducts(marketing);\n\n\t\tawait this.model.populate(marketing, {\n\t\t\tpath: 'shop',\n\t\t\tmodel: this.shopModel,\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.ACTIVATED_MARKETING_PROMOTIONS,\n\t\t\t\t'',\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\t\tif (marketing && !data.isEntireMenu)\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId: data.shop,\n\t\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t\t});\n\t\treturn marketing;\n\t}\n\n\t/**\n\t * Get record by ID\n\t * @param id ID of the record\n\t */\n\tpublic async findById(data: GetByIdInputDTO): Promise<DocumentType<Model>> {\n\t\tconst select = data.select || '';\n\t\tconst populate = data.populate || [];\n\t\tconst result = await this.model\n\t\t\t.findOne({\n\t\t\t\t_id: data._id,\n\t\t\t})\n\t\t\t.select(select)\n\t\t\t.populate(populate);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get records\n\t * @param query Query to find records\n\t */\n\tpublic async find(data?: Partial<FilterDTO>): Promise<PaginatedResponse<DocumentType<ModelType<Model>>>> {\n\t\ttry {\n\t\t\tconst paginate = createRawDocumentPaginatorObject(this.model as ReturnModelType<typeof this.model>);\n\t\t\tconst options: PaginatorOptions<typeof Model> = {\n\t\t\t\tpage: data?.page,\n\t\t\t\tsize: data?.size,\n\t\t\t\tsort: data?.sort,\n\t\t\t\tquery: data?.query,\n\t\t\t\tselect: data?.select,\n\t\t\t\tpopulate: data?.populate,\n\t\t\t};\n\t\t\tconst paginatedResult = await paginate(options);\n\t\t\treturn paginatedResult;\n\t\t} catch (err) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: `err: ${err}`,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(\n\t\t_id: mongoose.Types.ObjectId,\n\t\tdata: UpdateDTO,\n\t\tadmin?: mongoose.Types.ObjectId\n\t): Promise<DocumentType<Model>> {\n\t\tlet marketing: AllMarketingsDTO;\n\n\t\tconst partOfLimitedTimeOffer = await this.limitedTimeOfferParticipantModel.exists({\n\t\t\tmarketing: _id,\n\t\t});\n\n\t\tif (partOfLimitedTimeOffer) {\n\t\t\tforbiddenError(\n\t\t\t\t'You are currently participating in a Limited Time Offer. Please make your edits from the Limited Time Offer section.'\n\t\t\t);\n\t\t}\n\n\t\tconst oldMarketing = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!oldMarketing) {\n\t\t\tnotFoundError('Could not find marketing');\n\t\t}\n\n\t\tif (data.status && data.status === MarketingStatus.ACTIVE && oldMarketing.status !== MarketingStatus.ACTIVE) {\n\t\t\tif (\n\t\t\t\toldMarketing.status === MarketingStatus.PAUSED &&\n\t\t\t\toldMarketing.marketingType === MarketingType.DISCOUNT &&\n\t\t\t\t(await ltoParticipantService.isActiveParticipant(oldMarketing.shop._id))\n\t\t\t) {\n\t\t\t\tforbiddenError(\n\t\t\t\t\t'You are currently participating in a Limited Time Offer. Please manage your discount from the Limited Time Offer section or withdraw first.'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait this.verifyUnpauseConditions(_id);\n\t\t}\n\n\t\tswitch (oldMarketing.marketingType) {\n\t\t\tcase MarketingType.DISCOUNT: {\n\t\t\t\tmarketing = await discountMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.BUY1GET1: {\n\t\t\t\tmarketing = await buy1Get1MarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FEATURED: {\n\t\t\t\tif (!admin) {\n\t\t\t\t\tunauthorizedError(`Only admin is allowed to update ${data.marketingType} marketing`);\n\t\t\t\t}\n\t\t\t\tmarketing = await featuredMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.FREE_DELIVERY: {\n\t\t\t\tmarketing = await freeDeliveryMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.PUNCH_MARKETING: {\n\t\t\t\tmarketing = await punchMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MarketingType.SPEND_TO_GET: {\n\t\t\t\tmarketing = await spendToGetMarketingService.update(_id, data);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tmarketing = await this.model\n\t\t\t\t\t.findOneAndUpdate(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t_id: _id,\n\t\t\t\t\t\t\tdeletedAt: null,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnew: true,\n\t\t\t\t\t\t\trunValidators: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\t\t.populate({\n\t\t\t\t\t\tpath: 'shop',\n\t\t\t\t\t\tselect: '_id name shopId',\n\t\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (!marketing) {\n\t\t\tinternalServerError('Could not update marketing');\n\t\t}\n\n\t\tif (admin) {\n\t\t\tconst allChanges = getChangedFields(oldMarketing, marketing);\n\t\t\tconst relevantChanges = filterRelevantChanges(allChanges, data);\n\n\t\t\tif (Object.keys(relevantChanges).includes('status')) {\n\t\t\t\tif (data.status == MarketingStatus.ACTIVE) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.CONTINUED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t} else if (data.status == MarketingStatus.PAUSED) {\n\t\t\t\t\tthis.logMarketingAdminAction(\n\t\t\t\t\t\tadmin,\n\t\t\t\t\t\tAdminLogsUpdatedFieldsType.PAUSED_MARKETING_PROMOTIONS,\n\t\t\t\t\t\toldMarketing,\n\t\t\t\t\t\tmarketing\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.logMarketingAdminAction(\n\t\t\t\tadmin,\n\t\t\t\tAdminLogsUpdatedFieldsType.EDITED_MARKETING_PROMOTIONS,\n\t\t\t\toldMarketing,\n\t\t\t\tmarketing\n\t\t\t);\n\t\t}\n\n\t\tawait this.assignMarketingToProducts(marketing);\n\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: marketing.shop,\n\t\t\toperation: ChangeStreamOperationTypes.UPDATE,\n\t\t});\n\n\t\treturn marketing;\n\t}\n\n\tprivate async verifyUnpauseConditions(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null }).populate('products');\n\t\tif (!marketing) return;\n\n\t\tif ([MarketingType.BUY1GET1, MarketingType.DISCOUNT].includes(marketing.marketingType)) {\n\t\t\tconst typedMarketing = marketing as DocumentType<DiscountMarketing | Buy1Get1Marketing>;\n\t\t\tconst productIds = typedMarketing.products.map((product: any) => product.product);\n\t\t\tconst isAvailable = await productMarketingService.checkProductMarketingAvailability(\n\t\t\t\tproductIds,\n\t\t\t\tmarketing.isAppliedByCompany\n\t\t\t);\n\n\t\t\tif (!isAvailable) {\n\t\t\t\tconflictError(\"You can't activate discount and B1G1 on the same items at the same time\");\n\t\t\t}\n\t\t}\n\n\t\tif (CONFLICT_CHECK_MARKETING_TYPES.includes(marketing.marketingType)) {\n\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\tshop: marketing.shop as mongoose.Types.ObjectId,\n\t\t\t\tmarketingType: marketing.marketingType,\n\t\t\t\tduration: marketing.duration,\n\t\t\t\trewardTypeGroups: marketingConflictService.getRewardTypeGroups(\n\t\t\t\t\tmarketing.marketingType,\n\t\t\t\t\t(marketing as any).tiers\n\t\t\t\t),\n\t\t\t\texcludeMarketingId: marketingId,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async logMarketingAdminAction(\n\t\tadmin: mongoose.Types.ObjectId,\n\t\tactionType: AdminLogsUpdatedFieldsType,\n\t\toldDocument?: any,\n\t\tnewDocument?: any\n\t) {\n\t\tconst adminActivityLogsEvent: BaseEvent<Record<string, unknown>> = new AdminActivityLogsEvent({\n\t\t\tadmin,\n\t\t\tupdatedField: actionType,\n\t\t\toldDocument: oldDocument ?? '',\n\t\t\tnewDocument: newDocument ?? '',\n\t\t});\n\t\tawait publishEvent(adminActivityLogsEvent);\n\t}\n\n\t/**\n\t * Delete record\n\t * @param data Data for delete\n\t */\n\tpublic async deleteById(\n\t\tdata: DeleteDTO,\n\t\tadmin?: mongoose.Types.ObjectId,\n\t\toptions?: { skipLimitedTimeOfferGuard?: boolean }\n\t): Promise<string> {\n\t\tconst { _id, softDelete } = data;\n\n\t\tif (!options?.skipLimitedTimeOfferGuard) {\n\t\t\tconst partOfLimitedTimeOffer = await this.limitedTimeOfferParticipantModel.exists({\n\t\t\t\tmarketing: _id,\n\t\t\t});\n\n\t\t\tif (partOfLimitedTimeOffer) {\n\t\t\t\tforbiddenError(\n\t\t\t\t\t'You are currently participating in a Limited Time Offer. Please make your edits from the Limited Time Offer section.'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst record = await this.model.findById(_id).populate({\n\t\t\tpath: 'shop',\n\t\t\tselect: '_id name shopId',\n\t\t});\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: 'Record not found',\n\t\t\t});\n\t\t}\n\n\t\trecord.status = MarketingStatus.DELETED;\n\t\tawait record.save();\n\n\t\tconst result = await this.model.deleteRecord({ _id, deletedAt: null }, softDelete);\n\t\tif (!result) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'INTERNAL_SERVER_ERROR',\n\t\t\t\tmessage: 'Unable to delete marketing',\n\t\t\t});\n\t\t}\n\n\t\tif (admin) {\n\t\t\tthis.logMarketingAdminAction(admin, AdminLogsUpdatedFieldsType.DELETED_MARKETING_PROMOTIONS, record);\n\t\t}\n\n\t\tproductMarketingService.deleteMany({ marketing: data._id }, data.softDelete);\n\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\tshopId: record.shop._id,\n\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t});\n\t\treturn 'Record deleted successfully';\n\t}\n\n\tpublic async deleteManyByIds(marketingIds: mongoose.Types.ObjectId[], softDelete = true): Promise<void> {\n\t\tif (marketingIds.length === 0) return;\n\n\t\tconst marketings = await this.model\n\t\t\t.find({ _id: { $in: marketingIds } })\n\t\t\t.select('shop')\n\t\t\t.lean();\n\n\t\tawait this.model.updateMany(\n\t\t\t{ _id: { $in: marketingIds } },\n\t\t\t{ $set: { status: MarketingStatus.DELETED } }\n\t\t);\n\t\tawait this.model.deleteRecords({ _id: { $in: marketingIds } }, softDelete);\n\n\t\tawait productMarketingService.deleteManyByMarketingIds(marketingIds, softDelete);\n\n\t\tconst uniqueShopIds = [\n\t\t\t...new Map(\n\t\t\t\tmarketings\n\t\t\t\t\t.map((m: any) => m.shop)\n\t\t\t\t\t.filter(Boolean)\n\t\t\t\t\t.map((s: any) => {\n\t\t\t\t\t\tconst id = s instanceof mongoose.Types.ObjectId ? s : new mongoose.Types.ObjectId(s);\n\t\t\t\t\t\treturn [id.toString(), id] as [string, mongoose.Types.ObjectId];\n\t\t\t\t\t})\n\t\t\t).values(),\n\t\t];\n\n\t\tfor (const shopId of uniqueShopIds) {\n\t\t\tpublishSyncSingleShopSectionsInCacheProcessEvent({\n\t\t\t\tshopId,\n\t\t\t\toperation: ChangeStreamOperationTypes.DELETE,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic async handleNewProductForEntireMenuMarketing(data: ProductEntireMenuMarketingHandlerDTO) {\n\t\tconst existingMarketing = await this.model.findOne({\n\t\t\tshop: data.shop,\n\t\t\tmarketingType: { $in: [MarketingType.BUY1GET1, MarketingType.DISCOUNT] },\n\t\t\t'duration.end': { $gt: new Date() },\n\t\t\tstatus: MarketingStatus.ACTIVE,\n\t\t\tisEntireMenu: true,\n\t\t});\n\n\t\t// check if existing marketing\n\t\tif (existingMarketing) {\n\t\t\tconst productMarketingData: CreateProductMarketingDTO = {\n\t\t\t\tproduct: data.product,\n\t\t\t\tmarketing: existingMarketing._id,\n\t\t\t};\n\t\t\tif (existingMarketing.marketingType == MarketingType.BUY1GET1) {\n\t\t\t\tproductMarketingData.isBuy1Get1 = true;\n\t\t\t}\n\t\t\tif (existingMarketing.marketingType == MarketingType.DISCOUNT) {\n\t\t\t\tproductMarketingData.value = (existingMarketing as DocumentType<DiscountMarketing>).value;\n\t\t\t\tproductMarketingData.valueType = (existingMarketing as DocumentType<DiscountMarketing>).valueType;\n\t\t\t}\n\n\t\t\tawait productMarketingService.create(productMarketingData);\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByProductId(data.product);\n\t\t}\n\n\t\treturn 'Product handled successfully';\n\t}\n\n\tpublic async incrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate([\n\t\t\t\t{ path: 'lineItems' },\n\t\t\t\t{ path: 'marketings', select: 'isAppliedByCompany' },\n\t\t\t])) as DocumentType<RegularOrder> & {\n\t\t\tlineItems: DocumentType<LineItem>[];\n\t\t\tmarketings: DocumentType<Marketing>[];\n\t\t};\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent incremented successfully';\n\t}\n\n\tpublic async decrementMarketingSpentFromOrder(orderId: mongoose.Types.ObjectId): Promise<any> {\n\t\tconst order = (await this.orderModel\n\t\t\t.findById(orderId)\n\t\t\t.populate([\n\t\t\t\t{ path: 'lineItems' },\n\t\t\t\t{ path: 'marketings', select: 'isAppliedByCompany' },\n\t\t\t])) as DocumentType<RegularOrder> & {\n\t\t\tlineItems: DocumentType<LineItem>[];\n\t\t\tmarketings: DocumentType<Marketing>[];\n\t\t};\n\n\t\tif (!order || !order.marketings?.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst marketingSpendMap = this.marketingMetricsRepository.calculateMarketingSpendPerOrder(order);\n\n\t\tconst bulkOps = [...marketingSpendMap.entries()].map(([marketingId, amountSpent]) => ({\n\t\t\tupdateOne: {\n\t\t\t\tfilter: { _id: marketingId },\n\t\t\t\tupdate: { $inc: { amountSpent: -amountSpent } },\n\t\t\t},\n\t\t}));\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.unExpireMarketingsBelowSpendLimits(Array.from(marketingSpendMap.keys()));\n\n\t\treturn 'Marketing amount spent decremented successfully';\n\t}\n\n\tprivate async expireMarketingsExceedingSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tconst update = await this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: { $ne: MarketingStatus.EXPIRED },\n\t\t\t\t$expr: { $gte: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.EXPIRED, expiredAt: dayjs().toDate() } }\n\t\t);\n\n\t\tif (update.modifiedCount > 0) {\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByMarketingIds(marketingIds);\n\t\t}\n\t}\n\n\tprivate async unExpireMarketingsBelowSpendLimits(marketingIds: mongoose.Types.ObjectId[]) {\n\t\tconst update = await this.model.updateMany(\n\t\t\t{\n\t\t\t\t_id: { $in: marketingIds },\n\t\t\t\tisSpendLimitEnabled: true,\n\t\t\t\tstatus: MarketingStatus.EXPIRED,\n\t\t\t\t$expr: { $lt: ['$amountSpent', '$spendLimit'] },\n\t\t\t},\n\t\t\t{ $set: { status: MarketingStatus.ACTIVE }, $unset: { expiredAt: 1 } }\n\t\t);\n\n\t\tif (update.modifiedCount > 0) {\n\t\t\tawait synchronizeProductMarketingService.syncProductMarketingInfoByMarketingIds(marketingIds);\n\t\t}\n\t}\n\n\tpublic async recomputeAndSyncAllMarketingSpent(): Promise<void> {\n\t\tconst cursor = this.model\n\t\t\t.find({ marketingType: { $ne: MarketingType.FEATURED } }, { _id: 1 })\n\t\t\t.lean()\n\t\t\t.cursor();\n\n\t\tconst bulkOps: mongoose.AnyBulkWriteOperation[] = [];\n\t\tconst marketingIds: mongoose.Types.ObjectId[] = [];\n\n\t\tfor await (const marketing of cursor) {\n\t\t\tconst { amount } = await this.marketingMetricsRepository.calculateMarketingSpent(marketing._id);\n\n\t\t\tbulkOps.push({\n\t\t\t\tupdateOne: {\n\t\t\t\t\tfilter: { _id: marketing._id },\n\t\t\t\t\tupdate: {\n\t\t\t\t\t\t$set: { amountSpent: amount },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tmarketingIds.push(marketing._id);\n\n\t\t\tif (bulkOps.length >= 500) {\n\t\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t\t\tbulkOps.length = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (bulkOps.length) {\n\t\t\tawait this.model.bulkWrite(bulkOps);\n\t\t}\n\n\t\tawait this.expireMarketingsExceedingSpendLimits(marketingIds);\n\t\tawait this.unExpireMarketingsBelowSpendLimits(marketingIds);\n\t}\n\n\tprivate async publishEntireMenuSeedEvents(\n\t\tmarketing: DocumentType<Buy1Get1Marketing | DiscountMarketing>\n\t): Promise<number> {\n\t\tconst PAGE_SIZE = 500;\n\t\tconst marketingId = marketing._id.toString();\n\t\tconst shopId = ((marketing.shop as any)?._id ?? marketing.shop).toString();\n\n\t\tconst products = await ProductModel.find({ shop: marketing.shop }).select('_id').lean();\n\t\tconst productIds = products.map((p: any) => p._id.toString());\n\n\t\tfor (let i = 0; i < productIds.length; i += PAGE_SIZE) {\n\t\t\tawait publishEvent(\n\t\t\t\tnew MarketingEntireMenuSeedEvent({\n\t\t\t\t\tmarketingId,\n\t\t\t\t\tshopId,\n\t\t\t\t\tisAppliedByCompany: marketing.isAppliedByCompany ?? false,\n\t\t\t\t\tisBuy1Get1: marketing.marketingType === MarketingType.BUY1GET1,\n\t\t\t\t\tvalue: (marketing as any).value,\n\t\t\t\t\tvalueType: (marketing as any).valueType,\n\t\t\t\t\tproductIds: productIds.slice(i, i + PAGE_SIZE),\n\t\t\t\t})\n\t\t\t);\n\t\t}\n\n\t\treturn productIds.length;\n\t}\n\n\tpublic async reseedEntireMenuMarketing(marketingId: mongoose.Types.ObjectId): Promise<void> {\n\t\tconst logger = getLibraries().getLoggerKit();\n\n\t\tconst marketing = await this.model.findOne({ _id: marketingId, deletedAt: null });\n\t\tif (!marketing) internalServerError('Marketing not found');\n\n\t\tconst productCount = await this.publishEntireMenuSeedEvents(\n\t\t\tmarketing as DocumentType<Buy1Get1Marketing | DiscountMarketing>\n\t\t);\n\n\t\tlogger.debug(`entire-menu reseed dispatched — ${productCount} products`, {\n\t\t\ttag: 'marketing.seed.entire-menu',\n\t\t\tdata: { marketingId: marketingId.toString(), productCount },\n\t\t});\n\t}\n\n\tpublic async assignMarketingToProducts(marketing: DocumentType<Buy1Get1Marketing | DiscountMarketing>) {\n\t\tconst diffStartTime = dayjs(marketing.duration.start).diff(dayjs(), 'milliseconds');\n\t\tif (diffStartTime > 0) {\n\t\t\treturn await scheduleMarketingProductSync(marketing._id.toString(), diffStartTime);\n\t\t}\n\n\t\tif (marketing.isEntireMenu) {\n\t\t\tconst logger = getLibraries().getLoggerKit();\n\n\t\t\tconst productCount = await this.publishEntireMenuSeedEvents(marketing);\n\n\t\t\tlogger.debug(`entire-menu marketing seed dispatched — ${productCount} products`, {\n\t\t\t\ttag: 'marketing.seed.entire-menu',\n\t\t\t\tdata: { marketingId: marketing._id.toString(), productCount },\n\t\t\t});\n\t\t} else {\n\t\t\t// Non-entire-menu: the productMarketings were already created synchronously. The product-doc\n\t\t\t// sync is linear in product count (thousands → seconds), so page it into background events\n\t\t\t// instead of blocking the request — same pattern as the entire-menu branch above.\n\t\t\tconst shopId = ((marketing.shop as any)?._id ?? marketing.shop).toString();\n\t\t\tconst pms = await ProductMarketingModel.find({ marketing: marketing._id, deletedAt: null })\n\t\t\t\t.select('product')\n\t\t\t\t.lean();\n\t\t\tconst productIds = pms.map((p: any) => p.product.toString());\n\n\t\t\tconst PAGE_SIZE = 500;\n\t\t\tfor (let i = 0; i < productIds.length; i += PAGE_SIZE) {\n\t\t\t\tawait publishEvent(\n\t\t\t\t\tnew MarketingProductSyncEvent({\n\t\t\t\t\t\tmarketingId: marketing._id.toString(),\n\t\t\t\t\t\tshopId,\n\t\t\t\t\t\tproductIds: productIds.slice(i, i + PAGE_SIZE),\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport const marketingService = Container.get(MarketingService);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"spend-to-get-marketing.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/spend-to-get-marketing.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,IAAI,KAAK,EAAE,MAAM,4CAA4C,CAAC;AAE1F,OAAO,EACN,4BAA4B,IAAI,SAAS,EACzC,4BAA4B,IAAI,SAAS,EACzC,MAAM,gCAAgC,CAAC;AAIxC,qBACa,0BAA0B;IACtC,OAAO,CAAC,UAAU,CAAgC;;IAUrC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAgBrD,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;CA+BhG;AAED,eAAO,MAAM,0BAA0B,4BAA4C,CAAC"}
1
+ {"version":3,"file":"spend-to-get-marketing.service.d.ts","sourceRoot":"/","sources":["modules/marketing/services/spend-to-get-marketing.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,IAAI,KAAK,EAAE,MAAM,4CAA4C,CAAC;AAE1F,OAAO,EACN,4BAA4B,IAAI,SAAS,EACzC,4BAA4B,IAAI,SAAS,EACzC,MAAM,gCAAgC,CAAC;AAIxC,qBACa,0BAA0B;IACtC,OAAO,CAAC,UAAU,CAAgC;;IAUrC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAgBrD,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;CAgChG;AAED,eAAO,MAAM,0BAA0B,4BAA4C,CAAC"}
@@ -73,6 +73,7 @@ let SpendToGetMarketingService = class SpendToGetMarketingService {
73
73
  if (addedGroups.length > 0) {
74
74
  await marketing_conflict_service_1.marketingConflictService.assertNoConflict({
75
75
  shop: existing.shop,
76
+ marketingType: existing.marketingType,
76
77
  duration: data.duration ?? existing.duration,
77
78
  rewardTypeGroups: addedGroups,
78
79
  excludeMarketingId: _id,
@@ -1 +1 @@
1
- {"version":3,"file":"spend-to-get-marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/spend-to-get-marketing.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAA4C;AAC5C,yCAAyC;AAGzC,4DAAoE;AAKpE,6EAAwE;AACxE,2FAAoF;AAG7E,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAC9B,UAAU,CAAgC;IAElD;QACC,IAAI,CAAC,UAAU,GAAG,gBAAS,CAAC,GAAG,CAAC,iEAA6B,CAAC,CAAC;IAChE,CAAC;IAMM,KAAK,CAAC,MAAM,CAAC,IAAe;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,IAAI,kBAAS,CAAC;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,gBAAgB;aACzB,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAOM,KAAK,CAAC,MAAM,CAAC,GAA4B,EAAE,IAAe;QAChE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAErD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,sBAAe,CAAC,MAAM,EAAE,CAAC;gBAC5D,MAAM,SAAS,GAAG,qDAAwB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACvG,MAAM,SAAS,GAAG,qDAAwB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnG,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;gBAE1E,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC5B,MAAM,qDAAwB,CAAC,gBAAgB,CAAC;wBAC/C,IAAI,EAAE,QAAQ,CAAC,IAA+B;wBAC9C,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;wBAC5C,gBAAgB,EAAE,WAAW;wBAC7B,kBAAkB,EAAE,GAAG;qBACvB,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,kBAAS,CAAC;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,kBAAkB;aAC3B,CAAC,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;CACD,CAAA;AA1DY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,gBAAO,GAAE;;GACG,0BAA0B,CA0DtC;AAEY,QAAA,0BAA0B,GAAG,gBAAS,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport { SpendToGetMarketing as Model } from '@lyxa.ai/core/dist/libraries/mongo/models/';\nimport { MarketingStatus } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tCreateSpendToGetMarketingDTO as CreateDTO,\n\tUpdateSpendToGetMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport { marketingConflictService } from './marketing-conflict.service';\nimport { SpendToGetMarketingRepository } from './spend-to-get-marketing.repository';\n\n@Service()\nexport class SpendToGetMarketingService {\n\tprivate repository: SpendToGetMarketingRepository;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(SpendToGetMarketingRepository);\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(data: CreateDTO): Promise<DocumentType<Model>> {\n\t\tconst shop = await this.repository.findShopById(data.shop);\n\t\tif (!shop) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: `Shop not found`,\n\t\t\t});\n\t\t}\n\t\treturn await this.repository.create(data);\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(_id: mongoose.Types.ObjectId, data: UpdateDTO): Promise<DocumentType<Model>> {\n\t\tif (data.tiers) {\n\t\t\tconst existing = await this.repository.findById(_id);\n\n\t\t\tif (existing && existing.status === MarketingStatus.ACTIVE) {\n\t\t\t\tconst oldGroups = marketingConflictService.getRewardTypeGroups(existing.marketingType, existing.tiers);\n\t\t\t\tconst newGroups = marketingConflictService.getRewardTypeGroups(existing.marketingType, data.tiers);\n\t\t\t\tconst addedGroups = newGroups.filter(group => !oldGroups.includes(group));\n\n\t\t\t\tif (addedGroups.length > 0) {\n\t\t\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\t\t\tshop: existing.shop as mongoose.Types.ObjectId,\n\t\t\t\t\t\tduration: data.duration ?? existing.duration,\n\t\t\t\t\t\trewardTypeGroups: addedGroups,\n\t\t\t\t\t\texcludeMarketingId: _id,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst record = await this.repository.updateById(_id, data);\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: `Record not found`,\n\t\t\t});\n\t\t}\n\n\t\treturn record;\n\t}\n}\n\nexport const spendToGetMarketingService = Container.get(SpendToGetMarketingService);\n"]}
1
+ {"version":3,"file":"spend-to-get-marketing.service.js","sourceRoot":"/","sources":["modules/marketing/services/spend-to-get-marketing.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAA4C;AAC5C,yCAAyC;AAGzC,4DAAoE;AAKpE,6EAAwE;AACxE,2FAAoF;AAG7E,IAAM,0BAA0B,GAAhC,MAAM,0BAA0B;IAC9B,UAAU,CAAgC;IAElD;QACC,IAAI,CAAC,UAAU,GAAG,gBAAS,CAAC,GAAG,CAAC,iEAA6B,CAAC,CAAC;IAChE,CAAC;IAMM,KAAK,CAAC,MAAM,CAAC,IAAe;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,MAAM,IAAI,kBAAS,CAAC;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,gBAAgB;aACzB,CAAC,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAOM,KAAK,CAAC,MAAM,CAAC,GAA4B,EAAE,IAAe;QAChE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAErD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,sBAAe,CAAC,MAAM,EAAE,CAAC;gBAC5D,MAAM,SAAS,GAAG,qDAAwB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACvG,MAAM,SAAS,GAAG,qDAAwB,CAAC,mBAAmB,CAAC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACnG,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;gBAE1E,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC5B,MAAM,qDAAwB,CAAC,gBAAgB,CAAC;wBAC/C,IAAI,EAAE,QAAQ,CAAC,IAA+B;wBAC9C,aAAa,EAAE,QAAQ,CAAC,aAAa;wBACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;wBAC5C,gBAAgB,EAAE,WAAW;wBAC7B,kBAAkB,EAAE,GAAG;qBACvB,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,kBAAS,CAAC;gBACnB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,kBAAkB;aAC3B,CAAC,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;CACD,CAAA;AA3DY,gEAA0B;qCAA1B,0BAA0B;IADtC,IAAA,gBAAO,GAAE;;GACG,0BAA0B,CA2DtC;AAEY,QAAA,0BAA0B,GAAG,gBAAS,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC","sourcesContent":["import Container, { Service } from 'typedi';\nimport { TRPCError } from '@trpc/server';\nimport { DocumentType, mongoose } from '@typegoose/typegoose';\nimport { SpendToGetMarketing as Model } from '@lyxa.ai/core/dist/libraries/mongo/models/';\nimport { MarketingStatus } from '@lyxa.ai/core/dist/utilities/enum';\nimport {\n\tCreateSpendToGetMarketingDTO as CreateDTO,\n\tUpdateSpendToGetMarketingDTO as UpdateDTO,\n} from '@modules/marketing/validations';\nimport { marketingConflictService } from './marketing-conflict.service';\nimport { SpendToGetMarketingRepository } from './spend-to-get-marketing.repository';\n\n@Service()\nexport class SpendToGetMarketingService {\n\tprivate repository: SpendToGetMarketingRepository;\n\n\tconstructor() {\n\t\tthis.repository = Container.get(SpendToGetMarketingRepository);\n\t}\n\n\t/**\n\t * Create a new record\n\t * @param data Data for the new record\n\t */\n\tpublic async create(data: CreateDTO): Promise<DocumentType<Model>> {\n\t\tconst shop = await this.repository.findShopById(data.shop);\n\t\tif (!shop) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: `Shop not found`,\n\t\t\t});\n\t\t}\n\t\treturn await this.repository.create(data);\n\t}\n\n\t/**\n\t * Update record\n\t * @param id ID of the record\n\t * @param data Data to update\n\t */\n\tpublic async update(_id: mongoose.Types.ObjectId, data: UpdateDTO): Promise<DocumentType<Model>> {\n\t\tif (data.tiers) {\n\t\t\tconst existing = await this.repository.findById(_id);\n\n\t\t\tif (existing && existing.status === MarketingStatus.ACTIVE) {\n\t\t\t\tconst oldGroups = marketingConflictService.getRewardTypeGroups(existing.marketingType, existing.tiers);\n\t\t\t\tconst newGroups = marketingConflictService.getRewardTypeGroups(existing.marketingType, data.tiers);\n\t\t\t\tconst addedGroups = newGroups.filter(group => !oldGroups.includes(group));\n\n\t\t\t\tif (addedGroups.length > 0) {\n\t\t\t\t\tawait marketingConflictService.assertNoConflict({\n\t\t\t\t\t\tshop: existing.shop as mongoose.Types.ObjectId,\n\t\t\t\t\t\tmarketingType: existing.marketingType,\n\t\t\t\t\t\tduration: data.duration ?? existing.duration,\n\t\t\t\t\t\trewardTypeGroups: addedGroups,\n\t\t\t\t\t\texcludeMarketingId: _id,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst record = await this.repository.updateById(_id, data);\n\n\t\tif (!record) {\n\t\t\tthrow new TRPCError({\n\t\t\t\tcode: 'NOT_FOUND',\n\t\t\t\tmessage: `Record not found`,\n\t\t\t});\n\t\t}\n\n\t\treturn record;\n\t}\n}\n\nexport const spendToGetMarketingService = Container.get(SpendToGetMarketingService);\n"]}
@@ -59,7 +59,6 @@ declare const Buy1Get1MarketingValidationSchema: z.ZodObject<{
59
59
  isAppliedByCompany: boolean;
60
60
  isEntireMenu: boolean;
61
61
  pausedAt: Date | null;
62
- marketingType: MarketingType.BUY1GET1;
63
62
  products: {
64
63
  product: import("mongoose").Types.ObjectId;
65
64
  value?: number | undefined;
@@ -68,6 +67,7 @@ declare const Buy1Get1MarketingValidationSchema: z.ZodObject<{
68
67
  marketing?: import("mongoose").Types.ObjectId | undefined;
69
68
  isBuy1Get1?: boolean | undefined;
70
69
  }[];
70
+ marketingType: MarketingType.BUY1GET1;
71
71
  excludeProducts: import("mongoose").Types.ObjectId[];
72
72
  isOnlyForSubscriber: boolean;
73
73
  admin?: import("mongoose").Types.ObjectId | null | undefined;
@@ -437,7 +437,6 @@ declare const Buy1Get1MarketingValidationSchemaRefined: z.ZodEffects<z.ZodObject
437
437
  isAppliedByCompany: boolean;
438
438
  isEntireMenu: boolean;
439
439
  pausedAt: Date | null;
440
- marketingType: MarketingType.BUY1GET1;
441
440
  products: {
442
441
  product: import("mongoose").Types.ObjectId;
443
442
  value?: number | undefined;
@@ -446,6 +445,7 @@ declare const Buy1Get1MarketingValidationSchemaRefined: z.ZodEffects<z.ZodObject
446
445
  marketing?: import("mongoose").Types.ObjectId | undefined;
447
446
  isBuy1Get1?: boolean | undefined;
448
447
  }[];
448
+ marketingType: MarketingType.BUY1GET1;
449
449
  excludeProducts: import("mongoose").Types.ObjectId[];
450
450
  isOnlyForSubscriber: boolean;
451
451
  admin?: import("mongoose").Types.ObjectId | null | undefined;
@@ -487,7 +487,6 @@ declare const Buy1Get1MarketingValidationSchemaRefined: z.ZodEffects<z.ZodObject
487
487
  isAppliedByCompany: boolean;
488
488
  isEntireMenu: boolean;
489
489
  pausedAt: Date | null;
490
- marketingType: MarketingType.BUY1GET1;
491
490
  products: {
492
491
  product: import("mongoose").Types.ObjectId;
493
492
  value?: number | undefined;
@@ -496,6 +495,7 @@ declare const Buy1Get1MarketingValidationSchemaRefined: z.ZodEffects<z.ZodObject
496
495
  marketing?: import("mongoose").Types.ObjectId | undefined;
497
496
  isBuy1Get1?: boolean | undefined;
498
497
  }[];
498
+ marketingType: MarketingType.BUY1GET1;
499
499
  excludeProducts: import("mongoose").Types.ObjectId[];
500
500
  isOnlyForSubscriber: boolean;
501
501
  admin?: import("mongoose").Types.ObjectId | null | undefined;