A budget-vs-actual query is one of the more mechanical reports you'd write against Voyager: pull GLTOTAL.SMTD for actuals, GLTOTAL.SBUDGET for budget, subtract, done. Then someone looks at the revenue line and the variance sign is backwards — a property that's clearly ahead of budget on rental income reads as under. Nothing is broken. The subtraction is doing exactly what the numbers tell it to do; the numbers just don't follow the sign convention you assumed they would.
The GL's sign convention, and where SBUDGET breaks it
Actual GL postings in Voyager follow standard double-entry sign logic: revenue accounts run credit-negative, expense accounts run debit-positive. That convention is consistent and mechanical because it's enforced by the posting engine — nobody types a raw number into SMTD, it's derived from balanced transactions.
GLTOTAL.SBUDGET isn't derived that way. It's populated from whatever a human entered on the budget screen, account by account, and there's no posting engine forcing that entry to respect the credit-negative convention. On a verified live install, this plays out unevenly by account class: revenue budgets get stored positive while the matching revenue actuals are credit-negative — every nonzero pair mismatched in sign. Expense budgets are mostly sign-consistent with their actuals. Some income-type account classes, on the other hand, do carry budgets that are credit-negative, matching their actuals fine. Balance-sheet accounts carry zero budget across the board, because Voyager's budgeting is P&L-only.
There's no single rule that covers every account class. The sign behavior is a per-class fact you have to check, not a schema-level guarantee.
Why the naive subtraction fails silently
SUM(SMTD) - SUM(SBUDGET) assumes both operands are signed the same way. When they are — most expense accounts — the subtraction produces a normal, readable variance: positive means over budget, negative means under. When they aren't — revenue accounts on this pattern — the subtraction doesn't error out or return an obviously wrong number. It returns a plausible-looking variance with the sign flipped, which is worse than an error, because nothing about the output looks broken. It just tells you the opposite of the truth for exactly the accounts most people check first.
Normalizing before you variance
Before drafting a variance query, profile budget signs per account class rather than assuming a convention:
SELECT DISTINCT gt.IBOOK
FROM GLTOTAL gt
WHERE gt.SBUDGET <> 0
That tells you which book(s) actually carry budget data — the main accrual book, a dedicated budget-scenario book, or both; it's worth checking because Voyager's Advanced Budgeting & Forecasting module authors scenarios on separate MODEL properties and lands the resulting figures back on GLTOTAL.SBUDGET for the real property, sometimes in a book set aside for that scenario. From there, compare SMTD against SBUDGET for a small set of known revenue and expense accounts to see which way each class's signs actually run on that install, then normalize the query accordingly — for account classes where budget and actual run opposite signs, flip one side before subtracting (for example, compare -SUM(SMTD) to SUM(SBUDGET) for revenue) rather than trusting a single formula across every account.
A variance query built this way also needs a few other guards that are easy to skip on a first draft:
- Bound the period.
umonth <= GETDATE()— budget rows for future months exist and will otherwise pull ahead-of-time budget into a YTD comparison. - Group explicitly.
GLTOTALcarries department-segment grain (sSegment1..12); a naiveGROUP BYthat ignores segments will collapse rows that shouldn't be summed together. - Filter property type. Admin and ABF-model property records (the model properties budgeting scenarios get authored on) shouldn't be mixed into a real-property variance report.
- Expect zero budget on balance-sheet accounts. That's correct behavior, not a data gap — Voyager budgets P&L accounts only.
It's a per-install fact, not a fixed rule
One more reason to profile rather than assume: the specific sign pattern above — revenue positive, expense sign-consistent, some income classes credit-negative — is what's been verified live, but the underlying cause is that a human typed budget figures into a form with no system enforcing a sign convention. That means the mechanism is stable (budget entry isn't posting-engine-derived, so nothing forces it to mirror actuals), but the exact sign pattern per account class is a fact about how a given install's budgets were entered, not a rule Voyager guarantees across every database. Treat the profiling step as mandatory on a new install rather than reusing a sign map from a different one, even if the account classes look similar on paper.
Why this trap is worth documenting instead of rediscovering
None of this is visible from the schema. SBUDGET and SMTD sit on the same table, same grain, same apparent shape — there's nothing in GLTOTAL's structure that hints one of them was typed by a human on a form and the other was derived from balanced double-entry postings. The only way to know the signs can diverge by account class is to have pulled both columns for real accounts and compared them, which is exactly the kind of behavioral fact a schema diagram can't carry and a generic "Yardi Voyager budget variance SQL" guide has no way to know without having run it live.
PropETL's SQL Assistant carries the SBUDGET sign trap as execution-verified knowledge behind its budget-vs-actual query pattern — it profiles the sign convention and normalizes revenue accounts by default instead of handing back a variance report that's silently backwards on the line most people check first.
It's included in every paid tier, including the free 7-day trial — ask it to draft a budget-vs-actual report for a property and book, and compare it against the version you'd have written from the column names alone.
