Opportunity is what you booked. Work Order is what you did. Never add them.

The single most common way a home services Salesforce org starts lying about revenue — and the object discipline that prevents it.

There is a specific week in most home services Salesforce implementations where the numbers stop agreeing. Sales is reporting a record month. Operations is reporting a different record month. Accounting is reporting neither. Everyone is technically pulling from Salesforce, and everyone is technically right.

What happened is that the org grew two revenue stories and nobody picked one.

The two stories

Once Field Service is in the picture, a job exists on two different objects, for two different reasons.

Opportunity is the commercial story. Somebody agreed to buy something. It carries the sales cycle, the close date, the forecast category, the commission. It is a promise.

Work Order is the operational story. A crew went somewhere and did something. It carries the scheduling, the technician, the parts consumed, the actual labor hours. It is a performance.

Both are real. Both feel like revenue. Neither is the ledger.

Two objects, two purposes, one ledger. Roll up revenue at the Order Products layer and the numbers reconcile. Sum Opportunity Amount and Work Order Total together and you have counted the same job twice.

Why the double count is so easy to build

Nobody sets out to double-count. It happens because both paths produce a perfectly reasonable-looking report.

A sales manager wants pipeline, so somebody builds a report on Opportunity Amount. A service manager wants job revenue, so somebody builds a report on a Work Order rollup. Six months later a dashboard needs “total revenue,” and a well-meaning admin adds the two components to one dashboard. The moment a job exists on both objects — which is every job that was sold and then performed — that dashboard is wrong, and it is wrong by an amount that looks plausible.

The discipline, concretely

Three rules, enforced at the schema and report level rather than in a training deck.

  1. Order Products is the only object a revenue report is allowed to sum. Everything else rolls into it or reports on something other than dollars.
  2. Opportunity Amount is a forecast field, not a revenue field. Label it that way in the UI so nobody has to remember the convention.
  3. Work Order carries cost and completion, not price. Parts and labor land in Products Consumed and Expenses, which is how you get job-level margin without inventing a second revenue number.

At Doorvana this got tested immediately by the Wholesale line of business, which does not belong in Field Service at all. Wholesale sells product to dealers; there is no Service Appointment, no crew, no truck. Keeping it on the Opportunity → Order path and off the Work Order path entirely means the ledger stays consistent across all three lines of business without any special-case reporting logic.

Wholesale never touches Field Service objects

That rule sounds restrictive until you price the alternative. Putting wholesale orders on Work Orders would mean Field Service licenses for people who never dispatch anything, scheduling objects with nothing to schedule, and a permanent asterisk on every operational report. The carve-out is cheaper in every dimension.

force-app/main/default/objects/Order/fields/Revenue_Source__c.field-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Revenue_Source__c</fullName>
    <label>Revenue Source</label>
    <type>Picklist</type>
    <description>Which path produced this Order. Reporting always aggregates
        Order Products regardless of source; this field exists to slice the
        ledger by line of business, never to pick a different ledger.</description>
    <valueSet>
        <restricted>true</restricted>
        <valueSetDefinition>
            <sorted>false</sorted>
            <value><fullName>Service</fullName><default>false</default></value>
            <value><fullName>New Construction</fullName><default>false</default></value>
            <value><fullName>Wholesale</fullName><default>false</default></value>
        </valueSetDefinition>
    </valueSet>
</CustomField>

A restricted picklist rather than a free-text field, because the whole point is that the set of lines of business is a decision, not an input.

What it looks like in the org

Placeholder for a Salesforce screenshot showing the Order Products related list
Placeholder — swap in a real capture of the Order Products related list with the Revenue Source column visible. Scrub customer names and addresses before publishing.

The question to ask on day one

Before any of this is built, ask whoever owns the P&L a single question: when you say revenue, which number do you mean, and who else reports a different one?

If the answer takes more than a minute, the object model is not the problem yet. The business has not decided. Salesforce will faithfully implement whichever version of the answer you give it, including the ambiguous one.

Filed under

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.