You are here

commerce_avatax.calc.inc in Drupal Commerce Connector for AvaTax 7.5

AvaTax calculation/requests functions.

File

includes/commerce_avatax.calc.inc
View source
<?php

/**
 * @file
 * AvaTax calculation/requests functions.
 */

/**
 * Prepares the transaction request array to be sent to AvaTax.
 *
 * @param EntityDrupalWrapper $order_wrapper
 *    The wrapped order entity.
 * @param string $transaction_type
 *   The transaction type (e.g SalesOrder|SalesInvoice).
 * @param bool $commit
 *   Boolean indicating whether or not to commit the transaction.
 *
 * @return array|bool
 *   Returns the request body array to be sent, FALSE in case of failure.
 */
function commerce_avatax_create_transaction($order_wrapper, $transaction_type = 'SalesOrder', $commit = FALSE) {

  // Return FALSE in case there are no line items.
  if ($order_wrapper->commerce_line_items
    ->count() === 0) {
    return FALSE;
  }
  $company_code = commerce_avatax_company_code();

  // Prepare the Request Body.
  $request_body = array(
    'type' => $transaction_type,
    'companyCode' => $company_code,
    'date' => format_date(REQUEST_TIME, 'custom', 'c'),
    'code' => 'DC-' . $order_wrapper
      ->getIdentifier(),
    'customerCode' => _commerce_avatax_transaction_get_customer_code($order_wrapper),
    'currencyCode' => $order_wrapper->commerce_order_total->currency_code
      ->value(),
    'addresses' => array(
      'shipFrom' => _commerce_avatax_transaction_get_ship_from(),
      'shipTo' => _commerce_avatax_transaction_get_ship_to($order_wrapper),
    ),
  );
  if (!empty($commit)) {
    $request_body['commit'] = TRUE;
  }

  // For non anonymous orders, send the exemption code if it exists.
  if ($order_wrapper->uid
    ->value() > 0) {

    // Check the Exemptions status.
    if (variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'exemptions_status', FALSE)) {
      if (isset($order_wrapper->owner->{COMMERCE_AVATAX_EXEMPTION_CODE_FIELD})) {
        $exemption_code = $order_wrapper->owner->{COMMERCE_AVATAX_EXEMPTION_CODE_FIELD}
          ->value();
        if (!empty($exemption_code)) {
          $request_body['customerUsageType'] = $exemption_code;
        }
      }
    }

    // Check if the user has a VAT ID field.
    if (variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'add_vat_field', FALSE)) {
      if (isset($order_wrapper->owner->{COMMERCE_AVATAX_VAT_ID_FIELD})) {
        $vat_id = $order_wrapper->owner->{COMMERCE_AVATAX_VAT_ID_FIELD}
          ->value();
        if (!empty($vat_id)) {
          $request_body['businessIdentificationNo'] = $vat_id;
        }
      }
    }
  }
  $tax_included = FALSE;

  // If the ship from country is not in the US|CA, assume the price entered
  // is "VAT-inclusive" if specified in the settings.
  if (isset($request_body['addresses']['shipFrom']['country'])) {
    if (!in_array($request_body['addresses']['shipFrom']['country'], array(
      'CA',
      'US',
    ))) {
      $tax_included = variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'vat_inclusive', TRUE);
    }
  }
  _commerce_avatax_transaction_add_lines($request_body, $order_wrapper->commerce_line_items, $tax_included);
  $order = $order_wrapper
    ->value();
  drupal_alter('commerce_avatax_create_transaction', $request_body, $order);
  return $request_body;
}

/**
 * Returns the transaction "lines" that needs to be sent to the API.
 *
 * @param array $request_body
 *   The request body that needs to be altered.
 * @param array $line_items
 *   An array of line items wrapper that need to be added to the transaction.
 * @param bool $tax_included
 *   Boolean indicating whether or not the tax is included.
 */
