Ask a property accountant how AP works in Yardi Voyager and you'll hear a clean story: invoice comes in, it hits the register, it becomes a payable, it gets paid. Ask the same question about a capital project or a ground-up construction job, and the story gets a second track — one where the "invoice" is a payment certificate against a commitment, not a standalone bill.
That second track is what trips people up the first time they go looking for construction spend in the schema and can't find it where AP spend normally lives.
Commitment accounting, briefly
Construction and capital-project accounting runs on a different premise than routine AP. Before any money moves, a commitment is booked — a contract or purchase order value the project is on the hook for, whether or not a dollar has been paid yet. As work gets certified, draws (or payment certificates) get issued against that commitment: a contractor bills for percentage-complete, an architect or PM certifies it, and only the certified amount becomes payable.
The accounting question that matters at every stage isn't "what have we paid" — it's "what have we committed, what's been certified against it, and what's left to draw." A commitment can sit open for months with a fraction drawn. That's normal. It's the whole point of tracking commitment and actual separately instead of only recording spend when cash moves.
Standard AP tables aren't built to answer that question, because they only know about invoices and payments — not about a commitment that predates either.
Where Voyager keeps this: COMMITPAYMENTS
Voyager's Construction & Projects module carries this as its own layer, in a table named COMMITPAYMENTS. It sits between the job-cost commitment and Voyager's normal AP spine, and its job is to bridge the two: it's what turns a certified draw into an actual payable and, eventually, an actual payment.
The columns that do the bridging, execution-verified against a live Voyager schema:
hInvoiceRegisterDetail→GLInvRegDetail.HMY— the certified invoice line, when the draw is routed through the standard AP invoice registerhDetail→DETAIL.HMY— the GL distribution for that drawhTran→TRANS.HMY— the payable the draw creates (this is the confirmed link into Voyager's financial spine)hGlDisbursement→GLDisbursement.hMy— the payment run that eventually pays itbPaid— a paid flagdCommitted/cAmount— the committed value and the certified/drawn amount side by side, on the same row
That last pair is the whole point of the table existing: commitment and draw, sitting together, instead of a commitment number living only in a project-management system that never talks to the general ledger.
The trap: not every payable comes from the invoice register
Voyager's payable population — TRANS rows with iType = 3 — is a hub, not the end of a single pipeline. (We've written about decoding TRANS.iType and separately about the hub-and-spoke shape of Voyager's procure-to-pay architecture; 3 is the AP-invoice/payable code, and the payable is the hub both posts describe.) The normal route into that hub is the AP invoice register: GLInvRegTrans/GLInvRegDetail posts an invoice, and the header links out to the TRANS row it creates.
Construction payment certifications don't have to take that route. A draw can post through COMMITPAYMENTS and create its TRANS payable directly — with or without a matching row in the invoice register at all. That means a query that assumes every payable started life as a registered invoice will quietly miss real construction spend, not because the data is wrong, but because it came in through the other door.
The practical fix is to check both doors before you draw a conclusion about where a payable came from: reverse-lookup a TRANS row (iType = 3) against GLInvRegTrans.hPayable (the invoice-register path) and against COMMITPAYMENTS.hTran (the construction path). The two aren't mutually exclusive — a construction invoice can flow through both, registered and certified — and a payable matching neither is standalone, direct-entry AP. Three populations, not one, and the label depends on which link (if any) resolves.
What that looks like in ySQL
A starting shape for pulling committed-vs-drawn-vs-paid status per project, built from the verified COMMITPAYMENTS columns above:
SELECT
cp.hDetail AS gl_distribution,
cp.dCommitted AS committed_amount,
cp.cAmount AS certified_amount,
t.STOTALAMOUNT AS payable_amount,
cp.bPaid AS is_paid,
gd.Checkdate AS payment_date
FROM COMMITPAYMENTS cp
JOIN TRANS t ON cp.hTran = t.HMY
LEFT JOIN GLDisbursement gd ON cp.hGlDisbursement = gd.hMy
WHERE t.ITYPE = 3
AND cp.dCommitted <> 0
ORDER BY cp.dCommitted - ISNULL(cp.cAmount, 0) DESC
Ordering by the gap between dCommitted and cAmount surfaces exactly the number a project accountant actually wants: how much of each commitment is still undrawn. Extending it to flag invoice-register overlap means adding the GLInvRegTrans.hPayable reverse-lookup described above — worth doing before you report a total, not after.
One more thing worth knowing before you run anything like this at scale: Voyager's ySQL console caps results at 5,000 rows per query, and it does so silently — no error, just a truncated result set that looks complete. On a portfolio with real construction volume, bound by date or project before you trust a total.
Stop reverse-engineering the construction ledger from scratch
This is the kind of table nobody documents until they've been burned by it — a real commitment layer sitting one join away from the AP spine everyone already knows, invisible until a project's spend doesn't reconcile against the invoice register you were checking. PropETL's SQL Query Assistant carries COMMITPAYMENTS and the rest of Voyager's procure-to-pay architecture — invoice register anatomy, MM2 purchasing traps, the hub-and-spoke shape of the payable itself — as queryable, execution-verified knowledge inside Claude.
It ships in every tier. Start the free 7-day trial — no credit card — and ask it where a project's spend actually lives before you write the next query by hand.
