Skip to content
  1. Компоненты
  2. MiniShop3
  3. Интерфейс админки
  4. Настройки
  5. Доставки

Способы доставки ​

Настройки → Доставки

Откройте Пакеты → MiniShop3 → Настройки → Варианты доставки.

Поля доставки ​

ПолеТипОписание
namestringНазвание способа доставки
descriptiontextОписание для покупателя
pricestringДоплата за доставку: фиксированная сумма, процент от стоимости корзины (5%) или отрицательное значение-скидка. Допустимый процент — от −100 до 100, иначе доплата обнуляется с записью в журнал. В интерфейсе поле подписано «Доп. стоимость». В БД — varchar(11)
weight_pricefloatСтоимость за единицу веса
distance_pricefloatСтоимость за единицу расстояния — в расчёте сейчас не используется (резервное поле)
free_delivery_amountfloatСумма заказа для бесплатной доставки
logostringПуть к изображению
positiontinyint unsignedПорядок сортировки. В карте модели колонка объявлена как tinyint(1) unsigned, то есть предел — 255 записей. У способов оплаты та же колонка int, это историческое расхождение
activeboolАктивность
classstringPHP-класс обработчика
validation_rulesJSONПравила валидации полей
propertiesJSONJSON-свойства для своих данных обработчика

Диалог доставки ​

Форма разбита на вкладки:

  • Доставка — название, описание, логотип, активность;
  • Настройки — класс обработчика, доплата, цена за вес, порог бесплатной доставки, правила валидации;
  • Способы оплаты — переключатели связей. Вкладка появляется только при правке: у доставки без сохранения нет id, к которому привязывать оплату.

Связи пишутся сразу по переключению, через POST и DELETE /api/mgr/deliveries/{id}/payments, и не ждут кнопки сохранения.

Связь с оплатой ​

Каждый способ доставки связываете с нужными способами оплаты.

  • Наличные только для самовывоза
  • Онлайн-оплата для курьерской доставки
  • Свои комбинации для разных регионов

Когда покупатель выбирает доставку, список оплат фильтруется.

Расчёт стоимости ​

Стоимость доставки рассчитывается по формуле:

text
Итоговая стоимость = weight_price × вес + (price как сумма или процент от корзины)

Вес заказа — сумма weight × count по всем позициям. Отрицательный weight_price обнуляется с записью в журнал.

Если порог бесплатной доставки задан (free_delivery_amount больше нуля) и сумма заказа больше или равна ему, стоимость доставки равна 0. Значение поля по умолчанию — 0, то есть при незаполненном пороге бесплатной доставки не будет никогда.

Свой расчёт ​

Для сложной логики создайте свой класс-обработчик. Контрактов два, и они независимы:

ИнтерфейсМетодДля чего
DeliveryProviderInterfacegetCost()Расчёт стоимости. Класс указывают в поле class способа доставки
ShipmentProviderInterfaceverifyWebhook(), parseWebhook()Приём уведомлений службы доставки: проверка подписи и разбор события в ShipmentWebhookEvent

Уведомления принимает публичный маршрут ядра POST /api/v1/delivery/webhook/{delivery_id} — токен на нём не требуется, подлинность проверяет сам обработчик. Класс только для расчёта реализует первый интерфейс, о приёме уведомлений он ничего не знает.

Пример расчёта:

php
<?php
namespace MyComponent\Delivery;

use MiniShop3\Controllers\Delivery\DeliveryProviderInterface;
use MiniShop3\Model\msDelivery;
use MiniShop3\Model\msOrder;

class CustomDelivery implements DeliveryProviderInterface
{
    public function getCost(msOrder $order, msDelivery $delivery, float $cost): float
    {
        // Ваша логика расчёта
        $cartCost = $order->get('cart_cost');
        $weight = $order->get('weight');

        if ($cartCost > 10000) {
            return 0; // Бесплатно от 10000 руб
        }

        if ($weight > 5000) {
            return 500 + ($weight - 5000) * 0.1; // Наценка за тяжёлый заказ
        }

        return 300; // Базовая стоимость
    }
}

Укажите класс в поле class: MyComponent\Delivery\CustomDelivery.

Валидация полей заказа ​

Для каждого способа доставки задаёте обязательные поля и правила: полный адрес для курьера, телефон для самовывоза.

Визуальный конструктор ​

Визуальный режим ​

  1. Нажмите Добавить поле
  2. Выберите поле из списка (группы: Заказ, Адрес)
  3. Добавьте правила валидации для поля
  4. Для правил с параметрами укажите значение

