The short answer
A GRNI reconciliation ties open accruals to the GL, then labels each item.
A NetSuite GRNI reconciliation lists every open posting on the Accrued Purchases account by purchase order (PO) and PO line, as of a period end, until the list adds up to the general ledger (GL) balance. Then you label each remaining item. Some will be timing: a receipt waiting for its bill. The rest are real problems, such as a price variance, a variance journal, or a manual journal.
The balance doesn't need to reach zero. Oracle describes Accrued Purchases as the account that offsets the A/P register for inventory received but not yet billed (Oracle: Accounting for Received Purchase Orders). Open receipts belong there. What matters is that you can explain every dollar.
GRNI (goods received not invoiced), IRNB (inventory received not billed) and Accrued Purchases all mean the same thing here.
An accrual that won't tie holds up the close and invites audit questions. Reports built on POs and quantities often fail to tie because the balance lives in posting lines on one GL account.
How a purchase posts
A PO posts nothing. Per Oracle, the item receipt debits Inventory Asset and credits Accrued Purchases. The vendor bill then debits Accrued Purchases and credits Accounts Payable.
Oracle's own example shows how a residual appears (Oracle: Vendor Bill Variances). The PO is for 100 units at $5 with an exchange rate of 0.50. The receipt comes in at 4.5 x 98 x 0.52, which credits Accrued Purchases for $229.32. The bill arrives at 6 x 105 x 0.60, which debits the account for $378.00. That leaves $148.68 in Accrued Purchases until someone posts a variance journal to clear it. Price, quantity and rate all moved, and each one left something behind.
Posting the variance journals moves that $148.68 out of Accrued Purchases and into the item's variance accounts. This PO then carries a balance of $0.00. If all three variance accounts on the item are the same account, the journal lands as one combined amount.
Receipts and bills don't point at each other. Both link to the PO. Oracle shows the PO status moving to Pending Billing after receipt, and the Purchase Order History report nets receipts against bills for each PO. So advice that there is "no direct link" between receipt and bill is half right: the link exists, but it runs through the PO.
Finding receipts without bills, and bills without receipts
Two lists answer this. Build both from the subledger described later in this article, filtered to a period end.
- Receipts without bills: PO lines whose postings to Accrued Purchases are credits only, with no bill debit. These are your open receipts. Sort by age.
- Bills without receipts: bill debits on a PO line with no receipt credit, plus any bill debit with no PO link at all. A bill ahead of its receipt is timing. A bill with no PO link needs an owner.
For a native view of the second list, Post Vendor Bill Variances has an Include Bills Without Receipts box that adds bills with no item receipts.
One sign convention for the rest of this article. The accrual is a liability, so report it credit-positive. A positive balance means received but not billed. A negative balance means billed ahead of receipt, or billed for more than was received.
Why received minus billed fails
The first report most teams build is a saved search of quantity received minus quantity billed per PO line. It almost never ties, for four reasons.
It counts units, not value. A PO received and billed for the same quantity at different rates shows nothing, while the account still holds the price difference.
It reads transaction dates. The GL reads posting periods. A receipt dated after period end is correctly excluded from the GL balance, and a report filtered on transaction date will disagree around every month end. Tie on the posting period's end date.
It misses journals. Variance journals and manual adjustments hit the account, but they aren't receipts or bills, so a PO-quantity search never sees them.
It includes lines that never touched the account. Non-inventory and service lines, such as freight, often post straight to expense. Exclude them before you call anything a mismatch.
For a tie to the GL balance, VCG builds a saved search or query on posting lines for the Accrued Purchases account.
The causes to test, in order
Test in this order.
Classify every driver, check that the drivers add up to the balance, and only then decide what to clean up. Timing items clear by themselves. Anomalies need someone to act.
| Cause | What it looks like | How to confirm | Label |
|---|---|---|---|
| Bill before receipt | A bill debit on the PO with no credit yet | List postings dated after the cutoff; a later receipt clears it | Timing |
| Price variance | Small residual on a PO fully received and billed | Compare receipt rate with bill rate per item | Anomaly until posted |
| Quantity variance | Over-receipt never billed, or billed above received | Compare received and billed quantity per line | Anomaly |
| Exchange-rate variance | Residual on a foreign-currency PO | Compare receipt and bill exchange rates | Anomaly until posted |
| Posted variance journals | Balance moves with no receipt or bill | Follow PO to bill to variance journal | Explained |
| Bills off the PO or to expense | Bill debit with no PO link, or no accrual effect at all | List bill lines on the account with no PO link; check the line's account | Anomaly |
| Manual journals | Lines on the account, tagged or untagged | List journal lines on the account by period | Anomaly until tagged |
| Closed lines and returns | Closed line with a receipt but no bill; returned goods | List closed PO lines with a balance; list returns on the PO | Anomaly |
A bill posted before the receipt looks like over-billing on the day you run the report and disappears once the receipt posts, so list postings dated after the cutoff even though they're excluded from the balance.
Closed lines and returns are worth a separate check. Expect a return to reverse the receipt credit, and a vendor credit to post against the bill, but confirm in your account that each one reaches the accrual and links back to the PO line. If it does not, it lands in No PO.
Price variance is the usual reason a PO that's fully received and fully billed still shows a few dollars. The difference stays in Accrued Purchases until a variance journal moves it.
Post Vendor Bill Variances in plain terms
NetSuite can move these differences to variance accounts, but someone has to run it.
Oracle says that if you use Advanced Receiving, you can use the vendor bill variances process to generate journals that move these differences to variance accounts. The journals are system-generated, but the process is not automatic. The page is at Transactions > Payables > Post Vendor Bill Variances (Oracle: Posting Vendor Bill Variances).
Someone has to run it. You filter the list, tick the lines, confirm the posting period and click Create Journal Entries. Nothing posts on its own.
There are three variance types: bill price, bill quantity and exchange rate. Each posts to the account set on the item's Accounting subtab, and you can point all three at one account if you want a single combined variance. The journals cover inventory, non-inventory, other charge and service items only.
Match Bill to Receipt
The Match Bill to Receipt box on the PO line decides how variances are worked out. With it checked, variances are based on vendor bill lines, which Oracle says suits purchases that span accounting periods. Without it, variances are based on PO lines, and the line must be fully received and billed, or closed, before a variance can post. Oracle also says not to check the box on drop-ship lines.
Oracle's concept and procedure pages word the closed-line rule slightly differently, but both say closing the line is the route for lines that won't be fully received and billed.
Two filters people misread
Receipt linkage is a default filter, not a rule: tick Include Bills Without Receipts and unlinked bills appear. Bills pending approval also appear. Oracle explains that the quantity buckets must be allocated for NetSuite to work properly, which also helps prevent over-billing.
Why you can't edit the PO afterwards
Once variance journals post, the related POs, receipts and bills can't be changed until the journals are voided or deleted, with an open period and A/P unlocked. Oracle doesn't mention reversing journals, so don't assume a reversal lifts the lock; test it in a sandbox.
Standard costing is different
Under standard costing, the inventory asset is always held at standard cost, and the difference from actual cost is recognised at receipt. Oracle's example is a $7 standard cost against a $10 receipt: $7 to inventory, $3 to variance (Oracle: Standard Costing). That is separate from the later bill variance.
Building a subledger that ties
The balance lives in the ledger, so build the subledger from posting lines on the one accrual account, grouped by PO and PO line, as of a period end.
The recipe
Account: the one Accrued Purchases account. Date: posting period end date, not transaction date. One accounting book per run. Subsidiary from the posting line. Group by PO and PO line, with a separate No PO group. Amount: credit minus debit, so a credit reads positive.
This SuiteQL skeleton is a starting point. Confirm table and column names, and the link join, in your account. Run it through a paged helper, not a bare query (see the row-limit section below).
SELECT NVL(link.previousdoc, 0) AS po_id,
SUM(NVL(tal.credit, 0) - NVL(tal.debit, 0)) AS balance
FROM transactionaccountingline tal
JOIN transaction t ON t.id = tal.transaction
JOIN accountingperiod ap ON ap.id = t.postingperiod
LEFT JOIN (
SELECT DISTINCT nextdoc, previousdoc
FROM previoustransactionlinelink
) link ON link.nextdoc = tal.transaction
WHERE tal.account = :accrualAccount
AND tal.accountingbook = :book
AND ap.enddate <= :periodEnd
GROUP BY NVL(link.previousdoc, 0)
ORDER BY po_id
The control total is a separate statement with the same filter and no joins:
SELECT SUM(NVL(tal.credit, 0) - NVL(tal.debit, 0)) AS control_total
FROM transactionaccountingline tal
JOIN transaction t ON t.id = tal.transaction
JOIN accountingperiod ap ON ap.id = t.postingperiod
WHERE tal.account = :accrualAccount
AND tal.accountingbook = :book
AND ap.enddate <= :periodEnd
Show the grouped total, the control total and the gap on every run. This skeleton groups at document level. A bill that links to two POs will count twice, which is exactly the kind of gap the control total exposes. Line-level attribution and variance journals come next.
Item receipts credit the account on the transaction's main line. Vendor bills debit it on item lines. Journals touch it in two ways: bill-variance reclasses and manual adjustments. The report has to pick up all three.
A worked tie-out
Every figure below is made up for illustration.
| PO | Source | Balance |
|---|---|---|
| PO-1001 | Receipt awaiting bill (timing) | 4,200.00 |
| PO-1002 | Price residual, fully billed | 62.50 |
| PO-1003 | Bill ahead of receipt (timing) | -1,800.00 |
| No PO | Untagged manual journal | 950.00 |
| Subledger total | 3,412.50 | |
| Control total (GL) | 3,612.50 | |
| Gap | 200.00 |
Four labelled items, and still $200.00 unexplained. A gap like this means the report is missing rows, usually a journal that does not link to a PO or a row cut off by a query limit. Do not plug it. Find it first.
Linking postings back to the PO
In SuiteQL (NetSuite's SQL query language), the obvious "created from" and "applied to" fields tend to error out. VCG's consultants have found the reliable path is NetSuite's transaction line links, where the PO is the previous document and the receipt or bill is the next one. Receipts link at document level and bills at line level. This is observed behaviour, not documented behaviour, so test it in your own account.
Following variance journals through the bill
A variance journal links to the bill, not the PO. A query that only follows PO-to-transaction links misses those journals, and the PO comes out wrong by exactly the reclass amount. Add a second step that walks PO to bill to journal, joining on the linked line so each journal line maps to one bill and nothing fans out into duplicates.
Manual journals and the No PO bucket
One journal can touch several POs. If you sum it by transaction and give the total to each PO, you over-count. Attribute journals line by line.
A manual journal line only belongs to a PO if someone tagged it with that PO. Untagged lines go in an explicit "No PO" bucket so the subledger still reaches the GL total. Finding them takes a full scan of the account, because there are no links to follow. This bucket is also your answer to "which manual journals hit the account?"
Credit minus debit, not amount
Calculate each line from its debit and credit columns rather than the amount column, which can return a currency-translated figure. Test this in your own account.
Several subsidiaries
Scope by the subsidiary on the posting line, because the transaction header's subsidiary isn't available to the query module in a script. Make subsidiary, account and base currency report parameters so one report serves every entity, and set them on each deployment in the UI, since SDF (NetSuite's deployment tooling) doesn't carry parameter values. VCG's multi-subsidiary OneWorld work covers this kind of setup.
Multi-currency and multiple books
On foreign-currency POs, report in base currency from the ledger lines. Oracle lets you point all three variance types at one account; separate accounts give you a cleaner exchange-rate view.
Secondary accounting books are where totals quietly double. When an account carries a second, currency-translated book, the accounting-line data holds two rows for the same posting line, with the same transaction and line key and different amounts. Without an accounting-book filter, the subledger double-counts, and single-row lookups return whichever book came back first. The book filter is not optional. Reconcile each book separately, and tie each book's balance on its own instead of assuming the books agree.
Why exchange-rate variances do or do not reach a secondary book is not covered in Oracle's documentation. The variance journal is likely posted per book like any other transaction, so its effect can differ by book. Compare the journal's lines book by book before you rely on it.
The 5,000-row trap
Oracle documents that a single runSuiteQL call returns at most 5,000 rows and points to runSuiteQLPaged for more.
Paged results are capped at 100,000 rows in total without SuiteAnalytics Connect (Oracle: SuiteQL Limits).
On an account with tens of thousands of accrual lines, a direct query stops at 5,000 rows and looks clean, yet never ties.
How you page matters on large accounts. The built-in paged query returns at most 1,000 rows per page at 10 governance units a page (governance is the per-script usage budget NetSuite enforces), so 50,000 lines cost 500 units, enough to exhaust a Suitelet. Windows of 5,000 rows by row number cost 10 units each, five times fewer, but need a unique sort order in the inner query.
Build the tie-out into the report. On every run, compare the sum of the fetched rows with a control total that the database calculates over the same filter. The control total isn't affected by paging, duplicate joins or per-row currency translation. If the two differ, show the gap instead of a tidy number. A paged helper and control-total check fit in a small script, the kind VCG builds under SuiteScript automation.
Year-end cleanup of stale balances
Prove what each old item is before writing it off: age it, find the PO owner, and ask the vendor whether a bill is coming.
Then fix at the source in order: close PO lines that will never be billed, run Post Vendor Bill Variances for the closed and matched lines, and only then post manual write-off journals, each tagged to its PO line and approved. Keep the aging, reason and approver for each item, because auditors ask.
To stop the balance building up again, require PO-based bills, decide a Match Bill to Receipt policy for each item type, and set a cutoff rule for late bills.
A monthly review checklist
Each month, the controller should see:
- Subledger total equal to the GL balance, per subsidiary and per book.
- Zero gap between the fetched rows and the control total.
- Aging buckets and the top 10 POs by balance.
- Debit (over-billed) balances listed separately.
- The No PO bucket, reviewed line by line.
- Variances posted this period, by type.
- Postings dated after the cutoff.
AP fixes bill linkage, Purchasing handles over-receipts and line closures, and the controller approves write-offs.
When to bring in help
Bring in help when the subledger has never tied, when there are several subsidiaries or accounting books, or when a past rebuild produced numbers nobody trusts.
Those are rescue cases. The fix usually means rebuilding the report from the ledger and tightening the process that feeds it. For an example of reporting VCG has built beyond native NetSuite, see how VCG built a data warehouse and dashboards for ProSource.
If your accrual still won't tie after the steps above, Book an Assessment and VCG will review the GRNI build and the purchasing process behind it.
FAQ
Do I need Advanced Receiving for variance journals?
Oracle's page presents the process as the route if you use Advanced Receiving. It doesn't state Advanced Receiving as a hard requirement or say the process is exclusive to it. To confirm in your account, check whether Post Vendor Bill Variances appears under Transactions > Payables. If it does, you can use it.
Why does a fully received and billed PO still leave a residual?
Usually because the bill rate differs from the receipt rate, or the exchange rate moved. The difference stays in the account until someone posts a variance journal.
What do bills with no PO do to the accrual?
It depends on the line. A bill line coded to an expense account debits that expense and never touches Accrued Purchases, so it is not a reconciling item. A bill with item lines and no PO or receipt may debit the accrual or inventory depending on item type and setup. Test one in a sandbox. Either way, put every unlinked debit in the No PO bucket and give it an owner.
Does Match Bill to Receipt matter for purchases that span months?
Yes. Oracle says to use it when a purchase spans accounting periods, because variances are then based on vendor bill lines instead of PO lines.
Is there a standard NetSuite GRNI report?
The Purchase Order History report nets receipt amounts against bill amounts for each PO, which helps as a cross-check. For a tie to the GL balance, build the posting-line query above.



