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.

Labor lines and parts arrive on the Order by two different paths. WorkOrderLineItem.OrderId is the stamp that makes the bridge idempotent — a line that already carries an OrderId is never billed again.

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.IsConsumed is an inventory flag. It is false for every part logged against a PricebookEntry with no ProductItem behind 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_MODE and AccessLevel.SYSTEM_MODE declared 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.

Filed under

Disclosure: Doorvana's owner is also a co-owner of Nocona Consulting. This is a real engagement between two separate companies, but not an arm's-length one — and not an independent reference. More on the work page.

FAQ

What does IsConsumed mean on the Salesforce ProductConsumed object?

IsConsumed records whether the part was depleted from inventory, not whether it was used on the job. It is set when the consumption draws down a ProductItem — a stocked quantity at a Location or on a van. A ProductConsumed created against a PricebookEntry with no backing ProductItem stores IsConsumed = false even though the part was physically installed. Any logic that reads IsConsumed as was this part used will be wrong in every org that does not carry inventory in Salesforce.

Should a billing process filter ProductConsumed on IsConsumed?

Only if the org actually tracks inventory in Salesforce and you specifically want depleted stock. For billing, filter on whether the part carries a price instead — PricebookEntryId != null — because that is what determines whether there is anything to charge for. Filtering on IsConsumed in an org with no ProductItem records silently returns an empty list, which produces invoices that are missing every part and raises no error anywhere.

Why did Apex start failing with fields being inaccessible after API version 67.0?

From API version 67.0 Apex runs in user mode by default, including in test classes. Queries and DML are evaluated against the running user's field-level security rather than with full system access. Code that worked under an older API version fails with messages such as Operation failed due to fields being inaccessible on Sobject WorkOrderLineItem when the running user cannot see a field the code writes. The fix is explicit: WITH SYSTEM_MODE on SOQL and AccessLevel.SYSTEM_MODE on Database DML, in the places where system behaviour is genuinely intended.

Should a Field Service billing automation run in system mode?

Usually yes, and the reason is who triggers it. Billing is started by whoever completes the Work Order, which is typically a technician with no access to Order, Order Product or price book records. If the automation inherits that user's access it fails for precisely the population that sets it off, while passing for an administrator running it by hand. Declaring system mode explicitly and restricting the class's callers is more honest than discovering the asymmetry in production.

How do you catch a filter that silently returns nothing in Salesforce?

Assert on counts, not only on success. A test that checks an Order was created passes happily when the Order has half the lines it should. Asserting the exact number of Order Products against the number of billable inputs is what turns a silent omission into a failing test. Where a filter's meaning is inferred from its name, a rolled-back anonymous Apex probe that creates a record and reads the field back settles what the platform actually stores.

Working on something like this?

I can tell you what it takes.

If you run a home services business and this looks like a problem you have, book a call and we will talk through it.