Правила отображаются тегами, удаляются кликом по крестику.

JSON-режим ​

Переключатель открывает ручное редактирование JSON:

json
{
  "phone": "required",
  "email": "required|email",
  "city": "required|min:2",
  "street": "required|min:3",
  "building": "required"
}

В этом режиме правила копируют между доставками, задают regex и переносят JSON между установками.

Свои поля валидации ​

Кроме стандартных полей заказа и адреса можно добавить произвольные, например чекбокс согласия:

json
{
  "phone": "required",
  "email": "required|email",
  "agreement": "required|accepted"
}

Поля вне стандартного набора (agreement и другие) сохраняются в черновике заказа между шагами order/add и order/submit. При создании заказа они уходят в события msOnBeforeCreateOrder / msOnCreateOrder через параметр customFields.

Чекбоксы

На фронтенде чекбоксы отправляют состояние input.checked ('1' или '0'), а не статический атрибут value. Это обеспечивает корректную работу правила accepted.

Доступные поля для валидации ​

Поля заказа ​

ПолеОписание
order_commentКомментарий к заказу

Поля адреса ​

ПолеОписание
first_nameИмя
last_nameФамилия
phoneТелефон
emailEmail
countryСтрана
indexПочтовый индекс
regionРегион/область
cityГород
metroСтанция метро
streetУлица
buildingДом/строение
entranceПодъезд
floorЭтаж
roomКвартира/офис
commentКомментарий к адресу
text_addressПолный адрес текстом

Правила валидации ​

Валидацию выполняет встроенный PipeRuleValidator (src/Services/Validation/PipeRuleValidator.php), внешние библиотеки не нужны. Правила комбинируются через |.

Базовые правила ​

ПравилоОписание
requiredОбязательное поле
nullableПоле может быть null
presentПоле должно присутствовать (даже пустое)
acceptedЗначение должно быть "yes", "on", "1", true

Типы данных ​

ПравилоОписание
emailВалидный email
urlВалидный URL
ipIP адрес (v4 или v6)
ipv4IPv4 адрес
ipv6IPv6 адрес
numericЧисловое значение
integerЦелое число
booleanБулево значение
arrayМассив
jsonВалидный JSON

Строковые правила ​

ПравилоОписание
alphaТолько буквы
alpha_numБуквы и цифры
alpha_dashБуквы, цифры, дефис, подчёркивание
alpha_spacesБуквы и пробелы
uppercaseТолько заглавные буквы
lowercaseТолько строчные буквы

Правила с параметрами ​

ПравилоОписаниеСинтаксис
minМинимальная длина строки или значение числаmin:3
maxМаксимальная длина строки или значение числаmax:100
betweenЗначение в диапазонеbetween:1,10
digitsТочное количество цифрdigits:6
digits_betweenКоличество цифр в диапазонеdigits_between:4,8
inЗначение из спискаin:pickup,courier,post
not_inЗначение НЕ из спискаnot_in:test,demo
sameСовпадает с другим полемsame:email_confirm
differentОтличается от другого поляdifferent:old_password
regexСоответствует регулярному выражениюregex:/^[0-9]{6}$/
extensionРасширение файлаextension:jpg,png
mimesMIME-тип файлаmimes:jpg,png

Правила для дат ​

ПравилоОписаниеСинтаксис
dateВалидная дата в форматеdate:Y-m-d
afterДата после указаннойafter:2024-01-01
beforeДата до указаннойbefore:2025-12-31

Условные правила ​

ПравилоОписаниеСинтаксис
required_ifОбязательно, если другое поле = значениюrequired_if:delivery,courier
required_unlessОбязательно, если другое поле ≠ значениюrequired_unless:delivery,pickup
required_withОбязательно, если указано другое полеrequired_with:phone
required_withoutОбязательно, если НЕ указано другое полеrequired_without:email
required_with_allОбязательно, если указаны ВСЕ поляrequired_with_all:city,street
required_without_allОбязательно, если НЕ указано НИ ОДНО полеrequired_without_all:phone,email

У части правил нет своего текста ошибки

Проверка работает, но текста в PipeRuleValidator::DEFAULT_MESSAGES нет у after, before, digits_between и всех шести required_*. При провале покупатель увидит общее «The :attribute has unsupported validation rule», а задать свой текст нельзя (см. Сообщения об ошибках).

Примеры конфигураций ​

Одинаковые строки правил схлопываются

