'Packages',
'page callback' => 'uc_shipping_order_packages',
'page arguments' => array(3),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'weight' => 6,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/packages/new'] = array(
'title' => 'New packages',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_new_package', 3),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/edit'] = array(
'title' => 'Edit package',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_package_edit', 3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/cancel'] = array(
'title' => 'Cancel package shipment',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_package_cancel_confirm', 3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/packages/%uc_shipping_package/delete'] = array(
'title' => 'Delete package',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_package_delete_confirm', 3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments'] = array(
'title' => 'Shipments',
'page callback' => 'uc_shipping_order_shipments',
'page arguments' => array(3),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'weight' => 7,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/new'] = array(
'title' => 'New shipment',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_new_shipment', 3),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment'] = array(
'title callback' => 'uc_shipping_shipment_page_title',
'title arguments' => array(5),
'page callback' => 'uc_shipping_shipment_view',
'page arguments' => array(3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/view'] = array(
'title' => 'View',
'weight' => -5,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/edit'] = array(
'title' => 'Edit',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_shipment_edit', 3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'weight' => -1,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/print'] = array(
'title' => 'Print',
'page callback' => 'uc_shipping_shipment_print',
'page arguments' => array(3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'type' => MENU_LOCAL_TASK,
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/packing_slip'] = array(
'title' => 'Packing slip',
'page callback' => 'theme',
'page arguments' => array('uc_shipping_shipment_print', 3, 5, FALSE),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'type' => MENU_LOCAL_TASK,
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/delete'] = array(
'title' => 'Delete shipment',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_shipping_shipment_delete_confirm', 3, 5),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
$items['admin/store/orders/%uc_order/ship'] = array(
'title' => 'Ship packages',
'page callback' => 'uc_shipping_make_shipment',
'page arguments' => array(3),
'access callback' => 'uc_shipping_order_access',
'access arguments' => array(3),
'file' => 'uc_shipping.admin.inc',
);
return $items;
}
/**
* Title callback for admin/store/orders/%uc_order/shipments/%uc_shipping_shipment.
*/
function uc_shipping_shipment_page_title($shipment) {
return t('Shipment !id', array('!id' => $shipment->sid));
}
/**
* Ensures access to the Shipments tab.
*/
function uc_shipping_order_access($order) {
return user_access('fulfill orders') && uc_order_is_shippable($order);
}
/**
* Implements hook_permission().
*/
function uc_shipping_permission() {
return array(
'fulfill orders' => array(
'title' => t('Fulfill orders'),
),
);
}
/**
* Implements hook_theme().
*/
function uc_shipping_theme() {
return array(
'uc_shipping_new_package_fieldset' => array(
'render element' => 'fieldset',
'file' => 'uc_shipping.admin.inc',
),
'uc_shipping_edit_package_fieldset' => array(
'render element' => 'fieldset',
'file' => 'uc_shipping.admin.inc',
),
'uc_shipping_new_shipment' => array(
'render element' => 'form',
'file' => 'uc_shipping.admin.inc',
),
'uc_shipping_package_dimensions' => array(
'render element' => 'form',
),
'uc_shipping_address' => array(
'render element' => 'address',
),
'uc_shipping_shipment_print' => array(
'variables' => array('order' => NULL, 'shipment' => NULL, 'labels' => TRUE),
),
'uc_packing_slip' => array(
'variables' => array('order' => NULL, 'shipment' => NULL),
'template' => 'uc-packing-slip',
),
);
}
/**
* Preprocess function to make token values available as variables in the
* packing slip template.
*/
function template_preprocess_uc_packing_slip(&$variables) {
$tokens = token_generate('store', array()) + token_generate('site', array());
$variables['site_logo'] = $tokens['[site:logo]'];
$variables['store_name'] = $tokens['[store:name]'];
$variables['store_address'] = $tokens['[store:address]'];
$variables['store_phone'] = $tokens['[store:phone]'];
$order = $variables['order'];
$variables['order_link'] = l($order->order_id, url('user/' . $order->uid . '/orders/' . $order->order_id, array('absolute' => TRUE)));
$variables['order_email'] = check_plain($order->primary_email);
$variables['billing_address'] = uc_order_address($order, 'billing');
$variables['billing_phone'] = check_plain($order->billing_phone);
$variables['shipping_address'] = uc_order_address($order, 'delivery');
$variables['shipping_phone'] = check_plain($order->delivery_phone);
if (module_exists('uc_payment')) {
$payment_method = _uc_payment_method_data($order->payment_method, 'review');
if (empty($payment_method)) {
$payment_method = _uc_payment_method_data($order->payment_method, 'name');
}
$variables['payment_method'] = $payment_method;
}
else {
$variables['payment_method'] = '';
}
$shipment = $variables['shipment'];
$variables['carrier'] = check_plain($shipment->carrier);
$variables['tracking_number'] = check_plain($shipment->tracking_number);
$variables['packages'] = $shipment->packages;
}
/******************************************************************************
* Ubercart hooks *
******************************************************************************/
/**
* Implements hook_uc_order_pane().
*/
function uc_shipping_uc_order_pane() {
$panes[] = array(
'id' => 'packages',
'callback' => 'uc_shipping_order_pane_packages',
'title' => t('Tracking numbers'),
'desc' => t('Display tracking numbers of shipped packages.'),
'class' => 'pos-left',
'weight' => 7,
'show' => array('view', 'invoice', 'customer'),
);
return $panes;
}
/**
* Implements hook_uc_order_actions().
*/
function uc_shipping_uc_order_actions($order) {
$actions = array();
$module_path = base_path() . drupal_get_path('module', 'uc_shipping');
if (user_access('fulfill orders')) {
$result = db_query("SELECT COUNT(nid) FROM {uc_order_products} WHERE order_id = :id AND data LIKE :data", array(':id' => $order->order_id, ':data' => '%s:9:\"shippable\";s:1:\"1\";%'));
if ($result->fetchField()) {
$title = t('Package order !order_id products.', array('!order_id' => $order->order_id));
$actions[] = array(
'name' => t('Package'),
'url' => 'admin/store/orders/' . $order->order_id . '/packages',
'icon' => '',
'title' => $title,
);
$result = db_query("SELECT COUNT(package_id) FROM {uc_packages} WHERE order_id = :id", array(':id' => $order->order_id));
if ($result->fetchField()) {
$title = t('Ship order !order_id packages.', array('!order_id' => $order->order_id));
$actions[] = array(
'name' => t('Ship'),
'url' => 'admin/store/orders/' . $order->order_id . '/shipments',
'icon' => '',
'title' => $title,
);
}
}
}
return $actions;
}
/******************************************************************************
* Menu callbacks *
******************************************************************************/
/**
* Displays the details of a package.
*/
function uc_shipping_package_view($package) {
$shipment = uc_shipping_shipment_load($package->sid);
$build = array(
'#prefix' => '
',
'#suffix' => '
',
);
$rows = array();
$build['title'] = array(
'#markup' => t('Package %id:', array('%id' => $package->package_id)),
'#prefix' => '',
'#suffix' => '
',
);
$rows[] = array(t('Contents:'), filter_xss_admin($package->description));
if ($shipment) {
$methods = module_invoke_all('uc_shipping_method');
$method = $methods[$shipment->shipping_method];
$pkg_type = $method['ship']['pkg_types'][$package->pkg_type];
}
$rows[] = array(t('Package type:'), strlen($pkg_type) ? $pkg_type : check_plain($package->pkg_type));
if ($package->length && $package->width && $package->height) {
$rows[] = array(t('Dimensions:'), t('!l x !w x !h', array('!l' => uc_length_format($package->length), '!w' => uc_length_format($package->width), '!h' => uc_length_format($package->height))));
}
$rows[] = array(t('Insured value:'), array('#theme' => 'uc_price', '#price' => $package->value));
if ($package->tracking_number) {
$rows[] = array(t('Tracking number:'), check_plain($package->tracking_number));
}
if ($shipment && is_object($package->label_image) && file_exists($package->label_image->uri)) {
$rows[] = array(t('Label:'), l(t('Click to view.'), 'admin/store/orders/' . $package->order_id . '/shipments/labels/' . $shipment->shipping_method . '/' . $package->label_image->uri));
}
else {
$rows[] = array(t('Label:'), t('n/a'));
}
$build['package'] = array(
'#theme' => 'table',
'#rows' => $rows,
'attributes' => array('style' => 'width:auto;'),
);
return $build;
}
/**
* Displays length, width, and height fields on one line.
*
* @ingroup themeable
*/
function theme_uc_shipping_package_dimensions($variables) {
$form = $variables['form'];
$row = array();
foreach (element_children($form) as $dimension) {
$row[] = drupal_render($form[$dimension]);
}
return theme('table', array('rows' => array($row)));
}
/**
* Compacts the address into a table.
*
* @ingroup themeable
*/
function theme_uc_shipping_address($variables) {
$address = $variables['address'];
drupal_add_css(drupal_get_path('module', 'uc_cart') . '/uc_cart.css');
if ($address['#collapsed']) {
$collapsed = ' collapsed';
}
$output = '';
$req = '*';
foreach (element_children($address) as $field) {
list($type, $name) = explode('_', $field, 2);
if ($address !== NULL) {
$title = $address[$field]['#title'] . ':';
unset($address[$field]['#title']);
if ($name == 'street1') {
$title = uc_get_field_name('street') . ':';
}
elseif ($name == 'street2') {
$title = ' ';
}
$output .= '';
if ($address[$field]['#required']) {
$output .= $req;
}
$output .= $title . ' | '
. drupal_render($address[$field])
. ' |
';
}
}
$output .= '
';
$output .= drupal_render_children($address);
return $output;
}
/******************************************************************************
* Module and helper functions *
******************************************************************************/
/**
* Loads a package and its products.
*/
function uc_shipping_package_load($package_id) {
static $packages = array();
if (!isset($packages[$package_id])) {
$result = db_query("SELECT * FROM {uc_packages} WHERE package_id = :id", array(':id' => $package_id));
if ($package = $result->fetchObject()) {
$products = array();
$description = '';
$weight = 0;
$units = variable_get('uc_weight_unit', 'lb');
$addresses = array();
$result = db_query("SELECT op.order_product_id, pp.qty, pp.qty * op.weight AS weight, p.weight_units, op.nid, op.title, op.model, op.price, op.data FROM {uc_packaged_products} AS pp LEFT JOIN {uc_order_products} AS op ON op.order_product_id = pp.order_product_id LEFT JOIN {uc_products} AS p ON op.nid = p.nid WHERE pp.package_id = :id ORDER BY op.order_product_id", array(':id' => $package_id));
foreach ($result as $product) {
$address = uc_quote_get_default_shipping_address($product->nid);
// TODO: Lodge complaint that array_unique() compares as strings.
if (!in_array($address, $addresses)) {
$addresses[] = $address;
}
$description .= ', ' . $product->qty . ' x ' . $product->model;
// Normalize all weights to default units.
$weight += $product->weight * uc_weight_conversion($product->weight_units, $units);
$product->data = unserialize($product->data);
$products[$product->order_product_id] = $product;
}
$package->addresses = $addresses;
$package->description = substr($description, 2);
$package->weight = $weight;
$package->weight_units = $units;
$package->products = $products;
if ($package->label_image && $image = file_load($package->label_image)) {
$package->label_image = $image;
}
else {
unset($package->label_image);
}
$packages[$package_id] = $package;
}
else {
return FALSE;
}
}
return $packages[$package_id];
}
/**
* Saves a package.
*/
function uc_shipping_package_save($package) {
$package = (object)$package;
if (!isset($package->package_id)) {
$package->package_id = db_insert('uc_packages')
->fields(array('order_id' => $package->order_id))
->execute();
}
if (count($package->products)) {
$insert = db_insert('uc_packaged_products')
->fields(array('package_id', 'order_product_id', 'qty'));
foreach ($package->products as $id => $product) {
$insert->values(array(
'package_id' => $package->package_id,
'order_product_id' => $id,
'qty' => $product->qty,
));
$result = db_query("SELECT data FROM {uc_order_products} WHERE order_product_id = :id", array(':id' => $id));
if ($order_product = $result->fetchObject()) {
$order_product->data = unserialize($order_product->data);
$order_product->data['package_id'] = intval($package->package_id);
db_update('uc_order_products')
->fields(array('data' => serialize($order_product->data)))
->condition('order_product_id', $id)
->execute();
}
}
db_delete('uc_packaged_products')
->condition('package_id', $package->package_id)
->execute();
$insert->execute();
}
$fields = array(
'order_id' => $package->order_id,
'shipping_type' => $package->shipping_type,
);
if (isset($package->pkg_type)) {
$fields['pkg_type'] = $package->pkg_type;
}
if (isset($package->length) && isset($package->width) && isset($package->height) && isset($package->length_units)) {
$fields['length'] = $package->length;
$fields['width'] = $package->width;
$fields['height'] = $package->height;
$fields['length_units'] = $package->length_units;
}
if (isset($package->value)) {
$fields['value'] = $package->value;
}
if (isset($package->sid)) {
$fields['sid'] = $package->sid;
}
if (isset($package->tracking_number)) {
$fields['tracking_number'] = $package->tracking_number;
}
if (isset($package->label_image) && is_object($package->label_image)) {
$fields['label_image'] = $package->label_image->fid;
}
db_update('uc_packages')
->fields($fields)
->condition('package_id', $package->package_id)
->execute();
}
/**
* Deletes a package.
*/
function uc_shipping_package_delete($package_id) {
// @todo: Make these delete functions take the actual object.
$package = uc_shipping_package_load($package_id);
db_delete('uc_packages')
->condition('package_id', $package_id)
->execute();
db_delete('uc_packaged_products')
->condition('package_id', $package_id)
->execute();
if ($package->label_image) {
file_usage_delete($package->label_image, 'uc_shipping', 'package', $package_id);
file_delete($package->label_image);
}
drupal_set_message(t('Package @id has been deleted.', array('@id' => $package_id)));
}
/**
* Loads a shipment and its packages.
*/
function uc_shipping_shipment_load($shipment_id) {
$shipment = db_query("SELECT * FROM {uc_shipments} WHERE sid = :sid", array(':sid' => $shipment_id))->fetchObject();
if ($shipment) {
$result = db_query("SELECT package_id FROM {uc_packages} WHERE sid = :sid", array(':sid' => $shipment_id));
$packages = array();
foreach ($result as $package) {
$packages[$package->package_id] = uc_shipping_package_load($package->package_id);
}
$shipment->packages = $packages;
$extra = module_invoke_all('uc_shipment', 'load', $shipment);
if (is_array($extra)) {
foreach ($extra as $key => $value) {
$shipment->$key = $value;
}
}
}
return $shipment;
}
/**
* Saves a shipment.
*/
function uc_shipping_shipment_save($shipment) {
if (isset($shipment->origin)) {
foreach ($shipment->origin as $field => $value) {
$field = 'o_' . $field;
$shipment->$field = $value;
$fields[$field] = $value;
}
}
if (isset($shipment->destination)) {
foreach ($shipment->destination as $field => $value) {
$field = 'd_' . $field;
$shipment->$field = $value;
$fields[$field] = $value;
}
}
if (!$shipment->sid) {
drupal_write_record('uc_shipments', $shipment);
$shipment->is_new = TRUE;
}
else {
drupal_write_record('uc_shipments', $shipment, 'sid');
$shipment->is_new = FALSE;
}
if (is_array($shipment->packages)) {
foreach ($shipment->packages as $package) {
$package->sid = $shipment->sid;
// Since the products haven't changed, we take them out of the object so that they are not deleted and re-inserted.
$products = $package->products;
unset($package->products);
uc_shipping_package_save($package);
// But they're still necessary for hook_uc_shipment(), so they're added back in.
$package->products = $products;
}
}
module_invoke_all('uc_shipment', 'save', $shipment);
$order = uc_order_load($shipment->order_id);
rules_invoke_event('uc_shipment_save', $order, $shipment);
}
/**
* Deletes a shipment.
*/
function uc_shipping_shipment_delete($shipment_id) {
$shipment = uc_shipping_shipment_load($shipment_id);
db_update('uc_packages')
->fields(array(
'sid' => NULL,
'tracking_number' => NULL,
'label_image' => NULL,
))
->condition('sid', $shipment_id)
->execute();
db_delete('uc_shipments')
->condition('sid', $shipment_id)
->execute();
foreach ($shipment->packages as $package) {
if ($package->label_image) {
file_delete($package->label_image);
unset($package->label_image);
}
}
module_invoke_all('uc_shipment', 'delete', $shipment);
}
/**
* Shipping order pane callback.
*
* @see uc_shipping_uc_order_pane()
*/
function uc_shipping_order_pane_packages($op, $order) {
switch ($op) {
case 'view':
case 'customer':
$tracking = array();
$result = db_query("SELECT sid FROM {uc_shipments} WHERE order_id = :id", array(':id' => $order->order_id));
foreach ($result as $shipment) {
$shipment = uc_shipping_shipment_load($shipment->sid);
if ($shipment->tracking_number) {
$tracking[$shipment->carrier]['children'][] = check_plain($shipment->tracking_number);
}
else {
foreach ($shipment->packages as $package) {
if ($package->tracking_number) {
$tracking[$shipment->carrier]['children'][] = check_plain($package->tracking_number);
}
}
}
}
$build = array();
foreach ($tracking as $carrier => $item) {
$build['tracking'][] = array(
'#prefix' => '' . $carrier . ':',
'#theme' => 'item_list',
'#items' => $item,
);
}
return $build;
}
}
/**
* Chooses an address to fill out a form.
*/
function uc_shipping_select_address($addresses, $onchange = '', $title = NULL, $icon_suffix = FALSE) {
if (!is_array($addresses) || count($addresses) == 0) {
$addresses = array();
}
$store_address = variable_get('uc_quote_store_default_address', new UcAddress());
if (!in_array($store_address, $addresses)) {
$addresses[] = $store_address;
}
$blank = array(
'first_name' => '',
'last_name' => '',
'phone' => '',
'company' => '',
'street1' => '',
'street2' => '',
'city' => '',
'postal_code' => '',
'country' => 0,
'zone' => 0,
);
$options = array(drupal_json_encode($blank) => t('- Reset fields -'));
foreach ($addresses as $address) {
$options[drupal_json_encode($address)] = $address->company . ' ' . $address->street1 . ' ' . $address->city;
}
$select = array(
'#type' => 'select',
'#title' => is_null($title) ? t('Address book') : $title,
'#options' => $options,
'#default_value' => drupal_json_encode($addresses[0]),
'#attributes' => array('onchange' => $onchange),
'#suffix' => $icon_suffix ? uc_store_get_icon('file:address_book', FALSE, 'address-book-icon') : NULL,
);
return $select;
}
/**
* Helper function for addresses in forms.
*
* @ingroup forms
*/
function uc_shipping_address_form($form, &$form_state, $addresses, $order) {
drupal_add_js(drupal_get_path('module', 'uc_shipping') . '/uc_shipping.js');
$form['origin'] = array('#type' => 'fieldset',
'#title' => t('Origin address'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => -2,
'#theme' => 'uc_shipping_address',
);
$address = reset($addresses);
$form['origin']['pickup_address_select'] = uc_shipping_select_address($addresses, 'apply_address(\'pickup\', this.value);', t('Saved Addresses'), TRUE);
$form['origin']['pickup_address_select']['#weight'] = -2;
$form['origin']['pickup_email'] = uc_textfield(uc_get_field_name('email'), variable_get('uc_store_email', NULL), FALSE, NULL, 255);
$form['origin']['pickup_email']['#weight'] = -1;
$form['origin']['pickup_first_name'] = uc_textfield(uc_get_field_name('first_name'), $address->first_name, FALSE);
$form['origin']['pickup_last_name'] = uc_textfield(uc_get_field_name('last_name'), $address->last_name, FALSE);
$form['origin']['pickup_phone'] = uc_textfield(uc_get_field_name('phone'), variable_get('uc_store_phone', NULL), FALSE, NULL, 32, 16);
$form['origin']['pickup_company'] = uc_textfield(uc_get_field_name('company'), $address->company, FALSE);
$form['origin']['pickup_street1'] = uc_textfield(uc_get_field_name('street1'), $address->street1, FALSE, NULL, 64);
$form['origin']['pickup_street2'] = uc_textfield(uc_get_field_name('street2'), $address->street2, FALSE, NULL, 64);
$form['origin']['pickup_city'] = uc_textfield(uc_get_field_name('city'), $address->city, FALSE);
$form['origin']['pickup_country'] = uc_country_select(uc_get_field_name('country'), $address->country);
if (isset($_POST['pickup_country'])) {
$country = $_POST['pickup_country'];
}
else {
$country = $address->country;
}
$form['origin']['pickup_zone'] = uc_zone_select(uc_get_field_name('zone'), $address->zone, NULL, $country);
$form['origin']['pickup_postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $address->postal_code, FALSE, NULL, 10, 10);
$order_form = uc_order_pane_ship_to('edit-form', $order);
$form['destination'] = $order_form['ship_to'];
$form['destination']['delivery_email'] = uc_textfield(uc_get_field_name('email'), $order->primary_email, FALSE, NULL, 255);
$form['destination']['delivery_email']['#weight'] = -1;
$form['destination']['#type'] = 'fieldset';
$form['destination']['#title'] = t('Destination address');
$form['destination']['#collapsible'] = TRUE;
$form['destination']['#weight'] = -1;
$form['destination']['#theme'] = 'uc_shipping_address';
return $form;
}