One dropdown took one element. The one next to it took four.

Two dropdowns on the same screen flow, identical to the user. One is a record choice set. The other has to reach a Location through AssociatedLocation, which no record choice set can do — and the metadata element for the thing that can does not exist under the name you would guess.

The phone rings. A homeowner’s garage door will not close. The office has the customer’s household record on screen and needs to book a visit, which means answering two questions: who called, and which of their properties is the door at.

On the finished screen those are two dropdowns, one under the other, and they look exactly the same. Behind them, one is a single declarative element and the other is four.

The dropdown that costs one element

“Who called?” is a record choice set. Contact has an AccountId, the quick action runs from the Account, so the filter writes itself:

force-app/main/default/flows/Book_Service_Call.flow-meta.xml

<dynamicChoiceSets>
    <name>Contact_Choices</name>
    <dataType>String</dataType>
    <displayField>Name</displayField>
    <filterLogic>and</filterLogic>
    <filters>
        <field>AccountId</field>
        <operator>EqualTo</operator>
        <value>
            <elementReference>recordId</elementReference>
        </value>
    </filters>
    <object>Contact</object>
    <sortField>Name</sortField>
    <sortOrder>Asc</sortOrder>
    <valueField>Id</valueField>
</dynamicChoiceSets>

One element. It queries when the screen renders, it sorts, it needs no variables. This is what a record choice set is for and it is genuinely good.

Why the property dropdown cannot work the same way

Location has no Account lookup. There is no field on Location to filter on, because the relationship between a customer and a physical place is not a lookup at all — it is AssociatedLocation, the standard junction, where ParentRecordId points at the Account and LocationId at the Location.

That separation is deliberate and it is the reason the data model works: a household that moves keeps its record and gains an address, and a property outlives whoever lived in it. It is also the reason no record choice set can produce this list. A record choice set queries one object and filters on that object’s own fields. There is no traversal, no subquery, no AssociatedLocation.ParentRecordId to reach for from Location.

Four elements to fill one picklist

  1. Get Records on AssociatedLocation, ParentRecordId equal to the Account’s recordId, returning Id and LocationId.
  2. Decision — does this customer have a property at all? An empty collection is the normal case for a brand-new household, and it gets its own screen: “This customer has no property on file. A service call is booked at a property. Add the property as a Location linked to this account (Associated Locations), then book the call again.” A dropdown with nothing in it is not an error message.
  3. Loop over the junction records with an Assignment that Adds each LocationId to a text collection variable.
  4. Get Records on Location, Id In that collection.

Then the choice set itself, over the result of step four:

force-app/main/default/flows/Book_Service_Call.flow-meta.xml

<dynamicChoiceSets>
    <description>Collection choice set: the household's properties, gathered through
        AssociatedLocation because Location carries no Account lookup.</description>
    <name>Property_Choices</name>
    <collectionReference>Get_Properties</collectionReference>
    <dataType>String</dataType>
    <displayField>Name</displayField>
    <object>Location</object>
    <valueField>Id</valueField>
</dynamicChoiceSets>

On the screen, the two are indistinguishable — both are a DropdownBox field with a choiceReferences pointing at their choice set. The receptionist sees two dropdowns. One of them has a loop behind it.

The element name that does not exist

Here is the part worth the search result. In Flow Builder these are two visibly different things: Record Choice Set and Collection Choice Set, separate items in a separate menu. The natural assumption when hand-writing the metadata is that they are two elements.

They are not. Writing <collectionChoiceSets> gets this:

Element '{http://soap.sforce.com/2006/04/metadata}collectionChoiceSets' is invalid at this
location in type Flow

A collection choice set is a dynamicChoiceSets entry. The two are told apart entirely by which children they carry:

Record choice setCollection choice set
ElementdynamicChoiceSetsdynamicChoiceSets
Sourceobject + filtersobject + collectionReference
Querieson screen rendernever — the collection is already in memory
SortingsortField / sortOrderwhatever the Get Records did

collectionReference takes the name of a collection variable or, as here, a Get Records element with storeOutputAutomatically. Once you know it is one element with two modes, the XML is obvious. Until you know, the error tells you nothing about what to write instead — it only says what you wrote is not a thing.

The appointment the Work Type would have created twice

With the property chosen, the flow creates the WorkOrder — Work Type Diagnosis, Line of Business Service, the active ServiceTerritory, the standard price book, address copied from the Location’s Site_* fields — and then its first ServiceAppointment.

Field Service will create that appointment for you. A WorkType has an auto-create setting that produces a ServiceAppointment when a Work Order is created against it, and it is the right default in most orgs.

It is turned off here, because of two fields:

force-app/main/default/flows/Book_Service_Call.flow-meta.xml

<inputAssignments>
    <field>EarliestStartTime</field>
    <value><elementReference>Available_From</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>DueDate</field>
    <value><elementReference>Due_By</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>Duration</field>
    <value><elementReference>Get_Diagnosis_Work_Type.EstimatedDuration</elementReference></value>
</inputAssignments>
<inputAssignments>
    <field>DurationType</field>
    <value><elementReference>Get_Diagnosis_Work_Type.DurationType</elementReference></value>
</inputAssignments>

EarliestStartTime and DueDate are the customer’s availability window — the single most valuable thing collected on the call, and the thing the dispatcher schedules against. Auto-create cannot carry it. It produces an appointment with the Work Type’s duration and no window at all, which means the dispatcher has to phone the customer back to ask the question that was already answered.

So the flow creates the appointment, and auto-create stays off. Leaving both on is the obvious failure: two appointments for one visit, on a job model where the number of open appointments is exactly what decides whether the job may be completed.

Duration and DurationType still come from the Work Type rather than being written into the flow, so a diagnosis slot that turns out to be ninety minutes rather than sixty is a Work Type edit and not a deployment.

Both record creates carry a fault connector to a screen that shows $Flow.FaultMessage and names the two likely causes — no Diagnosis Work Type, no active Service Territory. A screen flow without a fault path on a create hands the receptionist a raw platform fault while a customer is on the phone.

Two more names that do not resolve

Adding a list view for jobs parked waiting on a part produced a second naming lesson in the same session:

Could not resolve list view column

List view columns in the Metadata API want field API names and relationship names. The Classic-era tokens — WORK_ORDER_NUMBER, ACCOUNT.NAME, SUBJECT, STATUS — are all rejected. So are the raw Id fields, AccountId, LocationId and OwnerId. And so is WorkOrderNumber, which is a real field and still not the right column name.

What works: Name for the record name, Account and Location for the lookups, then Subject, Status, LastModifiedDate.

Practical summary

  • A record choice set queries one object and filters its own fields. It cannot see through a junction, so a choice of junction-linked records needs Get Records, a loop, a collection, a second Get Records, and a collection choice set over the result.
  • Handle the empty collection with a screen that says what to do. It is the normal first-time case, not an error.
  • There is no collectionChoiceSets element. A collection choice set is a dynamicChoiceSets entry with collectionReference in place of filters.
  • Turn off Work Type appointment auto-create the moment your process knows something auto-create cannot carry — an availability window, a specific resource, anything. Otherwise you get two appointments for one visit.
  • Keep Duration sourced from the Work Type so timing stays configuration.
  • List view columns use API names and relationship names. Name, not WorkOrderNumber; Account, not AccountId.

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

Can a Salesforce Flow record choice set filter through a junction object?

No. A record choice set queries exactly one object and filters on that object's own fields, so it cannot reach a related record through a junction. To offer a choice of records linked by a junction, query the junction with Get Records, collect the target Ids into a collection variable, query the target object with an Id IN collection filter, and build a collection choice set over that result. That is four elements where a record choice set would have been one.

What is the Metadata API element name for a Flow collection choice set?

It is dynamicChoiceSets, the same element as a record choice set. The two are distinguished by their children: a record choice set carries object plus filters, while a collection choice set carries object plus collectionReference pointing at a collection variable or a Get Records result. There is no collectionChoiceSets element — writing one is rejected at deploy with an error that the element is invalid at this location in type Flow.

Why does a Location not appear in a choice of an Account's properties in Salesforce?

Because the standard Location object has no Account lookup. The relationship between an Account and a physical place is the AssociatedLocation junction, where ParentRecordId points at the Account and LocationId at the Location. Any list of an account's properties has to be assembled through that junction; filtering Location directly on an account field finds nothing, because no such field exists.

Should a Work Type auto-create the Service Appointment, or should a flow create it?

It depends on whether anything about the appointment is known at creation time that auto-create cannot carry. Auto-create produces a Service Appointment with the Work Type's duration and nothing else, so if the process captures a customer availability window that belongs on EarliestStartTime and DueDate, the flow has to create the appointment and auto-create must be turned off. Leaving both on produces two appointments for one visit.

Why does a Salesforce list view column fail with Could not resolve list view column?

Because list view columns in the Metadata API use field API names and relationship names, not the Classic-era column tokens. Name rather than WorkOrderNumber for the record name, and the relationship name rather than the Id field for a lookup — Account, not AccountId. The Classic-style uppercase tokens such as WORK_ORDER_NUMBER and ACCOUNT.NAME are rejected, and so are the raw Id fields.

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.