Обработка заказов

Обработка заказов в 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);