@zenky/storefront-vue 0.9.46 → 0.10.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/LICENSE.md +2 -2
- package/README.md +163 -6
- package/dist/cart/index.d.ts +559 -0
- package/dist/cart/index.js +13 -0
- package/dist/cart/index.js.map +1 -0
- package/dist/catalog/index.d.ts +73 -0
- package/dist/catalog/index.js +4 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/chunk-2OD7CLKD.js +102 -0
- package/dist/chunk-2OD7CLKD.js.map +1 -0
- package/dist/chunk-4MJKN7PL.js +111 -0
- package/dist/chunk-4MJKN7PL.js.map +1 -0
- package/dist/chunk-5QCUH3FF.js +15 -0
- package/dist/chunk-5QCUH3FF.js.map +1 -0
- package/dist/chunk-AL5W5JSD.js +41 -0
- package/dist/chunk-AL5W5JSD.js.map +1 -0
- package/dist/chunk-BIGTKVC6.js +38 -0
- package/dist/chunk-BIGTKVC6.js.map +1 -0
- package/dist/chunk-BWUKMWHL.js +45 -0
- package/dist/chunk-BWUKMWHL.js.map +1 -0
- package/dist/chunk-DKWDKVXQ.js +91 -0
- package/dist/chunk-DKWDKVXQ.js.map +1 -0
- package/dist/chunk-EMGQXBU3.js +3 -0
- package/dist/chunk-EMGQXBU3.js.map +1 -0
- package/dist/chunk-EYRCAUUP.js +45 -0
- package/dist/chunk-EYRCAUUP.js.map +1 -0
- package/dist/chunk-GW2QNTAD.js +327 -0
- package/dist/chunk-GW2QNTAD.js.map +1 -0
- package/dist/chunk-IGDDWGS4.js +557 -0
- package/dist/chunk-IGDDWGS4.js.map +1 -0
- package/dist/chunk-OQLU7FSR.js +1841 -0
- package/dist/chunk-OQLU7FSR.js.map +1 -0
- package/dist/chunk-PJ2LEKW6.js +229 -0
- package/dist/chunk-PJ2LEKW6.js.map +1 -0
- package/dist/chunk-PWHIDGKW.js +97 -0
- package/dist/chunk-PWHIDGKW.js.map +1 -0
- package/dist/chunk-RO25KNM5.js +36 -0
- package/dist/chunk-RO25KNM5.js.map +1 -0
- package/dist/chunk-V3VVDQIR.js +27 -0
- package/dist/chunk-V3VVDQIR.js.map +1 -0
- package/dist/chunk-VL3T36VM.js +386 -0
- package/dist/chunk-VL3T36VM.js.map +1 -0
- package/dist/chunk-XUGXLJZB.js +14 -0
- package/dist/chunk-XUGXLJZB.js.map +1 -0
- package/dist/chunk-Z3IW4ODZ.js +36 -0
- package/dist/chunk-Z3IW4ODZ.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/index.d.ts +54 -0
- package/dist/layout/index.js +102 -0
- package/dist/layout/index.js.map +1 -0
- package/dist/money/index.d.ts +26 -0
- package/dist/money/index.js +3 -0
- package/dist/money/index.js.map +1 -0
- package/dist/order/index.d.ts +34 -0
- package/dist/order/index.js +209 -0
- package/dist/order/index.js.map +1 -0
- package/dist/pagination/index.d.ts +87 -0
- package/dist/pagination/index.js +97 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/ports/index.d.ts +124 -0
- package/dist/ports/index.js +3 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/products/index.d.ts +105 -0
- package/dist/products/index.js +7 -0
- package/dist/products/index.js.map +1 -0
- package/dist/promotions/index.d.ts +10 -0
- package/dist/promotions/index.js +7 -0
- package/dist/promotions/index.js.map +1 -0
- package/dist/schedule-Dd3zwHEb.d.ts +27 -0
- package/dist/seo/index.d.ts +91 -0
- package/dist/seo/index.js +289 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/snippets/index.d.ts +17 -0
- package/dist/snippets/index.js +3 -0
- package/dist/snippets/index.js.map +1 -0
- package/dist/snippets-BC5bkSjm.d.ts +23 -0
- package/dist/stores/index.d.ts +196 -0
- package/dist/stores/index.js +8 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/theme/index.d.ts +60 -0
- package/dist/theme/index.js +3 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/utils/index.d.ts +66 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/website-settings/index.d.ts +38 -0
- package/dist/website-settings/index.js +3 -0
- package/dist/website-settings/index.js.map +1 -0
- package/package.json +122 -41
- package/dist/esm/index.js +0 -3642
- package/dist/zenky.d.ts +0 -10377
|
@@ -0,0 +1,1841 @@
|
|
|
1
|
+
import { useCartCredentials, usePromotionsChecker } from './chunk-4MJKN7PL.js';
|
|
2
|
+
import { createRuntimeState } from './chunk-5QCUH3FF.js';
|
|
3
|
+
import { useCatalogStore } from './chunk-Z3IW4ODZ.js';
|
|
4
|
+
import { usePromotionsStore } from './chunk-BWUKMWHL.js';
|
|
5
|
+
import { useStoreProfileStore, useAuthStore } from './chunk-PWHIDGKW.js';
|
|
6
|
+
import { getRemoteCatalog } from './chunk-EYRCAUUP.js';
|
|
7
|
+
import { useCartStore, useOrderSettingsStore } from './chunk-VL3T36VM.js';
|
|
8
|
+
import { useRuntime } from './chunk-V3VVDQIR.js';
|
|
9
|
+
import { minorToMajor, majorToMinor } from './chunk-RO25KNM5.js';
|
|
10
|
+
import { PaymentMethod, OrderAuthenticationMethod, DeliveryMethod, OrderCartCheckerResultAction, RecaptchaAction } from '@zenky/storefront-api';
|
|
11
|
+
import { computed, ref, watch, watchEffect, getCurrentScope, onScopeDispose, toValue } from 'vue';
|
|
12
|
+
import { storeToRefs } from 'pinia';
|
|
13
|
+
|
|
14
|
+
// src/cart/constants.ts
|
|
15
|
+
var ORDER_EVENTS = {
|
|
16
|
+
PriceRecalculated: "orders.price.recalculated",
|
|
17
|
+
Reload: "orders.reload",
|
|
18
|
+
Submitted: "orders.submitted",
|
|
19
|
+
Repeated: "orders.repeated",
|
|
20
|
+
PromotionsNotFound: "orders.promotions.not_found",
|
|
21
|
+
RewardsDisplaySelection: "orders.rewards.display_selection",
|
|
22
|
+
RewardsReload: "orders.rewards.reload"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/cart/normalizeAddress.ts
|
|
26
|
+
function normalizeAddress(value) {
|
|
27
|
+
if (!value) return "";
|
|
28
|
+
return value.toLowerCase().replace(/[,\s]+/g, " ").trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/cart/cartLineKey.ts
|
|
32
|
+
function buildCartLineKey(params) {
|
|
33
|
+
const modifiers = (params.modifiers ?? []).map((m) => `${m.modifier_id ?? ""}:${m.quantity}`).sort().join("|");
|
|
34
|
+
return [
|
|
35
|
+
params.productVariantId,
|
|
36
|
+
modifiers,
|
|
37
|
+
params.promotionId ?? "",
|
|
38
|
+
params.promotionRewardId ?? ""
|
|
39
|
+
].join("::");
|
|
40
|
+
}
|
|
41
|
+
function variantLineKey(variant) {
|
|
42
|
+
const modifiers = (variant.modifiers ?? []).map((m) => ({
|
|
43
|
+
modifier_id: m.modifier?.id ?? m.id,
|
|
44
|
+
modifiers_group_id: m.modifiers_group_id ?? void 0,
|
|
45
|
+
quantity: m.quantity
|
|
46
|
+
}));
|
|
47
|
+
return buildCartLineKey({
|
|
48
|
+
productVariantId: variant.product_variant_id,
|
|
49
|
+
modifiers,
|
|
50
|
+
promotionId: variant.promotion?.id,
|
|
51
|
+
promotionRewardId: variant.promotion?.reward_id
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function buildMainPaymentRequest(params) {
|
|
55
|
+
if (!params.method || params.method === PaymentMethod.Bonuses) return null;
|
|
56
|
+
return {
|
|
57
|
+
method: params.method,
|
|
58
|
+
bill: params.billMinor != null && params.billMinor > 0 ? minorToMajor(params.billMinor, params.currency) : void 0,
|
|
59
|
+
save_card: params.saveCard,
|
|
60
|
+
card_token_id: params.cardTokenId ?? void 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/cart/cashChangePresets.ts
|
|
65
|
+
var CASH_DENOMINATIONS_MAJOR = {
|
|
66
|
+
RU: [10, 50, 100, 200, 500, 1e3, 2e3, 5e3, 1e4],
|
|
67
|
+
KZ: [200, 500, 1e3, 2e3, 5e3, 1e4, 2e4],
|
|
68
|
+
BY: [5, 10, 20, 50, 100, 200, 500],
|
|
69
|
+
UA: [1, 2, 5, 10, 20, 50, 100, 200, 500, 1e3]
|
|
70
|
+
};
|
|
71
|
+
var DEFAULT_ROUND_STEPS_MAJOR = [10, 50, 100, 200, 500, 1e3];
|
|
72
|
+
function getDefaultRoundStepsMinor(currency = "RUB") {
|
|
73
|
+
return DEFAULT_ROUND_STEPS_MAJOR.map((value) => majorToMinor(value, currency));
|
|
74
|
+
}
|
|
75
|
+
function getCashDenominationsMinor(country, currency = "RUB") {
|
|
76
|
+
const code = country ?? "RU";
|
|
77
|
+
const major = CASH_DENOMINATIONS_MAJOR[code] ?? CASH_DENOMINATIONS_MAJOR.RU ?? [];
|
|
78
|
+
return major.map((d) => majorToMinor(d, currency));
|
|
79
|
+
}
|
|
80
|
+
function buildCashChangePresets({
|
|
81
|
+
totalMinor,
|
|
82
|
+
denominationsMinor,
|
|
83
|
+
roundStepsMinor = getDefaultRoundStepsMinor()
|
|
84
|
+
}) {
|
|
85
|
+
const total = Math.max(totalMinor, 0);
|
|
86
|
+
if (total === 0) return [...denominationsMinor];
|
|
87
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
88
|
+
for (const step of roundStepsMinor) {
|
|
89
|
+
candidates.add(Math.ceil((total + 1) / step) * step);
|
|
90
|
+
}
|
|
91
|
+
for (const d of denominationsMinor) {
|
|
92
|
+
if (d > total) candidates.add(d);
|
|
93
|
+
}
|
|
94
|
+
return [...candidates].sort((a, b) => a - b);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/cart/logCartError.ts
|
|
98
|
+
function logCartError({ scope, context }, e) {
|
|
99
|
+
const err = e?.err;
|
|
100
|
+
console.error(`[${scope}] failed`, {
|
|
101
|
+
...context ?? {},
|
|
102
|
+
httpCode: err?.http_code,
|
|
103
|
+
errorCode: err?.error_code,
|
|
104
|
+
message: err?.message,
|
|
105
|
+
meta: err?.meta,
|
|
106
|
+
// raw — полный объект ошибки на случай нестандартных полей
|
|
107
|
+
raw: e
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function getCartErrorCode(e) {
|
|
111
|
+
return e?.err?.error_code;
|
|
112
|
+
}
|
|
113
|
+
function getCartErrorHttpCode(e) {
|
|
114
|
+
return e?.err?.http_code;
|
|
115
|
+
}
|
|
116
|
+
function getValidationErrors(e) {
|
|
117
|
+
const err = e?.err;
|
|
118
|
+
if (!err || err.error_code !== "validation") return {};
|
|
119
|
+
const meta = err.meta;
|
|
120
|
+
return meta?.errors ?? {};
|
|
121
|
+
}
|
|
122
|
+
function getCartErrorMessage(e, fallback) {
|
|
123
|
+
const err = e?.err;
|
|
124
|
+
if (!err) return fallback;
|
|
125
|
+
if (err.error_code === "validation") {
|
|
126
|
+
const errors = err.meta?.errors;
|
|
127
|
+
if (errors) {
|
|
128
|
+
const messages = Object.values(errors).flat().filter(Boolean);
|
|
129
|
+
if (messages.length > 0) return messages.join(" ");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return err.message ?? fallback;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/cart/useCartMutation.ts
|
|
136
|
+
var getMutationState = createRuntimeState(() => ({
|
|
137
|
+
versions: /* @__PURE__ */ new Map()
|
|
138
|
+
}));
|
|
139
|
+
function useCartMutation() {
|
|
140
|
+
const runtime = useRuntime();
|
|
141
|
+
const { versions } = getMutationState(runtime);
|
|
142
|
+
async function run(options, fn) {
|
|
143
|
+
const { kind, debounce = 0 } = options;
|
|
144
|
+
const myVersion = (versions.get(kind) ?? 0) + 1;
|
|
145
|
+
versions.set(kind, myVersion);
|
|
146
|
+
const isStale = () => versions.get(kind) !== myVersion;
|
|
147
|
+
if (debounce > 0) {
|
|
148
|
+
await new Promise((resolve) => setTimeout(resolve, debounce));
|
|
149
|
+
if (isStale()) return void 0;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
return await fn(isStale);
|
|
153
|
+
} catch (e) {
|
|
154
|
+
if (isStale()) return void 0;
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function getVersion(kind) {
|
|
159
|
+
return versions.get(kind) ?? 0;
|
|
160
|
+
}
|
|
161
|
+
function bumpKindsWithPrefix(prefix) {
|
|
162
|
+
for (const kind of versions.keys()) {
|
|
163
|
+
if (kind.startsWith(prefix)) {
|
|
164
|
+
versions.set(kind, (versions.get(kind) ?? 0) + 1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { run, getVersion, bumpKindsWithPrefix };
|
|
169
|
+
}
|
|
170
|
+
function useCartQuantities() {
|
|
171
|
+
const cartStore = useCartStore();
|
|
172
|
+
function getProductQuantity(productId) {
|
|
173
|
+
return computed(() => {
|
|
174
|
+
const serverQty = cartStore.productQuantities[productId] ?? 0;
|
|
175
|
+
const adjustment = cartStore.optimisticAdjustments[productId] ?? 0;
|
|
176
|
+
return Math.max(0, serverQty + adjustment);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function getVariantQuantity(productVariantId, modifiers) {
|
|
180
|
+
return computed(() => {
|
|
181
|
+
const variantId = toValue(productVariantId);
|
|
182
|
+
if (!variantId) return 0;
|
|
183
|
+
const requestedMods = toValue(modifiers);
|
|
184
|
+
const match = cartStore.variants.find((cartItem) => {
|
|
185
|
+
if (cartItem.product_variant_id !== variantId) return false;
|
|
186
|
+
const cartMods = cartItem.modifiers ?? [];
|
|
187
|
+
if (cartMods.length !== requestedMods.length) return false;
|
|
188
|
+
if (requestedMods.length === 0) return true;
|
|
189
|
+
return requestedMods.every(
|
|
190
|
+
(mod) => cartMods.some((cm) => cm.id === mod.modifier_id && cm.quantity === mod.quantity)
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
return match?.quantity ?? 0;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
getProductQuantity,
|
|
198
|
+
getVariantQuantity
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function useCheckoutFlow() {
|
|
202
|
+
const profileStore = useStoreProfileStore();
|
|
203
|
+
const mode = computed(
|
|
204
|
+
() => profileStore.store?.settings?.orders?.authentication_method ?? OrderAuthenticationMethod.Disabled
|
|
205
|
+
);
|
|
206
|
+
const needsAuth = computed(() => mode.value === OrderAuthenticationMethod.Preauth);
|
|
207
|
+
const needsCustomerForm = computed(
|
|
208
|
+
() => mode.value === OrderAuthenticationMethod.Disabled || mode.value === OrderAuthenticationMethod.Confirmation
|
|
209
|
+
);
|
|
210
|
+
return { mode, needsAuth, needsCustomerForm };
|
|
211
|
+
}
|
|
212
|
+
function useOrderTotal() {
|
|
213
|
+
const loading = ref(false);
|
|
214
|
+
const sdk = useRuntime().sdk;
|
|
215
|
+
const credentials = useCartCredentials();
|
|
216
|
+
const cartStore = useCartStore();
|
|
217
|
+
const profileStore = useStoreProfileStore();
|
|
218
|
+
const { run } = useCartMutation();
|
|
219
|
+
const total = computed(() => cartStore.checkoutTotal);
|
|
220
|
+
async function fetchTotal() {
|
|
221
|
+
const storeId = profileStore.store?.id;
|
|
222
|
+
if (!storeId) return;
|
|
223
|
+
if (!cartStore.credentials?.id) return;
|
|
224
|
+
await run({ kind: "total", debounce: 100 }, async (isStale) => {
|
|
225
|
+
try {
|
|
226
|
+
const orderCredentials = credentials.getCredentials();
|
|
227
|
+
loading.value = true;
|
|
228
|
+
const fresh = await sdk.orders.getOrderTotal(storeId, orderCredentials);
|
|
229
|
+
if (isStale()) return;
|
|
230
|
+
cartStore.setCheckoutTotal(fresh);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
if (isStale()) return;
|
|
233
|
+
logCartError(
|
|
234
|
+
{ scope: "order-total", context: { orderId: cartStore.credentials?.id } },
|
|
235
|
+
e
|
|
236
|
+
);
|
|
237
|
+
} finally {
|
|
238
|
+
loading.value = false;
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return { total, loading, fetchTotal };
|
|
243
|
+
}
|
|
244
|
+
function useOrderSettings() {
|
|
245
|
+
const sdk = useRuntime().sdk;
|
|
246
|
+
const credentials = useCartCredentials();
|
|
247
|
+
const cartStore = useCartStore();
|
|
248
|
+
const profileStore = useStoreProfileStore();
|
|
249
|
+
const { run } = useCartMutation();
|
|
250
|
+
const loading = ref(false);
|
|
251
|
+
const settings = computed(() => cartStore.orderSettings);
|
|
252
|
+
const paymentMethods = computed(() => cartStore.paymentMethods);
|
|
253
|
+
const deliveryMethods = computed(() => cartStore.deliveryMethods);
|
|
254
|
+
async function fetchSettings() {
|
|
255
|
+
const storeId = profileStore.store?.id;
|
|
256
|
+
if (!storeId) return;
|
|
257
|
+
if (!cartStore.credentials?.id) return;
|
|
258
|
+
await run({ kind: "settings" }, async (isStale) => {
|
|
259
|
+
loading.value = true;
|
|
260
|
+
try {
|
|
261
|
+
const orderCredentials = credentials.getCredentials();
|
|
262
|
+
const fresh = await sdk.orders.getOrderSettings(storeId, orderCredentials);
|
|
263
|
+
if (isStale()) return;
|
|
264
|
+
cartStore.setOrderSettings(fresh);
|
|
265
|
+
} catch (e) {
|
|
266
|
+
if (isStale()) return;
|
|
267
|
+
logCartError(
|
|
268
|
+
{ scope: "order-settings", context: { orderId: cartStore.credentials?.id } },
|
|
269
|
+
e
|
|
270
|
+
);
|
|
271
|
+
} finally {
|
|
272
|
+
loading.value = false;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return { settings, paymentMethods, deliveryMethods, loading, fetchSettings };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/cart/useOnlinePaymentRetry.ts
|
|
280
|
+
function useOnlinePaymentRetry() {
|
|
281
|
+
const sdk = useRuntime().sdk;
|
|
282
|
+
const profileStore = useStoreProfileStore();
|
|
283
|
+
async function retry(creds, options = {}) {
|
|
284
|
+
const storeId = profileStore.store?.id;
|
|
285
|
+
if (!storeId) return null;
|
|
286
|
+
if (!creds.id) return null;
|
|
287
|
+
const credentials = { id: creds.id, token: creds.token ?? "" };
|
|
288
|
+
const currentUrl = options.currentUrl ?? (typeof window === "undefined" ? null : window.location.href);
|
|
289
|
+
if (!currentUrl) return null;
|
|
290
|
+
try {
|
|
291
|
+
const redirect = await sdk.orders.retryOnlinePayment(storeId, credentials, {
|
|
292
|
+
current_url: currentUrl
|
|
293
|
+
});
|
|
294
|
+
if (redirect?.redirect_url) {
|
|
295
|
+
return redirect.redirect_url;
|
|
296
|
+
}
|
|
297
|
+
} catch (e) {
|
|
298
|
+
if (!options.transactionId) {
|
|
299
|
+
logCartError({ scope: "payment-retry", context: { orderId: creds.id } }, e);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (!options.transactionId) return null;
|
|
304
|
+
try {
|
|
305
|
+
const redirect = await sdk.orders.getOnlinePaymentRedirect(
|
|
306
|
+
storeId,
|
|
307
|
+
credentials,
|
|
308
|
+
options.transactionId,
|
|
309
|
+
{ current_url: currentUrl }
|
|
310
|
+
);
|
|
311
|
+
return redirect?.redirect_url ?? null;
|
|
312
|
+
} catch (e) {
|
|
313
|
+
logCartError(
|
|
314
|
+
{
|
|
315
|
+
scope: "payment-retry-fallback",
|
|
316
|
+
context: { orderId: creds.id, transactionId: options.transactionId }
|
|
317
|
+
},
|
|
318
|
+
e
|
|
319
|
+
);
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { retry };
|
|
324
|
+
}
|
|
325
|
+
function useCartAddressSync() {
|
|
326
|
+
const authStore = useAuthStore();
|
|
327
|
+
const orderSettings = useOrderSettingsStore();
|
|
328
|
+
const profileStore = useStoreProfileStore();
|
|
329
|
+
const sdk = useRuntime().sdk;
|
|
330
|
+
async function syncOnLogin() {
|
|
331
|
+
const storeId = profileStore.store?.id;
|
|
332
|
+
if (!storeId) return;
|
|
333
|
+
const requestedToken = authStore.token;
|
|
334
|
+
if (!requestedToken) return;
|
|
335
|
+
let addresses;
|
|
336
|
+
try {
|
|
337
|
+
const response = await sdk.customer.getAddresses(storeId, void 0, requestedToken);
|
|
338
|
+
addresses = response.items ?? [];
|
|
339
|
+
} catch (e) {
|
|
340
|
+
console.error("[address-sync] failed to fetch saved addresses", e);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
|
|
344
|
+
if (addresses.length === 0) return;
|
|
345
|
+
if (!orderSettings.selectedAddress) {
|
|
346
|
+
const first = addresses[0];
|
|
347
|
+
if (!first) return;
|
|
348
|
+
orderSettings.setSelectedAddress(first.address, first.id, first.name);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (orderSettings.selectedAddressId && addresses.some((a) => a.id === orderSettings.selectedAddressId)) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const normalizedSelected = normalizeAddress(orderSettings.selectedAddress);
|
|
355
|
+
const match = addresses.find((a) => normalizeAddress(a.address) === normalizedSelected);
|
|
356
|
+
if (match) {
|
|
357
|
+
orderSettings.setSelectedAddress(match.address, match.id, match.name);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
orderSettings.setSelectedAddress(null, null, null);
|
|
361
|
+
}
|
|
362
|
+
watch(
|
|
363
|
+
() => authStore.isAuthenticated,
|
|
364
|
+
async (isAuth, wasAuth) => {
|
|
365
|
+
if (isAuth && !wasAuth) await syncOnLogin();
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/cart/useCartInitialization.ts
|
|
371
|
+
var ORDER_INCLUDES = [
|
|
372
|
+
"city.schedule",
|
|
373
|
+
"stock.schedule",
|
|
374
|
+
"delivery_address",
|
|
375
|
+
"delivery_interval",
|
|
376
|
+
"customer",
|
|
377
|
+
"payments",
|
|
378
|
+
"progress",
|
|
379
|
+
"promocode",
|
|
380
|
+
"variants.variant.options",
|
|
381
|
+
"variants.product.images",
|
|
382
|
+
"variants.product.categories",
|
|
383
|
+
"variants.modifiers.modifier",
|
|
384
|
+
"variants.modifiers.modifiers_group"
|
|
385
|
+
].join(",");
|
|
386
|
+
var ORDER_WITH = { with: ORDER_INCLUDES };
|
|
387
|
+
var getInitializationState = createRuntimeState(() => ({ promise: null }));
|
|
388
|
+
function useCartInitialization(credentials, sdk) {
|
|
389
|
+
const cartStore = useCartStore();
|
|
390
|
+
const runtime = useRuntime();
|
|
391
|
+
const state = getInitializationState(runtime);
|
|
392
|
+
const { fetchTotal } = useOrderTotal();
|
|
393
|
+
async function createNewOrder() {
|
|
394
|
+
const storeId = credentials.getStoreId();
|
|
395
|
+
const cityId = credentials.getCityId();
|
|
396
|
+
const apiToken = credentials.getApiToken();
|
|
397
|
+
const newOrder = await sdk.orders.createOrder(storeId, { city_id: cityId }, apiToken);
|
|
398
|
+
cartStore.setOrder(newOrder);
|
|
399
|
+
credentials.persistCredentials(newOrder);
|
|
400
|
+
runtime.events?.get()?.orderCreated({ order: newOrder });
|
|
401
|
+
}
|
|
402
|
+
async function loadOrderSettings() {
|
|
403
|
+
try {
|
|
404
|
+
const storeId = credentials.getStoreId();
|
|
405
|
+
const orderCredentials = credentials.getCredentials();
|
|
406
|
+
const settings = await sdk.orders.getOrderSettings(storeId, orderCredentials);
|
|
407
|
+
cartStore.setOrderSettings(settings);
|
|
408
|
+
} catch (e) {
|
|
409
|
+
console.error("[cart] Failed to load order settings", e);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function _doInitialize() {
|
|
413
|
+
cartStore.setInitializing(true);
|
|
414
|
+
try {
|
|
415
|
+
if (credentials.orderIdCookie.value && credentials.orderTokenCookie.value) {
|
|
416
|
+
const orderCredentials = credentials.buildCredentials(
|
|
417
|
+
credentials.orderIdCookie.value,
|
|
418
|
+
credentials.orderTokenCookie.value
|
|
419
|
+
);
|
|
420
|
+
try {
|
|
421
|
+
const storeId = credentials.getStoreId();
|
|
422
|
+
const existingOrder = await sdk.orders.getOrder(
|
|
423
|
+
storeId,
|
|
424
|
+
orderCredentials,
|
|
425
|
+
ORDER_WITH
|
|
426
|
+
);
|
|
427
|
+
if (credentials.isOrderActive(existingOrder)) {
|
|
428
|
+
cartStore.setOrder(existingOrder);
|
|
429
|
+
runtime.events?.get()?.orderLoaded({ order: existingOrder });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
credentials.clearPersistedCredentials();
|
|
433
|
+
cartStore.clearCart();
|
|
434
|
+
} catch {
|
|
435
|
+
credentials.clearPersistedCredentials();
|
|
436
|
+
cartStore.clearCart();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
await createNewOrder();
|
|
440
|
+
} catch (e) {
|
|
441
|
+
runtime.events?.get()?.orderInitializationFailed();
|
|
442
|
+
throw e;
|
|
443
|
+
} finally {
|
|
444
|
+
if (cartStore.hasCredentials) {
|
|
445
|
+
await loadOrderSettings();
|
|
446
|
+
if (cartStore.order?.variants?.length) {
|
|
447
|
+
await fetchTotal();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
cartStore.setInitializing(false);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function initializeOrder() {
|
|
454
|
+
if (cartStore.hasOrder) return Promise.resolve();
|
|
455
|
+
if (state.promise !== null) return state.promise;
|
|
456
|
+
state.promise = _doInitialize().finally(() => {
|
|
457
|
+
state.promise = null;
|
|
458
|
+
});
|
|
459
|
+
return state.promise;
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
initializeOrder
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function useOrderBonuses(deps = {}) {
|
|
466
|
+
const runtime = useRuntime();
|
|
467
|
+
const activity = deps.activity ?? runtime.activity;
|
|
468
|
+
const sdk = runtime.sdk;
|
|
469
|
+
const credentials = useCartCredentials();
|
|
470
|
+
const cartStore = useCartStore();
|
|
471
|
+
const profileStore = useStoreProfileStore();
|
|
472
|
+
const { total, fetchTotal } = useOrderTotal();
|
|
473
|
+
const { run: runMutation } = useCartMutation();
|
|
474
|
+
const maxAvailable = computed(() => total.value?.max_bonuses_payment ?? 0);
|
|
475
|
+
function bonusesAmountToApi(amount) {
|
|
476
|
+
const currency = profileStore.store?.settings?.currency ?? "RUB";
|
|
477
|
+
return minorToMajor(amount, currency);
|
|
478
|
+
}
|
|
479
|
+
async function apply(additionalPayments = [], pin, amount) {
|
|
480
|
+
const storeId = profileStore.store?.id;
|
|
481
|
+
if (!storeId) return;
|
|
482
|
+
if (!cartStore.credentials?.id) return;
|
|
483
|
+
const requested = amount ?? maxAvailable.value;
|
|
484
|
+
const finalAmount = Math.min(requested, maxAvailable.value);
|
|
485
|
+
if (!Number.isFinite(finalAmount) || finalAmount <= 0) return;
|
|
486
|
+
const bonusesPayment = {
|
|
487
|
+
method: PaymentMethod.Bonuses,
|
|
488
|
+
amount: bonusesAmountToApi(finalAmount)
|
|
489
|
+
};
|
|
490
|
+
if (pin) bonusesPayment.pin = pin;
|
|
491
|
+
try {
|
|
492
|
+
await runMutation({ kind: "bonuses" }, async (isStale) => {
|
|
493
|
+
activity.begin("bonuses");
|
|
494
|
+
try {
|
|
495
|
+
const orderCredentials = credentials.getCredentials();
|
|
496
|
+
const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
|
|
497
|
+
payments: [...additionalPayments, bonusesPayment]
|
|
498
|
+
});
|
|
499
|
+
if (isStale()) return;
|
|
500
|
+
if (Array.isArray(updatedOrder)) {
|
|
501
|
+
cartStore.setOrderPaymentsState(updatedOrder);
|
|
502
|
+
} else {
|
|
503
|
+
cartStore.setOrder(updatedOrder);
|
|
504
|
+
}
|
|
505
|
+
await fetchTotal();
|
|
506
|
+
} finally {
|
|
507
|
+
activity.end("bonuses");
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
} catch (e) {
|
|
511
|
+
logCartError(
|
|
512
|
+
{
|
|
513
|
+
scope: "bonuses-apply",
|
|
514
|
+
context: { amount, orderId: cartStore.credentials?.id }
|
|
515
|
+
},
|
|
516
|
+
e
|
|
517
|
+
);
|
|
518
|
+
throw e;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
async function remove(mainPayment) {
|
|
522
|
+
const storeId = profileStore.store?.id;
|
|
523
|
+
if (!storeId) return;
|
|
524
|
+
if (!cartStore.credentials?.id) return;
|
|
525
|
+
try {
|
|
526
|
+
await runMutation({ kind: "bonuses" }, async (isStale) => {
|
|
527
|
+
activity.begin("bonuses");
|
|
528
|
+
try {
|
|
529
|
+
const orderCredentials = credentials.getCredentials();
|
|
530
|
+
const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
|
|
531
|
+
payments: mainPayment ? [mainPayment] : []
|
|
532
|
+
});
|
|
533
|
+
if (isStale()) return;
|
|
534
|
+
if (Array.isArray(updatedOrder)) {
|
|
535
|
+
cartStore.setOrderPaymentsState(updatedOrder);
|
|
536
|
+
} else {
|
|
537
|
+
cartStore.setOrder(updatedOrder);
|
|
538
|
+
}
|
|
539
|
+
await fetchTotal();
|
|
540
|
+
} finally {
|
|
541
|
+
activity.end("bonuses");
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
} catch (e) {
|
|
545
|
+
logCartError(
|
|
546
|
+
{
|
|
547
|
+
scope: "bonuses-remove",
|
|
548
|
+
context: { orderId: cartStore.credentials?.id }
|
|
549
|
+
},
|
|
550
|
+
e
|
|
551
|
+
);
|
|
552
|
+
throw e;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
maxAvailable,
|
|
557
|
+
apply,
|
|
558
|
+
remove
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
var PAYMENT_DEBOUNCE_MS = 300;
|
|
562
|
+
function usePaymentSelection() {
|
|
563
|
+
const { sdk, notifier, activity } = useRuntime();
|
|
564
|
+
const credentials = useCartCredentials();
|
|
565
|
+
const cartStore = useCartStore();
|
|
566
|
+
const profileStore = useStoreProfileStore();
|
|
567
|
+
const { fetchTotal } = useOrderTotal();
|
|
568
|
+
const { run: runMutation } = useCartMutation();
|
|
569
|
+
async function sendPayments(payments, debounce = PAYMENT_DEBOUNCE_MS) {
|
|
570
|
+
const storeId = profileStore.store?.id;
|
|
571
|
+
if (!storeId) return;
|
|
572
|
+
if (!cartStore.credentials?.id) return;
|
|
573
|
+
await runMutation({ kind: "payment", debounce }, async (isStale) => {
|
|
574
|
+
activity.begin("payment");
|
|
575
|
+
try {
|
|
576
|
+
const orderCredentials = credentials.getCredentials();
|
|
577
|
+
const result = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
|
|
578
|
+
payments
|
|
579
|
+
});
|
|
580
|
+
if (isStale()) return;
|
|
581
|
+
if (Array.isArray(result)) {
|
|
582
|
+
cartStore.setOrderPaymentsState(result);
|
|
583
|
+
} else {
|
|
584
|
+
cartStore.patchOrder(result);
|
|
585
|
+
}
|
|
586
|
+
await fetchTotal();
|
|
587
|
+
} catch (e) {
|
|
588
|
+
if (isStale()) return;
|
|
589
|
+
logCartError(
|
|
590
|
+
{
|
|
591
|
+
scope: "payment",
|
|
592
|
+
context: {
|
|
593
|
+
methods: payments.map((p) => p.method),
|
|
594
|
+
count: payments.length,
|
|
595
|
+
orderId: cartStore.credentials?.id
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
e
|
|
599
|
+
);
|
|
600
|
+
notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u043F\u043E\u0441\u043E\u0431 \u043E\u043F\u043B\u0430\u0442\u044B"));
|
|
601
|
+
} finally {
|
|
602
|
+
activity.end("payment");
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
function buildPaymentRequest(selection) {
|
|
607
|
+
const payment = { method: selection.method };
|
|
608
|
+
if (selection.bill != null && selection.bill > 0) {
|
|
609
|
+
const currency = profileStore.store?.settings?.currency ?? "RUB";
|
|
610
|
+
payment.bill = minorToMajor(selection.bill, currency);
|
|
611
|
+
}
|
|
612
|
+
if (selection.save_card !== void 0) payment.save_card = selection.save_card;
|
|
613
|
+
if (selection.card_token_id) payment.card_token_id = selection.card_token_id;
|
|
614
|
+
return payment;
|
|
615
|
+
}
|
|
616
|
+
function getCurrentBonusesPayment() {
|
|
617
|
+
if (!cartStore.hasBonusesApplied) return null;
|
|
618
|
+
const { total } = useOrderTotal();
|
|
619
|
+
const bonusPayment = total.value?.payments?.find(
|
|
620
|
+
(p) => p.method === PaymentMethod.Bonuses
|
|
621
|
+
);
|
|
622
|
+
const amountMinor = bonusPayment ? Number(bonusPayment.amount) : 0;
|
|
623
|
+
if (!Number.isFinite(amountMinor) || amountMinor <= 0) return null;
|
|
624
|
+
const currency = profileStore.store?.settings?.currency ?? "RUB";
|
|
625
|
+
return {
|
|
626
|
+
method: PaymentMethod.Bonuses,
|
|
627
|
+
amount: minorToMajor(amountMinor, currency)
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function buildPaymentsWithBonuses(selection) {
|
|
631
|
+
const main = buildPaymentRequest(selection);
|
|
632
|
+
const bonuses = getCurrentBonusesPayment();
|
|
633
|
+
return bonuses ? [main, bonuses] : [main];
|
|
634
|
+
}
|
|
635
|
+
async function setPayment(selection) {
|
|
636
|
+
await sendPayments(buildPaymentsWithBonuses(selection));
|
|
637
|
+
}
|
|
638
|
+
async function syncPaymentNow(selection) {
|
|
639
|
+
await sendPayments(buildPaymentsWithBonuses(selection), 0);
|
|
640
|
+
}
|
|
641
|
+
async function clearPayments() {
|
|
642
|
+
await sendPayments([]);
|
|
643
|
+
}
|
|
644
|
+
return { setPayment, syncPaymentNow, clearPayments };
|
|
645
|
+
}
|
|
646
|
+
function useBonusesReconciliation(deps) {
|
|
647
|
+
const cartStore = useCartStore();
|
|
648
|
+
const profileStore = useStoreProfileStore();
|
|
649
|
+
const { maxAvailable, apply, remove } = useOrderBonuses({ activity: deps.activity });
|
|
650
|
+
function isPinEnabled() {
|
|
651
|
+
return profileStore.store?.settings?.loyalty?.bonuses?.pin_enabled ?? false;
|
|
652
|
+
}
|
|
653
|
+
function mainPayment() {
|
|
654
|
+
return buildMainPaymentRequest({
|
|
655
|
+
method: cartStore.selectedPaymentMethod,
|
|
656
|
+
billMinor: cartStore.selectedPaymentBill,
|
|
657
|
+
cardTokenId: cartStore.selectedCardTokenId,
|
|
658
|
+
saveCard: cartStore.selectedSaveCard,
|
|
659
|
+
currency: profileStore.store?.settings?.currency ?? "RUB"
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
function appliedBonusesMinor() {
|
|
663
|
+
const payments = cartStore.checkoutTotal?.payments ?? [];
|
|
664
|
+
const bonus = payments.find((p) => p.method === PaymentMethod.Bonuses);
|
|
665
|
+
return bonus?.amount ?? 0;
|
|
666
|
+
}
|
|
667
|
+
async function reconcileAfterOrderChange() {
|
|
668
|
+
const intent = cartStore.bonusesIntent;
|
|
669
|
+
if (!intent.enabled) return;
|
|
670
|
+
const payment = mainPayment();
|
|
671
|
+
if (maxAvailable.value <= 0) {
|
|
672
|
+
try {
|
|
673
|
+
if (cartStore.hasBonusesApplied) await remove(payment);
|
|
674
|
+
} catch (e) {
|
|
675
|
+
logCartError(
|
|
676
|
+
{
|
|
677
|
+
scope: "bonuses-reconcile-unavailable",
|
|
678
|
+
context: { orderId: cartStore.credentials?.id }
|
|
679
|
+
},
|
|
680
|
+
e
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
cartStore.setBonusesIntentEnabled(false);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (isPinEnabled()) {
|
|
687
|
+
try {
|
|
688
|
+
if (cartStore.hasBonusesApplied) await remove(payment);
|
|
689
|
+
} catch (e) {
|
|
690
|
+
logCartError(
|
|
691
|
+
{
|
|
692
|
+
scope: "bonuses-reconcile-pin",
|
|
693
|
+
context: { orderId: cartStore.credentials?.id }
|
|
694
|
+
},
|
|
695
|
+
e
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
cartStore.setBonusesRequiresConfirmation(true);
|
|
699
|
+
deps.notifier.message("\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F", {
|
|
700
|
+
description: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0441\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0431\u043E\u043D\u0443\u0441\u043E\u0432 \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E"
|
|
701
|
+
});
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const desired = intent.desiredAmountMinor;
|
|
705
|
+
const amount = desired === null ? maxAvailable.value : Math.min(desired, maxAvailable.value);
|
|
706
|
+
if (appliedBonusesMinor() === amount) return;
|
|
707
|
+
const additionalPayments = payment ? [payment] : [];
|
|
708
|
+
try {
|
|
709
|
+
await apply(additionalPayments, void 0, amount);
|
|
710
|
+
} catch (e) {
|
|
711
|
+
logCartError(
|
|
712
|
+
{ scope: "bonuses-reconcile", context: { orderId: cartStore.credentials?.id } },
|
|
713
|
+
e
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return { reconcileAfterOrderChange };
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/cart/useCartRecalculation.ts
|
|
721
|
+
function useCartRecalculation(deps) {
|
|
722
|
+
const { fetchTotal } = useOrderTotal();
|
|
723
|
+
const { reconcileAfterOrderChange } = useBonusesReconciliation({
|
|
724
|
+
notifier: deps.notifier,
|
|
725
|
+
activity: deps.activity
|
|
726
|
+
});
|
|
727
|
+
const { dispatchCheck } = usePromotionsChecker();
|
|
728
|
+
async function recalculateAfterOrderChange() {
|
|
729
|
+
await fetchTotal();
|
|
730
|
+
await reconcileAfterOrderChange();
|
|
731
|
+
dispatchCheck();
|
|
732
|
+
}
|
|
733
|
+
return { recalculateAfterOrderChange };
|
|
734
|
+
}
|
|
735
|
+
function useCustomerAttach(cache) {
|
|
736
|
+
const { sdk, notifier, activity } = useRuntime();
|
|
737
|
+
const credentials = useCartCredentials();
|
|
738
|
+
const cartStore = useCartStore();
|
|
739
|
+
const profileStore = useStoreProfileStore();
|
|
740
|
+
const { run: runMutation } = useCartMutation();
|
|
741
|
+
const cachedCustomer = cache.forOrder(() => cartStore.order?.id ?? null);
|
|
742
|
+
const isAttached = computed(
|
|
743
|
+
() => cartStore.order?.customer != null || cachedCustomer.value != null
|
|
744
|
+
);
|
|
745
|
+
async function attach(request) {
|
|
746
|
+
const storeId = profileStore.store?.id;
|
|
747
|
+
if (!storeId) return;
|
|
748
|
+
const orderId = cartStore.credentials?.id;
|
|
749
|
+
if (!orderId) return;
|
|
750
|
+
try {
|
|
751
|
+
await runMutation({ kind: "customer" }, async (isStale) => {
|
|
752
|
+
activity.begin("custom");
|
|
753
|
+
try {
|
|
754
|
+
const orderCredentials = credentials.getCredentials();
|
|
755
|
+
await sdk.orders.setOrderCustomer(storeId, orderCredentials, request);
|
|
756
|
+
if (isStale()) return;
|
|
757
|
+
cache.set(orderId, request);
|
|
758
|
+
const fresh = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
|
|
759
|
+
if (isStale()) return;
|
|
760
|
+
cartStore.setOrder(fresh);
|
|
761
|
+
} finally {
|
|
762
|
+
activity.end("custom");
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
} catch (e) {
|
|
766
|
+
logCartError(
|
|
767
|
+
{
|
|
768
|
+
scope: "customer-attach",
|
|
769
|
+
context: {
|
|
770
|
+
firstName: request.first_name,
|
|
771
|
+
lastName: request.last_name,
|
|
772
|
+
phoneCountry: request.phone?.country,
|
|
773
|
+
orderId: cartStore.credentials?.id
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
e
|
|
777
|
+
);
|
|
778
|
+
notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435"));
|
|
779
|
+
throw e;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return { isAttached, attach };
|
|
783
|
+
}
|
|
784
|
+
function normalizePhone(phone) {
|
|
785
|
+
if (!phone) return null;
|
|
786
|
+
const digits = phone.replace(/\D/g, "");
|
|
787
|
+
return digits || null;
|
|
788
|
+
}
|
|
789
|
+
function useCartCustomerAttachment(cache) {
|
|
790
|
+
const { sdk, notifier } = useRuntime();
|
|
791
|
+
const authStore = useAuthStore();
|
|
792
|
+
const cartStore = useCartStore();
|
|
793
|
+
const profileStore = useStoreProfileStore();
|
|
794
|
+
const orderSettings = useOrderSettingsStore();
|
|
795
|
+
const cartCredentials = useCartCredentials();
|
|
796
|
+
async function attachCurrentUser(notifyOnPhoneChange) {
|
|
797
|
+
if (!authStore.isAuthenticated || !cartStore.hasOrder) return;
|
|
798
|
+
const storeId = profileStore.store?.id;
|
|
799
|
+
if (!storeId) return;
|
|
800
|
+
const requestedToken = authStore.token;
|
|
801
|
+
const requestedOrderId = cartStore.order?.id;
|
|
802
|
+
const previousPhone = notifyOnPhoneChange ? normalizePhone(
|
|
803
|
+
cartStore.order?.customer?.phone?.number ?? cache.forOrder(() => cartStore.order?.id ?? null).value?.phone?.number ?? null
|
|
804
|
+
) : null;
|
|
805
|
+
try {
|
|
806
|
+
const credentials = cartCredentials.getCredentials();
|
|
807
|
+
await sdk.orders.setOrderCustomer(storeId, credentials, {
|
|
808
|
+
phone: { number: authStore.phone ?? "", country: authStore.countryCode }
|
|
809
|
+
});
|
|
810
|
+
console.log("[cart] customer attached to order");
|
|
811
|
+
const fresh = await sdk.orders.getOrder(storeId, credentials, ORDER_WITH);
|
|
812
|
+
if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
|
|
813
|
+
if (cartStore.order?.id !== requestedOrderId) return;
|
|
814
|
+
cartStore.setOrder(fresh);
|
|
815
|
+
cache.clear();
|
|
816
|
+
if (notifyOnPhoneChange) {
|
|
817
|
+
const newPhone = normalizePhone(fresh.customer?.phone?.number ?? null);
|
|
818
|
+
if (previousPhone && newPhone && previousPhone !== newPhone) {
|
|
819
|
+
const firstName = fresh.customer?.first_name;
|
|
820
|
+
notifier.success(
|
|
821
|
+
firstName ? `\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B \u2014 ${firstName}` : "\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B"
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
} catch (e) {
|
|
826
|
+
console.error("[cart] failed to attach customer to order", e);
|
|
827
|
+
if (notifyOnPhoneChange) {
|
|
828
|
+
notifier.error("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430");
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async function refreshOrderAfterLogout() {
|
|
833
|
+
orderSettings.setSelectedAddress(null, null, null);
|
|
834
|
+
if (!cartStore.hasOrder) return;
|
|
835
|
+
const storeId = profileStore.store?.id;
|
|
836
|
+
if (!storeId) return;
|
|
837
|
+
const requestedOrderId = cartStore.order?.id;
|
|
838
|
+
try {
|
|
839
|
+
const credentials = cartCredentials.getCredentials();
|
|
840
|
+
const fresh = await sdk.orders.getOrder(storeId, credentials, ORDER_WITH);
|
|
841
|
+
if (authStore.isAuthenticated) return;
|
|
842
|
+
if (cartStore.order?.id !== requestedOrderId) return;
|
|
843
|
+
cartStore.setOrder(fresh);
|
|
844
|
+
cache.clear();
|
|
845
|
+
} catch (e) {
|
|
846
|
+
console.error("[cart] failed to refresh order after logout", e);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
watch(
|
|
850
|
+
() => authStore.isAuthenticated,
|
|
851
|
+
async (isAuthenticated) => {
|
|
852
|
+
if (isAuthenticated) {
|
|
853
|
+
await attachCurrentUser(true);
|
|
854
|
+
} else {
|
|
855
|
+
await refreshOrderAfterLogout();
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
);
|
|
859
|
+
watch(
|
|
860
|
+
() => cartStore.order?.id,
|
|
861
|
+
async (newId, oldId) => {
|
|
862
|
+
if (!newId || newId === oldId) return;
|
|
863
|
+
if (!authStore.isAuthenticated) return;
|
|
864
|
+
if (cartStore.order?.customer) return;
|
|
865
|
+
await attachCurrentUser(false);
|
|
866
|
+
}
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
var KEY_STOCK_ID = "zenky_stock_id";
|
|
870
|
+
var YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
871
|
+
function useCartStockWatcher() {
|
|
872
|
+
const { sdk, notifier, storage, catalogReloader } = useRuntime();
|
|
873
|
+
const profileStore = useStoreProfileStore();
|
|
874
|
+
const catalogStore = useCatalogStore();
|
|
875
|
+
const cartStore = useCartStore();
|
|
876
|
+
const credentials = useCartCredentials();
|
|
877
|
+
const { fetchTotal } = useOrderTotal();
|
|
878
|
+
async function handleStockChange(updatedOrder) {
|
|
879
|
+
const newStockId = updatedOrder.stock?.id ?? null;
|
|
880
|
+
const currentStockId = profileStore.stock?.id ?? null;
|
|
881
|
+
if (newStockId === currentStockId) return;
|
|
882
|
+
try {
|
|
883
|
+
profileStore.setStock(updatedOrder.stock ?? null);
|
|
884
|
+
if (newStockId) storage.set(KEY_STOCK_ID, newStockId, { maxAge: YEAR_SECONDS });
|
|
885
|
+
else storage.remove(KEY_STOCK_ID);
|
|
886
|
+
if (profileStore.store && profileStore.city) {
|
|
887
|
+
const remoteCatalog = getRemoteCatalog(
|
|
888
|
+
profileStore.store,
|
|
889
|
+
profileStore.city,
|
|
890
|
+
updatedOrder.stock ?? null
|
|
891
|
+
);
|
|
892
|
+
if (remoteCatalog && remoteCatalog.hash !== catalogStore.currentHash && catalogReloader) {
|
|
893
|
+
await catalogReloader.reload(profileStore.store.id, remoteCatalog);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (cartStore.hasCredentials) {
|
|
897
|
+
await checkCartProducts();
|
|
898
|
+
}
|
|
899
|
+
} catch (e) {
|
|
900
|
+
logCartError(
|
|
901
|
+
{
|
|
902
|
+
scope: "stock-change",
|
|
903
|
+
context: { currentStockId, newStockId }
|
|
904
|
+
},
|
|
905
|
+
e
|
|
906
|
+
);
|
|
907
|
+
notifier.warning("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u0434\u043B\u044F \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u043A\u043B\u0430\u0434\u0430");
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async function checkCartProducts() {
|
|
911
|
+
try {
|
|
912
|
+
const storeId = credentials.getStoreId();
|
|
913
|
+
const orderCredentials = credentials.getCredentials();
|
|
914
|
+
const result = await sdk.orders.checkProducts(storeId, orderCredentials);
|
|
915
|
+
if (result.action === OrderCartCheckerResultAction.Cleanup) {
|
|
916
|
+
const freshOrder = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
|
|
917
|
+
cartStore.setOrder(freshOrder);
|
|
918
|
+
await fetchTotal();
|
|
919
|
+
notifier.warning("\u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0442\u043E\u0432\u0430\u0440\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0438 \u0431\u044B\u043B\u0438 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0438\u0437 \u043A\u043E\u0440\u0437\u0438\u043D\u044B");
|
|
920
|
+
} else if (result.order_variants.length > 0) {
|
|
921
|
+
await fetchTotal();
|
|
922
|
+
notifier.warning("\u0426\u0435\u043D\u044B \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u0442\u043E\u0432\u0430\u0440\u043E\u0432 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B");
|
|
923
|
+
}
|
|
924
|
+
} catch (e) {
|
|
925
|
+
logCartError(
|
|
926
|
+
{
|
|
927
|
+
scope: "stock-products-check",
|
|
928
|
+
context: { orderId: cartStore.credentials?.id }
|
|
929
|
+
},
|
|
930
|
+
e
|
|
931
|
+
);
|
|
932
|
+
notifier.warning("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u0442\u043E\u0432\u0430\u0440\u043E\u0432 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0435");
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return { handleStockChange };
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/cart/useOrderDelivery.ts
|
|
939
|
+
function useOrderDelivery(deps = {}) {
|
|
940
|
+
const runtime = useRuntime();
|
|
941
|
+
const activity = deps.activity ?? runtime.activity;
|
|
942
|
+
const notifier = deps.notifier ?? runtime.notifier;
|
|
943
|
+
const { sdk } = runtime;
|
|
944
|
+
const cartStore = useCartStore();
|
|
945
|
+
const credentials = useCartCredentials();
|
|
946
|
+
const { handleStockChange } = useCartStockWatcher();
|
|
947
|
+
const { fetchSettings } = useOrderSettings();
|
|
948
|
+
const { recalculateAfterOrderChange } = useCartRecalculation({
|
|
949
|
+
notifier,
|
|
950
|
+
activity
|
|
951
|
+
});
|
|
952
|
+
const { run: runMutation } = useCartMutation();
|
|
953
|
+
function hasCredentials() {
|
|
954
|
+
return Boolean(cartStore.credentials?.id && cartStore.credentials?.token);
|
|
955
|
+
}
|
|
956
|
+
async function settleAfter(options) {
|
|
957
|
+
await runMutation({ kind: "delivery-settle" }, async (isStale) => {
|
|
958
|
+
activity.begin("delivery");
|
|
959
|
+
try {
|
|
960
|
+
const storeId = credentials.getStoreId();
|
|
961
|
+
const orderCredentials = credentials.getCredentials();
|
|
962
|
+
const freshOrder = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
|
|
963
|
+
if (isStale()) return;
|
|
964
|
+
cartStore.setOrder(freshOrder);
|
|
965
|
+
await fetchSettings();
|
|
966
|
+
if (isStale()) return;
|
|
967
|
+
await recalculateAfterOrderChange();
|
|
968
|
+
if (options.withStockChange) {
|
|
969
|
+
if (options.awaitStockChange) await handleStockChange(freshOrder);
|
|
970
|
+
else void handleStockChange(freshOrder);
|
|
971
|
+
}
|
|
972
|
+
} catch (e) {
|
|
973
|
+
if (!isStale()) {
|
|
974
|
+
logCartError(
|
|
975
|
+
{
|
|
976
|
+
scope: "delivery-settle",
|
|
977
|
+
context: { orderId: cartStore.credentials?.id }
|
|
978
|
+
},
|
|
979
|
+
e
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
} finally {
|
|
983
|
+
activity.end("delivery");
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
async function setDelivery(request, options = {}) {
|
|
988
|
+
const awaitSettle = options.awaitSettle ?? false;
|
|
989
|
+
if (!hasCredentials()) {
|
|
990
|
+
return { ok: false, error: new Error("cart:no_credentials") };
|
|
991
|
+
}
|
|
992
|
+
const finalRequest = request.deliver_at === void 0 && cartStore.deliveryTimeIntent ? { ...request, deliver_at: cartStore.deliveryTimeIntent } : request;
|
|
993
|
+
try {
|
|
994
|
+
await runMutation({ kind: "delivery" }, async (isStale) => {
|
|
995
|
+
const storeId = credentials.getStoreId();
|
|
996
|
+
const orderCredentials = credentials.getCredentials();
|
|
997
|
+
await sdk.orders.setOrderDeliveryMethod(storeId, orderCredentials, finalRequest);
|
|
998
|
+
if (isStale()) return;
|
|
999
|
+
});
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
logCartError(
|
|
1002
|
+
{
|
|
1003
|
+
scope: "delivery",
|
|
1004
|
+
context: {
|
|
1005
|
+
method: request.delivery_method,
|
|
1006
|
+
stockId: request.stock_id,
|
|
1007
|
+
addressId: typeof request.delivery_address === "object" && request.delivery_address !== null && "id" in request.delivery_address ? request.delivery_address.id : void 0,
|
|
1008
|
+
table: request.on_premise?.table,
|
|
1009
|
+
orderId: cartStore.credentials?.id
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
e
|
|
1013
|
+
);
|
|
1014
|
+
return { ok: false, error: e };
|
|
1015
|
+
}
|
|
1016
|
+
const settlePromise = settleAfter({ withStockChange: true, awaitStockChange: awaitSettle });
|
|
1017
|
+
if (awaitSettle) await settlePromise;
|
|
1018
|
+
return { ok: true, value: void 0 };
|
|
1019
|
+
}
|
|
1020
|
+
async function runIntervalMutation(apiCall, errorContext, awaitSettle) {
|
|
1021
|
+
if (!hasCredentials()) {
|
|
1022
|
+
return { ok: false, error: new Error("cart:no_credentials") };
|
|
1023
|
+
}
|
|
1024
|
+
try {
|
|
1025
|
+
await runMutation({ kind: "delivery" }, async (isStale) => {
|
|
1026
|
+
await apiCall();
|
|
1027
|
+
if (isStale()) return;
|
|
1028
|
+
});
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
logCartError(
|
|
1031
|
+
{
|
|
1032
|
+
scope: "delivery",
|
|
1033
|
+
context: { ...errorContext, orderId: cartStore.credentials?.id }
|
|
1034
|
+
},
|
|
1035
|
+
e
|
|
1036
|
+
);
|
|
1037
|
+
return { ok: false, error: e };
|
|
1038
|
+
}
|
|
1039
|
+
const settlePromise = settleAfter({ withStockChange: false, awaitStockChange: awaitSettle });
|
|
1040
|
+
if (awaitSettle) await settlePromise;
|
|
1041
|
+
return { ok: true, value: void 0 };
|
|
1042
|
+
}
|
|
1043
|
+
async function setDeliveryInterval(selection, options = {}) {
|
|
1044
|
+
return runIntervalMutation(
|
|
1045
|
+
() => {
|
|
1046
|
+
const storeId = credentials.getStoreId();
|
|
1047
|
+
const orderCredentials = credentials.getCredentials();
|
|
1048
|
+
return sdk.orders.setOrderDeliveryInterval(storeId, orderCredentials, {
|
|
1049
|
+
date: selection.date,
|
|
1050
|
+
interval_id: selection.id
|
|
1051
|
+
});
|
|
1052
|
+
},
|
|
1053
|
+
{ intervalDate: selection.date, intervalId: selection.id },
|
|
1054
|
+
options.awaitSettle ?? false
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
async function resetDeliveryInterval(options = {}) {
|
|
1058
|
+
return runIntervalMutation(
|
|
1059
|
+
() => {
|
|
1060
|
+
const storeId = credentials.getStoreId();
|
|
1061
|
+
const orderCredentials = credentials.getCredentials();
|
|
1062
|
+
return sdk.orders.resetOrderDeliveryInterval(storeId, orderCredentials);
|
|
1063
|
+
},
|
|
1064
|
+
{},
|
|
1065
|
+
options.awaitSettle ?? false
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
async function setDeliveryTime(deliverAt, options = {}) {
|
|
1069
|
+
return runIntervalMutation(
|
|
1070
|
+
() => {
|
|
1071
|
+
const storeId = credentials.getStoreId();
|
|
1072
|
+
const orderCredentials = credentials.getCredentials();
|
|
1073
|
+
if (typeof orderCredentials === "string") {
|
|
1074
|
+
throw new Error("cart:no_credentials");
|
|
1075
|
+
}
|
|
1076
|
+
const path = sdk.client.getStoreUrl(
|
|
1077
|
+
storeId,
|
|
1078
|
+
`/orders/${orderCredentials.id}/checkout/delivery/time`,
|
|
1079
|
+
{ token: orderCredentials.token }
|
|
1080
|
+
);
|
|
1081
|
+
return sdk.client.request(
|
|
1082
|
+
"POST",
|
|
1083
|
+
path,
|
|
1084
|
+
{ deliver_at: deliverAt },
|
|
1085
|
+
orderCredentials.api_token ?? null
|
|
1086
|
+
);
|
|
1087
|
+
},
|
|
1088
|
+
{ deliverAt },
|
|
1089
|
+
options.awaitSettle ?? false
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
setDelivery,
|
|
1094
|
+
setDeliveryInterval,
|
|
1095
|
+
resetDeliveryInterval,
|
|
1096
|
+
setDeliveryTime
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
function useOrderPromocode(deps = {}) {
|
|
1100
|
+
const runtime = useRuntime();
|
|
1101
|
+
const { sdk } = runtime;
|
|
1102
|
+
const activity = deps.activity ?? runtime.activity;
|
|
1103
|
+
const notifier = deps.notifier ?? runtime.notifier;
|
|
1104
|
+
const credentials = useCartCredentials();
|
|
1105
|
+
const cartStore = useCartStore();
|
|
1106
|
+
const profileStore = useStoreProfileStore();
|
|
1107
|
+
const { recalculateAfterOrderChange } = useCartRecalculation({
|
|
1108
|
+
notifier,
|
|
1109
|
+
activity
|
|
1110
|
+
});
|
|
1111
|
+
const { run: runMutation } = useCartMutation();
|
|
1112
|
+
const loading = ref(false);
|
|
1113
|
+
const errorMessage = ref(null);
|
|
1114
|
+
const applied = computed(() => cartStore.order?.promocode ?? null);
|
|
1115
|
+
const isApplied = computed(() => applied.value !== null);
|
|
1116
|
+
function clearError() {
|
|
1117
|
+
errorMessage.value = null;
|
|
1118
|
+
}
|
|
1119
|
+
async function refreshOrder() {
|
|
1120
|
+
const storeId = profileStore.store?.id;
|
|
1121
|
+
if (!storeId) return;
|
|
1122
|
+
if (!cartStore.credentials?.id) return;
|
|
1123
|
+
try {
|
|
1124
|
+
const orderCredentials = credentials.getCredentials();
|
|
1125
|
+
const fresh = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
|
|
1126
|
+
cartStore.setOrder(fresh);
|
|
1127
|
+
} catch (e) {
|
|
1128
|
+
logCartError(
|
|
1129
|
+
{
|
|
1130
|
+
scope: "promocode-refresh-order",
|
|
1131
|
+
context: { orderId: cartStore.credentials?.id }
|
|
1132
|
+
},
|
|
1133
|
+
e
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async function apply(code) {
|
|
1138
|
+
const trimmed = code.trim();
|
|
1139
|
+
if (!trimmed) return;
|
|
1140
|
+
const storeId = profileStore.store?.id;
|
|
1141
|
+
if (!storeId) return;
|
|
1142
|
+
if (!cartStore.credentials?.id) return;
|
|
1143
|
+
errorMessage.value = null;
|
|
1144
|
+
loading.value = true;
|
|
1145
|
+
try {
|
|
1146
|
+
await runMutation({ kind: "promocode" }, async (isStale) => {
|
|
1147
|
+
activity.begin("promocode");
|
|
1148
|
+
try {
|
|
1149
|
+
const orderCredentials = credentials.getCredentials();
|
|
1150
|
+
await sdk.orders.setOrderPromocode(storeId, orderCredentials, {
|
|
1151
|
+
promocode: trimmed
|
|
1152
|
+
});
|
|
1153
|
+
if (isStale()) return;
|
|
1154
|
+
await refreshOrder();
|
|
1155
|
+
await recalculateAfterOrderChange();
|
|
1156
|
+
} finally {
|
|
1157
|
+
activity.end("promocode");
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
} catch (e) {
|
|
1161
|
+
errorMessage.value = getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0440\u043E\u043C\u043E\u043A\u043E\u0434");
|
|
1162
|
+
logCartError(
|
|
1163
|
+
{
|
|
1164
|
+
scope: "promocode-apply",
|
|
1165
|
+
context: { code: trimmed, orderId: cartStore.credentials?.id }
|
|
1166
|
+
},
|
|
1167
|
+
e
|
|
1168
|
+
);
|
|
1169
|
+
} finally {
|
|
1170
|
+
loading.value = false;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
async function remove() {
|
|
1174
|
+
const storeId = profileStore.store?.id;
|
|
1175
|
+
if (!storeId) return;
|
|
1176
|
+
if (!cartStore.credentials?.id) return;
|
|
1177
|
+
errorMessage.value = null;
|
|
1178
|
+
loading.value = true;
|
|
1179
|
+
try {
|
|
1180
|
+
await runMutation({ kind: "promocode" }, async (isStale) => {
|
|
1181
|
+
activity.begin("promocode");
|
|
1182
|
+
try {
|
|
1183
|
+
const orderCredentials = credentials.getCredentials();
|
|
1184
|
+
await sdk.orders.removeOrderPromocode(storeId, orderCredentials);
|
|
1185
|
+
if (isStale()) return;
|
|
1186
|
+
await refreshOrder();
|
|
1187
|
+
await recalculateAfterOrderChange();
|
|
1188
|
+
} finally {
|
|
1189
|
+
activity.end("promocode");
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
errorMessage.value = getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u043F\u0440\u043E\u043C\u043E\u043A\u043E\u0434");
|
|
1194
|
+
logCartError(
|
|
1195
|
+
{
|
|
1196
|
+
scope: "promocode-remove",
|
|
1197
|
+
context: {
|
|
1198
|
+
code: applied.value?.promocode,
|
|
1199
|
+
orderId: cartStore.credentials?.id
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
e
|
|
1203
|
+
);
|
|
1204
|
+
} finally {
|
|
1205
|
+
loading.value = false;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return {
|
|
1209
|
+
applied,
|
|
1210
|
+
isApplied,
|
|
1211
|
+
loading,
|
|
1212
|
+
errorMessage,
|
|
1213
|
+
apply,
|
|
1214
|
+
remove,
|
|
1215
|
+
clearError
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
function useCartDeliveryAttachment() {
|
|
1219
|
+
const { sdk } = useRuntime();
|
|
1220
|
+
const cartStore = useCartStore();
|
|
1221
|
+
const orderSettings = useOrderSettingsStore();
|
|
1222
|
+
const profileStore = useStoreProfileStore();
|
|
1223
|
+
const authStore = useAuthStore();
|
|
1224
|
+
const { setDelivery } = useOrderDelivery();
|
|
1225
|
+
async function recoverDeliveryAddress() {
|
|
1226
|
+
const selected = orderSettings.selectedAddress;
|
|
1227
|
+
const storeId = profileStore.store?.id;
|
|
1228
|
+
if (!selected || !storeId || !authStore.isAuthenticated) return false;
|
|
1229
|
+
let addresses;
|
|
1230
|
+
try {
|
|
1231
|
+
const response = await sdk.customer.getAddresses(storeId);
|
|
1232
|
+
addresses = response.items ?? [];
|
|
1233
|
+
} catch {
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
const target = normalizeAddress(selected);
|
|
1237
|
+
const match = addresses.find((a) => normalizeAddress(a.address) === target);
|
|
1238
|
+
if (!match) return false;
|
|
1239
|
+
const result = await setDelivery({
|
|
1240
|
+
delivery_method: DeliveryMethod.Delivery,
|
|
1241
|
+
delivery_address: { id: match.id }
|
|
1242
|
+
});
|
|
1243
|
+
if (!result.ok) return false;
|
|
1244
|
+
orderSettings.setSelectedAddress(match.address, match.id, match.name);
|
|
1245
|
+
return true;
|
|
1246
|
+
}
|
|
1247
|
+
async function reattachDelivery() {
|
|
1248
|
+
const order = cartStore.order;
|
|
1249
|
+
if (!order) return;
|
|
1250
|
+
let result = null;
|
|
1251
|
+
switch (orderSettings.deliveryMethod) {
|
|
1252
|
+
case DeliveryMethod.Delivery: {
|
|
1253
|
+
if (order.delivery_address) return;
|
|
1254
|
+
const addressId = orderSettings.selectedAddressId;
|
|
1255
|
+
if (!addressId) {
|
|
1256
|
+
const recovered = await recoverDeliveryAddress();
|
|
1257
|
+
if (!recovered && orderSettings.selectedAddress) {
|
|
1258
|
+
orderSettings.setSelectedAddress(null, null, null);
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
result = await setDelivery({
|
|
1263
|
+
delivery_method: DeliveryMethod.Delivery,
|
|
1264
|
+
delivery_address: { id: addressId }
|
|
1265
|
+
});
|
|
1266
|
+
break;
|
|
1267
|
+
}
|
|
1268
|
+
case DeliveryMethod.Pickup: {
|
|
1269
|
+
if (order.stock) return;
|
|
1270
|
+
const stock = orderSettings.selectedStock;
|
|
1271
|
+
if (!stock) return;
|
|
1272
|
+
result = await setDelivery({
|
|
1273
|
+
delivery_method: DeliveryMethod.Pickup,
|
|
1274
|
+
stock_id: stock.id
|
|
1275
|
+
});
|
|
1276
|
+
break;
|
|
1277
|
+
}
|
|
1278
|
+
case DeliveryMethod.OnPremise: {
|
|
1279
|
+
if (order.stock) return;
|
|
1280
|
+
const stock = orderSettings.selectedStock;
|
|
1281
|
+
if (!stock) return;
|
|
1282
|
+
result = await setDelivery({
|
|
1283
|
+
delivery_method: DeliveryMethod.OnPremise,
|
|
1284
|
+
stock_id: stock.id,
|
|
1285
|
+
on_premise: { table: orderSettings.onPremiseTable ?? "" }
|
|
1286
|
+
});
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
if (result && !result.ok) {
|
|
1291
|
+
if (getCartErrorCode(result.error) === "addresses.not_found") {
|
|
1292
|
+
const recovered = await recoverDeliveryAddress();
|
|
1293
|
+
if (!recovered) orderSettings.setSelectedAddress(null, null, null);
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
console.error("[cart-delivery-attach] failed", result.error);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
watch(
|
|
1300
|
+
() => cartStore.order?.id,
|
|
1301
|
+
async (newId, oldId) => {
|
|
1302
|
+
if (!newId || newId === oldId) return;
|
|
1303
|
+
await reattachDelivery();
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/cart/useCartOperations.ts
|
|
1309
|
+
var RUM_MARK_CART_ADD = "rum:cart:add";
|
|
1310
|
+
var RUM_MARK_CART_REMOVE = "rum:cart:remove";
|
|
1311
|
+
var RUM_METRIC_CART_ADD = "rum_cart_add_seconds";
|
|
1312
|
+
var RUM_METRIC_CART_REMOVE = "rum_cart_remove_seconds";
|
|
1313
|
+
var BATCH_DELAY = 250;
|
|
1314
|
+
var CART_LINE_KIND_PREFIX = "cart-line-";
|
|
1315
|
+
var getOperationsState = createRuntimeState(() => ({
|
|
1316
|
+
batches: /* @__PURE__ */ new Map(),
|
|
1317
|
+
inflight: /* @__PURE__ */ new Set()
|
|
1318
|
+
}));
|
|
1319
|
+
function useCartOperations(credentials, initialization, sdk, deps) {
|
|
1320
|
+
const { notifier, activity } = deps;
|
|
1321
|
+
const runtime = useRuntime();
|
|
1322
|
+
const { events, rum, commerceEvents } = runtime;
|
|
1323
|
+
const { batches, inflight } = getOperationsState(runtime);
|
|
1324
|
+
const cartStore = useCartStore();
|
|
1325
|
+
const profileStore = useStoreProfileStore();
|
|
1326
|
+
const { run: runMutation, bumpKindsWithPrefix } = useCartMutation();
|
|
1327
|
+
const { recalculateAfterOrderChange } = useCartRecalculation({ notifier, activity });
|
|
1328
|
+
function flushBatch(lineKey) {
|
|
1329
|
+
if (inflight.has(lineKey)) return;
|
|
1330
|
+
const batch = batches.get(lineKey);
|
|
1331
|
+
if (!batch) return;
|
|
1332
|
+
batches.delete(lineKey);
|
|
1333
|
+
if (batch.delta === 0) {
|
|
1334
|
+
activity.end("cart");
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
inflight.add(lineKey);
|
|
1338
|
+
const sentDelta = batch.delta;
|
|
1339
|
+
const isAdd = sentDelta > 0;
|
|
1340
|
+
const qty = Math.abs(sentDelta);
|
|
1341
|
+
const { variantId, productId, productName, variantLabel } = batch;
|
|
1342
|
+
const execute = async () => {
|
|
1343
|
+
const kind = `${CART_LINE_KIND_PREFIX}${lineKey}`;
|
|
1344
|
+
let optimisticCorrected = false;
|
|
1345
|
+
try {
|
|
1346
|
+
await runMutation({ kind }, async (isStale) => {
|
|
1347
|
+
const orderCredentials = credentials.getCredentials();
|
|
1348
|
+
const storeId = credentials.getStoreId();
|
|
1349
|
+
const wasInCartBefore = cartStore.order?.variants?.some((v) => variantLineKey(v) === lineKey) ?? false;
|
|
1350
|
+
const variantBeforeChange = !isAdd ? cartStore.order?.variants?.find((v) => variantLineKey(v) === lineKey) ?? null : null;
|
|
1351
|
+
if (isAdd) {
|
|
1352
|
+
rum?.mark(RUM_MARK_CART_ADD);
|
|
1353
|
+
const updatedOrder = await sdk.orders.addProductVariantToOrder(
|
|
1354
|
+
storeId,
|
|
1355
|
+
orderCredentials,
|
|
1356
|
+
{
|
|
1357
|
+
product_variant_id: variantId,
|
|
1358
|
+
quantity: qty,
|
|
1359
|
+
modifiers: batch.modifiers
|
|
1360
|
+
}
|
|
1361
|
+
);
|
|
1362
|
+
rum?.measure(RUM_METRIC_CART_ADD, RUM_MARK_CART_ADD);
|
|
1363
|
+
if (isStale()) return;
|
|
1364
|
+
cartStore.patchOrder(updatedOrder);
|
|
1365
|
+
credentials.persistCredentials(updatedOrder);
|
|
1366
|
+
} else {
|
|
1367
|
+
const removeRequest = {
|
|
1368
|
+
product_variant_id: variantId,
|
|
1369
|
+
quantity: qty,
|
|
1370
|
+
modifiers: batch.modifiers,
|
|
1371
|
+
...batch.promotionId ? {
|
|
1372
|
+
promotion_id: batch.promotionId,
|
|
1373
|
+
promotion_reward_id: batch.promotionRewardId
|
|
1374
|
+
} : {}
|
|
1375
|
+
};
|
|
1376
|
+
rum?.mark(RUM_MARK_CART_REMOVE);
|
|
1377
|
+
const updatedOrder = await sdk.orders.removeProductVariantFromOrder(
|
|
1378
|
+
storeId,
|
|
1379
|
+
orderCredentials,
|
|
1380
|
+
removeRequest
|
|
1381
|
+
);
|
|
1382
|
+
rum?.measure(RUM_METRIC_CART_REMOVE, RUM_MARK_CART_REMOVE);
|
|
1383
|
+
if (isStale()) return;
|
|
1384
|
+
cartStore.patchOrder(updatedOrder);
|
|
1385
|
+
}
|
|
1386
|
+
cartStore.adjustOptimistic(productId, -sentDelta);
|
|
1387
|
+
cartStore.adjustLineOptimistic(lineKey, -sentDelta);
|
|
1388
|
+
optimisticCorrected = true;
|
|
1389
|
+
const integrations = events?.get() ?? null;
|
|
1390
|
+
const orderVariant = cartStore.order?.variants?.find((v) => variantLineKey(v) === lineKey) ?? null;
|
|
1391
|
+
if (isAdd) {
|
|
1392
|
+
if (!wasInCartBefore) {
|
|
1393
|
+
integrations?.productAddedToCart({
|
|
1394
|
+
order: cartStore.order,
|
|
1395
|
+
orderVariant
|
|
1396
|
+
});
|
|
1397
|
+
} else {
|
|
1398
|
+
integrations?.productQuantityIncreased({
|
|
1399
|
+
order: cartStore.order,
|
|
1400
|
+
orderVariant
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
} else {
|
|
1404
|
+
const isNowInCart = cartStore.order?.variants?.some((v) => variantLineKey(v) === lineKey) ?? false;
|
|
1405
|
+
if (!isNowInCart) {
|
|
1406
|
+
const product = variantBeforeChange?.product ?? null;
|
|
1407
|
+
integrations?.productRemovedFromCart({
|
|
1408
|
+
order: cartStore.order,
|
|
1409
|
+
product
|
|
1410
|
+
});
|
|
1411
|
+
} else {
|
|
1412
|
+
integrations?.productQuantityDecreased({
|
|
1413
|
+
order: cartStore.order,
|
|
1414
|
+
orderVariant
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
const commerceOrderId = cartStore.credentials?.id;
|
|
1419
|
+
if (commerceOrderId && commerceEvents) {
|
|
1420
|
+
if (isAdd) {
|
|
1421
|
+
const currency = profileStore.currency;
|
|
1422
|
+
const cartTotal = cartStore.order?.total_price;
|
|
1423
|
+
commerceEvents.trackAddedToCart(commerceOrderId, {
|
|
1424
|
+
productId,
|
|
1425
|
+
productVariantId: variantId,
|
|
1426
|
+
quantity: qty,
|
|
1427
|
+
unitPrice: orderVariant ? minorToMajor(orderVariant.unit_price, currency) : void 0,
|
|
1428
|
+
totalPrice: cartTotal != null ? minorToMajor(cartTotal, currency) : void 0
|
|
1429
|
+
});
|
|
1430
|
+
} else {
|
|
1431
|
+
commerceEvents.trackRemovedFromCart(commerceOrderId, {
|
|
1432
|
+
productId,
|
|
1433
|
+
productVariantId: variantId,
|
|
1434
|
+
quantity: qty
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
await recalculateAfterOrderChange();
|
|
1439
|
+
if (isAdd && productName) {
|
|
1440
|
+
const parts = [productName, variantLabel].filter(Boolean);
|
|
1441
|
+
notifier.message("\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E:", { description: parts.join(", ") });
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
} catch (e) {
|
|
1445
|
+
logCartError(
|
|
1446
|
+
{
|
|
1447
|
+
scope: isAdd ? "cart-add" : "cart-remove",
|
|
1448
|
+
context: {
|
|
1449
|
+
lineKey,
|
|
1450
|
+
variantId,
|
|
1451
|
+
productId,
|
|
1452
|
+
qty,
|
|
1453
|
+
modifiers: batch.modifiers?.length ?? 0,
|
|
1454
|
+
promotionId: batch.promotionId,
|
|
1455
|
+
orderId: cartStore.credentials?.id
|
|
1456
|
+
}
|
|
1457
|
+
},
|
|
1458
|
+
e
|
|
1459
|
+
);
|
|
1460
|
+
const integrations = events?.get() ?? null;
|
|
1461
|
+
if (isAdd) {
|
|
1462
|
+
integrations?.productQuantityIncreaseFailed({ order: cartStore.order });
|
|
1463
|
+
} else {
|
|
1464
|
+
integrations?.productQuantityDecreaseFailed({ order: cartStore.order });
|
|
1465
|
+
}
|
|
1466
|
+
notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u043E\u0440\u0437\u0438\u043D\u0443"));
|
|
1467
|
+
try {
|
|
1468
|
+
await refreshCart({ silent: true });
|
|
1469
|
+
} catch (refreshErr) {
|
|
1470
|
+
logCartError(
|
|
1471
|
+
{ scope: "cart-refresh-after-fail", context: { lineKey, variantId } },
|
|
1472
|
+
refreshErr
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
} finally {
|
|
1476
|
+
if (!optimisticCorrected) {
|
|
1477
|
+
cartStore.adjustOptimistic(productId, -sentDelta);
|
|
1478
|
+
cartStore.adjustLineOptimistic(lineKey, -sentDelta);
|
|
1479
|
+
}
|
|
1480
|
+
activity.end("cart");
|
|
1481
|
+
inflight.delete(lineKey);
|
|
1482
|
+
if (batches.has(lineKey)) flushBatch(lineKey);
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
execute();
|
|
1486
|
+
}
|
|
1487
|
+
function enqueueDelta(request, delta) {
|
|
1488
|
+
const lineKey = buildCartLineKey({
|
|
1489
|
+
productVariantId: request.productVariantId,
|
|
1490
|
+
modifiers: request.modifiers,
|
|
1491
|
+
promotionId: request.promotionId,
|
|
1492
|
+
promotionRewardId: request.promotionRewardId
|
|
1493
|
+
});
|
|
1494
|
+
cartStore.adjustOptimistic(request.productId, delta);
|
|
1495
|
+
cartStore.adjustLineOptimistic(lineKey, delta);
|
|
1496
|
+
const existing = batches.get(lineKey);
|
|
1497
|
+
if (existing) {
|
|
1498
|
+
clearTimeout(existing.timer);
|
|
1499
|
+
existing.delta += delta;
|
|
1500
|
+
existing.timer = setTimeout(() => flushBatch(lineKey), BATCH_DELAY);
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
activity.begin("cart");
|
|
1504
|
+
const timer = setTimeout(() => flushBatch(lineKey), BATCH_DELAY);
|
|
1505
|
+
batches.set(lineKey, {
|
|
1506
|
+
lineKey,
|
|
1507
|
+
variantId: request.productVariantId,
|
|
1508
|
+
productId: request.productId,
|
|
1509
|
+
delta,
|
|
1510
|
+
modifiers: request.modifiers,
|
|
1511
|
+
promotionId: request.promotionId,
|
|
1512
|
+
promotionRewardId: request.promotionRewardId,
|
|
1513
|
+
productName: request.productName,
|
|
1514
|
+
variantLabel: request.variantLabel,
|
|
1515
|
+
timer
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
async function addToCart(request) {
|
|
1519
|
+
if (cartStore.initializing || !cartStore.hasCredentials) {
|
|
1520
|
+
throw new Error("cart:not_ready");
|
|
1521
|
+
}
|
|
1522
|
+
enqueueDelta(request, request.quantity ?? 1);
|
|
1523
|
+
}
|
|
1524
|
+
async function removeFromCart(request) {
|
|
1525
|
+
if (!cartStore.hasCredentials) return;
|
|
1526
|
+
enqueueDelta(request, -(request.quantity ?? 1));
|
|
1527
|
+
}
|
|
1528
|
+
async function refreshCart(options) {
|
|
1529
|
+
if (!cartStore.hasCredentials) return;
|
|
1530
|
+
const silent = options?.silent ?? false;
|
|
1531
|
+
if (!silent) {
|
|
1532
|
+
cartStore.setLoading(true);
|
|
1533
|
+
}
|
|
1534
|
+
try {
|
|
1535
|
+
const orderCredentials = credentials.getCredentials();
|
|
1536
|
+
const storeId = credentials.getStoreId();
|
|
1537
|
+
const freshOrder = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
|
|
1538
|
+
cartStore.setOrder(freshOrder);
|
|
1539
|
+
} catch (e) {
|
|
1540
|
+
logCartError(
|
|
1541
|
+
{
|
|
1542
|
+
scope: "cart-refresh",
|
|
1543
|
+
context: { orderId: cartStore.credentials?.id, silent }
|
|
1544
|
+
},
|
|
1545
|
+
e
|
|
1546
|
+
);
|
|
1547
|
+
cartStore.clearCart();
|
|
1548
|
+
credentials.clearPersistedCredentials();
|
|
1549
|
+
} finally {
|
|
1550
|
+
if (!silent) {
|
|
1551
|
+
cartStore.setLoading(false);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function cancelPendingCartMutations() {
|
|
1556
|
+
for (const batch of batches.values()) clearTimeout(batch.timer);
|
|
1557
|
+
batches.clear();
|
|
1558
|
+
inflight.clear();
|
|
1559
|
+
bumpKindsWithPrefix(CART_LINE_KIND_PREFIX);
|
|
1560
|
+
activity.end("cart");
|
|
1561
|
+
}
|
|
1562
|
+
function clearAndResetCart() {
|
|
1563
|
+
cancelPendingCartMutations();
|
|
1564
|
+
credentials.clearPersistedCredentials();
|
|
1565
|
+
cartStore.clearCart();
|
|
1566
|
+
initialization.initializeOrder().catch((e) => {
|
|
1567
|
+
logCartError({ scope: "cart-reinit" }, e);
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
addToCart,
|
|
1572
|
+
removeFromCart,
|
|
1573
|
+
refreshCart,
|
|
1574
|
+
clearAndResetCart
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
function useCheckoutSubmit(getRecaptchaPayload) {
|
|
1578
|
+
const { sdk, activity, events, commerceEvents } = useRuntime();
|
|
1579
|
+
const credentials = useCartCredentials();
|
|
1580
|
+
const cartStore = useCartStore();
|
|
1581
|
+
const profileStore = useStoreProfileStore();
|
|
1582
|
+
const result = ref(null);
|
|
1583
|
+
const submitting = ref(false);
|
|
1584
|
+
async function runBusy(fn) {
|
|
1585
|
+
activity.begin("custom");
|
|
1586
|
+
try {
|
|
1587
|
+
return await fn();
|
|
1588
|
+
} finally {
|
|
1589
|
+
activity.end("custom");
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
async function submit() {
|
|
1593
|
+
const storeId = profileStore.store?.id;
|
|
1594
|
+
if (!storeId) return null;
|
|
1595
|
+
if (!cartStore.credentials?.id) return null;
|
|
1596
|
+
submitting.value = true;
|
|
1597
|
+
try {
|
|
1598
|
+
return await runBusy(async () => {
|
|
1599
|
+
const orderCredentials = credentials.getCredentials();
|
|
1600
|
+
const recaptcha = await getRecaptchaPayload(RecaptchaAction.OrderSubmission);
|
|
1601
|
+
const integrations = events?.get() ?? null;
|
|
1602
|
+
const analyticsData = integrations ? await integrations.analytics.getAll() : null;
|
|
1603
|
+
const request = {
|
|
1604
|
+
notes: cartStore.comment || void 0,
|
|
1605
|
+
persons_count: cartStore.personsCount,
|
|
1606
|
+
recaptcha,
|
|
1607
|
+
...commerceEvents?.getIdentity() ?? {},
|
|
1608
|
+
...analyticsData?.length ? { analytics_data: analyticsData } : {}
|
|
1609
|
+
};
|
|
1610
|
+
const cartVariants = cartStore.order?.variants ?? [];
|
|
1611
|
+
const res = await sdk.orders.checkoutOrder(storeId, orderCredentials, request);
|
|
1612
|
+
const orderForEvent = { ...res.order, variants: res.order.variants ?? cartVariants };
|
|
1613
|
+
integrations?.orderSubmitted({ order: orderForEvent });
|
|
1614
|
+
result.value = res;
|
|
1615
|
+
return res;
|
|
1616
|
+
});
|
|
1617
|
+
} catch (e) {
|
|
1618
|
+
logCartError(
|
|
1619
|
+
{
|
|
1620
|
+
scope: "checkout-submit",
|
|
1621
|
+
context: {
|
|
1622
|
+
orderId: cartStore.credentials?.id,
|
|
1623
|
+
notes: cartStore.comment ? cartStore.comment.length : 0,
|
|
1624
|
+
personsCount: cartStore.personsCount
|
|
1625
|
+
}
|
|
1626
|
+
},
|
|
1627
|
+
e
|
|
1628
|
+
);
|
|
1629
|
+
throw e;
|
|
1630
|
+
} finally {
|
|
1631
|
+
submitting.value = false;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
async function confirm(code) {
|
|
1635
|
+
const storeId = profileStore.store?.id;
|
|
1636
|
+
if (!storeId) return false;
|
|
1637
|
+
if (!cartStore.credentials?.id) return false;
|
|
1638
|
+
try {
|
|
1639
|
+
return await runBusy(async () => {
|
|
1640
|
+
const orderCredentials = credentials.getCredentials();
|
|
1641
|
+
return await sdk.orders.confirmOrder(storeId, orderCredentials, { code });
|
|
1642
|
+
});
|
|
1643
|
+
} catch (e) {
|
|
1644
|
+
logCartError(
|
|
1645
|
+
{
|
|
1646
|
+
scope: "checkout-confirm",
|
|
1647
|
+
context: { orderId: cartStore.credentials?.id, codeLength: code.length }
|
|
1648
|
+
},
|
|
1649
|
+
e
|
|
1650
|
+
);
|
|
1651
|
+
throw e;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
async function resendCode() {
|
|
1655
|
+
const storeId = profileStore.store?.id;
|
|
1656
|
+
if (!storeId) return false;
|
|
1657
|
+
if (!cartStore.credentials?.id) return false;
|
|
1658
|
+
try {
|
|
1659
|
+
const orderCredentials = credentials.getCredentials();
|
|
1660
|
+
return await sdk.orders.resendOrderConfirmationCode(storeId, orderCredentials);
|
|
1661
|
+
} catch (e) {
|
|
1662
|
+
logCartError(
|
|
1663
|
+
{
|
|
1664
|
+
scope: "checkout-resend-code",
|
|
1665
|
+
context: { orderId: cartStore.credentials?.id }
|
|
1666
|
+
},
|
|
1667
|
+
e
|
|
1668
|
+
);
|
|
1669
|
+
throw e;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
async function cancel() {
|
|
1673
|
+
const storeId = profileStore.store?.id;
|
|
1674
|
+
if (!storeId) return false;
|
|
1675
|
+
if (!cartStore.credentials?.id) return false;
|
|
1676
|
+
try {
|
|
1677
|
+
const orderCredentials = credentials.getCredentials();
|
|
1678
|
+
const ok = await sdk.orders.cancelOrder(storeId, orderCredentials);
|
|
1679
|
+
if (ok) {
|
|
1680
|
+
result.value = null;
|
|
1681
|
+
credentials.clearPersistedCredentials();
|
|
1682
|
+
cartStore.clearCart();
|
|
1683
|
+
}
|
|
1684
|
+
return ok;
|
|
1685
|
+
} catch (e) {
|
|
1686
|
+
logCartError(
|
|
1687
|
+
{ scope: "checkout-cancel", context: { orderId: cartStore.credentials?.id } },
|
|
1688
|
+
e
|
|
1689
|
+
);
|
|
1690
|
+
throw e;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return { result, submitting, submit, confirm, resendCode, cancel };
|
|
1694
|
+
}
|
|
1695
|
+
function useStorefrontCart() {
|
|
1696
|
+
const { sdk, notifier, activity } = useRuntime();
|
|
1697
|
+
const cartStore = useCartStore();
|
|
1698
|
+
const { order, loading, initializing } = storeToRefs(cartStore);
|
|
1699
|
+
const variants = computed(() => cartStore.variants);
|
|
1700
|
+
const itemCount = computed(() => cartStore.itemCount);
|
|
1701
|
+
const totalPrice = computed(() => cartStore.totalPrice);
|
|
1702
|
+
const hasOrder = computed(() => cartStore.hasOrder);
|
|
1703
|
+
const credentials = useCartCredentials();
|
|
1704
|
+
const { getProductQuantity, getVariantQuantity } = useCartQuantities();
|
|
1705
|
+
const { initializeOrder } = useCartInitialization(credentials, sdk);
|
|
1706
|
+
const { addToCart, removeFromCart, refreshCart, clearAndResetCart } = useCartOperations(
|
|
1707
|
+
credentials,
|
|
1708
|
+
{ initializeOrder },
|
|
1709
|
+
sdk,
|
|
1710
|
+
{ notifier, activity }
|
|
1711
|
+
);
|
|
1712
|
+
const { recalculateAfterOrderChange } = useCartRecalculation({ notifier, activity });
|
|
1713
|
+
return {
|
|
1714
|
+
order,
|
|
1715
|
+
variants,
|
|
1716
|
+
itemCount,
|
|
1717
|
+
totalPrice,
|
|
1718
|
+
loading,
|
|
1719
|
+
initializing,
|
|
1720
|
+
hasOrder,
|
|
1721
|
+
getProductQuantity,
|
|
1722
|
+
getVariantQuantity,
|
|
1723
|
+
initializeOrder,
|
|
1724
|
+
addToCart,
|
|
1725
|
+
removeFromCart,
|
|
1726
|
+
refreshCart,
|
|
1727
|
+
clearAndResetCart,
|
|
1728
|
+
recalculate: recalculateAfterOrderChange
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
// src/cart/useOrderChannel.ts
|
|
1733
|
+
var getOrderChannelState = createRuntimeState(() => ({
|
|
1734
|
+
currentChannel: null,
|
|
1735
|
+
currentChannelName: null,
|
|
1736
|
+
initialized: false,
|
|
1737
|
+
priceReconcileTimer: null
|
|
1738
|
+
}));
|
|
1739
|
+
function useOrderChannel() {
|
|
1740
|
+
const runtime = useRuntime();
|
|
1741
|
+
const state = getOrderChannelState(runtime);
|
|
1742
|
+
if (state.initialized) return;
|
|
1743
|
+
const { realtime, notifier, events } = runtime;
|
|
1744
|
+
const resolvedRealtime = realtime?.get() ?? null;
|
|
1745
|
+
if (!resolvedRealtime) return;
|
|
1746
|
+
const rt = resolvedRealtime;
|
|
1747
|
+
state.initialized = true;
|
|
1748
|
+
const cartStore = useCartStore();
|
|
1749
|
+
const promotionsStore = usePromotionsStore();
|
|
1750
|
+
const { refreshCart } = useStorefrontCart();
|
|
1751
|
+
const { fetchTotal } = useOrderTotal();
|
|
1752
|
+
const { reconcileAfterOrderChange } = useBonusesReconciliation({ notifier });
|
|
1753
|
+
function getChannelName() {
|
|
1754
|
+
const creds = cartStore.credentials;
|
|
1755
|
+
if (!creds?.id || !creds?.token) return null;
|
|
1756
|
+
return `orders.${creds.id}.${creds.token}`;
|
|
1757
|
+
}
|
|
1758
|
+
function subscribeToOrder() {
|
|
1759
|
+
const channelName = getChannelName();
|
|
1760
|
+
if (channelName === state.currentChannelName) return;
|
|
1761
|
+
if (state.currentChannelName) {
|
|
1762
|
+
rt.unsubscribe(state.currentChannelName);
|
|
1763
|
+
state.currentChannel = null;
|
|
1764
|
+
state.currentChannelName = null;
|
|
1765
|
+
}
|
|
1766
|
+
if (!channelName) return;
|
|
1767
|
+
state.currentChannel = rt.subscribe(channelName);
|
|
1768
|
+
state.currentChannelName = channelName;
|
|
1769
|
+
if (!state.currentChannel) return;
|
|
1770
|
+
state.currentChannel.bind(ORDER_EVENTS.PriceRecalculated, () => {
|
|
1771
|
+
events?.get()?.orderPriceRecalculated({ order: cartStore.order });
|
|
1772
|
+
if (state.priceReconcileTimer) clearTimeout(state.priceReconcileTimer);
|
|
1773
|
+
state.priceReconcileTimer = setTimeout(() => {
|
|
1774
|
+
state.priceReconcileTimer = null;
|
|
1775
|
+
void fetchTotal().then(() => reconcileAfterOrderChange());
|
|
1776
|
+
}, 150);
|
|
1777
|
+
});
|
|
1778
|
+
state.currentChannel.bind(ORDER_EVENTS.Reload, () => {
|
|
1779
|
+
events?.get()?.orderShouldBeReloaded({ order: cartStore.order });
|
|
1780
|
+
void refreshCart({ silent: true }).then(() => fetchTotal());
|
|
1781
|
+
});
|
|
1782
|
+
state.currentChannel.bind(ORDER_EVENTS.Repeated, () => {
|
|
1783
|
+
});
|
|
1784
|
+
state.currentChannel.bind(ORDER_EVENTS.PromotionsNotFound, () => {
|
|
1785
|
+
const integrations = events?.get() ?? null;
|
|
1786
|
+
integrations?.orderPromotionsNotFound({ order: cartStore.order });
|
|
1787
|
+
integrations?.promotionsCheckerCompleted({ order: cartStore.order });
|
|
1788
|
+
promotionsStore.setChecking(false);
|
|
1789
|
+
promotionsStore.clearRewards();
|
|
1790
|
+
});
|
|
1791
|
+
state.currentChannel.bind(ORDER_EVENTS.RewardsDisplaySelection, (data) => {
|
|
1792
|
+
if (!data || typeof data !== "object") return;
|
|
1793
|
+
const payload = data;
|
|
1794
|
+
if (typeof payload.promotion_id === "string" && typeof payload.reward_id === "string") {
|
|
1795
|
+
const integrations = events?.get() ?? null;
|
|
1796
|
+
integrations?.displayOrderPromotionRewardSelection({
|
|
1797
|
+
order: cartStore.order,
|
|
1798
|
+
selection: payload
|
|
1799
|
+
});
|
|
1800
|
+
integrations?.promotionsCheckerCompleted({ order: cartStore.order });
|
|
1801
|
+
promotionsStore.setChecking(false);
|
|
1802
|
+
promotionsStore.addOrUpdateReward({
|
|
1803
|
+
offerId: payload.offer?.id ?? "",
|
|
1804
|
+
promotionId: payload.promotion_id,
|
|
1805
|
+
rewardId: payload.reward_id,
|
|
1806
|
+
context: payload.context?.type != null && payload.context?.id != null ? { type: payload.context.type, id: payload.context.id } : null,
|
|
1807
|
+
limit: payload.limit ?? 1
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
state.currentChannel.bind(ORDER_EVENTS.RewardsReload, () => {
|
|
1812
|
+
events?.get()?.orderPromotionRewardsShouldBeReloaded({
|
|
1813
|
+
order: cartStore.order
|
|
1814
|
+
});
|
|
1815
|
+
void refreshCart({ silent: true }).then(() => fetchTotal());
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
watchEffect(() => {
|
|
1819
|
+
if (rt.isReady.value) {
|
|
1820
|
+
subscribeToOrder();
|
|
1821
|
+
}
|
|
1822
|
+
});
|
|
1823
|
+
if (getCurrentScope()) {
|
|
1824
|
+
onScopeDispose(() => {
|
|
1825
|
+
if (state.priceReconcileTimer) {
|
|
1826
|
+
clearTimeout(state.priceReconcileTimer);
|
|
1827
|
+
state.priceReconcileTimer = null;
|
|
1828
|
+
}
|
|
1829
|
+
if (state.currentChannelName) {
|
|
1830
|
+
rt.unsubscribe(state.currentChannelName);
|
|
1831
|
+
}
|
|
1832
|
+
state.currentChannel = null;
|
|
1833
|
+
state.currentChannelName = null;
|
|
1834
|
+
state.initialized = false;
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useStorefrontCart, variantLineKey };
|
|
1840
|
+
//# sourceMappingURL=chunk-OQLU7FSR.js.map
|
|
1841
|
+
//# sourceMappingURL=chunk-OQLU7FSR.js.map
|