How to support odd accounting periods in AL

In this video, I tackle a question from a viewer on how to deal with odd accounting periods, in this case, how to get G/L Entries to be easier to query when you have 13 periods in a single year. Check out the video:

https://youtu.be/x65UYn6NdwQ

In this video, Erik responds to a viewer’s question about how to support companies that run 13 accounting periods per year in Business Central. While Business Central natively supports configuring 13 periods (each consisting of 28 days, with one or two extra days at year-end for leap years), the challenge is that individual G/L entries don’t easily show which period they belong to. Erik walks through building an AL extension that tags each G/L entry with its accounting period number and automatically assigns the period when new entries are posted.

The Problem: 13 Accounting Periods

Some companies operate with 13 accounting periods per year instead of the standard 12 monthly periods. Each period consists of 28 days, with the final period absorbing the extra day (or two in a leap year). Business Central can handle this configuration without issues, but when you look at individual G/L entries, there’s no straightforward way to see which of the 13 periods an entry belongs to. You’d need to do manual date-specific filtering to figure it out.

The goal is to add an “Accounting Period” field to G/L entries that automatically calculates and stores the period number, making filtering and reporting much easier.

Step 1: Add a Field to the G/L Entry Table

The first step is to create a table extension that adds an “Accounting Period” integer field to the G/L Entry table:

tableextension 50100 "My G/L Entry" extends "G/L Entry"
{
    fields
    {
        field(50100; "Accounting Period"; Integer)
        {
        }
    }

    keys
    {
        key(AP; "Accounting Period")
        {
        }
    }
}

Erik chose an integer type rather than a code field (like “P1”, “P2”, etc.) because integers sort naturally, whereas code-based sorting can get complicated. He also adds a key on the Accounting Period field, since this field will be frequently used for filtering and sorting.

Step 2: Display the Field on the G/L Entries Page

Next, a page extension makes the new field visible on the General Ledger Entries list page:

pageextension 50100 "My G/L Entry List" extends "General Ledger Entries"
{
    layout
    {
        addbefore("Posting Date")
        {
            field("Accounting Period"; Rec."Accounting Period")
            {
                ApplicationArea = All;
            }
        }
    }

    actions
    {
        addlast(processing)
        {
            action("Add Accounting Period")
            {
                Caption = 'Add Accounting Period to Posted Entries';
                ApplicationArea = All;

                trigger OnAction()
                var
                    OddAcctManagement: Codeunit "Odd Accounting Management";
                begin
                    OddAcctManagement.UpdateAccountingPeriods();
                end;
            }
        }
    }
}

The field is placed before “Posting Date” for easy visibility. An action button is also added so users can retroactively calculate and assign accounting periods to existing entries.

Step 3: Build the Calculation Logic

The core logic lives in a codeunit. Since you need modify permissions on the G/L Entry table (which can’t be granted in an extension object directly), the codeunit declares explicit permissions:

codeunit 50100 "Odd Accounting Management"
{
    Permissions = tabledata "G/L Entry" = rm;

    internal procedure UpdateAccountingPeriods()
    var
        Entry: Record "G/L Entry";
    begin
        if Entry.FindSet() then
            repeat
                Entry."Accounting Period" := CalcPeriod(Entry."Posting Date");
                Entry.Modify();
            until Entry.Next() = 0;
    end;

    local procedure CalcPeriod(PostingDate: Date): Integer
    var
        DayOfYear: Integer;
        FirstOfYear: Date;
        Day: Integer;
        Month: Integer;
        Year: Integer;
        Period: Integer;
    begin
        Day := Date2DMY(PostingDate, 1);
        Month := Date2DMY(PostingDate, 2);
        Year := Date2DMY(PostingDate, 3);
        FirstOfYear := DMY2Date(1, 1, Year);

        DayOfYear := PostingDate - FirstOfYear;
        Period := DayOfYear div 28 + 1;

        if Period > 13 then
            exit(13)
        else
            exit(Period);
    end;
}

How the Calculation Works

The CalcPeriod function works as follows:

  1. Extract the year from the posting date using Date2DMY
  2. Construct the first day of that year using DMY2Date(1, 1, Year)
  3. Calculate the day of year by subtracting the first of the year from the posting date (this gives a zero-based day count)
  4. Divide by 28 (integer division) and add 1 to get the period number
  5. Cap the result at 13 — this handles the last day or two of the year that would otherwise calculate as period 14

Erik notes an important detail about the permissions declaration: only assign the permissions you actually need. In this case, the codeunit needs read and modify access to G/L Entry, but not insert or delete. This follows the principle of least privilege.

Step 4: Automatically Tag New Entries

The retroactive action is useful for existing data, but new entries should be tagged automatically. To accomplish this, Erik subscribes to an event on the General Journal Post Line codeunit (Codeunit 12), specifically the OnBeforeInsertGLEntry event:

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post Line", 'OnBeforeInsertGlEntry', '', true, true)]
local procedure OnBeforeInsertGLEntry(var GenJnlLine: Record "Gen. Journal Line"; var GLEntry: Record "G/L Entry")
begin
    GLEntry."Accounting Period" := CalcPeriod(GLEntry."Posting Date");
end;

The event subscriber parameters include SkipOnMissingLicense and SkipOnMissingPermission set to true, which are sensible defaults for event subscribers. While the event provides both the journal line and the G/L entry, only the entry is needed here — the posting date on the entry is what determines the period.

Testing the Event Subscriber

Erik demonstrates by posting a sales order dated April 22nd. After posting, he uses the “Find Entries” function to locate the resulting G/L entries and confirms they all show the correct accounting period (period 4 in this case). The automatic tagging works as expected.

Performance Considerations

Erik mentions that the key added to the table extension (key(AP; "Accounting Period")) is important for filtering performance. In a high-volume environment, you might consider replicating the posting date into a composite key with the accounting period to support more efficient sorted queries. However, doing so introduces additional complexity in the UI and data management, so for most scenarios the simple key is sufficient.

Summary

This extension demonstrates a clean approach to supporting 13 accounting periods in Business Central:

  • Table Extension: Adds an “Accounting Period” integer field and a supporting key to the G/L Entry table
  • Page Extension: Displays the field on the General Ledger Entries page with an action to backfill existing records
  • Codeunit: Contains the period calculation logic (day-of-year divided by 28, capped at 13) and handles both retroactive updates and automatic tagging via an event subscriber
  • Permissions: Explicitly declared with only the minimum required access (read + modify)

For a complete solution, you’d likely also want to create API pages that expose the accounting period field, making it available for Power BI reporting and other integrations. Erik hints that this could be the subject of a future video.