Acumatica Route Screen — A Deep Dive is the screen the user actually lives in. Every other piece of Acumatica — the workflows, the reports, the integrations — exists to feed this screen or be fed by it. If you do not understand this screen, you do not understand the module. This guide is the field-tested walkthrough I wish someone had given me the first time I had to customise it.
What the screen actually does
Most Acumatica screens look similar at first glance — a form view, a tab, a grid. The difference is in the document lifecycle, the events that fire as you move through the tabs, and the state machine that controls what is editable. This screen is a document-entry screen: each row in the grid is a line item, the header aggregates the lines, and the release action posts the document to the sub-ledger.
Whenever you are debugging a document-entry screen, look at the header first. The header carries the document type, status, customer, dates, and totals. The lines are downstream of the header. If the lines look wrong, the header is the first place to look — most "line bug" tickets are actually "header state bug" tickets in disguise.
The header fields that matter
Not every field on the header is used in every workflow. The fields that are reliably used in customisations are:
| Field | Why it matters | Common customisation |
|---|---|---|
| Document type | Drives status, numbering, posting | Add a custom document type for a tenant-specific flow |
| Reference Nbr | External identifier; idempotency key | Stamp with an order ID from an external system |
| Status | Controls what is editable; part of the workflow | Auto-set a custom status from a business event |
| Date | Drives period, ageing, FX | Default to the customer's order date |
| Customer / Vendor | Drives pricing, terms, GL accounts | Filter by a custom field on the customer record |
| Branch | Drives ledgers, tax zones, restrict users | Default to the current branch |
protected void _(Events.FieldDefaulting<CRLead, CRLead.docDate> e)
{
var row = e.Row;
if (row?.CustomerID != null)
{
var customer = (Customer)PXSelect<Customer,
Where<Customer.bAccountID, Equal<Required<Customer.bAccountID>>>>
.Select(Base, row.CustomerID);
if (customer?.LastOrderDate != null)
{
e.NewValue = customer.LastOrderDate;
e.Cancel = true;
}
}
}
For deeper coverage of DAC fields and the defaulting pattern, see the DAC fundamentals article and the field attributes guide.
The tabs, and what they each do
The tab structure on this screen is more than layout — it is the navigation contract. Each tab loads a related set of records; each has its own data view; each can be hidden, shown, or disabled based on the header state. The conventions for the tabs on this screen, from left to right:
- Document details — the header fields. Always visible; always editable in
Holdstate. - Lines / Items — the line items. The big grid. Where most of the work happens.
- Taxes — per-line or per-document tax amounts. Auto-calculated but manually overridable.
- Addresses — ship-to / bill-to. Defaults from the customer; can be overridden per document.
- Financial details — terms, branch, GL accounts, retainage. Usually for advanced customisation.
- Approvals — workflow status, current approver, approval history.
- Activities — emails, tasks, events linked to the document.
- Files — attachments. The screen is also a file store.
If you add a custom tab and the tab disappears after an upgrade, the most common cause is the tab ID not being a unique key. Always use a namespace-prefixed tab ID (e.g. tabUsrCustom not tabCustom1). Acumatica uses the ID to identify the tab across versions.
The lines grid: the one you will spend the most time on
The lines grid is where the work is. The patterns that matter:
protected void _(Events.RowSelected<CRLeadLine> e)
{
var row = e.Row;
if (row == null) return;
var doc = Base.Document.Current;
if (doc == null) return;
PXUIStateAttribute.SetEnabled<CRLeadLine.qty>(
e.Cache, row, doc.Hold == false && doc.Status == "N");
}
For the full event pattern catalog, see the events explained article.
The toolbar actions
The toolbar on this screen carries the document lifecycle actions. The standard ones are Hold, Remove Hold, Submit for Approval, Approve, Reject, Release, Copy, Print, Email, and Cancel. Each is a graph action with a server-side handler. Adding a custom action to the toolbar is a common customisation:
public PXAction<CRLead> SubmitToCustomWorkflow;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Submit to Custom Workflow")]
protected void submitToCustomWorkflow()
{
var row = Base.Document.Current;
if (row == null) return;
Base.Document.Cache.SetValueExt<CRLead.Status>(row, "C");
Base.Document.Cache.SetValueExt<CRLead.CustomField>(row, "SUBMITTED");
Base.Document.Update(row);
Base.Actions.PressSave();
}
For more on actions and the workflow glue, see the workflow engine guide.
Release: what actually happens
The Release action is the most important button on the screen. It is also the most dangerous — once released, a document cannot be edited in place. Most customisations need to either add a pre-release validation or extend what release does (e.g. stamp a custom field, post to a custom table).
[PXOverride]
public virtual void Release(CRLead doc)
{
if (doc.CuryOrderTotal <= 0)
throw new PXException("Cannot release a document with zero total.");
Base.Release(doc);
var ext = doc.GetExtension<CRLeadExt>();
if (ext != null)
{
Base.Document.Cache.SetValueExt<CRLeadExt.usrReleasedAt>(doc, DateTime.UtcNow);
Base.Document.Update(doc);
Base.Actions.PressSave();
}
}
For more on overrides, see the PXGraph vs PXGraphExtension guide.
Performance on this screen
This screen's performance is dominated by three things:
- The number of lines (rows in the grid). 10 lines render instantly; 1,000 lines take 4 seconds; 10,000 lines take 30 seconds.
- The number of event handlers firing on each render. Every
RowSelectedon the lines fires per row per render. - The number of
PXSelectcalls in those handlers. Each one is a database round-trip.
Before you change anything, profile. Add timing logs to the event handlers, run the screen on production-scale data, and look at where the time goes. Optimising the wrong handler is the most expensive mistake.
For the full performance playbook, see the performance tuning guide and the SQL Server indexing guide.
Common customisation recipes
Three customisations I have implemented on this screen repeatedly, and the patterns they follow:
- Default the branch from the user. If the user is restricted to a single branch, default the branch field to that branch and hide the selector. Saves clicks, prevents misposting.
- Add a custom approval step. If the standard approval map does not match the business process, add a custom approval step. Configure in the workflow, then add a button on the toolbar that triggers it.
- Stamp an external reference. If the document is created from an external system (e-commerce, portal, EDI), set the
UsrExternalReffield on creation. Use it as the idempotency key.
For related reading, see the graph extension patterns, the cache attach pitfalls, and the events explained.
Wrapping up
That is the screen, top to bottom. The patterns here apply to most document-entry screens in Acumatica — the same structure, the same lifecycle, the same customisation playbook. Internalise the pattern, and the next screen you are asked to customise will be a 2-day job instead of a 2-week job.