Skip to content
NetSuite reporting

How to build a NetSuite budget vs actual report

Build a NetSuite budget vs actual report that reconciles: budget loading, period alignment, variance columns and department dimensions.

ERPray teamUpdated 8 min read
Short answer

A NetSuite budget vs actual report compares posted GL amounts to a loaded budget for the same account, period and subsidiary. Build it in the Financial Report Builder by adding a budget column to an income statement, then a variance column of actual minus budget. Budgets must be imported per account, period and dimension first.

Key takeaways

  • Budget vs actual is a join on four keys: account, period, subsidiary and dimension. If any one of them disagrees between the budget and the GL, the variance is fiction.
  • NetSuite budgets are their own record type, loaded per account per period. A blank budget column almost always means the budget was saved against a different dimension combination, not that the report is broken.
  • Actuals follow the posting period, not the transaction date. A 31 January invoice posted to February belongs in February's variance.
  • Variance sign has to be interpreted per account type: actual minus budget is good for revenue and bad for expense, so label the column favourable/unfavourable rather than positive/negative.
  • The Financial Report Builder handles the GL cleanly. The moment you need budget against a non-GL measure — headcount, units, orders — you are out of the report builder and into a query.

A NetSuite budget vs actual report looks like a two-column comparison and behaves like a four-way join. Most of the time it goes wrong the report itself is fine: the budget was loaded against a department the transactions never use, or against a subsidiary that rolls up differently, or into a period that closed under a different fiscal calendar. This walks through the data first, then the build, then the variance columns that actually survive a review meeting.

Budget vs actual variance
The difference between the amount posted to a general ledger account in a period and the budgeted amount for that same account, period, subsidiary and dimension combination. Expressed in currency and as a percentage of budget.

The budget record: what you are actually loading

In NetSuite a budget is not a field on an account. It is a separate record, and each row of it is keyed by a combination of dimensions. Set them up under Transactions > Financial > Set Up Budgets, or import them by CSV, which is what anyone with more than about fifty accounts does.

  • Account — one budget line per GL account. Roll-up accounts do not budget automatically; if you budget the parent and post to the children, the parent shows a budget and the children show none.
  • Accounting period — twelve monthly amounts per fiscal year, per account. A budget loaded as one annual figure will show the whole year against January and nothing after it.
  • Subsidiary — required on OneWorld. Budgets are entered in the subsidiary's currency, which matters the moment you view the report consolidated.
  • Department, class, location — optional, and this is the field pair that breaks most reports. Budget loaded with no department will not match actuals that carry one, and vice versa.
  • Customer and item — available for revenue budgeting where you need it, and worth avoiding unless you genuinely plan at that grain.
  • Budget category — only visible if the Multiple Budgets feature is enabled. Without it you have exactly one budget per year and no way to hold an original plan next to a reforecast.

Four ways to build the report, and what each costs you

There is no single right answer here. The choice depends on whether you need a formal financial statement, an operational drilldown, or a number in a spreadsheet by 9am.

ApproachGood forWhere it stops
Standard Budget Income StatementA fast, correct P&L with budget and variance columns. Respects your financial statement layout and account hierarchy.Fixed column set. Limited flexibility on comparative periods and custom variance maths.
Financial Report Builder (custom)Adding your own budget, variance, variance % and prior-year columns; alternate row layouts; department or class column headers.GL accounts only. No non-financial measures, and column arithmetic is limited to what the builder exposes.
Saved searchOperational cuts of actuals with custom formula columns and drill-through to transactions.Budget is a separate record with no join path from a transaction search, so a saved search cannot put both sides in one result set. See NetSuite saved search limitations.
SuiteQL or a SuiteAnalytics workbookAny grain, any variance formula, budget against non-GL measures, output straight into a spreadsheet or a dashboard.You own the definitions. Nothing stops you from double-counting intercompany lines or forgetting the posting flag.
Use the report builder for anything that has to tie to the statutory P&L. Use a query for anything that has to tie to an operational plan.
Free calculator
Reporting time savings calculator

