web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

Community site session details

Community site session details

Session Id :
Small and medium business | Business Central, N...
Suggested Answer

External Tax engine setup and testing for sales order

(3) ShareShare
ReportReport
Posted on by 12
My goal was to calculate all sales tax using our external tax engine instead of Business Central’s native calculation. The idea was that whenever a sales order is created or updated (adding lines, changing quantity, price, or ship-to address), Business Central should delegate the tax calculation to the external tax engine, receive the calculated tax amounts (state, local, total), and then reflect those values in the standard sales order totals (total tax, amount including tax) without using any custom fields.

Could some one share some code for testing the use case  
Samples request we will configure :-
{
  "invoice_general": {
    "taxpayer": "Test",
    "customer_no": "123",
    "customer_name": "Test",
    "invoice_no": "9090",
    "invoice_date": "12/25/2025",
    "transaction_date": "",
    "document_type": "SI",
    "origin_street": "50 Dr",
    "origin_city": "Quakers",
    "origin_state": "NJ",
    "origin_zip": "27631",
    "origin_country": "USA",
    "shipping_amt": "12000",
  },
  "invoice_line": [
    {
      "invoice_line": "1",
      "transaction_data": "",
      "so_no": "",
      "quantity": "1",
      "sales_amount": "10000",
      "discount_type": "$",
      "discount_value": "0",
      "destination_street": "50 Warrimoo Dr",
      "destination_city": "Quakers Hill",
      "destination_state": "NJ",
      "destination_zip": "27631",
      "destination_country": "USA",
     
    }
  ]
}

Sample response
{
  "summary": {
    "invoice_no": "9090",
    "state": "NEW JERSEY",
    "total_sales_tax": "662.5",
    "state_sales_tax": "662.5",
    "local_sales_tax": "0"
  },
  "line_details": [
    {
      "invoice_line": "1",
      "state": "NEW JERSEY",
      "total_sales_tax": "662.5",
      "state_sales_tax": "662.5",
      "local_sales_tax": "0",
      "tax_rate_state": "6.62",
      "tax_rate_local": "0",
      "tax_rate_total": "6.62",
    }
  ]
}

With the repsone we need to map sales line items with proper tax values and should reflect totals correctly and also in line items
I have the same question (1)
  • VK-06011525-0 Profile Picture
    12 on at
    As you are all the best minds in the community i would request to provide me a suggestion on how to implement it
    as there is no proper  doccumentation to refer would request your inputs
  • Suggested answer
    Assisted by AI
    OussamaSabbouh Profile Picture
    6,754 on at
    Hello ,
    What you want (external engine calculates tax, and standard “Total Tax / Amount Incl. Tax” updates with no custom fields) is only doable if you hook into BC’s tax calculation pipeline. You can’t “just set the totals” on the Sales Order page—those totals are derived from lines + the tax engine buffers. So the supported pattern is: call your API during tax calculation and feed the result back into the same temp tax lines BC uses (Sales Tax / VAT amount lines), so Statistics/totals/Posting Preview all use it.
    Basic, testable approach (recommended)
    Create one codeunit that:
    Builds request JSON from Sales Header + Sales Line
    Calls your endpoint with HttpClient
    Parses response JSON
    Writes tax back into the standard temp tax buffer used by the document (Sales Tax Amount Lines / VAT Amount Lines depending on your localization/setup)
    Subscribe to the integration event in the base tax calculation codeunit (US Sales Tax: search for events in codeunit “Sales Tax Calculate” / VAT: search in VAT calculation codeunits) and replace/override the calculated tax lines with your own.
    (Optional for testing) Add an action “Recalculate External Tax” on Sales Order to force recalculation, but the real goal is to trigger on quantity/price/ship-to changes via the standard recalc flow.
    Minimal sample code (HTTP + JSON + mapping skeleton)
    This is intentionally “testing-grade”: it shows the call + parsing + where you’d map per-line tax. You still need to plug it into the correct tax calculation event for your version.
    Copy code
    Al
    codeunit 50100 "External Tax Mgt."
    {
        SingleInstance = true;
     
        procedure GetTaxForSalesOrder(var SalesHeader: Record "Sales Header")
        var
            SalesLine: Record "Sales Line";
            Client: HttpClient;
            Request: HttpRequestMessage;
            Response: HttpResponseMessage;
            Content: HttpContent;
            JsonReq: JsonObject;
            JsonGen: JsonObject;
            JsonLines: JsonArray;
            JsonLine: JsonObject;
            JsonResp: JsonObject;
            Summary: JsonObject;
            LineDetails: JsonArray;
            LineObj: JsonObject;
            InvoiceNoTxt: Text;
            TotalTaxDec: Decimal;
            i: Integer;
        begin
            SalesHeader.TestField("No.");
     
            // Build request JSON (your sample shape)
            JsonGen.Add('taxpayer', 'Test');
            JsonGen.Add('customer_no', SalesHeader."Sell-to Customer No.");
            JsonGen.Add('customer_name', SalesHeader."Sell-to Customer Name");
            JsonGen.Add('invoice_no', SalesHeader."No.");
            JsonGen.Add('invoice_date', Format(Today(), 0, 9)); // adjust
            JsonGen.Add('document_type', 'SO');
            JsonGen.Add('shipping_amt', Format(SalesHeader."Shipping Amount", 0, 9)); // adjust if needed
     
            JsonReq.Add('invoice_general', JsonGen);
     
            SalesLine.SetRange("Document Type", SalesHeader."Document Type");
            SalesLine.SetRange("Document No.", SalesHeader."No.");
            SalesLine.SetRange(Type, SalesLine.Type::Item);
     
            if SalesLine.FindSet() then
                repeat
                    Clear(JsonLine);
                    JsonLine.Add('invoice_line', Format(SalesLine."Line No."));
                    JsonLine.Add('quantity', Format(SalesLine.Quantity, 0, 9));
                    JsonLine.Add('sales_amount', Format(SalesLine.Amount, 0, 9));
                    // destination fields: map from Ship-to on header as needed
                    JsonLines.Add(JsonLine);
                until SalesLine.Next() = 0;
     
            JsonReq.Add('invoice_line', JsonLines);
     
            // Call API
            Content.WriteFrom(JsonReq.ToString());
            Content.GetHeaders().Clear();
            Content.GetHeaders().Add('Content-Type', 'application/json');
     
            Request.Method('POST');
            Request.SetRequestUri('https://your-tax-endpoint/calc'); // TODO
            Request.Content := Content;
     
            Client.Send(Request, Response);
            if not Response.IsSuccessStatusCode() then
                Error('External tax engine call failed. Status: %1', Response.HttpStatusCode());
     
            Response.Content().ReadAs(InvoiceNoTxt);
            if not JsonResp.ReadFrom(InvoiceNoTxt) then
                Error('Invalid JSON response from tax engine.');
     
            // Parse summary
            if JsonResp.Get('summary', Summary) then begin
                Summary.Get('invoice_no', InvoiceNoTxt);
                Summary.Get('total_sales_tax', TotalTaxDec);
                // TotalTaxDec is what you need to push into BC’s tax buffers
            end;
     
            // Parse line details (per-line tax mapping)
            if JsonResp.Get('line_details', LineDetails) then begin
                for i := 0 to LineDetails.Count() - 1 do begin
                    LineDetails.Get(i, LineObj);
                    // LineObj has invoice_line + tax amounts/rates
                    // You’ll map that back to the Sales Line and/or the temp tax lines.
                end;
            end;
     
            // IMPORTANT:
            // Do NOT try to “set totals on the page”.
            // You must write these amounts into the standard tax calculation temp records
            // in the tax calculation event subscriber (next snippet).
        end;
    }
    Where the “real” integration happens
    You need to subscribe to the tax calc event and replace the temp tax lines BC uses. The exact event names differ by version/localization, so the practical step is:
    Open Base App source
    Search for IntegrationEvent inside the tax calculation codeunit used in your tenant (US Sales Tax vs VAT)
    Pick the event that exposes the temporary tax amount lines and override them
    Example shape (you will need to align names to your version):
    Copy code
    Al
    codeunit 50101 "External Tax Subscribers"
    {
        [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales Tax Calculate", 'OnAfterCalculateSalesTax', '', false, false)]
        local procedure OverrideSalesTax(var SalesHeader: Record "Sales Header"; var TempSalesTaxAmountLine: Record "Sales Tax Amount Line" temporary)
        var
            ExtTax: Codeunit "External Tax Mgt.";
        begin
            // 1) Call external engine
            ExtTax.GetTaxForSalesOrder(SalesHeader);
     
            // 2) Clear TempSalesTaxAmountLine
            // 3) Insert your own tax lines per Sales Line / jurisdiction
            // Result: Statistics / totals / Posting Preview use YOUR numbers.
        end;
    }
    Reality check (important)
    If you’re on US Sales Tax, BC traditionally does not show tax in the document totals the same way VAT does (it shows in Statistics and applies on posting). If you want VAT-style “Amount Incl. Tax” behavior, that’s a different functional requirement and often means using VAT-like handling (or accepting the US behavior).
    If you want it to work for Posting Preview, you must integrate at the posting/tax calculation level (events), not only on the page.
     
    Regards,
    Oussama 
  • Suggested answer
    YUN ZHU Profile Picture
    95,937 Super User 2025 Season 2 on at
    Manually entering tax rates in BC is very complicated, and I have experienced both the W1 and US versions, with different code handling on each side.
    However, the main approach is quite simple: let BC calculate the tax rate automatically first, and then modify the VAT Amount field on the page below. You can check the standard code.
    PS: How to adjust the VAT amount in Dynamics 365 Business Central (VAT Difference)
     
    Hope this can give you some hints.
    Thanks.
    ZHU
  • André Arnaud de Calavon Profile Picture
    301,371 Super User 2025 Season 2 on at
     
    Please note that volunteers on this forum have focus on specific products. You tagged several people, including me. My focus is not on Business Central. My daily work is with Dynamics 365 F&O. You also tagged a person who is not active anymore on this forum.
     
    There are plenty volunteers available with knowledge on BC. There is no need to tag them. The volunteers do spend their spare time in helping out on the community. For that reason, a first reply can take several hours to a day. 
     
    I do hope the replies above are already helpful for you. 

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

News and Announcements

Season of Giving Solutions is Here!

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > Small and medium business | Business Central, NAV, RMS

#1
OussamaSabbouh Profile Picture

OussamaSabbouh 1,688

#2
Khushbu Rajvi. Profile Picture

Khushbu Rajvi. 784 Super User 2025 Season 2

#3
YUN ZHU Profile Picture

YUN ZHU 595 Super User 2025 Season 2

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans