General Ledger sits underneath every other financial module, which makes GL extensions unusually high-stakes: a bug here doesn't stay contained to one screen, it shows up in every module that posts a batch. Most GL customization work is about allocation logic and validation, not the ledger structure itself, which is deliberately left alone in almost every implementation.
Core DACs and screens
Batch and GLTran are the two DACs that matter — a batch is the posting unit, and GLTran rows are its individual debit/credit lines. Manual entry happens on the Journal Transactions screen (GL301000), and the graph is JournalEntry. Recurring and allocation batches run through their own scheduled processes but ultimately produce the same Batch/GLTran structure as manual entries.
Extension points
A PXGraphExtension<JournalEntry> is where custom validation on batch release typically lives — enforcing a segment combination rule, requiring a custom field before a batch can post, or blocking postings to a closed sub-period the stock period-close logic doesn't already catch for a given scenario. Custom allocation logic (splitting a cost center's overhead across departments by headcount, for example) is usually its own graph or graph extension on the Allocations screen rather than a hack layered onto JournalEntry.
Acumatica checks the batch's financial period against open/closed status and against the module's own period-close state before allowing posting. A custom validation that re-checks period status independently risks disagreeing with the platform's own check after a version upgrade changes internal logic slightly. If you need an additional restriction (say, a "soft close" that only finance can override), add a condition on top of the existing check rather than reimplementing period validation from scratch.
A realistic scenario: custom allocation rule
A recurring GL ask is allocating a shared cost (rent, IT, insurance) across departments using a driver that isn't one of the stock allocation methods — say, square footage occupied rather than a fixed percentage or headcount. This is implemented as a custom allocation processing screen or extension that reads the driver values from a small custom DAC and produces GLTran lines through the standard batch creation API, rather than hand-writing rows into the table.
public class SquareFootageAllocation : PXGraph<SquareFootageAllocation>
{
public PXSelect<AllocationDriver> Drivers;
public virtual void RunAllocation(string sourceAccount, string sourceSubID, decimal totalAmount)
{
JournalEntry je = PXGraph.CreateInstance<JournalEntry>();
Batch batch = je.BatchModule.Insert(new Batch { Module = BatchModule.GL });
decimal totalSqFt = Drivers.Select().RowCast<AllocationDriver>().Sum(d => d.SquareFeet ?? 0);
foreach (AllocationDriver d in Drivers.Select())
{
decimal share = totalSqFt == 0 ? 0 : totalAmount * (d.SquareFeet ?? 0) / totalSqFt;
GLTran line = new GLTran
{
AccountID = d.TargetAccountID,
SubID = d.TargetSubID,
DebitAmt = share
};
je.GLTranModuleBatNbr.Insert(line);
}
je.Save();
}
}
Building the batch through JournalEntry's own insert path keeps the allocation subject to the same validation, numbering, and audit trail as a manually entered journal entry, rather than being a special case that finance can't reconcile against everything else.
Reversing and recurring batches
If the allocation needs to run every period, look at whether Acumatica's recurring transactions feature covers it before building a custom scheduled process — recurring batches already handle period advancement, reversal, and history in a way a bespoke scheduler would need to reimplement.
Testing considerations
Test any GL extension against a batch that spans multiple branches or ledgers if the client uses either — inter-branch and multi-ledger postings have their own balancing rules, and a validation written against a single-branch test batch can pass in dev and fail the first time it hits a real multi-entity posting.
Wrapping up
GL extensions carry more blast radius than almost any other module because every subledger eventually posts through it. Route allocation logic through the standard batch creation API, layer custom validation on top of existing period and posting checks rather than replacing them, and test against multi-branch data before calling it done.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.