Put a number on what the monthly variance pack currently costs in hours before you decide how much to automate.

Building it in the Financial Report Builder

  1. 1.Start from the standard Income Statement rather than a blank report. Reports > Financial > Income Statement, then Customize. You inherit the account layout and the sign conventions, which is most of the work.
  2. 2.On the Edit Columns tab, add a column and set its type to the budget alternate range. Choose the same period range as your actual column, and pick the budget category if you run multiple budgets.
  3. 3.Add a variance column as a formula of the actual column minus the budget column, and a second column for variance as a percentage of budget. Name them explicitly — Var $ and Var % beat NetSuite's default column labels.
  4. 4.On Filters, set subsidiary, department, class and location to match the grain the budget was loaded at. Leave a dimension unfiltered and you sum across it, which is usually what you want at the top level and never what you want in a drilldown.
  5. 5.Set the period range using relative periods (last fiscal quarter, this fiscal year to date) rather than named periods. Absolute periods mean somebody re-edits the report every month and eventually forgets.
  6. 6.Save with an audience, not for yourself. A variance report that only one person can open becomes that person's second job.

Period alignment: the variance nobody can explain

Actuals in NetSuite belong to a posting period, and the posting period is not always the month on the transaction date. An invoice dated 31 January entered on 3 February, after January was closed, posts to February. Your variance report is right and your sales team's spreadsheet is right, and they disagree by that invoice.

  • Always reconcile on posting period. If a stakeholder brings a number built on transaction date, that difference is the first thing to isolate.
  • Adjustment periods sit outside the twelve monthly periods. They carry real postings and no budget, so any period-13 activity shows as pure unfavourable variance unless you exclude it or budget for it deliberately.
  • Multiple fiscal calendars on OneWorld mean subsidiaries can have different period boundaries. A consolidated variance across two calendars is comparing different spans of days. Work out what your own period boundaries are with the fiscal period calculator before you argue about the number.
  • Reopened periods change history. If someone reopens and reposts March, a variance pack exported in April no longer matches the same report run in May. Date-stamp your exports.

Variance columns that survive a review

Four columns of arithmetic answer almost every question a budget holder asks. More than that and people stop reading the report.

ColumnFormulaWhy it earns its place
ActualSum of posted GL amounts for the periodThe fact.
BudgetBudget amount for the same account, period and dimensionThe plan, at the grain you actually planned at.
Var $Actual − BudgetMateriality is measured in currency. A 300% overspend on a $400 line is not news.
Var %(Actual − Budget) ÷ BudgetDirection and severity. Undefined where budget is zero, so print a dash rather than a divide-by-zero.
YTD Var $Cumulative actual − cumulative budget, year to dateSeparates timing from overspend. A line that is $40,000 over in March and $2,000 over YTD is a phasing issue.
Period variance tells you what happened. YTD variance tells you whether it matters.

Sign convention is where variance reports lose credibility. Actual minus budget is favourable on a revenue account and unfavourable on an expense account, so a report that colours everything positive as green is wrong half the time. Either flip the sign by account type or label the column Fav / (Unfav) and let the reader do it. If you want the reasoning behind picking materiality thresholds and writing the commentary, that is covered in budget vs actual variance analysis.

Pulling actuals with SuiteQL when the report will not bend

Once you need budget against something that is not a GL account — revenue per head, spend per order, cost per unit shipped — the report builder is finished and a query starts. The actuals half is straightforward: post amounts live on transaction accounting lines, and the period comes from the transaction header.

SELECT
    a.acctnumber                    AS account_number,
    BUILTIN.DF(tal.account)         AS account_name,
    ap.periodname                   AS period,
    SUM(tal.amount)                 AS actual_amount
FROM transactionaccountingline tal
JOIN transaction      t  ON t.id  = tal.transaction
JOIN accountingperiod ap ON ap.id = t.postingperiod
JOIN account          a  ON a.id  = tal.account
WHERE t.posting = 'T'
  AND ap.isadjust = 'F'
  AND ap.startdate >= TO_DATE('2026-01-01', 'YYYY-MM-DD')
  AND ap.enddate   <= TO_DATE('2026-06-30', 'YYYY-MM-DD')
  AND a.accttype IN ('Expense', 'OthExpense', 'COGS')
