@lyxa.ai/marketing 1.0.42 → 1.0.43

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 (184) hide show
  1. package/dist/lib/event/event-initializations.d.ts.map +1 -1
  2. package/dist/lib/event/event-initializations.js +1 -0
  3. package/dist/lib/event/event-initializations.js.map +1 -1
  4. package/dist/lib/event/event-listeners/event-listeners.d.ts +1 -0
  5. package/dist/lib/event/event-listeners/event-listeners.d.ts.map +1 -1
  6. package/dist/lib/event/event-listeners/event-listeners.js +52 -0
  7. package/dist/lib/event/event-listeners/event-listeners.js.map +1 -1
  8. package/dist/lib/event/events/marketing-product-seed.event.d.ts +17 -0
  9. package/dist/lib/event/events/marketing-product-seed.event.d.ts.map +1 -0
  10. package/dist/lib/event/events/marketing-product-seed.event.js +12 -0
  11. package/dist/lib/event/events/marketing-product-seed.event.js.map +1 -0
  12. package/dist/lib/index.d.ts +165 -83
  13. package/dist/lib/index.d.ts.map +1 -1
  14. package/dist/lib/modules/coupon/routers/coupon.router.d.ts +116 -0
  15. package/dist/lib/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  16. package/dist/lib/modules/coupon/routers/coupon.router.js +33 -1
  17. package/dist/lib/modules/coupon/routers/coupon.router.js.map +1 -1
  18. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts +32 -0
  19. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -0
  20. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js +503 -0
  21. package/dist/lib/modules/coupon/services/patterns/repository/CouponMetricsRepository.js.map +1 -0
  22. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts +407 -1
  23. package/dist/lib/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  24. package/dist/lib/modules/coupon/validations/coupon.validation.js +39 -1
  25. package/dist/lib/modules/coupon/validations/coupon.validation.js.map +1 -1
  26. package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts +1118 -0
  27. package/dist/lib/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -0
  28. package/dist/lib/modules/coupon/validations/create-coupon.validation.js +13 -0
  29. package/dist/lib/modules/coupon/validations/create-coupon.validation.js.map +1 -0
  30. package/dist/lib/modules/coupon/validations/index.d.ts +1 -0
  31. package/dist/lib/modules/coupon/validations/index.d.ts.map +1 -1
  32. package/dist/lib/modules/coupon/validations/index.js +1 -0
  33. package/dist/lib/modules/coupon/validations/index.js.map +1 -1
  34. package/dist/lib/modules/limited-time-offer/campaign/campaign.router.d.ts +29 -72
  35. package/dist/lib/modules/limited-time-offer/campaign/campaign.router.d.ts.map +1 -1
  36. package/dist/lib/modules/limited-time-offer/campaign/campaign.router.js +3 -16
  37. package/dist/lib/modules/limited-time-offer/campaign/campaign.router.js.map +1 -1
  38. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.d.ts +36 -0
  39. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.d.ts.map +1 -0
  40. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.js +48 -0
  41. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.js.map +1 -0
  42. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.d.ts +22 -0
  43. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.d.ts.map +1 -0
  44. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.js +131 -0
  45. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.js.map +1 -0
  46. package/dist/lib/modules/limited-time-offer/campaign/limited-time-offer-campaign.validation.d.ts +78 -78
  47. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.d.ts +1 -2
  48. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.d.ts.map +1 -1
  49. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.js +6 -11
  50. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.js.map +1 -1
  51. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.d.ts +16 -51
  52. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.d.ts.map +1 -1
  53. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.js +33 -156
  54. package/dist/lib/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.js.map +1 -1
  55. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.builder.d.ts +8 -0
  56. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.builder.d.ts.map +1 -0
  57. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.builder.js +13 -0
  58. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.builder.js.map +1 -0
  59. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.repository.d.ts +14 -0
  60. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.repository.d.ts.map +1 -0
  61. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.repository.js +77 -0
  62. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.repository.js.map +1 -0
  63. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.service.d.ts +1 -2
  64. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.service.d.ts.map +1 -1
  65. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.service.js +19 -26
  66. package/dist/lib/modules/limited-time-offer/config/limited-time-offer-config.service.js.map +1 -1
  67. package/dist/lib/modules/limited-time-offer/config/limited-time-offer.router.d.ts.map +1 -1
  68. package/dist/lib/modules/limited-time-offer/config/limited-time-offer.router.js.map +1 -1
  69. package/dist/lib/modules/limited-time-offer/limited-time-offer.router.d.ts +35 -78
  70. package/dist/lib/modules/limited-time-offer/limited-time-offer.router.d.ts.map +1 -1
  71. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.builder.d.ts +53 -0
  72. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.builder.d.ts.map +1 -0
  73. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.builder.js +55 -0
  74. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.builder.js.map +1 -0
  75. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.repository.d.ts +18 -0
  76. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.repository.d.ts.map +1 -0
  77. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.repository.js +126 -0
  78. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.repository.js.map +1 -0
  79. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.router.d.ts +6 -6
  80. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.router.d.ts.map +1 -1
  81. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.router.js +1 -2
  82. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.router.js.map +1 -1
  83. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.d.ts +1 -0
  84. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.d.ts.map +1 -0
  85. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.js +2 -0
  86. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.service.js.map +1 -0
  87. package/dist/lib/modules/limited-time-offer/participant/limited-time-offer-participant.validation.d.ts +24 -24
  88. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts +3 -1
  89. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts.map +1 -1
  90. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.js +13 -38
  91. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.js.map +1 -1
  92. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts +19 -8
  93. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts.map +1 -1
  94. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.js +19 -27
  95. package/dist/lib/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.js.map +1 -1
  96. package/dist/lib/modules/marketing/routers/marketing.router.d.ts +12 -3
  97. package/dist/lib/modules/marketing/routers/marketing.router.d.ts.map +1 -1
  98. package/dist/lib/modules/marketing/routers/marketing.router.js +7 -0
  99. package/dist/lib/modules/marketing/routers/marketing.router.js.map +1 -1
  100. package/dist/lib/modules/marketing/services/buy1-get1-marketing.service.d.ts.map +1 -1
  101. package/dist/lib/modules/marketing/services/buy1-get1-marketing.service.js +11 -9
  102. package/dist/lib/modules/marketing/services/buy1-get1-marketing.service.js.map +1 -1
  103. package/dist/lib/modules/marketing/services/discount-marketing.service.d.ts.map +1 -1
  104. package/dist/lib/modules/marketing/services/discount-marketing.service.js +15 -13
  105. package/dist/lib/modules/marketing/services/discount-marketing.service.js.map +1 -1
  106. package/dist/lib/modules/marketing/services/marketing.service.d.ts +1 -0
  107. package/dist/lib/modules/marketing/services/marketing.service.d.ts.map +1 -1
  108. package/dist/lib/modules/marketing/services/marketing.service.js +57 -7
  109. package/dist/lib/modules/marketing/services/marketing.service.js.map +1 -1
  110. package/dist/lib/modules/marketing/services/product-marketing.service.d.ts +11 -0
  111. package/dist/lib/modules/marketing/services/product-marketing.service.d.ts.map +1 -1
  112. package/dist/lib/modules/marketing/services/product-marketing.service.js +23 -0
  113. package/dist/lib/modules/marketing/services/product-marketing.service.js.map +1 -1
  114. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.d.ts.map +1 -1
  115. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.js +6 -31
  116. package/dist/lib/modules/marketing/services/synchronize-product-marketing.service.js.map +1 -1
  117. package/dist/lib/modules/marketing/validations/marketing.validation.d.ts +2 -2
  118. package/dist/lib/modules/marketing/validations/product-marketing.validation.d.ts +2 -2
  119. package/dist/lib/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +2 -2
  120. package/dist/lib/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +4 -4
  121. package/dist/types/event/event-initializations.d.ts.map +1 -1
  122. package/dist/types/event/event-listeners/event-listeners.d.ts +1 -0
  123. package/dist/types/event/event-listeners/event-listeners.d.ts.map +1 -1
  124. package/dist/types/event/events/marketing-product-seed.event.d.ts +17 -0
  125. package/dist/types/event/events/marketing-product-seed.event.d.ts.map +1 -0
  126. package/dist/types/index.d.ts +165 -83
  127. package/dist/types/index.d.ts.map +1 -1
  128. package/dist/types/modules/coupon/routers/coupon.router.d.ts +116 -0
  129. package/dist/types/modules/coupon/routers/coupon.router.d.ts.map +1 -1
  130. package/dist/types/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts +32 -0
  131. package/dist/types/modules/coupon/services/patterns/repository/CouponMetricsRepository.d.ts.map +1 -0
  132. package/dist/types/modules/coupon/validations/coupon.validation.d.ts +407 -1
  133. package/dist/types/modules/coupon/validations/coupon.validation.d.ts.map +1 -1
  134. package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts +1118 -0
  135. package/dist/types/modules/coupon/validations/create-coupon.validation.d.ts.map +1 -0
  136. package/dist/types/modules/coupon/validations/index.d.ts +1 -0
  137. package/dist/types/modules/coupon/validations/index.d.ts.map +1 -1
  138. package/dist/types/modules/limited-time-offer/campaign/campaign.router.d.ts +29 -72
  139. package/dist/types/modules/limited-time-offer/campaign/campaign.router.d.ts.map +1 -1
  140. package/dist/types/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.d.ts +36 -0
  141. package/dist/types/modules/limited-time-offer/campaign/limited-time-offer-campaign.builder.d.ts.map +1 -0
  142. package/dist/types/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.d.ts +22 -0
  143. package/dist/types/modules/limited-time-offer/campaign/limited-time-offer-campaign.repository.d.ts.map +1 -0
  144. package/dist/types/modules/limited-time-offer/campaign/limited-time-offer-campaign.validation.d.ts +78 -78
  145. package/dist/types/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.d.ts +1 -2
  146. package/dist/types/modules/limited-time-offer/campaign/services/limited-time-offer-campaign-lifecycle.service.d.ts.map +1 -1
  147. package/dist/types/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.d.ts +16 -51
  148. package/dist/types/modules/limited-time-offer/campaign/services/limited-time-offer-campaign.service.d.ts.map +1 -1
  149. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.builder.d.ts +8 -0
  150. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.builder.d.ts.map +1 -0
  151. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.repository.d.ts +14 -0
  152. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.repository.d.ts.map +1 -0
  153. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.service.d.ts +1 -2
  154. package/dist/types/modules/limited-time-offer/config/limited-time-offer-config.service.d.ts.map +1 -1
  155. package/dist/types/modules/limited-time-offer/config/limited-time-offer.router.d.ts.map +1 -1
  156. package/dist/types/modules/limited-time-offer/limited-time-offer.router.d.ts +35 -78
  157. package/dist/types/modules/limited-time-offer/limited-time-offer.router.d.ts.map +1 -1
  158. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.builder.d.ts +53 -0
  159. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.builder.d.ts.map +1 -0
  160. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.repository.d.ts +18 -0
  161. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.repository.d.ts.map +1 -0
  162. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.router.d.ts +6 -6
  163. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.router.d.ts.map +1 -1
  164. package/dist/types/modules/limited-time-offer/participant/limited-time-offer-participant.validation.d.ts +24 -24
  165. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts +3 -1
  166. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant-marketing.service.d.ts.map +1 -1
  167. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts +19 -8
  168. package/dist/types/modules/limited-time-offer/participant/services/limited-time-offer-participant.service.d.ts.map +1 -1
  169. package/dist/types/modules/marketing/routers/marketing.router.d.ts +12 -3
  170. package/dist/types/modules/marketing/routers/marketing.router.d.ts.map +1 -1
  171. package/dist/types/modules/marketing/services/buy1-get1-marketing.service.d.ts.map +1 -1
  172. package/dist/types/modules/marketing/services/discount-marketing.service.d.ts.map +1 -1
  173. package/dist/types/modules/marketing/services/marketing.service.d.ts +1 -0
  174. package/dist/types/modules/marketing/services/marketing.service.d.ts.map +1 -1
  175. package/dist/types/modules/marketing/services/product-marketing.service.d.ts +11 -0
  176. package/dist/types/modules/marketing/services/product-marketing.service.d.ts.map +1 -1
  177. package/dist/types/modules/marketing/services/synchronize-product-marketing.service.d.ts.map +1 -1
  178. package/dist/types/modules/marketing/validations/marketing.validation.d.ts +2 -2
  179. package/dist/types/modules/marketing/validations/product-marketing.validation.d.ts +2 -2
  180. package/dist/types/modules/punch-marketing-history/routers/punch-marketing-history.router.d.ts +2 -2
  181. package/dist/types/modules/punch-marketing-history/validations/punch-marketing-history.validation.d.ts +4 -4
  182. package/package.json +2 -2
  183. package/dist/types/events/event-initializations.d.ts +0 -2
  184. package/dist/types/events/event-initializations.d.ts.map +0 -1
