Post Directly to the G/L from AL Code

In this video, I show how you can post directly to the G/L from code without creating Journal Lines in the database, check it out:

https://youtu.be/B10D6X6Per4

In this video, Erik demonstrates how to post directly to the General Ledger (G/L) from AL code in Business Central — without manually creating journals, templates, or batches. This technique mirrors how the base application itself handles G/L postings internally, making it a clean and reliable approach for custom posting logic.

Learning from the Base Application

Before writing any custom code, Erik starts by examining how Business Central’s own base application handles G/L postings. This is always a good practice — if you’re unsure how to accomplish something, look at how it’s done in the base app.

The key code units to know about are:

  • Codeunit 80 — Sales-Post: handles posting of sales orders, sales invoices, sales credit memos, and other sales documents
  • Codeunit 90 — Purch.-Post: handles posting of purchase documents
  • Codeunit 12 — Gen. Jnl.-Post Line: the central code unit that actually posts journal lines to the G/L

Looking inside Codeunit 80, every G/L posting call uses the same pattern: it calls GenJnlPostLine.RunWithCheck(GenJournalLine). Whether it’s posting sales tax, rounding entries, or the main transaction amounts, the pattern is always the same — populate a Gen. Journal Line record in memory and pass it to RunWithCheck.

The critical insight here is that the Gen. Journal Line record is never stored in the database. It’s created in memory, populated with the necessary field values, and then passed directly to Codeunit 12 for posting. This means you don’t need to worry about journal templates, journal batches, or any of that infrastructure — you just create a record variable, fill in the fields, and post it.

The Basic Approach

The approach is straightforward:

  1. Declare a variable for Codeunit "Gen. Jnl.-Post Line"
  2. Declare a variable for Record "Gen. Journal Line"
  3. Initialize the line with Line.Init()
  4. Populate the required fields (posting date, document number, account type, account number, amount, etc.)
  5. Call GLPost.RunWithCheck(Line)

The Source Code

Erik builds out the example as a page extension on the Chart of Accounts page, adding a “Post something” action. Here’s the complete code:

pageextension 50100 CharOfA extends "Chart of Accounts"
{
    actions
    {
        addfirst(processing)
        {
            action(Test)
            {
                Caption = 'Post something';
                ApplicationArea = all;
                trigger OnAction()
                var
                    GLPost: Codeunit "Gen. Jnl.-Post Line";
                    Line: Record "Gen. Journal Line";
                begin
                    Line.Init();
                    Line."Posting Date" := TODAY();
                    Line."Document Type" := Line."Document Type"::" ";
                    Line."Document No." := 'X000004';
                    Line."Account Type" := Line."Account Type"::"G/L Account";
                    Line."Account No." := '10910';
                    Line.Description := 'Youtube Testing';
                    Line.Amount := 70;
                    GLPost.RunWithCheck(Line);

                    Line.Init();
                    Line."Posting Date" := TODAY();
                    Line."Document Type" := Line."Document Type"::" ";
                    Line."Document No." := 'X000004';
                    Line."Account Type" := Line."Account Type"::"G/L Account";
                    Line."Account No." := '10920';
                    Line.Description := 'Youtube Testing';
                    Line.Amount := -30;
                    GLPost.RunWithCheck(Line);

                    Line.Init();
                    Line."Posting Date" := TODAY();
                    Line."Document Type" := Line."Document Type"::" ";
                    Line."Document No." := 'X000004';
                    Line."Account Type" := Line."Account Type"::"G/L Account";
                    Line."Account No." := '10940';
                    Line.Description := 'Youtube Testing';
                    Line.Amount := -41;
                    GLPost.RunWithCheck(Line);
                end;
            }
        }
    }
}

Example 1: Using a Balance Account

The simplest approach is to use a balance account on the journal line. When you specify both an "Account No." and a "Bal. Account No.", Business Central will automatically create the offsetting entry. For a single debit/credit pair, this is the cleanest method:

Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000001';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10910';
Line.Description := 'Youtube Testing';
Line.Amount := 100;
Line."Bal. Account Type" := Line."Bal. Account Type"::"G/L Account";
Line."Bal. Account No." := '10920';
GLPost.RunWithCheck(Line);

