Creating a Tax Provider
Anatomy of a Tax Provider
Tax providers are utilized by Broadleaf Commerce to interface with an external provider (or some custom algorithm) for tax calculation. The cost of tax can be based on a number of factors including region and dollar total. If you encounter a situation where there is no existing Broadleaf Commerce tax module for your desired tax calculation method, you may find it necessary to develop your own custom tax provider. Let's review the TaxProvider
interface from the framework:
public Order calculateTaxForOrder(Order order, ModuleConfiguration config) throws TaxException;
public Order commitTaxForOrder(Order order, ModuleConfiguration config) throws TaxException;
public void cancelTax(Order order, ModuleConfiguration config) throws TaxException;
calculateTaxForOrder
- invoked during theblPricingWorkflow
to associateTaxDetail
objects to theFulfillmentGroup
s andFulfillmentGroupItem
s within the given ordercommitTaxForOrder
- invoked during theblCheckoutWorkflow
in theCommitTaxActivity
to communicate with an external tax provider that might need to manage tax documentscancelTax
- if an order has already gone through theCommitTaxActivity
but did not complete the entireblCheckoutWorkflow
and taxes that have might have been committed to the external provider need to be rolled back
Some providers (or your custom implementation) may not use the
commitTaxForOrder
orcancelTax
methods. At a minimum, yourTaxProvider
should createTaxDetail
objects for theFulfillmentGroup
s in an order via thecalculateTaxForOrder
method.
Selecting a provider
Multiple TaxProviders
can exist in the framework at once. In order to decide which provider to use, blTaxService
looks up ModuleConfiguration
s (a database entity) to get a list of the configured modules in the system. The logic works like this:
- Look up all the Module Configurations that have a type of
ModuleConfigurationType.TAX\_CALCULATION
, sorted bypriority
- Loop through each configuration
- If a configuration is set to the default, use that configuration
- If there is not a default configuration, use the first one in resulting list (the one with the highest priority)
- Take the determined configuration and try to invoke the delegate method by checking
canRespond
for each provider - If no provider could respond to the determined configuration and there is a requirement to calculate tax (via the
mustCalculate
property onblTaxService
) then throw a TaxException
This pattern means that each provider has a unique Module Configuration instance to go along with it.
Hooking up your provider
Add your provider to the blTaxProviders
bean:
<bean id="myTaxProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<bean class="com.mycompany.core.tax.provider.MyCustomTaxProvider" />
</list>
</property>
</bean>
<bean class="org.broadleafcommerce.common.extensibility.context.merge.LateStageMergeBeanPostProcessor">
<property name="collectionRef" value="myTaxProviders"/>
<property name="targetRef" value="blTaxProviders"/>
</bean>
You will also need a database table subclass of AbstractModuleConiguration
. See the docs for Extending Entities for more information on how to do this.
If you want to bypass the database step for
ModuleConfiguration
you can simply override theblTaxService
bean with a subclass ofTaxServiceImpl
and implement those methods yourself. Rather than a customTaxProvider
you would then have a customTaxService
Calculating tax
When implementing a TaxProvider
, the key to communicating the tax cost to Broadleaf Commerce is the Order instance passed into the calculateTaxForOrder method. While the specific informational needs for each tax calculation method will differ, you should have access here to the building blocks necessary to calculate any form of tax. Here are some hints:
- Call
order.getFulfillmentGroups()
to get all the fulfillment groups for the order. Multiple fulfillment groups mean that parts of the order are shipping to different locations. - Call
fulfillmentGroup.getAddress()
to get the shipping address. - Call
fulfillmentGroup.getItems()
to get all theFulfillmentGroupItem
s in aFulfillmentGroup
- Call
fulfillmentGroupItem.getOrderItem()
to get an OrderItem - Call
orderItem.getTaxablePrice()
to get the taxable amount for the item
You'll want to iterate through all the FulfillmentGroupItem
and FulfillmentGroupFee
objects in every FulfillmentGroup of the Order, creating and setting TaxDetail
objects when appropriate. You may also have additional taxes that reside on a FulfillmentGroup
, such as a shipping tax.
Responding with tax
Broadleaf Commerce expects that the Order instance returned from the calculateTaxForOrder method of your tax module has the pertinent tax cost totals assigned. Your tax module is on the hook for setting the list of TaxDetail
objects on:
- Every
FulfillmentGroup
- Every
FulfillmentGroupItem
- Every
FulfillmentGroupFee
The totalTax fields will be set in the pricing workflow during the TotalActivity
.