<dependency>
<groupId>com.broadleafcommerce.microservices</groupId>
<artifactId>broadleaf-avalara-tax</artifactId>
<version>1.0.0-GA</version>
</dependency>
TaxService
is an internal service responsible for providing and applying tax calculations for a cart. This is not a stand-alone microservice and cannot be accessed directly via a REST API. Rather, this service is internal to Cart Operations Service and is invoked as part of the Pricing functionality.
Note
|
For overall pricing, see CartPricingService. |
The default implementation of this interface is DefaultDelegatingTaxService, which replaces the deprecated DefaultTaxService
. The default implementation defers to TaxDelegate
, a component in the TaxCommon
library, which selects the best TaxProvider
and delegates to that.
The default implementation defers the actual calculating to the configured TaxProvider(s).
Tip
|
The TaxDelegate can select from a list of TaxProvider instances and uses a discovery process to find the best implementation for the provided request and ContextInfo . This allows different tenants and applications to make use of different TaxProviders .
|
Out of the box, having an address is required to calculate actual taxes, whether it’s shipping or billing address, depending on the fulfillment’s taxAddressSource
.
Note: If requesting estimated tax calculation, the address may not be needed, depending on whether any of the registered tax providers can declare a reasonable default rate.
For different requirements, you can override DefaultDelegatingTaxService#canCalculateActualTaxes
to customize its behavior.
The SimpleTaxProvider is a basic implementation of a TaxProvider
that is primarily suitable for estimating taxes, but may be effectively used as a fallback provider in case a more appropriate provider is unavailable. It reads in tax details from a file or from a configuration property. For more information, see Tax Common
SimpleTaxProvider supports both included & additional taxes. By default, taxes are added on top of the item’s price. To enable included prices, you’ll need to customize DefaultDelegatingTaxService#isItemIncludesTaxes(Cart, FulfillmentGroup, FulfillmentItem, CartItem, ContextInfo)
. The result of this method gets passed to the SimpleTaxProvider, which is used to determine how taxes will be calculated.
See VAT Taxes article for information and configuration.
Tax Codes can be configured on the product or category. The SimpleTaxProvider will use an item’s tax code (if present) to look up tax record configuration. If no configuration matches the tax code, then the SimpleTaxProvider will look up configuration based on the country code of the shipping address.
Each FulfillmentItem contains a list of TaxDetails, which in turn contain fields for the amount of tax calculated and the taxable amount of the items. The SimpleTaxProvider and DefaultTaxService will set these fields consistent with the tax type (sales tax or VAT).
The CartPricing object on Cart has additional fields to indicate if the cart’s taxes are included (VAT) or excluded (sales tax) from the subtotal.
The field "taxIncludedType" maps to the TaxIncludedType enum with possible values of YES, NO, and PARTIAL.
The field "includedTaxAmount" indicates the amount of tax that is included in the cart subtotal.
For CartPricing objects with only VAT taxes, the "taxIncludedType" will be "YES" and the "includedTaxAmount" will equal the "totalTax" field. When displaying taxes and totals to a user, avoid doing any calculations on the front-end and use the appropriate CartPricing fields.
Broadleaf, as well as 1st and 3rd parties, may provide additional TaxProvider
implementations. Each one should have a unique bean name. Each registered TaxProvider
will be passed to the DefaultTaxDelegate
upon startup.
In order to use these additional TaxProvider
implementation, one should only need to include them in the classpath. For example:
<dependency>
<groupId>com.broadleafcommerce.microservices</groupId>
<artifactId>broadleaf-avalara-tax</artifactId>
<version>1.0.0-GA</version>
</dependency>
This will register the AvalaraTaxProvider
at runtime, which will add it to a list provided to DefaultTaxDelegate
. The process to resolve the best TaxProvider
is defined here: Tax Common.
Out of the box, SimpleTaxProvider is used to calculate actual taxes. In your environment, you may have different requirements and constraints. This section will go through the following common use cases as well as how to achieve them:
Note
|
Cart#isTaxEstimated is used to indicate whether the tax calculation is estimated
|
In your project, you may have a geolocation service that can guess the address of the cart before the customer can provide the actual address. At this point, you may want to indicate that the tax calculation is estimated.
To achieve this, you can add an attribute to Cart’s attributes
map to when the address is an estimated address, then override DefaultCartPricingService#determineTaxCalculationStrategy
to something like the following:
public class MyPricingService extends DefaultCartPricingService {
public MyPricingService(CartItemPricingUtils cartItemPricingUtils, CartProvider cartProvider,
PricingProvider pricingProvider, OfferProvider offerProvider,
CatalogProvider<? extends CatalogItem> catalogProvider,
CartItemConfigurationService<? extends CatalogItem> cartItemConfigurationService,
TaxService taxService, FulfillmentPricingService fulfillmentPricingService,
ObjectMapper objectMapper, CartTotalsCalculator cartTotalsCalculator,
CartPricingRoundingHelper roundingHelper,
CartOperationServiceProperties cartOperationServiceProperties, TypeFactory typeFactory,
MessageSource messageSource) {
super(cartItemPricingUtils, cartProvider, pricingProvider, offerProvider, catalogProvider,
cartItemConfigurationService, taxService, fulfillmentPricingService, objectMapper,
cartTotalsCalculator, roundingHelper, cartOperationServiceProperties, typeFactory,
messageSource);
}
@Override
protected String determineTaxCalculationStrategy(@lombok.NonNull Cart cart,
@Nullable ContextInfo contextInfo) {
if (getTaxService().canCalculateTaxes(cart, contextInfo)) {
boolean isGeoLocationAddress = cart.getFulfillmentGroups().stream()
.anyMatch(fg ->
BooleanUtils.isTrue((Boolean) fg.getAttributes().get("IS_GEO_ADDRESS")));
if (isGeoLocationAddress) {
return DefaultTaxCalculationStrategies.ESTIMATED.name();
} else {
return DefaultTaxCalculationStrategies.ACTUAL.name();
}
} else {
return DefaultTaxCalculationStrategies.SKIP.name();
}
}
}
Then, override DefaultDelegatingTaxService#determinePreferredProviderId
to use a dedicated tax provider for estimated tax calculation as such:
public class MyDelegatingTaxService extends DefaultDelegatingTaxService {
public MyDelegatingTaxService(@lombok.NonNull TaxDelegate<?, ?> taxDelegate,
List<TaxAddressSourceHandler> taxAddressSourceHandlers,
@lombok.NonNull TypeFactory typeFactory) {
super(taxDelegate, taxAddressSourceHandlers, typeFactory);
}
@Override
protected String determinePreferredProviderId(@lombok.NonNull Cart cart,
boolean estimate,
@Nullable ContextInfo contextInfo) {
if (estimate) {
return "estimatedTaxProviderId";
} else {
return "actualTaxProviderId";
}
}
}
This way, after tax calculation, you can use the Cart#isTaxEstimated
flag to indicate that the tax calculation was estimated.
Important
|
DefaultDelegatingTaxService#canCalculateTaxes is determined based on the sufficiency of the fulfillment groups' addresses. At the bare minimum, the address must have stateProvinceRegion and country for taxes to be calculated. For custom behavior, please override DefaultDelegatingTaxService#fulfillmentGroupHasSufficientAddress
|
To achieve this, you simply need to configure the preferred and fall back tax providers via TaxDelegateProperties
(discriminated by tenant & application):
broadleaf.tax.delegate.preferred-tax-provider-id
broadleaf.tax.delegate.fallback-tax-provider-id
With this configuration, if the primary tax provider fails, the fallback tax provider would be used, which in this case would be an estimated tax provider.
Additionally, you can also override DefaultDelegatingTaxService#determinePreferredProviderId
to identify the preferred tax provider based on Cart
and/or ContextInfo
information, rather than using the property.