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
- Get Records on
AssociatedLocation,ParentRecordIdequal to the Account’srecordId, returningIdandLocationId. - 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.
- Loop over the junction records with an Assignment that
Adds eachLocationIdto a text collection variable. - Get Records on
Location,IdInthat 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 set | Collection choice set | |
|---|---|---|
| Element | dynamicChoiceSets | dynamicChoiceSets |
| Source | object + filters | object + collectionReference |
| Queries | on screen render | never — the collection is already in memory |
| Sorting | sortField / sortOrder | whatever 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
collectionChoiceSetselement. A collection choice set is adynamicChoiceSetsentry withcollectionReferencein place offilters. - 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
Durationsourced from the Work Type so timing stays configuration. - List view columns use API names and relationship names.
Name, notWorkOrderNumber;Account, notAccountId.