Acumatica · Customization

Acumatica Projects Module — Extension Patterns

Acumatica Projects Module — Extension Patterns is one of those Acumatica customisations that every team eventually needs and almost no team does well the first time.

John Kihiu12 min read

The Projects module (PM) is where Acumatica's flexibility gets tested hardest, because "project accounting" means something different at every client — cost-plus billing, fixed-fee milestones, T&M with markup, or some blend that changes by contract. Extension work here is less about new DACs and more about the billing and budget engines being configured, and occasionally extended, to match a specific contract structure.

Core DACs and screens

PMProject is the project record, PMTask breaks it into phases or work items, and PMBudget holds the budgeted vs. actual figures per task and cost code. Billing rules attach to PMBillingRule and drive what actually appears on an invoice generated from project transactions. The core graph is ProjectEntry, with a separate billing engine graph invoked when invoices are generated from accumulated project transactions (PMTran).

Extension points

A PXGraphExtension<ProjectEntry> is the natural home for custom budget control rules — for example, blocking a transaction from posting to a task once actual cost exceeds budget by more than a configurable percentage, which isn't a stock hard-stop by default. Custom fields on PMProject or PMTask (a custom project category, an external project-code cross-reference) are low-risk cache extensions; anything touching how revenue gets recognized or how a task rolls up into project totals needs more care because those numbers feed GL and, often, the client's external reporting.

Budget control belongs on the task, not the invoice

It's tempting to catch budget overruns at billing time, but by then the cost has already been incurred — the useful checkpoint is at transaction entry (timesheet, AP bill, PO receipt allocated to the project), where a warning or hard stop can actually change behavior. Build budget validation into the transaction entry graphs that write to PMTran, not into the billing engine.

A realistic scenario: milestone-based billing hold

A common PM customization: a project shouldn't generate its next milestone invoice until a prior deliverable task is marked complete, which isn't something the stock billing rule engine expresses directly. This is typically a small extension on the billing engine that checks task completion status before allowing the billing rule to produce a line for a dependent milestone.

C# · PROJECT BILLING EXTENSION
public class ProjectBilling_MilestoneHold_Extension : PXGraphExtension<ProjectBillingEngine>
{
    protected virtual bool CanBillMilestone(PMTask milestoneTask)
    {
        if (milestoneTask.DependentTaskID == null) return true;

        PMTask dependency = PXSelect<PMTask,
            Where<PMTask.taskID, Equal<Required<PMTask.taskID>>>>
            .Select(Base, milestoneTask.DependentTaskID).RowCast<PMTask>().FirstOrDefault();

        return dependency?.Status == "Completed";
    }
}

Note the exact graph and field names for the billing engine vary somewhat by Acumatica version and whether the client is on the newer Project Billing screens — verify against the installed version's PM object model before assuming this maps one-to-one; the dependency modeling in particular (a DependentTaskID-style field) is more often a custom addition than a stock field.

Revenue recognition considerations

If the client recognizes revenue on percentage-of-completion rather than as-billed, any customization touching task cost or budget also touches revenue recognition calculations, since POC methods typically compute recognized revenue from cost-to-date against budgeted cost. Changing budget rollup logic without accounting for this is one of the more common ways a PM customization quietly breaks the client's revenue recognition numbers without an obvious error anywhere.

Testing considerations

Test against a project with multiple tasks, at least one over-budget task, and a full billing cycle (accumulate transactions, run billing, generate the AR invoice) rather than a single task in isolation — PM bugs concentrate at the boundaries between task rollup, budget checks, and invoice generation.

Wrapping up

Projects module work rewards understanding the client's actual contract terms before writing anything — the billing rule engine is flexible enough to express most of them through configuration, and custom code should fill only the specific gap (a dependency, a hold condition) that configuration can't reach.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.