function _commerce_avatax_transaction_add_lines(&$request_body, $line_items, $tax_included = FALSE) {
  $lines = array();
  $tax_included = $tax_included ? 'true' : 'false';
  $discounted_line_item_types = array();

  // Commerce discount 2.x integration.
  if (module_exists('commerce_discount')) {
    $discounts = array();
    $discount_line_items = array();

    // Search for discounts.
    foreach ($line_items as $line_item_wrapper) {

      // Skip non discount line items.
      if ($line_item_wrapper->type
        ->value() != 'commerce_discount' || !$line_item_wrapper
        ->value()) {
        continue;
      }
      $line_item = $line_item_wrapper
        ->value();
      $discount_line_items[] = $line_item_wrapper;
      if (isset($line_item->data['discount_name'])) {
        $discounts[] = $line_item->data['discount_name'];
      }
    }

    // We need to load the discounts to see if "% offer types" are applied to
    // this order.
    if ($discounts) {

      // In case we have only have a "% discount offer type", we need to
      // properly tell AvaTax which line items are discounted.
      $discount_offer_types = array();

      // Load the discounts to see if we have any "%offer types".
      if ($discounts = entity_load_multiple_by_name('commerce_discount', $discounts)) {
        foreach ($discounts as $discount) {
          if (empty($discount->commerce_discount_offer)) {
            continue;
          }
          $discount_wrapper = entity_metadata_wrapper('commerce_discount', $discount);
          $offer_type = $discount_wrapper->commerce_discount_offer->type
            ->value();
          $discount_offer_types[$offer_type] = $offer_type;
        }
      }

      // In case of percentage discount, retrieve the line item types the
      // discount were applied to.
      if (isset($discount_offer_types['percentage']) && count($discount_offer_types) === 1) {
        $discounted_line_item_types = variable_get('commerce_discount_line_item_types', array_diff(commerce_product_line_item_types(), array(
          'product_discount',
        )));
      }

      // Now calculate the actual discount amount.
      if ($discount_line_items) {
        $discount_total = commerce_line_items_total($discount_line_items);
        if ($discount_total['amount'] < 0) {
          $request_body['discount'] = commerce_currency_amount_to_decimal($discount_total['amount'], $discount_total['currency_code']) * -1;
        }
      }
    }
  }

  // If we found discounts, and we weren't able to specify which
  // line item types are discounted, assume all are.
  if (!empty($request_body['discount']) && !$discounted_line_item_types) {
    $line_item_types = commerce_line_item_types();
    $discounted_line_item_types = array_keys($line_item_types);
  }
  $discounted_line_item_types = array_filter($discounted_line_item_types);

  // Loop over the line items passed.
  foreach ($line_items as $delta => $line_item_wrapper) {

    // Ensure the line item still exists.
    if (!$line_item_wrapper
      ->value()) {
      continue;
    }
    $line_item = $line_item_wrapper
      ->value();
    $discounted = 'false';

    // Check if this line item type is discounted.
    if (!empty($request_body['discount']) && in_array($line_item->type, $discounted_line_item_types)) {
      $discounted = 'true';
    }

    // Handles products.
    if (in_array($line_item->type, commerce_product_line_item_types())) {
      $tax_code = '';

      // Get the tax code from the "Tax code" term referenced by the product.
      if (isset($line_item_wrapper->commerce_product->commerce_avatax_code)) {
        if ($line_item_wrapper->commerce_product->commerce_avatax_code
          ->value()) {
          $tax_code = $line_item_wrapper->commerce_product->commerce_avatax_code->name
            ->value();
        }
      }
      $lines[] = array(
        'id' => $line_item->line_item_id,
        'number' => $delta + 1,
        'itemCode' => $line_item_wrapper->commerce_product->sku
          ->value(),
        'description' => $line_item_wrapper->commerce_product->title
          ->value(),
        'taxCode' => $tax_code,
        'quantity' => $line_item->quantity,
        'amount' => $line_item_wrapper->commerce_total->amount_decimal
          ->value(),
        // The discounted boolean needs to be set to TRUE, otherwise, discount
        // document level won't be applied.
        'discounted' => $discounted,
        'taxIncluded' => $tax_included,
      );
    }
    elseif ($line_item->type === 'shipping') {
      $lines[] = array(
        'id' => $line_item->line_item_id,
        'number' => $delta + 1,
        'itemCode' => 'Shipping',
        'description' => 'Shipping',
        // Retrieve the configured Shipping tax code.
        'taxCode' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'shipcode', 'FR020100'),
        'quantity' => $line_item->quantity,
        'amount' => $line_item_wrapper->commerce_total->amount_decimal
          ->value(),
        // Shipping shouldn't be discounted.
        'discounted' => 'false',
      );
    }
  }
  if ($lines) {
    $request_body['lines'] = $lines;
  }
}

/**
 * Helper function used to determine the customerCode sent.
 */
function _commerce_avatax_transaction_get_customer_code($order_wrapper) {

  // Get User name or e-mail address.
  if ($order_wrapper->uid
    ->value() === 0) {
    $mail = $order_wrapper->mail
      ->value();
    if ($mail == '') {
      $customer_code = 'administrator';
    }
    else {
      $user_email = $mail;
      $customer_code = commerce_avatax_email_to_username($user_email);
    }
  }
  else {
    $customer_code = $order_wrapper->owner->name
      ->value();
  }
  return $customer_code;
}

/**
 * Returns the shipFrom address for a transaction.
 */
function _commerce_avatax_transaction_get_ship_from() {
  return array(
    'line1' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_street1', ''),
    'line2' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_street2', ''),
    'city' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_city', ''),
    'region' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_state', ''),
    'country' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_country', ''),
    'postalCode' => variable_get(COMMERCE_AVATAX_VAR_PREFIX . 'primary_zip', ''),
  );
}

/**
 * Returns the shipTo address for a transaction.
 */
function _commerce_avatax_transaction_get_ship_to($order_wrapper) {
  $ship_to = array();
  $customer_profile_field = commerce_avatax_get_customer_profile_field();

  // Retrieve the address from the configured customer profile type.
  if (!empty($customer_profile_field) && isset($order_wrapper->{$customer_profile_field})) {
    if (isset($order_wrapper->{$customer_profile_field}->commerce_customer_address)) {
      $address = $order_wrapper->{$customer_profile_field}->commerce_customer_address
        ->value();

      // Prepare the Ships from address.
      $ship_to = array(
        'line1' => $address['thoroughfare'],
        'line2' => $address['premise'],
        'city' => $address['locality'],
        'region' => $address['administrative_area'],
        'country' => $address['country'],
        'postalCode' => $address['postal_code'],
      );
    }
  }
  return $ship_to;
}

Functions

Namesort descending Description
commerce_avatax_create_transaction Prepares the transaction request array to be sent to AvaTax.
_commerce_avatax_transaction_add_lines Returns the transaction "lines" that needs to be sent to the API.
_commerce_avatax_transaction_get_customer_code Helper function used to determine the customerCode sent.
_commerce_avatax_transaction_get_ship_from Returns the shipFrom address for a transaction.
_commerce_avatax_transaction_get_ship_to Returns the shipTo address for a transaction.