You are here

commerce_price_decimals_formatter.module in Commerce Price Decimals Formatter 7

Provides a display formatter for the price field in which you can specify the decimal places are displayed.

File

commerce_price_decimals_formatter.module
View source
<?php

/**
 * @file
 * Provides a display formatter for the price field in which you can
 * specify the decimal places are displayed.
 */

/**
 * Implements hook_field_formatter_info().
 */
function commerce_price_decimals_formatter_field_formatter_info() {
  return array(
    'commerce_price_decimals_formatter' => array(
      'label' => t('Formatted amount with n decimals'),
      'field types' => array(
        'commerce_price',
      ),
      'settings' => array(
        'calculation' => 'calculated_sell_price',
        'currencies' => commerce_price_decimals_formatter_get_default_currencies_settings(),
      ),
    ),
    'commerce_price_decimals_formatter_components' => array(
      'label' => t('Formatted amount with n decimals and components'),
      'field types' => array(
        'commerce_price',
      ),
      'settings' => array(
        'calculation' => 'calculated_sell_price',
        'currencies' => commerce_price_decimals_formatter_get_default_currencies_settings(),
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function commerce_price_decimals_formatter_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'] + field_info_formatter_settings($display['type']);
  $form = array();

  // Add the default calculation type selection option.
  if ($display['type'] != 'commerce_price_decimals_formatter_components') {
    $form = commerce_price_field_formatter_settings_form($field, $instance, $view_mode, $form, $form_state);
  }

  // Add the price decimals settings form.
  $form = array_merge($form, commerce_price_decimals_formatter_get_settings_form($settings));
  return $form;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function commerce_price_decimals_formatter_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'] + field_info_formatter_settings($display['type']);
  $summary = array();

  // Add the default calculation text to the summary.
  if ($display['type'] != 'commerce_price_decimals_formatter_components') {
    $summary[] = commerce_price_field_formatter_settings_summary($field, $instance, $view_mode);
  }

  // Add the currency settings.
  $summary = array_merge($summary, commerce_price_decimals_formatter_get_settings_summary($settings));
  return implode('<br />', $summary);
}

/**
 * Implements hook_field_formatter_prepare_view().
 *
 * Rely on the default implementation of prices.
 *
 * @see commerce_price_field_formatter_prepare_view
 */
function commerce_price_decimals_formatter_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {

  // Allow other modules to prepare the item values prior to formatting.
  foreach (module_implements('commerce_price_field_formatter_prepare_view') as $module) {
    $function = $module . '_commerce_price_field_formatter_prepare_view';
    $function($entity_type, $entities, $field, $instances, $langcode, $items, $displays);
  }
}

/**
 * Implements hook_field_formatter_view().
 */
function commerce_price_decimals_formatter_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'] + field_info_formatter_settings($display['type']);
  $settings['currencies'] += commerce_price_decimals_formatter_get_default_currencies_settings();

  // Loop through each price value in this field.
  foreach ($items as $delta => $item) {

    // Do not render a price if the amount is NULL (i.e. non-zero empty value).
    if (is_null($item['amount'])) {

      // TODO: Consider if we should render as N/A or something indicating a
      // price was not available as opposed to just leaving a blank.
      continue;
    }
    switch ($display['type']) {
      case 'commerce_price_decimals_formatter':
        $element[] = array(
          '#markup' => commerce_price_decimals_formatter_currency_format($item['amount'], $item['currency_code'], $settings, $entity),
        );
        break;
      case 'commerce_price_decimals_formatter_components':

        // Build an array of component display titles and their prices.
        $components = array();
        $weight = 0;
        foreach ($item['data']['components'] as $key => $component) {
          $component_type = commerce_price_component_type_load($component['name']);

          // Hook in with the Commerce Discount module
          if (isset($component['price']['data']['discount_component_title'])) {
            $component_type['display_title'] = $component['price']['data']['discount_component_title'];
          }
          if (empty($components[$component['name']])) {
            $components[$component['name']] = array(
              'title' => check_plain($component_type['display_title']),
              'price' => commerce_price_component_total($item, $component['name']),
              'weight' => $component_type['weight'],
            );
            $weight = max($weight, $component_type['weight']);
          }
        }

        // If there is only a single component and its price equals the field's,
        // then remove it and just show the actual price amount.
        if (count($components) == 1 && in_array('base_price', array_keys($components))) {
          $components = array();
        }

        // If the i18n_field module is available, we'll use it to translate
        // user-configurable field labels.
        if (module_exists('i18n_field') && !empty($instance['label'])) {
          $instance['label'] = i18n_field_translate_property($instance, 'label');
        }

        // Add the actual field value to the array.
        $components['commerce_price_formatted_amount'] = array(
          'title' => check_plain($instance['label']),
          'price' => $item,
          'weight' => $weight + 1,
        );

        // Sort the components by weight.
        uasort($components, 'drupal_sort_weight');

        // Format the prices for display.
        foreach ($components as $key => &$component) {
          $component['formatted_price'] = commerce_price_decimals_formatter_currency_format($component['price']['amount'], $component['price']['currency_code'], $settings, $entity);
        }
        $element[$delta] = array(
          '#markup' => theme('commerce_price_formatted_components', array(
            'components' => $components,
            'price' => $item,
          )),
        );
        break;
    }
  }
  return $element;
}

/**
 * Returns an array of all available enable currencies and decimals.
 */
function commerce_price_decimals_formatter_get_default_currencies_settings() {
  $enable_currencies = commerce_currencies(TRUE);
  $currencies = array();
  foreach ($enable_currencies as $code => $currency) {
    $currencies[$code] = array(
      'decimals' => $currency['decimals'],
      'force' => FALSE,
      'zero' => FALSE,
    );
  }
  return $currencies;
}

/**
 * Returns an array of all available settings.
 *
 * @param array $settings
 *   Array with display settings.
 *
 * @return array
 *   An array of currency arrays keyed with the settings.
 */
function commerce_price_decimals_formatter_get_settings_form($settings) {

  // Load defaults settings and append it.
  $defaults = commerce_price_decimals_formatter_get_default_currencies_settings();
  $settings['currencies'] += $defaults;
  $form = array();
  $form['currencies'] = array(
    '#type' => 'container',
    '#tree' => TRUE,
  );
  foreach ($settings['currencies'] as $code => $currency) {
    if (array_key_exists($code, $defaults)) {
      $form['currencies'][$code] = array(
        '#type' => 'fieldset',
        '#title' => t('Settings for @code', array(
          '@code' => $code,
        )),
        '#collapsible' => FALSE,
      );
      $form['currencies'][$code]['decimals'] = array(
        '#type' => 'textfield',
        '#title' => t('Number of decimals to show'),
        '#default_value' => $settings['currencies'][$code]['decimals'],
        '#required' => TRUE,
        '#element_validate' => array(
          'element_validate_integer',
        ),
        '#size' => 3,
      );
      $form['currencies'][$code]['force'] = array(
        '#type' => 'checkbox',
        '#title' => t('Force price decimals'),
        '#default_value' => $settings['currencies'][$code]['force'],
      );
      $form['currencies'][$code]['zero'] = array(
        '#type' => 'checkbox',
        '#title' => t('Sets zero decimal places when possible'),
        '#default_value' => $settings['currencies'][$code]['zero'],
      );
    }
  }
  return $form;
}

/**
 * Returns an array of all available settings.
 *
 * @param array $settings
 *   Array with display settings.
 *
 * @return array
 *   An array of currency settings summary.
 */
function commerce_price_decimals_formatter_get_settings_summary($settings) {

  // Load defaults settings and append it.
  $defaults = commerce_price_decimals_formatter_get_default_currencies_settings();
  $settings['currencies'] += $defaults;
  $summary = array();
  foreach ($settings['currencies'] as $code => $currency) {
    if (array_key_exists($code, $defaults)) {
      $summary[] = t('Decimals for @code: @force @number.', array(
        '@code' => $code,
        '@number' => $currency['decimals'],
        '@force' => $currency['force'] ? t('Force to') : t('Without force to'),
      ));
      if ($currency['zero']) {
        $summary[] = t('Sets zero decimal places when possible');
      }
    }
  }
  return $summary;
}

/**
 * Formats a price for a particular currency.
 *
 * @param number $amount
 *   A numeric price amount value.
 * @param string $currency_code
 *   The three character code of the currency.
 * @param array $settings
 *   Array with display settings.
 * @param object $object
 *   When present, the object to which the price is attached.
 * @param boolean $convert
 *   Boolean indicating whether or not the amount needs to be converted to a
 *   decimal price amount when formatting.
 *
 * @return string
 *   A fully formatted currency.
 */
function commerce_price_decimals_formatter_currency_format($amount, $currency_code, $settings, $object = NULL, $convert = TRUE) {

  // First load the currency array.
  $currency = commerce_currency_load($currency_code);

  // Then convert the price amount to the currency's major unit decimal value.
  if ($convert == TRUE) {
    $amount = commerce_currency_amount_to_decimal($amount, $currency_code);
  }

  // Invoke the custom format callback if specified.
  if (!empty($currency['format_callback'])) {
    return $currency['format_callback']($amount, $currency, $object);
  }

  // Check the number of decimals.
  if (isset($settings['currencies'][$currency_code]['force']) && $settings['currencies'][$currency_code]['force'] == FALSE && $settings['currencies'][$currency_code]['decimals'] < $currency['decimals']) {
    $decimals = strlen(substr(strrchr($amount, "."), 1));
    if ($decimals < $settings['currencies'][$currency_code]['decimals']) {
      $decimals = $settings['currencies'][$currency_code]['decimals'];
    }
  }
  else {
    $decimals = $settings['currencies'][$currency_code]['decimals'];
  }
  if (isset($settings['currencies'][$currency_code]['zero']) && $settings['currencies'][$currency_code]['zero'] == TRUE) {
    $zeros = (int) substr(strrchr($amount, "."), 1);
    if ($zeros == 0) {
      $decimals = 0;
    }
  }

  // Alter currency decimals for a correct round.
  $currency['decimals'] = $decimals;

  // Format the price as a number.
  $price = number_format(commerce_currency_round(abs($amount), $currency), $currency['decimals'], $currency['decimal_separator'], $currency['thousands_separator']);

  // Establish the replacement values to format this price for its currency.
  $replacements = array(
    '@code_before' => $currency['code_placement'] == 'before' ? $currency['code'] : '',
    '@symbol_before' => $currency['symbol_placement'] == 'before' ? $currency['symbol'] : '',
    '@price' => $price,
    '@symbol_after' => $currency['symbol_placement'] == 'after' ? $currency['symbol'] : '',
    '@code_after' => $currency['code_placement'] == 'after' ? $currency['code'] : '',
    '@negative' => $amount < 0 ? '-' : '',
    '@symbol_spacer' => $currency['symbol_spacer'],
    '@code_spacer' => $currency['code_spacer'],
  );
  return trim(t('@code_before@code_spacer@negative@symbol_before@price@symbol_spacer@symbol_after@code_spacer@code_after', $replacements));
}

/**
 * Implements hook_views_api().
 */
function commerce_price_decimals_formatter_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'commerce_price_decimals_formatter') . '/includes/views',
  );
}

/**
 * Implements hook_preprocess_views_view().
 *
 * When the line item summary and order total area handlers are present on Views
 * forms, it is natural for them to appear above the submit buttons that Views
 * creates for the form. This hook checks for their existence in the footer area
 * and moves them if necessary.
 */
function commerce_price_decimals_formatter_preprocess_views_view(&$vars) {
  $view = $vars['view'];

  // Determine if the line item summary or order total area handler is present
  // on the View.
  $has_handler = FALSE;
  foreach ($view->footer as $area) {
    if ($area instanceof commerce_price_decimals_formatter_handler_area_line_item_summary_decimals || $area instanceof commerce_price_decimals_formatter_handler_area_order_total_decimals) {
      $has_handler = TRUE;
    }
  }

  // If one of the handlers is present and the View in question is a form...
  if ($has_handler && views_view_has_form_elements($view)) {

    // Move the footer area into a row in the View positioned just above the
    // form's submit buttons.
    $vars['rows']['footer'] = array(
      '#type' => 'markup',
      '#markup' => $vars['footer'],
      '#weight' => 99,
    );
    $vars['footer'] = '';
  }
}