Обработка заказов
Обработка заказов в Shop-Script состоит в том, что над заказом выполняется действие, которое либо изменяет статус заказа, либо оставляет его статус без изменения. Действие может выполнять и другие операции на усмотрение разработчика. Информация о выполненном действии сохраняется в историю обработки заказа.
Все действия и статусы, настроенные в магазине, называются потоком. Настройки потока хранятся в конфигурационном файле wa-config/apps/shop/workflow.php
. Если этого файла нет, то используется стандартный файл wa-apps/shop/lib/config/data/workflow.php
.
Статус заказа
Статус заказа определяет то, какое текстовое обозначение написано рядом с номером заказа в бекенде и в личном кабинете покупателя. В бекенде статус влияет также на то, какие действия можно совершать с заказом в данный момент.
Каждый статус — это запись в актуальном файле workflow.php
.
Пример
array( 'name' => 'Какой-то статус', 'options' => array( // CSS-класс иконки статуса 'icon' => 'icon16 ss new', // CSS-стили для показа заказов в этом статусе 'style' => array( 'color' => '#009900', 'font-weight' => 'bold', ), ), // действия, доступные в этом статусе 'available_actions' => array( 'process', 'pay', 'ship', 'complete', 'comment', 'edit', 'editshippingdetails', 'message', 'delete', ), // стандартное имя класса статуса, которое обычно не стоит изменять 'classname' => 'shopWorkflowState', ),
Действие с заказом
Действие определяет:
- как нужно изменить статус заказа,
- должен ли пользователь заполнить форму при выполнении действия,
- должно ли произойти что-то еще, кроме или вместо смены статуса заказа.
От настроек действия зависит, чем оно должно выполняться: кнопкой или ссылкой, и где именно на странице заказа в бекенде должен находиться этот элемент.
Действия могут выполняться пользователями явно либо скрытым образом, без прямого участия пользователя. Например, действия «Редактировать» или «В обработку» выполняются пользователем. А действия «Создать» и «Ответ платежной системы (callback)» выполняются программным способом — для их выполнения нет отдельных кнопок в интерфейсе пользователя .
Простое действие со стандартной функциональностью — это только запись в файле workflow.php
.
Пример
array( 'name' => 'Обработать', 'options' => array( // Где размещать кнопку или ссылку на действие на странице заказа // '': кнопка действия с цветовой рамкой // 'top': ссылка с иконкой в боковой панели // 'bottom': ссылка с иконкой над историей заказа 'position' => 'bottom', // CSS-класс иконки действия 'icon' => 'add', // CSS-класс кнопки или ссылки 'button_class' => 'inline-link', // Описание действия в истории обработки заказа 'log_record' => 'Добавлен комментарий к заказу', // Если указать true, действие сразу покажет форму // на странице заказа вместо кнопки или ссылки 'head' => false, // Если указать true, то после нажатия на кнопку // действие покажет веб-форму для ввода данных 'html' => false, ), // В какой статус переводить заказ // null: оставить статус без изменения 'state' => null, // true: пометить действие как системное, // не предназначенное для выполнения пользователем 'internal' => false, // PHP-класс действия 'classname' => 'shopWorkflowAction', ),
Стандартная логика выполнения действия описана в классе shopWorkflowAction
. Имя этого класса по умолчанию добавляется в запись о каждом новом действии в файл workflow.php
, как показано в примере.
Нестандартные действия
Если вам нужна нестандартная логика для действия с заказами, замените для него в файле workflow.php
имя базового класса shopWorkflowAction
на собственный класс, унаследованный от него.
Если новое действие с собственным классом добавляется плагином, то плагин должен корректно обновить конфигурацию потока на случай удаления плагина. Иначе в файле workflow.php
останутся ссылки на более не существующий класс удаленного плагина, а это вызовет ошибку в работе магазина.
В собственном классе действия нужно реализовать обязательный публичный метод execute()
. Реализовывать и переопределять другие методы необязательно.
public function execute ($params = null)
В этом методе должен быть описан основной код, запускаемый при выполнении действия с заказом. Аргумент $params
содержит ID заказа, с которым выполняется действие.
Если действие использует веб-форму для получения данных от пользователя, то в методе execute()
эти данные доступны при помощи вызова waRequest::post()
. Как показать пользователю веб-форму при выполнении действия, рассказано в описании метода getHTML()
.
Если действие выполнилось успешно, то метод должен вернуть непустой результат — он будет передан в качестве аргумента для вызова метода postExecute()
. В противном случае postExecute()
вызван не будет.
Стандартный метод postExecute()
рассылает уведомления, настроенные для данного действия, и вызывает событие 'order_action.***'
для плагинов.
public function postExecute ($order_id = null, $result = null)
Этому методу в качестве аргумента передается ID заказа и непустой результат, который вернул метод execute()
.
Полученный результат должен представлять собой ассоциативный массив. Необязательный элемент этого массива, который по умолчанию поддерживается методом postExecute()
— это элемент с ключом 'update'
, который должен содержать подмассив элементов с ключами полей таблицы shop_order
и дополнительным ключом 'params'
— для сохранения параметров заказа в таблицу shop_order_params
.
Массив $result
также передается в качестве аргумента методам shopNotifications::sendEmail()
и shopNotifications::sendSms()
. Эти методы используют содержимое массива, чтобы сформировать текст уведомления, настроенного для данного действия. Содержимое массива $result
вместе с результатом, который вернул метод execute()
, доступно в Smarty-шаблонах уведомлений в виде переменной {$action_data}
.
public function getButton()
Возвращает HTML-код кнопки или ссылки для отображения на странице заказа в бекенде, с помощью которой пользователь выполняет действие.
public function isAvailable ($order)
Выполняет дополнительную поверку доступности действия для данного заказа. Аргумент $order
содержит ассоциативный массив свойств заказа. Доступность действия может зависеть, например, от статуса заказа, его состава и свойств покупателя.
Этот метод срабатывает, только если в настройках потока данное действие входит в число доступных для текущего статуса заказа.
public function getHTML ($order_id)
Показывает на странице заказа форму для ввода данных при выполнении действия, если значение параметра ['options']['html']
в свойствах действия равно true
.
array( 'name' => 'Мое действие', 'options' => array( 'html' => true, //... ), //... ),
Значения, введенные пользователем в форму, доступны в коде метода execute()
класса действия при помощи вызова waRequest::post()
.
Собственная веб-форма при выполнении действия
Для отображения формы по умолчанию используется стандартный шаблон wa-apps/shop/lib/workflow/templates/Action.html
. Для того чтобы использовать собственный шаблон, добавьте в эту же директорию файл с именем вида [Action_id]Action.html
. Форма должна отправлять данные методом post
по адресу ?module=workflow&action=perform
.
Форма должна содержать 2 поля типа hidden: id
(номер заказа) и action_id
(идентификатор действия).
При показе формы все видимые кнопки других действий автоматически скрываются. Поэтому в форме должна быть предусмотрена возможность скрыть ее и снова показать кнопки других действий.
Переопределять метод getHTML()
стоит, только если нужно изменить стандартную логику метода родительского класса, а не только шаблон формы.
Поток
Программное изменение настроек потока
Пользовательские изменения настроек потока записываются в файл wa-config/apps/shop/workflow.php
. Для этого удобно использовать статический метод shopWorkflow::setConfig($config)
. Он принимает в качестве аргумента массив, который должен стать содержимым конфигурационного файла.
Для чтения настроек потока используйте статический метод shopWorkflow::getConfig()
.
Чтобы выполнять действия, описанные в настройках потока, или получать информацию о них, нужно получить экземпляр класса shopWorkflow
.
$workflow = new shopWorkflow();
Примеры работы с потоком
/** * Получить список всех действий и информацию о каждом из них * * @var $actions array: action_id => shopWorkflowAction */ $actions = $workflow->getAllActions(); foreach ($actions as $action) { $action->getId(); $action->getName(); $action->getOptions(); $action->getOption('icon'); } /** * Получить список всех статусов и информацию о каждом из них * * @var $actions array: state_id => shopWorkflowState */ $states = $workflow->getAllStates(); foreach($states as $state) { $state->getId(); $state->getName(); $state->getOptions(); $state->getOption('icon'); } /** * Выполнить действие с заказом, если оно доступно для текущего статуса */ // 1. Получить информацию о заказе по ID $order_model = new shopOrderModel(); $order = $order_model->getById($order_id); // 2. Получить экземпляр класса потока // и массив доступных действий для заказа по его статусу $workflow = new shopWorkflow(); $actions = $workflow->getStateById($order['state_id'])->getActions($order); // 3. Выполнить действие, если оно доступно для заказа в текущем статусе if (isset($actions[$action_id])) { $workflow->getActionById($action_id)->run($order_id); } /** * Добавить новый статус в поток * * @var $new_state_id string e.g., 'some_state' * @var $new_state_settings array */ $workflow = shopWorkflow::getConfig(); $workflow['states'][$new_state_id] = $new_state_settings; shopWorkflow::setConfig($workflow); /** * Добавить новое действие в поток * * @var $new_action_id string e.g., 'some_action' * @var $new_action_settings array * @var $enabled_state_ids array e.g., array('new', 'processing', 'paid', 'shipped') */ // 1. Добавить действие $workflow = shopWorkflow::getConfig(); $workflow['actions'][$new_action_id] = $new_action_settings; // 2. Разрешить действие в перечисленных статусах foreach($enabled_state_ids as $state_id) { if (isset($workflow['states'][$state_id]['available_actions']) && !in_array($new_action_id, $workflow['states'][$state_id]['available_actions'])) { $workflow['states'][$state_id]['available_actions'][] = $new_action_id; } } // 3. Сохранить изменения shopWorkflow::setConfig($workflow);