@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.
- package/.medusa/server/src/admin/index.js +3739 -0
- package/.medusa/server/src/admin/index.mjs +3738 -0
- package/.medusa/server/src/api/admin/affiliate-applications/[id]/approve/route.js +28 -0
- package/.medusa/server/src/api/admin/affiliate-applications/[id]/reject/route.js +19 -0
- package/.medusa/server/src/api/admin/affiliate-applications/route.js +23 -0
- package/.medusa/server/src/api/admin/affiliate-distribution-reports/query.js +171 -0
- package/.medusa/server/src/api/admin/affiliate-distribution-reports/route.js +13 -0
- package/.medusa/server/src/api/admin/affiliate-program-settings/route.js +27 -0
- package/.medusa/server/src/api/admin/affiliate-reports/[id]/route.js +45 -0
- package/.medusa/server/src/api/admin/affiliate-reports/route.js +27 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/consignment-reminders/route.js +31 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/consignment-transfers/route.js +41 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/reconcile/route.js +34 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/revoke/route.js +36 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/[coupon_id]/rotate/route.js +40 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/coupon-codes/route.js +56 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/distribution/route.js +45 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/inactive/route.js +20 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/route.js +50 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/slugs/[slug_id]/route.js +19 -0
- package/.medusa/server/src/api/admin/affiliates/[id]/slugs/route.js +26 -0
- package/.medusa/server/src/api/admin/affiliates/route.js +43 -0
- package/.medusa/server/src/api/admin/consignment-report-response.js +36 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/approve/route.js +25 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/correct/route.js +18 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/reject/route.js +15 -0
- package/.medusa/server/src/api/admin/consignment-reports/[id]/route.js +11 -0
- package/.medusa/server/src/api/admin/consignment-reports/route.js +27 -0
- package/.medusa/server/src/api/admin/customers/[id]/affiliate/route.js +50 -0
- package/.medusa/server/src/api/admin/orders/[id]/affiliate-attribution/route.js +30 -0
- package/.medusa/server/src/api/affiliate-coupon-http.js +152 -0
- package/.medusa/server/src/api/affiliate-referral-cookie.js +34 -0
- package/.medusa/server/src/api/affiliate-self-service-http.js +37 -0
- package/.medusa/server/src/api/middlewares.js +786 -0
- package/.medusa/server/src/api/store/affiliate-referrals/apply-coupon/route.js +47 -0
- package/.medusa/server/src/api/store/affiliate-referrals/capture/route.js +76 -0
- package/.medusa/server/src/api/store/affiliate-referrals/carts/[cart_id]/route.js +20 -0
- package/.medusa/server/src/api/store/affiliates/consignment-inventory/route.js +53 -0
- package/.medusa/server/src/api/store/affiliates/consignment-report-response.js +31 -0
- package/.medusa/server/src/api/store/affiliates/consignment-reports/[id]/submit/route.js +40 -0
- package/.medusa/server/src/api/store/affiliates/consignment-reports/route.js +43 -0
- package/.medusa/server/src/api/store/affiliates/coupon-labels/route.js +14 -0
- package/.medusa/server/src/api/store/affiliates/me/commissions/route.js +35 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/revoke/route.js +36 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/[coupon_id]/rotate/route.js +40 -0
- package/.medusa/server/src/api/store/affiliates/me/coupon-codes/route.js +55 -0
- package/.medusa/server/src/api/store/affiliates/me/dashboard/route.js +15 -0
- package/.medusa/server/src/api/store/affiliates/me/route.js +85 -0
- package/.medusa/server/src/api/store/affiliates/me/slugs/[slug_id]/route.js +29 -0
- package/.medusa/server/src/api/store/affiliates/me/slugs/route.js +34 -0
- package/.medusa/server/src/api/store/affiliates/own-order-discount/route.js +22 -0
- package/.medusa/server/src/api/store/affiliates/own-order-promotion/route.js +30 -0
- package/.medusa/server/src/api/store/affiliates/register/route.js +27 -0
- package/.medusa/server/src/api/store/affiliates/resolve-referral/route.js +34 -0
- package/.medusa/server/src/api/validators.js +52 -0
- package/.medusa/server/src/api/wire-response.js +10 -0
- package/.medusa/server/src/integration/email-events.js +51 -0
- package/.medusa/server/src/integrations/inventory-transfer/batch-management-transfer-adapter.js +36 -0
- package/.medusa/server/src/integrations/inventory-transfer/native-inventory-transfer-adapter.js +30 -0
- package/.medusa/server/src/integrations/inventory-transfer/resolve-inventory-transfer-adapter.js +46 -0
- package/.medusa/server/src/integrations/inventory-transfer/types.js +3 -0
- package/.medusa/server/src/jobs/affiliate-attribution-expiry.js +20 -0
- package/.medusa/server/src/jobs/affiliate-consignment-report-reminders.js +15 -0
- package/.medusa/server/src/jobs/affiliate-promotion-freshness.js +24 -0
- package/.medusa/server/src/lib/query-string.js +15 -0
- package/.medusa/server/src/modules/affiliate/domain/affiliate-attribution-policy.js +81 -0
- package/.medusa/server/src/modules/affiliate/domain/affiliate-coupon-policy.js +61 -0
- package/.medusa/server/src/modules/affiliate/domain/commission-policy.js +33 -0
- package/.medusa/server/src/modules/affiliate/domain/consignment-reminder-rules.js +74 -0
- package/.medusa/server/src/modules/affiliate/domain/email-normalization.js +20 -0
- package/.medusa/server/src/modules/affiliate/domain/own-order-coupon-code.js +37 -0
- package/.medusa/server/src/modules/affiliate/domain/plugin-options.js +40 -0
- package/.medusa/server/src/modules/affiliate/index.js +13 -0
- package/.medusa/server/src/modules/affiliate/migrations/Migration20260827085154.js +423 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-application.js +38 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-cart-referral.js +65 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-code-redemption.js +41 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-coupon.js +55 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-distribution-settings.js +22 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-order-attribution.js +49 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-program-settings.js +28 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-referral-session.js +34 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate-slug.js +27 -0
- package/.medusa/server/src/modules/affiliate/models/affiliate.js +49 -0
- package/.medusa/server/src/modules/affiliate/models/commission-entry.js +62 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-report-line.js +21 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-report.js +31 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-transfer-line.js +16 -0
- package/.medusa/server/src/modules/affiliate/models/consignment-transfer.js +20 -0
- package/.medusa/server/src/modules/affiliate/models/customer-affiliate-assignment.js +58 -0
- package/.medusa/server/src/modules/affiliate/service.js +2321 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-application-manager.js +171 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-coupon-manager.js +441 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-discount.service.js +55 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-manager.js +242 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-partner-portal.service.js +138 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-report.service.js +436 -0
- package/.medusa/server/src/modules/affiliate/services/affiliate-slug-manager.js +163 -0
- package/.medusa/server/src/modules/affiliate/services/commission-ledger.service.js +729 -0
- package/.medusa/server/src/modules/affiliate/services/customer-affiliate-assignment-manager.js +350 -0
- package/.medusa/server/src/modules/affiliate/services/order-attribution-manager.js +1051 -0
- package/.medusa/server/src/modules/affiliate/services/referral-lifecycle-manager.js +1420 -0
- package/.medusa/server/src/modules/affiliate/services/utils.js +49 -0
- package/.medusa/server/src/modules/affiliate/types/internal.js +3 -0
- package/.medusa/server/src/paths/affiliate.js +7 -0
- package/.medusa/server/src/paths/index.js +18 -0
- package/.medusa/server/src/schemas/index.js +18 -0
- package/.medusa/server/src/schemas/store.js +24 -0
- package/.medusa/server/src/subscribers/affiliate-cart-updated.js +92 -0
- package/.medusa/server/src/subscribers/affiliate-order-canceled.js +70 -0
- package/.medusa/server/src/subscribers/affiliate-order-placed.js +63 -0
- package/.medusa/server/src/subscribers/affiliate-order-return-received.js +67 -0
- package/.medusa/server/src/subscribers/affiliate-product-eligibility-updated.js +87 -0
- package/.medusa/server/src/types/admin-api.js +3 -0
- package/.medusa/server/src/types/enums.js +53 -0
- package/.medusa/server/src/types/index.js +32 -0
- package/.medusa/server/src/types/public.js +3 -0
- package/.medusa/server/src/types/store-api.js +3 -0
- package/.medusa/server/src/utils/affiliate-cart-operation-lock.js +316 -0
- package/.medusa/server/src/utils/affiliate-promotion-mutation-authorization.js +49 -0
- package/.medusa/server/src/utils/cart-promotions.js +9 -0
- package/.medusa/server/src/utils/money.js +79 -0
- package/.medusa/server/src/utils/promotion-code-availability.js +34 -0
- package/.medusa/server/src/utils/registered-email-checkout.js +101 -0
- package/.medusa/server/src/utils/resolve-services.js +7 -0
- package/.medusa/server/src/utils/settle-cart-promotions.js +44 -0
- package/.medusa/server/src/workflows/affiliate/affiliate-coupon-cart.js +215 -0
- package/.medusa/server/src/workflows/affiliate/affiliate-coupon-command-authorization.js +20 -0
- package/.medusa/server/src/workflows/affiliate/apply-affiliate-coupon.js +174 -0
- package/.medusa/server/src/workflows/affiliate/apply-own-order-promotion.js +89 -0
- package/.medusa/server/src/workflows/affiliate/approve-consignment-report.js +186 -0
- package/.medusa/server/src/workflows/affiliate/correct-approved-consignment-report.js +263 -0
- package/.medusa/server/src/workflows/affiliate/create-affiliate-coupon.js +35 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-cancellation.js +21 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-correction-for-refund.js +24 -0
- package/.medusa/server/src/workflows/affiliate/create-commission-for-order.js +17 -0
- package/.medusa/server/src/workflows/affiliate/deactivate-affiliate.js +89 -0
- package/.medusa/server/src/workflows/affiliate/enable-distribution.js +120 -0
- package/.medusa/server/src/workflows/affiliate/enrich-affiliate-report.js +75 -0
- package/.medusa/server/src/workflows/affiliate/ensure-affiliate-coupon-promotion.js +410 -0
- package/.medusa/server/src/workflows/affiliate/ensure-affiliate-own-order-promotion.js +240 -0
- package/.medusa/server/src/workflows/affiliate/ensure-default-affiliate-coupon.js +32 -0
- package/.medusa/server/src/workflows/affiliate/expire-affiliate-attribution-state.js +131 -0
- package/.medusa/server/src/workflows/affiliate/finalize-affiliate-order-attribution.js +82 -0
- package/.medusa/server/src/workflows/affiliate/payment-authorization.js +33 -0
- package/.medusa/server/src/workflows/affiliate/promotion-eligibility.js +179 -0
- package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-cart-referral.js +219 -0
- package/.medusa/server/src/workflows/affiliate/reconcile-affiliate-coupon.js +388 -0
- package/.medusa/server/src/workflows/affiliate/refresh-affiliate-promotions.js +114 -0
- package/.medusa/server/src/workflows/affiliate/resolve-coupon-labels.js +40 -0
- package/.medusa/server/src/workflows/affiliate/revoke-affiliate-coupon.js +29 -0
- package/.medusa/server/src/workflows/affiliate/rotate-affiliate-coupon.js +36 -0
- package/.medusa/server/src/workflows/affiliate/run-consignment-report-reminders.js +58 -0
- package/.medusa/server/src/workflows/affiliate/submit-consignment-report.js +279 -0
- package/.medusa/server/src/workflows/affiliate/transfer-batch-aware-inventory.js +172 -0
- package/.medusa/server/src/workflows/affiliate/transfer-consignment-stock.js +106 -0
- package/.medusa/server/src/workflows/affiliate/update-affiliate-coupon-discounts.js +129 -0
- package/.medusa/server/src/workflows/affiliate/utils.js +118 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-affiliate-attribution.js +57 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-order-created-composition.js +111 -0
- package/.medusa/server/src/workflows/hooks/complete-cart-own-order-discount.js +75 -0
- package/.medusa/server/src/workflows/hooks/enforce-unique-promotion-codes.js +33 -0
- package/.medusa/server/src/workflows/hooks/validate-affiliate-customer-code.js +239 -0
- package/.medusa/server/src/workflows/hooks/validate-complete-cart-affiliate-referral.js +137 -0
- package/LICENSE +21 -0
- package/README.md +799 -0
- package/dist/integration/email-events.d.ts +20 -0
- package/dist/integration/email-events.js +50 -0
- 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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
|