Automating Prepayment Handling in Business Central – Part 2
In Part 1, we explored the core logic of handling prepayment invoices in Business Central using AL. In this part, we will dive deeper into the practical implementation, focusing on how prepayments are applied, invoices are generated, and item charges are assigned. This blog will break down the logic in a simplified, yet complete way.
Why Automate Prepayments?
In real-world business scenarios, companies often pay vendors before the invoice is fully posted. Handling these prepayments manually is tedious and error-prone:
- a. Matching payments with invoices can be cumbersome.
- b. Multiple vendors in one purchase order complicate tracking.
- c. Unapplying and reapplying payments manually is risky.
Our AL code automates this process: it creates purchase invoices, handles prepayment lines, applies payments, and ensures that item charges are correctly assigned.
1. Event Subscriber: Trigger After Posting Purchase Document
The automation starts with an event subscriber that triggers after a purchase document is posted:
[EventSubscriber(ObjectType::Codeunit, Codeunit::”Purch.-Post”, ‘OnAfterPostPurchaseDoc’, ”, false, false)]
procedure OnAfterPostPurchaseDocHandler(var PurchaseHeader: Record “Purchase Header”)
var
Rec_PreppaymentLines: Record PrepaymentLinesandPayment;
PurchInvoiceHeader: Record “Purchase Header”;
VendorInvoiceMap: Dictionary of [Code[20], Code[20]];
VendorNo: Code[20];
begin
// Collect unique vendors
Rec_PreppaymentLines.SetRange(“Purchase Order No.”, PurchaseHeader.”No.”);
Clear(VendorList);
if Rec_PreppaymentLines.FindSet() then
repeat
if not VendorList.Contains(Rec_PreppaymentLines.”Vendor No.”) then
VendorList.Add(Rec_PreppaymentLines.”Vendor No.”);
until Rec_PreppaymentLines.Next() = 0;
// Process each vendor
foreach VendorNo in VendorList do begin
// Create or reuse invoice
if VendorInvoiceMap.ContainsKey(VendorNo) then
PurchInvoiceHeader.Get(PurchInvoiceHeader.”Document Type”::Invoice, VendorInvoiceMap.Get(VendorNo))
else begin
PurchInvoiceHeader := CreatePurchaseInvoiceHeader(VendorNo);
VendorInvoiceMap.Add(VendorNo, PurchInvoiceHeader.”No.”);
end;
// Handle prepayment lines
Rec_PreppaymentLines.SetRange(“Purchase Order No.”, PurchaseHeader.”No.”);
Rec_PreppaymentLines.SetRange(“Vendor No.”, VendorNo);
if Rec_PreppaymentLines.FindSet() then
repeat
HandlePrepaymentLine(Rec_PreppaymentLines, PurchInvoiceHeader);
until Rec_PreppaymentLines.Next() = 0;
end;
end;
Key Takeaways:
- a. Collects unique vendors from prepayment lines.
- b. Creates a new purchase invoice for each vendor if not already existing.
- c. prepayment lines for each vendor automatically.
2. Handling Prepayment Lines
The HandlePrepaymentLine procedure ensures each prepayment is processed correctly:
procedure HandlePrepaymentLine(var PrepaymentLine: Record PrepaymentLinesandPayment; var PurchHeader: Record “Purchase Header”)
var
PaymentEntryNo: Integer;
begin
// Unapply previous payments if any
PaymentEntryNo := UnapplyPaymentFromPrepayInvoice(PrepaymentLine.”Prepayment Invoice”);
if PaymentEntryNo = 0 then
Error(‘Failed to unapply Vendor Ledger Entry for Document No. %1’, PrepaymentLine.”Prepayment Invoice”);
// Create credit memo and invoice line
CreateCreditMemoLine(PrepaymentLine, PrepaymentLine.”Prepayment Invoice”);
CreatePurchaseInvoiceLine(PurchHeader, PrepaymentLine);
// Assign item charges and post
AssignItemChargeToReceiptAndPost(PrepaymentLine, PurchHeader.”No.”, PrepaymentLine.”Purchase Order No.”);
end;
Highlights:
- a. Automatically unapplies previous payments to avoid double application.
- b. Creates credit memo lines and purchase invoice lines.
- c. Assigns item charges from purchase receipts to invoices.
3. Applying Payments to Invoice
The ApplyPaymentToInvoice procedure ensures the invoice is linked with the correct prepayment:
procedure ApplyPaymentToInvoice(InvoiceNo: Code[20]; PaymentEntryNo: Integer)
var
InvoiceEntry, VendLedEntry: Record “Vendor Ledger Entry”;
ApplyPostedEntries: Codeunit “VendEntry-Apply Posted Entries”;
ApplyUnapplyParameters: Record “Apply Unapply Parameters”;
begin
InvoiceEntry.SetRange(“Document No.”, InvoiceNo);
InvoiceEntry.SetRange(Open, true);
if InvoiceEntry.FindFirst() then begin
VendLedEntry.SetRange(“Entry No.”, PaymentEntryNo);
if VendLedEntry.FindFirst() then begin
InvoiceEntry.Validate(“Amount to Apply”, InvoiceEntry.”Remaining Amount”);
VendLedEntry.Validate(“Amount to Apply”, -InvoiceEntry.”Remaining Amount”);
ApplyUnapplyParameters.”Document No.” := VendLedEntry.”Document No.”;
ApplyPostedEntries.Apply(InvoiceEntry, ApplyUnapplyParameters);
end;
end;
end;
Benefits:
- a. Handles partial and full payments automatically.
- b. Ensures accurate ledger entries and reconciliation.
4. Assigning Item Charges
Item charges from receipts are automatically assigned to invoices:
procedure AssignItemChargeToReceiptAndPost(var PrepaymentLine: Record PrepaymentLinesandPayment; PurchInvoiceNo: Code[20]; PurchaseOrderNo: Code[20])
var
PurchRcptLine: Record “Purch. Rcpt. Line”;
ItemChargeAssign: Record “Item Charge Assignment (Purch)”;
begin
PurchRcptLine.SetRange(“Order No.”, PrepaymentLine.”Purchase Order No.”);
PurchRcptLine.SetFilter(Quantity, ‘>0’);
PurchRcptLine.SetRange(“No.”, PrepaymentLine.”Item No.”);
if PurchRcptLine.FindSet() then
repeat
ItemChargeAssign.Init();
ItemChargeAssign.”Document No.” := PurchInvoiceNo;
ItemChargeAssign.”Applies-to Doc. No.” := PurchRcptLine.”Document No.”;
ItemChargeAssign.”Item Charge No.” := PrepaymentLine.”Item Charge”;
ItemChargeAssign.”Qty. to Assign” := 1;
ItemChargeAssign.”Amount to Assign” := PrepaymentLine.Amount;
ItemChargeAssign.Insert(true);
until PurchRcptLine.Next() = 0;
end;
Outcome:
- a. Automates linking item charges to the correct invoice lines.
- b. Eliminates manual errors in item charge assignments.
To conclude, by implementing this automation:
- a. Prepayment handling becomes fully automated.
- b. Reduces manual errors in posting, payment application, and invoice creation.
- c. Supports multiple vendors and complex purchase orders seamlessly.
- d. Ensures auditability and correct ledger entries in Business Central.
This code can save significant time for finance teams while keeping processes accurate and transparent.
We hope you found this blog useful, and if you would like to discuss anything, you can reach out to us at transform@cloudfronts.com
