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