Avalara Tax integration

Edit on GitHub

Avalara is a software that calculates sales order taxes during checkout in the US market. It provides real-time tax calculation and an automatic filing of returns. 

General information

To enable the AvalaraTax partner integration, use the spryker-eco/avalara-tax module.

Install feature core

Follow the steps below to install the feature core.

Prerequisites

  1. To start the feature integration, overview and install the necessary features:
NAME VERSION INTEGRATION GUIDE
Spryker Core master Spryker Core feature integration
Cart master Cart feature integration
Product master Product feature integration
Tax master
Inventory Management master Inventory Management feature integration
Glue API: Checkout master Glue API: Checkout feature integration
  1. To register an application with the Avalara platform and get configuration options, follow Set up AvaTax.

1) Install the required modules using Composer

Install the required modules:

composer require spryker-eco/avalara-tax:"^0.1.0" --update-with-dependencies
Verification

Ensure that the following modules have been installed:

MODULE EXPECTED DIRECTORY
AvalaraTax vendor/spryker-eco/avalara-tax

2) Set up the configuration

Add the US ISO country code to the global store configuration:

config/Shared/stores.php

<?php

$stores = [];

$stores['DE'] = [
    ...
    'countries' => [..., 'US'],
    ...
];

Configure the Avalara credentials:

  1. Add the following template to configuration:

config/Shared/config_default.php

    <?php

    use SprykerEco\Shared\AvalaraTax\AvalaraTaxConstants;

    // >>> Avalara Tax
    $config[AvalaraTaxConstants::AVALARA_TAX_APPLICATION_NAME] = 'YOUR_APPLICATION_NAME';
    $config[AvalaraTaxConstants::AVALARA_TAX_APPLICATION_VERSION] = 'YOUR_APPLICATION_VERSION';
    $config[AvalaraTaxConstants::AVALARA_TAX_MACHINE_NAME] = 'YOUR_MACHINE_NAME';
    $config[AvalaraTaxConstants::AVALARA_TAX_ENVIRONMENT_NAME] = 'AVALARA_ENVIRONMENT_NAME';
    $config[AvalaraTaxConstants::AVALARA_TAX_ACCOUNT_ID] = 'YOUR_ACCAUNT_ID';
    $config[AvalaraTaxConstants::AVALARA_TAX_LICENSE_KEY] = 'YOUR_LICENSE_KEY';
    $config[AvalaraTaxConstants::AVALARA_TAX_COMPANY_CODE] = 'YOUR_COMPANY_CODE';
  1. Based on the data from your Avalara account, replace the placeholders in the template as described below.
PLACEHOLDER DESCRIPTION
YOUR_APPLICATION_NAME Application name.
YOUR_APPLICATION_VERSION Application version.
YOUR_MACHINE_NAME Name of the machine specific to your project.
AVALARA_ENVIRONMENT_NAME Environment name. Acceptable values are sandbox, production or the full URL of your AvaTax instance.
YOUR_ACCAUNT_ID Client identifier.
YOUR_LICENSE_KEY Client secret.
YOUR_COMPANY_CODE Company code.

3) Add translations

  1. Append glossary according to your configuration:

data/import/glossary.csv

>countries.iso.US,United States of America,en_US
countries.iso.US,vereinigte Staaten von Amerika,de_DE
  1. Import data:
console data:import glossary
Verification

Make sure that in the database, the configured data has been added to the spy_glossary table.

4) Set up database schema and transfer objects

Apply database changes, generate entity and transfer changes:

console transfer:generate
console propel:install
console transfer:generate
Verification

Make sure that the following changes have been applied by checking your database:

DATABASE ENTITY TYPE EVENT
spy_tax_avalara_api_log table created
spy_tax_avalara_sales_order table created
spy_tax_avalara_sales_order_item table created
spy_tax_avalara_sales_detail table created
spy_product_abstract.avalara_tax_code column created
spy_product.avalara_tax_code column created
Verification

Make sure that the following changes have been applied in the transfer objects:

TRANSFER TYPE EVENT PATH
AvalaraApiLogTransfer class created src/Generated/Shared/Transfer/AvalaraApiLogTransfer
AvalaraAddressTransfer class created src/Generated/Shared/Transfer/AvalaraAddressTransfer
AvalaraLineItemTransfer class created src/Generated/Shared/Transfer/AvalaraLineItemTransfer
AvalaraCreateTransactionTransfer class created src/Generated/Shared/Transfer/AvalaraCreateTransactionTransfer
AvalaraCreateTransactionRequestTransfer class created src/Generated/Shared/Transfer/AvalaraCreateTransactionRequestTransfer
AvalaraTransactionLineTransfer class created src/Generated/Shared/Transfer/AvalaraTransactionLineTransfer
AvalaraTransactionTransfer class created src/Generated/Shared/Transfer/AvalaraTransactionTransfer
AvalaraCreateTransactionResponseTransfer class created src/Generated/Shared/Transfer/AvalaraCreateTransactionResponseTransfer
AvalaraAddressValidationInfoTransfer class created src/Generated/Shared/Transfer/AvalaraAddressValidationInfoTransfer
AvalaraResolveAddressRequestTransfer class created src/Generated/Shared/Transfer/AvalaraResolveAddressRequestTransfer
AvalaraResolveAddressResponseTransfer class created src/Generated/Shared/Transfer/AvalaraResolveAddressResponseTransfer
QuoteTransfer.avalaraCreateTransactionResponse property created src/Generated/Shared/Transfer/QuoteTransfer
ProductConcreteTransfer.avalaraTaxCode property created src/Generated/Shared/Transfer/ProductConcreteTransfer
ProductAbstractTransfer.avalaraTaxCode property created src/Generated/Shared/Transfer/ProductAbstractTransfer
ItemTransfer.avalaraTaxCode property created src/Generated/Shared/Transfer/ItemTransfer
ItemTransfer.warehouse property created src/Generated/Shared/Transfer/ItemTransfer

5) Set up behavior

  1. Activate the following plugins:
PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
AvalaraItemTaxRateCalculatorPlugin Calculates taxes based on the response data received from the Avalara Tax API.
Use it instead of ProductItemTaxRateCalculatorPlugin, ProductOptionTaxRateCalculatorPlugin, and ShipmentTaxRateCalculatorPlugin.
None SprykerEco\Zed\AvalaraTax\Communication\Plugin\Calculation
AvalaraTaxCodeItemExpanderPlugin Expands CartChangeTransfer.items with an Avalara tax code. None SprykerEco\Zed\AvalaraTax\Communication\Plugin\Cart
AvalaraTaxCodeProductConcreteBeforeCreatePlugin Expands product concrete with an Avalara tax code. None SprykerEco\Zed\AvalaraTax\Communication\Plugin\Product
AvalaraTaxCheckoutPreConditionPlugin Checks if a request to Avalara was successful. None SprykerEco\Zed\AvalaraTax\Communication\Plugin\Checkout
ItemWarehouseCartOperationPostSavePlugin Expands QuoteTransfer.items with a warehouse property. None SprykerEco\Zed\AvalaraTax\Communication\Plugin\Cart
AvalaraReadCheckoutDataValidatorPlugin Validates the shipping address data. None SprykerEco\Zed\AvalaraTax\Communication\Plugin\CheckoutRestApi
src/Pyz/Zed/Calculation/CalculationDependencyProvider.php
<?php

namespace Pyz\Zed\Calculation;

use Spryker\Zed\Calculation\CalculationDependencyProvider as SprykerCalculationDependencyProvider;
use Spryker\Zed\Kernel\Container;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\Calculation\AvalaraItemTaxRateCalculatorPlugin;

