@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,410 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureAffiliateCouponPromotionWorkflow = exports.provisionAffiliateCouponPromotion = exports.isCurrentAffiliateCouponPromotion = exports.affiliateCouponProgramLockKeys = exports.affiliateCouponProvisioningLockKeys = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const utils_1 = require("@medusajs/framework/utils");
6
+ const core_flows_1 = require("@medusajs/medusa/core-flows");
7
+ const affiliate_cart_operation_lock_1 = require("../../utils/affiliate-cart-operation-lock");
8
+ const promotion_code_availability_1 = require("../../utils/promotion-code-availability");
9
+ const resolve_services_1 = require("../../utils/resolve-services");
10
+ const promotion_eligibility_1 = require("./promotion-eligibility");
11
+ const AFFILIATE_CAMPAIGN_PROVISIONING_LOCK = "affiliate-campaign-provisioning";
12
+ const couponCodeConflict = () => new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "coupon_code_conflict");
13
+ const provisioningErrorMessage = (error) => {
14
+ const message = error instanceof Error ? error.message : "Unknown error";
15
+ return `Affiliate coupon promotion provisioning failed: ${message}`.slice(0, 2_000);
16
+ };
17
+ const assertAffiliateCampaign = (campaign) => {
18
+ const budget = campaign.budget;
19
+ if (campaign.campaign_identifier !== promotion_eligibility_1.AFFILIATE_CUSTOMER_CAMPAIGN_IDENTIFIER ||
20
+ budget?.type !== "use_by_attribute" ||
21
+ budget.attribute !== "customer_email" ||
22
+ Number(budget.limit) !== 1) {
23
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "The Affiliate customer campaign does not have the required customer-email budget.");
24
+ }
25
+ };
26
+ const retrieveAffiliateCampaign = async (promotionService) => {
27
+ const campaigns = await promotionService.listCampaigns({
28
+ campaign_identifier: [promotion_eligibility_1.AFFILIATE_CUSTOMER_CAMPAIGN_IDENTIFIER],
29
+ }, { relations: ["budget"], take: 2 });
30
+ if (campaigns.length > 1) {
31
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Multiple Affiliate customer campaigns use the reserved identifier.");
32
+ }
33
+ if (campaigns[0]) {
34
+ assertAffiliateCampaign(campaigns[0]);
35
+ }
36
+ return campaigns[0] ?? null;
37
+ };
38
+ const ensureAffiliateCampaign = async (container, promotionService) => {
39
+ const existing = await retrieveAffiliateCampaign(promotionService);
40
+ if (existing) {
41
+ return existing;
42
+ }
43
+ return await (0, affiliate_cart_operation_lock_1.withAffiliateOperationLocks)(container, [AFFILIATE_CAMPAIGN_PROVISIONING_LOCK], async () => {
44
+ let campaign = await retrieveAffiliateCampaign(promotionService);
45
+ if (!campaign) {
46
+ await (0, core_flows_1.createCampaignsWorkflow)(container).run({
47
+ input: {
48
+ campaignsData: [
49
+ {
50
+ name: "Affiliate customer codes",
51
+ campaign_identifier: promotion_eligibility_1.AFFILIATE_CUSTOMER_CAMPAIGN_IDENTIFIER,
52
+ budget: {
53
+ type: "use_by_attribute",
54
+ attribute: "customer_email",
55
+ limit: 1,
56
+ },
57
+ },
58
+ ],
59
+ },
60
+ });
61
+ campaign = await retrieveAffiliateCampaign(promotionService);
62
+ }
63
+ if (!campaign) {
64
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "The Affiliate customer campaign could not be provisioned uniquely.");
65
+ }
66
+ return campaign;
67
+ });
68
+ };
69
+ const affiliateCouponProvisioningLockKeys = (affiliateId) => [
70
+ AFFILIATE_CAMPAIGN_PROVISIONING_LOCK,
71
+ (0, affiliate_cart_operation_lock_1.affiliateProgramSagaLockKey)(),
72
+ (0, affiliate_cart_operation_lock_1.affiliateSagaLockKey)(affiliateId),
73
+ ];
74
+ exports.affiliateCouponProvisioningLockKeys = affiliateCouponProvisioningLockKeys;
75
+ const affiliateCouponProgramLockKeys = () => [
76
+ AFFILIATE_CAMPAIGN_PROVISIONING_LOCK,
77
+ (0, affiliate_cart_operation_lock_1.affiliateProgramSagaLockKey)(),
78
+ ];
79
+ exports.affiliateCouponProgramLockKeys = affiliateCouponProgramLockKeys;
80
+ const promotionRelations = [
81
+ "campaign",
82
+ "rules",
83
+ "application_method",
84
+ "application_method.target_rules",
85
+ "application_method.target_rules.values",
86
+ ];
87
+ const promotionHasExactIdentity = (promotion, coupon, campaign) => promotion.code === coupon.code &&
88
+ promotion.campaign_id === campaign.id &&
89
+ promotion.campaign?.id === campaign.id &&
90
+ promotion.campaign.campaign_identifier ===
91
+ promotion_eligibility_1.AFFILIATE_CUSTOMER_CAMPAIGN_IDENTIFIER;
92
+ const isCurrentAffiliateCouponPromotion = (input) => {
93
+ const { promotion, coupon, affiliate, campaign, discount_rate: discountRate, eligible_variant_ids: eligibleVariantIds, } = input;
94
+ const applicationMethod = promotion.application_method;
95
+ const targetRules = applicationMethod?.target_rules ?? [];
96
+ const targetRule = targetRules[0];
97
+ const currentValues = (targetRule?.values ?? [])
98
+ .map(({ value }) => value)
99
+ .sort();
100
+ const desiredValues = (0, promotion_eligibility_1.affiliateEligibleVariantRuleValues)(eligibleVariantIds);
101
+ const targetRulesCurrent = targetRules.length === 1 &&
102
+ targetRule?.attribute === "items.variant.id" &&
103
+ targetRule.operator === "eq" &&
104
+ currentValues.length === desiredValues.length &&
105
+ currentValues.every((value, index) => value === desiredValues[index]);
106
+ return (promotionHasExactIdentity(promotion, coupon, campaign) &&
107
+ promotion.type === "standard" &&
108
+ promotion.is_automatic === false &&
109
+ promotion.status ===
110
+ (coupon.lifecycle_status === "enabled" && affiliate.status === "active"
111
+ ? "active"
112
+ : "inactive") &&
113
+ (promotion.rules?.length ?? 0) === 0 &&
114
+ applicationMethod?.type === "percentage" &&
115
+ applicationMethod.target_type === "items" &&
116
+ applicationMethod.allocation === "across" &&
117
+ Number(applicationMethod.value) === discountRate &&
118
+ targetRulesCurrent);
119
+ };
120
+ exports.isCurrentAffiliateCouponPromotion = isCurrentAffiliateCouponPromotion;
121
+ /**
122
+ * Conflicts and unexpected states are raised by this workflow's own checks
123
+ * (e.g. the bound promotion is missing or no longer the plugin's); anything
124
+ * else, such as a database error, may be transient.
125
+ */
126
+ const isDetectedInconsistency = (error) => utils_1.MedusaError.isMedusaError(error) &&
127
+ (error.type === utils_1.MedusaError.Types.CONFLICT ||
128
+ error.type === utils_1.MedusaError.Types.UNEXPECTED_STATE);
129
+ const deactivatePromotion = async (container, promotionId) => {
130
+ await (0, core_flows_1.updatePromotionsWorkflow)(container).run({
131
+ input: {
132
+ promotionsData: [{ id: promotionId, status: "inactive" }],
133
+ },
134
+ });
135
+ };
136
+ const persistProvisioningFailure = async (container, couponId, error, promotionId, mayDeactivatePromotion) => {
137
+ const affiliateService = (0, resolve_services_1.resolveAffiliateService)(container);
138
+ const failures = [error];
139
+ let latest = null;
140
+ try {
141
+ latest = await affiliateService.retrieveAffiliateCouponRecord(couponId);
142
+ if (latest.replaces_coupon_id &&
143
+ latest.lifecycle_status === "enabled" &&
144
+ latest.provisioning_status === "ready") {
145
+ const predecessor = await affiliateService.retrieveAffiliateCouponRecord(latest.replaces_coupon_id, latest.affiliate_id);
146
+ if (predecessor.lifecycle_status === "revoked") {
147
+ throw error;
148
+ }
149
+ }
150
+ }
151
+ catch (readError) {
152
+ if (readError === error) {
153
+ throw error;
154
+ }
155
+ failures.push(readError);
156
+ }
157
+ if (promotionId && mayDeactivatePromotion) {
158
+ try {
159
+ await deactivatePromotion(container, promotionId);
160
+ }
161
+ catch (deactivationError) {
162
+ failures.push(deactivationError);
163
+ }
164
+ }
165
+ try {
166
+ await affiliateService.setAffiliateCouponProvisioningState({
167
+ coupon_id: couponId,
168
+ ...(latest?.promotion_id ? { promotion_id: latest.promotion_id } : {}),
169
+ provisioning_status: "failed",
170
+ promotion_setup_error: provisioningErrorMessage(error),
171
+ });
172
+ }
173
+ catch (stateError) {
174
+ failures.push(stateError);
175
+ }
176
+ if (failures.length === 1) {
177
+ throw error;
178
+ }
179
+ throw new AggregateError(failures, "Affiliate coupon provisioning and fail-closed cleanup failed");
180
+ };
181
+ const loadPromotionByExactCode = async (promotionService, coupon, campaign) => {
182
+ const promotions = await promotionService.listPromotions({ code: [coupon.code] }, { relations: promotionRelations, take: 2 });
183
+ if (promotions.length > 1) {
184
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, `Multiple native promotions use Affiliate coupon code "${coupon.code}".`);
185
+ }
186
+ const promotion = promotions[0] ?? null;
187
+ if (promotion && !promotionHasExactIdentity(promotion, coupon, campaign)) {
188
+ throw couponCodeConflict();
189
+ }
190
+ return promotion;
191
+ };
192
+ const provisionAffiliateCouponPromotion = async (container, input) => {
193
+ const affiliateService = (0, resolve_services_1.resolveAffiliateService)(container);
194
+ const observed = await affiliateService.retrieveAffiliateCouponRecord(input.coupon_id);
195
+ const promotionService = container.resolve(utils_1.Modules.PROMOTION);
196
+ return await (0, affiliate_cart_operation_lock_1.withAffiliateOperationLocks)(container, observed.lifecycle_status === "revoked" && !observed.promotion_id
197
+ ? [
198
+ (0, affiliate_cart_operation_lock_1.affiliateProgramSagaLockKey)(),
199
+ (0, affiliate_cart_operation_lock_1.affiliateSagaLockKey)(observed.affiliate_id),
200
+ ]
201
+ : (0, exports.affiliateCouponProvisioningLockKeys)(observed.affiliate_id), async () => {
202
+ let coupon = await affiliateService.retrieveAffiliateCouponRecord(input.coupon_id, observed.affiliate_id);
203
+ const affiliate = await affiliateService.retrieveAffiliateRecord(coupon.affiliate_id);
204
+ if (coupon.lifecycle_status === "revoked" && !coupon.promotion_id) {
205
+ coupon = await affiliateService.setAffiliateCouponProvisioningState({
206
+ coupon_id: coupon.id,
207
+ provisioning_status: "ready",
208
+ });
209
+ return {
210
+ coupon,
211
+ outcome: "revoked_cleanup",
212
+ rotation_finalized: false,
213
+ revoked_coupon: null,
214
+ };
215
+ }
216
+ const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
217
+ let promotionId = coupon.promotion_id;
218
+ let mayDeactivatePromotion = false;
219
+ // A ready coupon is redeemable, and every cart update that sees it in
220
+ // another state removes it from that cart. Until this run writes to its
221
+ // live promotion, a transient failure must therefore leave the coupon
222
+ // untouched; a detected inconsistency still fails closed.
223
+ let readyCouponUntouched = coupon.provisioning_status === "ready" &&
224
+ coupon.promotion_id !== null &&
225
+ !coupon.promotion_setup_error;
226
+ try {
227
+ const campaign = await ensureAffiliateCampaign(container, promotionService);
228
+ let promotion = await loadPromotionByExactCode(promotionService, coupon, campaign);
229
+ let outcome = "unchanged";
230
+ if (coupon.promotion_id) {
231
+ if (!promotion || promotion.id !== coupon.promotion_id) {
232
+ throw couponCodeConflict();
233
+ }
234
+ mayDeactivatePromotion = true;
235
+ }
236
+ else if (promotion) {
237
+ const binding = await affiliateService.findAffiliateCouponByPromotionId(promotion.id);
238
+ if (binding && binding.id !== coupon.id) {
239
+ throw couponCodeConflict();
240
+ }
241
+ promotionId = promotion.id;
242
+ mayDeactivatePromotion = true;
243
+ outcome = "adopted";
244
+ }
245
+ else {
246
+ // The exact-code lookup above cannot see a native promotion whose
247
+ // code differs only in case (Medusa's unique index is case
248
+ // sensitive); such a promotion is the same code for a customer.
249
+ const caseConflicts = await (0, promotion_code_availability_1.findPromotionsWithCodeIgnoringCase)(promotionService, coupon.code);
250
+ if (caseConflicts.length) {
251
+ throw couponCodeConflict();
252
+ }
253
+ const created = await (0, core_flows_1.createPromotionsWorkflow)(container).run({
254
+ input: {
255
+ promotionsData: [
256
+ {
257
+ code: coupon.code,
258
+ type: "standard",
259
+ status: "inactive",
260
+ is_automatic: false,
261
+ campaign_id: campaign.id,
262
+ application_method: {
263
+ type: "percentage",
264
+ target_type: "items",
265
+ allocation: "across",
266
+ value: affiliate.customer_discount_rate_override ??
267
+ (await affiliateService.retrieveProgramSettings())
268
+ .default_customer_discount_rate,
269
+ target_rules: promotion_eligibility_1.affiliateEligibleItemTargetRules,
270
+ },
271
+ },
272
+ ],
273
+ },
274
+ });
275
+ promotionId = created.result[0]?.id ?? null;
276
+ if (!promotionId) {
277
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "The Affiliate coupon promotion workflow returned no promotion.");
278
+ }
279
+ mayDeactivatePromotion = true;
280
+ outcome = "created";
281
+ promotion = await loadPromotionByExactCode(promotionService, coupon, campaign);
282
+ if (!promotion || promotion.id !== promotionId) {
283
+ throw couponCodeConflict();
284
+ }
285
+ }
286
+ if (!promotion || !promotionId) {
287
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "The Affiliate coupon promotion could not be resolved.");
288
+ }
289
+ const wasReady = readyCouponUntouched;
290
+ const settings = await affiliateService.retrieveProgramSettings();
291
+ const discountRate = affiliate.customer_discount_rate_override ??
292
+ settings.default_customer_discount_rate;
293
+ const eligibleVariantIds = input.eligible_variant_ids ??
294
+ (await (0, promotion_eligibility_1.listAffiliateEligibleVariantIds)(query));
295
+ const wasCurrent = (0, exports.isCurrentAffiliateCouponPromotion)({
296
+ promotion,
297
+ coupon,
298
+ affiliate,
299
+ campaign,
300
+ discount_rate: discountRate,
301
+ eligible_variant_ids: eligibleVariantIds,
302
+ });
303
+ if (wasReady && wasCurrent) {
304
+ return {
305
+ coupon,
306
+ outcome: coupon.lifecycle_status === "revoked"
307
+ ? "revoked_cleanup"
308
+ : outcome,
309
+ rotation_finalized: false,
310
+ revoked_coupon: null,
311
+ };
312
+ }
313
+ // Only a coupon that is not redeemable yet (new, adopted, pending,
314
+ // failed or being rotated) goes through "pending". A ready coupon is
315
+ // updated in place and stays ready.
316
+ if (!wasReady) {
317
+ coupon = await affiliateService.setAffiliateCouponProvisioningState({
318
+ coupon_id: coupon.id,
319
+ promotion_id: promotionId,
320
+ provisioning_status: "pending",
321
+ });
322
+ }
323
+ if (!wasCurrent) {
324
+ readyCouponUntouched = false;
325
+ await (0, core_flows_1.updatePromotionsWorkflow)(container).run({
326
+ input: {
327
+ promotionsData: [
328
+ {
329
+ id: promotionId,
330
+ type: "standard",
331
+ is_automatic: false,
332
+ status: coupon.lifecycle_status === "enabled" &&
333
+ affiliate.status === "active"
334
+ ? "active"
335
+ : "inactive",
336
+ application_method: {
337
+ type: "percentage",
338
+ target_type: "items",
339
+ allocation: "across",
340
+ value: discountRate,
341
+ },
342
+ },
343
+ ],
344
+ },
345
+ });
346
+ if (promotion.rules?.length) {
347
+ await promotionService.removePromotionRules(promotionId, promotion.rules.map(({ id }) => id));
348
+ }
349
+ await (0, promotion_eligibility_1.ensureAffiliateEligibleItemTargetRules)(promotionService, query, promotionId, eligibleVariantIds);
350
+ if (outcome === "unchanged") {
351
+ outcome = "repaired";
352
+ }
353
+ }
354
+ const currentPromotion = await loadPromotionByExactCode(promotionService, coupon, campaign);
355
+ if (!currentPromotion ||
356
+ currentPromotion.id !== promotionId ||
357
+ !(0, exports.isCurrentAffiliateCouponPromotion)({
358
+ promotion: currentPromotion,
359
+ coupon,
360
+ affiliate,
361
+ campaign,
362
+ discount_rate: discountRate,
363
+ eligible_variant_ids: eligibleVariantIds,
364
+ })) {
365
+ throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "The Affiliate coupon promotion remained inconsistent after reconciliation.");
366
+ }
367
+ if (coupon.lifecycle_status === "enabled" &&
368
+ coupon.replaces_coupon_id &&
369
+ coupon.provisioning_status !== "ready") {
370
+ const finalized = await affiliateService.finalizeAffiliateCouponRotation({
371
+ affiliate_id: coupon.affiliate_id,
372
+ predecessor_coupon_id: coupon.replaces_coupon_id,
373
+ successor_coupon_id: coupon.id,
374
+ promotion_id: promotionId,
375
+ revoked_by_type: coupon.created_by_type,
376
+ revoked_by_id: coupon.created_by_id,
377
+ });
378
+ return {
379
+ coupon: finalized.coupon,
380
+ outcome,
381
+ rotation_finalized: true,
382
+ revoked_coupon: finalized.revoked_coupon,
383
+ };
384
+ }
385
+ if (!wasReady) {
386
+ coupon = await affiliateService.setAffiliateCouponProvisioningState({
387
+ coupon_id: coupon.id,
388
+ promotion_id: promotionId,
389
+ provisioning_status: "ready",
390
+ });
391
+ }
392
+ return {
393
+ coupon,
394
+ outcome: coupon.lifecycle_status === "revoked" ? "revoked_cleanup" : outcome,
395
+ rotation_finalized: false,
396
+ revoked_coupon: null,
397
+ };
398
+ }
399
+ catch (error) {
400
+ if (readyCouponUntouched && !isDetectedInconsistency(error)) {
401
+ throw error;
402
+ }
403
+ return await persistProvisioningFailure(container, coupon.id, error, promotionId, mayDeactivatePromotion);
404
+ }
405
+ });
406
+ };
407
+ exports.provisionAffiliateCouponPromotion = provisionAffiliateCouponPromotion;
408
+ const ensureAffiliateCouponPromotionStep = (0, workflows_sdk_1.createStep)("ensure-affiliate-coupon-promotion", async (input, { container }) => new workflows_sdk_1.StepResponse(await (0, exports.provisionAffiliateCouponPromotion)(container, input)));
409
+ exports.ensureAffiliateCouponPromotionWorkflow = (0, workflows_sdk_1.createWorkflow)("ensure-affiliate-coupon-promotion", (input) => new workflows_sdk_1.WorkflowResponse(ensureAffiliateCouponPromotionStep(input)));
410
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5zdXJlLWFmZmlsaWF0ZS1jb3Vwb24tcHJvbW90aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9hZmZpbGlhdGUvZW5zdXJlLWFmZmlsaWF0ZS1jb3Vwb24tcHJvbW90aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQU1BLHFFQUswQztBQUMxQyxxREFJa0M7QUFDbEMsNERBSW9DO0FBR3BDLDZGQUlrRDtBQUNsRCx5RkFBNEY7QUFDNUYsbUVBQXNFO0FBQ3RFLG1FQU1nQztBQUVoQyxNQUFNLG9DQUFvQyxHQUFHLGlDQUFpQyxDQUFBO0FBcUI5RSxNQUFNLGtCQUFrQixHQUFHLEdBQWdCLEVBQUUsQ0FDM0MsSUFBSSxtQkFBVyxDQUFDLG1CQUFXLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxzQkFBc0IsQ0FBQyxDQUFBO0FBRXJFLE1BQU0sd0JBQXdCLEdBQUcsQ0FBQyxLQUFjLEVBQVUsRUFBRTtJQUMxRCxNQUFNLE9BQU8sR0FBRyxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUE7SUFDeEUsT0FBTyxtREFBbUQsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUN2RSxDQUFDLEVBQ0QsS0FBSyxDQUNOLENBQUE7QUFDSCxDQUFDLENBQUE7QUFFRCxNQUFNLHVCQUF1QixHQUFHLENBQUMsUUFBcUIsRUFBUSxFQUFFO0lBQzlELE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxNQUFNLENBQUE7SUFDOUIsSUFDRSxRQUFRLENBQUMsbUJBQW1CLEtBQUssOERBQXNDO1FBQ3ZFLE1BQU0sRUFBRSxJQUFJLEtBQUssa0JBQWtCO1FBQ25DLE1BQU0sQ0FBQyxTQUFTLEtBQUssZ0JBQWdCO1FBQ3JDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxFQUMxQixDQUFDO1FBQ0QsTUFBTSxJQUFJ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