You are here

commerce_admin_order_advanced.module in Commerce Admin Order Advanced 7

Provides advanced admin order interface.

File

commerce_admin_order_advanced.module
View source
<?php

/**
 * @file
 * Provides advanced admin order interface.
 */

/**
 * Implements hook_menu().
 */
function commerce_admin_order_advanced_menu() {
  $items = array();
  $items['user/email/autocomplete'] = array(
    'title' => 'User email autocomplete',
    'page callback' => '_commerce_admin_order_advanced_email_autocomplete',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access user profiles',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['user/addressfield/autocomplete'] = array(
    'title' => 'User addressfield autocomplete',
    'page callback' => '_commerce_admin_order_advanced_profile_autocomplete',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access user profiles',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/commerce/orders/customers/search'] = array(
    'title' => 'Search for customers',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'commerce_admin_order_advanced_search_customers_form',
    ),
    'access callback' => 'commerce_customer_profile_access',
    'access arguments' => array(
      'view',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/commerce/orders/add/use-existing/%/%'] = array(
    'title' => 'Use existing user',
    'page callback' => 'commerce_admin_order_advanced_use_existing_user',
    'page arguments' => array(
      5,
      6,
    ),
    'access callback' => 'commerce_customer_profile_access',
    'access arguments' => array(
      'view',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function commerce_admin_order_advanced_menu_alter(&$items) {

  // Override the page callback for the default order add form to user our
  // customer form.
  if (!empty($items['admin/commerce/orders/add'])) {
    $items['admin/commerce/orders/add']['page callback'] = 'drupal_get_form';
    $items['admin/commerce/orders/add']['page arguments'] = array(
      'commerce_admin_order_advanced_search_customers_form',
    );
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alter the admin checkout form to add copy address functionality and a custom
 * line item search form.
 */
function commerce_admin_order_advanced_form_commerce_order_ui_order_form_alter(&$form, &$form_state) {
  $profile_types = commerce_customer_profile_types();

  // Loop through each of the commerce customer profile types to add copy
  // address functionality.
  foreach ($profile_types as $profile_type) {
    $copy_dest = $profile_type['type'];

    // Determine if this field should have the copy profile option.
    $copy_profile = variable_get('commerce_customer_profile_' . $copy_dest . '_profile_copy', FALSE);
    if (!empty($copy_profile)) {
      $language = $form['commerce_customer_' . $copy_dest]['#language'];

      // Get the field that profile values should copy from.
      $copy_source = variable_get('commerce_customer_profile_' . $copy_dest . '_profile_copy_source');

      // Add the ajax wrappers around the customer address form fields.
      $form['commerce_customer_' . $copy_dest]['#prefix'] = '<div id="commerce-admin-order-advanced-commerce-customer-' . $copy_dest . '-ajax-wrapper">';
      $form['commerce_customer_' . $copy_dest]['commerce_customer_address']['#suffix'] = '</div>';

      // Add a visible state to hide the profile if copy existing is clicked.
      $form['commerce_customer_' . $copy_dest][$language]['profiles'][0]['commerce_customer_address']['#states'] = array(
        'visible' => array(
          ':input[name="profile_copy_' . $copy_dest . '"]' => array(
            'checked' => FALSE,
          ),
        ),
      );

      // Create the checkbox to trigger the profile copy.
      $form['commerce_customer_' . $copy_dest][$language]['profiles'][0]['profile_copy_' . $copy_dest] = array(
        '#type' => 'checkbox',
        '#tree' => FALSE,
        '#title' => t('Copy the customers @copy_source to this @copy_dest profile.', array(
          '@copy_source' => $copy_source,
          '@copy_dest' => $copy_dest,
        )),
        '#options' => array(
          0 => FALSE,
          TRUE => $copy_dest,
        ),
        '#validate' => array(
          'commerce_admin_order_advanced_profile_copy_validate',
        ),
        '#weight' => -10,
        '#ajax' => array(
          'callback' => 'commerce_admin_order_advanced_profile_refresh',
          'wrapper' => 'commerce-admin-order-advanced-commerce-customer-' . $copy_dest . '-ajax-wrapper',
        ),
      );
    }
  }
}

/**
 * Validation callback for the admin copy address profile ajax function.
 */
function commerce_admin_order_advanced_profile_copy_validate($form, &$form_state) {
  $triggering_element = $form_state['triggering_element'];

  // Load the destination from the triggering element options.
  $copy_dest = $triggering_element['#options'][1];
  if ($triggering_element['#value'] == 0) {
    $form_state['copy_' . $copy_dest] = FALSE;
  }
  else {
    $form_state['copy_' . $copy_dest] = TRUE;

    // Determin the appropriate source to copy the profile fields from.
    $copy_source = variable_get('commerce_customer_profile_' . $copy_dest . '_profile_copy_source', FALSE);
    if (!empty($copy_source)) {
      $info = array(
        'commerce_customer_profile',
        $copy_source,
        $form['commerce_customer_' . $copy_dest]['#language'],
      );

      // Copy the form_state input values from the source to the destination.
      commerce_admin_order_advanced_profile_copy($info, $form_state['input']['commerce_customer_' . $copy_dest], $form_state['input']['commerce_customer_' . $copy_source], $form_state);

      // Copy the form_state submitted values from the source to the
      // destination.
      commerce_admin_order_advanced_profile_copy($info, $form_state['values']['commerce_customer_' . $copy_dest], $form_state['values']['commerce_customer_' . $copy_source], $form_state);
    }
  }
}

/**
 * Ajax callback refreshing the customer profile fields after a profile copy.
 */
function commerce_admin_order_advanced_profile_refresh($form, &$form_state) {
  $triggering_element = $form_state['triggering_element'];

  // Load the destination.
  $copy_dest = $triggering_element['#options'][1];
  return $form['commerce_customer_' . $copy_dest];
}

/**
 * Creates the form for searching customers by name or email address.
 */
function commerce_admin_order_advanced_search_customers_form($form, &$form_state) {
  $form = array(
    '#prefix' => '<div id="commerce-admin-order-advanced-order-user">',
    '#suffix' => '</div>',
    '#attached' => array(
      'js' => array(
        drupal_get_path('module', 'commerce_admin_order_advanced') . '/commerce_admin_order_advanced.js',
      ),
      'css' => array(
        drupal_get_path('module', 'commerce_admin_order_advanced') . '/commerce_admin_order_advanced.css',
      ),
    ),
  );
  $form['search'] = array(
    '#type' => 'fieldset',
    '#title' => t('Search for existing customers'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['search']['customer_email'] = array(
    '#type' => 'textfield',
    '#description' => t('Search by the customer\'s email address (must be an existing Drupal user).'),
    '#title' => t('Search customer\'s by email'),
    '#autocomplete_path' => 'user/email/autocomplete',
    '#ajax' => array(
      'callback' => 'commerce_admin_order_advanced_select_customer',
      'wrapper' => 'commerce-admin-order-advanced-order-user',
    ),
  );
  $form['search']['customer_profile'] = array(
    '#type' => 'textfield',
    '#description' => t('Search for customer profiles with a matching name or organization.'),
    '#title' => t('Search by profile (name or organization)'),
    '#autocomplete_path' => 'user/addressfield/autocomplete',
    '#ajax' => array(
      'callback' => 'commerce_admin_order_advanced_select_customer',
      'wrapper' => 'commerce-admin-order-advanced-order-user',
    ),
  );
  $form['customer'] = array(
    '#type' => 'container',
  );
  $form['new_customer'] = array(
    '#type' => 'fieldset',
    '#title' => t('Create a new customer'),
  );
  $form['new_customer']['email'] = array(
    '#type' => 'textfield',
    '#title' => t('New Customer Email'),
  );
  $form['actions'] = array(
    '#type' => 'container',
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Create Order'),
    '#suffix' => l(t('Reset'), 'admin/commerce/orders/add'),
  );
  return $form;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the admin create order from to add billing and shipping profile
 * fields whenever a customer is loaded by email or name.
 */
function commerce_admin_order_advanced_form_commerce_admin_order_advanced_search_customers_form_alter(&$form, &$form_state) {

  // Specify the parent form elements that we should react to.
  $parents = array(
    'customer_email',
    'customer_profile',
  );

  // If the triggering element was one of the customer search fields.
  if (!empty($form_state['triggering_element']['#parents'][0]) && in_array($form_state['triggering_element']['#parents'][0], $parents) && !empty($form_state['triggering_element']['#value'])) {
    $uid = NULL;

    // The UID will be submitted as [uid] or [uid]-[profile_id].
    if (is_numeric($form_state['triggering_element']['#value'])) {
      $uid = $form_state['triggering_element']['#value'];
    }
    else {
      preg_match('/([\\d]+)\\-([\\d]+)/', $form_state['triggering_element']['#value'], $key);
      if (!empty($key[1])) {
        $uid = $key[1];
      }
    }

    // Do not continue if we weren't able to find a selected user id.
    if (!is_numeric($uid)) {
      return;
    }
    $user = user_load((int) $uid);

    // If we're able to load a user, add the form fields for selecting their
    // billing and shipping profiles.
    if (!empty($user)) {

      // Hide "Create new customer"
      $form['new_customer']['#access'] = FALSE;
      $form_state['user'] = $user;
      $form['search']['#collapsed'] = TRUE;
      $uid = $user->uid;
      $customer_info = array(
        'Username: ' . $user->name,
        'Email Address: ' . $user->mail,
        'Member Since: ' . format_date($user->created),
      );
      $form['customer']['customer_info'] = array(
        '#markup' => '<h3>' . t('Selected Customer') . '</h3>' . implode($customer_info, '<br />'),
      );
      $form['customer']['billing_profile'] = array(
        '#type' => 'select',
        '#title' => t('Billing Profile'),
        '#options' => _commerce_admin_order_advanced_load_profiles($uid, 'billing'),
        '#default_value' => !empty($form_state['values']['billing_profile']) ? $form_state['values']['billing_profile'] : 0,
      );
      if (module_exists('commerce_shipping')) {
        $form['customer']['shipping_profile'] = array(
          '#type' => 'select',
          '#title' => t('Shipping Profile'),
          '#options' => _commerce_admin_order_advanced_load_profiles($uid, 'shipping'),
          '#default_value' => !empty($form_state['values']['shipping_profile']) ? $form_state['values']['shipping_profile'] : 0,
        );
      }
      $form['customer']['email']['#access'] = FALSE;
    }
    else {
      $form_state['user'] = NULL;
      $form_state['values']['shipping_profile'] = NULL;
      $form_state['values']['billing_profile'] = NULL;
    }
  }
}

/**
 * Validates the create order form to prevent duplicate customers.
 */
function commerce_admin_order_advanced_search_customers_form_validate(&$form, &$form_state) {
  if (empty($form_state['user'])) {
    if (empty($form_state['values']['email'])) {
      form_set_error('email', t('Please enter an email address for the new customer'));
    }
    else {
      $email = $form_state['values']['email'];
      if (!valid_email_address($email)) {
        form_set_error('email', t('Please enter a valid email address for the new customer.'));
      }
      else {
        $user = entity_load('user', array(), array(
          'mail' => $email,
        ));
        if (!empty($user)) {
          $use_existing = l(t('Use the existing user'), 'admin/commerce/orders/add/use-existing/nojs/' . reset($user)->uid, array(
            'attributes' => array(
              'class' => array(
                'use-ajax',
              ),
            ),
          ));
          form_set_error('email', t('A user with the email address @email already exists. !use_existing', array(
            '@email' => $email,
            '!use_existing' => $use_existing,
          )));
        }
      }
    }
  }
}

/**
 * Ajax callback to load an existing user by existing users email.
 */
function commerce_admin_order_advanced_use_existing_user($js, $uid) {
  if (!empty($js)) {
    $commands = array();

    // Invoke the ajax command that replaces the customer_email value and
    // triggers a change event to load the user.
    $commands[] = ajax_command_invoke(NULL, 'commerceAdminOrderUseExisting', array(
      $uid,
    ));
    ajax_deliver(array(
      '#type' => 'ajax',
      '#commands' => $commands,
    ));
  }
  else {
    return;
  }
}

/**
 * Submit handler for creating a new commerce order.
 */
function commerce_admin_order_advanced_search_customers_form_submit(&$form, &$form_state) {
  $uid = 0;
  if (!empty($form_state['user'])) {
    $user = $form_state['user'];
    $uid = $user->uid;
  }
  elseif (!empty($form_state['values']['email'])) {

    // Create a new user entity from the submitted email.
    $user = entity_create('user', array());
    $user->name = $form_state['values']['email'];
    $user->mail = $form_state['values']['email'];
    $user->status = 1;
    $user->roles = array(
      DRUPAL_AUTHENTICATED_RID => TRUE,
    );
    user_save($user);
    $uid = $user->uid;
  }

  // Initialize a new commerce order object.
  $order = commerce_order_new($uid);
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

  // Set the shipping profile.
  if (!empty($form_state['values']['shipping_profile'])) {
    $order_wrapper->commerce_customer_shipping
      ->set($form_state['values']['shipping_profile']);
  }

  // Set the billing profile.
  if (!empty($form_state['values']['billing_profile'])) {
    $order_wrapper->commerce_customer_billing
      ->set($form_state['values']['billing_profile']);
  }
  commerce_order_save($order);

  // Redirect the user to the admin order edit form.
  $form_state['redirect'] = 'admin/commerce/orders/' . $order->order_id . '/edit';
}

/**
 * Ajax callback for reloading our custom order create form.
 */
function commerce_admin_order_advanced_select_customer(&$form, &$form_state) {
  return $form;
}

/**
 * Provides a method to copy customer profiles.
 *
 * Helper function to copy a customer profile from one type to another through
 * the admin order form.
 */
function commerce_admin_order_advanced_profile_copy($info, &$target, $source, &$form_state) {
  list($entity_type, $bundle, $language) = $info;

  // Loop over all the field instances that could be attached to this entity.
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
    $field = NULL;
    $field = $source[$language]['profiles'][0][$field_name];

    // Loop over the source field value and copy its items to the target.
    if (is_array($field)) {
      foreach ($field as $langcode => $items) {
        if (is_array($items)) {
          foreach ($items as $delta => $item) {
            $target[$language]['profiles'][0][$field_name][$langcode][$delta] = $item;
          }
        }
      }
    }
  }
}

/**
 * Internal function to return email autocomplete.
 */
function _commerce_admin_order_advanced_email_autocomplete($email = '') {
  $users = db_select('users', 'u')
    ->condition('u.mail', '%' . $email . '%', 'LIKE')
    ->fields('u', array(
    'mail',
    'name',
    'uid',
  ))
    ->range(0, 50)
    ->execute()
    ->fetchAll();
  $emails = array();
  foreach ($users as $user) {
    $emails[$user->uid] = $user->mail;
  }
  return drupal_json_output($emails);
}

/**
 * Helper function to load customer profiles for autocomplete.
 */
function _commerce_admin_order_advanced_profile_autocomplete($name = '') {
  $customers = array();
  if (strlen($name) < 3) {
    return drupal_json_output($customers);
  }
  $query = db_select('field_data_commerce_customer_address', 'ca');
  $query
    ->join('commerce_customer_profile', 'cp', 'ca.entity_id = cp.profile_id');
  $query
    ->fields('ca');
  $query
    ->addField('cp', 'uid', 'uid');
  $db_or = db_or()
    ->condition('ca.commerce_customer_address_name_line', '%' . $name . '%', 'LIKE')
    ->condition('ca.commerce_customer_address_last_name', '%' . $name . '%', 'LIKE')
    ->condition('ca.commerce_customer_address_first_name', '%' . $name . '%', 'LIKE')
    ->condition('ca.commerce_customer_address_organisation_name', '%' . $name . '%', 'LIKE');
  $query
    ->condition($db_or);
  $query
    ->condition('cp.uid', '0', '<>');
  $result = $query
    ->execute()
    ->fetchAllAssoc('uid');
  if (!empty($result)) {
    foreach ($result as $uid => $profile) {
      $customer_key = $uid . '-' . $profile->entity_id;
      $customer_name = array(
        check_plain($profile->commerce_customer_address_name_line),
        check_plain($profile->commerce_customer_address_first_name),
        check_plain($profile->commerce_customer_address_last_name),
      );
      $customers[$customer_key] = '<strong> ' . trim(implode(' ', $customer_name)) . '</strong>';
      $customers[$customer_key] .= '<br />';
      $customers[$customer_key] .= check_plain($profile->commerce_customer_address_thoroughfare);
      $customers[$customer_key] .= ' ' . check_plain($profile->commerce_customer_address_locality);
      $customers[$customer_key] .= ', ' . check_plain($profile->commerce_customer_address_administrative_area);
    }
  }
  return drupal_json_output($customers);
}

/**
 * Helper function to load the customers profiles.
 */
function _commerce_admin_order_advanced_load_profiles($uid, $type = 'billing') {
  $customer_profiles = array(
    0 => t('New Profile'),
  );
  $profiles = commerce_customer_profile_load_multiple(array(), array(
    'uid' => $uid,
    'type' => $type,
    'status' => 1,
  ));
  if (!empty($profiles)) {
    foreach ($profiles as $profile) {
      $profile_wrapper = entity_metadata_wrapper('commerce_customer_profile', $profile);
      $customer_profile = $profile_wrapper->commerce_customer_address
        ->value();
      $customer_profiles[$profile->profile_id] = $customer_profile['thoroughfare'] . ' ' . $customer_profile['locality'] . ', ' . $customer_profile['administrative_area'];
    }
  }

  // Move the default profile to the top of the list.
  if (module_exists('commerce_addressbook')) {
    $default_profile_id = commerce_addressbook_get_default_profile_id($uid, $type);
    if (!empty($customer_profiles[$default_profile_id])) {
      $default_profile = [
        $default_profile_id => $customer_profiles[$default_profile_id],
      ];
      unset($customer_profiles[$default_profile_id]);
      $customer_profiles = $default_profile + $customer_profiles;
    }
  }
  return $customer_profiles;
}

Functions

Namesort descending Description
commerce_admin_order_advanced_form_commerce_admin_order_advanced_search_customers_form_alter Implements hook_form_FORM_ID_alter().
commerce_admin_order_advanced_form_commerce_order_ui_order_form_alter Implements hook_form_FORM_ID_alter().
commerce_admin_order_advanced_menu Implements hook_menu().
commerce_admin_order_advanced_menu_alter Implements hook_menu_alter().
commerce_admin_order_advanced_profile_copy Provides a method to copy customer profiles.
commerce_admin_order_advanced_profile_copy_validate Validation callback for the admin copy address profile ajax function.
commerce_admin_order_advanced_profile_refresh Ajax callback refreshing the customer profile fields after a profile copy.
commerce_admin_order_advanced_search_customers_form Creates the form for searching customers by name or email address.
commerce_admin_order_advanced_search_customers_form_submit Submit handler for creating a new commerce order.
commerce_admin_order_advanced_search_customers_form_validate Validates the create order form to prevent duplicate customers.
commerce_admin_order_advanced_select_customer Ajax callback for reloading our custom order create form.
commerce_admin_order_advanced_use_existing_user Ajax callback to load an existing user by existing users email.
_commerce_admin_order_advanced_email_autocomplete Internal function to return email autocomplete.
_commerce_admin_order_advanced_load_profiles Helper function to load the customers profiles.
_commerce_admin_order_advanced_profile_autocomplete Helper function to load customer profiles for autocomplete.