Building an Acumatica customization for one client is a different job from building one you intend to ship to fifty tenants. The single-client customization can hardcode a screen ID, assume one set of business rules, and get away with a manual publish. A SaaS-intended customization has to survive tenants you'll never meet, upgrades you don't control the timing of, and a support burden that scales with adoption instead of headcount — which changes almost every design decision from day one.
Extensions, never base modifications
This matters more for a shipped product than for a one-off: every base-code modification is a merge conflict waiting for the next Acumatica platform upgrade, and you don't control when your tenants upgrade. Build exclusively through PXGraphExtension and DAC extensions with Usr-prefixed fields, never by editing generated or base project code directly. A customization built entirely on the extension model survives platform upgrades with, at worst, a recompile; one with base modifications can silently break a tenant's instance on their next scheduled upgrade with no signal to you.
public class SOOrderEntry_VerticalExt : PXGraphExtension
{
public static bool IsActive() =>
PXAccess.FeatureInstalled();
[PXDBString(20)]
[PXUIField(DisplayName = "Vertical Reference")]
public virtual string UsrVerticalRef { get; set; }
protected virtual void _(Events.RowSelected e)
{
if (!IsActive()) return;
PXUIFieldAttribute.SetVisible(
e.Cache, e.Row, ShouldShowForCustomerClass(e.Row));
}
}
Feature toggles instead of per-tenant code forks
The moment you fork the customization codebase per client — "Acme gets version A, Beta Corp gets version B" — you have committed to maintaining N codebases forever. Acumatica's own feature management (PXAccess.FeatureInstalled, configured per license/tenant) or a simple custom setup screen with boolean flags lets one codebase serve every tenant, with behavior gated by what's enabled for that specific tenant. Every conditional branch checks a flag; there is exactly one project to publish and one version to support.
A configuration screen, not a support ticket, for every variation
Anything a tenant might reasonably want to vary — a threshold, a default value, a list of codes — belongs on a custom setup screen backed by its own DAC, not as a value a developer edits in code per client. This is the difference between "email support to change a number" and "the customer's admin changes it themselves," and it's the single biggest lever on your support ticket volume as tenant count grows.
A customization designed for one client can tolerate an ad hoc "email me before you upgrade" process. A customization with real SaaS distribution cannot — you need a tested, repeatable way to validate a new customization package against a current-version sandbox before every Acumatica platform release, and a communication plan for tenants when a breaking change is unavoidable. Retrofitting this after tenant ten is far more expensive than designing for it before tenant one.
Data isolation across tenants
If you're distributing through Acumatica's own multitenancy (one site, many tenants, shared schema) rather than one dedicated instance per customer, every custom DAC and every BQL query your extension writes needs to respect Acumatica's tenant-scoping automatically — which Acumatica handles for you at the platform level for standard entities, but any custom table you introduce must still be designed so it can never leak across tenant boundaries in a Generic Inquiry or a custom report that a careless query might bypass.
Versioning the package deliberately
Treat the customization project's version number as a real contract: tenants on version 2.3 should be able to jump to 2.4 without action beyond the standard publish, but 2.x to 3.x should carry release notes describing what changed and why. This discipline is cheap to build in early and expensive to bolt on once tenants are already confused about which version they're running and what's different.
Wrapping up
The technical shape of a SaaS-intended Acumatica customization is not exotic — extensions over modifications, feature flags over forks, configuration screens over support tickets, and a real upgrade-testing habit. What's different from a single-client build is that every shortcut you'd tolerate once now multiplies by tenant count, so the discipline that feels like overhead on customer one is what keeps customer fifty from being unsupportable.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.