Purchase Orders sits between Inventory and AP, and most PO customizations exist because a client's procurement policy has an approval or vendor-selection rule that the stock module doesn't express on its own. The module's DACs are stable and well-documented; the interesting extension work is almost always in approval routing and vendor evaluation.
Core DACs and screens
POOrder and POLine make up the order (Purchase Orders screen, PO301000), and POReceipt/POReceiptLine track what's actually come in against it. The graph is POOrderEntry for order entry and POReceiptEntry for receiving. Three-way matching against APInvoice happens on the AP side but reads back into PO receipt data, so PO and AP extensions frequently need to be considered together.
Extension points
Custom approval routing again leans on the Approval Maps engine rather than bespoke code — amount thresholds, vendor class, or buyer department are all conditions the map already supports. Where custom code earns its place is vendor evaluation logic that depends on data outside the PO record itself: a vendor scorecard pulling from delivery history, quality holds, or an external system, used to flag or block a PO before it's released.
Acumatica already supports over/under-receipt tolerance percentages at the item or PO class level. A custom validation that hardcodes a tolerance percentage in a RowPersisting handler will drift out of sync the first time the business changes the tolerance policy and someone updates the configuration screen without realizing code elsewhere has its own hardcoded copy.
A realistic scenario: vendor scorecard gate
A recurring PO customization: block or warn on releasing a PO to a vendor whose on-time delivery or quality score has dropped below a threshold, computed from a rolling window of past receipts. This reads historical POReceipt data (or a summarized scorecard table refreshed on a schedule) at PO release time.
public class POOrderEntry_VendorScorecard_Extension : PXGraphExtension<POOrderEntry>
{
public PXAction<POOrder> release;
[PXOverride]
public void Release(List<POOrder> list, Action<List<POOrder>> baseMethod)
{
POOrder order = Base.Document.Current;
if (order != null)
{
var score = VendorScorecards.Select(order.VendorID).RowCast<VendorScorecard>().FirstOrDefault();
if (score != null && score.OnTimeDeliveryPct < 80m)
{
Base.Document.Cache.RaiseExceptionHandling<POOrder.vendorID>(order, order.VendorID,
new PXSetPropertyException("Vendor on-time delivery is below 80% over the last 12 months. Review before releasing.", PXErrorLevel.Warning));
}
}
baseMethod(list);
}
public PXSelect<VendorScorecard> VendorScorecards;
}
Using a warning rather than a hard error here is deliberate — a buyer may have a legitimate reason to proceed with a flagged vendor (sole source, urgent need), and a hard block turns a useful signal into a workflow obstruction the business will just ask you to remove.
Drop-ship and blanket orders
PO customizations built and tested against a standard warehouse-receipt order commonly break against drop-ship orders (where receipt and shipment are tied to a sales order) or blanket/standing orders (where a single PO releases across multiple receipts over time). If the client uses either pattern, include them explicitly in test scenarios — they exercise different paths through POOrderEntry than a simple order-then-receive flow.
Testing considerations
Test against a PO with a partial receipt, an over-receipt within tolerance, and an over-receipt outside tolerance — these three cases are where custom validation logic most often either fires when it shouldn't or fails to fire when it should, because the tolerance math has more edge cases than it first appears.
Wrapping up
PO extensions work best when approval and tolerance policy stay in configuration, and custom code is reserved for logic that genuinely needs external or historical data the stock module doesn't track — like a vendor scorecard. Test against drop-ship and blanket order flows, not just the simplest receive-against-PO case.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.