array( 'title' => t('Configure quotes'), ), ); } /** * Implements hook_menu(). */ function uc_quote_menu() { $items = array(); $items['admin/store/settings/quotes'] = array( 'title' => 'Shipping quote settings', 'description' => 'Configure the shipping quote settings.', 'page callback' => 'uc_quote_overview', 'access arguments' => array('configure quotes'), 'file' => 'uc_quote.admin.inc', ); $items['admin/store/settings/quotes/overview'] = array( 'title' => 'Overview', 'description' => 'View general shipping quote settings.', 'access arguments' => array('configure quotes'), 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/store/settings/quotes/edit'] = array( 'title' => 'Quote settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_quote_admin_settings'), 'access arguments' => array('configure quotes'), 'weight' => -8, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_quote.admin.inc', ); $items['admin/store/settings/quotes/methods'] = array( 'title' => 'Quote methods', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_quote_method_settings'), 'access arguments' => array('configure quotes'), 'weight' => -5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_quote.admin.inc', ); $items['admin/store/settings/quotes/methods/general'] = array( 'title' => 'General settings', 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); return $items; } /** * Implements hook_init(). */ function uc_quote_init() { drupal_add_css(drupal_get_path('module', 'uc_quote') . '/uc_quote.css', array('every_page' => TRUE)); global $conf; $conf['i18n_variables'][] = 'uc_quote_err_msg'; $conf['i18n_variables'][] = 'uc_quote_pane_description'; } /** * Implements hook_theme(). */ function uc_quote_theme() { return array( 'uc_quote_method_settings' => array( 'render element' => 'form', 'file' => 'uc_quote.admin.inc', ), 'uc_cart_pane_quotes' => array( 'render element' => 'form', ), 'uc_quote_returned_rates' => array( 'render element' => 'form', ), ); } /** * Implements hook_node_insert(). */ function uc_quote_node_insert($node) { uc_quote_node_update($node); } /** * Implements hook_node_update(). */ function uc_quote_node_update($node) { if (uc_product_is_product($node->type)) { if (isset($node->shipping_type)) { uc_quote_set_shipping_type('product', $node->nid, $node->shipping_type); } if ($node->street1) { db_merge('uc_quote_product_locations') ->key(array('nid' => $node->nid)) ->fields(array( 'first_name' => $node->first_name, 'last_name' => $node->last_name, 'company' => $node->company, 'street1' => $node->street1, 'street2' => $node->street2, 'city' => $node->city, 'zone' => $node->zone, 'postal_code' => $node->postal_code, 'country' => $node->country, )) ->execute(); } else { db_delete('uc_quote_product_locations') ->condition('nid', $node->nid) ->execute(); } } } /** * Implements hook_node_load(). */ function uc_quote_node_load($nodes, $types) { $product_types = array_intersect(uc_product_types(), $types); if (empty($product_types)) { return; } $shipping_type = variable_get('uc_store_shipping_type', 'small_package'); $shipping_types = db_query("SELECT id, shipping_type FROM {uc_quote_shipping_types} WHERE id_type = :type AND id IN (:ids)", array(':type' => 'product', ':ids' => array_keys($nodes)))->fetchAllKeyed(); $addresses = db_query("SELECT nid, first_name, last_name, company, street1, street2, city, zone, postal_code, country, phone FROM {uc_quote_product_locations} WHERE nid IN (:nids)", array(':nids' => array_keys($nodes)), array('fetch' => 'UcAddress'))->fetchAllAssoc('nid'); foreach ($nodes as $nid => &$node) { if (!in_array($node->type, $product_types)) { continue; } if (isset($shipping_types[$nid])) { $node->shipping_type = $shipping_types[$nid]; } else { $node->shipping_type = $shipping_type; } if (isset($addresses[$nid])) { $node->shipping_address = $addresses[$nid]; unset($node->shipping_address->nid); } else { $node->shipping_address = variable_get('uc_quote_store_default_address', new UcAddress()); } } } /** * Implements hook_node_delete(). */ function uc_quote_node_delete($node) { db_delete('uc_quote_shipping_types') ->condition('id_type', 'product') ->condition('id', $node->nid) ->execute(); db_delete('uc_quote_product_locations') ->condition('nid', $node->nid) ->execute(); } /** * Implements hook_form_alter(). * * Adds a default shipping address for products and manufacturers. If it is * left blank, products default to their manufacturers', which default to * the store's. */ function uc_quote_form_alter(&$form, &$form_state, $form_id) { // Alter the product node form. if (uc_product_is_product_form($form)) { // Get the shipping address. if (isset($form['#node']->shipping_address)) { $address = $form['#node']->shipping_address; } // Use the store default if the product does not have an address set. if (empty($address)) { $address = variable_get('uc_quote_store_default_address', new UcAddress()); } // Store the country to use for the zone select based on $_POST. // TODO: Fix this for D6! Neither the $_POST or $form_state are available // when the node form is being processed. : ( if (isset($_POST['country'])) { $country = $_POST['country']; } else { $country = $address->country; } // Initialize the shipping fieldset array. if (!isset($form['shipping'])) { $form['shipping'] = array(); } $form['shipping'] += array( '#type' => 'fieldset', '#title' => t('Shipping settings'), '#collapsible' => TRUE, '#weight' => -3, '#attributes' => array('class' => array('product-shipping')), '#group' => 'additional_settings', ); $form['shipping']['shipping_type'] = array( '#type' => 'select', '#title' => t('Default product shipping type'), '#empty_value' => '', '#empty_option' => t('- Store default -'), '#default_value' => isset($form['#node']->nid) ? uc_quote_get_shipping_type('product', $form['#node']->nid) : '', '#options' => uc_quote_shipping_type_options(), '#weight' => -7, ); // Add the default pickup address fieldset. $form['shipping']['default_address'] = array( '#type' => 'fieldset', '#title' => t('Default product pickup address'), '#description' => t('When delivering products to customers, the original location of the product must be known in order to accurately quote the shipping cost and set up a delivery. If this pickup address is left blank, this product will default to the store pickup address.', array('!url' => url('admin/store/settings/quotes'))), '#collapsible' => TRUE, '#collapsed' => TRUE, '#weight' => -6, ); $form['shipping']['default_address']['first_name'] = uc_textfield(uc_get_field_name('first_name'), $address->first_name, FALSE); $form['shipping']['default_address']['last_name'] = uc_textfield(uc_get_field_name('last_name'), $address->last_name, FALSE); $form['shipping']['default_address']['company'] = uc_textfield(uc_get_field_name('company'), $address->company, FALSE); $form['shipping']['default_address']['street1'] = uc_textfield(uc_get_field_name('street1'), $address->street1, FALSE, NULL, 64); $form['shipping']['default_address']['street2'] = uc_textfield(uc_get_field_name('street2'), $address->street2, FALSE, NULL, 64); $form['shipping']['default_address']['city'] = uc_textfield(uc_get_field_name('city'), $address->city, FALSE); $form['shipping']['default_address']['zone'] = uc_zone_select(uc_get_field_name('zone'), $address->zone, NULL, $country); $form['shipping']['default_address']['postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $address->postal_code, FALSE, NULL, 10, 10); $form['shipping']['default_address']['country'] = uc_country_select(uc_get_field_name('country'), $address->country); } } /** * Implements hook_form_FORM_ID_alter() for uc_cart_checkout_form(). * * Adds Ajax shipping quote functionality to the checkout form. */ function uc_quote_form_uc_cart_checkout_form_alter(&$form, &$form_state) { $ajax = array( 'callback' => 'uc_quote_checkout_returned_rates', 'effect' => 'slide', 'progress' => array( 'type' => 'throbber', 'message' => t('Receiving shipping quotes...'), ), ); if (isset($form['panes']['delivery']['delivery_postal_code'])) { $form['panes']['delivery']['delivery_postal_code']['#ajax'] = $ajax; } if (isset($form['panes']['delivery']['delivery_address_select'])) { $form['panes']['delivery']['delivery_address_select']['#ajax'] = $ajax; } // @todo: Figure out what needs to be done when the copy-address box is checked. if (isset($form['panes']['payment']['line_items'])) { $form['panes']['quotes']['quotes']['quote_option']['#ajax'] = array( 'callback' => 'uc_payment_get_totals', 'wrapper' => 'line-items-div', ); } } /****************************************************************************** * Ubercart Hooks * ******************************************************************************/ /** * Implements hook_uc_cart_pane(). */ function uc_quote_uc_cart_pane($items) { if (arg(0) == 'cart') { if (!variable_get('uc_cap_quotes_enabled', FALSE) || (variable_get('uc_cart_delivery_not_shippable', TRUE) && !uc_cart_is_shippable())) { return array(); } $body = drupal_get_form('uc_cart_pane_quotes', $items); } else { $body = ''; } $panes[] = array('id' => 'quotes', 'title' => t('Shipping quotes'), 'enabled' => FALSE, 'weight' => 5, 'body' => $body, ); return $panes; } /** * Defines the shipping quote checkout pane. */ function uc_quote_uc_checkout_pane() { $panes[] = array( 'id' => 'quotes', 'callback' => 'uc_checkout_pane_quotes', 'title' => t('Calculate shipping cost'), 'desc' => t('Extra information necessary to ship.'), 'weight' => 5, 'shippable' => TRUE, ); return $panes; } /** * Defines the shipping quote order pane. */ function uc_quote_uc_order_pane() { $panes = array(); $panes[] = array( 'id' => 'quotes', 'callback' => 'uc_order_pane_quotes', 'title' => t('Shipping quote'), 'desc' => t('Get a shipping quote for the order from a quoting module.'), 'class' => 'abs-left', 'weight' => 7, 'show' => array('edit'), ); return $panes; } /** * Implements hook_uc_order(). */ function uc_quote_uc_order($op, &$order, $arg2) { switch ($op) { case 'save': if (isset($order->quote['method'])) { db_merge('uc_order_quotes') ->key(array('order_id' => $order->order_id)) ->fields(array( 'method' => $order->quote['method'], 'accessorials' => $order->quote['accessorials'], 'rate' => $order->quote['rate'], )) ->execute(); } break; case 'load': $quote = db_query("SELECT method, accessorials, rate FROM {uc_order_quotes} WHERE order_id = :id", array(':id' => $order->order_id))->fetchAssoc(); $order->quote = $quote; $order->quote['accessorials'] = strval($quote['accessorials']); break; case 'delete': db_delete('uc_order_quotes') ->condition('order_id', $order->order_id) ->execute(); break; } } /** * Implements hook_uc_line_item(). */ function uc_quote_uc_line_item() { $items[] = array( 'id' => 'shipping', 'title' => t('Shipping'), 'weight' => 1, 'default' => FALSE, 'stored' => TRUE, 'calculated' => TRUE, 'display_only' => FALSE, 'add_list' => TRUE, ); return $items; } /** * Implements hook_uc_shipping_type(). */ function uc_quote_uc_shipping_type() { $weight = variable_get('uc_quote_type_weight', array('small_package' => 0)); $types = array(); $types['small_package'] = array( 'id' => 'small_package', 'title' => t('Small package'), 'weight' => $weight['small_package'], ); return $types; } /****************************************************************************** * Module Functions * ******************************************************************************/ /** * Stores the shipping type of products and manufacturers. * * Fulfillment modules are invoked for products that match their shipping type. * This function stores the shipping type of a product or a manufacturer. * * @param $id_type * Type can be 'product' or 'manufacturer'. * @param $id * Either the node id or term id of the object receiving the shipping type. * @param $shipping_type * The type of product that is fulfilled by various fulfillment modules. */ function uc_quote_set_shipping_type($id_type, $id, $shipping_type) { if ($shipping_type !== '') { db_merge('uc_quote_shipping_types') ->key(array( 'id_type' => $id_type, 'id' => $id, )) ->fields(array('shipping_type' => $shipping_type)) ->execute(); } else { db_delete('uc_quote_shipping_types') ->condition('id_type', $id_type) ->condition('id', $id) ->execute(); } } /** * Retrieves a product's or manufacturer's shipping type from the database. * * @param $id_type * Type can be 'product' or 'manufacturer'. * @param $id * Either the node id or term id of the object that was assigned * the shipping type. * * @return * The shipping type. */ function uc_quote_get_shipping_type($id_type, $id) { static $types = array(); if (!isset($types[$id_type][$id])) { $types[$id_type][$id] = db_query("SELECT shipping_type FROM {uc_quote_shipping_types} WHERE id_type = :type AND id = :id", array(':type' => $id_type, ':id' => $id))->fetchField(); } return $types[$id_type][$id]; } /** * Gets a product's shipping type, defaulting to the store's if it * doesn't exist. * * @param $product * The product object. * * @return * The shipping type. */ function uc_product_get_shipping_type($product) { $shipping_type = variable_get('uc_store_shipping_type', 'small_package'); if ($type = uc_quote_get_shipping_type('product', $product->nid)) { $shipping_type = $type; } return $shipping_type; } /** * Gets a product's default shipping address. * * Loads the default shipping address of a product, it's manufacturer's, or the * store's, whichever is available. * * @param $nid * A product node id. * * @return * An address object. */ function uc_quote_get_default_shipping_address($nid) { $address = db_query("SELECT first_name, last_name, company, street1, street2, city, zone, postal_code, country, phone FROM {uc_quote_product_locations} WHERE nid = :nid", array(':nid' => $nid), array('fetch' => 'UcAddress'))->fetchObject(); if (empty($address)) { $address = variable_get('uc_quote_store_default_address', new UcAddress()); } return $address; } /** * Cart pane callback. * * @see theme_uc_cart_pane_quotes() * @ingroup forms */ function uc_cart_pane_quotes($form, &$form_state, $items) { global $user; $form['delivery_country'] = uc_country_select(uc_get_field_name('country'), isset($form_state['values']['delivery_country']) ? $form_state['values']['delivery_country'] : uc_store_default_country(), NULL, 'name', TRUE); $country_id = isset($_POST['delivery_country']) ? intval($_POST['delivery_country']) : uc_store_default_country(); $form['delivery_zone'] = uc_zone_select(uc_get_field_name('zone'), isset($form_state['values']['delivery_zone']) ? $form_state['values']['delivery_zone'] : NULL, NULL, $country_id, 'name', TRUE); $form['delivery_postal_code'] = uc_textfield(uc_get_field_name('postal_code'), isset($form_state['values']['delivery_postal_code']) ? $form_state['values']['delivery_postal_code'] : '', TRUE, NULL, 10, 10); $form['get_quote'] = array( '#type' => 'button', '#value' => t('Calculate'), '#ajax' => array( 'callback' => 'uc_quote_cart_returned_rates', 'wrapper' => 'quote', ), ); $form['page'] = array( '#type' => 'hidden', '#value' => 'cart', ); $order = new UcOrder(); $order->uid = $user->uid; $order->delivery_country = $form['delivery_country']['#default_value']; $order->delivery_zone = $form['delivery_zone']['#default_value']; $order->delivery_postal_code = $form['delivery_postal_code']['#default_value']; $order->products = $items; module_load_include('inc', 'uc_quote', 'uc_quote.pages'); $quotes = uc_quote_assemble_quotes($order); $quote_options = array(); if (!empty($quotes)) { foreach ($quotes as $method => $data) { foreach ($data as $accessorial => $quote) { $key = $method . '---' . $accessorial; if (isset($quote['rate'])) { $quote_options[$key] = t('!label: !price', array('!label' => $quote['option_label'], '!price' => $quote['format'])); } } } } $form['quote'] = array( '#theme' => 'item_list', '#items' => $quote_options, '#prefix' => '
' . $quote['debug'] . '', ); } if (!isset($quote['rate']) && count($return[$key])) { $return[$key]['#prefix'] = $quote['label'] . ': '; } } } } $num_quotes = count($quote_options); $default = key($quote_options); if ($num_quotes > 1) { if (isset($order->quote['method']) && isset($order->quote['accessorials'])) { $default = $order->quote['method'] . '---' . $order->quote['accessorials']; } $return['quote_option'] = array( '#type' => 'radios', '#options' => $quote_options, '#default_value' => $default, ); } elseif ($num_quotes == 1) { $return['quote_option'] = array( '#type' => 'hidden', '#value' => $default, '#suffix' => $quote_options[$default], ); } else { $return['error'] = array( '#markup' => filter_xss_admin(variable_get('uc_quote_err_msg', t("There were problems getting a shipping quote. Please verify the delivery address and product information and try again.\nIf this does not resolve the issue, please call @phone to complete your order.", array('@phone' => variable_get('uc_store_phone', NULL))))), ); } $return['#theme'] = 'uc_quote_returned_rates'; return $return; } /** * */ function uc_quote_cart_returned_rates($form, $form_state) { $commands[] = ajax_command_replace('#quote', drupal_render($form['quote'])); $commands[] = ajax_command_prepend('#quote', theme('status_messages')); return array('#type' => 'ajax', '#commands' => $commands); } /** * AJAX callback for calculated shipping rates. */ function uc_quote_checkout_returned_rates($form, $form_state) { $commands[] = ajax_command_replace('#quote', drupal_render($form['panes']['quotes']['quotes'])); $commands[] = ajax_command_prepend('#quote', theme('status_messages')); // Show default shipping rate as a line item. if (isset($form['panes']['payment']['line_items'])) { $commands[] = ajax_command_replace('#line-items-div', drupal_render($form['panes']['payment']['line_items'])); $commands[] = ajax_command_prepend('#line-items-div', theme('status_messages')); } return array('#type' => 'ajax', '#commands' => $commands); } /** * AJAX callback for calculated shipping rates. */ function uc_quote_order_returned_rates($form, $form_state) { $commands[] = ajax_command_replace('#quote', drupal_render($form['quotes']['quotes'])); $commands[] = ajax_command_prepend('#quote', theme('status_messages')); return array('#type' => 'ajax', '#commands' => $commands); } /** * Displays the returned shipping rates. * * @ingroup themeable */ function theme_uc_quote_returned_rates($variables) { $form = $variables['form']; $output = ''; $keys = element_children($form); // Render notes and error messages after each radio button. if (count($keys) > 1) { foreach ($keys as $key) { if ($key == 'quote_option') { continue; } if (isset($form['quote_option'][$key])) { $output .= drupal_render($form['quote_option'][$key]); } $output .= drupal_render($form[$key]); } } $output .= drupal_render_children($form); return '