@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
package/.medusa/server/src/modules/affiliate/services/customer-affiliate-assignment-manager.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CustomerAffiliateAssignmentManager = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const affiliate_attribution_policy_1 = require("../domain/affiliate-attribution-policy");
|
|
6
|
+
class CustomerAffiliateAssignmentManager {
|
|
7
|
+
constructor(store, affiliateService, slugService) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.affiliateService = affiliateService;
|
|
10
|
+
this.slugService = slugService;
|
|
11
|
+
}
|
|
12
|
+
async getActiveAssignment(customerId, context) {
|
|
13
|
+
return await this.getValidAssignmentByIdentity({ customer_id: customerId }, new Date(), context);
|
|
14
|
+
}
|
|
15
|
+
async getActiveAssignmentByIdentity(input, context) {
|
|
16
|
+
return await this.getValidAssignmentByIdentity(input, new Date(), context);
|
|
17
|
+
}
|
|
18
|
+
async getValidAssignmentByIdentity(input, now = new Date(), context) {
|
|
19
|
+
const assignment = await this.findActiveAssignmentByIdentity(input, context, now);
|
|
20
|
+
return assignment ? await this.attachAffiliate(assignment, context) : null;
|
|
21
|
+
}
|
|
22
|
+
async findActiveAssignmentByIdentity(input, context, now = new Date()) {
|
|
23
|
+
const [customerAssignments, emailAssignments] = await Promise.all([
|
|
24
|
+
input.customer_id
|
|
25
|
+
? this.store.listCustomerAffiliateAssignments({ customer_id: input.customer_id, active: true }, { order: { assigned_at: "DESC" } }, context)
|
|
26
|
+
: Promise.resolve([]),
|
|
27
|
+
input.normalized_email
|
|
28
|
+
? this.store.listCustomerAffiliateAssignments({ normalized_email: input.normalized_email, active: true }, { order: { assigned_at: "DESC" } }, context)
|
|
29
|
+
: Promise.resolve([]),
|
|
30
|
+
]);
|
|
31
|
+
const candidates = Array.from(new Map([...customerAssignments, ...emailAssignments].map((assignment) => [
|
|
32
|
+
assignment.id,
|
|
33
|
+
assignment,
|
|
34
|
+
])).values());
|
|
35
|
+
const expiredCandidates = candidates.filter((assignment) => this.isExpired(assignment, now));
|
|
36
|
+
for (const assignment of expiredCandidates) {
|
|
37
|
+
await this.endAssignment(assignment.id, "expired", context, now);
|
|
38
|
+
}
|
|
39
|
+
if (expiredCandidates.length) {
|
|
40
|
+
await this.store.flush(context);
|
|
41
|
+
}
|
|
42
|
+
const validCandidates = candidates.filter((assignment) => !this.isExpired(assignment, now));
|
|
43
|
+
const newest = (assignments) => [...assignments].sort((left, right) => {
|
|
44
|
+
const assignedAtDifference = new Date(right.assigned_at).getTime() -
|
|
45
|
+
new Date(left.assigned_at).getTime();
|
|
46
|
+
return assignedAtDifference || left.id.localeCompare(right.id);
|
|
47
|
+
})[0];
|
|
48
|
+
const customerAssignment = input.customer_id
|
|
49
|
+
? newest(validCandidates.filter((assignment) => assignment.customer_id === input.customer_id))
|
|
50
|
+
: undefined;
|
|
51
|
+
const emailAssignment = input.normalized_email
|
|
52
|
+
? newest(validCandidates.filter((assignment) => assignment.normalized_email === input.normalized_email))
|
|
53
|
+
: undefined;
|
|
54
|
+
if (customerAssignment &&
|
|
55
|
+
emailAssignment &&
|
|
56
|
+
customerAssignment.affiliate_id !== emailAssignment.affiliate_id) {
|
|
57
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "Customer and email are assigned to different affiliates.");
|
|
58
|
+
}
|
|
59
|
+
if (customerAssignment &&
|
|
60
|
+
emailAssignment &&
|
|
61
|
+
customerAssignment.id !== emailAssignment.id) {
|
|
62
|
+
// Preserve the customer-precedence row as the identity's live record.
|
|
63
|
+
// The email-only row remains historical so attribution/redemption FKs
|
|
64
|
+
// continue to reference the exact row that existed at the time.
|
|
65
|
+
await this.endAssignment(emailAssignment.id, "same_affiliate_identity_consolidated", context, now);
|
|
66
|
+
await this.store.flush(context);
|
|
67
|
+
}
|
|
68
|
+
return customerAssignment ?? emailAssignment ?? null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Order ingestion is the authoritative point for linking a guest
|
|
72
|
+
* email-assignment to the now known Customer ID. It never changes the
|
|
73
|
+
* affiliate and therefore cannot be used as a reassignment path.
|
|
74
|
+
*/
|
|
75
|
+
async resolveAssignmentForOrderIdentity(input, context) {
|
|
76
|
+
const assignment = await this.getValidAssignmentByIdentity(input, new Date(), context);
|
|
77
|
+
return assignment
|
|
78
|
+
? await this.attachMissingIdentity(assignment, input, context)
|
|
79
|
+
: null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checkout identity attachment is deliberately non-reassigning. Only the
|
|
83
|
+
* Admin-facing assignCustomer path below may end and replace an assignment.
|
|
84
|
+
*/
|
|
85
|
+
async assignIdentity(input, context) {
|
|
86
|
+
if (!input.customer_id && !input.normalized_email) {
|
|
87
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "A customer ID or normalized email is required.");
|
|
88
|
+
}
|
|
89
|
+
if (input.customer_id) {
|
|
90
|
+
await this.assertCustomerIsNotAffiliate(input.customer_id, context);
|
|
91
|
+
}
|
|
92
|
+
const affiliate = await this.store.retrieveAffiliate(input.affiliate_id, context);
|
|
93
|
+
this.affiliateService.assertAffiliateIsActive(affiliate);
|
|
94
|
+
return await this.createAssignmentFromAffiliate({ ...input, affiliate }, context);
|
|
95
|
+
}
|
|
96
|
+
async createAssignmentFromAffiliate(input, context) {
|
|
97
|
+
this.affiliateService.assertAffiliateIsActive(input.affiliate);
|
|
98
|
+
const now = input.assigned_at ?? new Date();
|
|
99
|
+
const activeAssignment = await this.getValidAssignmentByIdentity(input, now, context);
|
|
100
|
+
if (activeAssignment) {
|
|
101
|
+
if (activeAssignment.affiliate_id !== input.affiliate.id) {
|
|
102
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "This customer identity is already assigned to another affiliate.");
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
assignment: await this.attachMissingIdentity(activeAssignment, input, context),
|
|
106
|
+
created: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (input.affiliate.customer_assignment_mode === "order_only") {
|
|
110
|
+
return { assignment: null, created: false };
|
|
111
|
+
}
|
|
112
|
+
const validUntil = (0, affiliate_attribution_policy_1.calculateAssignmentValidUntil)({
|
|
113
|
+
customer_assignment_mode: input.affiliate.customer_assignment_mode,
|
|
114
|
+
assignment_duration_days: input.affiliate.assignment_duration_days,
|
|
115
|
+
assigned_at: now,
|
|
116
|
+
existing_assignment: null,
|
|
117
|
+
});
|
|
118
|
+
const created = await this.store.createCustomerAffiliateAssignments({
|
|
119
|
+
customer_id: input.customer_id ?? null,
|
|
120
|
+
normalized_email: input.normalized_email ?? null,
|
|
121
|
+
affiliate_id: input.affiliate.id,
|
|
122
|
+
source: input.source,
|
|
123
|
+
active: true,
|
|
124
|
+
assigned_at: now,
|
|
125
|
+
customer_assignment_mode: input.affiliate.customer_assignment_mode,
|
|
126
|
+
assignment_duration_days: input.affiliate.assignment_duration_days,
|
|
127
|
+
valid_until: validUntil ?? null,
|
|
128
|
+
ended_at: null,
|
|
129
|
+
ended_reason: null,
|
|
130
|
+
metadata: input.metadata ?? null,
|
|
131
|
+
}, context);
|
|
132
|
+
return {
|
|
133
|
+
assignment: await this.attachAffiliate(created, context),
|
|
134
|
+
created: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async assignCustomer(input, context) {
|
|
138
|
+
await this.assertCustomerIsNotAffiliate(input.customer_id, context);
|
|
139
|
+
const affiliate = await this.store.retrieveAffiliate(input.affiliate_id, context);
|
|
140
|
+
this.affiliateService.assertAffiliateIsActive(affiliate);
|
|
141
|
+
if (affiliate.customer_assignment_mode === "order_only") {
|
|
142
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "An order_only affiliate cannot be assigned manually.");
|
|
143
|
+
}
|
|
144
|
+
const assignedAt = new Date();
|
|
145
|
+
const candidates = await this.listActiveAssignmentsByIdentity(input, context);
|
|
146
|
+
const validAffiliates = new Set(candidates
|
|
147
|
+
.filter((candidate) => !this.isExpired(candidate, assignedAt))
|
|
148
|
+
.map((candidate) => candidate.affiliate_id));
|
|
149
|
+
if (validAffiliates.size > 1) {
|
|
150
|
+
for (const candidate of candidates) {
|
|
151
|
+
await this.endAssignment(candidate.id, "reassigned", context, assignedAt);
|
|
152
|
+
}
|
|
153
|
+
await this.store.flush(context);
|
|
154
|
+
}
|
|
155
|
+
const activeAssignment = await this.getValidAssignmentByIdentity({
|
|
156
|
+
customer_id: input.customer_id,
|
|
157
|
+
normalized_email: input.normalized_email,
|
|
158
|
+
}, assignedAt, context);
|
|
159
|
+
if (activeAssignment?.affiliate_id === affiliate.id) {
|
|
160
|
+
return await this.attachMissingIdentity(activeAssignment, input, context);
|
|
161
|
+
}
|
|
162
|
+
if (activeAssignment) {
|
|
163
|
+
await this.endAssignment(activeAssignment.id, "reassigned", context, assignedAt);
|
|
164
|
+
}
|
|
165
|
+
let created;
|
|
166
|
+
try {
|
|
167
|
+
const result = await this.createAssignmentFromAffiliate({
|
|
168
|
+
...input,
|
|
169
|
+
affiliate,
|
|
170
|
+
assigned_at: assignedAt,
|
|
171
|
+
}, context);
|
|
172
|
+
if (!result.assignment) {
|
|
173
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "A manual assignment unexpectedly produced no persistent assignment.");
|
|
174
|
+
}
|
|
175
|
+
created = result.assignment;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
// The facade owns a transaction for every Admin mutation. Once an
|
|
179
|
+
// INSERT fails, PostgreSQL rejects any recovery read in that same
|
|
180
|
+
// transaction; rethrow so the transaction rolls the prior end back.
|
|
181
|
+
if (context?.transactionManager) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
// Two concurrent requests can both pass the activeAssignment check
|
|
185
|
+
// above; the DB's partial unique index on (customer_id) where
|
|
186
|
+
// active = true then lets only one insert through. Resolve the race
|
|
187
|
+
// instead of surfacing the loser's constraint-violation error as-is.
|
|
188
|
+
const concurrentAssignment = await this.getActiveAssignment(input.customer_id, context);
|
|
189
|
+
if (!concurrentAssignment) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (concurrentAssignment.affiliate_id === affiliate.id) {
|
|
193
|
+
return concurrentAssignment;
|
|
194
|
+
}
|
|
195
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Customer was concurrently assigned to a different affiliate. Please retry.");
|
|
196
|
+
}
|
|
197
|
+
return await this.attachAffiliate(created, context);
|
|
198
|
+
}
|
|
199
|
+
async removeActiveAssignment(input, context) {
|
|
200
|
+
const candidates = await this.listActiveAssignmentsByIdentity(input, context);
|
|
201
|
+
const activeAffiliates = new Set(candidates.map((candidate) => candidate.affiliate_id));
|
|
202
|
+
if (activeAffiliates.size > 1) {
|
|
203
|
+
const ended = await Promise.all(candidates.map((candidate) => this.endAssignment(candidate.id, input.ended_reason, context)));
|
|
204
|
+
await this.store.flush(context);
|
|
205
|
+
return ended[0] ?? null;
|
|
206
|
+
}
|
|
207
|
+
const activeAssignment = await this.getValidAssignmentByIdentity(input, new Date(), context);
|
|
208
|
+
if (!activeAssignment) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return await this.endAssignment(activeAssignment.id, input.ended_reason, context);
|
|
212
|
+
}
|
|
213
|
+
async listAssignmentHistory(customerId, context) {
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const assignments = await this.store.listCustomerAffiliateAssignments({ customer_id: customerId }, { order: { assigned_at: "DESC" } }, context);
|
|
216
|
+
return await Promise.all(assignments.map((assignment) => assignment.active && this.isExpired(assignment, now)
|
|
217
|
+
? this.endAssignment(assignment.id, "expired", context, now)
|
|
218
|
+
: this.attachAffiliate(assignment, context)));
|
|
219
|
+
}
|
|
220
|
+
async expireAssignmentsBatch(input = {}, context) {
|
|
221
|
+
const now = input.now ?? new Date();
|
|
222
|
+
const assignments = await this.store.listCustomerAffiliateAssignments({ active: true, valid_until: { $lte: now } }, { take: input.limit ?? 100, order: { assigned_at: "ASC" } }, context);
|
|
223
|
+
return await Promise.all(assignments.map((assignment) => this.endAssignment(assignment.id, "expired", context, now)));
|
|
224
|
+
}
|
|
225
|
+
async expireOpenAssignmentsBatch(now, limit, context) {
|
|
226
|
+
const assignments = await this.store.listCustomerAffiliateAssignments({ active: true, valid_until: { $lte: now } }, { take: limit + 1, order: { assigned_at: "ASC" } }, context);
|
|
227
|
+
const due = assignments.slice(0, limit);
|
|
228
|
+
let processed = 0;
|
|
229
|
+
for (const assignment of due) {
|
|
230
|
+
const updated = await this.store.updateCustomerAffiliateAssignments({
|
|
231
|
+
selector: {
|
|
232
|
+
id: assignment.id,
|
|
233
|
+
active: true,
|
|
234
|
+
valid_until: { $lte: now },
|
|
235
|
+
},
|
|
236
|
+
data: {
|
|
237
|
+
active: false,
|
|
238
|
+
ended_at: now,
|
|
239
|
+
ended_reason: "expired",
|
|
240
|
+
},
|
|
241
|
+
}, context);
|
|
242
|
+
processed += Array.isArray(updated)
|
|
243
|
+
? updated.length
|
|
244
|
+
: Number(Boolean(updated));
|
|
245
|
+
}
|
|
246
|
+
return { processed, has_more: assignments.length > limit };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Ends every active assignment when a customer becomes an affiliate.
|
|
250
|
+
* Historical attribution remains auditable.
|
|
251
|
+
*/
|
|
252
|
+
async deleteAssignmentsForCustomer(customerId, context) {
|
|
253
|
+
const assignments = await this.store.listCustomerAffiliateAssignments({
|
|
254
|
+
customer_id: customerId,
|
|
255
|
+
active: true,
|
|
256
|
+
}, undefined, context);
|
|
257
|
+
if (assignments.length) {
|
|
258
|
+
for (const assignment of assignments) {
|
|
259
|
+
await this.endAssignment(assignment.id, "customer_became_affiliate", context);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
async removeAssignmentsForAffiliate(affiliateId, reason = "affiliate_inactive", context, now = new Date()) {
|
|
264
|
+
const activeAssignments = await this.store.listCustomerAffiliateAssignments({
|
|
265
|
+
affiliate_id: affiliateId,
|
|
266
|
+
active: true,
|
|
267
|
+
}, undefined, context);
|
|
268
|
+
const endedAssignments = [];
|
|
269
|
+
for (const assignment of activeAssignments) {
|
|
270
|
+
const updated = await this.store.updateCustomerAffiliateAssignments({
|
|
271
|
+
selector: { id: assignment.id, active: true },
|
|
272
|
+
data: { active: false, ended_at: now, ended_reason: reason },
|
|
273
|
+
}, context);
|
|
274
|
+
const ended = Array.isArray(updated) ? updated[0] : updated;
|
|
275
|
+
if (ended) {
|
|
276
|
+
endedAssignments.push(await this.attachAffiliate(ended, context));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return endedAssignments;
|
|
280
|
+
}
|
|
281
|
+
async isCustomerAffiliate(customerId) {
|
|
282
|
+
const affiliates = await this.store.listAffiliates({
|
|
283
|
+
customer_id: customerId,
|
|
284
|
+
});
|
|
285
|
+
return affiliates.some((affiliate) => affiliate.status !== "rejected");
|
|
286
|
+
}
|
|
287
|
+
async assertCustomerIsNotAffiliate(customerId, context) {
|
|
288
|
+
const affiliates = await this.store.listAffiliates({ customer_id: customerId }, undefined, context);
|
|
289
|
+
if (affiliates.some((affiliate) => affiliate.status !== "rejected")) {
|
|
290
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "This customer is a registered affiliate and cannot be assigned a referring affiliate.");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async endAssignment(assignmentId, reason, context, endedAt = new Date()) {
|
|
294
|
+
const updated = (await this.store.updateCustomerAffiliateAssignments({
|
|
295
|
+
id: assignmentId,
|
|
296
|
+
active: false,
|
|
297
|
+
ended_at: endedAt,
|
|
298
|
+
ended_reason: reason,
|
|
299
|
+
}, context));
|
|
300
|
+
return await this.attachAffiliate(updated, context);
|
|
301
|
+
}
|
|
302
|
+
async listActiveAssignmentsByIdentity(input, context) {
|
|
303
|
+
const [customerAssignments, emailAssignments] = await Promise.all([
|
|
304
|
+
input.customer_id
|
|
305
|
+
? this.store.listCustomerAffiliateAssignments({ customer_id: input.customer_id, active: true }, undefined, context)
|
|
306
|
+
: Promise.resolve([]),
|
|
307
|
+
input.normalized_email
|
|
308
|
+
? this.store.listCustomerAffiliateAssignments({ normalized_email: input.normalized_email, active: true }, undefined, context)
|
|
309
|
+
: Promise.resolve([]),
|
|
310
|
+
]);
|
|
311
|
+
return Array.from(new Map([...customerAssignments, ...emailAssignments].map((assignment) => [
|
|
312
|
+
assignment.id,
|
|
313
|
+
assignment,
|
|
314
|
+
])).values());
|
|
315
|
+
}
|
|
316
|
+
isExpired(assignment, now) {
|
|
317
|
+
return (assignment.valid_until !== null &&
|
|
318
|
+
new Date(assignment.valid_until).getTime() <= now.getTime());
|
|
319
|
+
}
|
|
320
|
+
async attachMissingIdentity(assignment, input, context) {
|
|
321
|
+
if ((input.customer_id &&
|
|
322
|
+
assignment.customer_id &&
|
|
323
|
+
input.customer_id !== assignment.customer_id) ||
|
|
324
|
+
(input.normalized_email &&
|
|
325
|
+
assignment.normalized_email &&
|
|
326
|
+
input.normalized_email !== assignment.normalized_email)) {
|
|
327
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "Customer identity does not match its existing affiliate assignment.");
|
|
328
|
+
}
|
|
329
|
+
const missingCustomerId = input.customer_id && !assignment.customer_id;
|
|
330
|
+
const missingEmail = input.normalized_email && !assignment.normalized_email;
|
|
331
|
+
if (!missingCustomerId && !missingEmail) {
|
|
332
|
+
return assignment;
|
|
333
|
+
}
|
|
334
|
+
const updated = (await this.store.updateCustomerAffiliateAssignments({
|
|
335
|
+
id: assignment.id,
|
|
336
|
+
...(missingCustomerId ? { customer_id: input.customer_id } : {}),
|
|
337
|
+
...(missingEmail ? { normalized_email: input.normalized_email } : {}),
|
|
338
|
+
}, context));
|
|
339
|
+
return await this.attachAffiliate(updated, context);
|
|
340
|
+
}
|
|
341
|
+
async attachAffiliate(assignment, context) {
|
|
342
|
+
const affiliate = await this.affiliateService.retrieveAffiliateWithSlugs(assignment.affiliate_id, context);
|
|
343
|
+
return {
|
|
344
|
+
...assignment,
|
|
345
|
+
affiliate,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
exports.CustomerAffiliateAssignmentManager = CustomerAffiliateAssignmentManager;
|
|
350
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tZXItYWZmaWxpYXRlLWFzc2lnbm1lbnQtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL2FmZmlsaWF0ZS9zZXJ2aWNlcy9jdXN0b21lci1hZmZpbGlhdGUtYXNzaWdubWVudC1tYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFEQUF1RDtBQWdCdkQseUZBQXNGO0FBRXRGLE1BQWEsa0NBQWtDO0lBQzdDLFlBQ21CLEtBQTJCLEVBQzNCLGdCQUFrQyxFQUNsQyxXQUFpQztRQUZqQyxVQUFLLEdBQUwsS0FBSyxDQUFzQjtRQUMzQixxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBQ2xDLGdCQUFXLEdBQVgsV0FBVyxDQUFzQjtJQUNqRCxDQUFDO0lBRUosS0FBSyxDQUFDLG1CQUFtQixDQUN2QixVQUFrQixFQUNsQixPQUFpQjtRQUVqQixPQUFPLE1BQU0sSUFBSSxDQUFDLDRCQUE0QixDQUM1QyxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsRUFDM0IsSUFBSSxJQUFJLEVBQUUsRUFDVixPQUFPLENBQ1IsQ0FBQTtJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsNkJBQTZCLENBQ2pDLEtBQTZCLEVBQzdCLE9BQWlCO1FBRWpCLE9BQU8sTUFBTSxJQUFJLENBQUMsNEJBQTRCLENBQUMsS0FBSyxFQUFFLElBQUksSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUE7SUFDNUUsQ0FBQztJQUVELEtBQUssQ0FBQyw0QkFBNEIsQ0FDaEMsS0FBNkIsRUFDN0IsR0FBRyxHQUFHLElBQUksSUFBSSxFQUFFLEVBQ2hCLE9BQWlCO1FBRWpCLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLDhCQUE4QixDQUMxRCxLQUFLLEVBQ0wsT0FBTyxFQUNQLEdBQUcsQ0FDSixDQUFBO1FBQ0QsT0FBTyxVQUFVLENBQUMsQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFD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
|