@@ -0,0 +1,503 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CouponMetricsRepository = void 0;
4
+ const models_1 = require("@lyxa.ai/core/dist/libraries/mongo/models");
5
+ const dayjs_1 = require("@lyxa.ai/core/dist/utilities/dayjs");
6
+ const enum_1 = require("@lyxa.ai/core/dist/utilities/enum");
7
+ const shared_1 = require("@lyxa.ai/core/dist/utilities/shared");
8
+ const time_1 = require("@lyxa.ai/core/dist/utilities/time");
9
+ const utils_1 = require("../../../../../utility/utils");
10
+ class CouponMetricsRepository {
11
+ model;
12
+ orderModel;
13
+ constructor() {
14
+ this.model = models_1.CouponModel;
15
+ this.orderModel = models_1.OrderModel;
16
+ }
17
+ getBaseCouponFilter() {
18
+ return {
19
+ updatedVersion: { $exists: false },
20
+ couponType: { $nin: [enum_1.CouponType.REFERRAL_CODE, enum_1.CouponType.REFERRAL_REWARD] },
21
+ deletedAt: null,
22
+ };
23
+ }
24
+ getCouponCreatedWithinDateRangeFilter(startDate, endDate) {
25
+ return {
26
+ ...this.getBaseCouponFilter(),
27
+ createdAt: {
28
+ $gte: startDate,
29
+ $lte: endDate,
30
+ },
31
+ };
32
+ }
33
+ getCouponOrderMatch(startDate, endDate) {
34
+ return {
35
+ placedAt: {
36
+ $gte: startDate,
37
+ $lte: endDate,
38
+ },
39
+ status: enum_1.RegularOrderStatus.DELIVERED,
40
+ 'adjustedFinance.coupon.coupon': { $ne: null },
41
+ };
42
+ }
43
+ async getActiveCouponsCount(now) {
44
+ return this.model.countDocuments({
45
+ ...this.getBaseCouponFilter(),
46
+ status: enum_1.Status.ACTIVE,
47
+ 'duration.start': { $lte: now },
48
+ 'duration.end': { $gte: now },
49
+ });
50
+ }
51
+ async getTotalCreatedCoupons(startDate, endDate) {
52
+ return this.model
53
+ .find({
54
+ ...this.getCouponCreatedWithinDateRangeFilter(startDate, endDate),
55
+ })
56
+ .select('_id');
57
+ }
58
+ async getCouponUsageMetrics(startDate, endDate, coupons = []) {
59
+ const match = {
60
+ ...this.getCouponOrderMatch(startDate, endDate),
61
+ 'adjustedFinance.coupon.coupon': { $in: coupons },
62
+ };
63
+ const [result] = await this.orderModel.aggregate([
64
+ {
65
+ $match: match,
66
+ },
67
+ {
68
+ $group: {
69
+ _id: null,
70
+ totalCouponOrders: { $sum: 1 },
71
+ totalRevenue: {
72
+ $sum: '$adjustedFinance.pricing.total',
73
+ },
74
+ totalProfit: {
75
+ $sum: '$adjustedFinance.companyProfit.totalProfit',
76
+ },
77
+ uniqueCoupons: {
78
+ $addToSet: '$adjustedFinance.coupon.coupon',
79
+ },
80
+ },
81
+ },
82
+ {
83
+ $project: {
84
+ _id: 0,
85
+ totalCouponOrders: 1,
86
+ totalRevenue: 1,
87
+ totalProfit: 1,
88
+ redeemedCoupons: {
89
+ $size: '$uniqueCoupons',
90
+ },
91
+ },
92
+ },
93
+ ]);
94
+ return (result ?? {
95
+ totalCouponOrders: 0,
96
+ totalRevenue: 0,
97
+ totalProfit: 0,
98
+ redeemedCoupons: 0,
99
+ });
100
+ }
101
+ async getCouponMetricsByType(duration) {
102
+ const startDate = (0, dayjs_1.dayjs)(duration.startDate).startOf('day').toDate();
103
+ const endDate = (0, dayjs_1.dayjs)(duration.endDate).endOf('day').toDate();
104
+ const [totalOrders, activeCouponsCount] = await Promise.all([
105
+ this.orderModel.countDocuments({
106
+ placedAt: {
107
+ $gte: startDate,
108
+ $lte: endDate,
109
+ },
110
+ status: enum_1.RegularOrderStatus.DELIVERED,
111
+ }),
112
+ this.model.aggregate([
113
+ {
114
+ $match: {
115
+ ...this.getBaseCouponFilter(),
116
+ status: enum_1.Status.ACTIVE,
117
+ 'duration.start': { $lte: (0, dayjs_1.dayjs)().toDate() },
118
+ 'duration.end': { $gte: (0, dayjs_1.dayjs)().toDate() },
119
+ },
120
+ },
121
+ {
122
+ $group: {
123
+ _id: '$couponType',
124
+ count: { $sum: 1 },
125
+ },
126
+ },
127
+ ]),
128
+ ]);
129
+ const activeCouponsMap = activeCouponsCount.reduce((acc, curr) => {
130
+ acc[curr._id] = curr.count;
131
+ return acc;
132
+ }, {});
133
+ const result = await this.orderModel.aggregate([
134
+ {
135
+ $match: {
136
+ ...this.getCouponOrderMatch(startDate, endDate),
137
+ },
138
+ },
139
+ {
140
+ $lookup: {
141
+ from: 'coupons',
142
+ localField: 'adjustedFinance.coupon.coupon',
143
+ foreignField: '_id',
144
+ as: 'couponDetails',
145
+ },
146
+ },
147
+ {
148
+ $unwind: '$couponDetails',
149
+ },
150
+ {
151
+ $match: {
152
+ 'couponDetails.couponType': { $nin: [enum_1.CouponType.REFERRAL_CODE, enum_1.CouponType.REFERRAL_REWARD] },
153
+ },
154
+ },
155
+ {
156
+ $group: {
157
+ _id: '$couponDetails.couponType',
158
+ couponType: { $first: '$couponDetails.couponType' },
159
+ totalCouponOrders: { $sum: 1 },
160
+ totalRevenue: {
161
+ $sum: '$adjustedFinance.pricing.total',
162
+ },
163
+ totalProfit: {
164
+ $sum: '$adjustedFinance.companyProfit.totalProfit',
165
+ },
166
+ amountSpent: {
167
+ $sum: '$adjustedFinance.pricing.couponDiscount',
168
+ },
169
+ },
170
+ },
171
+ {
172
+ $addFields: {
173
+ averageOrderValue: {
174
+ $cond: [
175
+ { $eq: ['$totalCouponOrders', 0] },
176
+ 0,
177
+ {
178
+ $round: [{ $divide: ['$totalRevenue', '$totalCouponOrders'] }, 2],
179
+ },
180
+ ],
181
+ },
182
+ usage: {
183
+ $cond: [
184
+ { $eq: [totalOrders, 0] },
185
+ 0,
186
+ {
187
+ $multiply: [
188
+ 100,
189
+ {
190
+ $round: [{ $divide: ['$totalCouponOrders', totalOrders] }, 2],
191
+ },
192
+ ],
193
+ },
194
+ ],
195
+ },
196
+ },
197
+ },
198
+ {
199
+ $project: {
200
+ _id: 0,
201
+ couponType: 1,
202
+ totalCouponOrders: 1,
203
+ totalRevenue: { $round: ['$totalRevenue', 2] },
204
+ totalProfit: { $round: ['$totalProfit', 2] },
205
+ averageOrderValue: 1,
206
+ usage: 1,
207
+ },
208
+ },
209
+ ]);
210
+ this.fillMissingRows(result);
211
+ const tableData = result.map(item => ({
212
+ ...item,
213
+ couponTypeTitle: this.getCouponTypeTitle(item.couponType),
214
+ ongoing: activeCouponsMap[item.couponType] ?? 0,
215
+ redemptionRate: 0,
216
+ }));
217
+ return tableData;
218
+ }
219
+ getCouponTypeTitle(couponType) {
220
+ const couponTypeTitles = {
221
+ [enum_1.CouponType.GLOBAL]: 'Global',
222
+ [enum_1.CouponType.SHOPS_CATEGORIES]: 'Shop/Category',
223
+ [enum_1.CouponType.INDIVIDUAL_STORE]: 'Single Shop',
224
+ [enum_1.CouponType.INDIVIDUAL_USER]: 'Individual User',
225
+ [enum_1.CouponType.CUSTOM_COUPON]: 'Customized Coupon',
226
+ [enum_1.CouponType.REFERRAL_CODE]: 'Referral Code',
227
+ [enum_1.CouponType.REFERRAL_REWARD]: 'Referral Reward',
228
+ };
229
+ return couponTypeTitles[couponType] || 'Unknown';
230
+ }
231
+ fillMissingRows(data) {
232
+ const missingTypes = Object.values(enum_1.CouponType).filter(type => ![enum_1.CouponType.REFERRAL_CODE, enum_1.CouponType.REFERRAL_REWARD].includes(type) &&
233
+ !data.some(row => row.couponType === type));
234
+ missingTypes.forEach(type => {
235
+ data.push({
236
+ couponType: type,
237
+ couponTypeTitle: this.getCouponTypeTitle(type),
238
+ totalCouponOrders: 0,
239
+ totalRevenue: 0,
240
+ totalProfit: 0,
241
+ averageOrderValue: 0,
242
+ usage: 0,
243
+ ongoing: 0,
244
+ redemptionRate: 0,
245
+ });
246
+ });
247
+ data.sort((a, b) => this.compareCouponTypes(a.couponType, b.couponType));
248
+ }
249
+ compareCouponTypes(a, b) {
250
+ const order = [
251
+ enum_1.CouponType.GLOBAL,
252
+ enum_1.CouponType.SHOPS_CATEGORIES,
253
+ enum_1.CouponType.INDIVIDUAL_STORE,
254
+ enum_1.CouponType.INDIVIDUAL_USER,
255
+ enum_1.CouponType.CUSTOM_COUPON,
256
+ ];
257
+ return order.indexOf(a) - order.indexOf(b);
258
+ }
259
+ calculateRedemptionRate(totalCreated, redeemedCoupons) {
260
+ if (totalCreated <= 0) {
261
+ return 0;
262
+ }
263
+ return Number(((redeemedCoupons / totalCreated) * 100).toFixed(2));
264
+ }
265
+ async getCouponMetrics(duration) {
266
+ const startDate = (0, dayjs_1.dayjs)(duration.startDate).startOf('day').toDate();
267
+ const endDate = (0, dayjs_1.dayjs)(duration.endDate).endOf('day').toDate();
268
+ const now = new Date();
269
+ const [activeCoupons, createdCoupons] = await Promise.all([
270
+ this.getActiveCouponsCount(now),
271
+ this.getTotalCreatedCoupons(startDate, endDate),
272
+ ]);
273
+ const createdCouponIds = createdCoupons.map(coupon => coupon._id);
274
+ const usageMetrics = await this.getCouponUsageMetrics(startDate, endDate, createdCouponIds);
275
+ const redemptionRate = this.calculateRedemptionRate(createdCouponIds.length, usageMetrics.redeemedCoupons);
276
+ return {
277
+ activeCoupons,
278
+ totalCreated: createdCouponIds.length,
279
+ redemptionRate,
280
+ totalCouponOrders: usageMetrics.totalCouponOrders,
281
+ totalRevenue: (0, shared_1.roundBaseCurrency)(usageMetrics.totalRevenue),
282
+ totalProfit: (0, shared_1.roundBaseCurrency)(usageMetrics.totalProfit),
283
+ };
284
+ }
285
+ getOrderGraphMatchStage(startDate, endDate, itemType) {
286
+ return {
287
+ placedAt: {
288
+ $gte: startDate,
289
+ $lte: endDate,
290
+ },
291
+ orderType: enum_1.OrderType.REGULAR,
292
+ status: enum_1.RegularOrderStatus.DELIVERED,
293
+ ...(itemType
294
+ ? {
295
+ itemType,
296
+ }
297
+ : {}),
298
+ };
299
+ }
300
+ async getCouponOrdersGraphData(data) {
301
+ const startDate = (0, dayjs_1.dayjs)(data.startDate).startOf('day').toDate();
302
+ const endDate = (0, dayjs_1.dayjs)(data.endDate).endOf('day').toDate();
303
+ const dateFormat = data.graphType === enum_1.GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';
304
+ const result = await this.orderModel.aggregate([
305
+ {
306
+ $match: {
307
+ ...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),
308
+ },
309
+ },
310
+ {
311
+ $addFields: {
312
+ hasCoupon: {
313
+ $cond: [{ $ifNull: ['$adjustedFinance.coupon.coupon', false] }, 1, 0],
314
+ },
315
+ date: {
316
+ $dateToString: {
317
+ format: dateFormat,
318
+ date: '$placedAt',
319
+ timezone: time_1.LyxaTimezone,
320
+ },
321
+ },
322
+ },
323
+ },
324
+ {
325
+ $group: {
326
+ _id: '$date',
327
+ totalOrders: { $sum: 1 },
328
+ ordersWithCoupon: {
329
+ $sum: '$hasCoupon',
330
+ },
331
+ },
332
+ },
333
+ {
334
+ $project: {
335
+ _id: 0,
336
+ date: '$_id',
337
+ totalOrders: 1,
338
+ ordersWithCoupon: 1,
339
+ ordersWithoutCoupon: { $subtract: ['$totalOrders', '$ordersWithCoupon'] },
340
+ },
341
+ },
342
+ {
343
+ $sort: { date: 1 },
344
+ },
345
+ ]);
346
+ const graphData = (0, utils_1.fillMissingPeriods)(result, data.startDate, data.endDate, data.graphType, {
347
+ totalOrders: 0,
348
+ ordersWithCoupon: 0,
349
+ ordersWithoutCoupon: 0,
350
+ });
351
+ return graphData;
352
+ }
353
+ async getUsersNewVsReturningGraphData(data) {
354
+ const startDate = (0, dayjs_1.dayjs)(data.startDate).startOf('day').toDate();
355
+ const endDate = (0, dayjs_1.dayjs)(data.endDate).endOf('day').toDate();
356
+ const dateFormat = data.graphType === enum_1.GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';
357
+ const result = await this.orderModel.aggregate([
358
+ {
359
+ $match: {
360
+ ...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),
361
+ 'adjustedFinance.coupon.coupon': { $ne: null },
362
+ },
363
+ },
364
+ {
365
+ $addFields: {
366
+ date: {
367
+ $dateToString: {
368
+ format: dateFormat,
369
+ date: '$placedAt',
370
+ timezone: time_1.LyxaTimezone,
371
+ },
372
+ },
373
+ },
374
+ },
375
+ {
376
+ $group: {
377
+ _id: {
378
+ user: '$user',
379
+ date: '$date',
380
+ },
381
+ },
382
+ },
383
+ {
384
+ $lookup: {
385
+ from: 'orders',
386
+ let: { userId: '$_id.user' },
387
+ pipeline: [
388
+ {
389
+ $match: {
390
+ $expr: {
391
+ $and: [
392
+ { $eq: ['$user', '$$userId'] },
393
+ { $eq: ['$status', enum_1.RegularOrderStatus.DELIVERED] },
394
+ {
395
+ $expr: {
396
+ $lt: [
397
+ {
398
+ $ifNull: ['$placedAt', '$createdAt'],
399
+ },
400
+ startDate,
401
+ ],
402
+ },
403
+ },
404
+ ],
405
+ },
406
+ },
407
+ },
408
+ { $limit: 1 },
409
+ ],
410
+ as: 'previousOrders',
411
+ },
412
+ },
413
+ {
414
+ $addFields: {
415
+ isReturning: {
416
+ $gt: [{ $size: '$previousOrders' }, 0],
417
+ },
418
+ date: '$_id.date',
419
+ },
420
+ },
421
+ {
422
+ $group: {
423
+ _id: '$date',
424
+ totalUsers: { $sum: 1 },
425
+ returningUsers: {
426
+ $sum: {
427
+ $cond: ['$isReturning', 1, 0],
428
+ },
429
+ },
430
+ newUsers: {
431
+ $sum: {
432
+ $cond: ['$isReturning', 0, 1],
433
+ },
434
+ },
435
+ },
436
+ },
437
+ {
438
+ $project: {
439
+ _id: 0,
440
+ date: '$_id',
441
+ totalUsers: 1,
442
+ newUsers: 1,
443
+ returningUsers: 1,
444
+ },
445
+ },
446
+ { $sort: { date: 1 } },
447
+ ]);
448
+ const graphData = (0, utils_1.fillMissingPeriods)(result, data.startDate, data.endDate, data.graphType, {
449
+ totalUsers: 0,
450
+ newUsers: 0,
451
+ returningUsers: 0,
452
+ });
453
+ return graphData;
454
+ }
455
+ async getCouponProfitGraphData(data) {
456
+ const startDate = (0, dayjs_1.dayjs)(data.startDate).startOf('day').toDate();
457
+ const endDate = (0, dayjs_1.dayjs)(data.endDate).endOf('day').toDate();
458
+ const dateFormat = data.graphType === enum_1.GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';
459
+ const result = await this.orderModel.aggregate([
460
+ {
461
+ $match: {
462
+ ...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),
463
+ 'adjustedFinance.coupon.coupon': { $ne: null },
464
+ },
465
+ },
466
+ {
467
+ $addFields: {
468
+ date: {
469
+ $dateToString: {
470
+ format: dateFormat,
471
+ date: '$placedAt',
472
+ timezone: time_1.LyxaTimezone,
473
+ },
474
+ },
475
+ },
476
+ },
477
+ {
478
+ $group: {
479
+ _id: '$date',
480
+ totalProfit: {
481
+ $sum: '$adjustedFinance.companyProfit.totalProfit',
482
+ },
483
+ },
484
+ },
485
+ {
486
+ $project: {
487
+ _id: 0,
488
+ date: '$_id',
489
+ totalProfit: 1,
490
+ },
491
+ },
492
+ {
493
+ $sort: { date: 1 },
494
+ },
495
+ ]);
496
+ const graphData = (0, utils_1.fillMissingPeriods)(result, data.startDate, data.endDate, data.graphType, {
497
+ totalProfit: 0,
498
+ });
499
+ return graphData;
500
+ }
501
+ }
502
+ exports.CouponMetricsRepository = CouponMetricsRepository;
503
+ //# sourceMappingURL=CouponMetricsRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CouponMetricsRepository.js","sourceRoot":"/","sources":["modules/coupon/services/patterns/repository/CouponMetricsRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { Coupon, CouponModel, OrderModel } from '@lyxa.ai/core/dist/libraries/mongo/models';\nimport { Duration } from '@lyxa.ai/core/dist/libraries/mongo/models/shared/duration.model';\nimport { dayjs } from '@lyxa.ai/core/dist/utilities/dayjs';\nimport {\n\tCouponType,\n\tGraphType,\n\tItemType,\n\tOrderType,\n\tRegularOrderStatus,\n\tStatus,\n} from '@lyxa.ai/core/dist/utilities/enum';\nimport { roundBaseCurrency } from '@lyxa.ai/core/dist/utilities/shared';\nimport { LyxaTimezone } from '@lyxa.ai/core/dist/utilities/time';\nimport {\n\tCouponMetricsByTypeDTO,\n\tCouponMetricsOutputDTO,\n\tDateInputDTO,\n\tGraphInputDTO,\n} from '@modules/coupon/validations/coupon.validation';\nimport { mongoose } from '@typegoose/typegoose';\nimport { fillMissingPeriods } from '@utility/utils';\n\nexport class CouponMetricsRepository {\n\tprivate readonly model: typeof CouponModel;\n\tprivate orderModel: typeof OrderModel;\n\n\tconstructor() {\n\t\tthis.model = CouponModel as typeof CouponModel;\n\t\tthis.orderModel = OrderModel as typeof OrderModel;\n\t}\n\n\tprivate getBaseCouponFilter(): mongoose.FilterQuery<Coupon> {\n\t\treturn {\n\t\t\tupdatedVersion: { $exists: false },\n\t\t\tcouponType: { $nin: [CouponType.REFERRAL_CODE, CouponType.REFERRAL_REWARD] },\n\t\t\tdeletedAt: null,\n\t\t};\n\t}\n\n\tprivate getCouponCreatedWithinDateRangeFilter(\n\t\tstartDate: Date,\n\t\tendDate: Date\n\t): mongoose.FilterQuery<Coupon> {\n\t\treturn {\n\t\t\t...this.getBaseCouponFilter(),\n\t\t\tcreatedAt: {\n\t\t\t\t$gte: startDate,\n\t\t\t\t$lte: endDate,\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate getCouponOrderMatch(startDate: Date, endDate: Date): mongoose.PipelineStage.Match['$match'] {\n\t\treturn {\n\t\t\tplacedAt: {\n\t\t\t\t$gte: startDate,\n\t\t\t\t$lte: endDate,\n\t\t\t},\n\t\t\tstatus: RegularOrderStatus.DELIVERED,\n\t\t\t'adjustedFinance.coupon.coupon': { $ne: null },\n\t\t};\n\t}\n\n\tprivate async getActiveCouponsCount(now: Date) {\n\t\treturn this.model.countDocuments({\n\t\t\t...this.getBaseCouponFilter(),\n\t\t\tstatus: Status.ACTIVE,\n\t\t\t'duration.start': { $lte: now },\n\t\t\t'duration.end': { $gte: now },\n\t\t});\n\t}\n\n\tprivate async getTotalCreatedCoupons(startDate: Date, endDate: Date) {\n\t\treturn this.model\n\t\t\t.find({\n\t\t\t\t...this.getCouponCreatedWithinDateRangeFilter(startDate, endDate),\n\t\t\t})\n\t\t\t.select('_id');\n\t}\n\n\tprivate async getCouponUsageMetrics(\n\t\tstartDate: Date,\n\t\tendDate: Date,\n\t\tcoupons: mongoose.Types.ObjectId[] = []\n\t) {\n\t\tconst match = {\n\t\t\t...this.getCouponOrderMatch(startDate, endDate),\n\t\t\t'adjustedFinance.coupon.coupon': { $in: coupons },\n\t\t};\n\n\t\tconst [result] = await this.orderModel.aggregate<{\n\t\t\ttotalCouponOrders: number;\n\t\t\ttotalRevenue: number;\n\t\t\ttotalProfit: number;\n\t\t\tredeemedCoupons: number;\n\t\t}>([\n\t\t\t{\n\t\t\t\t$match: match,\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: null,\n\n\t\t\t\t\ttotalCouponOrders: { $sum: 1 },\n\n\t\t\t\t\ttotalRevenue: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.pricing.total',\n\t\t\t\t\t},\n\n\t\t\t\t\ttotalProfit: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.companyProfit.totalProfit',\n\t\t\t\t\t},\n\n\t\t\t\t\tuniqueCoupons: {\n\t\t\t\t\t\t$addToSet: '$adjustedFinance.coupon.coupon',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\t_id: 0,\n\t\t\t\t\ttotalCouponOrders: 1,\n\t\t\t\t\ttotalRevenue: 1,\n\t\t\t\t\ttotalProfit: 1,\n\t\t\t\t\tredeemedCoupons: {\n\t\t\t\t\t\t$size: '$uniqueCoupons',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t]);\n\n\t\treturn (\n\t\t\tresult ?? {\n\t\t\t\ttotalCouponOrders: 0,\n\t\t\t\ttotalRevenue: 0,\n\t\t\t\ttotalProfit: 0,\n\t\t\t\tredeemedCoupons: 0,\n\t\t\t}\n\t\t);\n\t}\n\n\tpublic async getCouponMetricsByType(duration: DateInputDTO) {\n\t\tconst startDate = dayjs(duration.startDate).startOf('day').toDate();\n\t\tconst endDate = dayjs(duration.endDate).endOf('day').toDate();\n\n\t\tconst [totalOrders, activeCouponsCount] = await Promise.all([\n\t\t\tthis.orderModel.countDocuments({\n\t\t\t\tplacedAt: {\n\t\t\t\t\t$gte: startDate,\n\t\t\t\t\t$lte: endDate,\n\t\t\t\t},\n\t\t\t\tstatus: RegularOrderStatus.DELIVERED,\n\t\t\t}),\n\t\t\tthis.model.aggregate([\n\t\t\t\t{\n\t\t\t\t\t$match: {\n\t\t\t\t\t\t...this.getBaseCouponFilter(),\n\t\t\t\t\t\tstatus: Status.ACTIVE,\n\t\t\t\t\t\t'duration.start': { $lte: dayjs().toDate() },\n\t\t\t\t\t\t'duration.end': { $gte: dayjs().toDate() },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t$group: {\n\t\t\t\t\t\t_id: '$couponType',\n\t\t\t\t\t\tcount: { $sum: 1 },\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t]),\n\t\t]);\n\n\t\tconst activeCouponsMap = activeCouponsCount.reduce(\n\t\t\t(acc, curr) => {\n\t\t\t\tacc[curr._id] = curr.count;\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, number>\n\t\t);\n\n\t\tconst result = await this.orderModel.aggregate([\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t...this.getCouponOrderMatch(startDate, endDate),\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'coupons',\n\t\t\t\t\tlocalField: 'adjustedFinance.coupon.coupon',\n\t\t\t\t\tforeignField: '_id',\n\t\t\t\t\tas: 'couponDetails',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$unwind: '$couponDetails',\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t'couponDetails.couponType': { $nin: [CouponType.REFERRAL_CODE, CouponType.REFERRAL_REWARD] },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$couponDetails.couponType',\n\n\t\t\t\t\tcouponType: { $first: '$couponDetails.couponType' },\n\n\t\t\t\t\ttotalCouponOrders: { $sum: 1 },\n\n\t\t\t\t\ttotalRevenue: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.pricing.total',\n\t\t\t\t\t},\n\n\t\t\t\t\ttotalProfit: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.companyProfit.totalProfit',\n\t\t\t\t\t},\n\n\t\t\t\t\tamountSpent: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.pricing.couponDiscount',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$addFields: {\n\t\t\t\t\taverageOrderValue: {\n\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t{ $eq: ['$totalCouponOrders', 0] },\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$round: [{ $divide: ['$totalRevenue', '$totalCouponOrders'] }, 2],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\n\t\t\t\t\tusage: {\n\t\t\t\t\t\t$cond: [\n\t\t\t\t\t\t\t{ $eq: [totalOrders, 0] },\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t$multiply: [\n\t\t\t\t\t\t\t\t\t100,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t$round: [{ $divide: ['$totalCouponOrders', totalOrders] }, 2],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\t_id: 0,\n\t\t\t\t\tcouponType: 1,\n\t\t\t\t\ttotalCouponOrders: 1,\n\t\t\t\t\ttotalRevenue: { $round: ['$totalRevenue', 2] },\n\t\t\t\t\ttotalProfit: { $round: ['$totalProfit', 2] },\n\t\t\t\t\taverageOrderValue: 1,\n\t\t\t\t\tusage: 1,\n\t\t\t\t},\n\t\t\t},\n\t\t]);\n\n\t\tthis.fillMissingRows(result);\n\n\t\tconst tableData = result.map(item => ({\n\t\t\t...item,\n\t\t\tcouponTypeTitle: this.getCouponTypeTitle(item.couponType),\n\t\t\tongoing: activeCouponsMap[item.couponType] ?? 0,\n\t\t\tredemptionRate: 0, // calculate after business discussion on how to define redemption rate by coupon type\n\t\t}));\n\n\t\treturn tableData;\n\t}\n\n\tprivate getCouponTypeTitle(couponType: CouponType) {\n\t\tconst couponTypeTitles: Record<CouponType, string> = {\n\t\t\t[CouponType.GLOBAL]: 'Global',\n\t\t\t[CouponType.SHOPS_CATEGORIES]: 'Shop/Category',\n\t\t\t[CouponType.INDIVIDUAL_STORE]: 'Single Shop',\n\t\t\t[CouponType.INDIVIDUAL_USER]: 'Individual User',\n\t\t\t[CouponType.CUSTOM_COUPON]: 'Customized Coupon',\n\t\t\t[CouponType.REFERRAL_CODE]: 'Referral Code',\n\t\t\t[CouponType.REFERRAL_REWARD]: 'Referral Reward',\n\t\t};\n\t\treturn couponTypeTitles[couponType] || 'Unknown';\n\t}\n\n\tprivate fillMissingRows(data: CouponMetricsByTypeDTO[]) {\n\t\tconst missingTypes = Object.values(CouponType).filter(\n\t\t\ttype =>\n\t\t\t\t![CouponType.REFERRAL_CODE, CouponType.REFERRAL_REWARD].includes(type) &&\n\t\t\t\t!data.some(row => row.couponType === type)\n\t\t);\n\n\t\tmissingTypes.forEach(type => {\n\t\t\tdata.push({\n\t\t\t\tcouponType: type,\n\t\t\t\tcouponTypeTitle: this.getCouponTypeTitle(type),\n\t\t\t\ttotalCouponOrders: 0,\n\t\t\t\ttotalRevenue: 0,\n\t\t\t\ttotalProfit: 0,\n\t\t\t\taverageOrderValue: 0,\n\t\t\t\tusage: 0,\n\t\t\t\tongoing: 0,\n\t\t\t\tredemptionRate: 0,\n\t\t\t});\n\t\t});\n\n\t\tdata.sort((a, b) => this.compareCouponTypes(a.couponType, b.couponType));\n\t}\n\n\tprivate compareCouponTypes(a: CouponType, b: CouponType) {\n\t\tconst order = [\n\t\t\tCouponType.GLOBAL,\n\t\t\tCouponType.SHOPS_CATEGORIES,\n\t\t\tCouponType.INDIVIDUAL_STORE,\n\t\t\tCouponType.INDIVIDUAL_USER,\n\t\t\tCouponType.CUSTOM_COUPON,\n\t\t];\n\t\treturn order.indexOf(a) - order.indexOf(b);\n\t}\n\n\tprivate calculateRedemptionRate(totalCreated: number, redeemedCoupons: number) {\n\t\tif (totalCreated <= 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn Number(((redeemedCoupons / totalCreated) * 100).toFixed(2));\n\t}\n\n\tpublic async getCouponMetrics(duration: DateInputDTO): Promise<CouponMetricsOutputDTO> {\n\t\tconst startDate = dayjs(duration.startDate).startOf('day').toDate();\n\t\tconst endDate = dayjs(duration.endDate).endOf('day').toDate();\n\t\tconst now = new Date();\n\n\t\tconst [activeCoupons, createdCoupons] = await Promise.all([\n\t\t\tthis.getActiveCouponsCount(now),\n\t\t\tthis.getTotalCreatedCoupons(startDate, endDate),\n\t\t]);\n\n\t\tconst createdCouponIds = createdCoupons.map(coupon => coupon._id);\n\n\t\tconst usageMetrics = await this.getCouponUsageMetrics(startDate, endDate, createdCouponIds);\n\n\t\tconst redemptionRate = this.calculateRedemptionRate(\n\t\t\tcreatedCouponIds.length,\n\t\t\tusageMetrics.redeemedCoupons\n\t\t);\n\n\t\treturn {\n\t\t\tactiveCoupons,\n\n\t\t\ttotalCreated: createdCouponIds.length,\n\t\t\tredemptionRate,\n\n\t\t\ttotalCouponOrders: usageMetrics.totalCouponOrders,\n\t\t\ttotalRevenue: roundBaseCurrency(usageMetrics.totalRevenue),\n\t\t\ttotalProfit: roundBaseCurrency(usageMetrics.totalProfit),\n\t\t};\n\t}\n\n\tpublic getOrderGraphMatchStage(startDate: Date, endDate: Date, itemType?: ItemType) {\n\t\treturn {\n\t\t\tplacedAt: {\n\t\t\t\t$gte: startDate,\n\t\t\t\t$lte: endDate,\n\t\t\t},\n\t\t\torderType: OrderType.REGULAR,\n\t\t\tstatus: RegularOrderStatus.DELIVERED,\n\t\t\t...(itemType\n\t\t\t\t? {\n\t\t\t\t\t\titemType,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\tpublic async getCouponOrdersGraphData(data: GraphInputDTO) {\n\t\tconst startDate = dayjs(data.startDate).startOf('day').toDate();\n\t\tconst endDate = dayjs(data.endDate).endOf('day').toDate();\n\n\t\tconst dateFormat = data.graphType === GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';\n\n\t\tconst result = await this.orderModel.aggregate([\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$addFields: {\n\t\t\t\t\thasCoupon: {\n\t\t\t\t\t\t$cond: [{ $ifNull: ['$adjustedFinance.coupon.coupon', false] }, 1, 0],\n\t\t\t\t\t},\n\t\t\t\t\tdate: {\n\t\t\t\t\t\t$dateToString: {\n\t\t\t\t\t\t\tformat: dateFormat,\n\t\t\t\t\t\t\tdate: '$placedAt',\n\t\t\t\t\t\t\ttimezone: LyxaTimezone,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$date',\n\n\t\t\t\t\ttotalOrders: { $sum: 1 },\n\n\t\t\t\t\tordersWithCoupon: {\n\t\t\t\t\t\t$sum: '$hasCoupon',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\t_id: 0,\n\t\t\t\t\tdate: '$_id',\n\t\t\t\t\ttotalOrders: 1,\n\t\t\t\t\tordersWithCoupon: 1,\n\t\t\t\t\tordersWithoutCoupon: { $subtract: ['$totalOrders', '$ordersWithCoupon'] },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$sort: { date: 1 },\n\t\t\t},\n\t\t]);\n\n\t\t// Fill in missing dates with zero for graph continuity\n\t\tconst graphData = fillMissingPeriods(result, data.startDate, data.endDate, data.graphType, {\n\t\t\ttotalOrders: 0,\n\t\t\tordersWithCoupon: 0,\n\t\t\tordersWithoutCoupon: 0,\n\t\t});\n\n\t\treturn graphData;\n\t}\n\n\tpublic async getUsersNewVsReturningGraphData(data: GraphInputDTO) {\n\t\tconst startDate = dayjs(data.startDate).startOf('day').toDate();\n\t\tconst endDate = dayjs(data.endDate).endOf('day').toDate();\n\n\t\tconst dateFormat = data.graphType === GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';\n\n\t\tconst result = await this.orderModel.aggregate([\n\t\t\t// Orders in selected period\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),\n\t\t\t\t\t'adjustedFinance.coupon.coupon': { $ne: null },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Build graph bucket\n\t\t\t{\n\t\t\t\t$addFields: {\n\t\t\t\t\tdate: {\n\t\t\t\t\t\t$dateToString: {\n\t\t\t\t\t\t\tformat: dateFormat,\n\t\t\t\t\t\t\tdate: '$placedAt',\n\t\t\t\t\t\t\ttimezone: LyxaTimezone,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Unique user per period bucket\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: {\n\t\t\t\t\t\tuser: '$user',\n\t\t\t\t\t\tdate: '$date',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Lookup prior order (existence check)\n\t\t\t{\n\t\t\t\t$lookup: {\n\t\t\t\t\tfrom: 'orders',\n\t\t\t\t\tlet: { userId: '$_id.user' },\n\t\t\t\t\tpipeline: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$match: {\n\t\t\t\t\t\t\t\t$expr: {\n\t\t\t\t\t\t\t\t\t$and: [\n\t\t\t\t\t\t\t\t\t\t{ $eq: ['$user', '$$userId'] },\n\t\t\t\t\t\t\t\t\t\t{ $eq: ['$status', RegularOrderStatus.DELIVERED] },\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t$expr: {\n\t\t\t\t\t\t\t\t\t\t\t\t$lt: [\n\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t$ifNull: ['$placedAt', '$createdAt'],\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartDate,\n\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ $limit: 1 },\n\t\t\t\t\t],\n\t\t\t\t\tas: 'previousOrders',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Classify\n\t\t\t{\n\t\t\t\t$addFields: {\n\t\t\t\t\tisReturning: {\n\t\t\t\t\t\t$gt: [{ $size: '$previousOrders' }, 0],\n\t\t\t\t\t},\n\t\t\t\t\tdate: '$_id.date',\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t// Aggregate graph metrics\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$date',\n\n\t\t\t\t\ttotalUsers: { $sum: 1 },\n\n\t\t\t\t\treturningUsers: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: ['$isReturning', 1, 0],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\n\t\t\t\t\tnewUsers: {\n\t\t\t\t\t\t$sum: {\n\t\t\t\t\t\t\t$cond: ['$isReturning', 0, 1],\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\t_id: 0,\n\t\t\t\t\tdate: '$_id',\n\t\t\t\t\ttotalUsers: 1,\n\t\t\t\t\tnewUsers: 1,\n\t\t\t\t\treturningUsers: 1,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{ $sort: { date: 1 } },\n\t\t]);\n\n\t\tconst graphData = fillMissingPeriods(result, data.startDate, data.endDate, data.graphType, {\n\t\t\ttotalUsers: 0,\n\t\t\tnewUsers: 0,\n\t\t\treturningUsers: 0,\n\t\t});\n\n\t\treturn graphData;\n\t}\n\n\tpublic async getCouponProfitGraphData(data: GraphInputDTO) {\n\t\tconst startDate = dayjs(data.startDate).startOf('day').toDate();\n\t\tconst endDate = dayjs(data.endDate).endOf('day').toDate();\n\n\t\tconst dateFormat = data.graphType === GraphType.MONTHLY ? '%Y-%m' : '%Y-%m-%d';\n\n\t\tconst result = await this.orderModel.aggregate([\n\t\t\t{\n\t\t\t\t$match: {\n\t\t\t\t\t...this.getOrderGraphMatchStage(startDate, endDate, data.itemType),\n\t\t\t\t\t'adjustedFinance.coupon.coupon': { $ne: null },\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$addFields: {\n\t\t\t\t\tdate: {\n\t\t\t\t\t\t$dateToString: {\n\t\t\t\t\t\t\tformat: dateFormat,\n\t\t\t\t\t\t\tdate: '$placedAt',\n\t\t\t\t\t\t\ttimezone: LyxaTimezone,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$group: {\n\t\t\t\t\t_id: '$date',\n\n\t\t\t\t\ttotalProfit: {\n\t\t\t\t\t\t$sum: '$adjustedFinance.companyProfit.totalProfit',\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$project: {\n\t\t\t\t\t_id: 0,\n\t\t\t\t\tdate: '$_id',\n\t\t\t\t\ttotalProfit: 1,\n\t\t\t\t},\n\t\t\t},\n\n\t\t\t{\n\t\t\t\t$sort: { date: 1 },\n\t\t\t},\n\t\t]);\n\n\t\t// Fill in missing dates with zero for graph continuity\n\t\tconst graphData = fillMissingPeriods(result, data.startDate, data.endDate, data.graphType, {\n\t\t\ttotalProfit: 0,\n\t\t});\n\n\t\treturn graphData;\n\t}\n}\n"]}