@zenky/storefront-vue 0.9.38 → 0.9.41

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/dist/cjs/index.js CHANGED
@@ -8,6 +8,7 @@ var api = require('@zenky/api');
8
8
  var pinia = require('pinia');
9
9
  var ui = require('@zenky/ui');
10
10
  var events = require('@zenky/events');
11
+ var dateFns = require('date-fns');
11
12
  var Pusher = require('pusher-js');
12
13
 
13
14
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -1556,6 +1557,1348 @@ function useOfferItem(errorHandler) {
1556
1557
  }));
1557
1558
  }
1558
1559
 
1560
+ function getUpdatedVariants(currentOrder, order) {
1561
+ var _a, _b;
1562
+ if (!((_a = order === null || order === void 0 ? void 0 : order.variants) === null || _a === void 0 ? void 0 : _a.length)) {
1563
+ return [];
1564
+ }
1565
+ else if (!((_b = currentOrder === null || currentOrder === void 0 ? void 0 : currentOrder.variants) === null || _b === void 0 ? void 0 : _b.length)) {
1566
+ return (order === null || order === void 0 ? void 0 : order.variants) || [];
1567
+ }
1568
+ let current = lodashEs.cloneDeep(currentOrder.variants);
1569
+ let currentVariants = [];
1570
+ const currentlyHasVariants = current && current.length > 0;
1571
+ if (currentlyHasVariants) {
1572
+ currentVariants = current.map((variant) => variant.uuid);
1573
+ }
1574
+ const actualVariantIds = order.variants.map((variant) => variant.uuid);
1575
+ const createdVariantIds = order.variants
1576
+ .filter((variant) => currentVariants.indexOf(variant.uuid) === -1)
1577
+ .map((variant) => variant.uuid);
1578
+ if (currentlyHasVariants) {
1579
+ current = current.filter((variant) => actualVariantIds.indexOf(variant.uuid) !== -1);
1580
+ }
1581
+ if (createdVariantIds.length > 0) {
1582
+ current = current || [];
1583
+ order.variants.filter((variant) => createdVariantIds.indexOf(variant.uuid) !== -1)
1584
+ .forEach((variant) => current.push(variant));
1585
+ }
1586
+ order.variants.forEach((variant) => {
1587
+ const index = current.findIndex((item) => item.uuid === variant.uuid);
1588
+ if (index === -1) {
1589
+ return;
1590
+ }
1591
+ current[index].quantity = variant.quantity;
1592
+ current[index].original_total_price = variant.original_total_price;
1593
+ current[index].total_price = variant.total_price;
1594
+ current[index].discount_difference = variant.discount_difference;
1595
+ current[index].has_discount = variant.has_discount;
1596
+ });
1597
+ return current;
1598
+ }
1599
+
1600
+ function initializeOrderCustomer(credentialsResolver) {
1601
+ events.useCustomerAuthenticated().subscribe('order.customer.initialize', async ({ customer }) => {
1602
+ const credentials = credentialsResolver();
1603
+ if (credentials === null) {
1604
+ return;
1605
+ }
1606
+ try {
1607
+ await api.setOrderCheckoutCustomer(credentials, {
1608
+ phone: {
1609
+ number: customer.phone.e164,
1610
+ country: customer.phone.country,
1611
+ },
1612
+ });
1613
+ }
1614
+ catch (e) {
1615
+ console.error('[orders/customer] Unable to attach customer to order.', { e });
1616
+ }
1617
+ });
1618
+ }
1619
+
1620
+ const useOrderStore = pinia.defineStore({
1621
+ id: 'zenky:storefront:order',
1622
+ state: () => ({
1623
+ order: null,
1624
+ token: null,
1625
+ loading: false,
1626
+ inclusions: null,
1627
+ orderSettings: {
1628
+ loaded: false,
1629
+ loading: false,
1630
+ settings: null,
1631
+ },
1632
+ }),
1633
+ getters: {
1634
+ credentials() {
1635
+ if (!this.order || !this.order.id || this.token === null) {
1636
+ return null;
1637
+ }
1638
+ return {
1639
+ id: this.order.id,
1640
+ token: this.token,
1641
+ };
1642
+ },
1643
+ settings() {
1644
+ return this.orderSettings.settings;
1645
+ },
1646
+ settingsLoaded() {
1647
+ return this.orderSettings.loaded;
1648
+ },
1649
+ },
1650
+ actions: {
1651
+ async initialize(storeId, cityId, inclusions) {
1652
+ this.inclusions = inclusions;
1653
+ const storage = getStorage();
1654
+ const orderId = storage.get('order.id');
1655
+ const orderToken = storage.get('order.token');
1656
+ initializeOrderCustomer(() => this.credentials);
1657
+ this.loading = true;
1658
+ if (orderId && orderToken) {
1659
+ return this.load(storeId, cityId, orderId, orderToken);
1660
+ }
1661
+ return this.create(storeId, cityId);
1662
+ },
1663
+ async load(storeId, cityId, orderId, token) {
1664
+ var _a, _b, _c;
1665
+ const { item, load } = useOrderItem();
1666
+ await load({ id: orderId, token }, this.inclusions !== null ? { with: this.inclusions.order } : {});
1667
+ if (((_c = (_b = (_a = item.value) === null || _a === void 0 ? void 0 : _a.current_status) === null || _b === void 0 ? void 0 : _b.status) === null || _c === void 0 ? void 0 : _c.kind.id) === 'pending') {
1668
+ this.order = item.value;
1669
+ this.token = item.value.token;
1670
+ this.loading = false;
1671
+ events.useOrderLoaded().publish(this.order);
1672
+ await this.loadSettings();
1673
+ return true;
1674
+ }
1675
+ return this.create(storeId, cityId);
1676
+ },
1677
+ async loadSettings() {
1678
+ if (this.orderSettings.loading) {
1679
+ return;
1680
+ }
1681
+ else if (!this.credentials || !this.order || !this.order.city_id) {
1682
+ return;
1683
+ }
1684
+ this.orderSettings.loading = true;
1685
+ try {
1686
+ this.orderSettings.settings = await api.getOrderSettings(this.credentials, this.order.city_id);
1687
+ }
1688
+ catch (e) {
1689
+ console.error('[storefront/order] Unable to load order settings.', { e });
1690
+ }
1691
+ finally {
1692
+ this.orderSettings.loading = false;
1693
+ this.orderSettings.loaded = true;
1694
+ }
1695
+ },
1696
+ async create(storeId, cityId) {
1697
+ try {
1698
+ const order = await api.createOrder({ city_id: cityId });
1699
+ const storage = getStorage();
1700
+ storage.set('order.id', order.id);
1701
+ storage.set('order.token', order.token);
1702
+ return await this.load(storeId, cityId, order.id, order.token);
1703
+ }
1704
+ catch (e) {
1705
+ ui.useNotification('error', 'Ошибка инициализации заказа', 'Не удалось инициализировать заказ. Обновите страницу.');
1706
+ }
1707
+ return false;
1708
+ },
1709
+ reset() {
1710
+ const storage = getStorage();
1711
+ storage.delete('order.id');
1712
+ storage.delete('order.token');
1713
+ },
1714
+ async add(productVariantId, quantity, modifiers = [], promotion = null) {
1715
+ if (!this.credentials) {
1716
+ throw new Error('Order is not yet initialized.');
1717
+ }
1718
+ try {
1719
+ const params = {
1720
+ quantity,
1721
+ modifiers,
1722
+ product_variant_id: productVariantId,
1723
+ };
1724
+ if (promotion && promotion.id && promotion.reward_id) {
1725
+ params.promotion_id = promotion.id;
1726
+ params.promotion_reward_id = promotion.reward_id;
1727
+ }
1728
+ const order = await api.addOrderProductVariant(this.credentials, params, this.inclusions !== null ? { with: this.inclusions.product } : {});
1729
+ events.useProductQuantityIncreased().publish({
1730
+ order,
1731
+ productVariantId,
1732
+ quantity,
1733
+ modifiers,
1734
+ promotion,
1735
+ });
1736
+ this.update(order);
1737
+ return true;
1738
+ }
1739
+ catch (e) {
1740
+ ui.useNotification('error', 'Товар не добавлен', api.getApiErrorMessage(e, 'Не удалось добавить товар в корзину. Повторите попытку'));
1741
+ }
1742
+ return false;
1743
+ },
1744
+ async remove(productVariantId, quantity, modifiers = [], promotion = null) {
1745
+ if (!this.credentials) {
1746
+ throw new Error('Order is not yet initialized.');
1747
+ }
1748
+ try {
1749
+ const params = {
1750
+ quantity,
1751
+ modifiers,
1752
+ product_variant_id: productVariantId,
1753
+ };
1754
+ if (promotion && promotion.id && promotion.reward_id) {
1755
+ params.promotion_id = promotion.id;
1756
+ params.promotion_reward_id = promotion.reward_id;
1757
+ }
1758
+ const order = await api.removeOrderProductVariant(this.credentials, params, this.inclusions !== null ? { with: this.inclusions.product } : {});
1759
+ events.useProductQuantityDecreased().publish({
1760
+ order,
1761
+ productVariantId,
1762
+ quantity,
1763
+ modifiers,
1764
+ promotion,
1765
+ });
1766
+ this.update(order);
1767
+ return true;
1768
+ }
1769
+ catch (e) {
1770
+ ui.useNotification('error', 'Товар не удалён', api.getApiErrorMessage(e, 'Не удалось удалить товар из корзины. Повторите попытку'));
1771
+ }
1772
+ return false;
1773
+ },
1774
+ update(order) {
1775
+ if (this.order === null) {
1776
+ this.order = order;
1777
+ return;
1778
+ }
1779
+ this.order.total_price = order.total_price;
1780
+ this.order.original_total_price = order.original_total_price;
1781
+ this.order.discount_difference = order.discount_difference;
1782
+ this.order.has_discount = order.has_discount;
1783
+ this.order.variants = getUpdatedVariants(this.order, order);
1784
+ },
1785
+ async recreate(storeId, cityId, inclusions = null) {
1786
+ this.reset();
1787
+ if (!this.inclusions && inclusions) {
1788
+ this.inclusions = inclusions;
1789
+ }
1790
+ this.loading = true;
1791
+ return this.create(storeId, cityId);
1792
+ },
1793
+ },
1794
+ });
1795
+
1796
+ function useStageDetectors(order) {
1797
+ const hasCustomer = () => !!order.customer && !!order.customer.id;
1798
+ const hasDelivery = () => {
1799
+ var _a;
1800
+ if (['pickup', 'on_premise'].indexOf(order.delivery_method.id) > -1) {
1801
+ return !!order.stock && !!order.stock.id;
1802
+ }
1803
+ return !!(order === null || order === void 0 ? void 0 : order.delivery_address) && !!((_a = order === null || order === void 0 ? void 0 : order.delivery_address) === null || _a === void 0 ? void 0 : _a.id);
1804
+ };
1805
+ return {
1806
+ hasCustomer,
1807
+ hasDelivery,
1808
+ };
1809
+ }
1810
+ exports.CheckoutStage = void 0;
1811
+ (function (CheckoutStage) {
1812
+ CheckoutStage["Customer"] = "customer";
1813
+ CheckoutStage["Delivery"] = "delivery";
1814
+ CheckoutStage["Payments"] = "payments";
1815
+ CheckoutStage["Summary"] = "summary";
1816
+ })(exports.CheckoutStage || (exports.CheckoutStage = {}));
1817
+ function useOrderCheckout() {
1818
+ const currentStage = vue.ref(exports.CheckoutStage.Customer);
1819
+ const stages = vue.ref({
1820
+ [exports.CheckoutStage.Customer]: {
1821
+ active: false,
1822
+ completed: false,
1823
+ },
1824
+ [exports.CheckoutStage.Delivery]: {
1825
+ active: false,
1826
+ completed: false,
1827
+ },
1828
+ [exports.CheckoutStage.Payments]: {
1829
+ active: false,
1830
+ completed: false,
1831
+ },
1832
+ [exports.CheckoutStage.Summary]: {
1833
+ active: false,
1834
+ completed: false,
1835
+ },
1836
+ });
1837
+ const { order, settingsLoaded } = pinia.storeToRefs(useOrderStore());
1838
+ const { loaded: customerLoaded } = pinia.storeToRefs(useCustomerStore());
1839
+ const ready = vue.computed(() => {
1840
+ if (!order.value || !order.value.id || !order.value.token) {
1841
+ return false;
1842
+ }
1843
+ else if (!customerLoaded.value) {
1844
+ return false;
1845
+ }
1846
+ else if (!settingsLoaded.value) {
1847
+ return false;
1848
+ }
1849
+ return true;
1850
+ });
1851
+ const setStage = (newStage) => {
1852
+ currentStage.value = newStage;
1853
+ Object.keys(stages.value).forEach((key) => {
1854
+ stages.value[key].active = false;
1855
+ stages.value[key].completed = false;
1856
+ });
1857
+ stages.value[newStage].active = true;
1858
+ switch (newStage) {
1859
+ case exports.CheckoutStage.Customer:
1860
+ break;
1861
+ case exports.CheckoutStage.Delivery:
1862
+ stages.value.customer.completed = true;
1863
+ break;
1864
+ case exports.CheckoutStage.Payments:
1865
+ stages.value.customer.completed = true;
1866
+ stages.value.delivery.completed = true;
1867
+ break;
1868
+ case exports.CheckoutStage.Summary:
1869
+ stages.value.customer.completed = true;
1870
+ stages.value.delivery.completed = true;
1871
+ stages.value.payments.completed = true;
1872
+ break;
1873
+ default:
1874
+ console.error(`[checkout] Unknown stage provided: "${newStage}", resetting to customer stage.`);
1875
+ stages.value.customer.completed = false;
1876
+ stages.value.delivery.completed = false;
1877
+ stages.value.payments.completed = false;
1878
+ break;
1879
+ }
1880
+ };
1881
+ vue.watch(ready, () => {
1882
+ if (!ready.value || order.value === null) {
1883
+ return setStage(exports.CheckoutStage.Customer);
1884
+ }
1885
+ const detectors = useStageDetectors(order.value);
1886
+ if (!detectors.hasCustomer()) {
1887
+ return setStage(exports.CheckoutStage.Customer);
1888
+ }
1889
+ else if (!detectors.hasDelivery()) {
1890
+ return setStage(exports.CheckoutStage.Delivery);
1891
+ }
1892
+ return setStage(exports.CheckoutStage.Payments);
1893
+ }, { immediate: true });
1894
+ return {
1895
+ ready,
1896
+ order,
1897
+ stages,
1898
+ currentStage,
1899
+ setStage,
1900
+ };
1901
+ }
1902
+
1903
+ function useCheckoutConfirmation(order) {
1904
+ const form = vue.ref({ code: '' });
1905
+ const confirming = vue.ref(false);
1906
+ const resending = vue.ref(false);
1907
+ const credentials = vue.computed(() => ({
1908
+ id: order.value.id,
1909
+ token: order.value.token,
1910
+ }));
1911
+ const confirm = async () => {
1912
+ if (confirming.value) {
1913
+ return null;
1914
+ }
1915
+ else if (!form.value.code) {
1916
+ ui.useNotification('error', 'Ошибка', 'Нужно указать код из SMS.');
1917
+ return null;
1918
+ }
1919
+ confirming.value = true;
1920
+ try {
1921
+ return await api.confirmOrder(credentials.value, form.value);
1922
+ }
1923
+ catch (e) {
1924
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось подтвердить заказ.'));
1925
+ }
1926
+ finally {
1927
+ confirming.value = false;
1928
+ }
1929
+ return null;
1930
+ };
1931
+ const resendCode = async () => {
1932
+ if (resending.value) {
1933
+ return;
1934
+ }
1935
+ resending.value = true;
1936
+ try {
1937
+ const result = await api.resendOrderConfirmationCode(credentials.value);
1938
+ if (result.success) {
1939
+ ui.useNotification('success', 'Код выслан', 'Код был повторно выслан на ваш телефон.');
1940
+ }
1941
+ else {
1942
+ ui.useNotification('error', 'Ошибка', 'Не удалось выслать код подтверждения. Повторите попытку позднее.');
1943
+ }
1944
+ }
1945
+ catch (e) {
1946
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось выслать код подтверждения.'));
1947
+ }
1948
+ finally {
1949
+ resending.value = false;
1950
+ }
1951
+ };
1952
+ return {
1953
+ form,
1954
+ confirming,
1955
+ confirm,
1956
+ resending,
1957
+ resendCode,
1958
+ };
1959
+ }
1960
+
1961
+ function useCustomerCheckoutForm(emit) {
1962
+ var _a;
1963
+ const ready = vue.ref(false);
1964
+ const saving = vue.ref(false);
1965
+ const form = vue.ref({
1966
+ phone: {
1967
+ number: '',
1968
+ country: 'RU',
1969
+ },
1970
+ first_name: '',
1971
+ last_name: '',
1972
+ });
1973
+ const { customer } = pinia.storeToRefs(useCustomerStore());
1974
+ const { order, credentials } = pinia.storeToRefs(useOrderStore());
1975
+ if (((_a = order.value) === null || _a === void 0 ? void 0 : _a.customer) && order.value.customer.id) {
1976
+ form.value.phone.number = order.value.customer.phone.national;
1977
+ form.value.phone.country = order.value.customer.phone.country;
1978
+ form.value.first_name = order.value.customer.first_name || '';
1979
+ form.value.last_name = order.value.customer.last_name || '';
1980
+ }
1981
+ else if (customer.value && customer.value.id) {
1982
+ form.value.phone.number = customer.value.phone.national;
1983
+ form.value.phone.country = customer.value.phone.country;
1984
+ form.value.first_name = customer.value.first_name || '';
1985
+ form.value.last_name = customer.value.last_name || '';
1986
+ }
1987
+ const save = async () => {
1988
+ if (!credentials.value) {
1989
+ return;
1990
+ }
1991
+ else if (saving.value) {
1992
+ return;
1993
+ }
1994
+ else if (!form.value.phone.number || !form.value.phone.country) {
1995
+ ui.useNotification('error', 'Ошибка', 'Нужно указать номер телефона.');
1996
+ return;
1997
+ }
1998
+ else if (!form.value.first_name) {
1999
+ ui.useNotification('error', 'Ошибка', 'Нужно указать имя.');
2000
+ return;
2001
+ }
2002
+ saving.value = true;
2003
+ try {
2004
+ await api.setOrderCheckoutCustomer(credentials.value, form.value);
2005
+ emit('completed');
2006
+ }
2007
+ catch (e) {
2008
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось сохранить контактные данные.'));
2009
+ }
2010
+ finally {
2011
+ saving.value = false;
2012
+ }
2013
+ };
2014
+ return {
2015
+ ready,
2016
+ saving,
2017
+ customer,
2018
+ form,
2019
+ save,
2020
+ };
2021
+ }
2022
+
2023
+ function useTotalPrice() {
2024
+ const { order } = pinia.storeToRefs(useOrderStore());
2025
+ return {
2026
+ totalPrice: vue.computed(() => { var _a; return (_a = order.value) === null || _a === void 0 ? void 0 : _a.total_price; }),
2027
+ originalTotalPrice: vue.computed(() => { var _a; return (_a = order.value) === null || _a === void 0 ? void 0 : _a.original_total_price; }),
2028
+ hasDiscount: vue.computed(() => { var _a; return (_a = order.value) === null || _a === void 0 ? void 0 : _a.has_discount; }),
2029
+ discountDifference: vue.computed(() => { var _a; return (_a = order.value) === null || _a === void 0 ? void 0 : _a.discount_difference; }),
2030
+ };
2031
+ }
2032
+ function useTotalQuantity() {
2033
+ const { order } = pinia.storeToRefs(useOrderStore());
2034
+ const quantity = vue.computed(() => {
2035
+ var _a;
2036
+ if (!order.value) {
2037
+ return 0;
2038
+ }
2039
+ return (((_a = order.value) === null || _a === void 0 ? void 0 : _a.variants) || [])
2040
+ .map((variant) => variant.quantity)
2041
+ .reduce((variantQuantity, total) => variantQuantity + total, 0);
2042
+ });
2043
+ return {
2044
+ order,
2045
+ quantity,
2046
+ };
2047
+ }
2048
+ function useOrderVariants() {
2049
+ const { order } = pinia.storeToRefs(useOrderStore());
2050
+ const variants = vue.computed(() => {
2051
+ if (!order.value || !order.value.variants || !order.value.variants.length) {
2052
+ return [];
2053
+ }
2054
+ return order.value.variants;
2055
+ });
2056
+ return {
2057
+ variants,
2058
+ };
2059
+ }
2060
+ const useVariantControls = (variant, modifiers, modifiersHash, promotion) => {
2061
+ const { order } = pinia.storeToRefs(useOrderStore());
2062
+ const { add: addVariant, remove: removeVariant } = useOrderStore();
2063
+ const loading = vue.ref(false);
2064
+ const operation = vue.ref(null);
2065
+ const orderVariant = vue.computed(() => {
2066
+ var _a, _b;
2067
+ if (!((_b = (_a = order.value) === null || _a === void 0 ? void 0 : _a.variants) === null || _b === void 0 ? void 0 : _b.length)) {
2068
+ return null;
2069
+ }
2070
+ return order.value.variants.find((item) => {
2071
+ if (item.product_variant_id !== variant.value.id || item.modifiers_hash !== modifiersHash.value) {
2072
+ return false;
2073
+ }
2074
+ else if (promotion.value === null) {
2075
+ return item.promotion_id === null;
2076
+ }
2077
+ return item.promotion_id === promotion.value.id && item.promotion_reward_id === promotion.value.reward_id;
2078
+ });
2079
+ });
2080
+ const totalQuantity = vue.computed(() => (orderVariant.value ? orderVariant.value.quantity : 0));
2081
+ const quantityModel = vue.ref(totalQuantity.value);
2082
+ const updateVariant = async (type, quantity) => {
2083
+ if (loading.value) {
2084
+ return;
2085
+ }
2086
+ loading.value = true;
2087
+ operation.value = type;
2088
+ let result;
2089
+ if (type === 'add') {
2090
+ result = await addVariant(variant.value.id, quantity, modifiers.value, promotion.value);
2091
+ }
2092
+ else {
2093
+ result = await removeVariant(variant.value.id, quantity, modifiers.value, promotion.value);
2094
+ ui.useNotification('success', 'Товар удалён', `Товар${variant.value.name ? ` «${variant.value.name}»` : ''} удалён из корзины!`);
2095
+ }
2096
+ if (result) {
2097
+ ui.useNotification('success', `Товар ${type === 'add' ? 'добавлен' : 'удалён'}`, `Товар${variant.value.name ? ` «${variant.value.name}»` : ''} ${type === 'add' ? 'добавлен в корзину' : 'удалён из корзины'}!`);
2098
+ }
2099
+ loading.value = false;
2100
+ };
2101
+ const add = async () => updateVariant('add', 1);
2102
+ const remove = async () => updateVariant('remove', totalQuantity.value);
2103
+ const getQuantity = (value) => {
2104
+ if (typeof value === 'string') {
2105
+ return parseInt(value, 10);
2106
+ }
2107
+ return value;
2108
+ };
2109
+ const increase = async (quantity = 1) => {
2110
+ quantityModel.value = getQuantity(quantityModel.value) + quantity;
2111
+ };
2112
+ const decrease = async (quantity = 1) => {
2113
+ quantityModel.value = Math.max(0, getQuantity(quantityModel.value) - quantity);
2114
+ };
2115
+ vue.watch(totalQuantity, () => {
2116
+ quantityModel.value = totalQuantity.value;
2117
+ });
2118
+ vue.watch(quantityModel, lodashEs.debounce(async (val) => {
2119
+ const difference = getQuantity(val) - totalQuantity.value;
2120
+ if (difference < 0) {
2121
+ await updateVariant('remove', Math.abs(difference));
2122
+ }
2123
+ else if (difference > 0) {
2124
+ await updateVariant('add', difference);
2125
+ }
2126
+ }, 1000));
2127
+ return {
2128
+ orderVariant,
2129
+ totalQuantity,
2130
+ quantityModel,
2131
+ add,
2132
+ increase,
2133
+ decrease,
2134
+ remove,
2135
+ loading,
2136
+ operation,
2137
+ };
2138
+ };
2139
+
2140
+ function getDeliveryMethod(order, settings) {
2141
+ if (!order.value || !settings.value) {
2142
+ return 'delivery';
2143
+ }
2144
+ const methods = settings.value.delivery_methods.map((method) => method.id);
2145
+ if (methods.indexOf(order.value.delivery_method.id) !== -1) {
2146
+ return order.value.delivery_method.id;
2147
+ }
2148
+ return methods[0];
2149
+ }
2150
+ function getStockId(order, stocks) {
2151
+ const availableStocks = stocks.value.map((stock) => stock.id);
2152
+ if (order.value && order.value.stock_id !== null && availableStocks.indexOf(order.value.stock_id) !== -1) {
2153
+ return order.value.stock_id;
2154
+ }
2155
+ return availableStocks[0];
2156
+ }
2157
+ function useCustomerAddresses(customer) {
2158
+ const addresses = vue.ref([]);
2159
+ const loaded = vue.ref(false);
2160
+ if (!customer.value || !customer.value.id) {
2161
+ loaded.value = true;
2162
+ }
2163
+ else {
2164
+ api.getCustomerAddresses({ count: 100 }).then((response) => {
2165
+ addresses.value = response.items;
2166
+ loaded.value = true;
2167
+ });
2168
+ }
2169
+ return {
2170
+ addresses,
2171
+ loaded,
2172
+ };
2173
+ }
2174
+ function getDeliveryPayload(form, addressesResolver) {
2175
+ const data = { delivery_method: form.method };
2176
+ const errors = [];
2177
+ if (form.method === 'pickup') {
2178
+ if (form.pickup.stock_id) {
2179
+ data.stock_id = form.pickup.stock_id;
2180
+ }
2181
+ else {
2182
+ errors.push('Нужно выбрать точку самовывоза.');
2183
+ }
2184
+ }
2185
+ else if (form.method === 'on_premise') {
2186
+ if (form.on_premise.stock_id) {
2187
+ data.stock_id = form.on_premise.stock_id;
2188
+ }
2189
+ else {
2190
+ errors.push('Нужно выбрать точку самовывоза.');
2191
+ }
2192
+ if (form.on_premise.table) {
2193
+ data.on_premise = { table: form.on_premise.table };
2194
+ }
2195
+ else {
2196
+ errors.push('Нужно указать номер стола.');
2197
+ }
2198
+ }
2199
+ else if (form.method === 'delivery') {
2200
+ if (form.delivery.address.id) {
2201
+ data.delivery_address = { id: form.delivery.address.id };
2202
+ }
2203
+ else {
2204
+ const { errors: addressErrors, data: address } = useAddressFormValue(form.delivery.address, addressesResolver);
2205
+ if (addressErrors.length > 0) {
2206
+ errors.push(addressErrors[0]);
2207
+ }
2208
+ else {
2209
+ data.delivery_address = address;
2210
+ }
2211
+ }
2212
+ }
2213
+ if (form.deliver_at.mode === 'select' && form.deliver_at.date) {
2214
+ data.deliver_at = dateFns.format(Date.parse(form.deliver_at.date), 'yyyy-MM-dd HH:mm');
2215
+ }
2216
+ return {
2217
+ data,
2218
+ errors,
2219
+ };
2220
+ }
2221
+ function useDeliveryCheckoutForm(emit) {
2222
+ var _a, _b, _c, _d;
2223
+ const { credentials, order, settings } = pinia.storeToRefs(useOrderStore());
2224
+ const { customer } = pinia.storeToRefs(useCustomerStore());
2225
+ const { stocks, addressesResolver } = pinia.storeToRefs(useStoreStore());
2226
+ const { addresses, loaded } = useCustomerAddresses(customer);
2227
+ const { totalPrice } = useTotalPrice();
2228
+ const ready = vue.computed(() => loaded.value);
2229
+ const currentAddress = vue.computed(() => { var _a; return (_a = order.value) === null || _a === void 0 ? void 0 : _a.delivery_address; });
2230
+ const saving = vue.ref(false);
2231
+ const form = vue.ref({
2232
+ method: getDeliveryMethod(order, settings),
2233
+ delivery: {
2234
+ address: getAddressForm(),
2235
+ },
2236
+ pickup: {
2237
+ stock_id: getStockId(order, stocks),
2238
+ },
2239
+ on_premise: {
2240
+ stock_id: getStockId(order, stocks),
2241
+ table: '',
2242
+ },
2243
+ deliver_at: {
2244
+ mode: 'asap',
2245
+ date: '',
2246
+ },
2247
+ });
2248
+ form.value.delivery.address.id = ((_b = (_a = order.value) === null || _a === void 0 ? void 0 : _a.delivery_address) === null || _b === void 0 ? void 0 : _b.id) || '';
2249
+ if ((_d = (_c = order.value) === null || _c === void 0 ? void 0 : _c.deliver_at_local) === null || _d === void 0 ? void 0 : _d.datetime) {
2250
+ form.value.deliver_at.mode = 'select';
2251
+ form.value.deliver_at.date = dateFns.format(Date.parse(order.value.deliver_at_local.iso), 'yyyy-MM-dd HH:mm');
2252
+ }
2253
+ const priceIsNotEnough = vue.computed(() => {
2254
+ var _a, _b;
2255
+ const method = (_a = settings.value) === null || _a === void 0 ? void 0 : _a.delivery_methods.find((item) => item.id === form.value.method);
2256
+ if (!method || !method.min_price || !method.min_price.value) {
2257
+ return false;
2258
+ }
2259
+ return method.min_price.value > (((_b = totalPrice.value) === null || _b === void 0 ? void 0 : _b.value) || 0);
2260
+ });
2261
+ const save = async () => {
2262
+ if (!credentials.value || !addressesResolver.value) {
2263
+ return;
2264
+ }
2265
+ else if (saving.value) {
2266
+ return;
2267
+ }
2268
+ const { data, errors } = getDeliveryPayload(form.value, addressesResolver.value);
2269
+ if (errors.length > 0) {
2270
+ ui.useNotification('error', 'Ошибка', errors[0]);
2271
+ return;
2272
+ }
2273
+ saving.value = true;
2274
+ try {
2275
+ await api.setOrderCheckoutDelivery(credentials.value, data);
2276
+ emit('completed');
2277
+ }
2278
+ catch (e) {
2279
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось сохранить способ доставки.'));
2280
+ }
2281
+ finally {
2282
+ saving.value = false;
2283
+ }
2284
+ };
2285
+ return {
2286
+ ready,
2287
+ form,
2288
+ addresses,
2289
+ currentAddress,
2290
+ addressesResolver,
2291
+ priceIsNotEnough,
2292
+ saving,
2293
+ save,
2294
+ };
2295
+ }
2296
+
2297
+ function getPaymentMethod(settings) {
2298
+ if (!settings.value) {
2299
+ return 'cash';
2300
+ }
2301
+ const available = settings.value.payment_methods.map((method) => method.id);
2302
+ return available[0];
2303
+ }
2304
+ function useBillSuggestions(unpaidAmount, staticAmounts = [10, 50, 200, 5000], availableBills = [100, 500, 1000]) {
2305
+ const { totalPrice } = useTotalPrice();
2306
+ const getClosestMultipleOf = (price, amount) => Math.ceil(price / amount) * amount;
2307
+ const suggestions = vue.computed(() => {
2308
+ const items = new Set([
2309
+ ...staticAmounts,
2310
+ ...availableBills.map((billItem) => getClosestMultipleOf(unpaidAmount.value, billItem)),
2311
+ unpaidAmount.value,
2312
+ ]);
2313
+ const bills = [...items]
2314
+ .filter((suggestion) => suggestion > unpaidAmount.value)
2315
+ .sort((first, second) => (first - second))
2316
+ .map((amount) => {
2317
+ var _a, _b;
2318
+ return ({
2319
+ value: amount,
2320
+ label: `${(_a = totalPrice.value) === null || _a === void 0 ? void 0 : _a.currency.prefix}${amount}${(_b = totalPrice.value) === null || _b === void 0 ? void 0 : _b.currency.suffix}`,
2321
+ });
2322
+ });
2323
+ bills.unshift({
2324
+ label: 'Без сдачи',
2325
+ value: '',
2326
+ });
2327
+ bills.push({
2328
+ label: 'Другая',
2329
+ value: 'custom',
2330
+ });
2331
+ return bills;
2332
+ });
2333
+ return {
2334
+ suggestions,
2335
+ };
2336
+ }
2337
+ function useBonusesPreviewChecker(order, credentials, bonuses, setBonuses) {
2338
+ var _a;
2339
+ const bonusesAreChecking = vue.ref(false);
2340
+ const unpaidAmount = vue.ref((_a = order.value) === null || _a === void 0 ? void 0 : _a.total_price);
2341
+ const bonusesChecker = async () => {
2342
+ var _a;
2343
+ if (!credentials.value) {
2344
+ return;
2345
+ }
2346
+ else if (bonusesAreChecking.value) {
2347
+ return;
2348
+ }
2349
+ else if (!bonuses.value) {
2350
+ unpaidAmount.value = (_a = order.value) === null || _a === void 0 ? void 0 : _a.total_price;
2351
+ return;
2352
+ }
2353
+ bonusesAreChecking.value = true;
2354
+ try {
2355
+ const preview = await api.getOrderBonusesPaymentPreview(credentials.value, bonuses.value);
2356
+ unpaidAmount.value = preview.unpaid;
2357
+ if (parseFloat(bonuses.value) > parseFloat(preview.bonuses.trimmed)) {
2358
+ setBonuses(preview.bonuses.trimmed);
2359
+ ui.useNotification('warning', 'Сумма бонусов изменена', 'Вы указали сумму бонусов, превышающую лимит списания. Мы изменили сумму на максимально доступную.');
2360
+ }
2361
+ }
2362
+ catch (e) {
2363
+ //
2364
+ }
2365
+ finally {
2366
+ bonusesAreChecking.value = false;
2367
+ }
2368
+ };
2369
+ vue.watch(bonuses, lodashEs.debounce(async () => bonusesChecker(), 1000));
2370
+ return {
2371
+ unpaidAmount,
2372
+ bonusesAreChecking,
2373
+ };
2374
+ }
2375
+ function getPaymentsPayload(form) {
2376
+ const payments = [];
2377
+ if (parseFloat(form.bonuses) > 0.0) {
2378
+ payments.push({
2379
+ method: 'bonuses',
2380
+ amount: form.bonuses,
2381
+ });
2382
+ }
2383
+ const payment = { method: form.method };
2384
+ if (form.method === 'cash' && form.bill) {
2385
+ payment.bill = form.bill;
2386
+ }
2387
+ else if (form.method === 'card_token') {
2388
+ payment.card_token_id = form.card_token_id;
2389
+ }
2390
+ else if (form.method === 'cloudpayments') {
2391
+ payment.save_card = form.save_card === true;
2392
+ }
2393
+ payments.push(payment);
2394
+ return payments;
2395
+ }
2396
+ function useDeliveryPaymentsForm(emit) {
2397
+ const { order, credentials, settings } = pinia.storeToRefs(useOrderStore());
2398
+ const form = vue.ref({
2399
+ method: getPaymentMethod(settings),
2400
+ bill: '',
2401
+ save_card: false,
2402
+ bonuses: '',
2403
+ card_token_id: '',
2404
+ });
2405
+ const saving = vue.ref(false);
2406
+ const bonuses = vue.computed(() => form.value.bonuses);
2407
+ const { unpaidAmount, bonusesAreChecking } = useBonusesPreviewChecker(order, credentials, bonuses, (amount) => {
2408
+ form.value.bonuses = amount;
2409
+ });
2410
+ const save = async () => {
2411
+ if (!credentials.value) {
2412
+ return;
2413
+ }
2414
+ else if (saving.value) {
2415
+ return;
2416
+ }
2417
+ const payments = getPaymentsPayload(form.value);
2418
+ try {
2419
+ await api.setOrderCheckoutPayment(credentials.value, { payments });
2420
+ emit('completed');
2421
+ }
2422
+ catch (e) {
2423
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось сохранить способ оплаты.'));
2424
+ }
2425
+ finally {
2426
+ saving.value = false;
2427
+ }
2428
+ };
2429
+ return {
2430
+ form,
2431
+ unpaidAmount,
2432
+ bonusesAreChecking,
2433
+ saving,
2434
+ save,
2435
+ };
2436
+ }
2437
+
2438
+ function useCheckoutSummary(emit) {
2439
+ const orderStore = useOrderStore();
2440
+ const { credentials, settings } = pinia.storeToRefs(orderStore);
2441
+ const { store, city } = pinia.storeToRefs(useStoreStore());
2442
+ const form = vue.ref({
2443
+ persons_count: '',
2444
+ notes: '',
2445
+ });
2446
+ const hasPersonsCount = vue.computed(() => {
2447
+ if (!settings.value) {
2448
+ return false;
2449
+ }
2450
+ const index = settings.value.options.findIndex((option) => option.kind === 'persons_count');
2451
+ return index !== -1;
2452
+ });
2453
+ const saving = vue.ref(false);
2454
+ const save = async () => {
2455
+ if (!credentials.value) {
2456
+ return;
2457
+ }
2458
+ else if (saving.value) {
2459
+ return;
2460
+ }
2461
+ const payload = {
2462
+ notes: form.value.notes,
2463
+ };
2464
+ if (hasPersonsCount.value && form.value.persons_count) {
2465
+ payload.notes = `Персон: ${form.value.persons_count}\n\n${payload.notes}`.trim();
2466
+ }
2467
+ saving.value = true;
2468
+ try {
2469
+ const response = await api.checkoutOrder(credentials.value, payload);
2470
+ if (response.confirmation_required) {
2471
+ emit('confirmation', credentials.value);
2472
+ }
2473
+ else {
2474
+ emit('completed', response);
2475
+ }
2476
+ if (store.value && city.value) {
2477
+ await orderStore.recreate(store.value.id, city.value.id);
2478
+ }
2479
+ }
2480
+ catch (e) {
2481
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось оформить заказ.'));
2482
+ }
2483
+ finally {
2484
+ saving.value = false;
2485
+ }
2486
+ };
2487
+ return {
2488
+ form,
2489
+ hasPersonsCount,
2490
+ saving,
2491
+ save,
2492
+ };
2493
+ }
2494
+
2495
+ function useCheckoutTotal() {
2496
+ const { credentials } = pinia.storeToRefs(useOrderStore());
2497
+ const loading = vue.ref(false);
2498
+ const total = vue.ref(null);
2499
+ const load = async () => {
2500
+ if (loading.value) {
2501
+ return;
2502
+ }
2503
+ else if (!credentials.value) {
2504
+ return;
2505
+ }
2506
+ loading.value = true;
2507
+ try {
2508
+ total.value = await api.getOrderCheckoutTotal(credentials.value);
2509
+ }
2510
+ catch (e) {
2511
+ //
2512
+ }
2513
+ finally {
2514
+ loading.value = false;
2515
+ }
2516
+ };
2517
+ return {
2518
+ loading,
2519
+ load,
2520
+ total,
2521
+ };
2522
+ }
2523
+
2524
+ function useSingleModifier(props, emit) {
2525
+ const value = vue.computed({
2526
+ get: () => props.modelValue,
2527
+ set: (val) => emit('update:modelValue', val),
2528
+ });
2529
+ const checked = vue.computed({
2530
+ get: () => value.value.quantity > 0,
2531
+ set: (val) => {
2532
+ value.value.quantity = val ? 1 : 0;
2533
+ },
2534
+ });
2535
+ const shouldUseQuantityControls = vue.computed(() => props.modifier.max_quantity > 1);
2536
+ const increase = () => {
2537
+ if (props.limitReached) {
2538
+ return;
2539
+ }
2540
+ value.value.quantity = Math.min(value.value.quantity + 1, props.modifier.max_quantity);
2541
+ };
2542
+ const decrease = () => {
2543
+ value.value.quantity = Math.max(value.value.quantity - 1, props.modifier.min_quantity);
2544
+ };
2545
+ return {
2546
+ value,
2547
+ checked,
2548
+ shouldUseQuantityControls,
2549
+ increase,
2550
+ decrease,
2551
+ };
2552
+ }
2553
+
2554
+ function getModifierQuantity(modifier, value) {
2555
+ const index = value.value.findIndex((item) => item.modifier_id === modifier.modifier.id);
2556
+ if (index === -1) {
2557
+ return 0;
2558
+ }
2559
+ return value.value[index].quantity;
2560
+ }
2561
+ function getDefaultModifiersGroupModifiers(group, value) {
2562
+ const modifiers = {};
2563
+ group.modifiers.forEach((modifier) => {
2564
+ const quantity = Array.isArray(value.value) ? getModifierQuantity(modifier, value) : 0;
2565
+ modifiers[modifier.modifier.id] = {
2566
+ quantity,
2567
+ modifier_id: modifier.modifier.id,
2568
+ };
2569
+ });
2570
+ return modifiers;
2571
+ }
2572
+ function getSelectedModifiers$1(group, modifiers) {
2573
+ const selected = [];
2574
+ Object.keys(modifiers).forEach((modifierId) => {
2575
+ if (!modifiers[modifierId].quantity) {
2576
+ return;
2577
+ }
2578
+ selected.push({
2579
+ modifiers_group_id: group.group.id,
2580
+ modifier_id: modifierId,
2581
+ quantity: modifiers[modifierId].quantity,
2582
+ });
2583
+ });
2584
+ return selected;
2585
+ }
2586
+ function useModifiersGroup(props, emit) {
2587
+ const value = vue.computed({
2588
+ get: () => props.modelValue,
2589
+ set: (val) => emit('update:modelValue', val),
2590
+ });
2591
+ const selectedModifier = vue.ref('');
2592
+ const modifiers = vue.ref(getDefaultModifiersGroupModifiers(props.group, value));
2593
+ const shouldUseQuantityControls = vue.computed(() => props.group.max_quantity > 1);
2594
+ const totalSelectedCount = vue.computed(() => {
2595
+ return value.value
2596
+ .map((modifier) => modifier.quantity)
2597
+ .reduce((current, total) => current + total, 0);
2598
+ });
2599
+ const limitReached = vue.computed(() => {
2600
+ if (!shouldUseQuantityControls.value) {
2601
+ return selectedModifier.value !== '';
2602
+ }
2603
+ return totalSelectedCount.value >= props.group.max_quantity;
2604
+ });
2605
+ const requiredModifierIsMissing = vue.computed(() => {
2606
+ if (!props.group.is_required) {
2607
+ return false;
2608
+ }
2609
+ else if (props.group.min_quantity === 0) {
2610
+ return false;
2611
+ }
2612
+ return totalSelectedCount.value < props.group.min_quantity;
2613
+ });
2614
+ vue.watch(selectedModifier, (val) => {
2615
+ if (!val) {
2616
+ value.value = [];
2617
+ return;
2618
+ }
2619
+ value.value = [{
2620
+ modifiers_group_id: props.group.group.id,
2621
+ modifier_id: val,
2622
+ quantity: 1,
2623
+ }];
2624
+ });
2625
+ vue.watch(modifiers, (val) => {
2626
+ value.value = getSelectedModifiers$1(props.group, val);
2627
+ }, { deep: true });
2628
+ return {
2629
+ selectedModifier,
2630
+ modifiers,
2631
+ shouldUseQuantityControls,
2632
+ requiredModifierIsMissing,
2633
+ limitReached,
2634
+ };
2635
+ }
2636
+
2637
+ function getModifiers(product, variant) {
2638
+ const modifiers = {};
2639
+ ((product === null || product === void 0 ? void 0 : product.modifiers) || []).forEach((modifier) => {
2640
+ modifiers[modifier.modifier.id] = {
2641
+ modifier_id: modifier.modifier.id,
2642
+ quantity: 0,
2643
+ };
2644
+ });
2645
+ ((product === null || product === void 0 ? void 0 : product.modifiers_groups) || []).forEach((group) => {
2646
+ modifiers[group.group.id] = [];
2647
+ });
2648
+ if (!variant.value || !variant.value.selected_modifiers || !variant.value.selected_modifiers.modifiers) {
2649
+ return modifiers;
2650
+ }
2651
+ else if (!variant.value.selected_modifiers.modifiers.length) {
2652
+ return modifiers;
2653
+ }
2654
+ variant.value.selected_modifiers.modifiers.forEach((modifier) => {
2655
+ if (typeof modifier.modifiers_group_id !== 'undefined' && modifier.modifiers_group_id) {
2656
+ modifiers[modifier.modifiers_group_id].push(modifier);
2657
+ }
2658
+ else if (typeof modifier.modifier_id !== 'undefined' && modifier.modifier_id) {
2659
+ modifiers[modifier.modifier_id] = {
2660
+ modifier_id: modifier.modifier_id,
2661
+ quantity: modifier.quantity,
2662
+ };
2663
+ }
2664
+ });
2665
+ return modifiers;
2666
+ }
2667
+ function getSelectedModifiers(modifiers) {
2668
+ const selected = [];
2669
+ Object.keys(modifiers.value).forEach((modifierId) => {
2670
+ const isGroup = Array.isArray(modifiers.value[modifierId]);
2671
+ if (!isGroup && !modifiers.value[modifierId].quantity) {
2672
+ return;
2673
+ }
2674
+ else if (!isGroup) {
2675
+ selected.push({
2676
+ modifier_id: modifierId,
2677
+ quantity: modifiers.value[modifierId].quantity,
2678
+ });
2679
+ return;
2680
+ }
2681
+ modifiers.value[modifierId].forEach((modifier) => {
2682
+ if (!modifier.modifier_id || !modifier.quantity || !modifier.modifiers_group_id) {
2683
+ return;
2684
+ }
2685
+ selected.push({
2686
+ modifiers_group_id: modifier.modifiers_group_id,
2687
+ modifier_id: modifier.modifier_id,
2688
+ quantity: modifier.quantity,
2689
+ });
2690
+ });
2691
+ });
2692
+ return selected;
2693
+ }
2694
+ function useModifiers(product, variant, recalculate) {
2695
+ var _a, _b;
2696
+ const modifiersHash = vue.ref(((_b = (_a = variant.value) === null || _a === void 0 ? void 0 : _a.selected_modifiers) === null || _b === void 0 ? void 0 : _b.hash) || null);
2697
+ const modifiers = vue.ref(getModifiers(product, variant));
2698
+ const selectedModifiers = vue.computed(() => getSelectedModifiers(modifiers));
2699
+ vue.watch(variant, () => {
2700
+ modifiers.value = getModifiers(product, variant);
2701
+ });
2702
+ vue.watch(selectedModifiers, async () => {
2703
+ modifiersHash.value = await recalculate(selectedModifiers);
2704
+ });
2705
+ return {
2706
+ modifiers,
2707
+ selectedModifiers,
2708
+ modifiersHash,
2709
+ };
2710
+ }
2711
+
2712
+ async function recalculatePrice(variant, modifiers) {
2713
+ if (!variant.value) {
2714
+ return null;
2715
+ }
2716
+ else if (!modifiers.value.length) {
2717
+ return null;
2718
+ }
2719
+ const { cityId } = pinia.storeToRefs(useStoreStore());
2720
+ try {
2721
+ return await api.getProductVariantPrice(variant.value.product_id, variant.value.id, modifiers.value, {
2722
+ city_id: cityId.value || undefined,
2723
+ });
2724
+ }
2725
+ catch (e) {
2726
+ //
2727
+ }
2728
+ return null;
2729
+ }
2730
+ function getProductVariantsPricesRange(variants) {
2731
+ if (!variants.length) {
2732
+ return null;
2733
+ }
2734
+ const sorted = [...variants].sort((first, second) => Math.sign(first.price.value - second.price.value));
2735
+ return {
2736
+ min: sorted[0].price,
2737
+ max: sorted[sorted.length - 1].price,
2738
+ };
2739
+ }
2740
+ function useVariantPrice(variants, variant) {
2741
+ const result = vue.ref(null);
2742
+ const recalculating = vue.ref(false);
2743
+ const totalPrice = vue.computed(() => {
2744
+ if (result.value !== null) {
2745
+ return {
2746
+ recalculated: true,
2747
+ recalculating: recalculating.value,
2748
+ price: result.value.price,
2749
+ original_price: result.value.original_price,
2750
+ has_discount: result.value.has_discount,
2751
+ discount_id: result.value.discount_id,
2752
+ discount_difference: result.value.discount_difference,
2753
+ discount_percentage: result.value.discount_percentage,
2754
+ };
2755
+ }
2756
+ else if (!variant.value) {
2757
+ return null;
2758
+ }
2759
+ return {
2760
+ recalculated: false,
2761
+ recalculating: recalculating.value,
2762
+ price: variant.value.price,
2763
+ original_price: variant.value.original_price,
2764
+ has_discount: variant.value.has_discount,
2765
+ discount_id: variant.value.discount_id,
2766
+ discount_difference: variant.value.discount_difference,
2767
+ discount_percentage: variant.value.discount_percentage,
2768
+ };
2769
+ });
2770
+ const pricesRange = vue.computed(() => getProductVariantsPricesRange(variants));
2771
+ const recalculate = async (modifiers) => {
2772
+ if (recalculating.value) {
2773
+ return null;
2774
+ }
2775
+ recalculating.value = true;
2776
+ result.value = await recalculatePrice(variant, modifiers);
2777
+ recalculating.value = false;
2778
+ return result.value !== null ? result.value.modifiers_hash : null;
2779
+ };
2780
+ return {
2781
+ totalPrice,
2782
+ pricesRange,
2783
+ recalculating,
2784
+ recalculate,
2785
+ };
2786
+ }
2787
+
2788
+ function getOptionsList(variants) {
2789
+ const options = [];
2790
+ if (variants.length <= 1) {
2791
+ return options;
2792
+ }
2793
+ variants.forEach((variant) => {
2794
+ if (!variant.option_values || !variant.option_values.length) {
2795
+ return;
2796
+ }
2797
+ variant.option_values.forEach((optionValue) => {
2798
+ if (!optionValue.option || !optionValue.option.id) {
2799
+ return;
2800
+ }
2801
+ const optionIndex = options.findIndex((option) => { var _a; return option.id === ((_a = optionValue.option) === null || _a === void 0 ? void 0 : _a.id); });
2802
+ if (optionIndex === -1) {
2803
+ options.push({
2804
+ id: optionValue.option.id,
2805
+ name: optionValue.option.name,
2806
+ type: optionValue.option.type,
2807
+ values: [optionValue],
2808
+ });
2809
+ return;
2810
+ }
2811
+ const valueIndex = options[optionIndex].values.findIndex((item) => item.id === optionValue.id);
2812
+ if (valueIndex !== -1) {
2813
+ return;
2814
+ }
2815
+ options[optionIndex].values.push(optionValue);
2816
+ });
2817
+ });
2818
+ return options;
2819
+ }
2820
+ function useVariant(variants) {
2821
+ const options = vue.ref(getOptionsList(variants));
2822
+ const selectedOptions = vue.ref({});
2823
+ options.value.forEach((option) => {
2824
+ selectedOptions.value[option.id] = option.values.length > 0 ? option.values[0].id : null;
2825
+ });
2826
+ const variant = vue.computed(() => {
2827
+ if (!Array.isArray(variants) || !variants.length) {
2828
+ return null;
2829
+ }
2830
+ else if (variants.length === 1) {
2831
+ return variants[0];
2832
+ }
2833
+ else if (!Object.keys(selectedOptions.value).length) {
2834
+ return variants[0];
2835
+ }
2836
+ const variantIndex = variants.findIndex((item) => {
2837
+ if (!item.option_values || !item.option_values.length) {
2838
+ return false;
2839
+ }
2840
+ return item.option_values.every((optionValue) => {
2841
+ if (!optionValue.option || !optionValue.option.id) {
2842
+ return false;
2843
+ }
2844
+ else if (typeof selectedOptions.value[optionValue.option.id] === 'undefined') {
2845
+ return false;
2846
+ }
2847
+ else if (selectedOptions.value[optionValue.option.id] !== optionValue.id) {
2848
+ return false;
2849
+ }
2850
+ return true;
2851
+ });
2852
+ });
2853
+ if (variantIndex === -1) {
2854
+ return null;
2855
+ }
2856
+ return variants[variantIndex];
2857
+ });
2858
+ return {
2859
+ variant,
2860
+ options,
2861
+ selectedOptions,
2862
+ };
2863
+ }
2864
+
2865
+ function useProductItem(errorHandler) {
2866
+ return useItemLoader(api.getProduct, getApiErrorHandler(errorHandler, function (e) {
2867
+ ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Товар не найден.'));
2868
+ }));
2869
+ }
2870
+ function useProductCard(item) {
2871
+ var _a;
2872
+ const product = vue.ref({
2873
+ id: item.id,
2874
+ short_id: item.short_id,
2875
+ category_id: Array.isArray(item.categories) && item.categories.length > 0 ? item.categories[0].id : null,
2876
+ slug: item.slug,
2877
+ name: item.name,
2878
+ description: item.description,
2879
+ images: item.media || [],
2880
+ modifiers: item.modifiers || [],
2881
+ modifiers_groups: item.modifiers_groups || [],
2882
+ variants_count: ((_a = item.variants) === null || _a === void 0 ? void 0 : _a.length) || 0,
2883
+ has_images: (item.media || []).length > 0,
2884
+ has_modifiers: (item.modifiers || []).length > 0 || (item.modifiers_groups || []).length > 0,
2885
+ });
2886
+ const { variant, options, selectedOptions, } = useVariant(item.variants || []);
2887
+ const { totalPrice, pricesRange, recalculate, } = useVariantPrice(item.variants || [], variant);
2888
+ const { modifiers, selectedModifiers, modifiersHash, } = useModifiers(item, variant, recalculate);
2889
+ return {
2890
+ product,
2891
+ variant,
2892
+ options,
2893
+ selectedOptions,
2894
+ pricesRange,
2895
+ totalPrice,
2896
+ modifiers,
2897
+ modifiersHash,
2898
+ selectedModifiers,
2899
+ };
2900
+ }
2901
+
1559
2902
  function useProductsPricesRange(categoryId, errorHandler) {
1560
2903
  return useDataLoader(async () => api.getProductsPricesRange(categoryId), getApiErrorHandler(errorHandler, function (e) {
1561
2904
  ui.useNotification('error', 'Ошибка', api.getApiErrorMessage(e, 'Не удалось загрузить диапазон цен.'));
@@ -1607,7 +2950,7 @@ function useProductsPricesRangeFilters(categoryId, values) {
1607
2950
  };
1608
2951
  }
1609
2952
 
1610
- var ProductsSourceType;
2953
+ exports.ProductsSourceType = void 0;
1611
2954
  (function (ProductsSourceType) {
1612
2955
  ProductsSourceType["Category"] = "category";
1613
2956
  ProductsSourceType["Collection"] = "collection";
@@ -1615,20 +2958,20 @@ var ProductsSourceType;
1615
2958
  ProductsSourceType["GroupContext"] = "group_context";
1616
2959
  ProductsSourceType["VariantOptionContext"] = "variant_option_context";
1617
2960
  ProductsSourceType["VariantOptionValueContext"] = "variant_option_value_context";
1618
- })(ProductsSourceType || (ProductsSourceType = {}));
1619
- var ProductsSorting;
2961
+ })(exports.ProductsSourceType || (exports.ProductsSourceType = {}));
2962
+ exports.ProductsSorting = void 0;
1620
2963
  (function (ProductsSorting) {
1621
2964
  ProductsSorting["Manual"] = "manual";
1622
2965
  ProductsSorting["NameAsc"] = "name";
1623
2966
  ProductsSorting["NameDesc"] = "-name";
1624
2967
  ProductsSorting["PriceAsc"] = "price";
1625
2968
  ProductsSorting["PriceDesc"] = "-price";
1626
- })(ProductsSorting || (ProductsSorting = {}));
1627
- var ProductsFiltersType;
2969
+ })(exports.ProductsSorting || (exports.ProductsSorting = {}));
2970
+ exports.ProductsFiltersType = void 0;
1628
2971
  (function (ProductsFiltersType) {
1629
2972
  ProductsFiltersType["Features"] = "features";
1630
2973
  ProductsFiltersType["FeaturesGroups"] = "featuresGroups";
1631
- })(ProductsFiltersType || (ProductsFiltersType = {}));
2974
+ })(exports.ProductsFiltersType || (exports.ProductsFiltersType = {}));
1632
2975
 
