@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,441 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AffiliateCouponManager = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const affiliate_coupon_policy_1 = require("../domain/affiliate-coupon-policy");
|
|
6
|
+
class AffiliateCouponManager {
|
|
7
|
+
constructor(store, generateCode = affiliate_coupon_policy_1.generateAffiliateCouponCode) {
|
|
8
|
+
this.store = store;
|
|
9
|
+
this.generateCode = generateCode;
|
|
10
|
+
}
|
|
11
|
+
async retrieveCoupon(couponId, affiliateId, context) {
|
|
12
|
+
await this.store.flush(context);
|
|
13
|
+
const [coupon] = await this.store.listAffiliateCoupons({
|
|
14
|
+
id: couponId,
|
|
15
|
+
...(affiliateId ? { affiliate_id: affiliateId } : {}),
|
|
16
|
+
}, { take: 1 }, context);
|
|
17
|
+
if (!coupon) {
|
|
18
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Affiliate coupon was not found.");
|
|
19
|
+
}
|
|
20
|
+
return coupon;
|
|
21
|
+
}
|
|
22
|
+
async findCouponByCode(code, context) {
|
|
23
|
+
const normalizedCode = this.normalizeCode(code);
|
|
24
|
+
const [coupon] = await this.store.listAffiliateCoupons({ code: normalizedCode }, { take: 1 }, context);
|
|
25
|
+
return coupon ?? null;
|
|
26
|
+
}
|
|
27
|
+
async findEnabledCouponByCode(code, context) {
|
|
28
|
+
const normalizedCode = this.normalizeCode(code);
|
|
29
|
+
const [coupon] = await this.store.listAffiliateCoupons({ code: normalizedCode, lifecycle_status: "enabled" }, { take: 1 }, context);
|
|
30
|
+
return coupon ?? null;
|
|
31
|
+
}
|
|
32
|
+
async findCouponByPromotionId(promotionId, context) {
|
|
33
|
+
const [coupon] = await this.store.listAffiliateCoupons({ promotion_id: promotionId }, { take: 1 }, context);
|
|
34
|
+
return coupon ?? null;
|
|
35
|
+
}
|
|
36
|
+
async findCouponByCreationIdempotencyKey(idempotencyKey, affiliateId, context) {
|
|
37
|
+
const [coupon] = await this.store.listAffiliateCoupons({
|
|
38
|
+
creation_idempotency_key: idempotencyKey,
|
|
39
|
+
...(affiliateId ? { affiliate_id: affiliateId } : {}),
|
|
40
|
+
}, { take: 1 }, context);
|
|
41
|
+
return coupon ?? null;
|
|
42
|
+
}
|
|
43
|
+
async listCouponBindingsByPromotionIds(promotionIds, context) {
|
|
44
|
+
if (!promotionIds.length) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return await this.store.listAffiliateCoupons({ promotion_id: [...new Set(promotionIds)] }, { order: { created_at: "ASC" } }, context);
|
|
48
|
+
}
|
|
49
|
+
async listCoupons(input, context) {
|
|
50
|
+
return await this.store.listAndCountAffiliateCoupons({
|
|
51
|
+
...(input.affiliate_id ? { affiliate_id: input.affiliate_id } : {}),
|
|
52
|
+
...(input.lifecycle_status
|
|
53
|
+
? { lifecycle_status: input.lifecycle_status }
|
|
54
|
+
: {}),
|
|
55
|
+
...(input.provisioning_status
|
|
56
|
+
? { provisioning_status: input.provisioning_status }
|
|
57
|
+
: {}),
|
|
58
|
+
}, {
|
|
59
|
+
skip: input.offset,
|
|
60
|
+
take: input.limit,
|
|
61
|
+
order: { created_at: "DESC" },
|
|
62
|
+
}, context);
|
|
63
|
+
}
|
|
64
|
+
async ensureDefaultCoupon(input, context = {}) {
|
|
65
|
+
await this.lockAffiliateAndKeys(input.affiliate_id, [], context);
|
|
66
|
+
await this.assertAffiliateExists(input.affiliate_id, context);
|
|
67
|
+
const coupons = await this.store.listAffiliateCoupons({ affiliate_id: input.affiliate_id }, { take: 1, order: { created_at: "ASC" } }, context);
|
|
68
|
+
if (coupons.length) {
|
|
69
|
+
return { coupon: null, created: false };
|
|
70
|
+
}
|
|
71
|
+
const candidates = this.generateCodeCandidates();
|
|
72
|
+
await this.lockCodeCandidates(candidates, context);
|
|
73
|
+
const code = await this.selectAvailableGeneratedCode(candidates, context);
|
|
74
|
+
const coupon = await this.createPendingCoupon({
|
|
75
|
+
affiliate_id: input.affiliate_id,
|
|
76
|
+
code,
|
|
77
|
+
origin: "default",
|
|
78
|
+
creation_idempotency_key: null,
|
|
79
|
+
replaces_coupon_id: null,
|
|
80
|
+
created_by_type: "system",
|
|
81
|
+
created_by_id: null,
|
|
82
|
+
}, context);
|
|
83
|
+
return { coupon, created: true };
|
|
84
|
+
}
|
|
85
|
+
async createCoupon(input, context = {}) {
|
|
86
|
+
if (input.origin !== "manual" || input.replaces_coupon_id != null) {
|
|
87
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Manual coupon creation cannot define a replacement coupon.");
|
|
88
|
+
}
|
|
89
|
+
const idempotencyKey = this.requireIdempotencyKey(input.creation_idempotency_key);
|
|
90
|
+
const requestedCode = input.code !== undefined
|
|
91
|
+
? this.normalizeCode(input.code)
|
|
92
|
+
: undefined;
|
|
93
|
+
const generatedCandidates = requestedCode
|
|
94
|
+
? []
|
|
95
|
+
: this.generateCodeCandidates();
|
|
96
|
+
await this.lockAffiliateAndKeys(input.affiliate_id, [
|
|
97
|
+
`affiliate-coupon-idempotency:${idempotencyKey}`,
|
|
98
|
+
...(requestedCode
|
|
99
|
+
? [`affiliate-coupon-code:${requestedCode}`]
|
|
100
|
+
: generatedCandidates.map((code) => `affiliate-coupon-code:${code}`)),
|
|
101
|
+
], context);
|
|
102
|
+
await this.assertAffiliateExists(input.affiliate_id, context);
|
|
103
|
+
const idempotent = await this.findByIdempotencyKey(idempotencyKey, context);
|
|
104
|
+
if (idempotent) {
|
|
105
|
+
this.assertIdempotentIntent(idempotent, {
|
|
106
|
+
...input,
|
|
107
|
+
creation_idempotency_key: idempotencyKey,
|
|
108
|
+
code: requestedCode,
|
|
109
|
+
replaces_coupon_id: null,
|
|
110
|
+
created_by_id: input.created_by_id ?? null,
|
|
111
|
+
});
|
|
112
|
+
return { coupon: idempotent, created: false };
|
|
113
|
+
}
|
|
114
|
+
const coupons = await this.listAllAffiliateCoupons(input.affiliate_id, context);
|
|
115
|
+
this.assertCouponSlotAvailable(coupons);
|
|
116
|
+
const code = requestedCode ??
|
|
117
|
+
(await this.selectAvailableGeneratedCode(generatedCandidates, context));
|
|
118
|
+
await this.assertCodeAvailable(code, context);
|
|
119
|
+
const coupon = await this.createPendingCoupon({
|
|
120
|
+
...input,
|
|
121
|
+
code,
|
|
122
|
+
creation_idempotency_key: idempotencyKey,
|
|
123
|
+
replaces_coupon_id: null,
|
|
124
|
+
created_by_id: input.created_by_id ?? null,
|
|
125
|
+
}, context);
|
|
126
|
+
return { coupon, created: true };
|
|
127
|
+
}
|
|
128
|
+
async createRotation(input, context = {}) {
|
|
129
|
+
const idempotencyKey = this.requireIdempotencyKey(input.creation_idempotency_key);
|
|
130
|
+
const requestedCode = input.code !== undefined
|
|
131
|
+
? this.normalizeCode(input.code)
|
|
132
|
+
: undefined;
|
|
133
|
+
const generatedCandidates = requestedCode
|
|
134
|
+
? []
|
|
135
|
+
: this.generateCodeCandidates();
|
|
136
|
+
await this.lockAffiliateAndKeys(input.affiliate_id, [
|
|
137
|
+
`affiliate-coupon:${input.coupon_id}`,
|
|
138
|
+
`affiliate-coupon-idempotency:${idempotencyKey}`,
|
|
139
|
+
...(requestedCode
|
|
140
|
+
? [`affiliate-coupon-code:${requestedCode}`]
|
|
141
|
+
: generatedCandidates.map((code) => `affiliate-coupon-code:${code}`)),
|
|
142
|
+
], context);
|
|
143
|
+
await this.assertAffiliateExists(input.affiliate_id, context);
|
|
144
|
+
const idempotent = await this.findByIdempotencyKey(idempotencyKey, context);
|
|
145
|
+
if (idempotent) {
|
|
146
|
+
this.assertIdempotentIntent(idempotent, {
|
|
147
|
+
affiliate_id: input.affiliate_id,
|
|
148
|
+
code: requestedCode,
|
|
149
|
+
origin: "rotation",
|
|
150
|
+
creation_idempotency_key: idempotencyKey,
|
|
151
|
+
replaces_coupon_id: input.coupon_id,
|
|
152
|
+
created_by_type: input.created_by_type,
|
|
153
|
+
created_by_id: input.created_by_id ?? null,
|
|
154
|
+
});
|
|
155
|
+
return { coupon: idempotent, created: false };
|
|
156
|
+
}
|
|
157
|
+
const predecessor = await this.retrieveCoupon(input.coupon_id, input.affiliate_id, context);
|
|
158
|
+
if (!(0, affiliate_coupon_policy_1.isAffiliateCouponRotationSource)(predecessor)) {
|
|
159
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "Only an enabled, ready affiliate coupon can be rotated.");
|
|
160
|
+
}
|
|
161
|
+
const [activeSuccessor] = await this.store.listAffiliateCoupons({
|
|
162
|
+
replaces_coupon_id: predecessor.id,
|
|
163
|
+
lifecycle_status: "enabled",
|
|
164
|
+
}, { take: 1 }, context);
|
|
165
|
+
if (activeSuccessor) {
|
|
166
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The affiliate coupon already has an active rotation successor.");
|
|
167
|
+
}
|
|
168
|
+
const coupons = await this.listAllAffiliateCoupons(input.affiliate_id, context);
|
|
169
|
+
const code = requestedCode ??
|
|
170
|
+
(await this.selectAvailableGeneratedCode(generatedCandidates, context));
|
|
171
|
+
await this.assertCodeAvailable(code, context);
|
|
172
|
+
const prospectiveCoupons = [
|
|
173
|
+
...coupons,
|
|
174
|
+
{
|
|
175
|
+
id: `rotation:${idempotencyKey}`,
|
|
176
|
+
lifecycle_status: "enabled",
|
|
177
|
+
replaces_coupon_id: predecessor.id,
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
if ((0, affiliate_coupon_policy_1.countLogicalEnabledAffiliateCouponSlots)(prospectiveCoupons) >
|
|
181
|
+
affiliate_coupon_policy_1.MAX_ENABLED_COUPONS_PER_AFFILIATE) {
|
|
182
|
+
throw this.couponLimitReached();
|
|
183
|
+
}
|
|
184
|
+
const coupon = await this.createPendingCoupon({
|
|
185
|
+
affiliate_id: input.affiliate_id,
|
|
186
|
+
code,
|
|
187
|
+
origin: "rotation",
|
|
188
|
+
creation_idempotency_key: idempotencyKey,
|
|
189
|
+
replaces_coupon_id: predecessor.id,
|
|
190
|
+
created_by_type: input.created_by_type,
|
|
191
|
+
created_by_id: input.created_by_id ?? null,
|
|
192
|
+
}, context);
|
|
193
|
+
return { coupon, created: true };
|
|
194
|
+
}
|
|
195
|
+
async revokeCoupon(input, context = {}) {
|
|
196
|
+
await this.lockAffiliateAndKeys(input.affiliate_id, [`affiliate-coupon:${input.coupon_id}`], context);
|
|
197
|
+
const coupon = await this.retrieveCoupon(input.coupon_id, input.affiliate_id, context);
|
|
198
|
+
if (coupon.lifecycle_status === "revoked") {
|
|
199
|
+
return coupon;
|
|
200
|
+
}
|
|
201
|
+
return (await this.store.updateAffiliateCoupons({
|
|
202
|
+
id: coupon.id,
|
|
203
|
+
lifecycle_status: "revoked",
|
|
204
|
+
provisioning_status: "pending",
|
|
205
|
+
promotion_setup_error: null,
|
|
206
|
+
revoked_at: input.revoked_at ?? new Date(),
|
|
207
|
+
revoked_by_type: input.revoked_by_type,
|
|
208
|
+
revoked_by_id: input.revoked_by_id ?? null,
|
|
209
|
+
}, context));
|
|
210
|
+
}
|
|
211
|
+
async setProvisioningState(input, context = {}) {
|
|
212
|
+
const observed = await this.retrieveCoupon(input.coupon_id, undefined, context);
|
|
213
|
+
const observedPromotionId = input.promotion_id === undefined
|
|
214
|
+
? observed.promotion_id
|
|
215
|
+
: input.promotion_id;
|
|
216
|
+
await this.lockAffiliateAndKeys(observed.affiliate_id, [
|
|
217
|
+
`affiliate-coupon:${input.coupon_id}`,
|
|
218
|
+
...(observedPromotionId
|
|
219
|
+
? [`affiliate-coupon-promotion:${observedPromotionId}`]
|
|
220
|
+
: []),
|
|
221
|
+
], context);
|
|
222
|
+
const coupon = await this.retrieveCoupon(input.coupon_id, undefined, context);
|
|
223
|
+
const promotionId = input.promotion_id === undefined ? coupon.promotion_id : input.promotion_id;
|
|
224
|
+
if (coupon.promotion_id &&
|
|
225
|
+
input.promotion_id !== undefined &&
|
|
226
|
+
input.promotion_id !== coupon.promotion_id) {
|
|
227
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "An affiliate coupon promotion binding cannot be replaced.");
|
|
228
|
+
}
|
|
229
|
+
if (promotionId) {
|
|
230
|
+
await this.assertPromotionAvailable(promotionId, coupon.id, context);
|
|
231
|
+
}
|
|
232
|
+
const setupError = input.promotion_setup_error?.trim() || null;
|
|
233
|
+
if (input.provisioning_status === "failed" && !setupError) {
|
|
234
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "A failed affiliate coupon requires a setup error.");
|
|
235
|
+
}
|
|
236
|
+
if (input.provisioning_status !== "failed" && setupError) {
|
|
237
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Only a failed affiliate coupon may retain a setup error.");
|
|
238
|
+
}
|
|
239
|
+
if (input.provisioning_status === "ready" &&
|
|
240
|
+
coupon.lifecycle_status === "enabled" &&
|
|
241
|
+
!promotionId) {
|
|
242
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "An enabled, ready affiliate coupon requires a promotion binding.");
|
|
243
|
+
}
|
|
244
|
+
if (input.provisioning_status === "ready" &&
|
|
245
|
+
coupon.lifecycle_status === "enabled" &&
|
|
246
|
+
coupon.replaces_coupon_id &&
|
|
247
|
+
coupon.provisioning_status !== "ready") {
|
|
248
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "A rotation successor becomes ready only through rotation finalization.");
|
|
249
|
+
}
|
|
250
|
+
return (await this.store.updateAffiliateCoupons({
|
|
251
|
+
id: coupon.id,
|
|
252
|
+
promotion_id: promotionId,
|
|
253
|
+
provisioning_status: input.provisioning_status,
|
|
254
|
+
promotion_setup_error: input.provisioning_status === "failed" ? setupError : null,
|
|
255
|
+
}, context));
|
|
256
|
+
}
|
|
257
|
+
async finalizeRotation(input, context = {}) {
|
|
258
|
+
const observedSuccessor = await this.retrieveCoupon(input.successor_coupon_id, input.affiliate_id, context);
|
|
259
|
+
const observedPromotionId = input.promotion_id ?? observedSuccessor.promotion_id;
|
|
260
|
+
await this.lockAffiliateAndKeys(input.affiliate_id, [
|
|
261
|
+
`affiliate-coupon:${input.predecessor_coupon_id}`,
|
|
262
|
+
`affiliate-coupon:${input.successor_coupon_id}`,
|
|
263
|
+
...(observedPromotionId
|
|
264
|
+
? [`affiliate-coupon-promotion:${observedPromotionId}`]
|
|
265
|
+
: []),
|
|
266
|
+
], context);
|
|
267
|
+
const [predecessor, successor] = await Promise.all([
|
|
268
|
+
this.retrieveCoupon(input.predecessor_coupon_id, input.affiliate_id, context),
|
|
269
|
+
this.retrieveCoupon(input.successor_coupon_id, input.affiliate_id, context),
|
|
270
|
+
]);
|
|
271
|
+
if (successor.replaces_coupon_id !== predecessor.id) {
|
|
272
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The affiliate coupon is not the requested rotation successor.");
|
|
273
|
+
}
|
|
274
|
+
if (input.promotion_id &&
|
|
275
|
+
successor.promotion_id &&
|
|
276
|
+
input.promotion_id !== successor.promotion_id) {
|
|
277
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "An affiliate coupon promotion binding cannot be replaced.");
|
|
278
|
+
}
|
|
279
|
+
const promotionId = input.promotion_id ?? successor.promotion_id;
|
|
280
|
+
if (predecessor.lifecycle_status === "revoked") {
|
|
281
|
+
if (successor.lifecycle_status !== "enabled") {
|
|
282
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The rotation successor is no longer enabled.");
|
|
283
|
+
}
|
|
284
|
+
if (successor.provisioning_status === "ready") {
|
|
285
|
+
return { coupon: successor, revoked_coupon: predecessor };
|
|
286
|
+
}
|
|
287
|
+
if (!promotionId) {
|
|
288
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "The rotation successor requires a promotion binding.");
|
|
289
|
+
}
|
|
290
|
+
await this.assertPromotionAvailable(promotionId, successor.id, context);
|
|
291
|
+
const repairedSuccessor = (await this.store.updateAffiliateCoupons({
|
|
292
|
+
id: successor.id,
|
|
293
|
+
promotion_id: promotionId,
|
|
294
|
+
provisioning_status: "ready",
|
|
295
|
+
promotion_setup_error: null,
|
|
296
|
+
}, context));
|
|
297
|
+
return { coupon: repairedSuccessor, revoked_coupon: predecessor };
|
|
298
|
+
}
|
|
299
|
+
if (!(0, affiliate_coupon_policy_1.isAffiliateCouponRotationSource)(predecessor)) {
|
|
300
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The rotation predecessor is no longer enabled and ready.");
|
|
301
|
+
}
|
|
302
|
+
if (successor.lifecycle_status !== "enabled" ||
|
|
303
|
+
successor.provisioning_status === "ready") {
|
|
304
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The rotation successor is not pending finalization.");
|
|
305
|
+
}
|
|
306
|
+
if (!promotionId) {
|
|
307
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "The rotation successor requires a promotion binding.");
|
|
308
|
+
}
|
|
309
|
+
if (successor.promotion_id && successor.promotion_id !== promotionId) {
|
|
310
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "An affiliate coupon promotion binding cannot be replaced.");
|
|
311
|
+
}
|
|
312
|
+
await this.assertPromotionAvailable(promotionId, successor.id, context);
|
|
313
|
+
const updatedSuccessor = (await this.store.updateAffiliateCoupons({
|
|
314
|
+
id: successor.id,
|
|
315
|
+
promotion_id: promotionId,
|
|
316
|
+
provisioning_status: "ready",
|
|
317
|
+
promotion_setup_error: null,
|
|
318
|
+
}, context));
|
|
319
|
+
const updatedPredecessor = (await this.store.updateAffiliateCoupons({
|
|
320
|
+
id: predecessor.id,
|
|
321
|
+
lifecycle_status: "revoked",
|
|
322
|
+
provisioning_status: "pending",
|
|
323
|
+
promotion_setup_error: null,
|
|
324
|
+
revoked_at: input.revoked_at ?? new Date(),
|
|
325
|
+
revoked_by_type: input.revoked_by_type,
|
|
326
|
+
revoked_by_id: input.revoked_by_id ?? null,
|
|
327
|
+
}, context));
|
|
328
|
+
return {
|
|
329
|
+
coupon: updatedSuccessor,
|
|
330
|
+
revoked_coupon: updatedPredecessor,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
normalizeCode(code) {
|
|
334
|
+
try {
|
|
335
|
+
return (0, affiliate_coupon_policy_1.normalizeAffiliateCouponCode)(code);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, error instanceof Error ? error.message : "Invalid affiliate coupon code.");
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
async createPendingCoupon(input, context) {
|
|
342
|
+
return await this.store.createAffiliateCoupons({
|
|
343
|
+
...input,
|
|
344
|
+
lifecycle_status: "enabled",
|
|
345
|
+
provisioning_status: "pending",
|
|
346
|
+
promotion_id: null,
|
|
347
|
+
promotion_setup_error: null,
|
|
348
|
+
revoked_at: null,
|
|
349
|
+
revoked_by_type: null,
|
|
350
|
+
revoked_by_id: null,
|
|
351
|
+
}, context);
|
|
352
|
+
}
|
|
353
|
+
async assertAffiliateExists(affiliateId, context) {
|
|
354
|
+
await this.store.flush(context);
|
|
355
|
+
const [affiliate] = await this.store.listAffiliates({ id: affiliateId }, { take: 1 }, context);
|
|
356
|
+
if (!affiliate) {
|
|
357
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Affiliate was not found.");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async listAllAffiliateCoupons(affiliateId, context) {
|
|
361
|
+
return await this.store.listAffiliateCoupons({ affiliate_id: affiliateId }, { order: { created_at: "ASC" } }, context);
|
|
362
|
+
}
|
|
363
|
+
assertCouponSlotAvailable(coupons) {
|
|
364
|
+
if ((0, affiliate_coupon_policy_1.countLogicalEnabledAffiliateCouponSlots)(coupons) >=
|
|
365
|
+
affiliate_coupon_policy_1.MAX_ENABLED_COUPONS_PER_AFFILIATE) {
|
|
366
|
+
throw this.couponLimitReached();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
couponLimitReached() {
|
|
370
|
+
return new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "coupon_limit_reached");
|
|
371
|
+
}
|
|
372
|
+
generateCodeCandidates() {
|
|
373
|
+
return Array.from({ length: affiliate_coupon_policy_1.AFFILIATE_COUPON_CODE_GENERATION_MAX_ATTEMPTS }, () => this.normalizeCode(this.generateCode()));
|
|
374
|
+
}
|
|
375
|
+
async lockCodeCandidates(candidates, context) {
|
|
376
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
377
|
+
await this.lockKeys(uniqueCandidates.map((code) => `affiliate-coupon-code:${code}`), context);
|
|
378
|
+
}
|
|
379
|
+
async selectAvailableGeneratedCode(candidates, context) {
|
|
380
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
381
|
+
const existing = await this.store.listAffiliateCoupons({ code: uniqueCandidates }, undefined, context);
|
|
382
|
+
const unavailable = new Set(existing.map(({ code }) => code));
|
|
383
|
+
const available = candidates.find((code) => !unavailable.has(code));
|
|
384
|
+
if (!available) {
|
|
385
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "No unique affiliate coupon code could be generated.");
|
|
386
|
+
}
|
|
387
|
+
return available;
|
|
388
|
+
}
|
|
389
|
+
async assertPromotionAvailable(promotionId, couponId, context) {
|
|
390
|
+
const bindings = await this.store.listAffiliateCoupons({ promotion_id: promotionId }, undefined, context);
|
|
391
|
+
if (bindings.some(({ id }) => id !== couponId)) {
|
|
392
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "Affiliate coupon promotion is already bound.");
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async assertCodeAvailable(code, context) {
|
|
396
|
+
const [existing] = await this.store.listAffiliateCoupons({ code }, { take: 1 }, context);
|
|
397
|
+
if (existing) {
|
|
398
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "Affiliate coupon code is unavailable.");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
async findByIdempotencyKey(idempotencyKey, context) {
|
|
402
|
+
return await this.findCouponByCreationIdempotencyKey(idempotencyKey, undefined, context);
|
|
403
|
+
}
|
|
404
|
+
assertIdempotentIntent(coupon, input) {
|
|
405
|
+
if (coupon.affiliate_id !== input.affiliate_id ||
|
|
406
|
+
coupon.origin !== input.origin ||
|
|
407
|
+
coupon.creation_idempotency_key !== input.creation_idempotency_key ||
|
|
408
|
+
coupon.replaces_coupon_id !== input.replaces_coupon_id ||
|
|
409
|
+
coupon.created_by_type !== input.created_by_type ||
|
|
410
|
+
coupon.created_by_id !== input.created_by_id ||
|
|
411
|
+
(input.code !== undefined && coupon.code !== input.code)) {
|
|
412
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.CONFLICT, "The affiliate coupon idempotency key was already used for a different intent.");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
requireIdempotencyKey(value) {
|
|
416
|
+
const key = value?.trim();
|
|
417
|
+
if (!key) {
|
|
418
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Affiliate coupon creation requires an idempotency key.");
|
|
419
|
+
}
|
|
420
|
+
return key;
|
|
421
|
+
}
|
|
422
|
+
async lockAffiliateAndKeys(affiliateId, keys, context) {
|
|
423
|
+
await this.lockKeys([`affiliate-state:${affiliateId}`], context);
|
|
424
|
+
await this.lockKeys(keys, context);
|
|
425
|
+
}
|
|
426
|
+
async lockKeys(keys, context) {
|
|
427
|
+
const uniqueKeys = [...new Set(keys)].sort();
|
|
428
|
+
if (!uniqueKeys.length) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const manager = context.transactionManager;
|
|
432
|
+
if (!manager) {
|
|
433
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.UNEXPECTED_STATE, "Affiliate coupon changes require an Affiliate transaction.");
|
|
434
|
+
}
|
|
435
|
+
for (const key of uniqueKeys) {
|
|
436
|
+
await manager.execute("select pg_advisory_xact_lock(hashtext(?))", [key], "all");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
exports.AffiliateCouponManager = AffiliateCouponManager;
|
|
441
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLWNvdXBvbi1tYW5hZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvYWZmaWxpYXRlL3NlcnZpY2VzL2FmZmlsaWF0ZS1jb3Vwb24tbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxxREFBdUQ7QUFPdkQsK0VBTzBDO0FBdUIxQyxNQUFhLHNCQUFzQjtJQUNqQyxZQUNtQixLQUEyQixFQUMzQixlQUE2QixxREFBMkI7UUFEeEQsVUFBSyxHQUFMLEtBQUssQ0FBc0I7UUFDM0IsaUJBQVksR0FBWixZQUFZLENBQTRDO0lBQ3hFLENBQUM7SUFFSixLQUFLLENBQUMsY0FBYyxDQUNsQixRQUFnQixFQUNoQixXQUFvQixFQUNwQixPQUFpQjtRQUVqQixNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBQy9CLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsb0JBQW9CLENBQ3BEO1lBQ0UsRUFBRSxFQUFFLFFBQVE7WUFDWixHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ3RELEVBQ0QsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFFLEVBQ1gsT0FBTyxDQUNSLENBQUE7UUFFRCxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDWixNQUFNLElBQUksbUJBQVcsQ0FDbkIsbUJBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUMzQixpQ0FBaUMsQ0FDbEMsQ0FBQTtRQUNILENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQTtJQUNmLENBQUM7SUFFRCxLQUFLLENBQUMsZ0JBQWdCLENBQ3BCLElBQVksRUFDWixPQUFpQjtRQUVqQixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsYUFBYSxD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
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AffiliateDiscountService = void 0;
|
|
4
|
+
const money_1 = require("../../../utils/money");
|
|
5
|
+
class AffiliateDiscountService {
|
|
6
|
+
constructor(store) {
|
|
7
|
+
this.store = store;
|
|
8
|
+
}
|
|
9
|
+
async getOwnOrderDiscount(input) {
|
|
10
|
+
const affiliates = await this.store.listAffiliates({
|
|
11
|
+
customer_id: input.customer_id,
|
|
12
|
+
status: "active",
|
|
13
|
+
});
|
|
14
|
+
const affiliate = affiliates[0] ?? null;
|
|
15
|
+
if (!affiliate || affiliate.own_order_discount_rate <= 0) {
|
|
16
|
+
return {
|
|
17
|
+
applicable: false,
|
|
18
|
+
affiliate,
|
|
19
|
+
discount_rate: 0,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
applicable: true,
|
|
24
|
+
affiliate,
|
|
25
|
+
discount_rate: affiliate.own_order_discount_rate,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async calculateGuaranteedOwnOrderDiscount(input) {
|
|
29
|
+
const discount = await this.getOwnOrderDiscount({
|
|
30
|
+
customer_id: input.customer_id,
|
|
31
|
+
});
|
|
32
|
+
if (!discount.applicable) {
|
|
33
|
+
return {
|
|
34
|
+
applicable: false,
|
|
35
|
+
discount_rate: 0,
|
|
36
|
+
eligible_base_amount_net: 0,
|
|
37
|
+
discount_amount_net: 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const decimals = (0, money_1.getCurrencyDecimals)(input.currency_code);
|
|
41
|
+
const eligibleBase = money_1.Money.sum(input.items
|
|
42
|
+
.filter((item) => item.eligible && !item.is_giftcard)
|
|
43
|
+
.map((item) => money_1.Money.of(item.discounted_net_subtotal).max(money_1.Money.zero())));
|
|
44
|
+
return {
|
|
45
|
+
applicable: true,
|
|
46
|
+
discount_rate: discount.discount_rate,
|
|
47
|
+
eligible_base_amount_net: eligibleBase.toRoundedNumber(decimals),
|
|
48
|
+
discount_amount_net: eligibleBase
|
|
49
|
+
.applyPercentageRate(discount.discount_rate)
|
|
50
|
+
.toRoundedNumber(decimals),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.AffiliateDiscountService = AffiliateDiscountService;
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWZmaWxpYXRlLWRpc2NvdW50LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbW9kdWxlcy9hZmZpbGlhdGUvc2VydmljZXMvYWZmaWxpYXRlLWRpc2NvdW50LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBT0EsZ0RBQWlFO0FBRWpFLE1BQWEsd0JBQXdCO0lBQ25DLFlBQTZCLEtBQTJCO1FBQTNCLFVBQUssR0FBTCxLQUFLLENBQXNCO0lBQUcsQ0FBQztJQUU1RCxLQUFLLENBQUMsbUJBQW1CLENBQ3ZCLEtBQTRCO1FBRTVCLE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUM7WUFDakQsV0FBVyxFQUFFLEtBQUssQ0FBQyxXQUFXO1lBQzlCLE1BQU0sRUFBRSxRQUFRO1NBQ2pCLENBQUMsQ0FBQTtRQUNGLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUE7UUFFdkMsSUFBSSxDQUFDLFNBQVMsSUFBSSxTQUFTLENBQUMsdUJBQXVCLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDekQsT0FBTztnQkFDTCxVQUFVLEVBQUUsS0FBSztnQkFDakIsU0FBUztnQkFDVCxhQUFhLEVBQUUsQ0FBQzthQUNqQixDQUFBO1FBQ0gsQ0FBQztRQUVELE9BQU87WUFDTCxVQUFVLEVBQUUsSUFBSTtZQUNoQixTQUFTO1lBQ1QsYUFBYSxFQUFFLFNBQVMsQ0FBQyx1QkFBdUI7U0FDakQsQ0FBQTtJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsbUNBQW1DLENBQ3ZDLEtBQXNDO1FBRXRDLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDO1lBQzlDLFdBQVcsRUFBRSxLQUFLLENBQUMsV0FBVztTQUMvQixDQUFDLENBQUE7UUFFRixJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3pCLE9BQU87Z0JBQ0wsVUFBVSxFQUFFLEtBQUs7Z0JBQ2pCLGFBQWEsRUFBRSxDQUFDO2dCQUNoQix3QkFBd0IsRUFBRSxDQUFDO2dCQUMzQixtQkFBbUIsRUFBRSxDQUFDO2FBQ3ZCLENBQUE7UUFDSCxDQUFDO1FBRUQsTUFBTSxRQUFRLEdBQUcsSUFBQSwyQkFBbUIsRUFBQyxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUE7UUFDekQsTUFBTSxZQUFZLEdBQUcsYUFBSyxDQUFDLEdBQUcsQ0FDNUIsS0FBSyxDQUFDLEtBQUs7YUFDUixNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDO2FBQ3BELEdBQUcsQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsYUFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxHQUFHLENBQUMsYUFBSyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FDM0UsQ0FBQTtRQUVELE9BQU87WUFDTCxVQUFVLEVBQUUsSUFBSTtZQUNoQixhQUFhLEVBQUUsUUFBUSxDQUFDLGFBQWE7WUFDckMsd0JBQXdCLEVBQUUsWUFBWSxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUM7WUFDaEUsbUJBQW1CLEVBQUUsWUFBWTtpQkFDOUIsbUJBQW1CLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBQztpQkFDM0MsZUFBZSxDQUFDLFFBQVEsQ0FBQztTQUM3QixDQUFBO0lBQ0gsQ0FBQztDQUNGO0FBM0RELDREQTJEQyJ9
|