commerce_avatax.module in Drupal Commerce Connector for AvaTax 7.3
Same filename and directory in other branches
Calculate Sales Tax using AvaTax Calc service from Avalara, Inc.
Copyright (C) adTumbler, Inc, adTumbler.com
File
commerce_avatax.moduleView source
<?php
/**
* @file
* Calculate Sales Tax using AvaTax Calc service from Avalara, Inc.
*
* Copyright (C) adTumbler, Inc, adTumbler.com
*
*/
/**
* Conditional includes for AvaTax Calc configuration options.
*/
if (variable_get('commerce_avatax_product_version', '') == 'basic') {
require_once 'commerce_avatax_basic.inc';
}
if (variable_get('commerce_avatax_product_version', '') == 'pro') {
require_once 'commerce_avatax_pro.inc';
}
if (variable_get('commerce_avatax_erp_status', '')) {
require_once 'commerce_avatax_erp.inc';
}
/**
* Implements hook_libraries_info().
*/
function commerce_avatax_libraries_info() {
$libraries = array();
$libraries['avalara_avatax'] = array(
'name' => 'Avalara Avatax',
'vendor url' => 'http://avalara.com/',
'download url' => 'http://developer.avalara.com/',
'version' => array(
'file' => 'classes/ATConfig.class.php',
'pattern' => '/@copyright .+ - (\\d+) Avalara, Inc. All rights reserved.',
),
'files' => array(
'php' => array(
'ava_tax.php',
'credentials.php',
),
),
);
return $libraries;
}
/**
* Implements hook_permission().
*/
function commerce_avatax_permission() {
return array(
'administer avatax' => array(
'title' => t('Administer AvaTax'),
'description' => t('Allows users to configure AvaTax'),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_commerce_line_item_type_info().
*/
function commerce_avatax_commerce_line_item_type_info() {
$line_item_types = array();
$line_item_types['avatax'] = array(
'name' => t('AvaTax'),
'description' => t('Calculate AvaTax'),
'add_form_submit_value' => t('Add tax line'),
'base' => 'commerce_avatax_line_item',
'callbacks' => array(
'configuration' => 'commerce_avatax_configure_line_item',
),
);
return $line_item_types;
}
/**
* Line item callback: configures the AvaTax line item type on module enable.
*/
function commerce_avatax_configure_line_item() {
$field_name = 'avatax';
$type = 'avatax';
$field = field_info_field($field_name);
$instance = field_info_instance('commerce_line_item', $field_name, $type);
if (empty($field)) {
$field = array(
'field_name' => $field_name,
'type' => 'list_text',
'cardinality' => 1,
'entity_types' => array(
'commerce_line_item',
),
'translatable' => FALSE,
'locked' => TRUE,
);
$field = field_create_field($field);
}
if (empty($instance)) {
$instance = array(
'field_name' => $field_name,
'entity_type' => 'commerce_line_item',
'bundle' => $type,
'label' => t('AvaTax Calc'),
'required' => TRUE,
'settings' => array(),
'widget' => array(
'type' => 'options_select',
'weight' => 0,
),
'display' => array(
'display' => array(
'label' => 'hidden',
'weight' => 0,
),
),
);
field_create_instance($instance);
}
}
/**
* Returns the title of an avatax line item.
*/
function commerce_avatax_line_item_title($line_item) {
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
if (!empty($line_item->data['avatax']['display_title'])) {
return $line_item->data['avatax']['display_title'];
}
}
/**
* Implements hook_commerce_price_component_type_info().
*/
function commerce_avatax_commerce_price_component_type_info() {
// Get Sales Tax description
$tax_title = variable_get('commerce_avatax_tax_description', '');
$components = array();
// Define a generic avatax price component type.
$components['avatax'] = array(
'title' => t('AvaTax sales tax'),
'display_title' => $tax_title,
'weight' => 25,
);
return $components;
}
/**
* Implements hook_commerce_order_update().
*/
function commerce_avatax_commerce_order_update($order) {
// Calculate sales tax between shipping module and review checkout.
if (!variable_get('commerce_avatax_checkout_form', '') && variable_get('commerce_avatax_tax_address', '') == 'Shipping') {
if ($order->original->status == 'checkout_shipping' && $order->status == 'checkout_review' && !isset($order->avatax)) {
commerce_avatax_calculate_sales_tax($order);
}
}
// Delete sales tax line item if cart is updated
if ($order->status == 'cart') {
if ($order->original->commerce_order_total['und'][0]['amount'] != $order->commerce_order_total['und'][0]['amount']) {
commerce_avatax_delete_avatax_line_items($order);
}
}
}
/**
* Calculate sales tax using regular web site checkout.
*/
function commerce_avatax_calculate_sales_tax($order) {
$sales_tax = commerce_avatax_retrieve_sales_tax($order);
// If we have a sales tax amount.
if ($sales_tax) {
// Create a new AvaTax line item.
$line_item = commerce_avatax_line_item_create($sales_tax, $order->order_id);
// Add the line item data as a property of the order.
$order->avatax['avatax'] = $line_item;
commerce_avatax_delete_avatax_line_items($order);
commerce_avatax_add_avatax_line_item($line_item, $order);
}
}
/**
* Calculate sales tax for manual order entry.
*/
function commerce_avatax_manual_calculate_sales_tax($order) {
$sales_tax = commerce_avatax_retrieve_sales_tax($order);
// If we have a sales tax amount.
if ($sales_tax) {
// Create a new AvaTax line item.
$line_item = commerce_avatax_line_item_create($sales_tax, $order->order_id);
// Add the line item data as a property of the order.
$order->avatax['avatax'] = $line_item;
}
}
/**
* Creates a sales tax line item
*
* @param $sales_tax
* A price array used to establish the base unit price for the AvaTax.
* @param $order
* If available, the order to which the AvaTax line item will belong.
*
* @return
* The sales tax line item.
*/
function commerce_avatax_line_item_create($sales_tax, $order_id = 0) {
$avatax_service = commerce_avatax_service_load();
// Create the new line item for AvaTax.
$line_item = commerce_avatax_line_item_new($sales_tax, $order_id);
// Set the price component of the unit price if it hasn't already been done.
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$data = $line_item_wrapper->commerce_unit_price->data
->value();
if (empty($data['components'])) {
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add($line_item_wrapper->commerce_unit_price
->value(), $avatax_service['price_component'], $line_item_wrapper->commerce_unit_price
->value(), TRUE, FALSE);
}
return $line_item;
}
/**
* Creates a new AvaTax line item populated with the sales tax values.
*
* @param $sales_tax
* A price array used to initialize the value of the line item's unit price.
* @param $order_id
* The ID of the order the line item belongs to.
* @param $data
* An array value to initialize the line item's data array with.
* @param $type
* The name of the line item type being created; defaults to 'avatax'.
*
* @return
* The avatax line item initialized to the given
* unit price.
*/
function commerce_avatax_line_item_new($sales_tax, $order_id = 0, $data = array(), $type = 'avatax') {
// Ensure a default product line item type.
if (empty($type)) {
$type = 'avatax';
}
// Create the new line item.
$line_item = entity_create('commerce_line_item', array(
'type' => $type,
'order_id' => $order_id,
'quantity' => 1,
'data' => $data,
));
// Populate line item with the sales tax unit price data.
commerce_avatax_line_item_populate($line_item, $sales_tax);
return $line_item;
}
/**
* Populates a sales tax line item with the specified values.
*
* @param $sales_tax
* A sales tax array to be added to the value of the line item's unit price.
*/
function commerce_avatax_line_item_populate($line_item, $sales_tax) {
$avatax_service = commerce_avatax_service_load();
// Use the label to store the display title of the avatax service.
$line_item->line_item_label = 'AvaTax Calc';
$line_item->quantity = 1;
$line_item->data['avatax'] = $avatax_service;
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
// Set the unit price.
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$line_item_wrapper->commerce_unit_price = $sales_tax;
}
/**
* Adds a avatax line item to an order, and saves the order.
*
* @param $line_item
* An unsaved tax saas line item that should be added to the order.
* @param $order
* The order to add the tax saas line item to.
* @param $skip_save
* Boolean indicating whether or not to skip saving the order in this function.
*
* @return
* The saved tax saas line item object or FALSE on failure.
*/
function commerce_avatax_add_avatax_line_item($line_item, $order) {
// Do not proceed without a valid order.
if (empty($order)) {
return FALSE;
}
// Save the incoming line item now so we get its ID.
commerce_line_item_save($line_item);
// Add it to the order's line item reference value.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_wrapper->commerce_line_items[] = $line_item;
commerce_order_calculate_total($order);
commerce_order_save($order);
}
/**
* Returns a form with the elements required to add an AvaTax line item through the line
* item manager widget.
*/
function commerce_avatax_line_item_add_form($form, &$form_state) {
// Calculate the sales tax amount for this order.
$order = $form_state['commerce_order'];
commerce_avatax_manual_calculate_sales_tax($order);
// Store the available rates in the form.
$form = array();
$form['#attached']['css'][] = drupal_get_path('module', 'commerce_avatax') . '/theme/commerce_avatax.admin.css';
$form['avatax_rate'] = array(
'#type' => 'value',
'#value' => $order->avatax,
);
// Create an options array for the sales tax amount
$options = commerce_avatax_options($order);
$form['avatax'] = array(
'#type' => 'radios',
'#title' => t('AvaTax Calc'),
'#options' => $options,
'#default_value' => key($options),
);
return $form;
}
/**
* Adds the selected tax saas information to a new tax saas line item.
*
* @param $line_item
* The newly created line item object.
* @param $element
* The array representing the widget form element.
* @param $form_state
* The present state of the form upon the latest submission.
* @param $form
* The actual form array.
*
* @return
* NULL if all is well or an error message if something goes wrong.
*/
function commerce_avatax_line_item_add_form_submit($line_item, $element, &$form_state, $form) {
$order = $form_state['commerce_order'];
// Ensure a quantity of 1.
$line_item->quantity = 1;
// Use the values for avatax
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $element['actions']['avatax_rate']['#value']['avatax']);
$sales_tax = $line_item_wrapper->commerce_unit_price
->value();
// Populate the line item with the appropriate data.
commerce_avatax_line_item_populate($line_item, $sales_tax);
}
/**
* Turns the sales tax amount into a form element options array.
*
* @param $order
* An order object with an avatax property defined as an array of
* sales tax values.
*
* @return
* An options array of calculated tax saas rates labeled using the display
* title of the tax saas services.
*/
function commerce_avatax_options($order) {
$options = array();
$line_item = $order->avatax['avatax'];
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$options['avatax'] = t('!avatax: !price', array(
'!avatax' => 'Sales tax',
'!price' => commerce_currency_format($line_item_wrapper->commerce_unit_price->amount
->value(), $line_item_wrapper->commerce_unit_price->currency_code
->value()),
));
return $options;
}
/**
* Deletes all AvaTax line items on an order.
*
* @param $order
* The order object to delete the avatax line items from.
*/
function commerce_avatax_delete_avatax_line_items($order) {
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
// When deleting more than one line item, metadata_wrapper will give problems
// if deleting while looping through the line items. So first remove from order
// and then delete the line items.
$line_item_ids = array();
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
// If this line item is an avatax saas line item...
if ($line_item_wrapper->type
->value() == 'avatax') {
// Store its ID for later deletion and remove the reference from the line
// item reference field.
$line_item_ids[] = $line_item_wrapper->line_item_id
->value();
$order_wrapper->commerce_line_items
->offsetUnset($delta);
}
}
// If we found any avatax line items...
if (!empty($line_item_ids)) {
// First save the order to update the line item reference field value.
commerce_order_save($order);
// Then delete the line items.
commerce_line_item_delete_multiple($line_item_ids);
}
}
/**
* Returns the avatax service array.
*
* @return
* An array with the AvaTax Calc service details.
*/
function commerce_avatax_service_load() {
$avatax_service = array(
'name' => 'avatax',
'base' => 'avatax',
'display_title' => 'Sales tax',
'description' => 'The calculated sales tax amount',
'price_component' => 'avatax',
'weight' => 0,
'module' => 'commerce_avatax',
'title' => 'Drupal Commerce Connector for AvaTax Calc',
'admin_list' => TRUE,
);
return $avatax_service;
}
/**
* AvaTax service: returns the sales tax amount as an array.
*
* @param $order
* The order object to delete the avatax line items from.
*
* @return
* The AvaTax Calc sales tax values as an array.
*/
function commerce_avatax_retrieve_sales_tax($order) {
// Get Company Code, Company Use Mode and Pro Mode.
$company_code = variable_get('commerce_avatax_company_code', '');
$use_mode = variable_get('commerce_avatax_use_mode', '');
$pro_mode = variable_get('commerce_avatax_pro_mode', '');
// Sales Tax Shipping code
$shipcode = variable_get('commerce_avatax_shipcode', '');
$sales_tax = array(
'amount' => 0,
'currency_code' => 'USD',
'data' => array(),
);
// Build order wrapper
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
// Exit if there are no line items in the order wrapper
if (count($order_wrapper->commerce_line_items) == 0) {
drupal_set_message(t('There are no line items for this order. Please Cancel this entry!'), 'error');
return $sales_tax;
}
// Add logic - integrate with physical goods module - to use billing address if only digital products with the order
// Get address to be used for sales tax
if (variable_get('commerce_avatax_tax_address', '') == 'Billing') {
if (isset($order_wrapper->commerce_customer_billing->commerce_customer_address)) {
$billing_address = $order_wrapper->commerce_customer_billing->commerce_customer_address
->value();
$street1 = $billing_address['thoroughfare'];
$street2 = $billing_address['premise'];
$city = $billing_address['locality'];
$state = $billing_address['administrative_area'];
$country = $billing_address['country'];
$zip = $billing_address['postal_code'];
}
}
if (variable_get('commerce_avatax_tax_address', '') == 'Shipping') {
if (isset($order_wrapper->commerce_customer_shipping->commerce_customer_address)) {
$shipping_address = $order_wrapper->commerce_customer_shipping->commerce_customer_address
->value();
$street1 = $shipping_address['thoroughfare'];
$street2 = $shipping_address['premise'];
$city = $shipping_address['locality'];
$state = $shipping_address['administrative_area'];
$country = $shipping_address['country'];
$zip = $shipping_address['postal_code'];
}
}
// exit if address is incomplete
if (!$street1 || !$city || !$state || !$zip) {
drupal_set_message(t('Sales tax not calculated as shipping address incomplete. Please select "Cancel" and try again.'), 'error');
return $sales_tax;
}
// exit if delivery address state is not in list of active states
$avatax_states = array();
$avatax_states_str = variable_get('commerce_avatax_select_states', '');
if ($avatax_states_str) {
$avatax_states = explode(", ", $avatax_states_str);
if (!in_array($state, $avatax_states)) {
return $sales_tax;
}
}
// Get primary business location
$primary_street1 = variable_get('commerce_avatax_primary_street1', '');
$primary_street2 = variable_get('commerce_avatax_primary_street2', '');
$primary_city = variable_get('commerce_avatax_primary_city', '');
$primary_state = variable_get('commerce_avatax_primary_state', '');
$primary_zip = variable_get('commerce_avatax_primary_zip', '');
// Get User name or e-mail address
if ($order->uid == 0) {
if (arg(0) == 'system' && $order->order_id == 0) {
drupal_set_message(t('Order # has not been allocated. Please select "Cancel". You must FIRST save the order to allocate an order # - then add sales tax. '), 'error');
return $sales_tax;
}
elseif ($order->order_id != 0 && $order->mail == '') {
$user_id = 'administrator';
}
else {
$user_email = $order->mail;
$user_id = commerce_avatax_email_to_username($user_email);
}
}
else {
$user_data = user_load($order->uid);
$user_id = $user_data->name;
}
// Construct arguments for AvaTax functions
$ava_args = compact('company_code', 'pro_mode', 'user_id', 'shipcode', 'use_mode', 'street1', 'street2', 'city', 'state', 'country', 'zip', 'primary_street1', 'primary_street2', 'primary_city', 'primary_state', 'primary_zip');
// Get tax amount - conditional on version
$avatax_result = commerce_avatax_get_tax($order, $order_wrapper, $ava_args);
// Check that there was a return from the tax request.
if (!$avatax_result) {
// AvaTax will return an error code - Integrator to determine if the checkout is to be blocked!
return $sales_tax;
}
$sales_tax = array(
'amount' => $avatax_result['tax_amount'] * 100,
'currency_code' => 'USD',
'data' => array(),
'tax_details' => $avatax_result['tax_details'],
);
return $sales_tax;
}
// Generate AvaTax user name as approximation of e-mail address
function commerce_avatax_email_to_username($user_email) {
// Default to the first part of the e-mail address.
$name = substr($user_email, 0, strpos($user_email, '@'));
// Remove possible illegal characters.
$name = preg_replace('/[^A-Za-z0-9_.-]/', '', $name);
// Trim that value for spaces and length.
$name = trim(substr($name, 0, USERNAME_MAX_LENGTH - 4));
return $name;
}
Functions
Name | Description |
---|---|
commerce_avatax_add_avatax_line_item | Adds a avatax line item to an order, and saves the order. |
commerce_avatax_calculate_sales_tax | Calculate sales tax using regular web site checkout. |
commerce_avatax_commerce_line_item_type_info | Implements hook_commerce_line_item_type_info(). |
commerce_avatax_commerce_order_update | Implements hook_commerce_order_update(). |
commerce_avatax_commerce_price_component_type_info | Implements hook_commerce_price_component_type_info(). |
commerce_avatax_configure_line_item | Line item callback: configures the AvaTax line item type on module enable. |
commerce_avatax_delete_avatax_line_items | Deletes all AvaTax line items on an order. |
commerce_avatax_email_to_username | |
commerce_avatax_libraries_info | Implements hook_libraries_info(). |
commerce_avatax_line_item_add_form | Returns a form with the elements required to add an AvaTax line item through the line item manager widget. |
commerce_avatax_line_item_add_form_submit | Adds the selected tax saas information to a new tax saas line item. |
commerce_avatax_line_item_create | Creates a sales tax line item |
commerce_avatax_line_item_new | Creates a new AvaTax line item populated with the sales tax values. |
commerce_avatax_line_item_populate | Populates a sales tax line item with the specified values. |
commerce_avatax_line_item_title | Returns the title of an avatax line item. |
commerce_avatax_manual_calculate_sales_tax | Calculate sales tax for manual order entry. |
commerce_avatax_options | Turns the sales tax amount into a form element options array. |
commerce_avatax_permission | Implements hook_permission(). |
commerce_avatax_retrieve_sales_tax | AvaTax service: returns the sales tax amount as an array. |
commerce_avatax_service_load | Returns the avatax service array. |