class CalculationDependencyProvider extends SprykerCalculationDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CalculationExtension\Dependency\Plugin\CalculationPluginInterface[]
     */
    protected function getQuoteCalculatorPluginStack(Container $container)
    {
        return [
            new AvalaraItemTaxRateCalculatorPlugin(),
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CalculationExtension\Dependency\Plugin\CalculationPluginInterface[]
     */
    protected function getOrderCalculatorPluginStack(Container $container)
    {
        return [
            new AvalaraItemTaxRateCalculatorPlugin(),
        ];
    }
}
Verification

Make sure you’ve enabled the plugins:

  1. Adding items to a cart and proceed to checkout.
  2. On the summary page, you should see the calculated taxes for your order.
src/Pyz/Zed/Cart/CartDependencyProvider.php
<?php

namespace Pyz\Zed\Cart;

use Spryker\Zed\Cart\CartDependencyProvider as SprykerCartDependencyProvider;
use Spryker\Zed\Kernel\Container;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\Cart\AvalaraTaxCodeItemExpanderPlugin;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\Cart\ItemWarehouseCartOperationPostSavePlugin;

class CartDependencyProvider extends SprykerCartDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CartExtension\Dependency\Plugin\ItemExpanderPluginInterface[]
     */
    protected function getExpanderPlugins(Container $container): array
    {
        return [
            new AvalaraTaxCodeItemExpanderPlugin(),
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CartExtension\Dependency\Plugin\CartOperationPostSavePluginInterface[]
     */
    protected function getPostSavePlugins(Container $container): array
    {
        return [
            new ItemWarehouseCartOperationPostSavePlugin(),
        ];
    }
}
Verification
  1. Add an address to a warehouse
  2. Increase the product stock of an item.
  3. Add the item to a cart.
  4. Proceed to the summary page of checkout.
  5. In the spy_tax_avalara_api_log table, check that the ShipFrom property is specified in the request data.
src/Pyz/Zed/Product/ProductDependencyProvider.php
<?php

namespace Pyz\Zed\Product;

use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Product\ProductDependencyProvider as SprykerProductDependencyProvider;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\Product\AvalaraTaxCodeProductConcreteBeforeCreatePlugin;

class ProductDependencyProvider extends SprykerProductDependencyProvider
{

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\ProductExtension\Dependency\Plugin\ProductConcreteCreatePluginInterface[]
     */
    protected function getProductConcreteBeforeCreatePlugins(Container $container)
    {
        return [
            new AvalaraTaxCodeProductConcreteBeforeCreatePlugin(),
        ];
    }
}
Verification
  1. Create an abstract product with the Avalara tax code specified.
  2. Create a variant of this product.
  3. Check that the concrete product inherits the Avalara tax code.

src/Pyz/Zed/Checkout/CheckoutDependencyProvider.php

<?php

namespace Pyz\Zed\Checkout;

use Spryker\Zed\Checkout\CheckoutDependencyProvider as SprykerCheckoutDependencyProvider;
use Spryker\Zed\Kernel\Container;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\Checkout\AvalaraTaxCheckoutPreConditionPlugin;

class CheckoutDependencyProvider extends SprykerCheckoutDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPreConditionPluginInterface[]
     */
    protected function getCheckoutPreConditions(Container $container)
    {
        return [
            new AvalaraTaxCheckoutPreConditionPlugin(),
        ];
    }
}
Verification
  1. Create a warehouse with a non-US address.
  2. Specify product stock for a product.
  3. Add the product to a cart.
  4. On the summary page of checkout, check that you can’t place the order because of a failed request to the Avalara API.

src/Pyz/Zed/CheckoutRestApi/CheckoutRestApiDependencyProvider.php

<?php

namespace Pyz\Zed\CheckoutRestApi;

use Spryker\Zed\CheckoutRestApi\CheckoutRestApiDependencyProvider as SprykerCheckoutRestApiDependencyProvider;
use SprykerEco\Zed\AvalaraTax\Communication\Plugin\CheckoutRestApi\AvalaraReadCheckoutDataValidatorPlugin;

class CheckoutRestApiDependencyProvider extends SprykerCheckoutRestApiDependencyProvider
{
    /**
     * @return \Spryker\Zed\CheckoutRestApiExtension\Dependency\Plugin\ReadCheckoutDataValidatorPluginInterface[]
     */
    protected function getReadCheckoutDataValidatorPlugins(): array
    {
        return [
            new AvalaraReadCheckoutDataValidatorPlugin(),
        ];
    }
}
Verification

Make sure that you’ve activated AvalaraReadCheckoutDataValidatorPlugin:

  1. Send an incorrect address to the /checkout-data endpoint.
  2. Make sure that request with the incorrect shipping address does not pass the validation check:

Request:

Request:
{
    "data": {
        "type": "checkout-data",
        "attributes": {
            "idCart": "1ce91011-8d60-59ef-9fe0-4493ef3628b2",
            "shippingAddress": {
                "salutation": "Mr",
                "email": "spencor.hopkin@spryker.com",
                "firstName": "spencor",
                "lastName": "hopkin",
                "address1": "West road",
                "address2": "212",
                "address3": "",
                "zipCode": "11111",
                "city": "Test",
                "iso2Code": "US",
                "company": "Spryker",
                "phone": "+380663344123",
                "isDefaultShipping": true,
                "isDefaultBilling": true
            }
        }
    }
}

Response:

{
    "errors": [
        {
            "code": "1101",
            "status": 422,
            "detail": "Address not geocoded."
        },
        {
            "code": "1101",
            "status": 422,
            "detail": "The city could not be determined."
        }
    ]
}
  1. Update the following data import .csv files:
FILE NAME COLUMN TO ADD LOCATION
product_abstract.csv avalara_tax_code data/import/common/common/product_abstract.csv
product_concrete.csv avalara_tax_code data/import/common/common/product_concrete.csv
  1. To handle the new field, adjust ProductAbstract and ProductConcrete data importers using the following example:
    • data/import/common/common/product_abstract.csv
    • data/import/common/common/product_concrete.csv

data/import/common/common/product_abstract.csv

>category_key,category_product_order,abstract_sku,name.en_US,name.de_DE,url.en_US,url.de_DE,attribute_key_1,value_1,attribute_key_1.en_US,value_1.en_US,attribute_key_1.de_DE,value_1.de_DE,attribute_key_2,value_2,attribute_key_2.en_US,value_2.en_US,attribute_key_2.de_DE,value_2.de_DE,attribute_key_3,value_3,attribute_key_3.en_US,value_3.en_US,attribute_key_3.de_DE,value_3.de_DE,attribute_key_4,value_4,attribute_key_4.en_US,value_4.en_US,attribute_key_4.de_DE,value_4.de_DE,attribute_key_5,value_5,attribute_key_6,value_6,attribute_key_6.en_US,value_6.en_US,attribute_key_6.de_DE,value_6.de_DE,color_code,description.en_US,description.de_DE,tax_set_name,meta_title.en_US,meta_title.de_DE,meta_keywords.en_US,meta_keywords.de_DE,meta_description.en_US,meta_description.de_DE,new_from,new_to,avalaraTaxCode
digital-cameras,16,001,Canon IXUS 160,Canon IXUS 160,/en/canon-ixus-160-1,/de/canon-ixus-160-1,megapixel,20 MP,,,,,flash_range_tele,1.3-1.5 m,flash_range_tele,4.2-4.9 ft,,,memory_slots,1,,,,,usb_version,2,,,,,brand,Canon,,,color,Red,color,Weinrot,#DC2E09,"Add a personal touch Make shots your own with quick and easy control over picture settings such as brightness and colour intensity. Preview the results while framing using Live View Control and enjoy sharing them with friends using the 6.8 cm (2.7”) LCD screen. Combine with a Canon Connect Station and you can easily share your photos and movies with the world on social media sites and online albums like irista, plus enjoy watching them with family and friends on an HD TV. Effortlessly enjoy great shots of friends thanks to Face Detection technology. It detects multiple faces in a single frame making sure they remain in focus and with optimum brightness. Face Detection also ensures natural skin tones even in unusual lighting conditions.","Beeindruckende Aufnahmen, ganz einfach Smart Auto ermöglicht die mühelose Aufnahme von fantastischen Fotos und Movies – die Kamera wählt in diesem Modus automatisch die idealen Einstellungen für die jeweilige Aufnahmesituation. Sie müssen nur noch das Motiv anvisieren und auslösen. Ein Druck auf die Hilfe-Taste führt zu leicht verständlichen Erklärungen der Kamerafunktionen. Zahlreiche Kreativfilter laden zum Experimentieren ein und bieten echten Fotospaß. So lässt sich neben vielen anderen Optionen der Verzeichnungseffekt eines Fisheye-Objektivs nachempfinden oder in Fotos und Movies werden die Dinge wie Miniaturmodelle dargestellt.",Entertainment Electronics,Canon IXUS 160,Canon IXUS 160,"Canon,Entertainment Electronics","Canon,Entertainment Electronics",Add a personal touch Make shots your own with quick and easy control over picture settings such as brightness and colour intensity. Preview the results whi,"Beeindruckende Aufnahmen, ganz einfach Smart Auto ermöglicht die mühelose Aufnahme von fantastischen Fotos und Movies – die Kamera wählt in diesem Modus au","",2020-01-01 00:00:00.000000,PC040111
  1. Import data:
console data:import product-abstract
console data:import product-concrete
Verification

Open spy_product_abstract, and spy_product and make sure that all data has been imported.

FEATURE REQUIRED FOR THE CURRENT FEATURE INTEGRATION GUIDE
Avalara Tax + Product Option Avalara Tax + Product Options feature integration
Avalara Tax + Shipment Avalara Tax + Shipment feature integration