@hartl-services/medusa-affiliate 0.3.0

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 (169) hide show
  1. package/.medusa/server/src/admin/index.js +3739 -0
  2. package/.medusa/server/src/admin/index.mjs +3738 -0
  3. package/.medusa/server/src/api/admin/affiliate-applications/[id]/approve/route.js +28 -0
  4. package/.medusa/server/src/api/admin/affiliate-applications/[id]/reject/route.js +19 -0
  5. package/.medusa/server/src/api/admin/affiliate-applications/route.js +23 -0
  6. package/.medusa/server/src/api/admin/affiliate-distribution-reports/query.js +171 -0
  7. package/.medusa/server/src/api/admin/affiliate-distribution-reports/route.js +13 -0
  8. package/.medusa/server/src/api/admin/affiliate-program-settings/route.js +27 -0
  9. package/.medusa/server/src/api/admin/affiliate-reports/[id]/route.js +45 -0
  10. package/.medusa/server/src/api/admin/affiliate-reports/route.js +27 -0
  11. package/.medusa/server/src/api/admin/affiliates/[id]/consignment-reminders/route.js +31 -0
  12. package/.medusa/server/src/api/admin/affiliates/[id]/consignment-transfers/route.js +41 -0
  13. package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/reconcile/route.js +34 -0
  14. package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/revoke/route.js +36 -0
  15. package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/rotate/route.js +40 -0
  16. package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/route.js +56 -0
  17. package/.medusa/server/src/api/admin/affiliates/[id]/distribution/route.js +45 -0
  18. package/.medusa/server/src/api/admin/affiliates/[id]/inactive/route.js +20 -0
  19. package/.medusa/server/src/api/admin/affiliates/[id]/route.js +50 -0
  20. package/.medusa/server/src/api/admin/affiliates/[id]/slugs/[slug_id]/route.js +19 -0
  21. package/.medusa/server/src/api/admin/affiliates/[id]/slugs/route.js +26 -0
  22. package/.medusa/server/src/api/admin/affiliates/route.js +43 -0
  23. package/.medusa/server/src/api/admin/consignment-report-response.js +36 -0
  24. package/.medusa/server/src/api/admin/consignment-reports/[id]/approve/route.js +25 -0
  25. package/.medusa/server/src/api/admin/consignment-reports/[id]/correct/route.js +18 -0
  26. package/.medusa/server/src/api/admin/consignment-reports/[id]/reject/route.js +15 -0
  27. package/.medusa/server/src/api/admin/consignment-reports/[id]/route.js +11 -0
  28. package/.medusa/server/src/api/admin/consignment-reports/route.js +27 -0
  29. package/.medusa/server/src/api/admin/customers/[id]/affiliate/route.js +50 -0
  30. package/.medusa/server/src/api/admin/orders/[id]/affiliate-attribution/route.js +30 -0
  31. package/.medusa/server/src/api/affiliate-coupon-http.js +152 -0
  32. package/.medusa/server/src/api/affiliate-referral-cookie.js +34 -0
  33. package/.medusa/server/src/api/affiliate-self-service-http.js +37 -0
  34. package/.medusa/server/src/api/middlewares.js +786 -0
  35. package/.medusa/server/src/api/store/affiliate-referrals/apply-coupon/route.js +47 -0
  36. package/.medusa/server/src/api/store/affiliate-referrals/capture/route.js +76 -0
  37. package/.medusa/server/src/api/store/affiliate-referrals/carts/[cart_id]/route.js +20 -0
  38. package/.medusa/server/src/api/store/affiliates/consignment-inventory/route.js +53 -0
  39. package/.medusa/server/src/api/store/affiliates/consignment-report-response.js +31 -0
  40. package/.medusa/server/src/api/store/affiliates/consignment-reports/[id]/submit/route.js +40 -0
  41. package/.medusa/server/src/api/store/affiliates/consignment-reports/route.js +43 -0
  42. package/.medusa/server/src/api/store/affiliates/coupon-labels/route.js +14 -0
  43. package/.medusa/server/src/api/store/affiliates/me/commissions/route.js +35 -0
  44. package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/revoke/route.js +36 -0
  45. package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/rotate/route.js +40 -0
  46. package/.medusa/server/src/api/store/affiliates/me/coupon-codes/route.js +55 -0
  47. package/.medusa/server/src/api/store/affiliates/me/dashboard/route.js +15 -0
  48. package/.medusa/server/src/api/store/affiliates/me/route.js +85 -0
  49. package/.medusa/server/src/api/store/affiliates/me/slugs/[slug_id]/route.js +29 -0
  50. package/.medusa/server/src/api/store/affiliates/me/slugs/route.js +34 -0
  51. package/.medusa/server/src/api/store/affiliates/own-order-discount/route.js +22 -0
  52. package/.medusa/server/src/api/store/affiliates/own-order-promotion/route.js +30 -0
  53. package/.medusa/server/src/api/store/affiliates/register/route.js +27 -0
  54. package/.medusa/server/src/api/store/affiliates/resolve-referral/route.js +34 -0
  55. package/.medusa/server/src/api/validators.js +52 -0
  56. package/.medusa/server/src/api/wire-response.js +10 -0
  57. package/.medusa/server/src/integration/email-events.js +51 -0
  58. package/.medusa/server/src/integrations/inventory-transfer/batch-management-transfer-adapter.js +36 -0
  59. package/.medusa/server/src/integrations/inventory-transfer/native-inventory-transfer-adapter.js +30 -0
  60. package/.medusa/server/src/integrations/inventory-transfer/resolve-inventory-transfer-adapter.js +46 -0
  61. package/.medusa/server/src/integrations/inventory-transfer/types.js +3 -0
  62. package/.medusa/server/src/jobs/affiliate-attribution-expiry.js +20 -0
  63. package/.medusa/server/src/jobs/affiliate-consignment-report-reminders.js +15 -0
  64. package/.medusa/server/src/jobs/affiliate-promotion-freshness.js +24 -0
  65. package/.medusa/server/src/lib/query-string.js +15 -0
  66. package/.medusa/server/src/modules/affiliate/domain/affiliate-attribution-policy.js +81 -0
  67. package/.medusa/server/src/modules/affiliate/domain/affiliate-coupon-policy.js +61 -0
  68. package/.medusa/server/src/modules/affiliate/domain/commission-policy.js +33 -0
  69. package/.medusa/server/src/modules/affiliate/domain/consignment-reminder-rules.js +74 -0
  70. package/.medusa/server/src/modules/affiliate/domain/email-normalization.js +20 -0
  71. package/.medusa/server/src/modules/affiliate/domain/own-order-coupon-code.js +37 -0
  72. package/.medusa/server/src/modules/affiliate/domain/plugin-options.js +40 -0
  73. package/.medusa/server/src/modules/affiliate/index.js +13 -0
  74. package/.medusa/server/src/modules/affiliate/migrations/Migration20260827085154.js +423 -0
  75. package/.medusa/server/src/modules/affiliate/models/affiliate-application.js +38 -0
  76. package/.medusa/server/src/modules/affiliate/models/affiliate-cart-referral.js +65 -0
  77. package/.medusa/server/src/modules/affiliate/models/affiliate-code-redemption.js +41 -0
  78. package/.medusa/server/src/modules/affiliate/models/affiliate-coupon.js +55 -0
  79. package/.medusa/server/src/modules/affiliate/models/affiliate-distribution-settings.js +22 -0
  80. package/.medusa/server/src/modules/affiliate/models/affiliate-order-attribution.js +49 -0
  81. package/.medusa/server/src/modules/affiliate/models/affiliate-program-settings.js +28 -0
  82. package/.medusa/server/src/modules/affiliate/models/affiliate-referral-session.js +34 -0
  83. package/.medusa/server/src/modules/affiliate/models/affiliate-slug.js +27 -0
  84. package/.medusa/server/src/modules/affiliate/models/affiliate.js +49 -0
  85. package/.medusa/server/src/modules/affiliate/models/commission-entry.js +62 -0
  86. package/.medusa/server/src/modules/affiliate/models/consignment-report-line.js +21 -0
  87. package/.medusa/server/src/modules/affiliate/models/consignment-report.js +31 -0
  88. package/.medusa/server/src/modules/affiliate/models/consignment-transfer-line.js +16 -0
  89. package/.medusa/server/src/modules/affiliate/models/consignment-transfer.js +20 -0
  90. package/.medusa/server/src/modules/affiliate/models/customer-affiliate-assignment.js +58 -0
  91. package/.medusa/server/src/modules/affiliate/service.js +2321 -0
  92. package/.medusa/server/src/modules/affiliate/services/affiliate-application-manager.js +171 -0
  93. package/.medusa/server/src/modules/affiliate/services/affiliate-coupon-manager.js +441 -0
  94. package/.medusa/server/src/modules/affiliate/services/affiliate-discount.service.js +55 -0
  95. package/.medusa/server/src/modules/affiliate/services/affiliate-manager.js +242 -0
  96. package/.medusa/server/src/modules/affiliate/services/affiliate-partner-portal.service.js +138 -0
  97. package/.medusa/server/src/modules/affiliate/services/affiliate-report.service.js +436 -0
  98. package/.medusa/server/src/modules/affiliate/services/affiliate-slug-manager.js +163 -0
  99. package/.medusa/server/src/modules/affiliate/services/commission-ledger.service.js +729 -0
  100. package/.medusa/server/src/modules/affiliate/services/customer-affiliate-assignment-manager.js +350 -0
  101. package/.medusa/server/src/modules/affiliate/services/order-attribution-manager.js +1051 -0
  102. package/.medusa/server/src/modules/affiliate/services/referral-lifecycle-manager.js +1420 -0
  103. package/.medusa/server/src/modules/affiliate/services/utils.js +49 -0
  104. package/.medusa/server/src/modules/affiliate/types/internal.js +3 -0
  105. package/.medusa/server/src/paths/affiliate.js +7 -0
  106. package/.medusa/server/src/paths/index.js +18 -0
  107. package/.medusa/server/src/schemas/index.js +18 -0
  108. package/.medusa/server/src/schemas/store.js +24 -0
  109. package/.medusa/server/src/subscribers/affiliate-cart-updated.js +92 -0
  110. package/.medusa/server/src/subscribers/affiliate-order-canceled.js +70 -0
  111. package/.medusa/server/src/subscribers/affiliate-order-placed.js +63 -0
  112. package/.medusa/server/src/subscribers/affiliate-order-return-received.js +67 -0
  113. package/.medusa/server/src/subscribers/affiliate-product-eligibility-updated.js +87 -0
  114. package/.medusa/server/src/types/admin-api.js +3 -0
  115. package/.medusa/server/src/types/enums.js +53 -0
  116. package/.medusa/server/src/types/index.js +32 -0
  117. package/.medusa/server/src/types/public.js +3 -0
  118. package/.medusa/server/src/types/store-api.js +3 -0
  119. package/.medusa/server/src/utils/affiliate-cart-operation-lock.js +316 -0
  120. package/.medusa/server/src/utils/affiliate-promotion-mutation-authorization.js +49 -0
  121. package/.medusa/server/src/utils/cart-promotions.js +9 -0
  122. package/.medusa/server/src/utils/money.js +79 -0
  123. package/.medusa/server/src/utils/promotion-code-availability.js +34 -0
  124. package/.medusa/server/src/utils/registered-email-checkout.js +101 -0
  125. package/.medusa/server/src/utils/resolve-services.js +7 -0
  126. package/.medusa/server/src/utils/settle-cart-promotions.js +44 -0
  127. package/.medusa/server/src/workflows/affiliate/affiliate-coupon-cart.js +215 -0
  128. package/.medusa/server/src/workflows/affiliate/affiliate-coupon-command-authorization.js +20 -0
  129. package/.medusa/server/src/workflows/affiliate/apply-affiliate-coupon.js +174 -0
  130. package/.medusa/server/src/workflows/affiliate/apply-own-order-promotion.js +89 -0
  131. package/.medusa/server/src/workflows/affiliate/approve-consignment-report.js +186 -0
  132. package/.medusa/server/src/workflows/affiliate/correct-approved-consignment-report.js +263 -0
  133. package/.medusa/server/src/workflows/affiliate/create-affiliate-coupon.js +35 -0
  134. package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-cancellation.js +21 -0
  135. package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-refund.js +24 -0
  136. package/.medusa/server/src/workflows/affiliate/create-commission-for-order.js +17 -0
  137. package/.medusa/server/src/workflows/affiliate/deactivate-affiliate.js +89 -0
  138. package/.medusa/server/src/workflows/affiliate/enable-distribution.js +120 -0
  139. package/.medusa/server/src/workflows/affiliate/enrich-affiliate-report.js +75 -0
  140. package/.medusa/server/src/workflows/affiliate/ensure-affiliate-coupon-promotion.js +410 -0
  141. package/.medusa/server/src/workflows/affiliate/ensure-affiliate-own-order-promotion.js +240 -0
  142. package/.medusa/server/src/workflows/affiliate/ensure-default-affiliate-coupon.js +32 -0
  143. package/.medusa/server/src/workflows/affiliate/expire-affiliate-attribution-state.js +131 -0
  144. package/.medusa/server/src/workflows/affiliate/finalize-affiliate-order-attribution.js +82 -0
  145. package/.medusa/server/src/workflows/affiliate/payment-authorization.js +33 -0
  146. package/.medusa/server/src/workflows/affiliate/promotion-eligibility.js +179 -0
  147. package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-cart-referral.js +219 -0
  148. package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-coupon.js +388 -0
  149. package/.medusa/server/src/workflows/affiliate/refresh-affiliate-promotions.js +114 -0
  150. package/.medusa/server/src/workflows/affiliate/resolve-coupon-labels.js +40 -0
  151. package/.medusa/server/src/workflows/affiliate/revoke-affiliate-coupon.js +29 -0
  152. package/.medusa/server/src/workflows/affiliate/rotate-affiliate-coupon.js +36 -0
  153. package/.medusa/server/src/workflows/affiliate/run-consignment-report-reminders.js +58 -0
  154. package/.medusa/server/src/workflows/affiliate/submit-consignment-report.js +279 -0
  155. package/.medusa/server/src/workflows/affiliate/transfer-batch-aware-inventory.js +172 -0
  156. package/.medusa/server/src/workflows/affiliate/transfer-consignment-stock.js +106 -0
  157. package/.medusa/server/src/workflows/affiliate/update-affiliate-coupon-discounts.js +129 -0
  158. package/.medusa/server/src/workflows/affiliate/utils.js +118 -0
  159. package/.medusa/server/src/workflows/hooks/complete-cart-affiliate-attribution.js +57 -0
  160. package/.medusa/server/src/workflows/hooks/complete-cart-order-created-composition.js +111 -0
  161. package/.medusa/server/src/workflows/hooks/complete-cart-own-order-discount.js +75 -0
  162. package/.medusa/server/src/workflows/hooks/enforce-unique-promotion-codes.js +33 -0
  163. package/.medusa/server/src/workflows/hooks/validate-affiliate-customer-code.js +239 -0
  164. package/.medusa/server/src/workflows/hooks/validate-complete-cart-affiliate-referral.js +137 -0
  165. package/LICENSE +21 -0
  166. package/README.md +799 -0
  167. package/dist/integration/email-events.d.ts +20 -0
  168. package/dist/integration/email-events.js +50 -0
  169. package/package.json +84 -0
