The expenses that weren’t missing
A query concluded that a large share of monthly expenses never reached the income statement. The finance lead didn’t believe it, and was right. The transactions were there. The role running the query just couldn’t see them.
A confident, wrong conclusion
A freight cost review compared carrier bills with what the shipping platforms recorded as label cost. Part of the gap pointed at a company card, so the analysis went looking for card-paid freight in the general ledger and came back with a striking conclusion: a large monthly amount appeared on the card statements but never seemed to reach the P&L.
The finance lead pushed back immediately. He reviews every month-end with the team, and a gap that size would not have gone unnoticed. He was right.
What was actually happening
The SuiteQL ran under an integration role, and that role didn’t have view permission for Credit Card Charge or Credit Card Refund transactions. SuiteQL doesn’t error when a role can’t see a record type. It returns the rows the role can see and quietly leaves out the rest:
SELECT t.type, SUM(tal.amount) AS total
FROM transactionaccountingline tal
JOIN transaction t ON t.id = tal.transaction
WHERE tal.account = ?
AND t.trandate BETWEEN ? AND ?
GROUP BY t.type
-- no CardChrg rows: not because there are none,
-- because this role can't see them
Once the two permissions were added to the role, the card charges appeared, the expense category reconciled, and the “missing” money was exactly where it belonged.
Why this bites integration roles in particular
Integration and reporting roles are usually built by adding permissions as features need them: customers, items, sales orders, vendor bills. Transaction types that only accounting enters — card charges, card refunds, journal entries in some setups, intercompany transactions, payroll journals — are easy to miss because nothing failed when they were left out. The role worked for every request until the first one that needed them.
The same blind spot shows up in other ways we’ve seen:
- Vendor payment queries returning nothing until Pay Bills is added.
- System notes authored by other users not appearing through a query, even though they show on the record in the UI.
- A Suitelet that works for administrators and shows an empty list to warehouse staff.
A quick permission check before any analysis
Count transactions by type for a recent period under the role you’re about to use, and compare it to the same count run by an administrator:
SELECT t.type, COUNT(*) AS n
FROM transaction t
WHERE t.trandate >= ADD_MONTHS(TRUNC(SYSDATE,'MM'), -1)
GROUP BY t.type
ORDER BY t.type
Any type the administrator sees and the role doesn’t is a gap in the role, and every report built on it inherits that gap.
The broader lesson
The best check in this case wasn’t a query. It was someone who knows the business saying “that can’t be right.” Analysis that contradicts what experienced people see every month deserves a second look at the plumbing before it reaches a conclusion. That’s why we reconcile to the books first and present findings second.
Related: the costs nobody looks at until renewal, and an empty list is not an error message.