The form showed Texas. The record had no state.
A screen flow swapped typed address fields for the Address component with Google address lookup. The suggestion filled Street, City, State and ZIP on screen, the deploy succeeded, every test passed, and the Lead saved with StateCode blank. What the lookup actually returns in province and country, how to map it to State and StateCode, and why an update needs the code alone.
Someone types the first few characters of a street into a search box, picks the suggestion that matches, and watches the form fill itself in. Street, City, State, ZIP. Texas, right there on the screen. They save.
The record had no state. Nothing on the screen said so, the deploy had succeeded, and every automated test in the project was green.
This is how that happened, and the two lines of mapping that fixed it.
Swapping typed fields for address search
At Doorvana every hand-keyed Lead is created through a screen flow,
New_Service_Lead, which overrides the Lead New action. (Why that is the only way in is
its own post.) Until this week the flow asked for
the job-site address as separate text inputs. Typing an address into four boxes is slow, and the
person doing it is usually on the phone with a homeowner.
Version 12 replaced them with the standard Address component, flowruntime:address, with
address lookup switched on:
force-app/main/default/flows/New_Service_Lead.flow-meta.xml
<fields>
<name>Property_Address</name>
<extensionName>flowruntime:address</extensionName>
<fieldType>ComponentInstance</fieldType>
<inputParameters>
<name>addressLabel</name>
<value><stringValue>Job Site Address</stringValue></value>
</inputParameters>
<inputParameters>
<name>showAddressLookup</name>
<value><booleanValue>true</booleanValue></value>
</inputParameters>
<inputParameters>
<name>addressLookupLabel</name>
<value><stringValue>Search Address</stringValue></value>
</inputParameters>
<inputParameters>
<name>country</name>
<value><stringValue>United States</stringValue></value>
</inputParameters>
...
</fields> The component’s outputs are street, city, province, postalCode and country, and the
flow’s Create Records element maps them onto the Lead. The org has State and
Country/Territory Picklists enabled, which means every standard address has two state fields:
State, the full name, and StateCode, the two-letter picklist value. That pairing has
bitten this project before, so the
first draft was careful about it. It was careful in the wrong direction.
The assumption in the first draft
The first Draft version assumed the component’s province would be a state code. It kept
province only when it was two letters long, and mapped that to StateCode:
New_Service_Lead, first Draft of v12 (reconstructed)
<formulas>
<name>State_Code_To_Save</name>
<dataType>String</dataType>
<expression>IF(LEN(TRIM({!Property_Address.province})) = 2,
UPPER(TRIM({!Property_Address.province})), "")</expression>
</formulas>
<!-- Create_Lead -->
<inputAssignments>
<field>StateCode</field>
<value><elementReference>State_Code_To_Save</elementReference></value>
</inputAssignments> That reads as defensive: if the value is not a clean code, write nothing rather than write something a restricted picklist will reject. And it would have been fine, if the lookup returned codes.
It does not. The first on-screen run picked a Google suggestion, the form showed Texas, and
the saved Lead had StateCode blank. In this build, the address lookup put the full state
name in province. The two-letter guard did exactly what it was written to do. It threw the
state away, quietly, on every address that came through search.
The fix: map both shapes
The flow now takes either shape. A two-letter value goes to StateCode, anything longer goes
to State, and each formula writes blank when the value belongs to the other field:
force-app/main/default/flows/New_Service_Lead.flow-meta.xml
<formulas>
<description>Two-letter province value goes to StateCode.</description>
<name>State_Code_To_Save</name>
<dataType>String</dataType>
<expression>IF(OR({!Is_Supply}, {!Is_TBD}), "",
IF(LEN(TRIM({!Property_Address.province})) = 2,
UPPER(TRIM({!Property_Address.province})), ""))</expression>
</formulas>
<formulas>
<description>A full state name, which is what the address lookup returns, goes to State.</description>
<name>State_Name_To_Save</name>
<dataType>String</dataType>
<expression>IF(OR({!Is_Supply}, {!Is_TBD}), "",
IF(LEN(TRIM({!Property_Address.province})) = 2,
"", TRIM({!Property_Address.province})))</expression>
</formulas> And Create_Lead maps both:
force-app/main/default/flows/New_Service_Lead.flow-meta.xml
<inputAssignments>
<field>State</field>
<value><elementReference>State_Name_To_Save</elementReference></value>
</inputAssignments>
<inputAssignments>
<field>StateCode</field>
<value><elementReference>State_Code_To_Save</elementReference></value>
</inputAssignments> (Is_Supply and Is_TBD are there because a dealer buying doors has no job site, and a new
subdivision lot may not have a street address yet. Hidden inputs can keep old values when the
user goes back, so every saved value is guarded.)
This was confirmed two ways in the browser: once through the lookup, and once by typing an
address and choosing the state from the dropdown. Both Leads were read back with State
Texas and StateCode TX.
Country had the same assumption
The component’s country input had been set to US. On screen, the Country picklist sat on
--None--. Setting the input to United States selected it. Same mistake, other field: a code
where the component wanted a name.
The same field, on update, behaved the other way
The Address component turns up a second time in the same flow, as an optional Billing Address
for business clients, and that value has to reach the converted Account. The after-save
conversion flow, Lead_Convert_Build_Property_And_Household, writes it onto an Account that
already exists by the time it runs. So this is an update, not an insert.
The obvious move was to write the name to BillingState, which is what the component returns.
On the update, supplying BillingState with a blank BillingStateCode left the code null.
The state disappeared again, by the opposite route.
So the conversion flow converts the name to a code and writes only BillingStateCode:
force-app/main/default/flows/Lead_Convert_Build_Property_And_Household.flow-meta.xml
<formulas>
<description>The address component returns the full state name. Written as a code only,
because on an update a blank code clears the state even when a name is supplied.</description>
<name>Billing_State_Code</name>
<dataType>String</dataType>
<expression>IF(LEN(TRIM({!$Record.Billing_State__c})) = 2,
UPPER(TRIM({!$Record.Billing_State__c})),
CASE(TRIM({!$Record.Billing_State__c}),
"Alabama", "AL",
"Alaska", "AK",
...
"Texas", "TX",
...
"Wyoming", "WY",
""))</expression>
</formulas> A CASE over fifty-one names is not elegant. It is explicit, it runs in a flow with no Apex, and
an unrecognised value produces blank rather than a value the restricted picklist rejects.
Why nothing automated caught it
The deploy succeeded, and every automated check was green. Neither fact says anything about
this bug, because an Apex test cannot run a screen flow. SalesReworkTest can insert Leads,
convert them and assert on what comes out, but the step that went wrong is the one between a
person picking a suggestion and the Create Records element. That step only exists on screen.
The only check that found it was the one that sounds too simple to bother with: run the screen,
save, open the record, and read every field back. Not the page; the fields. The page showed an
address. The StateCode field was empty.
The red herring: the search box that was not there
One thing slowed this down. The first screenshot of the new screen had no Search Address box
at all, which looked like a missing setting: showAddressLookup not taking effect, or a Maps
setting not enabled in the org. It was neither. The box was in the page’s element tree and had
simply rendered late. A second look found it.
Worth knowing before you go digging through MapsAndLocationSettings: if the lookup field is
missing from a screenshot, check whether it is missing from the page.
What was verified, and what was not
- The lookup path and the typed path with the state dropdown were each run on screen, saved, and
read back. Both held
StateTexas andStateCodeTX. - The country input was confirmed on screen with both values.
- The billing update was found in a validation run of the conversion tests and fixed by writing the code alone.
- What the component returns for addresses outside the United States was not tested.
Practical summary
flowruntime:addresswithshowAddressLookuptrue put the full state name inprovincein this build. Do not assume a code.- Map a two-letter
provincetoStateCodeand anything longer toState. Saving a Lead that way gave both fields on read-back. - Set the component’s
countryinput toUnited States, notUS. - On an update to an existing address, a name with a blank code left the code null. Convert the
name and write the
...StateCodefield alone. - Apex cannot run a screen flow. Save on screen and read the record back.
- If the Search Address box is missing from a screenshot, look again before you change settings.