array( 'label' => t('Price table'), 'description' => t('This field stores multiple prices for products based ib the quantity selected.'), 'settings' => array(), 'instance_settings' => array(), 'default_widget' => 'commerce_price_table_multiple', 'default_formatter' => 'commerce_multiprice_default', 'property_type' => 'commerce_price_table', 'property_callbacks' => array('commerce_price_table_property_info_callback'), ), ); } /** * Implements hook_field_widget_info(). */ function commerce_price_table_field_widget_info() { return array( 'commerce_price_table_multiple' => array( 'label' => t('Price table'), 'field types' => array('commerce_price_table'), 'settings' => array( 'currency_code' => 'default', ), ), ); } /** * Implements hook_field_formatter_info(). */ function commerce_price_table_field_formatter_info() { return array( 'commerce_multiprice_default' => array( 'label' => t('Price chart'), 'field types' => array('commerce_price_table'), 'settings' => array( 'calculation' => FALSE, 'price_label' => t('Price'), 'quantity_label' => t('Quantity'), ), ), ); } /** * Implements hook_field_widget_form(). */ function commerce_price_table_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { // Use the default currency if the setting is not present. if (empty($instance['widget']['settings']['currency_code']) || $instance['widget']['settings']['currency_code'] == 'default') { $default_currency_code = NULL; } else { $default_currency_code = $instance['widget']['settings']['currency_code']; } // If a price has already been set for this instance prepare default values. if (isset($items[$delta]['amount'])) { $currency = commerce_currency_load($items[$delta]['currency_code']); // Round the default value. $default_amount = commerce_currency_amount_to_decimal($items[$delta]['amount'], $currency['code']); // Run it through number_format() to add the decimal places in if necessary. if (strpos($default_amount, '.') === FALSE || strpos($default_amount, '.') > strlen($default_amount) - $currency['decimals']) { $default_amount = number_format($default_amount, $currency['decimals'], '.', ''); } $default_currency_code = $items[$delta]['currency_code']; } else { $default_amount = NULL; } // Load the default currency for this instance. $default_currency = commerce_currency_load($default_currency_code); $element['#attached']['css'][] = drupal_get_path('module', 'commerce_price_table') . '/theme/commerce_price_table.css'; if ($instance['widget']['type'] == 'commerce_price_table_multiple') { $element['amount'] = array( '#type' => 'textfield', '#title' => $element['#title'], '#description' => t('Amount for the price.'), '#default_value' => $default_amount, '#size' => 10, '#field_suffix' => $default_currency['code'], ); $element['currency_code'] = array( '#type' => 'value', '#default_value' => $default_currency['code'], ); $element['min_qty'] = array( '#type' => 'textfield', '#title' => t('Min Qty'), '#description' => t('Minimum quantity for the price range.'), '#default_value' => isset($items[$delta]['min_qty']) ? $items[$delta]['min_qty'] : 0, '#size' => 10, ); $element['max_qty'] = array( '#type' => 'textfield', '#title' => t('Max Qty'), '#description' => t('Maximum quantity for the price range. Use "-1" for unlimited quantity.'), '#default_value' => isset($items[$delta]['max_qty']) ? $items[$delta]['max_qty'] : 0, '#size' => 10, ); } $element['data'] = array( '#type' => 'value', '#default_value' => !empty($items[$delta]['data']) ? $items[$delta]['data'] : array('components' => array()), ); return $element; } /** * Implements hook_field_presave(). */ function commerce_price_table_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { // Convert amounts to integers and serialize data arrays before saving. foreach ($items as $delta => $item) { // Convert the price amount to an integer based on the currency. $items[$delta]['amount'] = commerce_currency_decimal_to_amount( $items[$delta]['amount'], $items[$delta]['currency_code'] ); // Serialize an existing data array. if (isset($item['data']) && is_array($item['data'])) { $items[$delta]['data'] = serialize($item['data']); } if (empty($item['min_qty'])) { $items[$delta]['min_qty'] = 0; } if (empty($item['max_qty'])) { $items[$delta]['max_qty'] = 0; } } } /** * Implements hook_field_load(). */ function commerce_price_table_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { // Convert amounts to their floating point values and deserialize data arrays. foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { // Unserialize the data array if necessary. if (!empty($items[$id][$delta]['data'])) { $items[$id][$delta]['data'] = unserialize($items[$id][$delta]['data']); } else { $items[$id][$delta]['data'] = array('components' => array()); } } } } /** * Implements hook_field_validate(). */ function commerce_price_table_field_validate($entity_type, $entity, $field, $instance, $langcode, &$items, &$errors) { // Ensure only numeric values are entered in price fields. foreach ($items as $delta => $item) { if (!empty($item['amount']) && !is_numeric($item['amount'])) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => 'price_numeric', 'message' => t('%name: you must enter a numeric value for the price.', array('%name' => check_plain($instance['label']))), ); } // @TODO this is not displaying the message but the error id. if ($item['max_qty'] < $item['min_qty'] && $item['max_qty'] <> -1) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => 'price_table_quantity', 'message' => t('%name: Max quantity needs to be higher than min quantity.', array('%name' => check_plain($instance['label']))), ); } // @TODO Add extra validations, as no repeating qty and always force to have quantity for 1?. } } /** * Implementation of hook_field_is_empty(). */ function commerce_price_table_field_is_empty($item, $field) { return !isset($item['amount']) || (string) $item['amount'] == ''; } /** * Callback to alter the property info of price fields. * * @see commerce_price_field_info(). */ function commerce_price_table_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { $name = $field['field_name']; $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'commerce_price_table'; $property['getter callback'] = 'entity_metadata_field_verbatim_get'; $property['setter callback'] = 'entity_metadata_field_verbatim_set'; $property['auto creation'] = 'commerce_price_field_data_auto_creation'; $property['property info'] = commerce_price_table_field_data_property_info(); unset($property['query callback']); } /** * Implements hook_field_instance_settings_form(). */ function commerce_price_table_field_instance_settings_form($field, $instance) { $settings = $instance['settings']; $form['commerce_price_table'] = array( '#type' => 'fieldset', '#title' => t('Commerce price table settings'), ); $form['commerce_price_table']['hide_default_price'] = array( '#type' => 'checkbox', '#title' => t('Hide default price'), '#description' => t('Activate this checkbox to hide the default price and display the price table instead.'), '#default_value' => isset($settings['commerce_price_table']['hide_default_price']) ? $settings['commerce_price_table']['hide_default_price'] : NULL, ); return $form; } /** * Implements hook_field_formatter_settings_form(). */ function commerce_price_table_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $element = array(); if ($display['type'] == 'commerce_multiprice_default') { $element['price_label'] = array( '#type' => 'textfield', '#title' => t('Price label for the price table'), '#default_value' => isset($settings['price_label']) ? $settings['price_label'] : t('Price'), ); $element['quantity_label'] = array( '#type' => 'textfield', '#title' => t('Quantity label for the price table'), '#default_value' => isset($settings['quantity_label']) ? $settings['quantity_label'] : t('Quantity'), ); } return $element; } /** * Implements hook_field_formatter_settings_summary(). */ function commerce_price_table_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $summary = array(); if ($display['type'] == 'commerce_multiprice_default') { $summary = array( t('Quantity label: !quantity_label', array('!quantity_label' => isset($settings['quantity_label']) ? $settings['quantity_label'] : t('Quantity'))), t('Price label: !price_label', array('!price_label' => isset($settings['price_label']) ? $settings['price_label'] : t('Price'))), ); } return implode('
', $summary); } /** * Defines info for the properties of the Price field data structure. */ function commerce_price_table_field_data_property_info($name = NULL) { return array( 'amount' => array( 'label' => t('Amount'), 'description' => !empty($name) ? t('Amount value of field %name', array('%name' => $name)) : '', 'type' => 'decimal', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', ), 'currency_code' => array( 'label' => t('Currency'), 'description' => !empty($name) ? t('Currency code of field %name', array('%name' => $name)) : '', 'type' => 'text', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', 'options list' => 'commerce_currency_code_options_list', ), 'min_qty' => array( 'label' => t('Min Qty'), 'description' => !empty($name) ? t('Min quantity value of field %name', array('%name' => $name)) : '', 'type' => 'integer', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', ), 'max_qty' => array( 'label' => t('Max Qty'), 'description' => !empty($name) ? t('Max quantity value of field %name', array('%name' => $name)) : '', 'type' => 'integer', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', ), 'data' => array( 'label' => t('Data'), 'description' => !empty($name) ? t('Data array of field %name', array('%name' => $name)) : '', 'type' => 'struct', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', ), ); } /** * Implements hook_theme(). */ function commerce_price_table_theme() { return array( 'commerce_multiprice_default' => array( 'variables' => array('items' => array()), ), ); } /** * Implements hook_field_formatter_view(). */ function commerce_price_table_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { if ($display['type'] == 'commerce_multiprice_default') { $header = array(isset($display['settings']['quantity_label']) ? $display['settings']['quantity_label'] : t('Quantity')); $row = array(isset($display['settings']['price_label']) ? $display['settings']['price_label'] : t('Price')); foreach ($items as $delta => $item) { if (isset($item['min_qty']) && $item['max_qty'] && $item['amount']) { $header[] = commerce_price_table_display_quantity_headers($item); $line_item = commerce_product_line_item_new($entity, $item['min_qty']); $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item); $line_item_wrapper->commerce_unit_price->amount = $item['amount']; // Empty the price components to recalculate them. $line_item->commerce_unit_price[LANGUAGE_NONE][0]['data']['components'] = array(); $price = array( 'amount' => $item['amount'], 'currency_code' => $item['currency_code'], 'data' => array(), ); // Alter the base price to the current one. $line_item_wrapper->commerce_unit_price->data = commerce_price_component_add( $line_item_wrapper->commerce_unit_price->value(), 'base_price', $price, TRUE ); // Invoke the calculation rule event. rules_invoke_event('commerce_product_calculate_sell_price', $line_item); $row[] = array('data' => commerce_currency_format($line_item_wrapper->commerce_unit_price->amount->value(), $line_item_wrapper->commerce_unit_price->currency_code->value(), $entity)); } } } $element[] = array( '#markup' => theme('table', array('header' => $header, 'rows' => array($row))), ); return $element; } /** * Get the price for the min qty possible in a product. */ function commerce_price_table_get_amount_qty($product, $quantity = 1, $price_table_name) { $items = array(); $product_wrapper = entity_metadata_wrapper('commerce_product', $product); $fields = commerce_info_fields('commerce_price_table', 'commerce_product'); foreach ($fields as $field) { if (!empty($product->{$field['field_name']})) { foreach ($product_wrapper->{$field['field_name']}->value() as $item) { //if the field name equals what is designated in the rule, add it to the items array if ($field['field_name'] == $price_table_name) { $items[] = $item; } } } } // Sort the items by quantity and return the matching one. uasort($items, 'commerce_price_table_sort_by_qty'); foreach ($items as $item) { if ($quantity <= $item['max_qty'] && $quantity >= $item['min_qty']) { return $item; } } // Handle the unlimited qty. foreach ($items as $item) { if ($item['max_qty'] == -1) { return $item; } } // We fallback to the higher one if no match was found. return end($items); } /** * Sort the price fields by quantity. */ function commerce_price_table_sort_by_qty($a, $b) { $a_qty = (is_array($a) && isset($a['min_qty'])) ? $a['min_qty'] : 0; $b_qty = (is_array($b) && isset($b['min_qty'])) ? $b['min_qty'] : 0; if ($a_qty == $b_qty) { return 0; } return ($a_qty < $b_qty) ? -1 : 1; } /** * Return the settings of all the price table field of a bundle. */ function commerce_price_table_get_field_instance_settings($entity_type = 'commerce_product', $bundle = 'product') { $settings = array(); $fields = commerce_info_fields('commerce_price_table', $entity_type); if (isset($fields) && is_array($fields)) { foreach ($fields as $field) { $settings[] = field_info_instance($entity_type, $field['field_name'], $bundle); } } return $settings; } /** * Implements hook_form_FORM_ID_alter(). */ function commerce_price_table_form_commerce_product_ui_product_form_alter(&$form, &$form_state, $form_id) { $access = TRUE; foreach (commerce_price_table_get_field_instance_settings($form['#entity_type'], $form['#bundle']) as $setting) { if (isset($setting['settings']['commerce_price_table']['hide_default_price']) && $setting['settings']['commerce_price_table']['hide_default_price'] == TRUE) { $access = FALSE; } } $form['commerce_price']['#access'] = $access; } /** * Implements hook_node_view_alter(). */ function commerce_price_table_node_view_alter(&$build) { // Check if there is a product reference field in the node. $found_product_reference = FALSE; $fields = commerce_info_fields('commerce_product_reference', $build['#entity_type']); foreach ($fields as $field) { if (isset($build[$field['field_name']])) { $found_product_reference = TRUE; } } // If there is one, we check if the setting for hiding the default price is // enabled. if ($found_product_reference) { $access = TRUE; foreach (commerce_price_table_get_field_instance_settings() as $setting) { if (isset($setting['settings']['commerce_price_table']['hide_default_price']) && $setting['settings']['commerce_price_table']['hide_default_price'] == TRUE) { $access = FALSE; } } $build['product:commerce_price']['#access'] = $access; } } /** * Helper function that takes care of the quantity displayed in the headers of * the price table. */ function commerce_price_table_display_quantity_headers($item) { // Set the quantity text to unlimited if it's -1. $max_qty = $item['max_qty'] == -1 ? t('Unlimited') : $item['max_qty']; // If max and min qtys are the same, only show one. if ($item['min_qty'] == $max_qty) { $quantity_text = $item['min_qty']; } else { $quantity_text = $item['min_qty'] . ' - ' . $max_qty; } return $quantity_text; }