Last active
August 29, 2015 14:00
-
-
Save msupko/6fb60b3107e5d44413cb to your computer and use it in GitHub Desktop.
Fixing price calculation for commerce_bundle_add_to_cart_form()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Provides product bundles for Drupal Commerce | |
*/ | |
module_load_include('inc', 'commerce_bundle', 'includes/commerce_bundle.field'); | |
/** | |
* Implements hook_views_api(). | |
*/ | |
function commerce_bundle_views_api() { | |
return array( | |
'api' => 3, | |
'path' => drupal_get_path('module', 'commerce_bundle') . '/includes/views', | |
); | |
} | |
/** | |
* Implements of hook_entity_info_alter(). | |
*/ | |
function commerce_bundle_entity_info_alter(&$info) { | |
// Add custom view mode for add to cart. | |
if (isset($info['commerce_product'])) { | |
$info['commerce_product']['view modes']['commerce_bundle_add_to_cart_form'] = array( | |
'label' => t('Commerce Bundle: Add to cart form'), | |
'custom settings' => FALSE, | |
); | |
} | |
return $info; | |
} | |
/** | |
* Retuns an array of bundle line item types | |
*/ | |
function commerce_bundle_line_item_types() { | |
$types = array(); | |
foreach (commerce_line_item_types() as $type => $line_item_type) { | |
if (!empty($line_item_type['product']) && !empty($line_item_type['bundle'])) { | |
$types[] = $type; | |
} | |
} | |
return $types; | |
} | |
/** | |
* Implements hook_commerce_bundle_item_can_delete(). | |
*/ | |
function commerce_bundle_commerce_bundle_item_can_delete($entity) { | |
$line_item_types = commerce_bundle_line_item_types(); | |
if (empty($line_item_types)) { | |
return TRUE; | |
} | |
// Use EntityFieldQuery to look for line items referencing this bundle item | |
// and do not allow the delete to occur if one exists. | |
$query = new EntityFieldQuery(); | |
$query | |
->addTag('commerce_bundle_commerce_bundle_item_can_delete') | |
->entityCondition('entity_type', 'commerce_line_item', '=') | |
->entityCondition('bundle', $line_item_types, 'IN') | |
->fieldCondition('commerce_bundle_item_id', 'target_id', $entity->item_id, '=') | |
->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT') | |
->count(); | |
return $query->execute() == 0; | |
} | |
/** | |
* Implements hook_commerce_line_item_type_info(). | |
*/ | |
function commerce_bundle_commerce_line_item_type_info() { | |
$line_item_types = array(); | |
$line_item_types['commerce_bundle_line_item'] = array( | |
'type' => 'commerce_bundle_line_item', | |
'name' => t('Bundle Line Item'), | |
'description' => t('References Bundle Groups, Bundle Items, and Bundle Products.'), | |
'product' => TRUE, | |
'bundle' => TRUE, | |
'add_form_submit_value' => t('Add bundle'), | |
'base' => 'commerce_product_line_item', | |
'callbacks' => array( | |
'title' => 'commerce_bundle_line_item_title', | |
), | |
); | |
return $line_item_types; | |
} | |
/** | |
* Returns a title of the bundle line item for use in Views. | |
* | |
* @param $line_item | |
* The bundle line item object whose title should be returned. | |
* | |
* @return | |
* The appropriate title depending on if the line item is a control row or not. | |
*/ | |
function commerce_bundle_line_item_title($line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
// Currently, just return the product's title. However, in the future replace | |
// this with extra bundle info. | |
if ($product = $line_item_wrapper->commerce_product->value()) { | |
return check_plain($product->title); | |
} | |
} | |
/** | |
* Generates unique config ids used to group similar line items. | |
* | |
* @param $line_items | |
* An array of line item objects. | |
* | |
* @return | |
* An array keyed by bundle configuration IDs with values of keys from the | |
* given line items array. | |
*/ | |
function commerce_bundle_line_item_get_bundle_configs($line_items) { | |
$config_field_names = array( | |
'commerce_bundle_id', | |
'commerce_bundle_group_id', | |
'commerce_bundle_item_id', | |
'commerce_product', | |
); | |
$add_to_cart_combine = FALSE; | |
$bundles = array(); | |
foreach ($line_items as $delta => $line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
foreach ($config_field_names as $config_field_name) { | |
if (!isset($line_item_wrapper->{$config_field_name})) { | |
continue 2; | |
} | |
} | |
$bundle_id = NULL; | |
$config_ids = array(); | |
foreach ($config_field_names as $config_field_name) { | |
$entity_id = $line_item_wrapper->{$config_field_name}->raw(); | |
if (empty($entity_id)) { | |
continue 2; | |
} | |
if ($config_field_name == 'commerce_bundle_id') { | |
$bundle_id = $entity_id; | |
} | |
$config_ids[$config_field_name] = $entity_id; | |
} | |
if ($bundle_id) { | |
$bundles[$bundle_id][$delta] = implode('_', $config_ids); | |
// Set flag if we want each bundle as its own line item instead of | |
// combining bundles. We only care if we find one TRUE. | |
$add_to_cart_combine = $line_item->data['context']['add_to_cart_combine'] || $add_to_cart_combine; | |
} | |
} | |
$return = array(); | |
if (!empty($bundles)) { | |
foreach ($bundles as $bundle_id => $configs) { | |
// Hash entity ids representing the config. | |
$hash_data = implode('_', array_values($configs)); | |
// If we do not want to combine line items then add a unique value to the | |
// hash data. | |
$hash_data = !$add_to_cart_combine ? $hash_data . '_' . microtime(true) : $hash_data; | |
$hash = drupal_hash_base64($hash_data); | |
// Store line item ids; | |
$return[$hash] = array(); | |
foreach ($configs as $delta => $combined_config_ids) { | |
$return[$hash][$delta] = $line_items[$delta]; | |
} | |
} | |
} | |
return $return; | |
} | |
/** | |
* Creates a new bundle product line item populated with the proper product values. | |
* | |
* @param $product | |
* The fully loaded product entity. | |
* @param $bundle_item | |
* The fully loaded bundle item entity. | |
* @param $group | |
* The fully loaded product group. | |
* @param $bundler | |
* The fully loaded entity bundling all the product groups. | |
* @param $quantity | |
* The quantity to set for the product. | |
* @param $order_id | |
* The ID of the order the line item belongs to (if available). | |
* @param $data | |
* A data array to set on the new line item. The following information in the | |
* data array may be used on line item creation: | |
* - $data['context']['display_path']: if present will be used to set the line | |
* item's display_path field value. | |
* @param $type | |
* The type of product line item to create. Must be a product line item as | |
* defined in the line item type info array, and the line item type must | |
* include the expected product related fields. Defaults to the base product | |
* line item type defined by the Product Reference module. | |
* | |
* @return | |
* The fully loaded line item populated with the product data as specified. | |
*/ | |
function commerce_bundle_product_line_item_new($product, $bundle_item, $group, $bundler, $quantity = 1, $order_id = 0, $data = array(), $type = 'commerce_bundle_line_item') { | |
// Ensure a default product line item type. | |
if (empty($type)) { | |
$type = 'commerce_bundle_line_item'; | |
} | |
// Create the new line item. | |
$line_item = entity_create('commerce_line_item', array( | |
'type' => $type, | |
'order_id' => $order_id, | |
'quantity' => $quantity, | |
'data' => $data, | |
)); | |
// Populate it with the entity information. | |
commerce_bundle_product_line_item_populate($line_item, $bundler, $group, $bundle_item, $product); | |
// Return the line item. | |
return $line_item; | |
} | |
/** | |
* Populates an existing bundle product line item with the product and quantity data. | |
* | |
* @param $line_item | |
* The fully loaded line item object, populated by reference. | |
* @param $bundler | |
* The fully loaded entity bundling all the product groups. | |
* @param $group | |
* The fully loaded product group. | |
* @param $bundle_item | |
* The fully loaded bundle item entity. | |
* @param $product | |
* The fully loaded product entity. | |
*/ | |
function commerce_bundle_product_line_item_populate($line_item, $bundler, $group, $bundle_item, $product) { | |
// Wrap the entities. | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
$group_wrapper = entity_metadata_wrapper('commerce_product', $group); | |
$bundle_item_wrapper = entity_metadata_wrapper('commerce_bundle_item', $bundle_item); | |
// Set the bundler reference - lazy set to allow any entity type. | |
if (isset($line_item_wrapper->commerce_bundle_id)) { | |
$line_item_wrapper->commerce_bundle_id = $bundler; | |
} | |
// Set the product group reference. | |
if (isset($line_item_wrapper->commerce_bundle_group_id)) { | |
$line_item_wrapper->commerce_bundle_group_id = $group; | |
} | |
// Set the bundle item reference. | |
if (isset($line_item_wrapper->commerce_bundle_item_id)) { | |
$line_item_wrapper->commerce_bundle_item_id = $bundle_item->item_id; | |
} | |
// Set the product reference. | |
$line_item_wrapper->commerce_product = $product->product_id; | |
// Set the label to be the product SKU. | |
/** @todo: should label be set to something indicating bundle? **/ | |
$line_item->line_item_label = $product->sku; | |
// Set unit quantity and update line item quantity. | |
$unit_quantity = NULL; | |
if (isset($group_wrapper->commerce_bundle_unit_quantity) && | |
isset($line_item_wrapper->commerce_bundle_unit_quantity)) { | |
// Set the unit quanity. | |
$unit_quantity = $group_wrapper->commerce_bundle_unit_quantity->value(); | |
$line_item_wrapper->commerce_bundle_unit_quantity = $unit_quantity; | |
// Update line item quantity. | |
$line_item->quantity *= (int) $unit_quantity; | |
} | |
$bundle_unit_price = commerce_bundle_get_bundle_product_price($group->product_id, $bundle_item->item_id); | |
// Set the unit price as the resolved bundle price. | |
$line_item_wrapper->commerce_unit_price = $bundle_unit_price; | |
// TODO: add an alter hook for bundle price ? | |
if (!is_null($bundle_unit_price)) { | |
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add( | |
$bundle_unit_price, | |
'base_price', | |
$bundle_unit_price, | |
TRUE | |
); | |
} | |
} | |
/** | |
* Builds an appropriate cart form ID based on the product groups on the form. | |
* | |
* @see commerce_bundle_forms(). | |
*/ | |
function commerce_bundle_add_to_cart_form_id($group_ids) { | |
// Make sure the length of the form id is limited. | |
$data = implode('_', $group_ids); | |
if (strlen($data) > 50) { | |
$data = drupal_hash_base64($data); | |
} | |
return 'commerce_bundle_add_to_cart_form_' . $data; | |
} | |
/** | |
* Implements hook_forms(). | |
* | |
* To provide distinct form IDs for add to cart forms, the product IDs | |
* referenced by the form are appended to the base ID, | |
* commerce_cart_add_to_cart_form. When such a form is built or submitted, this | |
* function will return the proper callback function to use for the given form. | |
*/ | |
function commerce_bundle_forms($form_id, $args) { | |
$forms = array(); | |
// Construct a valid cart form ID from the arguments. | |
if (strpos($form_id, 'commerce_bundle_add_to_cart_form_') === 0) { | |
$forms[$form_id] = array( | |
'callback' => 'commerce_bundle_add_to_cart_form', | |
); | |
} | |
return $forms; | |
} | |
/** | |
* Builds an Add to Cart form for a set of products. | |
* | |
* @param $line_items | |
* An array of fully formed bundle product line itema whose data will be | |
* used in the following ways by the form: | |
* - $line_item->data['context']['add_to_cart_combine']: a boolean indicating | |
* whether or not to attempt to combine the product added to the cart with | |
* existing line items of matching fields. | |
* - $line_item->data['context']['show_single_product_attributes']: a boolean | |
* indicating whether or not product attribute fields with single options | |
* should be shown on the Add to Cart form. | |
* - $line_item->quantity: the default value for the quantity widget if | |
* included (determined by the $show_quantity parameter). | |
* - $line_item->commerce_product: the value of this field will be used as the | |
* default product ID when the form is built for multiple products. | |
* The line item's data array will be used on submit to set the data array of | |
* the product line item created by the form. | |
* @param $show_quantity | |
* Boolean indicating whether or not to show the quantity widget; defaults to | |
* FALSE resulting in a hidden field holding the quantity. | |
* @param $context | |
* Information on the context of the form's placement, allowing it to update | |
* product fields on the page based on the currently selected default product. | |
* Should be an associative array containing the following keys: | |
* - class_prefix: a prefix used to target HTML containers for replacement | |
* with rendered fields as the default product is updated. For example, | |
* nodes display product fields in their context wrapped in spans with the | |
* class node-#-product-field_name. The class_prefix for the add to cart | |
* form displayed on a node would be node-# with this form's AJAX refresh | |
* adding the suffix -product-field_name. | |
* - view_mode: a product view mode that tells the AJAX refresh how to render | |
* the replacement fields. | |
* If no context is specified, AJAX replacement of rendered fields will not | |
* happen. This parameter only affects forms containing multiple products. | |
* | |
* @return | |
* The form array. | |
*/ | |
function commerce_bundle_add_to_cart_form($form, &$form_state, $line_items, $show_quantity = FALSE, $default_quantity = 1, $context = array()) { | |
global $user; | |
// Store the context in the form state for use during AJAX refreshes. | |
$form_state['context'] = $context; | |
// Store the line item passed to the form builder for reference on submit. | |
$form_state['line_items'] = &$line_items; | |
// Initial product group storage. | |
if (!isset($form_state['product_groups'])) { | |
$form_state['product_groups'] = array(); | |
} | |
// Add a generic class ID. | |
$form['#attributes']['class'][] = drupal_html_class('commerce-bundle-add-to-cart'); | |
$form['#attributes']['class'][] = drupal_html_class('commerce-add-to-cart'); | |
// Store the customer uid in the form so other modules can override with a | |
// selection widget if necessary. | |
$form['uid'] = array( | |
'#type' => 'value', | |
'#value' => $user->uid, | |
); | |
// Add container for all product groups. | |
$form['product_groups'] = array( | |
'#type' => 'container', | |
'#attributes' => array('class' => array( | |
'commerce-bundle-product-groups', | |
)), | |
); | |
$groups_container = &$form['product_groups']; | |
// Build attribute forms per product group. | |
$group_ids = array(); | |
$form_state['bundle_total_price'] = 0; | |
$currency_code = NULL; | |
foreach ($line_items as &$line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (!isset($line_item_wrapper->commerce_bundle_group_id)) { | |
continue; | |
} | |
$group_unit_qty = 1; | |
if (isset($line_item_wrapper->commerce_bundle_unit_quantity)) { | |
$group_unit_qty = $line_item_wrapper->commerce_bundle_unit_quantity->value(); | |
// Skip if unit qty is set to 0. | |
if (!$group_unit_qty) { | |
continue; | |
} | |
} | |
$group_id = $line_item_wrapper->commerce_bundle_group_id->raw(); | |
$group_ids[] = $line_item_wrapper->commerce_bundle_group_id->raw(); | |
$group_element_key = 'product_group_' . $group_id; | |
// Build group form. | |
$groups_container[$group_element_key] = array( | |
'#type' => 'container', | |
'#attributes' => array('class' => array( | |
'commerce-bundle-product-group', | |
'commerce-bundle-product-group-' . $group_id, | |
)), | |
'#tree' => TRUE, | |
); | |
$group_form = &$groups_container[$group_element_key]; | |
// Initialize group form state. | |
if (!isset($form_state['product_groups'][$group_element_key])) { | |
$form_state['product_groups'][$group_element_key] = array(); | |
} | |
// Add group fields. | |
// TODO: add setting for group product view mode? | |
$group_form['header'] = array( | |
'#prefix' => '<div class="commerce-bundle-group-header">', | |
'#suffix' => '</div>', | |
'unit_quantity' => array( | |
'#prefix' => '<span class="commerce-bundle-group-quantity">', | |
'#markup' => t('@unit_quantity x', array('@unit_quantity' => $group_unit_qty)), | |
'#suffix' => '</span> ', | |
), | |
'title' => array( | |
'#prefix' => '<span class="commerce-bundle-group-title">', | |
'#suffix' => '</span>', | |
), | |
); | |
if ($group_title = $line_item_wrapper->commerce_bundle_group_id->title->value()) { | |
$group_form['header']['title']['#markup'] = check_plain($group_title); | |
} | |
else { | |
$group_form['header']['title']['#markup'] = t('Product'); | |
} | |
// Build attribute form. | |
$group_form = commerce_bundle_line_item_attribute_form($line_item, $group_form, $form_state); | |
// Update product to attributes default product. | |
if (!empty($form_state['product_groups'][$group_element_key]['default_product'])) { | |
$group_default_product = $form_state['product_groups'][$group_element_key]['default_product']; | |
$line_item_wrapper->commerce_product = $group_default_product; | |
// Find the bundle_item for the default product. | |
$default_bundle_item = commerce_bundle_item_get_product_item( | |
$line_item_wrapper->commerce_bundle_group_id->commerce_bundle_items->value(), | |
$group_default_product->product_id | |
); | |
// Set line item bundle item even if return is NULL. | |
$line_item_wrapper->commerce_bundle_item_id = $default_bundle_item; | |
} | |
// Update the bundle total price. Default currency code is the first line | |
// item in $form_state. | |
if (empty($currency_code)) { | |
$currency_code = $line_item_wrapper->commerce_unit_price->currency_code->value(); | |
} | |
$bundle_product_price = commerce_bundle_get_bundle_product_price($group_id, $line_item_wrapper->commerce_bundle_item_id->raw(), TRUE); | |
$form_state['bundle_total_price'] += $group_unit_qty * $bundle_product_price['amount']; | |
// Loop through the fields on the referenced product's type. | |
foreach (field_info_instances('commerce_product', $line_item_wrapper->commerce_product->type->value()) as $product_field_name => $product_field) { | |
$reference_view_mode = $context['view_mode']; | |
if (!isset($product_field['display'][$reference_view_mode])) { | |
$reference_view_mode = 'default'; | |
} | |
// Only prepare visible fields. | |
if (!isset($product_field['display'][$reference_view_mode]['type']) || $product_field['display'][$reference_view_mode]['type'] != 'hidden') { | |
// Add the product field to the entity's content array. | |
$content_key = 'product:' . $product_field_name; | |
$group_form[$content_key] = field_view_field('commerce_product', $line_item_wrapper->commerce_product->value(), $product_field_name, $reference_view_mode); | |
// Construct an array of classes that will be used to theme and | |
// target the rendered field for AJAX replacement. | |
$classes = array( | |
'commerce-product-field', | |
drupal_html_class('commerce-product-field-' . $product_field_name), | |
drupal_html_class('field-' . $product_field_name), | |
drupal_html_class(implode('-', array($context['entity_type'], $context['entity_id'], 'product', $product_field_name))), | |
); | |
// Add an extra class to distinguish empty product fields. | |
if (empty($group_form[$content_key])) { | |
$classes[] = 'commerce-product-field-empty'; | |
} | |
// Ensure the field's content array has a prefix and suffix key. | |
$group_form[$content_key] += array( | |
'#prefix' => '', | |
'#suffix' => '', | |
); | |
// Add the custom div before and after the prefix and suffix. | |
$group_form[$content_key]['#prefix'] = '<div class="' . implode(' ', $classes) . '">' . $group_form[$content_key]['#prefix']; | |
$group_form[$content_key]['#suffix'] .= '</div>'; | |
} | |
} | |
// Attach "extra fields" to the bundle representing all the extra fields | |
// currently attached to products. | |
foreach (field_info_extra_fields('commerce_product', $line_item_wrapper->commerce_product->type->value(), 'display') as $product_extra_field_name => $product_extra_field) { | |
$display = field_extra_fields_get_display('commerce_product', $line_item_wrapper->commerce_product->type->value(), $reference_view_mode); | |
// Only include extra fields that specify a theme function and that | |
// are visible on the current view mode. | |
if (!empty($product_extra_field['theme']) && | |
!empty($display[$product_extra_field_name]['visible'])) { | |
// Add the product extra field to the entity's content array. | |
$content_key = 'product:' . $product_extra_field_name; | |
$variables = array( | |
$product_extra_field_name => $line_item_wrapper->commerce_product->{$product_extra_field_name}->value(), | |
'label' => $product_extra_field['label'] . ':', | |
'product' => $line_item_wrapper->commerce_product->value(), | |
); | |
$group_form[$content_key] = array( | |
'#markup' => theme($product_extra_field['theme'], $variables), | |
'#attached' => array( | |
'css' => array(drupal_get_path('module', 'commerce_product') . '/theme/commerce_product.theme.css'), | |
), | |
); | |
// Construct an array of classes that will be used to theme and | |
// target the rendered field for AJAX replacement. | |
$classes = array( | |
'commerce-product-extra-field', | |
drupal_html_class('commerce-product-extra-field-' . $product_extra_field_name), | |
drupal_html_class(implode('-', array($context['entity_type'], $context['entity_id'], 'product', $product_extra_field_name))), | |
); | |
// Add an extra class to distinguish empty fields. | |
if (empty($group_form[$content_key])) { | |
$classes[] = 'commerce-product-extra-field-empty'; | |
} | |
// Ensure the extra field's content array has a prefix and suffix key. | |
$group_form[$content_key] += array( | |
'#prefix' => '', | |
'#suffix' => '', | |
); | |
// Add the custom div before and after the prefix and suffix. | |
$group_form[$content_key]['#prefix'] = '<div class="' . implode(' ', $classes) . '">' . $group_form[$content_key]['#prefix']; | |
$group_form[$content_key]['#suffix'] .= '</div>'; | |
} | |
} | |
} | |
unset($line_item, $group_form, $group_state); | |
// Process bundle configurations after default product is set. | |
$bundle_configs = commerce_bundle_line_item_get_bundle_configs($form_state['line_items']); | |
foreach ($bundle_configs as $bundle_config_id => $bundle_config_line_items) { | |
foreach ($bundle_config_line_items as $line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (isset($line_item_wrapper->commerce_bundle_config_id)) { | |
$line_item_wrapper->commerce_bundle_config_id = $bundle_config_id; | |
} | |
} | |
} | |
// Store the form ID as a class of the form to avoid the incrementing form ID | |
// from causing the AJAX refresh not to work. | |
$form['#attributes']['class'][] = drupal_html_class(commerce_bundle_add_to_cart_form_id($group_ids)); | |
// Render the quantity field as either a textfield if shown or a hidden | |
// field if not. | |
if ($show_quantity) { | |
$form['quantity'] = array( | |
'#type' => 'textfield', | |
'#title' => t('Quantity'), | |
'#default_value' => $default_quantity, | |
'#datatype' => 'integer', | |
'#size' => 5, | |
'#weight' => 45, | |
); | |
} | |
else { | |
$form['quantity'] = array( | |
'#type' => 'hidden', | |
'#value' => $default_quantity, | |
'#datatype' => 'integer', | |
'#weight' => 45, | |
); | |
} | |
// Add the total bundle price to the form. | |
$form['bundle_total_price'] = array( | |
'#prefix' => '<div class="total-bundle-price">', | |
'#markup' => t('@label', array('@label' => $context['bundle_total_price_label'])) . ' ' . commerce_currency_format($form_state['bundle_total_price'], $currency_code), | |
'#suffix' => '</div>', | |
); | |
$form['submit'] = array( | |
'#type' => 'submit', | |
'#value' => t('Add to cart'), | |
'#weight' => 50, | |
); | |
// Add the handlers manually since we're using hook_forms() to associate this | |
// form with form IDs based on the $product_ids. | |
$form['#validate'][] = 'commerce_bundle_add_to_cart_form_validate'; | |
$form['#submit'][] = 'commerce_bundle_add_to_cart_form_submit'; | |
return $form; | |
} | |
/** | |
* Form validate handler: validate the bundle and quantity to add to the cart. | |
*/ | |
function commerce_bundle_add_to_cart_form_validate($form, &$form_state) { | |
// Check to ensure the quantity is valid. | |
if (!is_numeric($form_state['values']['quantity']) || $form_state['values']['quantity'] <= 0) { | |
form_set_error('quantity', t('You must specify a valid quantity to add to the cart.')); | |
} | |
// If the custom data type attribute of the quantity element is integer, | |
// ensure we only accept whole number values. | |
if ($form['quantity']['#datatype'] == 'integer' && | |
(int) $form_state['values']['quantity'] != $form_state['values']['quantity']) { | |
form_set_error('quantity', t('You must specify a whole number for the quantity.')); | |
} | |
// Validate any line item fields that may have been included on the form. | |
if (!empty($form_state['line_items'])) { | |
foreach ($form_state['line_items'] as $line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (!isset($line_item_wrapper->commerce_bundle_group_id)) { | |
continue; | |
} | |
/** @todo: throw an error if we dont have a group? **/ | |
$group_id = $line_item_wrapper->commerce_bundle_group_id->raw(); | |
if (empty($form['product_groups']['product_group_' . $group_id]) || | |
empty($form_state['values']['product_group_' . $group_id])) { | |
continue; | |
} | |
$group_form = $form['product_groups']['product_group_' . $group_id]; | |
$group_form_state_values = &$form_state['values']['product_group_' . $group_id]; | |
// If the attributes matching product selector was used, set the value of the | |
// product_id field to match; this will be fixed on rebuild when the actual | |
// default product will be selected based on the product selector value. | |
if (!empty($group_form_state_values['attributes']['product_select'])) { | |
form_set_value($group_form['product_id'], $group_form_state_values['attributes']['product_select'], $form_state); | |
} | |
/** @todo: pass in nest form state? or does this figure it out ***/ | |
if (!empty($group_form['line_item_fields'])) { | |
field_attach_form_validate('commerce_line_item', $line_item, $group_form['line_item_fields'], $form_state); | |
} | |
} | |
} | |
} | |
/** | |
* Form submit handler: add the selected bundle to the cart. | |
*/ | |
function commerce_bundle_add_to_cart_form_submit($form, &$form_state) { | |
if (empty($form_state['line_items'])) { | |
return; | |
} | |
$add_to_cart_deltas = array(); | |
$bundle_quantity = isset($form_state['values']['quantity']) ? $form_state['values']['quantity'] : 1; | |
foreach ($form_state['line_items'] as $state_line_item_delta => &$state_line_item) { | |
// If the line item passed to the function is new... | |
if (empty($state_line_item->line_item_id)) { | |
$state_line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $state_line_item); | |
if (!isset($state_line_item_wrapper->commerce_bundle_id) || | |
!isset($state_line_item_wrapper->commerce_bundle_group_id) || | |
!isset($state_line_item_wrapper->commerce_bundle_item_id)) { | |
continue; | |
} | |
$group_id = $state_line_item_wrapper->commerce_bundle_group_id->raw(); | |
if (empty($form_state['values']['product_group_' . $group_id])) { | |
continue; | |
} | |
$group_form_state_values = &$form_state['values']['product_group_' . $group_id]; | |
$group_form = $form['product_groups']['product_group_' . $group_id]; | |
$product_id = $group_form_state_values['product_id']; | |
$product = commerce_product_load($product_id); | |
$bundler_wrapper = $state_line_item_wrapper->commerce_bundle_id; | |
$group_wrapper = $state_line_item_wrapper->commerce_bundle_group_id; | |
$bundle_item_wrapper = $state_line_item_wrapper->commerce_bundle_item_id; | |
// Create the new bundle product line item of the same type. | |
$line_item = commerce_bundle_product_line_item_new($product, | |
$bundle_item_wrapper->value(), | |
$group_wrapper->value(), | |
$bundler_wrapper->value(), | |
$bundle_quantity, | |
0, | |
$state_line_item->data, | |
$state_line_item->type | |
); | |
// Allow modules to prepare this as necessary. This hook is defined by the | |
// Product Pricing module. | |
drupal_alter('commerce_product_calculate_sell_price_line_item', $line_item); | |
// Remove line item field values the user didn't have access to modify. | |
if (!empty($group_form_state_values['line_item_fields'])) { | |
foreach ($group_form_state_values['line_item_fields'] as $field_name => $value) { | |
// Note that we're checking the Commerce Cart settings that we inserted | |
// into this form element array back when we built the form. This means a | |
// module wanting to alter a line item field widget to be available must | |
// update both its form element's #access value and the field_access value | |
// of the #commerce_cart_settings array. | |
if (empty($form['line_item_fields'][$field_name]['#commerce_cart_settings']['field_access'])) { | |
unset($group_form_state_values['line_item_fields'][$field_name]); | |
} | |
} | |
} | |
// Unset the line item field values array if it is now empty. | |
if (empty($group_form_state_values['line_item_fields'])) { | |
unset($group_form_state_values['line_item_fields']); | |
} | |
// Add field data to the line item. | |
if (!empty($group_form['line_item_fields'])) { | |
field_attach_submit('commerce_line_item', $line_item, $group_form['line_item_fields'], $form_state); | |
} | |
// Process the unit price through Rules so it reflects the user's actual | |
// purchase price. | |
rules_invoke_event('commerce_product_calculate_sell_price', $line_item); | |
// Only attempt an Add to Cart if the line item has a valid unit price. | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (!is_null($line_item_wrapper->commerce_unit_price->value())) { | |
// Add the product to the specified shopping cart. | |
$state_line_item = $line_item; | |
$add_to_cart_deltas[] = $state_line_item_delta; | |
} | |
else { | |
drupal_set_message(t('%title could not be added to your cart.', array('%title' => entity_label($bundler_wrapper->type(), $bundler_wrapper->value()))), 'error'); | |
return; | |
} | |
} | |
} | |
unset($state_line_item); | |
// Set bundle configuration. | |
$line_item_configs_updated = array(); | |
$bundle_configs = commerce_bundle_line_item_get_bundle_configs($form_state['line_items']); | |
foreach ($bundle_configs as $bundle_config_id => $bundle_config_line_items) { | |
foreach ($bundle_config_line_items as $line_item) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (isset($line_item_wrapper->commerce_bundle_config_id)) { | |
$line_item_wrapper->commerce_bundle_config_id = $bundle_config_id; | |
if (isset($line_item->line_item_id)) { | |
$line_item_configs_updated[] = $line_item->line_item_id; | |
} | |
} | |
} | |
} | |
// Save line items. | |
foreach ($form_state['line_items'] as $delta => $line_item) { | |
if (in_array($delta, $add_to_cart_deltas)) { | |
// Add new line items to the cart. | |
$form_state['line_items'][$delta] = commerce_cart_product_add( | |
$form_state['values']['uid'], | |
$line_item, | |
isset($line_item->data['context']['add_to_cart_combine']) ? $line_item->data['context']['add_to_cart_combine'] : TRUE | |
); | |
} | |
elseif (isset($line_item->line_item_id) && | |
in_array($line_item->line_item_id, $line_item_configs_updated)) { | |
// Update existing line items. | |
commerce_line_item_save($line_item); | |
// Clear the line item cache so that updates show on next load.. | |
entity_get_controller('commerce_line_item')->resetCache(array($line_item->line_item_id)); | |
} | |
} | |
} | |
/** | |
* Returns the attribute form for a given bundle line item. | |
* | |
* @param $line_item | |
* The bundle line item. | |
* @param $form | |
* The form array. | |
* @param $form_state | |
* The form state. | |
* | |
* @return | |
* An attribute form array | |
*/ | |
function commerce_bundle_line_item_attribute_form($line_item, $form, &$form_state) { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
if (!isset($line_item_wrapper->commerce_bundle_group_id)) { | |
return $form; | |
} | |
$group_wrapper = $line_item_wrapper->commerce_bundle_group_id; | |
if (!isset($group_wrapper->commerce_bundle_items)) { | |
return $form; | |
} | |
$group_id = $line_item_wrapper->commerce_bundle_group_id->raw(); | |
$group_element_key = 'product_group_' . $group_id; | |
$group_state_storage = &$form_state['product_groups'][$group_element_key]; | |
$group_state_values = &$form_state['values'][$group_element_key]; | |
$group_product_ids = array(); | |
foreach ($group_wrapper->commerce_bundle_items as $bundle_item_wrapper) { | |
// Skip disabled bundle items. | |
if (!$bundle_item_wrapper->status->value()) { | |
continue; | |
} | |
if (isset($bundle_item_wrapper->commerce_bundle_product) && | |
($bundle_item_product_id = $bundle_item_wrapper->commerce_bundle_product->raw())) { | |
$group_product_ids[$bundle_item_product_id] = $bundle_item_product_id; | |
} | |
} | |
if (empty($group_product_ids)) { | |
return $form; | |
} | |
// We've gathered all the product id's for the current group of interest. | |
// Load all the active products intended for sale on this form. | |
$products = commerce_product_load_multiple($group_product_ids, array('status' => 1)); | |
// If no products were returned... | |
if (count($products) == 0) { | |
$form['submit'] = array( | |
'#type' => 'submit', | |
'#value' => t('Product not available'), | |
'#weight' => 15, | |
// Do not set #disabled in order not to prevent submission. | |
'#attributes' => array('disabled' => 'disabled'), | |
'#validate' => array('commerce_cart_add_to_cart_form_disabled_validate'), | |
); | |
} | |
else { | |
// If the form is for a single product and displaying attributes on a single | |
// product Add to Cart form is disabled in the form context, store the | |
// product_id in a hidden form field for use by the submit handler. | |
if (count($products) == 1 && empty($line_item->data['context']['show_single_product_attributes'])) { | |
$group_state_storage['default_product'] = reset($products); | |
$form['product_id'] = array( | |
'#type' => 'hidden', | |
'#value' => key($products), | |
); | |
} | |
else { | |
// However, if more than one products are represented on it, attempt to | |
// use smart select boxes for the product selection. If the products are | |
// all of the same type and there are qualifying fields on that product | |
// type, display their options for customer selection. | |
$qualifying_fields = array(); | |
$same_type = TRUE; | |
$type = ''; | |
// Find the default product so we know how to set default options on the | |
// various Add to Cart form widgets and an array of any matching product | |
// based on attribute selections so we can add a selection widget. | |
$matching_products = array(); | |
$default_product = NULL; | |
$attribute_names = array(); | |
$unchanged_attributes = array(); | |
foreach ($products as $product_id => $product) { | |
$product_wrapper = entity_metadata_wrapper('commerce_product', $product); | |
// Store the first product type. | |
if (empty($type)) { | |
$type = $product->type; | |
} | |
// If the current product type is different from the first, we are not | |
// dealing with a set of same typed products. | |
if ($product->type != $type) { | |
$same_type = FALSE; | |
} | |
// If the form state contains a set of attribute data, use it to try | |
// and determine the default product. | |
$changed_attribute = NULL; | |
if (!empty($group_state_values['attributes'])) { | |
$match = TRUE; | |
// Set an array of checked attributes for later comparison against the | |
// default matching product. | |
if (empty($attribute_names)) { | |
$attribute_names = (array) array_diff_key($group_state_values['attributes'], array('product_select' => '')); | |
$unchanged_attributes = $group_state_values['unchanged_attributes']; | |
} | |
foreach ($attribute_names as $key => $value) { | |
// If this is the attribute widget that was changed... | |
if ($value != $unchanged_attributes[$key]) { | |
// Store the field name. | |
$changed_attribute = $key; | |
// Clear the input for the "Select a product" widget now if it | |
// exists on the form since we know an attribute was changed. | |
unset($form_state['input'][$group_element_key]['attributes']['product_select']); | |
} | |
// If a field name has been stored and we've moved past it to | |
// compare the next attribute field... | |
if (!empty($changed_attribute) && $changed_attribute != $key) { | |
// Wipe subsequent values from the form state so the attribute | |
// widgets can use the default values from the new default product. | |
unset($form_state['input'][$group_element_key]['attributes'][$key]); | |
// Don't accept this as a matching product. | |
continue; | |
} | |
if ($product_wrapper->{$key}->raw() != $value) { | |
$match = FALSE; | |
} | |
} | |
// If the changed field name has already been stored, only accept the | |
// first matching product by ignoring the rest that would match. An | |
// exception is granted for additional matching products that share | |
// the exact same attribute values as the first. | |
if ($match && !empty($changed_attribute) && !empty($matching_products)) { | |
reset($matching_products); | |
$matching_product = $matching_products[key($matching_products)]; | |
$matching_product_wrapper = entity_metadata_wrapper('commerce_product', $matching_product); | |
foreach ($attribute_names as $key => $value) { | |
if ($product_wrapper->{$key}->raw() != $matching_product_wrapper->{$key}->raw()) { | |
$match = FALSE; | |
} | |
} | |
} | |
if ($match) { | |
$matching_products[$product_id] = $product; | |
} | |
} | |
} | |
// Set the default product now if it isn't already set. | |
if (empty($matching_products)) { | |
// If a product ID value was passed in, use that product if it exists. | |
if (!empty($group_state_values['product_id']) && | |
!empty($products[$group_state_values['product_id']])) { | |
$default_product = $products[$group_state_values['product_id']]; | |
} | |
elseif (empty($form_state['values']) && | |
!empty($line_item_wrapper->commerce_product) && | |
!empty($products[$line_item_wrapper->commerce_product->raw()])) { | |
// If this is the first time the form is built, attempt to use the | |
// product specified by the line item. | |
$default_product = $products[$line_item_wrapper->commerce_product->raw()]; | |
} | |
else { | |
reset($products); | |
$default_product = $products[key($products)]; | |
} | |
} | |
else { | |
// If the product selector has a value, use that. | |
if (!empty($group_state_values['attributes']['product_select']) && | |
!empty($products[$group_state_values['attributes']['product_select']]) && | |
in_array($products[$group_state_values['attributes']['product_select']], $matching_products)) { | |
$default_product = $products[$group_state_values['attributes']['product_select']]; | |
} | |
else { | |
reset($matching_products); | |
$default_product = $matching_products[key($matching_products)]; | |
} | |
} | |
// Wrap the default product for later use. | |
$default_product_wrapper = entity_metadata_wrapper('commerce_product', $default_product); | |
$group_state_storage['default_product'] = $default_product; | |
// If all the products are of the same type... | |
if ($same_type) { | |
// Loop through all the field instances on that product type. | |
foreach (field_info_instances('commerce_product', $type) as $field_name => $instance) { | |
// A field qualifies if it is single value, required and uses a widget | |
// with a definite set of options. For the sake of simplicity, this is | |
// currently restricted to fields defined by the options module. | |
$field = field_info_field($field_name); | |
// If the instance is of a field type that is eligible to function as | |
// a product attribute field and if its attribute field settings | |
// specify that this functionality is enabled... | |
if (commerce_cart_field_attribute_eligible($field) && commerce_cart_field_instance_is_attribute($instance)) { | |
// Get the options properties from the options module for the | |
// attribute widget type selected for the field, defaulting to the | |
// select list options properties. | |
$commerce_cart_settings = commerce_cart_field_instance_attribute_settings($instance); | |
switch ($commerce_cart_settings['attribute_widget']) { | |
case 'checkbox': | |
$widget_type = 'onoff'; | |
break; | |
case 'radios': | |
$widget_type = 'buttons'; | |
break; | |
default: | |
$widget_type = 'select'; | |
} | |
$properties = _options_properties($widget_type, FALSE, TRUE, TRUE); | |
// Try to fetch localized names. | |
$allowed_values = NULL; | |
// Prepare translated options if using the i18n_field module. | |
if (module_exists('i18n_field')) { | |
if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) { | |
$allowed_values = $translate($field); | |
_options_prepare_options($allowed_values, $properties); | |
} | |
// Translate the field title if set. | |
if (!empty($instance['label'])) { | |
$instance['label'] = i18n_field_translate_property($instance, 'label'); | |
} | |
} | |
// Otherwise just use the base language values. | |
if (empty($allowed_values)) { | |
$allowed_values = _options_get_options($field, $instance, $properties, 'commerce_product', $default_product); | |
} | |
// Only consider this field a qualifying attribute field if we could | |
// derive a set of options for it. | |
if (!empty($allowed_values)) { | |
$qualifying_fields[$field_name] = array( | |
'field' => $field, | |
'instance' => $instance, | |
'commerce_cart_settings' => $commerce_cart_settings, | |
'options' => $allowed_values, | |
'weight' => $instance['widget']['weight'], | |
'required' => $instance['required'], | |
); | |
} | |
} | |
} | |
} | |
// Otherwise for products of varying types, display a simple select list | |
// by product title. | |
if (!empty($qualifying_fields)) { | |
$used_options = array(); | |
$field_has_options = array(); | |
// Sort the fields by weight. | |
uasort($qualifying_fields, 'drupal_sort_weight'); | |
foreach ($qualifying_fields as $field_name => $data) { | |
// Build an options array of widget options used by referenced products. | |
foreach ($products as $product_id => $product) { | |
$product_wrapper = entity_metadata_wrapper('commerce_product', $product); | |
// Only add options to the present array that appear on products that | |
// match the default value of the previously added attribute widgets. | |
foreach ($used_options as $used_field_name => $unused) { | |
// Don't apply this check for the current field being evaluated. | |
if ($used_field_name == $field_name) { | |
continue; | |
} | |
if (isset($form['attributes'][$used_field_name]['#default_value'])) { | |
if ($product_wrapper->{$used_field_name}->raw() != $form['attributes'][$used_field_name]['#default_value']) { | |
continue 2; | |
} | |
} | |
} | |
// With our hard dependency on widgets provided by the Options | |
// module, we can make assumptions about where the data is stored. | |
if ($product_wrapper->{$field_name}->raw() != NULL) { | |
$field_has_options[$field_name] = TRUE; | |
} | |
$used_options[$field_name][] = $product_wrapper->{$field_name}->raw(); | |
} | |
// If for some reason no options for this field are used, remove it | |
// from the qualifying fields array. | |
if (empty($field_has_options[$field_name]) || empty($used_options[$field_name])) { | |
unset($qualifying_fields[$field_name]); | |
} | |
else { | |
$form['attributes'][$field_name] = array( | |
'#type' => $data['commerce_cart_settings']['attribute_widget'], | |
'#title' => commerce_cart_attribute_widget_title($data['instance']), | |
'#options' => array_intersect_key($data['options'], drupal_map_assoc($used_options[$field_name])), | |
'#default_value' => $default_product_wrapper->{$field_name}->raw(), | |
'#weight' => $data['instance']['widget']['weight'], | |
'#ajax' => array( | |
'callback' => 'commerce_bundle_add_to_cart_form_attributes_refresh', | |
), | |
); | |
// Add the empty value if the field is not required and products on | |
// the form include the empty value. | |
if (!$data['required'] && in_array('', $used_options[$field_name])) { | |
$form['attributes'][$field_name]['#empty_value'] = ''; | |
} | |
$form['unchanged_attributes'][$field_name] = array( | |
'#type' => 'value', | |
'#value' => $default_product_wrapper->{$field_name}->raw(), | |
); | |
} | |
} | |
if (!empty($form['attributes'])) { | |
$form['attributes'] += array( | |
'#tree' => 'TRUE', | |
'#prefix' => '<div class="attribute-widgets">', | |
'#suffix' => '</div>', | |
'#weight' => 0, | |
); | |
$form['unchanged_attributes'] += array( | |
'#tree' => 'TRUE', | |
); | |
// If the matching products array is empty, it means this is the first | |
// time the form is being built. We should populate it now with | |
// products that match the default attribute options. | |
if (empty($matching_products)) { | |
foreach ($products as $product_id => $product) { | |
$product_wrapper = entity_metadata_wrapper('commerce_product', $product); | |
$match = TRUE; | |
foreach (element_children($form['attributes']) as $field_name) { | |
if ($product_wrapper->{$field_name}->raw() != $form['attributes'][$field_name]['#default_value']) { | |
$match = FALSE; | |
} | |
} | |
if ($match) { | |
$matching_products[$product_id] = $product; | |
} | |
} | |
} | |
// If there were more than one matching products for the current | |
// attribute selection, add a product selection widget. | |
if (count($matching_products) > 1) { | |
$options = array(); | |
foreach ($matching_products as $product_id => $product) { | |
$options[$product_id] = $product->title; | |
} | |
// Note that this element by default is a select list, so its | |
// #options are not sanitized here. Sanitization will occur in a | |
// check_plain() in the function form_select_options(). If you alter | |
// this element to another #type, such as 'radios', you are also | |
// responsible for looping over its #options array and sanitizing | |
// the values. | |
$form['attributes']['product_select'] = array( | |
'#type' => 'select', | |
'#title' => t('Select a product'), | |
'#options' => $options, | |
'#default_value' => $default_product->product_id, | |
'#weight' => 40, | |
'#ajax' => array( | |
'callback' => 'commerce_bundle_add_to_cart_form_attributes_refresh', | |
), | |
); | |
} | |
$form['product_id'] = array( | |
'#type' => 'hidden', | |
'#value' => $default_product->product_id, | |
); | |
} | |
} | |
// If the products referenced were of different types or did not posess | |
// any qualifying attribute fields... | |
if (!$same_type || empty($qualifying_fields)) { | |
// For a single product form, just add the hidden product_id field. | |
if (count($products) == 1) { | |
$form['product_id'] = array( | |
'#type' => 'hidden', | |
'#value' => $default_product->product_id, | |
); | |
} | |
else { | |
// Otherwise add a product selection widget. | |
$options = array(); | |
foreach ($products as $product_id => $product) { | |
$options[$product_id] = $product->title; | |
} | |
// Note that this element by default is a select list, so its #options | |
// are not sanitized here. Sanitization will occur in a check_plain() in | |
// the function form_select_options(). If you alter this element to | |
// another #type, such as 'radios', you are also responsible for looping | |
// over its #options array and sanitizing the values. | |
$form['product_id'] = array( | |
'#type' => 'select', | |
'#options' => $options, | |
'#default_value' => $default_product->product_id, | |
'#weight' => 0, | |
'#ajax' => array( | |
'callback' => 'commerce_bundle_add_to_cart_form_attributes_refresh', | |
), | |
); | |
} | |
} | |
} | |
} | |
return $form; | |
} | |
/** | |
* Ajax callback: returns AJAX commands when an attribute widget is changed. | |
*/ | |
function commerce_bundle_add_to_cart_form_attributes_refresh($form, $form_state) { | |
$commands = array(); | |
// Render the form afresh to capture any changes to the available widgets | |
// based on the latest selection. | |
$commands[] = ajax_command_replace('.' . drupal_html_class($form['#form_id']), drupal_render($form)); | |
// Allow other modules to add arbitrary AJAX commands on the refresh. | |
drupal_alter('commerce_bundle_add_to_cart_form_attributes_refresh', $commands, $form, $form_state); | |
return array('#type' => 'ajax', '#commands' => $commands); | |
} | |
/** | |
* Implements hook_commerce_cart_line_item_refresh(). | |
* | |
* Ensures the proper price for a bundle line item is set according to bundles | |
* pricing precedence (group, bundle item, product). | |
*/ | |
function commerce_bundle_commerce_cart_line_item_refresh($line_item, $order_wrapper) { | |
if ($line_item->type == 'commerce_bundle_line_item') { | |
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); | |
commerce_bundle_product_line_item_populate($line_item_wrapper->value(), | |
$line_item_wrapper->commerce_bundle_id->value(), | |
$line_item_wrapper->commerce_bundle_group_id->value(), | |
$line_item_wrapper->commerce_bundle_item_id->value(), | |
$line_item_wrapper->commerce_product->value()); | |
// Process the unit price through Rules so it reflects the user's actual | |
// current purchase price. | |
rules_invoke_event('commerce_product_calculate_sell_price', $line_item); | |
} | |
} | |
/** | |
* Implements hook_commerce_cart_product_comparison_properties_alter(). | |
*/ | |
function commerce_bundle_commerce_cart_product_comparison_properties_alter(&$comparison_properties, $line_item_clone) { | |
// Force separate line items based on generated bundle config id. | |
$comparison_properties[] = 'commerce_bundle_config_id'; | |
} | |
/** | |
* Returns the correct price for a bundle product based on pricing precedence. | |
* | |
* @param $group_id | |
* The grouping products product id. | |
* @param $bundle_item_id | |
* The bundle item's id. | |
* @return array $bundle_unit_price | |
* A price field data array. | |
*/ | |
function commerce_bundle_get_bundle_product_price($group_id, $bundle_item_id, $apply_discounts = FALSE) { | |
$group_product = commerce_product_load($group_id); | |
$group_wrapper = entity_metadata_wrapper('commerce_product', $group_product); | |
$bundle_item = entity_load('commerce_bundle_item', array($bundle_item_id)); | |
$bundle_item_wrapper = entity_metadata_wrapper('commerce_bundle_item', current($bundle_item)); | |
// Calculate the bundle unit price on the line item. | |
if ($group_wrapper->commerce_bundle_group_price->value()) { | |
$bundle_unit_price = commerce_price_wrapper_value($group_wrapper, 'commerce_bundle_group_price', TRUE); | |
} | |
elseif ($bundle_item_wrapper->commerce_bundle_price->value()) { | |
$bundle_unit_price = commerce_price_wrapper_value($bundle_item_wulesapper, 'commerce_bundle_price', TRUE); | |
} | |
elseif ($apply_discounts) { | |
$p = commerce_product_load($bundle_item_wrapper->commerce_bundle_product->product_id->raw()); | |
$bundle_unit_price = commerce_product_calculate_sell_price($p); | |
} | |
else { | |
$bundle_unit_price = commerce_price_wrapper_value($bundle_item_wrapper->commerce_bundle_product, 'commerce_price', TRUE); | |
} | |
return $bundle_unit_price; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment