bcquality/community/knowledge/data-modeling/document-line-prices-follow-prices-including-vat.good.al
Kilian Seizinger dda13d5150 knowledge(data-modeling): cover prepayment, service line and ExclTax helper in Prices Including VAT article
Extend the community article on sales/purchase line prices following the
header's Prices Including VAT:

- Group the line fields: fields that follow the header flag (Unit Price,
  Direct Unit Cost, Line Amount, discounts, Prepmt. Line Amount,
  Prepmt. Amt. Inv., Prepmt Amt to Deduct, Prepmt Amt Deducted) versus
  fields with a fixed basis (Amount, VAT Base Amount, Prepayment Amount
  are net; Amount Including VAT, Prepmt. Amt. Incl. VAT,
  Prepmt. Amount Inv. Incl. VAT are gross).
- Add the rule to combine only fields of the same group, with BaseApp's
  UpdatePrepmtAmounts as the correct example.
- Add the misleading-name case: CalculateOutstandingAmountExclTax on
  Sales Line and Purchase Line is based on Line Amount and includes VAT
  on a Prices Including VAT document. BaseApp pairs it only with
  Prepmt. Line Amount (same basis); extension code that treats it as net
  is wrong.
- Mention that Service Line uses the same caption switch and
  UpdateVATAmounts split for Unit Price and Line Amount.
- Samples: add GetOutstandingNetAmount (bad: trusts the helper's name;
  good: takes the uninvoiced share of Amount).
- al-data-modeling-review: widen the scope and the worklist rule to
  service lines, the extra prepayment fields and
  CalculateOutstandingAmountExclTax, and exclude code that only combines
  fields of the same group.

Verified against BCApps W1 BaseApp (SalesLine, PurchaseLine, ServiceLine,
SalesHeader, Sales Line CaptionClass Mgmt).

Refs #151
2026-10-01 12:07:39 +02:00

52 lines
2.4 KiB
AL

// Demonstration only; independently authored, not copied from BaseApp.
codeunit 50640 "Sales Doc. VAT Basis Good"
{
procedure GetNetAndGrossTotals(SalesHeader: Record "Sales Header"; var NetTotal: Decimal; var GrossTotal: Decimal)
var
SalesLine: Record "Sales Line";
begin
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
// Amount is always net and "Amount Including VAT" always gross, after line and
// invoice discounts, whatever the header's "Prices Including VAT" says.
SalesLine.CalcSums(Amount, "Amount Including VAT");
NetTotal := SalesLine.Amount;
GrossTotal := SalesLine."Amount Including VAT";
end;
procedure GetOutstandingNetAmount(SalesLine: Record "Sales Line"): Decimal
var
SalesHeader: Record "Sales Header";
Currency: Record Currency;
begin
if SalesLine.Quantity = 0 then
exit(0);
SalesHeader.Get(SalesLine."Document Type", SalesLine."Document No.");
Currency.Initialize(SalesHeader."Currency Code");
// Amount is net after line and invoice discounts on every document, so the
// uninvoiced share needs no VAT conversion.
exit(Round(
SalesLine.Amount * (SalesLine.Quantity - SalesLine."Quantity Invoiced") / SalesLine.Quantity,
Currency."Amount Rounding Precision"));
end;
procedure SetUnitPriceFromNetSourcePrice(var SalesLine: Record "Sales Line"; NetSourcePrice: Decimal)
var
SalesHeader: Record "Sales Header";
Currency: Record Currency;
UnitPrice: Decimal;
begin
SalesHeader.Get(SalesLine."Document Type", SalesLine."Document No.");
// Scope of the conversion below: Normal VAT only; Full VAT and Sales Tax need their own handling.
SalesLine.TestField("VAT Calculation Type", SalesLine."VAT Calculation Type"::"Normal VAT");
Currency.Initialize(SalesHeader."Currency Code");
UnitPrice := NetSourcePrice;
// "Unit Price" is gross on a Prices Including VAT document: convert the net source price into that basis.
if SalesHeader."Prices Including VAT" then
UnitPrice := Round(NetSourcePrice * (1 + SalesLine."VAT %" / 100), Currency."Unit-Amount Rounding Precision");
SalesLine.Validate("Unit Price", UnitPrice);
SalesLine.Modify(true);
end;
}