# Корзина, доставка и оплата Модуль Commerce управляет корзиной, оформлением заказа, доставкой, оплатой, купонами, избранным, просмотренными товарами и историей заказов покупателя. ## Акции корзины Раздел `Магазин -> Настройки -> Акции` создаёт автоматические правила без промокода. Поддерживаются два основных сценария: - покупатель кладёт в корзину основной товар и получает скидку на связанный товар; - покупатель кладёт в корзину нужный товар, а Commerce автоматически добавляет другой товар в подарок. Для настройки нажмите **Акция** и заполните две части. 1. В блоке **Когда применять** выберите конкретные товары, разделы каталога либо любую позицию корзины. Укажите необходимое количество и, если нужно, минимальную сумму корзины. 2. В блоке **Что дать покупателю** выберите процентную скидку, скидку в рублях, специальную цену или товар в подарок. 3. Для скидки выберите товары либо разделы, на которые она распространяется. Для подарка найдите один реальный товар каталога. 4. При необходимости задайте срок, максимальное число срабатываний в одной корзине и приоритет. 5. Включите **Разрешить купон**, только если акция должна суммироваться с промокодом. Один комплект условия даёт указанное количество позиций со скидкой или подарков. Например, при количестве условия `1` и результате `1` две исходные позиции дадут скидку двум связанным позициям. Поле **Не более срабатываний** ограничивает это число; `0` означает без ограничения. При пересечении нескольких скидок одна товарная строка получает наиболее выгодную из них. Подарок является обычным товаром каталога с нулевой ценой: он участвует в расчёте упаковки, сохраняется в составе заказа и автоматически исчезает из корзины, если условие акции больше не выполняется. Покупатель не может вручную изменить количество или удалить подарок. Каждый созданный заказ хранит исходные и итоговые цены, название акции и признак подарка. Отдельный журнал применения позволяет увидеть правило в карточке заказа даже после последующего изменения или удаления самой акции. ## Журнал онлайн-оплаты Вкладка **Журнал оплат** показывает всю последовательность работы платёжного шлюза: начало создания платежа, полученный идентификатор транзакции, callback, подтверждение, ошибку и возврат. Те же события показаны в карточке конкретного заказа в блоке **История оплаты**. Если callback не пришёл, откройте заказ и нажмите **Проверить оплату**. Кнопка появляется только у онлайн-оплаты с уже созданной транзакцией и у шлюза, который умеет запрашивать статус. Commerce обращается непосредственно к платёжной системе, проверяет номер транзакции, сумму и валюту. Только подтверждённый ответ может отметить заказ оплаченным. Повторное нажатие безопасно: событие успешной оплаты отправляется модулям один раз. Журнал нужен для разбора спорного платежа без доступа к системным логам. В нём нет API-ключей, подписей, email, телефонов, полных callback и банковских ответов. Хранятся только номер заказа, шлюз, транзакция, этап, статус, сумма, валюта, безопасный код результата и время. Запись журнала не останавливает оплату при временной проблеме самой таблицы: ошибка попадёт в серверный журнал, а запрос к платёжной системе продолжится. После обновления Commerce сначала примените его миграцию, создающую таблицу журнала. ## Сохранённые фильтры заказов Над таблицей заказов находится список **Сохранённые представления**. Он нужен для повторяющихся рабочих очередей, например «Новые неоплаченные» или «Заказы на возвратный звонок». 1. Настройте поиск, статус и признак оплаты обычными фильтрами. 2. Нажмите кнопку с закладкой и плюсом. 3. Введите понятное название и сохраните. 4. В следующий раз выберите это название в списке — фильтры применятся сразу. Если сохранить другой набор под тем же названием, старое представление обновится. Кнопка корзины удаляет выбранный набор. Представления персональные: другие сотрудники их не видят, а сами заказы и настройки магазина не изменяются. ## Заказ, созданный менеджером Сотрудник с правом `manage_orders` может создать заказ без публичной корзины: 1. Откройте `Заказы` и нажмите **Создать заказ** справа от заголовка. 2. Найдите существующего пользователя сайта по имени, телефону, email или ID. Его профиль будет связан с заказом, а контакты заполнятся автоматически. Если покупатель ещё не зарегистрирован, заполните имя и телефон либо email вручную. 3. Найдите товары по названию, артикулу или ID. Каждый вариант товара является отдельной позицией, поэтому в заказ попадут правильные артикул, URL и характеристики варианта. 4. Укажите количество. Поле цены можно изменить: новая сумма действует только внутри этого заказа и не меняет цену товара в каталоге. 5. Если для сочетания товаров действует акция, форма покажет её автоматически. Например, после добавления кровати и матраса появится скидка на матрас или подарок. Расчёт использует выбранное количество и цену позиции, в том числе цену, которую менеджер изменил только для этого заказа. 6. При необходимости задайте отдельную скидку менеджера, доставку, оплату, статус и признак оплаты. Сначала применяется акция, затем скидка менеджера, после этого считаются доставка и платёжная наценка. 7. После сохранения откроется карточка заказа. В ней видны каталожная и ручная цена, скидка, автор создания и история действий. Заказ хранит снимок позиции на момент создания: ID документа, название, артикул, изображение, вариант, каталожную цену, цену менеджера, количество и сумму. Последующие изменения товара не переписывают этот снимок. Выбор **Отправить письмо** запускает обычные уведомления Commerce после успешного создания заказа; платёжный переход автоматически не открывается. Предварительный расчёт в панели нужен для удобства, но не является источником истины. При сохранении сервер заново проверяет действующие акции. Поэтому устаревший экран или изменённые в браузере суммы не смогут сохранить неверную скидку. Сработавшее правило и подарок записываются в карточку заказа так же, как при оформлении через публичную корзину. ## Изменение состава заказа В карточке сохранённого заказа нажмите **Изменить состав**. Редактор позволяет: - найти и добавить новый товар или конкретный вариант; - изменить количество и цену отдельной позиции; - удалить лишнюю позицию; - задать общую скидку менеджера; - увидеть новый итог до сохранения. Редактор сохраняет сведения, которые уже попали в заказ. Название, артикул, изображение и характеристики старой позиции не заменяются данными из каталога. Это важно для истории: заказ продолжает показывать именно тот товар, который покупатель выбрал в момент оформления. Актуальные данные каталога используются только для новых позиций. Купонные и другие ранее рассчитанные скидки показаны отдельно и не перезаписываются скидкой менеджера. Стоимость доставки и доплаты также сохраняются. После изменения Commerce пересчитывает сумму товаров и общий итог. Каждое сохранение попадает в историю заказа и общий аудит. Там видно, какие позиции были добавлены, удалены или изменены. Состав нельзя менять у оплаченного или отменённого заказа, а также после создания платёжной транзакции. Это защищает уже зафиксированные расчёты. Модули могут проверить или дополнить операцию через хуки: - `commerce.order.composition_updating` — перед записью; - `commerce.order.composition_updated` — после успешного сохранения. В оба хука передаются заказ, предыдущий и новый состав, рассчитанные суммы, точная разница и ID сотрудника. ## Мини-корзина в шаблоне сайта Вставьте нужный тег в общий шаблон сайта, системный блок или другое место, где обрабатываются публичные теги AVE.cms: | Тег | Что выводит | | --- | --- | | `[mod_basket]` | Мини-корзину: количество товаров, сумму и ссылку на корзину | | `[mod_basket:mini]` | То же, что `[mod_basket]` | | `[mod_basket:fav]` | Компактную ссылку и счётчик избранного | | `[mod_basket:viewed]` | Компактную ссылку и счётчик просмотренных товаров | HTML этих элементов редактируется в `Магазин -> Настройки -> Шаблоны`: `mini.twig`, `favorites_mini.twig` и `viewed_mini.twig`. Там же находятся шаблоны полной корзины, оформления, избранного и истории заказов. Редактор работает с тем же файлом, который использует публичный сайт. Если активная тема переопределяет шаблон в `templates/<тема>/views/system_basket/`, редактируется именно файл темы; в остальных случаях — системный файл из `modules/commerce/app/Basket/view/`. Карточка шаблона показывает источник и путь. После сохранения кеш Twig очищается, поэтому изменения применяются сразу. ## Данные Twig-шаблонов В редакторе каждого шаблона есть раскрывающийся блок «Доступные данные». Нажатие на переменную вставляет её в текущую позицию CodeMirror. Twig по умолчанию экранирует HTML. Фильтр `|raw` применяйте только к подготовленной движком разметке, например `{{ product_cards.html|raw }}`. Во всех публичных шаблонах, кроме писем, доступны настроенные адреса: - `{{ basket_urls.cart }}` — корзина; - `{{ basket_urls.checkout }}` — оформление; - `{{ basket_urls.favorites }}` — избранное; - `{{ basket_urls.viewed }}` — просмотренные товары; - `{{ basket_urls.orders }}` — история заказов. Также доступен объект `basket_pages`. Например, `{{ basket_pages.cart.title }}` выводит заголовок страницы корзины. У каждой страницы `cart`, `checkout`, `favorites`, `viewed` и `orders` есть свойства `path`, `title`, `description` и `template_id`. ### Мини-корзина: `mini.twig` - `{{ basket.quantity }}` — общее количество товаров; - `{{ basket.total }}` — сумма товаров. - `{{ basket.total_order }}` — сумма после автоматических скидок и купона. ### Товар добавлен: `added.twig` Объект `product`: `id`, `name`, `article`, `alias`, `image`, `price`, `quantity`, `amount`. Пример: `{{ product.name }}` и `{{ product.price|number_format(0, '.', ' ') }}`. ### Корзина: `cart.twig` - `basket.products` — массив товарных позиций; - `basket.quantity` — общее количество; - `basket.gift_quantity` — количество подарков; - `basket.total` — сумма без скидки; - `basket.promotion_discount` — скидки автоматических акций; - `basket.coupon_discount` — скидка купона; - `basket.discount` — общая скидка; - `basket.total_order` — итог после всех скидок; - `basket.promotions` — сработавшие правила; - `basket.coupon_blocked` — текущая акция запрещает купон. Поля `product.*` доступны внутри цикла: ```twig {% for product in basket.products %} {{ product.name }} — {{ product.quantity }} × {{ product.price }} {% endfor %} ``` Кроме показанных в примере значений доступны `product.hash` для изменения и удаления позиции и `product.price_field_id` для кнопок добавления товара. Для акций используются `product.base_price`, `product.base_amount`, `product.final_price`, `product.final_amount`, `product.promotion_title` и `product.is_gift`. У подарка `final_price` и `final_amount` равны нулю. ### Оформление: `checkout.twig` - `csrf` — токен формы; - `basket.*` и `basket.products` — текущая корзина; - `basket.shipment.ready` — готовы ли упаковки к расчёту; - `basket.shipment.summary.places` — число грузовых мест; - `basket.shipment.summary.weight_kg` — общий вес; - `basket.shipment.summary.volume_m3` — общий объём; - `options.delivery` — способы доставки; - `options.payment` — способы оплаты; - `options.payment_enabled` — доступна ли оплата; - `options.show_basket` — нужно ли показывать состав; - `values.first_name`, `values.phone`, `values.email` и другие введённые поля; - `values.last_name`, `values.region`, `values.city`, `values.postcode`, `values.address`, `values.description` — остальные поля покупателя; - `errors._form`, `errors.first_name`, `errors.phone`, `errors.email`, `errors.city` — ошибки. - `errors.account` — ошибка входа или регистрации из оформления; - `account.logged_in` и `account.user` — состояние текущего покупателя; - `account.registration.enabled`, `policy_url`, `policy_label` — возможность создать кабинет и данные обязательного согласия; - `account.login_url`, `account.remember_url`, `account.profile_url` — адреса входа, восстановления пароля и профиля. В цикле `options.delivery` доступны поля способа, например `item.id`, `item.delivery_title`, `item.delivery_price`. В цикле `options.payment` — `item.id`, `item.payment_title`, `item.payment_description`, `item.payment_delivery`. В цикле `basket.shipment.missing_products` доступны `item.product_id` и `item.title`. ### Успешный заказ `success.twig` и `one_click_success.twig` получают объект `order`. Основные поля: `order_id`, `order_total`, `order_first_name`, `order_last_name`, `order_phone`, `order_email`, `delivery_title`, `payment_title`, `order_description`. ### Покупка в один клик: `one_click.twig` Доступны `csrf` и объект `product` с теми же полями, что в `added.twig`. ### Избранное и просмотренные `favorites.twig` и `viewed.twig` получают: - `products` — массив товаров; - `products_count_label` — готовое количество с правильным склонением, например `1 товар` или `12 товаров`; - `product_cards.handled` — признак, что товарный модуль подготовил карточки; - `product_cards.html` — готовая разметка карточек; - `product.*` внутри цикла `products`. Если дизайн карточек управляется товарным модулем, используйте: ```twig {% if product_cards.handled %} {{ product_cards.html|raw }} {% else %} {# собственный цикл products #} {% endif %} ``` `favorites_mini.twig` и `viewed_mini.twig` получают только `{{ count }}`. Страница избранного поддерживает массовые AJAX-действия. Кнопка с атрибутом `data-favorites-add-all` добавляет в корзину только те доступные товары, которых там ещё нет. Повторное нажатие не увеличивает их количество. Кнопка `data-favorites-clear` очищает список и сразу обновляет страницу и счётчик в шапке без перезагрузки. История просмотренных товаров заполняется автоматически при открытии публичной карточки. Products вызывает hook `catalog.product.viewed`, а Commerce сохраняет ID в сессии и, для вошедшего покупателя, в его аккаунте. Если история отключена в настройках Commerce, hook остаётся безопасным и не влияет на вывод товара. Кнопка с атрибутом `data-viewed-clear` очищает историю AJAX-запросом и сразу обновляет страницу и счётчик в шапке. Commerce также дополняет общий view model товарной карточки свойством `item.favorite`. Благодаря этому опубликованный шаблон карточки может сохранить состояние кнопки после перезагрузки: ```twig ``` ### История и страница заказа `orders.twig` получает массив `orders`. Внутри цикла доступны `order.id`, `order.order_id`, `order.order_type`, `order.order_published`, `order.status_name`, `order.status_color`, `order.order_pay`, `order.order_total`, `order.items_count`, массив до четырёх сохранённых изображений `order.product_thumbs` и число остальных позиций `order.products_more`. `order.twig` получает один объект `order`, его массив `order.products` и `csrf` для повторной оплаты. У товара заказа доступны `name`, `title`, `price`, `quantity`, `amount` и нормализованное `image`. У заказа также доступны `status_tone` (`new`, `progress`, `done`, `cancel`), номер текущего этапа `status_step` от 0 до 3 и признак `status_cancelled`. `order_not_found.twig` специальных данных не получает: это статичная страница, но настроенные `basket_urls.*` в ней доступны. ### Письма `mail_admin.twig` и `mail_client.twig` получают `order` и `settings`. Кроме полей заказа доступны `{{ settings.from_name }}` и `{{ settings.from_email }}`. Публичные `basket_urls.*` в письма намеренно не добавляются. ## Сервисы доставки 1. Откройте `Магазин -> Настройки -> Доставка`. 2. В блоке «Сервисы расчёта» откройте нужного перевозчика и укажите API-ключи, город или индекс отправителя. 3. Включите расчёт и сохраните сервис. 4. В блоке «Способы доставки» создайте или откройте способ получения заказа. 5. Выберите сервис расчёта и нужный тип выдачи: курьер, ПВЗ или терминал. Назначение сервиса способу доставки не включает его автоматически. Чтобы расчёт работал, в карточке сервиса должны быть заполнены обязательные реквизиты и включён переключатель «Включить расчёт». Секретные значения после сохранения не показываются; пустое поле при повторном сохранении оставляет прежний секрет. Кнопка «Удалить реквизиты» удаляет настройки выбранного перевозчика. Доступны СДЭК, ПЭК, Boxberry и Деловые Линии. Расчёт является справочным и сам по себе не меняет итог заказа. Фиксированная стоимость способа доставки продолжает работать независимо от API. На странице оформления расчёт запускается автоматически после выбора способа доставки и заполнения города или индекса. Под названием способа показываются ориентировочная цена перевозчика и срок. Эта сумма информационная: в итог заказа по-прежнему входит только стоимость, заданная у способа доставки. СДЭК может рассчитать тариф по городу или индексу, Boxberry — курьерский тариф по индексу. Для ПЭК, Деловых Линий и выбора конкретного ПВЗ публичная форма должна дополнительно передать внутренний код пункта назначения сервиса: `pecom_city_id`, `dellin_kladr` или `boxberry_pvz`. Эти поля можно добавить в собственный `checkout.twig`; движок уже принимает их и передаёт gateway. ## Платёжные сервисы ПСБ и YooKassa устанавливаются отдельными модулями. Их ключи настраиваются на странице соответствующего модуля. Чтобы связать сервис с вариантом оплаты: 1. Откройте `Магазин -> Настройки -> Оплата`. 2. Убедитесь, что сервис помечен как подключённый. Кнопка со стрелкой открывает его реквизиты. 3. Откройте способ оплаты и выберите платёжный сервис. 4. При необходимости ограничьте способ оплаты выбранными способами доставки. Если сервис не выбран, способ считается оплатой без онлайн-перехода: например, наличными, картой при получении или банковским переводом. ## Публичные страницы Адреса корзины, оформления, избранного, просмотренных товаров и личных заказов настраиваются в `Магазин -> Настройки -> Публичные страницы`. Для каждой страницы можно выбрать общий шаблон сайта. Commerce подставляет содержимое страницы в его `[tag:maincontent]`. ## Вход и регистрация при оформлении Commerce использует общую систему публичных пользователей, а не создаёт отдельных «покупателей корзины». 1. Если посетитель уже вошёл, новый заказ сразу получает его ID и появляется в истории заказов. 2. Если введённый email уже зарегистрирован, форма предлагает войти прямо на странице оформления. После входа текущая корзина сохраняется, несмотря на смену ID сессии. 3. Для нового email показывается необязательный переключатель создания личного кабинета. Согласие с политикой становится обязательным только после включения этого переключателя. 4. После заказа аккаунт сразу открыт в текущем браузере, а данные имени, телефона, города, адреса и индекса становятся начальными данными профиля. 5. Постоянный пароль не отправляется по почте. Покупатель получает одноразовую ссылку и задаёт пароль самостоятельно. Если письмо не дошло, доступ можно восстановить обычной формой «Не помню пароль». Сценарий включается в `Система -> Пользователи сайта -> Регистрация`, в блоке **Аккаунт после заказа**. Там же задаются текст согласия, адрес политики и Twig- шаблон письма. Если публичная регистрация или этот сценарий выключены, оформление заказа продолжает работать в гостевом режиме.