@@ -0,0 +1,436 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AffiliateReportService = void 0;
4
+ const money_1 = require("../../../utils/money");
5
+ const utils_1 = require("./utils");
6
+ class AffiliateReportService {
7
+ constructor(store) {
8
+ this.store = store;
9
+ /**
10
+ * Commission entries for a date range are fetched a page at a time and
11
+ * folded into the running totals map instead of loading the whole range
12
+ * into memory at once, so peak memory is bounded by the batch size and the
13
+ * number of distinct affiliate/currency groups, not by entry count.
14
+ */
15
+ this.overviewBatchSize = 500;
16
+ }
17
+ /**
18
+ * Portal-only aggregation over the immutable ledger. The Admin report keeps
19
+ * its existing accumulator below; both paths share the same bounded scanner.
20
+ */
21
+ async getPortalLedgerTotals(input) {
22
+ const from = (0, utils_1.startOfUtcDate)(input.from);
23
+ const to = (0, utils_1.endOfUtcDate)(input.to);
24
+ const totals = new Map();
25
+ await this.forEachAttributionInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
26
+ for (const attribution of batch) {
27
+ const current = this.getPortalGroup(totals, attribution.currency_code);
28
+ // order_id is permanently unique on the immutable attribution model,
29
+ // so every live row is one distinct attributed order.
30
+ current.attributed_order_count += 1;
31
+ }
32
+ });
33
+ await this.forEachEntryInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
34
+ for (const entry of batch) {
35
+ const current = this.getPortalGroup(totals, entry.currency_code);
36
+ const amount = money_1.Money.of(entry.commission_amount);
37
+ if (entry.type === "order_commission") {
38
+ current.direct = current.direct.add(amount);
39
+ current.base = current.base.add(money_1.Money.of(entry.base_amount_net));
40
+ }
41
+ else if (entry.type === "affiliate_referral_bonus" ||
42
+ entry.type === "affiliate_own_order_referrer_bonus") {
43
+ current.referral = current.referral.add(amount);
44
+ }
45
+ else if (entry.type === "refund_correction" ||
46
+ entry.type === "cancellation_correction" ||
47
+ entry.type === "manual_correction") {
48
+ current.correction = current.correction.add(amount);
49
+ }
50
+ }
51
+ });
52
+ await this.forEachDirectCommissionOrderInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
53
+ for (const entry of batch) {
54
+ if (entry.commission_amount <= 0)
55
+ continue;
56
+ const current = this.getPortalGroup(totals, entry.currency_code);
57
+ current.commissioned_order_count += 1;
58
+ }
59
+ });
60
+ return Array.from(totals.values())
61
+ .map((total) => {
62
+ const decimals = (0, money_1.getCurrencyDecimals)(total.currency_code);
63
+ return {
64
+ currency_code: total.currency_code,
65
+ direct_commission_amount: total.direct.toRoundedNumber(decimals),
66
+ referral_commission_amount: total.referral.toRoundedNumber(decimals),
67
+ correction_amount: total.correction.toRoundedNumber(decimals),
68
+ net_commission_amount: total.direct
69
+ .add(total.referral)
70
+ .add(total.correction)
71
+ .toRoundedNumber(decimals),
72
+ commission_base_amount: total.base.toRoundedNumber(decimals),
73
+ attributed_order_count: total.attributed_order_count,
74
+ commissioned_order_count: total.commissioned_order_count
75
+ };
76
+ })
77
+ .sort((a, b) => a.currency_code.localeCompare(b.currency_code));
78
+ }
79
+ async getPortalLedgerPage(input) {
80
+ const from = (0, utils_1.startOfUtcDate)(input.from);
81
+ const to = (0, utils_1.endOfUtcDate)(input.to);
82
+ let count = 0;
83
+ await this.forEachEntryInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
84
+ count += batch.length;
85
+ });
86
+ const [totals, entries] = await Promise.all([
87
+ this.getPortalLedgerTotals(input),
88
+ this.store.listCommissionEntries({
89
+ affiliate_id: input.affiliate_id,
90
+ occurred_at: { $gte: from, $lte: to }
91
+ }, {
92
+ order: { occurred_at: "DESC", id: "DESC" },
93
+ skip: input.offset,
94
+ take: input.limit
95
+ })
96
+ ]);
97
+ return { entries, count, totals };
98
+ }
99
+ async getOverview(input) {
100
+ const from = (0, utils_1.startOfUtcDate)(input.from);
101
+ const to = (0, utils_1.endOfUtcDate)(input.to);
102
+ const totals = new Map();
103
+ const affiliateIds = new Set();
104
+ await this.forEachAttributionInRange({ from, to }, (batch) => {
105
+ for (const attribution of batch) {
106
+ affiliateIds.add(attribution.affiliate_id);
107
+ const current = this.getReportGroup(totals, {
108
+ affiliate_id: attribution.affiliate_id,
109
+ currency_code: attribution.currency_code
110
+ }, this.reportGroupKey(attribution.affiliate_id, attribution.currency_code));
111
+ // The storage model enforces one live immutable attribution per order.
112
+ current.attributed_order_count += 1;
113
+ }
114
+ });
115
+ await this.forEachDirectCommissionOrderInRange({ from, to }, (batch) => {
116
+ for (const entry of batch) {
117
+ if (entry.commission_amount <= 0)
118
+ continue;
119
+ const current = this.getReportGroup(totals, {
120
+ affiliate_id: entry.affiliate_id,
121
+ currency_code: entry.currency_code
122
+ }, this.reportGroupKey(entry.affiliate_id, entry.currency_code));
123
+ current.commissioned_order_count += 1;
124
+ current.order_count += 1;
125
+ }
126
+ });
127
+ await this.forEachBonusOnlyOrderInRange({ from, to }, (batch) => {
128
+ for (const entry of batch) {
129
+ if (entry.commission_amount <= 0)
130
+ continue;
131
+ const current = this.getReportGroup(totals, {
132
+ affiliate_id: entry.affiliate_id,
133
+ currency_code: entry.currency_code
134
+ }, this.reportGroupKey(entry.affiliate_id, entry.currency_code));
135
+ current.order_count += 1;
136
+ }
137
+ });
138
+ await this.forEachEntryInRange({ from, to }, (batch) => {
139
+ this.accumulateEntriesInto(totals, batch, (entry) => {
140
+ affiliateIds.add(entry.affiliate_id);
141
+ return {
142
+ affiliate_id: entry.affiliate_id,
143
+ currency_code: entry.currency_code
144
+ };
145
+ });
146
+ });
147
+ const affiliates = affiliateIds.size
148
+ ? await this.store.listAffiliates({ id: Array.from(affiliateIds) })
149
+ : [];
150
+ const affiliatesById = new Map(affiliates.map((affiliate) => [affiliate.id, affiliate]));
151
+ const overviewItems = this.finalizeTotals(totals).map((total) => ({
152
+ ...total,
153
+ display_name: affiliatesById.get(total.affiliate_id)?.display_name ??
154
+ total.affiliate_id,
155
+ status: affiliatesById.get(total.affiliate_id)?.status ?? "inactive"
156
+ }));
157
+ return {
158
+ from: from.toISOString().slice(0, 10),
159
+ to: to.toISOString().slice(0, 10),
160
+ affiliates: overviewItems.sort((a, b) => a.display_name.localeCompare(b.display_name))
161
+ };
162
+ }
163
+ async getDetail(input) {
164
+ const from = (0, utils_1.startOfUtcDate)(input.from);
165
+ const to = (0, utils_1.endOfUtcDate)(input.to);
166
+ const affiliate = await this.store.retrieveAffiliate(input.affiliate_id);
167
+ // Totals are accumulated over the whole range in bounded batches so they
168
+ // stay correct even though only one page of entries is loaded for display.
169
+ const totals = new Map();
170
+ let entriesCount = 0;
171
+ await this.forEachAttributionInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
172
+ for (const attribution of batch) {
173
+ const current = this.getReportGroup(totals, {
174
+ currency_code: attribution.currency_code
175
+ }, this.reportGroupKey(attribution.affiliate_id, attribution.currency_code));
176
+ current.attributed_order_count += 1;
177
+ }
178
+ });
179
+ await this.forEachEntryInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
180
+ entriesCount += batch.length;
181
+ this.accumulateEntriesInto(totals, batch, (entry) => ({
182
+ currency_code: entry.currency_code
183
+ }));
184
+ });
185
+ await this.forEachDirectCommissionOrderInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
186
+ for (const entry of batch) {
187
+ if (entry.commission_amount <= 0)
188
+ continue;
189
+ const current = this.getReportGroup(totals, { currency_code: entry.currency_code }, this.reportGroupKey(entry.affiliate_id, entry.currency_code));
190
+ current.commissioned_order_count += 1;
191
+ current.order_count += 1;
192
+ }
193
+ });
194
+ await this.forEachBonusOnlyOrderInRange({ from, to, affiliate_id: input.affiliate_id }, (batch) => {
195
+ for (const entry of batch) {
196
+ if (entry.commission_amount <= 0)
197
+ continue;
198
+ const current = this.getReportGroup(totals, { currency_code: entry.currency_code }, this.reportGroupKey(entry.affiliate_id, entry.currency_code));
199
+ current.order_count += 1;
200
+ }
201
+ });
202
+ const entries = await this.store.listCommissionEntries({
203
+ affiliate_id: input.affiliate_id,
204
+ occurred_at: {
205
+ $gte: from,
206
+ $lte: to
207
+ }
208
+ }, {
209
+ order: {
210
+ occurred_at: "DESC",
211
+ id: "DESC"
212
+ },
213
+ skip: input.offset,
214
+ take: input.limit
215
+ });
216
+ return {
217
+ from: from.toISOString().slice(0, 10),
218
+ to: to.toISOString().slice(0, 10),
219
+ affiliate,
220
+ entries,
221
+ entries_count: entriesCount,
222
+ totals: this.finalizeTotals(totals)
223
+ };
224
+ }
225
+ /**
226
+ * Scans every commission entry in the range in bounded batches using keyset
227
+ * pagination on the unique `id` (stable: no row is skipped or double-counted
228
+ * across batch boundaries the way an occurred_at skip/take would when several
229
+ * entries share a timestamp). The aggregation is order-independent, so a plain
230
+ * `id ASC` cursor is sufficient.
231
+ */
232
+ async forEachEntryInRange(range, onBatch) {
233
+ let cursor;
234
+ for (;;) {
235
+ const batch = await this.store.listCommissionEntries({
236
+ ...(range.affiliate_id ? { affiliate_id: range.affiliate_id } : {}),
237
+ occurred_at: {
238
+ $gte: range.from,
239
+ $lte: range.to
240
+ },
241
+ ...(cursor ? { id: { $gt: cursor } } : {})
242
+ }, {
243
+ order: {
244
+ id: "ASC"
245
+ },
246
+ take: this.overviewBatchSize
247
+ });
248
+ if (!batch.length) {
249
+ return;
250
+ }
251
+ onBatch(batch);
252
+ if (batch.length < this.overviewBatchSize) {
253
+ return;
254
+ }
255
+ cursor = batch[batch.length - 1].id;
256
+ }
257
+ }
258
+ /** Scans immutable attribution snapshots with the same bounded keyset rule. */
259
+ async forEachAttributionInRange(range, onBatch) {
260
+ let cursor;
261
+ for (;;) {
262
+ const batch = await this.store.listAffiliateOrderAttributions({
263
+ ...(range.affiliate_id ? { affiliate_id: range.affiliate_id } : {}),
264
+ attributed_at: { $gte: range.from, $lte: range.to },
265
+ ...(cursor ? { id: { $gt: cursor } } : {})
266
+ }, { order: { id: "ASC" }, take: this.overviewBatchSize });
267
+ if (!batch.length)
268
+ return;
269
+ onBatch(batch);
270
+ if (batch.length < this.overviewBatchSize)
271
+ return;
272
+ cursor = batch[batch.length - 1].id;
273
+ }
274
+ }
275
+ /**
276
+ * Counts positive direct commissions by a bounded `order_id` keyset rather
277
+ * than retaining every order ID. Direct order-commission rows are immutable
278
+ * one-per-order bookings (`oc:<order_id>`); the order keyset also preserves
279
+ * the distinct-order result for legacy duplicate rows without unbounded
280
+ * process memory. Generated service reads exclude soft-deleted history.
281
+ */
282
+ async forEachDirectCommissionOrderInRange(range, onBatch) {
283
+ let cursor;
284
+ for (;;) {
285
+ const batch = await this.store.listCommissionEntries({
286
+ ...(range.affiliate_id ? { affiliate_id: range.affiliate_id } : {}),
287
+ type: "order_commission",
288
+ occurred_at: { $gte: range.from, $lte: range.to },
289
+ ...(cursor ? { order_id: { $gt: cursor } } : {})
290
+ }, {
291
+ order: { order_id: "ASC", id: "ASC" },
292
+ take: this.overviewBatchSize
293
+ });
294
+ if (!batch.length)
295
+ return;
296
+ // Rows for the same order are adjacent in this query. Deduplicate only
297
+ // the current bounded batch; the cursor advances by order_id, so later
298
+ // batches cannot reintroduce a completed order.
299
+ const distinctOrders = batch.filter((entry, index) => index === 0 || entry.order_id !== batch[index - 1].order_id);
300
+ onBatch(distinctOrders);
301
+ if (batch.length < this.overviewBatchSize)
302
+ return;
303
+ cursor = batch[batch.length - 1].order_id ?? undefined;
304
+ if (!cursor)
305
+ return;
306
+ }
307
+ }
308
+ /**
309
+ * Same bounded `order_id`-keyset scan as
310
+ * `forEachDirectCommissionOrderInRange`, but over the referral/own-order
311
+ * bonus types instead of `order_commission`. A given order has at most one
312
+ * row in this type set system-wide (the own-order-bonus path short-circuits
313
+ * the normal attribution flow entirely, and a regular referral bonus is
314
+ * bound to a single `referrer_affiliate_id`), so the same order-only,
315
+ * cross-affiliate-agnostic adjacent dedup stays correct: whenever both a
316
+ * direct and a bonus row exist for one order, they belong to two different
317
+ * affiliates (buyer vs. referrer), never the same one.
318
+ */
319
+ async forEachBonusOnlyOrderInRange(range, onBatch) {
320
+ let cursor;
321
+ for (;;) {
322
+ const batch = await this.store.listCommissionEntries({
323
+ ...(range.affiliate_id ? { affiliate_id: range.affiliate_id } : {}),
324
+ type: ["affiliate_referral_bonus", "affiliate_own_order_referrer_bonus"],
325
+ occurred_at: { $gte: range.from, $lte: range.to },
326
+ ...(cursor ? { order_id: { $gt: cursor } } : {})
327
+ }, {
328
+ order: { order_id: "ASC", id: "ASC" },
329
+ take: this.overviewBatchSize
330
+ });
331
+ if (!batch.length)
332
+ return;
333
+ const distinctOrders = batch.filter((entry, index) => index === 0 || entry.order_id !== batch[index - 1].order_id);
334
+ onBatch(distinctOrders);
335
+ if (batch.length < this.overviewBatchSize)
336
+ return;
337
+ cursor = batch[batch.length - 1].order_id ?? undefined;
338
+ if (!cursor)
339
+ return;
340
+ }
341
+ }
342
+ /**
343
+ * Sums commission_amount by immutable affiliate/currency snapshot into the
344
+ * given map. Callers can feed entries in as many batches as needed.
345
+ */
346
+ accumulateEntriesInto(totals, entries, seed) {
347
+ for (const entry of entries) {
348
+ const current = this.getReportGroup(totals, seed(entry), this.reportGroupKey(entry.affiliate_id, entry.currency_code));
349
+ const commissionAmount = money_1.Money.of(entry.commission_amount);
350
+ current.total_commission_amount =
351
+ current.total_commission_amount.add(commissionAmount);
352
+ current.entry_count += 1;
353
+ if (!commissionAmount.isNegative()) {
354
+ current.positive_commission_amount =
355
+ current.positive_commission_amount.add(commissionAmount);
356
+ }
357
+ else {
358
+ current.negative_correction_amount =
359
+ current.negative_correction_amount.add(commissionAmount);
360
+ }
361
+ // "Bestellungswert": the net basis behind every entry that actually
362
+ // paid out, so it stays a meaningful denominator next to the
363
+ // commission total (see affiliate-report.service.ts's own module
364
+ // docs). Zero-amount entries don't count as "earned from".
365
+ if (entry.commission_amount > 0) {
366
+ current.order_value_amount = current.order_value_amount.add(money_1.Money.of(entry.base_amount_net));
367
+ }
368
+ if (entry.type === "affiliate_referral_bonus" ||
369
+ entry.type === "affiliate_own_order_referrer_bonus") {
370
+ current.referral_bonus_amount =
371
+ current.referral_bonus_amount.add(commissionAmount);
372
+ }
373
+ }
374
+ }
375
+ /**
376
+ * Keeps the running totals as Money values, then applies the exact decimal
377
+ * precision of the group's currency only at the response boundary.
378
+ */
379
+ finalizeTotals(totals) {
380
+ return Array.from(totals.values()).map((total) => {
381
+ const decimalPlaces = (0, money_1.getCurrencyDecimals)(total.currency_code);
382
+ return {
383
+ ...total,
384
+ total_commission_amount: total.total_commission_amount.toRoundedNumber(decimalPlaces),
385
+ positive_commission_amount: total.positive_commission_amount.toRoundedNumber(decimalPlaces),
386
+ negative_correction_amount: total.negative_correction_amount.toRoundedNumber(decimalPlaces),
387
+ referral_bonus_amount: total.referral_bonus_amount.toRoundedNumber(decimalPlaces),
388
+ order_value_amount: total.order_value_amount.toRoundedNumber(decimalPlaces),
389
+ attributed_order_count: total.attributed_order_count,
390
+ commissioned_order_count: total.commissioned_order_count,
391
+ order_count: total.order_count
392
+ };
393
+ });
394
+ }
395
+ getReportGroup(totals, seed, key) {
396
+ const current = totals.get(key);
397
+ if (current)
398
+ return current;
399
+ const created = {
400
+ ...seed,
401
+ total_commission_amount: money_1.Money.zero(),
402
+ positive_commission_amount: money_1.Money.zero(),
403
+ negative_correction_amount: money_1.Money.zero(),
404
+ referral_bonus_amount: money_1.Money.zero(),
405
+ order_value_amount: money_1.Money.zero(),
406
+ entry_count: 0,
407
+ attributed_order_count: 0,
408
+ commissioned_order_count: 0,
409
+ order_count: 0
410
+ };
411
+ totals.set(key, created);
412
+ return created;
413
+ }
414
+ /** A reversible tuple encoding prevents delimiter collisions in report maps. */
415
+ reportGroupKey(affiliateId, currencyCode) {
416
+ return JSON.stringify([affiliateId, currencyCode]);
417
+ }
418
+ getPortalGroup(totals, currencyCode) {
419
+ const current = totals.get(currencyCode);
420
+ if (current)
421
+ return current;
422
+ const created = {
423
+ currency_code: currencyCode,
424
+ direct: money_1.Money.zero(),
425
+ referral: money_1.Money.zero(),
426
+ correction: money_1.Money.zero(),
427
+ base: money_1.Money.zero(),
428
+ attributed_order_count: 0,
429
+ commissioned_order_count: 0
430
+ };
431
+ totals.set(currencyCode, created);
432
+ return created;
433
+ }
434
+ }
435
+ exports.AffiliateReportService = AffiliateReportService;
436
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLXJlcG9ydC5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvYWZmaWxpYXRlL3NlcnZpY2VzL2FmZmlsaWF0ZS1yZXBvcnQuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFLQSxnREFBaUU7QUFNakUsbUNBQXNEO0FBK0J0RCxNQUFhLHNCQUFzQjtJQVNqQyxZQUE2QixLQUEyQjtRQUEzQixVQUFLLEdBQUwsS0FBSyxDQUFzQjtRQVJ4RDs7Ozs7V0FLRztRQUNjLHNCQUFpQixHQUFHLEdBQUcsQ0FBQTtJQUVtQixDQUFDO0lBRTVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxLQUkzQjtRQUNDLE1BQU0sSUFBSSxHQUFHLElBQUEsc0JBQWMsRUFBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUE7UUFDdkMsTUFBTSxFQUFFLEdBQUcsSUFBQSxvQkFBWSxFQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNqQyxNQUFNLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFXbkIsQ0FBQTtRQUVILE1BQU0sSUFBSSxDQUFDLHlCQUF5QixDQUNsQyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsWUFBWSxFQUFFLEtBQUssQ0FBQyxZQUFZLEVBQUUsRUFDOUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUNSLEtBQUssTUFBTSxXQUFXLElBQUksS0FBSyxFQUFFLENBQUM7Z0JBQ2hDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLFdBQVcsQ0FBQyxhQUFhLENBQUMsQ0FBQTtnQkFDdEUscUVBQXFFO2dCQUNyRSxzREFBc0Q7Z0JBQ3RELE9BQU8sQ0FBQyxzQkFBc0IsSUFBSSxDQUFDLENBQUE7WUFDckMsQ0FBQztRQUNILENBQUMsQ0FDRixDQUFBO1FBRUQsTUFBTSxJQUFJLENBQUMsbUJBQW1CLENBQzVCLEVB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
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AffiliateSlugManager = void 0;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ class AffiliateSlugManager {
6
+ constructor(store) {
7
+ this.store = store;
8
+ }
9
+ normalizeSlug(value) {
10
+ const slug = value
11
+ .trim()
12
+ .toLowerCase()
13
+ .replace(/['"]/g, "")
14
+ .replace(/[^a-z0-9]+/g, "-")
15
+ .replace(/^-+|-+$/g, "")
16
+ .replace(/-{2,}/g, "-");
17
+ if (!slug) {
18
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Affiliate slug is required.");
19
+ }
20
+ return slug;
21
+ }
22
+ async createSlug(input, context) {
23
+ const slug = this.normalizeSlug(input.slug);
24
+ // When called right after the affiliate was created in the same
25
+ // transaction, flush so the existence check below sees it. A filtered
26
+ // `list` is used rather than `retrieve` because, within an open
27
+ // transaction, the generated `retrieve` does not observe a row created
28
+ // (and flushed) earlier in the same transaction, whereas `list` does.
29
+ await this.store.flush(context);
30
+ await this.assertSlugIsAvailable(slug, undefined, context);
31
+ const [affiliate] = await this.store.listAffiliates({ id: input.affiliate_id }, { take: 1 }, context);
32
+ if (!affiliate) {
33
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, `Affiliate with id "${input.affiliate_id}" was not found.`);
34
+ }
35
+ const existingSlugs = await this.store.listAffiliateSlugs({
36
+ affiliate_id: input.affiliate_id,
37
+ }, undefined, context);
38
+ const makePrimary = existingSlugs.length === 0 || (input.is_primary ?? false);
39
+ const active = input.active ?? true;
40
+ if (makePrimary && !active) {
41
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "A primary affiliate slug must be active.");
42
+ }
43
+ const created = await this.store.createAffiliateSlugs({
44
+ affiliate_id: input.affiliate_id,
45
+ slug,
46
+ active,
47
+ is_primary: false,
48
+ }, context);
49
+ if (makePrimary) {
50
+ return await this.setPrimarySlug({ affiliate_id: input.affiliate_id, slug_id: created.id }, context);
51
+ }
52
+ return created;
53
+ }
54
+ async updateSlug(input, context) {
55
+ const [existing] = await this.store.listAffiliateSlugs({ id: input.id, affiliate_id: input.affiliate_id }, { take: 1 }, context);
56
+ if (!existing) {
57
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Affiliate slug was not found for this affiliate.");
58
+ }
59
+ const nextSlug = input.slug !== undefined ? this.normalizeSlug(input.slug) : existing.slug;
60
+ if (nextSlug !== existing.slug) {
61
+ await this.assertSlugIsAvailable(nextSlug, existing.id, context);
62
+ }
63
+ const nextActive = input.active ?? existing.active;
64
+ const nextPrimary = input.is_primary ?? existing.is_primary;
65
+ if (existing.is_primary && (!nextActive || !nextPrimary)) {
66
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "A primary affiliate slug must remain active and primary.");
67
+ }
68
+ if (nextPrimary && !nextActive) {
69
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "A primary affiliate slug must be active.");
70
+ }
71
+ const updated = (await this.store.updateAffiliateSlugs({
72
+ id: input.id,
73
+ slug: nextSlug,
74
+ active: nextActive,
75
+ is_primary: existing.is_primary,
76
+ }, context));
77
+ if (nextPrimary && !existing.is_primary) {
78
+ return await this.setPrimarySlug({ affiliate_id: input.affiliate_id, slug_id: updated.id }, context);
79
+ }
80
+ return updated;
81
+ }
82
+ async setPrimarySlug(input, context) {
83
+ await this.store.flush(context);
84
+ const [target] = await this.store.listAffiliateSlugs({ id: input.slug_id, affiliate_id: input.affiliate_id }, { take: 1 }, context);
85
+ if (!target) {
86
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Affiliate slug was not found for this affiliate.");
87
+ }
88
+ if (!target.active) {
89
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "An inactive affiliate slug cannot be primary.");
90
+ }
91
+ await this.clearPrimarySlug(input.affiliate_id, context);
92
+ const updated = (await this.store.updateAffiliateSlugs({
93
+ id: target.id,
94
+ active: true,
95
+ is_primary: true,
96
+ }, context));
97
+ await this.store.updateAffiliates({
98
+ id: input.affiliate_id,
99
+ primary_slug_id: updated.id,
100
+ }, context);
101
+ return updated;
102
+ }
103
+ async listSlugs(affiliateId, context) {
104
+ return await this.store.listAffiliateSlugs({
105
+ affiliate_id: affiliateId,
106
+ }, {
107
+ order: {
108
+ created_at: "ASC",
109
+ },
110
+ }, context);
111
+ }
112
+ /**
113
+ * Normalizes and asserts a slug is free before a caller commits to a
114
+ * multi-row write (H3), so an affiliate row is never inserted only to have
115
+ * its primary slug rejected by the global unique index afterwards.
116
+ */
117
+ async assertSlugAvailable(slug, context) {
118
+ const normalized = this.normalizeSlug(slug);
119
+ await this.assertSlugIsAvailable(normalized, undefined, context);
120
+ return normalized;
121
+ }
122
+ async resolveActiveSlug(slugValue) {
123
+ const slug = this.normalizeSlug(slugValue);
124
+ const slugs = await this.store.listAffiliateSlugs({
125
+ slug,
126
+ active: true,
127
+ });
128
+ return slugs[0] ?? null;
129
+ }
130
+ async generateAvailableSlug(displayName, context) {
131
+ const base = this.normalizeSlug(displayName);
132
+ let candidate = base;
133
+ let suffix = 2;
134
+ while (!(await this.isSlugAvailable(candidate, undefined, context))) {
135
+ candidate = `${base}-${suffix}`;
136
+ suffix += 1;
137
+ }
138
+ return candidate;
139
+ }
140
+ async clearPrimarySlug(affiliateId, context) {
141
+ await this.store.updateAffiliateSlugs({
142
+ selector: {
143
+ affiliate_id: affiliateId,
144
+ is_primary: true,
145
+ },
146
+ data: {
147
+ is_primary: false,
148
+ },
149
+ }, context);
150
+ }
151
+ async isSlugAvailable(slug, currentSlugId, context) {
152
+ const existing = await this.store.listAffiliateSlugs({ slug }, undefined, context);
153
+ return existing.every((existingSlug) => existingSlug.id === currentSlugId);
154
+ }
155
+ async assertSlugIsAvailable(slug, currentSlugId, context) {
156
+ if (await this.isSlugAvailable(slug, currentSlugId, context)) {
157
+ return;
158
+ }
159
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.DUPLICATE_ERROR, `Affiliate slug "${slug}" is already in use.`);
160
+ }
161
+ }
162
+ exports.AffiliateSlugManager = AffiliateSlugManager;
163
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLXNsdWctbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2FmZmlsaWF0ZS9zZXJ2aWNlcy9hZmZpbGlhdGUtc2x1Zy1tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFEQUF1RDtBQVV2RCxNQUFhLG9CQUFvQjtJQUMvQixZQUE2QixLQUEyQjtRQUEzQixVQUFLLEdBQUwsS0FBSyxDQUFzQjtJQUFHLENBQUM7SUFFNUQsYUFBYSxDQUFDLEtBQWE7UUFDekIsTUFBTSxJQUFJLEdBQUcsS0FBSzthQUNmLElBQUksRUFBRTthQUNOLFdBQVcsRUFBRTthQUNiLE9BQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO2FBQ3BCLE9BQU8sQ0FBQyxhQUFhLEVBQUUsR0FBRyxDQUFDO2FBQzNCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDO2FBQ3ZCLE9BQU8sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUE7UUFFekIsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsTUFBTSxJQUFJLG1CQUFXLENBQ25CLG1CQUFXLENBQUMsS0FBSyxDQUFDLFlBQVksRUFDOUIsNkJBQTZCLENBQzlCLENBQUE7UUFDSCxDQUFDO1FBRUQsT0FBTyxJQUFJLENBQUE7SUFDYixDQUFDO0lBRUQsS0FBSyxDQUFDLFVBQVUsQ0FDZCxLQUErQixFQUMvQixPQUFpQjtRQUVqQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQTtRQUUzQyxnRUFBZ0U7UUFDaEUsc0VBQXNFO1FBQ3RFLGdFQUFnRTtRQUNoRSx1RUFBdUU7UUFDdkUsc0VBQXNFO1FBQ3RFLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDL0IsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMs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