This single call creates two G/L entries — a debit of 100 on account 10910 and a credit of 100 on account 10920. Erik notes that he tries to always use the balance account approach when possible.

Example 2: Posting Without a Balance Account

Sometimes you need more control — for instance, when you have two debits and one credit, or any scenario where a simple balance account won’t work. In that case, you remove the balance account fields and call RunWithCheck multiple times with the same document number:

// First entry: debit 123
Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000002';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10910';
Line.Description := 'Youtube Testing';
Line.Amount := 123;
GLPost.RunWithCheck(Line);

// Second entry: credit 123
Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000002';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10920';
Line.Description := 'Youtube Testing';
Line.Amount := -123;
GLPost.RunWithCheck(Line);

This gives you full control over each individual G/L entry while keeping them all under the same document number.

Example 3: Multi-Line Postings (Two Debits, One Credit)

The real power of this approach becomes apparent when you need more complex posting scenarios. For example, posting one debit of 70, offset by a credit of 30 to one account and a credit of 40 to another — three entries total instead of the four you’d get using balance accounts:

// Debit 70 to account 10910
Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000003';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10910';
Line.Description := 'Youtube Testing';
Line.Amount := 70;
GLPost.RunWithCheck(Line);

// Credit 30 to account 10920
Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000003';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10920';
Line.Description := 'Youtube Testing';
Line.Amount := -30;
GLPost.RunWithCheck(Line);

// Credit 40 to account 10940
Line.Init();
Line."Posting Date" := TODAY();
Line."Document Type" := Line."Document Type"::" ";
Line."Document No." := 'X000003';
Line."Account Type" := Line."Account Type"::"G/L Account";
Line."Account No." := '10940';
Line.Description := 'Youtube Testing';
Line.Amount := -40;
GLPost.RunWithCheck(Line);

Built-in Consistency Checks

One of the great benefits of posting through Codeunit 12 is the built-in consistency checking. Erik demonstrates this by deliberately making the amounts not balance (using -41 instead of -40 in the final example). When the amounts don’t sum to zero, Business Central throws an error:

“The transaction cannot be completed because it will cause inconsistencies in the G/L Entry table. Check when how the consistency function is used in the transaction to find the reason for the error.”

This is handled by Codeunit 12 itself, which verifies that the sum of all amounts in the G/L Entry table remains zero. If it detects an imbalance, the table is marked as inconsistent and the transaction cannot be committed. This is a critical safety net — the biggest mistake you can make when posting to the G/L is creating entries that don’t balance, and the system protects you from this.

Minimum Required Fields

Based on the examples in the video, the minimum fields you typically need to populate for a basic G/L posting are:

  • Posting Date — when the entry should be posted
  • Document Type — can be blank for simple entries
  • Document No. — a document number to group entries (could come from a number series)
  • Account Type — typically “G/L Account”
  • Account No. — the G/L account number
  • Description — a description for the entry
  • Amount — positive for debit, negative for credit

Depending on your scenario, you may need to specify additional fields such as currency code, posting groups, dimensions, and more.

A Note on Dimensions

Erik mentions that dimensions can make this approach significantly more complicated. If you need to create new dimension sets, you can’t entirely work in memory anymore — dimension sets need to be resolved and stored. This is a topic for a separate discussion, but it’s worth keeping in mind when planning your posting logic.

Summary

Posting directly to the G/L from AL code is simpler than it might seem. The key takeaways are:

  • Use Codeunit "Gen. Jnl.-Post Line" and its RunWithCheck function
  • Create Gen. Journal Line records in memory — no need for journal templates or batches
  • Use the balance account for simple debit/credit pairs
  • For complex multi-line postings, call RunWithCheck multiple times with the same document number
  • Codeunit 12 handles consistency checking automatically, ensuring your entries always balance
  • When in doubt, look at how the base application does it — Codeunit 80 (Sales-Post) and Codeunit 90 (Purch.-Post) are excellent references