Broadleaf Microservices
  • v1.0.0-latest-prod

The Tax Module

This page describes the tax calculation module embedded within the Billing service. BillingTaxDetail is the pertinent domain entity, and it is described in detail on this page as well.

Billing Tax Detail

The BillingTaxDetail object captures the taxes calculated by a tax processor for a specific item. The DefaultDelegatingTaxService receives a response from a TaxProvider in a form of TaxCalculationResponse. Processing it, the Tax Service creates a BillingTaxDetail for each tax for a given item.

Tip
See the Data Model of the tax detail on the Billing Domain page

Fields of interest

  • The billingEventItemId field holds the reference to a specific BillingEventItem

  • The amount field holds the amount of this tax.

  • The rate field holds the rate of this tax.

    • The (item total amount * rate) should equal the number in the amount field.

  • The type field holds the type of the tax for which this tax detail was created.

    • This is usually the type of jurisdiction this tax originates from - region, city, state, etc.

Calculating Taxes

The Tax Module is composed of two message handlers and related services. Below is an outline of the tax calculation process.

Tax Calculation Processor Handler

The first message handler, TaxCalculationProcessorHandler, takes in requests that need taxes calculated from the BillingProcessingRequestInitializer. It delegates the calculation to an Autowired TaxService bean. A high-level walkthrough of the process follows:

  1. The default implementation, DefaultDelegatingTaxService, determines if taxes can be calculated for this request.

  2. Then, grouping the items by nexus, it creates requests (TaxCalculationRequest) for each group of items.

    • The createRequest(…​) method calls multiple hook-points that clients can customize to influence the determination of values for a few of the fields.

    • In this stage, a TaxItem is created for each taxable item and embedded within the request. As mentioned in Item’s Fields of Interest, the taxable boolean determines the necessity to calculate taxes for a given item.

  3. Then, the service gets the TaxDelegate to calculate the taxes using the created request.

    • Through a complicated process, DefaultTaxDelegate determines the appropriate TaxProvider to use for the request.

    • A specific provider can be specified as prioritized in the request, but the preference would only be honored if such a provider is detected at runtime.

  4. The response object, TaxCalculationResponse, contains a summary of calculated tax for each taxable item, as well as specifics for a given tax contained in TaxDetails.

    • The information on the response object is applied to create BillingTaxDetails recording the calculation.

    • Note that if a TaxDetail on the response object is for the entire BillingEvent, the module creates prorated tax details for each item according to their share of the BillingEvent#billTotal.

    • During the process, tax totals are set both on the items and the overall BillingEvent object.

  5. A successful TaxCalculationResult is created if no error is thrown.

    • If an error was thrown by a tax provider, or by our handling of its results, a TaxCalculationResult with a GATEWAY_ERROR is built instead.

    • Either way, the TaxCalculationResult is added onto the respective BillingProcessingRequest.

Note
There are only a few ways that items may end up with zero-valued taxes. If it is not a legitimate value from your tax processor, tax fields are also set to 0 when the Billing Event total is less than or equal to zero, or when items were not taxable.

Tax Calculation Processor Result Handler

The processor handler then sends the processed DTOs in a message to the TaxCalculationProcessorResultHandler.

  1. The second message handler then filters out all requests that have already been processed by it to avoid double-processing.

  2. Then, it splits the given requests depending on their tax calculation results and delegates finalization of each separate type of result to the DefaultTaxCalculationHandlerService:

    • Successful requests are prepared for Payment Processing, unless the billing.job.execute-only-tax-calculation configuration property is set to true.

      • If the aforementioned property is true, successful BillingProcessingRequests obtain the TAX_CALCULATED status and do not get sent via a message to a further destination.

    • Requests that saw a Gateway Error result have multiple paths.

      • A specific number of allowed retries can be configured for such results, via the billing.job.tax-calculation-error-retry-limit property.

        • The running counter of gateway error for a specific request is kept in BillingProcessingRequest#taxCalculationResult#gatewayErrorCount field.

        • If the counter for a request is below the configured limit, the request is added to the list which will be sent to reprocess in the following step.

        • If the counter is equal to the maximum allowed limit, the request is added to the DeadLetter queue

    • Logic exists to separately handle requests with a FAILURE result, but the out-of-box TaxService cannot differentiate regular failures from a GATEWAY_ERROR outcome, and so this status is deprecated in the flow.

      • However, if a client desired to restore its usage, such requests are not given a chance of retry - they are sent to the DeadLetter queue.

  3. Finally, entities from the successful DTOs are persisted to the database. Their BillingProcessingRequests are then sent on to continue the flow in the PaymentProcessorHandler Only the BillingProcessingRequests of the reprocessing group are persisted. Then, they are sent back to the previous step - TaxCalculationProcessingHandler.

Adding a new tax processor

To find out how a new tax processor can be added, follow the link to this Common Customizations page.