GROUP BY a.acctnumber, BUILTIN.DF(tal.account), ap.periodname
ORDER BY a.acctnumber, ap.periodname
Posted expense actuals by account and period. The budget side is joined in afterwards.

Three details in that query do the real work. t.posting = 'T' restricts to transactions that hit the GL, which drops sales orders and other non-posting documents. ap.isadjust = 'F' keeps period 13 out of a monthly comparison. And SUM(tal.amount) on accounting lines rather than transaction lines means debits and credits net correctly without you reasoning about the mainline flag. Run it through the SuiteQL formatter if you inherited it as one long line.

Multi-subsidiary, multi-currency variance

Budgets are entered in subsidiary currency. Consolidated reporting translates actuals at the rates in your currency exchange rate table, and translates budget at whatever rate type the report is configured for. If those two differ, you get variance that is pure FX and nothing to do with the budget holder. Report subsidiary-level variance in local currency for accountability, and consolidated variance for the board, and never mix them in one column. The mechanics of consolidation and elimination are their own topic, covered in NetSuite consolidated multi-subsidiary reporting.

Asking for the variance instead of building it

The arithmetic in a budget vs actual report is subtraction. The work is everything around it: matching the grain, agreeing the period basis, excluding adjustment periods, deciding sign convention, and rebuilding it slightly differently for each of six budget holders. That is a week a quarter for most finance teams, and it is why the report is usually a fortnight old by the time anyone reads it.

is built for that gap. Ask which cost centres are more than 10% over budget year to date, and by how much and get the answer computed live against your own NetSuite account, with the SuiteQL it ran printed underneath — so you can check the posting flag and the period filter yourself rather than trusting a black box. It is read-only by default and NetSuite-first, with Infor SyteLine, M3 and LN ready.

Frequently asked questions

Why is my NetSuite budget column blank?

Almost always a grain mismatch. Budgets are stored per account, period, subsidiary and optional department, class or location. If the budget was loaded without a department but your report groups by department, NetSuite has no budget row to show. Check the budget record's dimensions against the report's grouping and filters, and confirm the subsidiary and fiscal year match.

How do I import budgets into NetSuite by CSV?

Use Setup > Import/Export > Import CSV Records and choose the Budgets import type. One row per account per period per dimension combination, with subsidiary included on OneWorld. Import into an open fiscal year, match the accounting period names exactly as NetSuite has them, and run a small test file first — budget imports overwrite rather than accumulate.

Can a saved search show budget and actual side by side?

No. Budget is a separate record type with no join path from a transaction saved search, so you cannot get both measures into one saved search result. Use the Financial Report Builder for GL comparisons, or a SuiteQL query or SuiteAnalytics workbook if you need a custom grain or a non-GL measure alongside the budget.

Should budget vs actual use transaction date or posting period?

Posting period. Budgets are loaded per accounting period, and the GL is closed per accounting period, so posting period is the only basis on which the two sides tie. Transaction date differs whenever something is entered after its month closed, which is common at the start of every month and is the most frequent cause of unexplained variance.

How do I hold an original budget and a reforecast in NetSuite?

Enable the Multiple Budgets feature, which adds a budget category to the budget record. Load the original plan under one category and each reforecast under another, then choose the category on the report column. Without that feature you have one budget per fiscal year and reforecasting means overwriting the plan you are trying to measure against.

Why does consolidated budget variance differ from the sum of subsidiary variances?

Currency translation. Budgets are entered in subsidiary currency, and consolidation translates actuals and budget using the rates configured for the report. If the two sides translate at different rates, the difference appears as variance that no budget holder caused. Review local-currency variance for accountability and consolidated variance for group reporting.

Your ERP already knows. Start asking.

ERPray computes answers like these live from your own ERP account and shows the exact query behind every number. Early access is open for NetSuite teams — free plan at launch.