The parts filter that read correctly and would have billed nothing
A billing bridge filtered parts on IsConsumed = true, which reads like parts that were used. It means parts that came out of inventory. For a company with no warehouse it is false forever, and every spring would have fallen off every invoice without a single error.
A technician replaces a broken spring and logs the part on the job. The invoice goes out with the labor and the service fee on it — and no spring. Not on that job. On every job.
No error. No warning. The Work Order looks complete, the Order exists, the customer pays,
and the money for the part is just missing. This was five characters of a WHERE clause and
it passed a human review, because the clause reads like plain English and means something
else.
What the bridge does
At Doorvana a completed WorkOrder becomes a Draft Order on the single revenue
ledger that the accounting integration invoices from. One OrderItem per priced
WorkOrderLineItem, one OrderItem per part logged as ProductConsumed, and every line
stamped back with the standard WorkOrderLineItem.OrderId so nothing can be billed twice.
The filter that looked obviously right
The first draft of the parts query filtered on IsConsumed:
FROM ProductConsumed
WHERE WorkOrderId IN :workOrderIds
AND IsConsumed = true
Read that out loud. Parts that were consumed. Parts that were used on the job. That is what you want to bill for, and the field name is the sentence.
Twelve of thirteen tests passed. The one that failed was the parts test, and it failed on a count: it expected two Order Products on the Order and got one. The labor line was there. The spring was not.
What IsConsumed actually tracks
A rolled-back anonymous Apex probe settled it. Create a ProductConsumed against a
PricebookEntry that has no backing inventory ProductItem, read the field back, roll the
whole thing away:
anonymous Apex, rolled back
Savepoint sp = Database.setSavepoint();
ProductConsumed part = new ProductConsumed(
WorkOrderId = workOrderId,
PricebookEntryId = springEntryId,
QuantityConsumed = 2
);
insert part;
System.debug([SELECT Id, IsConsumed FROM ProductConsumed WHERE Id = :part.Id][0].IsConsumed);
Database.rollback(sp);
// false IsConsumed does not mean “was this part used”. It means “did this come out of
inventory” — whether the consumption drew down a ProductItem, a stocked quantity at a
Location or on a van. It is Field Service’s inventory flag, and it is doing exactly what it
was designed to do.
Doorvana holds no inventory. Technicians collect product from vendor locations on the way to
a job, which means there are no ProductItem records, which means every part the company
will ever log has IsConsumed = false. Forever. The filter was not going to return fewer
parts than expected. It was going to return none, on every job, for the life of the org.
The filter came out, and the query now says why it is missing:
force-app/main/default/classes/WorkOrderBillingSelector.cls
/**
* Deliberately no IsConsumed filter. IsConsumed tracks inventory depletion and is false for a
* part logged against a price book entry with no ProductItem, which is every part Doorvana
* logs: it holds no inventory. Filtering on it would silently bill no parts at all.
*/
public static List<ProductConsumed> selectBillableParts(Set<Id> workOrderIds) {
return [
SELECT Id, WorkOrderId, WorkOrderLineItemId, PricebookEntryId,
QuantityConsumed, UnitPrice, PricebookEntry.UnitPrice,
PricebookEntry.Product2.ProductCode
FROM ProductConsumed
WHERE WorkOrderId IN :workOrderIds
AND PricebookEntryId != null
WITH SYSTEM_MODE
ORDER BY ProductConsumedNumber
];
} PricebookEntryId != null is the filter that actually expresses the intent: bill the parts
that have a price. Whether a part came out of a warehouse is a different question that this
org will never need to ask.
The second thing the tests found: user mode is the default now
The same build produced a failure that had nothing to do with billing logic:
Operation failed due to fields being inaccessible on Sobject WorkOrderLineItem
It came from the administrator’s own test setup, writing OrderId onto a
WorkOrderLineItem. An administrator. On a standard field.
From API version 67.0, Apex runs in user mode by default, test classes included. Queries and DML are evaluated against the running user’s field-level security rather than with full system access, and a field the running user cannot see is a field the code cannot write.
That matters far more here than it does in a test. The bridge is triggered by the
WorkOrder moving into the Completed status category, so it runs as whoever completed
the job — a technician, who has no access to Order, OrderItem or price book records and
no reason to. Left in user mode, the automation would have failed for exactly the population
that sets it off, and passed every time an administrator ran it by hand.
So every query in the selector carries WITH SYSTEM_MODE, the DML uses
AccessLevel.SYSTEM_MODE, and the class header says why in prose rather than leaving the
next person to infer it.
Verified, then believed
The bridge was checked end to end in the sandbox UI rather than only in Apex: one completed test Work Order produced a Draft Order carrying the labor line, the service fee, the spring as its own Order Product, and a negative service fee credit where the fee applies toward the repair. That last step is what turns thirteen passing tests into a thing you are willing to say works.
Practical summary
ProductConsumed.IsConsumedis an inventory flag. It isfalsefor every part logged against aPricebookEntrywith noProductItembehind it, however physically consumed that part was.- For billing, filter on
PricebookEntryId != null. That is the question you are actually asking. - Assert on counts in tests. A silent filter produces a valid-looking record with fewer children than it should have, and existence assertions cannot see the difference.
- From API 67.0, Apex defaults to user mode everywhere, including tests. Automation triggered
by low-privilege users needs
WITH SYSTEM_MODEandAccessLevel.SYSTEM_MODEdeclared deliberately, not discovered. - A rolled-back anonymous Apex probe is the cheapest way to learn what a standard field actually stores in your org’s configuration.
The most expensive bugs on this project so far have all had the same shape: something read correctly, reviewed clean, and meant something other than what it said.