Order processing

Order processing in Shop-Script constitutes the execution of actions, which may either change order status (state) or keep it unchanged. An action may also do any additional job that a developer may want it to do. Information about a completed action is logged to order processing history.

All actions and order states set up in an online store are called workflow. Workflow settings are stored in configuration file wa-config/apps/shop/workflow.php. If this file does not exist, default settings file wa-apps/shop/lib/config/data/workflow.php is used.

Order status

The status defines the text caption displayed next to order ID in the backend and in customer’s personal account. In backend, the status also defines the list of available actions which can be executed on an order.

A status is represented by an entry in the currently valid workflow.php file.

Example

array(
    'name' => 'New',
    'options' => array(
        // CSS class of status icon
        'icon'  => 'icon16 ss new',
        // CSS styles for showing orders in this status
        'style' => array(
            'color'       => '#009900',
            'font-weight' => 'bold',
        ),
    ),
    // actions available in this status
    'available_actions' => array(
        'process',
        'pay',
        'ship',
        'complete',
        'comment',
        'edit',
        'editshippingdetails',
        'message',
        'delete',
    ),
    // default class name for order states, usually does not need to be changed
    'classname' => 'shopWorkflowState',
),

Order action

An action defined the following:

Action settings also define how the action must be executed, by a button or a link, and where on the order-viewing page it must be displayed.

Actions can be executed manually by users or automatically, without direct user input. For example, actions “Edit” and “Processing” are executed by a user, and actions “Create” and “Callback” are executed programmatically and there are no buttons in the UI to trigger them.

A simple action with default functionality constitutes only a record in file workflow.php.

Example

array(
    'name'      => 'Some action',
    'options'   => array(
        // Where action button or link must be located on order-viewing page
        // '': action button with colored border
        // 'top': link & icon in the sidebar
        // 'bottom': link & icon above order history
        'position'     => 'bottom',

        // CSS class of action button
        'icon'         => 'add',

        // CSS class of action button or link
        'button_class' => 'inline-link',

        // Action description in order history
        'log_record'   => 'Added comment to order',

        // If set to true, action will immediately show a web form
        // on order-viewing page instead of a button or link
        'head'         => false,

        // If set to true, action will show a web form
        // upon click on a button
        'html'         => false,
    ),

    // Status to set for order on action execution
    // null: keep status unchanged
    'state'     => null,

    // true: make action hidden from users,
    // to be executed only via source code
    'internal'  => false,

    // action’s PHP class
    'classname' => 'shopWorkflowAction',
),

Default order action functionality is describe in class shopWorkflowAction. This class name is added to every new action entry in file workflow.php as shown in the example.

Custom action functionality

If you need custom functionality for an order action, change default class name shopWorkflowAction in file workflow.php to your own class extending that base class.

If a new custom action is added by a plugin, then the plugin must correctly update the workflow configuration file when the plugin is uninstalled. Failure to do so will leave links to non-existent classes in file workflow.php, and that will cause errors in the operation of an online store.

In a custom order action class you must implement public method execute(). Implementation or overriding of other methods are optional.

public function execute ($params = null)

This method must contain the main logic to be run when an order action is executed. Argument $params contains the ID of the order an action is executed on.

If an action shows a user a web form to collect extra data, then you can access the submitted data by calling waRequest::post() in execute() method. How to show a web form during action execution is described in getHTML() method paragraph.

If an action has been successfully completed, then the method must return non-empty result which must will be passed to postExecute() method as an argument. Otherwise postExecute() would not be called.

Default method postExecute() sends out notifications set up for the current order action and triggers 'order_action.***' event for plugins.

public function postExecute ($order_id = null, $result = null)

This method accepts order ID and the non-empty result returned by execute() method as arguments.

The passed result must be an associative array. By default, this method supports an optional array item with key 'update'. That item must contain a sub-array of items with keys matching the fields of shop_order table and may contain an optional 'params' item with order parameters to be saved to shop_order_params table.

The $result array is also passed as an argument to methods shopNotifications::sendEmail() and shopNotifications::sendSms(). Those methods use the array contents to generate the text of an order notification set up for the executed action. The $result array contents including the result returned by execute() method are available in notifications Smarty templates as {$action_data} variable.

public function getButton()

Returns HTML code of the button or link to be displayed on the order-viewing page for a user to execute an action.

public function isAvailable ($order)

Performs an additional check whether an action is available for execution on a given order. Argument $order contains an associative array of order properties. The availability of an action may depend on the order status, its contents, or the customer properties, for example.

This method is called only if an action is available for the current order status.

public function getHTML ($order_id)

Show a web form on the order-viewing page when a user executes an action, if action parameter ['options']['html'] is set to true.

array(
    'name'    => 'Some action',
    'options' => array(
        'html' => true,
        //...
    ),
    //...
),

Values submitted via a form are available in execute() method of the action class by calling waRequest::post().

Custom order action form

To display an action form, default template file wa-apps/shop/lib/workflow/templates/Action.html is used. If you want to show a custom form, add a custom template file named by rule [Action_id]Action.html to the same directory. A form must send data using post method to URL ?module=workflow&action=perform.

A form must contain 2 hidden fields: id (order ID) and action_id (action ID).

When an action form is shown, all other visible action buttons are automatically hidden. Therefore, a custom form be capable of hiding itself and showing the hidden action buttons when necessary.

It might be necessary to override method getHTML() only if you want to change its default logic and not only the default form template.

Workflow

Changing workflow settings programmatically

Custom workflow settings are saved to configuration file wa-config/apps/shop/workflow.php. To do so, use static method shopWorkflow::setConfig($config). Its single argument must be an array of the new settings to be saved to the file.

To read the workflow settings, use static method shopWorkflow::getConfig().

To execute order action or to read their properties, first obtain an instance of shopWorkflow class.

$workflow = new shopWorkflow();

Workflow management examples

/**
* Obtain the list of actions and get their properties
* 
* @var $actions array: action_id => shopWorkflowAction
*/

$actions = $workflow->getAllActions();
foreach ($actions as $action) {
    $action->getId();
    $action->getName();
    $action->getOptions();
    $action->getOption('icon');
}



/**
* Obtain the list of states and get their properties
* 
* @var $actions array: state_id => shopWorkflowState
*/

$states = $workflow->getAllStates();
foreach($states as $state) {
    $state->getId();
    $state->getName();
    $state->getOptions();
    $state->getOption('icon');
}


/**
* Execute an order action if it’s available for current order status
*/

// 1. Get order info by its ID
$order_model = new shopOrderModel();
$order = $order_model->getById($order_id);

// 2. Obtain an instance of workflow class
// and the array of available action by order status
$workflow = new shopWorkflow();
$actions = $workflow->getStateById($order['state_id'])->getActions($order);

// 3. Execute an action if it’s available for an order in its current state
if (isset($actions[$action_id])) {
    $workflow->getActionById($action_id)->run($order_id);
}



/**
* Add a new status to the workflow
* 
* @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);



/**
* Add a new action to the 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. Add action
$workflow = shopWorkflow::getConfig();
$workflow['actions'][$new_action_id] = $new_action_settings;

// 2. Enable action for listed order states
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. Save settings
shopWorkflow::setConfig($workflow);