@devx-commerce/plugin-discounts-svaraa 0.0.2-beta.2 → 0.0.2-beta.21
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/api/admin/discounts/validators.js +16 -1
- package/.medusa/server/src/api/middlewares.js +4 -1
- package/.medusa/server/src/api/pos/discounts/apply/route.js +151 -0
- package/.medusa/server/src/api/pos/discounts/apply-freebie/route.js +20 -0
- package/.medusa/server/src/api/pos/discounts/auto-apply/route.js +188 -0
- package/.medusa/server/src/api/pos/discounts/cart-eligible-coupons/[cart_id]/route.js +39 -0
- package/.medusa/server/src/api/pos/discounts/coupons/[id]/route.js +25 -0
- package/.medusa/server/src/api/pos/discounts/middlewares.js +99 -0
- package/.medusa/server/src/api/pos/discounts/offers-on-product/[variant_id]/route.js +19 -0
- package/.medusa/server/src/api/pos/discounts/price-rules/route.js +89 -0
- package/.medusa/server/src/api/pos/discounts/price-rules-list/route.js +54 -0
- package/.medusa/server/src/api/pos/discounts/remove/route.js +41 -0
- package/.medusa/server/src/api/pos/discounts/remove-store-discount/route.js +54 -0
- package/.medusa/server/src/api/pos/discounts/validators.js +358 -0
- package/.medusa/server/src/api/pos/discounts/verify-store-discount/route.js +394 -0
- package/.medusa/server/src/api/store/discounts/apply/route.js +126 -8
- package/.medusa/server/src/api/store/discounts/auto-apply/route.js +143 -80
- package/.medusa/server/src/api/store/discounts/cart-eligible-coupons/[cart_id]/route.js +23 -3
- package/.medusa/server/src/api/store/discounts/price-rules/route.js +65 -2
- package/.medusa/server/src/api/store/discounts/remove/route.js +23 -3
- package/.medusa/server/src/index.js +2 -1
- package/.medusa/server/src/lib/constant.js +126 -0
- package/.medusa/server/src/lib/helper.js +68 -0
- package/.medusa/server/src/lib/redis-utils.js +33 -0
- package/.medusa/server/src/lib/send-otp-helper.js +37 -0
- package/.medusa/server/src/lib/utils.js +11 -2
- package/.medusa/server/src/modules/discounts/migrations/Migration20250822052209.js +14 -0
- package/.medusa/server/src/modules/discounts/migrations/Migration20250825073038.js +14 -0
- package/.medusa/server/src/modules/discounts/migrations/Migration20250901083847.js +14 -0
- package/.medusa/server/src/modules/discounts/migrations/Migration20250918095658.js +14 -0
- package/.medusa/server/src/modules/discounts/models/price-rule.js +13 -1
- package/.medusa/server/src/modules/discounts/service.js +9 -7
- package/.medusa/server/src/subscribers/index.js +18 -0
- package/.medusa/server/src/subscribers/promocode-subscriber.js +77 -0
- package/.medusa/server/src/workflows/discount/add-more-code-in-price-rule/index.js +3 -3
- package/.medusa/server/src/workflows/discount/apply-coupon/index.js +36 -26
- package/.medusa/server/src/workflows/discount/apply-coupon/steps/apply-coupon-step.js +25 -187
- package/.medusa/server/src/workflows/discount/apply-coupon/steps/generate-adjustment-line-item.js +27 -9
- package/.medusa/server/src/workflows/discount/apply-coupon/steps/validate-coupon-step.js +3 -5
- package/.medusa/server/src/workflows/discount/auto-apply-coupon/steps/auto-apply-coupon.js +157 -87
- package/.medusa/server/src/workflows/discount/calculate-coupon-discount/index.js +36 -32
- package/.medusa/server/src/workflows/discount/calculate-coupon-discount/step/fetch-price-rule-by-code.js +36 -0
- package/.medusa/server/src/workflows/discount/create-discount-price-rule/steps/create-discount-price-rule-step.js +2 -2
- package/.medusa/server/src/workflows/discount/create-subscriber-to-discount-price-rule.js +10 -0
- package/.medusa/server/src/workflows/discount/get-cart-eligible-coupons/index.js +2 -1
- package/.medusa/server/src/workflows/discount/get-cart-eligible-coupons/step/get-cart-eligible-coupon.js +33 -10
- package/.medusa/server/src/workflows/discount/index.js +5 -1
- package/.medusa/server/src/workflows/discount/remove-coupon/index.js +22 -2
- package/.medusa/server/src/workflows/discount/remove-coupon/step/remove-coupon-step.js +45 -26
- package/.medusa/server/src/workflows/discount/rounded-cart-total/index.js +13 -0
- package/.medusa/server/src/workflows/discount/steps/rounded-cart-total.js +168 -0
- package/.medusa/server/src/workflows/discount/update-coupon-code-stats/index.js +12 -0
- package/.medusa/server/src/workflows/discount/update-coupon-code-stats/step/update-coupon-code-stats.js +52 -0
- package/.medusa/server/src/workflows/discount/update-discount-price-rule/index.js +1 -3
- package/package.json +4 -2
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const api_response_1 = require("../../../../lib/api-response");
|
|
6
|
+
const framework_1 = require("@medusajs/framework");
|
|
7
|
+
const send_otp_helper_1 = require("../../../../lib/send-otp-helper");
|
|
8
|
+
const POST = async (req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
const { cart_id, amount, otp } = req.validatedBody;
|
|
11
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] START - cart_id: ${cart_id}, amount: ${amount}, otp: ${otp ? 'provided' : 'not provided'}`);
|
|
12
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
13
|
+
const cartService = req.scope.resolve("cart");
|
|
14
|
+
// Resolve staff mobile and store manager from authenticated user's profile
|
|
15
|
+
const userId = req.auth_context?.actor_id;
|
|
16
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] UserId: ${userId}`);
|
|
17
|
+
let storeManagerMobile = "";
|
|
18
|
+
let storeManagerName = "";
|
|
19
|
+
let storeManagerId = "";
|
|
20
|
+
let allowedDiscountAmount = null;
|
|
21
|
+
let isAllowed = true;
|
|
22
|
+
const assignedStores = [];
|
|
23
|
+
if (userId) {
|
|
24
|
+
try {
|
|
25
|
+
const remoteQuery = req.scope.resolve(utils_1.ContainerRegistrationKeys.REMOTE_QUERY);
|
|
26
|
+
const query = (0, utils_1.remoteQueryObjectFromString)({
|
|
27
|
+
entryPoint: "user",
|
|
28
|
+
variables: { id: userId },
|
|
29
|
+
fields: [
|
|
30
|
+
"id",
|
|
31
|
+
"email",
|
|
32
|
+
"first_name",
|
|
33
|
+
"last_name",
|
|
34
|
+
"user_profile.phone_number",
|
|
35
|
+
"user_profile.allowed_discount_amount",
|
|
36
|
+
"user_profile.store_ids",
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
const [user] = await remoteQuery(query);
|
|
40
|
+
const profile = user?.user_profile;
|
|
41
|
+
const allowed_discount = profile?.allowed_discount_amount;
|
|
42
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Staff user retrieved - email: ${user?.email}`);
|
|
43
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Staff profile phone: ${profile?.phone_number}`);
|
|
44
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Allowed discount amount: ${allowed_discount}`);
|
|
45
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Assigned stores: ${JSON.stringify(profile?.store_ids)}`);
|
|
46
|
+
if (allowed_discount != null) {
|
|
47
|
+
const parsed = Number(allowed_discount);
|
|
48
|
+
if (!Number.isNaN(parsed)) {
|
|
49
|
+
allowedDiscountAmount = parsed;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (profile?.store_ids && Array.isArray(profile.store_ids)) {
|
|
53
|
+
assignedStores.push(...profile.store_ids);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Failed to resolve staff phone for user_id ${userId}: ${e?.message || e}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Allowed amount: ${allowedDiscountAmount}`);
|
|
61
|
+
if (allowedDiscountAmount != null) {
|
|
62
|
+
isAllowed = amount <= allowedDiscountAmount;
|
|
63
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Discount check - amount: ${amount}, allowed limit: ${allowedDiscountAmount}, isAllowed: ${isAllowed}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] No allowed discount limit set for staff, allowing discount: ${amount}`);
|
|
67
|
+
}
|
|
68
|
+
// Get store manager from first assigned store's metadata
|
|
69
|
+
if (assignedStores.length > 0) {
|
|
70
|
+
try {
|
|
71
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Fetching store manager for store: ${assignedStores[0]}`);
|
|
72
|
+
const remoteQuery = req.scope.resolve(utils_1.ContainerRegistrationKeys.REMOTE_QUERY);
|
|
73
|
+
const storeQuery = (0, utils_1.remoteQueryObjectFromString)({
|
|
74
|
+
entryPoint: "store",
|
|
75
|
+
variables: { id: assignedStores[0] },
|
|
76
|
+
fields: ["id", "name", "metadata"],
|
|
77
|
+
});
|
|
78
|
+
const [store] = await remoteQuery(storeQuery);
|
|
79
|
+
const metadata = store?.metadata;
|
|
80
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Store metadata: ${JSON.stringify(metadata)}`);
|
|
81
|
+
if (metadata?.store_manager_id) {
|
|
82
|
+
storeManagerId = metadata.store_manager_id;
|
|
83
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Store manager ID found: ${storeManagerId}`);
|
|
84
|
+
// Get store manager's phone number
|
|
85
|
+
try {
|
|
86
|
+
const managerQuery = (0, utils_1.remoteQueryObjectFromString)({
|
|
87
|
+
entryPoint: "user",
|
|
88
|
+
variables: { id: storeManagerId },
|
|
89
|
+
fields: [
|
|
90
|
+
"id",
|
|
91
|
+
"first_name",
|
|
92
|
+
"last_name",
|
|
93
|
+
"user_profile.phone_number",
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
const [managerUser] = await remoteQuery(managerQuery);
|
|
97
|
+
const managerProfile = managerUser?.user_profile;
|
|
98
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Store manager retrieved - name: ${managerUser?.first_name} ${managerUser?.last_name}, phone: ${managerProfile?.phone_number}`);
|
|
99
|
+
if (managerProfile?.phone_number) {
|
|
100
|
+
storeManagerMobile = String(managerProfile.phone_number).trim();
|
|
101
|
+
storeManagerName = [
|
|
102
|
+
managerUser.first_name,
|
|
103
|
+
managerUser.last_name,
|
|
104
|
+
]
|
|
105
|
+
.filter(Boolean)
|
|
106
|
+
.join(" ") || managerUser.id;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Failed to get store manager phone: ${e?.message || e}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
framework_1.logger.warn(`[VERIFY-STORE-DISCOUNT] No store_manager_id in store metadata`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Failed to get store metadata: ${e?.message || e}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
framework_1.logger.warn(`[VERIFY-STORE-DISCOUNT] No assigned stores found for staff`);
|
|
123
|
+
}
|
|
124
|
+
// Validate that store manager mobile is set
|
|
125
|
+
if (!storeManagerMobile) {
|
|
126
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Store manager mobile not set for discount verification`);
|
|
127
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Store manager mobile number not found for discount verification", 400));
|
|
128
|
+
}
|
|
129
|
+
const otpPhone = storeManagerMobile;
|
|
130
|
+
const otpRecipientName = storeManagerName || "Store Manager";
|
|
131
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] OTP will be sent to: ${otpPhone} (${otpRecipientName})`);
|
|
132
|
+
// If discount is above allowed limit and we don't have any mobile number,
|
|
133
|
+
// we cannot proceed with OTP-based approval.
|
|
134
|
+
if (!otpPhone && !isAllowed) {
|
|
135
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("No phone number found for OTP verification", 400));
|
|
136
|
+
}
|
|
137
|
+
// If within allowed limit, apply discount directly without OTP
|
|
138
|
+
if (isAllowed) {
|
|
139
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Discount is within allowed limit (amount: ${amount}, allowed: ${allowedDiscountAmount}), applying directly without OTP`);
|
|
140
|
+
const { data: carts } = await query.graph({
|
|
141
|
+
entity: "cart",
|
|
142
|
+
fields: ["*", "items.*"],
|
|
143
|
+
filters: { id: cart_id },
|
|
144
|
+
});
|
|
145
|
+
if (!carts?.length) {
|
|
146
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Cart not found: ${cart_id}`);
|
|
147
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Cart ${cart_id} not found`, 404));
|
|
148
|
+
}
|
|
149
|
+
const cart = carts[0];
|
|
150
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart retrieved - items: ${cart.items?.length || 0}, total: ${cart.total || 0}`);
|
|
151
|
+
if (!cart.items?.length) {
|
|
152
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] ❌ Cart is empty`);
|
|
153
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Cannot apply store discount to empty cart", 400));
|
|
154
|
+
}
|
|
155
|
+
// Validate discount amount doesn't exceed cart total
|
|
156
|
+
const cartTotal = Number(cart.total) || Number(cart.subtotal) || 0;
|
|
157
|
+
if (amount > cartTotal && cartTotal > 0) {
|
|
158
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Discount (${amount}) exceeds cart total (${cartTotal})`);
|
|
159
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Discount amount (${amount}) cannot exceed cart total (${cartTotal})`, 400));
|
|
160
|
+
}
|
|
161
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart total: ${cartTotal}, discount amount: ${amount}`);
|
|
162
|
+
const itemIds = cart.items.map((i) => i.id);
|
|
163
|
+
// Remove existing store-discount adjustments to keep it idempotent
|
|
164
|
+
const { data: existingAdjustments } = await query.graph({
|
|
165
|
+
entity: "line_item_adjustment",
|
|
166
|
+
fields: ["id"],
|
|
167
|
+
filters: {
|
|
168
|
+
item_id: { $in: itemIds },
|
|
169
|
+
code: "STORE_DISCOUNT",
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
if (existingAdjustments?.length) {
|
|
173
|
+
await cartService.deleteLineItemAdjustments(existingAdjustments.map((a) => a.id));
|
|
174
|
+
}
|
|
175
|
+
// Apply new store discount as a line-item adjustment on the first item
|
|
176
|
+
const targetItemId = itemIds[0];
|
|
177
|
+
await cartService.addLineItemAdjustments({
|
|
178
|
+
item_id: targetItemId,
|
|
179
|
+
code: "STORE_DISCOUNT",
|
|
180
|
+
amount,
|
|
181
|
+
});
|
|
182
|
+
// Update cart metadata to record store discount info
|
|
183
|
+
const currentCart = await cartService.retrieveCart(cart_id);
|
|
184
|
+
const prevMeta = currentCart.metadata || {};
|
|
185
|
+
const storeDiscountMeta = {
|
|
186
|
+
amount,
|
|
187
|
+
code: "STORE_DISCOUNT",
|
|
188
|
+
verified: false,
|
|
189
|
+
};
|
|
190
|
+
await cartService.updateCarts(cart_id, {
|
|
191
|
+
metadata: {
|
|
192
|
+
...prevMeta,
|
|
193
|
+
store_discount: storeDiscountMeta,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
// Return only success message - frontend will fetch cart separately
|
|
197
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
198
|
+
storeDiscount: storeDiscountMeta,
|
|
199
|
+
}, "Store discount applied"));
|
|
200
|
+
}
|
|
201
|
+
// If OTP is not provided and discount is above allowed limit,
|
|
202
|
+
// send a new OTP to store manager/staff mobile for this discount.
|
|
203
|
+
if (!otp && !isAllowed) {
|
|
204
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Discount above allowed limit, sending OTP to ${otpRecipientName}`);
|
|
205
|
+
try {
|
|
206
|
+
// Initialize options from medusa-config.ts (same as lines 175-198)
|
|
207
|
+
const passwordlessOptions = {
|
|
208
|
+
smsRateLimitMinutes: 0.5,
|
|
209
|
+
blockDurationMinutes: 5,
|
|
210
|
+
codeExpiryMinutes: 5,
|
|
211
|
+
maxAttempts: 5,
|
|
212
|
+
smsProviders: [
|
|
213
|
+
{ provider: "gupshup", priority: 2 },
|
|
214
|
+
{ provider: "sns", priority: 1 },
|
|
215
|
+
],
|
|
216
|
+
// Gupshup Configuration
|
|
217
|
+
gupshupOptions: {
|
|
218
|
+
template: "Your verification code is {passCode}",
|
|
219
|
+
accountId: process.env.GUPSHUP_ACCOUNT_ID || "",
|
|
220
|
+
accountPassword: process.env.GUPSHUP_PASSWORD || "",
|
|
221
|
+
},
|
|
222
|
+
// AWS SNS Configuration
|
|
223
|
+
snsOptions: {
|
|
224
|
+
template: "Your verification code is {passCode}. This code will expire in 5 minutes. Do not share this code with anyone.",
|
|
225
|
+
region: process.env.AWS_REGION || "",
|
|
226
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID || "",
|
|
227
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "",
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
// Generate OTP (using same code length as config)
|
|
231
|
+
const codeLength = passwordlessOptions.codeLength || 4;
|
|
232
|
+
const CODE_MIN = Math.pow(10, codeLength - 1);
|
|
233
|
+
const CODE_MAX = Math.pow(10, codeLength) - 1;
|
|
234
|
+
const generatedOtp = Math.floor(Math.random() * (CODE_MAX - CODE_MIN + 1) + CODE_MIN).toString();
|
|
235
|
+
// const generatedOtp = "1234";
|
|
236
|
+
// Send OTP via SNS using options from config
|
|
237
|
+
// Use store manager's phone if available, otherwise staff phone
|
|
238
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Sending OTP to ${otpPhone} for cart ${cart_id}`);
|
|
239
|
+
const smsSent = await (0, send_otp_helper_1.sendOtpViaSNS)(otpPhone, generatedOtp, passwordlessOptions, framework_1.logger);
|
|
240
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] OTP sent result: ${smsSent}, OTP: ${generatedOtp}`);
|
|
241
|
+
if (!smsSent) {
|
|
242
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Failed to send OTP via SNS`);
|
|
243
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Failed to send OTP for store discount", 500));
|
|
244
|
+
}
|
|
245
|
+
// Store OTP in cart metadata
|
|
246
|
+
const cartService = req.scope.resolve("cart");
|
|
247
|
+
// Get cart for validation
|
|
248
|
+
const { data: cartData } = await query.graph({
|
|
249
|
+
entity: "cart",
|
|
250
|
+
fields: ["total", "subtotal"],
|
|
251
|
+
filters: { id: cart_id },
|
|
252
|
+
});
|
|
253
|
+
if (!cartData?.length) {
|
|
254
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Cart not found: ${cart_id}`);
|
|
255
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Cart ${cart_id} not found`, 404));
|
|
256
|
+
}
|
|
257
|
+
const cartTotal = Number(cartData[0].total) || Number(cartData[0].subtotal) || 0;
|
|
258
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart total: ${cartTotal}, discount amount: ${amount}`);
|
|
259
|
+
if (amount > cartTotal && cartTotal > 0) {
|
|
260
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Discount (${amount}) exceeds cart total (${cartTotal})`);
|
|
261
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Discount amount (${amount}) cannot exceed cart total (${cartTotal})`, 400));
|
|
262
|
+
}
|
|
263
|
+
const currentCart = await cartService.retrieveCart(cart_id);
|
|
264
|
+
const prevMeta = currentCart.metadata || {};
|
|
265
|
+
// Store OTP in pending_store_discount (NOT store_discount) until verified
|
|
266
|
+
const pendingStoreDiscountMeta = {
|
|
267
|
+
amount,
|
|
268
|
+
otp: generatedOtp,
|
|
269
|
+
};
|
|
270
|
+
// Remove any existing store_discount metadata and store only in pending
|
|
271
|
+
const { store_discount, ...restMeta } = prevMeta;
|
|
272
|
+
await cartService.updateCarts(cart_id, {
|
|
273
|
+
metadata: {
|
|
274
|
+
...restMeta,
|
|
275
|
+
pending_store_discount: pendingStoreDiscountMeta,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] OTP stored in cart metadata for pending verification`);
|
|
279
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
280
|
+
cart_id,
|
|
281
|
+
amount,
|
|
282
|
+
otp_sent_to: otpPhone,
|
|
283
|
+
otp_recipient_name: otpRecipientName,
|
|
284
|
+
store_manager_mobile: storeManagerMobile,
|
|
285
|
+
}, `OTP sent to ${otpRecipientName} for store discount verification`));
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
framework_1.logger.error("Failed to send OTP for store discount", e);
|
|
289
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(e?.message || "Failed to send OTP for store discount", 500));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// If OTP is provided, verify it from cart metadata
|
|
293
|
+
try {
|
|
294
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Verifying OTP for cart ${cart_id}`);
|
|
295
|
+
const cartService = req.scope.resolve("cart");
|
|
296
|
+
// Retrieve cart to get metadata
|
|
297
|
+
const currentCart = await cartService.retrieveCart(cart_id);
|
|
298
|
+
const metadata = currentCart.metadata || {};
|
|
299
|
+
const pendingStoreDiscountMeta = (metadata.pending_store_discount || {});
|
|
300
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Pending discount meta: ${JSON.stringify(pendingStoreDiscountMeta)}`);
|
|
301
|
+
// Verify OTP from cart metadata
|
|
302
|
+
if (!pendingStoreDiscountMeta.otp) {
|
|
303
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] No OTP found in cart metadata`);
|
|
304
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("No OTP found in cart metadata", 400));
|
|
305
|
+
}
|
|
306
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Comparing OTP - provided: ${otp}, stored: ${pendingStoreDiscountMeta.otp}`);
|
|
307
|
+
if (pendingStoreDiscountMeta.otp !== otp) {
|
|
308
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Invalid OTP provided`);
|
|
309
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Invalid OTP. Please enter the correct OTP sent to your phone", 400));
|
|
310
|
+
}
|
|
311
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] ✅ OTP verified successfully`);
|
|
312
|
+
// OTP verified – now fetch cart with items using query.graph
|
|
313
|
+
const { data: carts } = await query.graph({
|
|
314
|
+
entity: "cart",
|
|
315
|
+
fields: ["*", "items.*"],
|
|
316
|
+
filters: { id: cart_id },
|
|
317
|
+
});
|
|
318
|
+
if (!carts?.length) {
|
|
319
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Cart not found: ${cart_id}`);
|
|
320
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Cart ${cart_id} not found`, 404));
|
|
321
|
+
}
|
|
322
|
+
const cart = carts[0];
|
|
323
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart retrieved - items count: ${cart.items?.length || 0}`);
|
|
324
|
+
if (!cart.items?.length) {
|
|
325
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] ❌ Cart is empty - cannot apply discount`);
|
|
326
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Cannot apply store discount to empty cart", 400));
|
|
327
|
+
}
|
|
328
|
+
// Validate discount amount doesn't exceed cart total
|
|
329
|
+
// Use subtotal if total is undefined
|
|
330
|
+
const cartTotal = Number(cart.total) || Number(cart.subtotal) || 0;
|
|
331
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart total: ${cartTotal}, discount amount: ${amount}`);
|
|
332
|
+
if (amount > cartTotal && cartTotal > 0) {
|
|
333
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] Discount (${amount}) exceeds cart total (${cartTotal})`);
|
|
334
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Discount amount (${amount}) cannot exceed cart total (${cartTotal})`, 400));
|
|
335
|
+
}
|
|
336
|
+
if (cartTotal === 0) {
|
|
337
|
+
framework_1.logger.warn(`[VERIFY-STORE-DISCOUNT] Cart total is 0, skipping validation`);
|
|
338
|
+
}
|
|
339
|
+
const itemIds = cart.items.map((i) => i.id);
|
|
340
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Cart has ${itemIds.length} items`);
|
|
341
|
+
// Remove existing store-discount adjustments to keep it idempotent
|
|
342
|
+
const { data: existingAdjustments } = await query.graph({
|
|
343
|
+
entity: "line_item_adjustment",
|
|
344
|
+
fields: ["id"],
|
|
345
|
+
filters: {
|
|
346
|
+
item_id: { $in: itemIds },
|
|
347
|
+
code: "STORE_DISCOUNT",
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
if (existingAdjustments?.length) {
|
|
351
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Removing ${existingAdjustments.length} existing store discount adjustments`);
|
|
352
|
+
await cartService.deleteLineItemAdjustments(existingAdjustments.map((a) => a.id));
|
|
353
|
+
}
|
|
354
|
+
// Apply new store discount as a line-item adjustment on the first item
|
|
355
|
+
const targetItemId = itemIds[0];
|
|
356
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] Applying store discount - amount: ${amount}, targetItemId: ${targetItemId}`);
|
|
357
|
+
await cartService.addLineItemAdjustments({
|
|
358
|
+
item_id: targetItemId,
|
|
359
|
+
code: "STORE_DISCOUNT",
|
|
360
|
+
amount,
|
|
361
|
+
});
|
|
362
|
+
// Update cart metadata to record store discount info
|
|
363
|
+
// Remove pending_store_discount and set verified store_discount
|
|
364
|
+
const currentMetadata = cart.metadata || {};
|
|
365
|
+
const { pending_store_discount, ...restMeta } = currentMetadata;
|
|
366
|
+
const verifiedStoreDiscountMeta = {
|
|
367
|
+
amount,
|
|
368
|
+
code: "STORE_DISCOUNT",
|
|
369
|
+
verified: true,
|
|
370
|
+
};
|
|
371
|
+
await cartService.updateCarts(cart_id, {
|
|
372
|
+
metadata: {
|
|
373
|
+
...restMeta,
|
|
374
|
+
store_discount: verifiedStoreDiscountMeta,
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
framework_1.logger.info(`[VERIFY-STORE-DISCOUNT] ✅ COMPLETE - Discount verified and applied | amount: ${amount}`);
|
|
378
|
+
// Return only success message - frontend will fetch cart separately
|
|
379
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
380
|
+
storeDiscount: verifiedStoreDiscountMeta,
|
|
381
|
+
}, "Store discount OTP verified and discount applied"));
|
|
382
|
+
}
|
|
383
|
+
catch (e) {
|
|
384
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] ❌ Error: ${e?.message || e}`);
|
|
385
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(e?.message || "Failed to verify store discount OTP", 500));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
framework_1.logger.error(`[VERIFY-STORE-DISCOUNT] ERROR - ${error?.message || error}`, error);
|
|
390
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(error?.message || "Internal server error", 500));
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
exports.POST = POST;
|
|
394
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3Bvcy9kaXNjb3VudHMvdmVyaWZ5LXN0b3JlLWRpc2NvdW50L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUlBLHFEQUltQztBQUNuQywrREFJc0M7QUFFdEMsbURBQTZDO0FBQzdDLHFFQUFnRTtBQUV6RCxNQUFNLElBQUksR0FBRyxLQUFLLEVBQ3ZCLEdBQWdFLEVBQ2hFLEdBQW1CLEVBQ25CLEVBQUU7SUFDRixJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxHQUFHLENBQUMsYUFBYSxDQUFDO1FBRW5ELGtCQUFNLENBQUMsSUFBSSxDQUFDLDRDQUE0QyxPQUFPLGFBQWEsTUFBTSxVQUFVLEdBQUcsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1FBRWpJLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTlDLDJFQUEyRTtRQUMzRSxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsWUFBWSxFQUFFLFFBQThCLENBQUM7UUFFaEUsa0JBQU0sQ0FBQyxJQUFJLENBQUMsbUNBQW1DLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFFekQsSUFBSSxrQkFBa0IsR0FBRyxFQUFFLENBQUM7UUFDNUIsSUFBSSxnQkFBZ0IsR0FBRyxFQUFFLENBQUM7UUFDMUIsSUFBSSxjQUFjLEdBQUcsRUFBRSxDQUFDO1FBQ3hCLElBQUkscUJBQXFCLEdBQWtCLElBQUksQ0FBQztRQUNoRCxJQUFJLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFD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
|
|
@@ -3,30 +3,148 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.POST = POST;
|
|
4
4
|
const api_response_1 = require("../../../../lib/api-response");
|
|
5
5
|
const apply_coupon_1 = require("../../../../workflows/discount/apply-coupon");
|
|
6
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
7
|
+
const utils_2 = require("../../../../lib/utils");
|
|
6
8
|
async function POST(req, res) {
|
|
7
9
|
try {
|
|
10
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
11
|
+
const userId = req.auth_context?.actor_id || req.auth_context;
|
|
12
|
+
const { data: carts } = await query.graph({
|
|
13
|
+
entity: "cart",
|
|
14
|
+
fields: [
|
|
15
|
+
"*",
|
|
16
|
+
"items.*",
|
|
17
|
+
"items.variant.*",
|
|
18
|
+
"items.variant.prices.*",
|
|
19
|
+
"items.variant.extended_variant.mrp",
|
|
20
|
+
"promotions.*",
|
|
21
|
+
],
|
|
22
|
+
filters: { id: req.validatedBody.cart_id },
|
|
23
|
+
});
|
|
24
|
+
if (!carts.length) {
|
|
25
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(`Cart ${req.validatedBody.cart_id} not found`, 404));
|
|
26
|
+
}
|
|
27
|
+
const { data: discount_codes } = await query.graph({
|
|
28
|
+
entity: "discount_code",
|
|
29
|
+
fields: [
|
|
30
|
+
"*",
|
|
31
|
+
],
|
|
32
|
+
filters: { code: req.validatedBody.code },
|
|
33
|
+
});
|
|
34
|
+
if (!discount_codes.length) {
|
|
35
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
36
|
+
discountResult: {
|
|
37
|
+
isApplied: false,
|
|
38
|
+
discount: 0,
|
|
39
|
+
message: "This coupon code is invalid or does not exist."
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
if ((req.validatedBody.code.toLowerCase().includes("birth") || req.validatedBody.code.toLowerCase().includes("anni"))
|
|
44
|
+
&& !userId) {
|
|
45
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
46
|
+
discountResult: {
|
|
47
|
+
isApplied: false,
|
|
48
|
+
discount: 0,
|
|
49
|
+
message: "This coupon is valid only for registered users. Please log in to apply it."
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
if (userId && (req.validatedBody.code.toLowerCase().includes("birth") || req.validatedBody.code.toLowerCase().includes("anni"))) {
|
|
54
|
+
const { data: customer } = await query.graph({
|
|
55
|
+
entity: "customer",
|
|
56
|
+
fields: ["metadata", "extended_customer.id"],
|
|
57
|
+
filters: { id: userId },
|
|
58
|
+
});
|
|
59
|
+
const externalId = customer?.[0]?.extended_customer?.id;
|
|
60
|
+
if (externalId) {
|
|
61
|
+
const { data: ext } = await query.graph({
|
|
62
|
+
entity: "extended_customer",
|
|
63
|
+
fields: ["birth_date", "anniversary_date"],
|
|
64
|
+
filters: { id: externalId },
|
|
65
|
+
});
|
|
66
|
+
const todayIST = (0, utils_2.getISTMonthDay)(new Date());
|
|
67
|
+
const bday = ext?.[0]?.birth_date;
|
|
68
|
+
const anniv = ext?.[0]?.anniversary_date;
|
|
69
|
+
const isBirthday = bday && (0, utils_2.getISTMonthDay)(bday) === todayIST;
|
|
70
|
+
const isAnniversary = anniv && (0, utils_2.getISTMonthDay)(anniv) === todayIST;
|
|
71
|
+
if ((!isBirthday && req.validatedBody.code.toLowerCase().includes("birth")) || (!isAnniversary && req.validatedBody.code.toLowerCase().includes("anni"))) {
|
|
72
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
73
|
+
discountResult: {
|
|
74
|
+
isApplied: false,
|
|
75
|
+
discount: 0,
|
|
76
|
+
message: "This coupon can only be applied on your birthday or anniversary."
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const today = new Date();
|
|
82
|
+
const currentYear = today.getFullYear();
|
|
83
|
+
const startOfYear = new Date(currentYear, 0, 1); // Jan 1
|
|
84
|
+
const endOfYear = new Date(currentYear, 11, 31, 23, 59, 59); // Dec 31
|
|
85
|
+
const { data: orders } = await query.graph({
|
|
86
|
+
entity: "order",
|
|
87
|
+
fields: ["*", "metadata"],
|
|
88
|
+
filters: {
|
|
89
|
+
customer_id: userId,
|
|
90
|
+
created_at: {
|
|
91
|
+
$gte: startOfYear.toISOString(),
|
|
92
|
+
$lte: endOfYear.toISOString(),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const matchingOrders = orders?.filter((order) => {
|
|
97
|
+
if (req.validatedBody.code?.toLowerCase()?.includes("birth")) {
|
|
98
|
+
return order?.metadata?.code?.toLowerCase()?.includes("birth") ||
|
|
99
|
+
order?.metadata?.discount?.code?.toLowerCase()?.includes("birth");
|
|
100
|
+
}
|
|
101
|
+
if (req.validatedBody.code?.toLowerCase()?.includes("anni")) {
|
|
102
|
+
return order?.metadata?.code?.toLowerCase()?.includes("anni") ||
|
|
103
|
+
order?.metadata?.discount?.code?.toLowerCase()?.includes("anni");
|
|
104
|
+
}
|
|
105
|
+
return order?.metadata?.code === req.validatedBody.code ||
|
|
106
|
+
order?.metadata?.discount?.code === req.validatedBody.code;
|
|
107
|
+
});
|
|
108
|
+
if (matchingOrders.length !== 0) {
|
|
109
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
110
|
+
discountResult: {
|
|
111
|
+
isApplied: false,
|
|
112
|
+
discount: 0,
|
|
113
|
+
message: "You have already used this coupon"
|
|
114
|
+
}
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
8
120
|
const { result, errors } = await (0, apply_coupon_1.applyCouponWorkflow)(req.scope).run({
|
|
9
121
|
input: {
|
|
10
122
|
...req.validatedBody,
|
|
123
|
+
cart: carts[0],
|
|
11
124
|
customerId: req.auth_context?.actor_id || undefined,
|
|
12
125
|
},
|
|
13
126
|
throwOnError: false
|
|
14
127
|
});
|
|
15
|
-
if (!result) {
|
|
16
|
-
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Failed to apply coupon", 400));
|
|
17
|
-
}
|
|
18
128
|
if (errors && errors.length > 0) {
|
|
19
129
|
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
130
|
+
discountResult: {
|
|
131
|
+
isApplied: false,
|
|
132
|
+
discount: 0,
|
|
133
|
+
message: errors?.[0]?.error?.message || "Failed to apply coupon"
|
|
134
|
+
}
|
|
23
135
|
}));
|
|
24
136
|
}
|
|
25
|
-
|
|
137
|
+
if (!result) {
|
|
138
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)("Failed to apply coupon", 400));
|
|
139
|
+
}
|
|
140
|
+
// Return only discount result - frontend will fetch cart separately
|
|
141
|
+
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createSuccessResponse)({
|
|
142
|
+
discountResult: result,
|
|
143
|
+
}));
|
|
26
144
|
}
|
|
27
145
|
catch (error) {
|
|
28
146
|
console.log("error: ", error);
|
|
29
147
|
return (0, api_response_1.sendApiResponse)(res, (0, api_response_1.createErrorResponse)(error.message, 500));
|
|
30
148
|
}
|
|
31
149
|
}
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
150
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2Rpc2NvdW50cy9hcHBseS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWVBLG9CQXNLQztBQWhMRCwrREFJc0M7QUFDdEMsOEVBQWtGO0FBRWxGLHFEQUFzRTtBQUN0RSxpREFBdUQ7QUFFaEQsS0FBSyxVQUFVLElBQUksQ0FDeEIsR0FBdUQsRUFDdkQsR0FBbUI7SUFFbkIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDakUsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLFlBQVksRUFBRSxRQUFRLElBQUksR0FBRyxDQUFDLFlBQVksQ0FBQztRQUM5RCxNQUFNLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztZQUN4QyxNQUFNLEVBQUUsTUFBTTtZQUNkLE1BQU0sRUFBRTtnQkFDTixHQUFHO2dCQUNILFNBQVM7Z0JBQ1QsaUJBQWlCO2dCQUNqQix3QkFBd0I7Z0JBQ3hCLG9DQUFvQztnQkFDcEMsY0FBYzthQUNmO1lBQ0QsT0FBTyxFQUFFLEVBQUUsRUFBRSxFQUFFLEdBQUcsQ0FBQyxhQUFhLENBQUMsT0FBTyxFQUFFO1NBQzNDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbEIsT0FBTyxJQUFBLDhCQUFlLEVBQ3BCLEdBQUcsRUFDSCxJQUFBLGtDQUFtQixFQUFDLFFBQVEsR0FBRyxDQUFDLGFBQWEsQ0FBQyxPQUFPLFlBQVksRUFBRSxHQUFHLENBQUMsQ0FDeEUsQ0FBQztRQUNKLENBQUM7UUFDRCxNQUFNLEVBQUUsSUFBSSxFQUFFLGNBQWMsRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtB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
|