@zenky/storefront-vue 0.9.47 → 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.
Files changed (94) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +163 -6
  3. package/dist/cart/index.d.ts +559 -0
  4. package/dist/cart/index.js +13 -0
  5. package/dist/cart/index.js.map +1 -0
  6. package/dist/catalog/index.d.ts +73 -0
  7. package/dist/catalog/index.js +4 -0
  8. package/dist/catalog/index.js.map +1 -0
  9. package/dist/chunk-2OD7CLKD.js +102 -0
  10. package/dist/chunk-2OD7CLKD.js.map +1 -0
  11. package/dist/chunk-4MJKN7PL.js +111 -0
  12. package/dist/chunk-4MJKN7PL.js.map +1 -0
  13. package/dist/chunk-5QCUH3FF.js +15 -0
  14. package/dist/chunk-5QCUH3FF.js.map +1 -0
  15. package/dist/chunk-AL5W5JSD.js +41 -0
  16. package/dist/chunk-AL5W5JSD.js.map +1 -0
  17. package/dist/chunk-BIGTKVC6.js +38 -0
  18. package/dist/chunk-BIGTKVC6.js.map +1 -0
  19. package/dist/chunk-BWUKMWHL.js +45 -0
  20. package/dist/chunk-BWUKMWHL.js.map +1 -0
  21. package/dist/chunk-DKWDKVXQ.js +91 -0
  22. package/dist/chunk-DKWDKVXQ.js.map +1 -0
  23. package/dist/chunk-EMGQXBU3.js +3 -0
  24. package/dist/chunk-EMGQXBU3.js.map +1 -0
  25. package/dist/chunk-EYRCAUUP.js +45 -0
  26. package/dist/chunk-EYRCAUUP.js.map +1 -0
  27. package/dist/chunk-GW2QNTAD.js +327 -0
  28. package/dist/chunk-GW2QNTAD.js.map +1 -0
  29. package/dist/chunk-IGDDWGS4.js +557 -0
  30. package/dist/chunk-IGDDWGS4.js.map +1 -0
  31. package/dist/chunk-OQLU7FSR.js +1841 -0
  32. package/dist/chunk-OQLU7FSR.js.map +1 -0
  33. package/dist/chunk-PJ2LEKW6.js +229 -0
  34. package/dist/chunk-PJ2LEKW6.js.map +1 -0
  35. package/dist/chunk-PWHIDGKW.js +97 -0
  36. package/dist/chunk-PWHIDGKW.js.map +1 -0
  37. package/dist/chunk-RO25KNM5.js +36 -0
  38. package/dist/chunk-RO25KNM5.js.map +1 -0
  39. package/dist/chunk-V3VVDQIR.js +27 -0
  40. package/dist/chunk-V3VVDQIR.js.map +1 -0
  41. package/dist/chunk-VL3T36VM.js +386 -0
  42. package/dist/chunk-VL3T36VM.js.map +1 -0
  43. package/dist/chunk-XUGXLJZB.js +14 -0
  44. package/dist/chunk-XUGXLJZB.js.map +1 -0
  45. package/dist/chunk-Z3IW4ODZ.js +36 -0
  46. package/dist/chunk-Z3IW4ODZ.js.map +1 -0
  47. package/dist/index.d.ts +26 -0
  48. package/dist/index.js +21 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/layout/index.d.ts +54 -0
  51. package/dist/layout/index.js +102 -0
  52. package/dist/layout/index.js.map +1 -0
  53. package/dist/money/index.d.ts +26 -0
  54. package/dist/money/index.js +3 -0
  55. package/dist/money/index.js.map +1 -0
  56. package/dist/order/index.d.ts +34 -0
  57. package/dist/order/index.js +209 -0
  58. package/dist/order/index.js.map +1 -0
  59. package/dist/pagination/index.d.ts +87 -0
  60. package/dist/pagination/index.js +97 -0
  61. package/dist/pagination/index.js.map +1 -0
  62. package/dist/ports/index.d.ts +124 -0
  63. package/dist/ports/index.js +3 -0
  64. package/dist/ports/index.js.map +1 -0
  65. package/dist/products/index.d.ts +105 -0
  66. package/dist/products/index.js +7 -0
  67. package/dist/products/index.js.map +1 -0
  68. package/dist/promotions/index.d.ts +10 -0
  69. package/dist/promotions/index.js +7 -0
  70. package/dist/promotions/index.js.map +1 -0
  71. package/dist/schedule-Dd3zwHEb.d.ts +27 -0
  72. package/dist/seo/index.d.ts +91 -0
  73. package/dist/seo/index.js +289 -0
  74. package/dist/seo/index.js.map +1 -0
  75. package/dist/snippets/index.d.ts +17 -0
  76. package/dist/snippets/index.js +3 -0
  77. package/dist/snippets/index.js.map +1 -0
  78. package/dist/snippets-BC5bkSjm.d.ts +23 -0
  79. package/dist/stores/index.d.ts +196 -0
  80. package/dist/stores/index.js +8 -0
  81. package/dist/stores/index.js.map +1 -0
  82. package/dist/theme/index.d.ts +60 -0
  83. package/dist/theme/index.js +3 -0
  84. package/dist/theme/index.js.map +1 -0
  85. package/dist/utils/index.d.ts +66 -0
  86. package/dist/utils/index.js +4 -0
  87. package/dist/utils/index.js.map +1 -0
  88. package/dist/website-settings/index.d.ts +38 -0
  89. package/dist/website-settings/index.js +3 -0
  90. package/dist/website-settings/index.js.map +1 -0
  91. package/package.json +121 -47
  92. package/dist/cjs/index.cjs +0 -3742
  93. package/dist/esm/index.mjs +0 -3642
  94. 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