1633
2976
  function getSourceParams(source, promotion) {
1634
2977
  if (source === null || !source.id || !source.type) {
@@ -1638,26 +2981,26 @@ function getSourceParams(source, promotion) {
1638
2981
  return {};
1639
2982
  }
1640
2983
  switch (source.type) {
1641
- case ProductsSourceType.Category:
2984
+ case exports.ProductsSourceType.Category:
1642
2985
  return { category_id: source.id };
1643
- case ProductsSourceType.Collection:
2986
+ case exports.ProductsSourceType.Collection:
1644
2987
  return { collection_id: source.id };
1645
- case ProductsSourceType.CategoryContext:
2988
+ case exports.ProductsSourceType.CategoryContext:
1646
2989
  return {
1647
2990
  context_type: 'category',
1648
2991
  context_id: source.id,
1649
2992
  };
1650
- case ProductsSourceType.GroupContext:
2993
+ case exports.ProductsSourceType.GroupContext:
1651
2994
  return {
1652
2995
  context_type: 'group',
1653
2996
  context_id: source.id,
1654
2997
  };
1655
- case ProductsSourceType.VariantOptionContext:
2998
+ case exports.ProductsSourceType.VariantOptionContext:
1656
2999
  return {
1657
3000
  context_type: 'variant_option',
1658
3001
  context_id: source.id,
1659
3002
  };
1660
- case ProductsSourceType.VariantOptionValueContext:
3003
+ case exports.ProductsSourceType.VariantOptionValueContext:
1661
3004
  return {
1662
3005
  context_type: 'variant_option_value',
1663
3006
  context_id: source.id,
@@ -1671,11 +3014,11 @@ function getSortingParams(sorting) {
1671
3014
  return {};
1672
3015
  }
1673
3016
  switch (sorting) {
1674
- case ProductsSorting.Manual:
1675
- case ProductsSorting.NameAsc:
1676
- case ProductsSorting.NameDesc:
1677
- case ProductsSorting.PriceAsc:
1678
- case ProductsSorting.PriceDesc:
3017
+ case exports.ProductsSorting.Manual:
3018
+ case exports.ProductsSorting.NameAsc:
3019
+ case exports.ProductsSorting.NameDesc:
3020
+ case exports.ProductsSorting.PriceAsc:
3021
+ case exports.ProductsSorting.PriceDesc:
1679
3022
  return { order_by: sorting };
1680
3023
  default:
1681
3024
  return {};
@@ -1751,7 +3094,7 @@ function getCategoryIdFromSource(source) {
1751
3094
  if (!source || !source.type || !source.id || source.error) {
1752
3095
  return null;
1753
3096
  }
1754
- else if (source.type !== ProductsSourceType.Category) {
3097
+ else if (source.type !== exports.ProductsSourceType.Category) {
1755
3098
  return null;
1756
3099
  }
1757
3100
  return source.id;
@@ -1762,9 +3105,9 @@ function useProductsList(config, errorHandler) {
1762
3105
  }));
1763
3106
  const store = useStoreStore();
1764
3107
  const cityId = vue.computed(() => store.cityId);
1765
- const sorting = vue.ref(config.value.sorting || ProductsSorting.Manual);
3108
+ const sorting = vue.ref(config.value.sorting || exports.ProductsSorting.Manual);
1766
3109
  const search = vue.ref(config.value.search || '');
1767
- const { filters, load: loadFilters, items: features, } = config.value.filtersType === ProductsFiltersType.Features
3110
+ const { filters, load: loadFilters, items: features, } = config.value.filtersType === exports.ProductsFiltersType.Features
1768
3111
  ? useFeaturesFilters(getCategoryIdFromSource(config.value.source), config.value.filters)
1769
3112
  : useFeaturesGroupsFilters(getCategoryIdFromSource(config.value.source), config.value.filters);
1770
3113
  const { filters: pricesFilters, load: loadPricesRange, range, } = useProductsPricesRangeFilters(getCategoryIdFromSource(config.value.source), config.value.pricesFilters);
@@ -1940,17 +3283,17 @@ function getProductsSource(source) {
1940
3283
  return null;
1941
3284
  }
1942
3285
  switch (injected) {
1943
- case ProductsSourceType.Category:
3286
+ case exports.ProductsSourceType.Category:
1944
3287
  return getInjectedSource(exports.ZenkyInjection.Category, exports.ZenkyInjection.CategoryNotFound, injected);
1945
- case ProductsSourceType.Collection:
3288
+ case exports.ProductsSourceType.Collection:
1946
3289
  return getInjectedSource(exports.ZenkyInjection.Collection, exports.ZenkyInjection.CollectionNotFound, injected);
1947
- case ProductsSourceType.CategoryContext:
3290
+ case exports.ProductsSourceType.CategoryContext:
1948
3291
  return getInjectedSource(exports.ZenkyInjection.CategoryContext, exports.ZenkyInjection.CategoryContextNotFound, injected);
1949
- case ProductsSourceType.GroupContext:
3292
+ case exports.ProductsSourceType.GroupContext:
1950
3293
  return getInjectedSource(exports.ZenkyInjection.GroupContext, exports.ZenkyInjection.GroupContextNotFound, injected);
1951
- case ProductsSourceType.VariantOptionContext:
3294
+ case exports.ProductsSourceType.VariantOptionContext:
1952
3295
  return getInjectedSource(exports.ZenkyInjection.VariantOptionContext, exports.ZenkyInjection.VariantOptionContextNotFound, injected);
1953
- case ProductsSourceType.VariantOptionValueContext:
3296
+ case exports.ProductsSourceType.VariantOptionValueContext:
1954
3297
  return getInjectedSource(exports.ZenkyInjection.VariantOptionValueContext, exports.ZenkyInjection.VariantOptionValueContextNotFound, injected);
1955
3298
  default:
1956
3299
  return null;
@@ -2146,6 +3489,7 @@ exports.getApiErrorHandler = getApiErrorHandler;
2146
3489
  exports.getExistedAddress = getExistedAddress;
2147
3490
  exports.getExistedAddressDisplayValue = getExistedAddressDisplayValue;
2148
3491
  exports.getFirstMedium = getFirstMedium;
3492
+ exports.getProductVariantsPricesRange = getProductVariantsPricesRange;
2149
3493
  exports.getProductsListParamsFromQuery = getProductsListParamsFromQuery;
2150
3494
  exports.getProductsListQueryParams = getProductsListQueryParams;
2151
3495
  exports.getProductsPaginationRequest = getProductsPaginationRequest;
@@ -2155,7 +3499,9 @@ exports.getSelectedAddressDisplayValue = getSelectedAddressDisplayValue;
2155
3499
  exports.getStockAddress = getStockAddress;
2156
3500
  exports.getStockSchedule = getStockSchedule;
2157
3501
  exports.getStorage = getStorage;
3502
+ exports.getUpdatedVariants = getUpdatedVariants;
2158
3503
  exports.getWebsocketsConnection = getWebsocketsConnection;
3504
+ exports.initializeOrderCustomer = initializeOrderCustomer;
2159
3505
  exports.initializePromotions = initializePromotions;
2160
3506
  exports.subscribeToOrderEvents = subscribeToOrderEvents;
2161
3507
  exports.useAddressFormValue = useAddressFormValue;
@@ -2164,22 +3510,29 @@ exports.useArticleItem = useArticleItem;
2164
3510
  exports.useArticlesList = useArticlesList;
2165
3511
  exports.useAuthenticationForm = useAuthenticationForm;
2166
3512
  exports.useAuthenticationStatusChecker = useAuthenticationStatusChecker;
3513
+ exports.useBillSuggestions = useBillSuggestions;
2167
3514
  exports.useCategories = useCategories;
2168
3515
  exports.useCategoriesStore = useCategoriesStore;
2169
3516
  exports.useCategoriesTree = useCategoriesTree;
2170
3517
  exports.useCategory = useCategory;
2171
3518
  exports.useCategoryFeaturesGroupsList = useCategoryFeaturesGroupsList;
2172
3519
  exports.useCategoryFeaturesList = useCategoryFeaturesList;
3520
+ exports.useCheckoutConfirmation = useCheckoutConfirmation;
3521
+ exports.useCheckoutSummary = useCheckoutSummary;
3522
+ exports.useCheckoutTotal = useCheckoutTotal;
2173
3523
  exports.useCollection = useCollection;
2174
3524
  exports.useCollections = useCollections;
2175
3525
  exports.useCollectionsStore = useCollectionsStore;
2176
3526
  exports.useCustomer = useCustomer;
3527
+ exports.useCustomerCheckoutForm = useCustomerCheckoutForm;
2177
3528
  exports.useCustomerStore = useCustomerStore;
2178
3529
  exports.useDadata = useDadata;
2179
3530
  exports.useDataDestroyer = useDataDestroyer;
2180
3531
  exports.useDataLoader = useDataLoader;
2181
3532
  exports.useDeliveryAddressDestroyer = useDeliveryAddressDestroyer;
2182
3533
  exports.useDeliveryAddressesList = useDeliveryAddressesList;
3534
+ exports.useDeliveryCheckoutForm = useDeliveryCheckoutForm;
3535
+ exports.useDeliveryPaymentsForm = useDeliveryPaymentsForm;
2183
3536
  exports.useEditAddressForm = useEditAddressForm;
2184
3537
  exports.useFeaturesFilters = useFeaturesFilters;
2185
3538
  exports.useFeaturesGroupsFilters = useFeaturesGroupsFilters;
@@ -2188,15 +3541,22 @@ exports.useItemLoader = useItemLoader;
2188
3541
  exports.useListLoader = useListLoader;
2189
3542
  exports.useLogin = useLogin;
2190
3543
  exports.useLogout = useLogout;
3544
+ exports.useModifiers = useModifiers;
3545
+ exports.useModifiersGroup = useModifiersGroup;
2191
3546
  exports.useOfferItem = useOfferItem;
2192
3547
  exports.useOffersList = useOffersList;
3548
+ exports.useOrderCheckout = useOrderCheckout;
2193
3549
  exports.useOrderItem = useOrderItem;
3550
+ exports.useOrderStore = useOrderStore;
3551
+ exports.useOrderVariants = useOrderVariants;
2194
3552
  exports.useOrdersList = useOrdersList;
2195
3553
  exports.usePaginatedLoader = usePaginatedLoader;
2196
3554
  exports.usePasswordReset = usePasswordReset;
2197
3555
  exports.usePaymentMethodDestroyer = usePaymentMethodDestroyer;
2198
3556
  exports.usePaymentMethodsList = usePaymentMethodsList;
2199
3557
  exports.usePhoneConfirmation = usePhoneConfirmation;
3558
+ exports.useProductCard = useProductCard;
3559
+ exports.useProductItem = useProductItem;
2200
3560
  exports.useProductsList = useProductsList;
2201
3561
  exports.useProductsPricesRange = useProductsPricesRange;
2202
3562
  exports.useProductsPricesRangeFilters = useProductsPricesRangeFilters;
@@ -2204,7 +3564,13 @@ exports.usePromotionsStore = usePromotionsStore;
2204
3564
  exports.useRegistration = useRegistration;
2205
3565
  exports.useReversedTree = useReversedTree;
2206
3566
  exports.useRewardsSelector = useRewardsSelector;
3567
+ exports.useSingleModifier = useSingleModifier;
2207
3568
  exports.useSplittedCategoriesTree = useSplittedCategoriesTree;
2208
3569
  exports.useStateDataLoader = useStateDataLoader;
2209
3570
  exports.useStoreStore = useStoreStore;
2210
3571
  exports.useSuggestionsRestrictedToCity = useSuggestionsRestrictedToCity;
3572
+ exports.useTotalPrice = useTotalPrice;
3573
+ exports.useTotalQuantity = useTotalQuantity;
3574
+ exports.useVariant = useVariant;
3575
+ exports.useVariantControls = useVariantControls;
3576
+ exports.useVariantPrice = useVariantPrice;