OrderFieldManager::validate() склеивает наборы через array_unique(array_merge(...)), а array_unique работает по значениям. Если двум полям задана одна и та же строка правил, в наборе останется только первое — у остальных форматные проверки (min, email, regex) не применятся. Обязательность при этом всё равно проверяется на сабмите другим путём.

В примерах ниже такие повторы есть; чтобы правила работали для каждого поля, строки должны различаться — например required|min:2 и required|min:3. Дефект заведён как modx-pro/MiniShop3#949.

Курьерская доставка ​

Требуется полный адрес:

json
{
  "first_name": "required|min:2",
  "last_name": "required|min:2",
  "phone": "required|regex:/^\\+?[0-9]{10,15}$/",
  "email": "required|email",
  "city": "required|min:2",
  "street": "required|min:3",
  "building": "required",
  "room": "required_if:building_type,apartment"
}

Самовывоз ​

Минимум данных для связи:

json
{
  "first_name": "required|min:2",
  "phone": "required"
}

Почтовая доставка ​

Требуется индекс и полный адрес:

json
{
  "first_name": "required",
  "last_name": "required",
  "phone": "required",
  "index": "required|digits:6",
  "region": "required",
  "city": "required",
  "street": "required",
  "building": "required"
}

Доставка в постамат ​

Только контактные данные:

json
{
  "first_name": "required",
  "phone": "required|regex:/^\\+?[0-9]{10,15}$/",
  "email": "required|email"
}

Поля в конструкторе ​

Список полей в конструкторе правил не статичен: к встроенному набору добавляются поля msOrder и msOrderAddress из «Полей форм (админка)» и из «Своих полей». Технические колонки вроде id и служебных дат из списка исключаются.

То есть своё поле заказа, заведённое в утилитах, появляется в конструкторе само — отдельно прописывать его не нужно.

Сообщения об ошибках ​

Сообщения по умолчанию английские: язык интерфейса они не читают. Для полей заказа действуют четыре текста, зашитые в OrderFieldManager::validate() — для required, numeric, min и email; остальные приходят из PipeRuleValidator::DEFAULT_MESSAGES.

Своих сообщений задать нельзя

setValidationMessages() и setValidationRules() затираются в начале каждого вызова validate(), а properties доставки валидация не читает вовсе: источником правил служит только колонка validation_rules. Рабочего способа подменить тексты в текущем коде нет — дефект заведён как modx-pro/MiniShop3#950.

Manager API ​

Эндпоинты админки. Вся группа закрыта правом mssetting_save.

МетодРоутОписание
GET/api/mgr/deliveriesСписок. Фильтры filter_name и filter_active
POST/api/mgr/deliveriesСоздать
GET / PUT / DELETE/api/mgr/deliveries/{id}Чтение, правка, удаление
DELETE/api/mgr/deliveries/bulkМассовое удаление
POST/api/mgr/deliveries/sortПорядок после перетаскивания
PUT/api/mgr/deliveries/positionsПроставить позиции списком
POST / DELETE/api/mgr/deliveries/{id}/paymentsСвязать и отвязать способ оплаты
GET/api/mgr/deliveries-activeАктивные доставки для выпадающих списков формы заказа. Отдаёт белый список полей, без properties и прочих секретов

У оплат набор зеркальный: /api/mgr/payments, /sort, /positions, /bulk, /{id}/deliveries и GET /api/mgr/payments-active (принимает delivery_id, чтобы оставить только связанные).

Web API ​

Оба GET-запроса идут с токеном магазина: заголовок Bearer/MS3Token или кука ms3_token. Query-параметром токен не принимается — TokenMiddleware вырезает его из строки запроса. Передавать токен вручную при этом не обязательно: на маршруте без токена сервер сам выпустит анонимный, так что запрос не упадёт.

Правила возвращаются только для активной доставки

Выборка правил идёт с условием active = 1. Для выключенного способа доставки оба эндпоинта вернут пустой набор, и это неотличимо от «у доставки нет своих правил».

Получение правил валидации ​

http
GET /api/v1/order/delivery/validation-rules?delivery_id=1

Ответ:

json
{
  "success": true,
  "data": {
    "validation_rules": {
      "phone": "required",
      "city": "required|min:2",
      "street": "required"
    }
  }
}

Получение обязательных полей ​

http
GET /api/v1/order/delivery/required-fields?delivery_id=1

Ответ (карта поле → правила, только с required):

json
{
  "success": true,
  "data": {
    "requires": {
      "phone": "required",
      "city": "required",
      "street": "required"
    }
  }
}

Эти два запроса обновляют форму заказа при смене способа доставки.