You are here

commerce_reports_tax.module in Commerce Reporting 7.4

Module file for Commerce Reports Tax.

File

modules/tax/commerce_reports_tax.module
View source
<?php

/**
 * @file
 * Module file for Commerce Reports Tax.
 */

/**
 * Implements hook_help().
 */
function commerce_reports_tax_help($path, $arg) {
  switch ($path) {
    case 'admin/commerce/reports/tax':
      return '<p>' . t('Here you can see reports about the tax gathered by your store.') . '</p>';
  }
}

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

/**
 * Implements hook_menu().
 */
function commerce_reports_tax_menu() {
  $items = array();
  $items['admin/commerce/config/tax-reports'] = array(
    'title' => 'Tax reports settings',
    'description' => 'Configure tax reports settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'commerce_reports_tax_form',
    ),
    'access arguments' => array(
      'configure commerce tax reports',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'commerce_reports_tax.admin.inc',
  );
  $items['admin/commerce/config/tax-reports/generate'] = array(
    'title' => 'Generate tax report',
    'page callback' => 'commerce_reports_tax_generate_callback',
    'access arguments' => array(
      'configure commerce tax reports',
    ),
    'type' => MENU_CALLBACK,
    'file' => 'commerce_reports_tax.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function commerce_reports_tax_menu_alter(&$items) {
  $items['admin/commerce/reports/tax']['title'] = 'Tax';
  $items['admin/commerce/reports/tax']['type'] = MENU_LOCAL_TASK;
  $items['admin/commerce/reports/tax']['weight'] = 50;
  if (empty($items['admin/commerce/reports'])) {
    $items['admin/commerce/reports'] = array(
      'title' => 'Reports',
      'description' => 'View reports for your store.',
      'access arguments' => array(
        'access commerce tax reports',
      ),
      'page callback' => 'drupal_goto',
      'page arguments' => array(
        'admin/commerce/reports/tax',
      ),
      'weight' => 50,
    );
  }
}

/**
 * Implements hook_permission().
 */
function commerce_reports_tax_permission() {
  return array(
    'access commerce tax reports' => array(
      'title' => t('Access commerce tax reports'),
    ),
    'configure commerce tax reports' => array(
      'title' => t('Configure commerce tax reports'),
    ),
  );
}

/**
 * Implements hook_commerce_reports_dashboard().
 */
function commerce_reports_tax_commerce_reports_dashboard() {
  return array(
    'commerce_reports_tax' => array(
      'title' => t('Taxes'),
      'type' => COMMERCE_REPORTS_DASHBOARD_BROAD_BLOCK,
      'report' => array(
        'title' => t('Tax reports'),
        'path' => 'admin/commerce/reports/tax',
      ),
      'sections' => array(
        'default' => array(
          'module' => 'commerce_reports_tax',
          'block' => 'commerce_reports_tax',
        ),
      ),
      'weight' => 5,
    ),
  );
}

/**
 * Implements hook_commerce_reports_dashboard_alter().
 */
function commerce_reports_tax_commerce_reports_dashboard_alter(&$info) {
  $info['customer_statistics']['weight'] = 10;
  $info['sales_statistics']['weight'] = 300;
  $info['billing_information']['type'] = COMMERCE_REPORTS_DASHBOARD_ROW;
}

/**
 * Implements hook_block_info().
 *
 * Defines the custom blocks defined by commerce_reports_tax.
 */
function commerce_reports_tax_block_info() {
  return array(
    'commerce_reports_tax' => array(
      'info' => t('Commerce Tax Reports'),
    ),
  );
}

/**
 * Implements hook_block_view().
 *
 * Implementation of the dashboard block used in commerce_reports.
 */
function commerce_reports_tax_block_view($delta = '') {
  module_load_include('inc', 'commerce_reports_tax', 'commerce_reports_tax.admin');
  switch ($delta) {
    case 'commerce_reports_tax':
      return commerce_reports_tax_table();
  }
}

/**
 * Implements hook_entity_insert().
 */
function commerce_reports_tax_entity_insert($entity, $type) {
  if ($type == 'commerce_order' && in_array($entity->status, variable_get('commerce_reports_tax_order_statuses', array(
    'pending',
    'processing',
    'completed',
  )))) {
    commerce_reports_tax_order_save($entity);
  }
}

/**
 * Implements hook_entity_update().
 */
function commerce_reports_tax_entity_update($entity, $type) {
  if ($type == 'commerce_order' && isset($entity->original)) {

    // Mark original entity for easy reference.
    $original = $entity->original;

    // Check the updated status against statuses in settings.
    $order_current_status = in_array($entity->status, variable_get('commerce_reports_tax_order_statuses', array(
      'pending',
      'processing',
      'completed',
    )));

    // Check the original status, so we know if we need to remove last record.
    $order_previous_status = in_array($original->status, variable_get('commerce_reports_tax_order_statuses', array(
      'pending',
      'processing',
      'completed',
    )));

    // The order changed to a order status we're interested in, but did not
    // previously have a watched status. Create a new record.
    if ($order_current_status && !$order_previous_status) {
      commerce_reports_tax_order_save($entity);
    }
    elseif (!$order_current_status && $order_previous_status) {
      commerce_reports_tax_order_delete($entity->order_id);
    }
    elseif ($order_current_status && $order_previous_status && $original->commerce_order_total != $entity->commerce_order_total) {
      commerce_reports_tax_order_save($entity);
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function commerce_reports_tax_entity_delete($entity, $type) {
  if ($type == 'commerce_order') {
    commerce_reports_tax_order_delete($entity->order_id);
  }
}

/**
 * Returns the order statuses that should be taken into account.
 */
function commerce_reports_tax_order_statuses() {
  return variable_get('commerce_reports_tax_order_generate_statuses', array(
    'pending',
    'completed',
  ));
}

/**
 * Generate historic tax information.
 */
function commerce_reports_tax_generate() {
  $batch = array(
    'title' => t('Generating tax report'),
    'operations' => array(
      array(
        '_commerce_reports_tax_generate',
        array(),
      ),
    ),
    'finished' => '_commerce_reports_tax_finished',
    'file' => drupal_get_path('module', 'commerce_reports_tax') . '/commerce_reports_tax.batch.inc',
  );
  batch_set($batch);
}

/**
 * Deletes the tax information of multiple orders.
 */
function commerce_reports_tax_order_delete_multiple($order_ids = array()) {
  return db_query("DELETE FROM {commerce_reports_tax} WHERE order_id IN (:order_ids)", array(
    ':order_ids' => $order_ids,
  ));
}

/**
 * Deletes the tax information of an order.
 */
function commerce_reports_tax_order_delete($order_id) {
  return commerce_reports_tax_order_delete_multiple(array(
    $order_id,
  ));
}

/**
 * Returns all reported taxes.
 */
function commerce_reports_tax($start, $end) {
  $results = db_query("SELECT crt.tax_rate AS name, ctr.display_title AS title, crt.currency_code, SUM(crt.taxable) AS taxable, SUM(crt.taxed) AS taxed FROM {commerce_reports_tax} crt LEFT JOIN {commerce_tax_rate} ctr ON crt.tax_rate = ctr.name LEFT JOIN {commerce_order} co ON crt.order_id = co.order_id WHERE co.created BETWEEN :start AND :end GROUP BY crt.tax_rate, crt.currency_code", array(
    ':start' => $start,
    ':end' => $end,
  ));
  $rows = array();
  while ($result = $results
    ->fetchAssoc()) {
    $rows[$result['name']] = array(
      $result['title'],
      commerce_currency_format($result['taxable'], $result['currency_code']),
      commerce_currency_format($result['taxed'], $result['currency_code']),
    );
  }
  return $rows;
}

/**
 * Stores the tax information of an order.
 *
 * If the order has already been processed in the past it will compare the
 * information in the database with the generated information and update
 * accordingly.
 */
function commerce_reports_tax_order_save($order) {

  /** @var EntityDrupalWrapper $wrapper */
  $wrapper = entity_metadata_wrapper('commerce_order', $order);
  $order_id = $wrapper->order_id
    ->value();

  // Loop through all line items and get taxes from taxable line items.
  $applied_taxes = array();
  foreach ($wrapper->commerce_line_items as $line_item_wrapper) {
    if ($tax_rates = _commerce_reports_tax_get_tax_rates($line_item_wrapper)) {

      // Taxable.
      $taxable = $line_item_wrapper->commerce_total
        ->value();

      // Taxed.
      foreach ($tax_rates as $tax_rate) {
        $taxed = commerce_price_component_total($taxable, $tax_rate['price_component']);
        $id = $tax_rate['name'] . $order_id . $taxed['currency_code'];

        // Create fresh record if it doesn't exist.
        if (empty($applied_taxes[$id])) {
          $applied_taxes[$id] = new stdClass();
          $applied_taxes[$id]->tax_rate = $tax_rate['name'];
          $applied_taxes[$id]->order_id = $order_id;
          $applied_taxes[$id]->currency_code = $taxed['currency_code'];
          $applied_taxes[$id]->taxable = 0;
          $applied_taxes[$id]->taxed = 0;
        }

        // Increase the amounts.
        $applied_taxes[$id]->taxable += $taxable['amount'];
        $applied_taxes[$id]->taxed += round($taxed['amount']);
      }
    }
    else {

      // TODO: Keep track of amount where taxes were not applied?
    }
  }

  // Check to see if save records exist before saving new record.
  $result = db_query("SELECT tax_rate, order_id, currency_code, taxable, taxed FROM {commerce_reports_tax} WHERE order_id = :order_id", array(
    ':order_id' => $order->order_id,
  ));

  // If we have records, update the existing, else add new.
  if ($result
    ->rowCount() > 0) {

    // Update record.
    while ($row = $result
      ->fetchAssoc()) {
      $tax_record_id = $row['tax_rate'] . $row['order_id'] . $row['currency_code'];
      drupal_write_record('commerce_reports_tax', $applied_taxes[$tax_record_id], array(
        'tax_rate',
        'order_id',
        'currency_code',
      ));
    }
  }
  else {

    // Insert any of the remaining applied taxes.
    foreach ($applied_taxes as $tax) {
      drupal_write_record('commerce_reports_tax', $tax);
    }
  }
}

/**
 * Helper function for getting applied tax rate from line item wrapper.
 *
 * @param EntityDrupalWrapper $line_item_wrapper
 *   The wrapped line item entity.
 *
 * @return array
 *   An array set of tax rates from given line item.
 */
function _commerce_reports_tax_get_tax_rates(EntityDrupalWrapper $line_item_wrapper) {
  $commerce_total_data = $line_item_wrapper->commerce_total->data
    ->value();
  $tax_rates = array();
  foreach ($commerce_total_data['components'] as $component) {
    if (!empty($component['price']['data']['tax_rate']['name'])) {
      $tax_rates[$component['price']['data']['tax_rate']['name']] = commerce_tax_rate_load($component['price']['data']['tax_rate']['name']);
    }
  }
  return $tax_rates;
}

/**
 * Returns the order states to report on.
 *
 * @return array
 *    Array of order states for Views to filter on.
 */
function commerce_reports_tax_reportable_order_states() {
  if (function_exists('commerce_reports_reportable_order_states')) {
    return commerce_reports_reportable_order_states();
  }
  $cache =& drupal_static(__FUNCTION__, array());
  if (empty($cache)) {
    $report_states = array(
      'pending',
      'completed',
    );

    // Validate states.
    $order_states = commerce_order_states();
    foreach ($report_states as $state) {
      if (!in_array($state, $order_states)) {
        unset($report_states[$state]);
      }
    }

    // Use array_combine to match View's 'value' => 'value',
    $cache = array_combine($report_states, $report_states);
  }
  return $cache;
}