关于 field-service-data-capture-reference-configure
面向 Salesforce Field Service 的 Data Capture Flow 编写与部署技能,适用于构建移动端和离线表单的开发者。它提供组件元数据、Repeater、数据写入及部署限制的具体规则,帮助处理复杂表单和部署问题。
主要功能
- 01DataCaptureFlow 元数据结构指导
- 02移动端输入与选择组件参考
- 03Repeater 数据预填、遍历与展示模式
- 04记录创建、更新及删除约束
- 05部署错误与表单显示问题排查
适用场景
- 01编写 Field Service 移动离线表单
- 02将 Repeater 条目保存为子记录
- 03从现有记录集合预填重复行
- 04诊断 Flow 部署或移动端表单不可见
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill field-service-data-capture-reference-configure安装后在 Claude Code 中输入/field-service-data-capture-reference-configure确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/Data_Capture_All_Components.flow-meta.xmlDataCapture_Showcase.flow-meta.xmlRepeater_with_prepopulation.flow-meta.xml详细介绍
查询 Fs 数据采集参考
何时使用此技能
构建、编辑和部署 Salesforce 数据采集流(processType DataCaptureFlow)——Field Service 移动端/离线表单。适用于使用 runtime_service_fieldservice:dc* 组件编写 flow-meta.xml、Repeater 循环(.AllItems)、主从关系子记录持久化、视觉优化(渐变横幅、进度条、提示框)、配置支持对象的 FLS/权限集、调试 DataCaptureFlow 部署错误,或排查已部署的表单为何未显示在 FSL Mobile Forms 选项卡上(DDC/WorkPlan OWD + AssignedResource 共享前提条件)。
工作流
Salesforce 数据采集流技能
使用 processType: DataCaptureFlow 构建、编辑和部署 Salesforce 流(Field Service 移动端/离线表单)。
必需元数据(每个流都需要)
<processType>DataCaptureFlow</processType>
<areMetricsLoggedToDataCloud>false</areMetricsLoggedToDataCloud>
<environments>Offline</environments>
可选的 IsLlmTargetable 自定义属性——如果包含该属性,它必须是 JSON 字符串,而不是布尔值:
<customProperties>
<name>IsLlmTargetable</name>
<value><stringValue>{"value":"false"}</stringValue></value>
</customProperties>
<booleanValue>false</booleanValue> 形式可以部署,但会阻止激活——错误:The value of the IsLlmTargetable custom property's value field must be a string in JSON format。完全省略该属性也可以。
XML 结构规则
Salesforce 的 Flow 架构会强制按类型分组——所有同类型元素必须连续放在同一个区块中。违反此规则时,部署会失败,并显示 Element X is duplicated at this location。
分组顺序不限,但每组中的元素必须相邻:
- 所有
<choices>放在一起 - 所有
<dynamicChoiceSets>放在一起 - 所有
<screens>放在一起 - 所有
<decisions>放在一起 - 所有
<recordLookups>放在一起 - 所有
<recordCreates>放在一起 - 所有
<recordUpdates>放在一起 - 所有
<loops>放在一起 - 所有
<assignments>放在一起 - 所有
<variables>放在一起
连接器引用决定执行顺序,而不是 XML 顺序。
组件参考
所有扩展:前缀 runtime_service_fieldservice:
| 组件 | 扩展 | fieldType |
|---|---|---|
| 短文本 | dcTextInput | ComponentInstance |
| 长文本 | dcLongText | ComponentInstance |
| 电子邮件 | dcEmail | ComponentInstance |
| 电话 | dcPhone | ComponentInstance |
| 姓名 | dcName | ComponentInstance |
| 数值 | dcNumeric | ComponentInstance |
| 计数器 | dcCounter | ComponentInstance |
| 日期 | dcDate | ComponentInstance |
| 日期和时间 | dcDateTime | ComponentInstance |
| 复选框 | dcCheckbox | ComponentInstance |
| 开关 | dcToggle | ComponentInstance |
| 地址/GPS | dcAddress | ComponentInstance |
| 查找 | dcLookup | ComponentInstance |
| 静态图像 | dcFileView | ComponentInstance |
| 上传图像(移动端) | dcUpImage | ComponentInstance |
| 上传文件(移动端) | dcUpFile | ComponentInstance |
| 签名(移动端) | dcSignature | ComponentInstance |
| 单选选项列表 | dcPicklist | ComponentChoice |
| 多选选项列表 | dcPicklist | ComponentMultiChoice |
| 单选按钮 | dcRbGroup | ComponentChoice |
| 复选框组 | dcCbGroup | ComponentMultiChoice |
| 矩阵 | dcMatrix | ComponentMultiChoice |
| 显示文本 | (无) | DisplayText |
| 区段 | (无) | RegionContainer + Region |
| Repeater | (无) | Repeater |
注意:<fieldType>Range</fieldType> 不是 DataCaptureFlow 中有效的滑块 fieldType(尽管 Builder UI 列表中出现了“Range/Slider”)。此流程类型不支持仅通过元数据配置滑块——请使用 dcNumeric 或 dcCounter。forceContent:repeater(Lightning 通用组件)≠ <fieldType>Repeater</fieldType>(FSL 离线组件)。两者概念相似,但 XML 不同。
设置标签
| fieldType | 方法 |
|---|---|
ComponentInstance | <inputParameters><name>label</name><value><stringValue>…</stringValue></value></inputParameters> |
ComponentChoice / ComponentMultiChoice | <fieldText>标签</fieldText> |
DisplayText | <fieldText>HTML</fieldText> |
所有 ComponentInstance 扩展(包括 dcAddress 和 dcToggle)都接受 label inputParameter,无需使用 DisplayText 包装。
必填标记
每个输入字段都需要在字段级别设置 <isRequired>true/false</isRequired>。对于所有 dc* 组件,这一个标记就足够了——无需额外的 required / isRequired inputParameter。
dcLookup 还接受一个 isRequired inputParameter(布尔值),但必填规则由字段级别的 <isRequired> 驱动。
dcCheckbox 和 dcToggle 在语法上接受 <isRequired>true</isRequired>,但运行时不会强制执行。
每个输入字段还必须包含
<inputsOnNextNavToAssocScrn>UseStoredValues</inputsOnNextNavToAssocScrn>
<storeOutputAutomatically>true</storeOutputAutomatically>
<styleProperties>
<verticalAlignment><stringValue>top</stringValue></verticalAlignment>
<width><stringValue>12</stringValue></width>
</styleProperties>
例外: <fieldType>Repeater</fieldType> 明确不支持 storeOutputAutomatically(“the storeOutputAutomatically field isn't supported”)。Repeater 输出始终可通过 .AllItems 使用——无需选择启用。
屏幕规则
每个屏幕都必须包含以下三项,否则不会呈现 Next/Finish 按钮:
<allowFinish>true</allowFinish>(即使在非最终屏幕上也要设置)<showFooter>true</showFooter><nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel>
另外:
<start>后的第一个元素必须始终是<screens>元素- 所有
<screens>元素必须在 XML 中放在一起
Repeater — 遍历行并创建子记录
Repeater 的输出集合以 .AllItems 的形式公开。已确认在 API v66 上正常工作。
<fields>
<name>MyRepeater</name>
<fieldType>Repeater</fieldType>
<fields>
<name>Row_PartName</name>
<extensionName>runtime_service_fieldservice:dcTextInput</extensionName>
<fieldType>ComponentInstance</fieldType>
<storeOutputAutomatically>true</storeOutputAutomatically>
</fields>
<fields>
<name>Row_PartQty</name>
<extensionName>runtime_service_fieldservice:dcCounter</extensionName>
<fieldType>ComponentInstance</fieldType>
<storeOutputAutomatically>true</storeOutputAutomatically>
</fields>
<isRequired>false</isRequired>
</fields>
<loops>
<name>Loop_Parts</name>
<collectionReference>MyRepeater.AllItems</collectionReference>
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Create_Part</targetReference>
</nextValueConnector>
</loops>
<recordCreates>
<name>Create_Part</name>
<object>CustomFormPart__c</object>
<connector>
<targetReference>Loop_Parts</targetReference>
</connector>
<inputAssignments>
<field>PartName__c</field>
<value>
<elementReference>Loop_Parts.Row_PartName.value</elementReference>
</value>
</inputAssignments>
<inputAssignments>
<field>Quantity__c</field>
<value>
<elementReference>Loop_Parts.Row_PartQty.value</elementReference>
</value>
</inputAssignments>
<storeOutputAutomatically>true</storeOutputAutomatically>
</recordCreates>
不要这样做:
<collectionReference>MyRepeater</collectionReference>→Element "MyRepeater" doesn't exist<collectionReference>MyRepeater.items</collectionReference>→ 通用服务器错误<collectionReference>MyRepeater.data</collectionReference>→ 通用服务器错误- 在循环体内部,
MyRepeater.Row_PartName.value不起作用——必须使用循环的名称,而不是 Repeater 的名称。
跨行验证无法编译。 r_GR4.AllItems[$Items].field.value 和 r_GR4.AllItems[$Items - 1].field.value 这类公式引用会因 Syntax error 而失败。每行验证可通过在嵌套字段自身的 validationRule 中使用普通的 fieldName.value 实现。对于跨行规则,请使用屏幕后置的 loops + decisions。
其他 Repeater 访问器目前无法解析。 只有 .AllItems 可用。AddedItems、PrepopulatedItems、RemovedItems 在部署时都会因 doesn't exist 而失败。
根据现有集合预填充 Repeater
将现有 SObject 集合绑定到 Repeater,使其为每条源记录渲染一行预填充内容。随后,用户可以在提交前编辑、添加或移除行。
模式:recordLookups(获取源集合,getFirstRecordOnly=false,storeOutputAutomatically=true)→ 包含 Repeater 的屏幕,通过 collection inputParameter 进行绑定 → 嵌套字段使用 SourceCollection[$EachItem].FieldApiName 作为其 value 默认值。
<recordLookups>
<name>Get_Source</name>
<object>ServiceResource</object>
<getFirstRecordOnly>false</getFirstRecordOnly>
<storeOutputAutomatically>true</storeOutputAutomatically>
<connector><targetReference>Screen_Repeater</targetReference></connector>
</recordLookups>
<fields>
<name>accountRepeater</name>
<fieldType>Repeater</fieldType>
<inputParameters>
<name>collection</name>
<value><elementReference>Get_Source</elementReference></value>
</inputParameters>
<fields>
<name>account_info</name>
<fieldType>DisplayText</fieldType>
<fieldText><p>Id: {!Get_Source[$EachItem].Id}</p></fieldText>
</fields>
<fields>
<name>name</name>
<extensionName>runtime_service_fieldservice:dcTextInput</extensionName>
<fieldType>ComponentInstance</fieldType>
<inputParameters>
<name>label</name>
<value><stringValue>Name</stringValue></value>
</inputParameters>
<inputParameters>
<name>value</name>
<value><elementReference>Get_Source[$EachItem].Name</elementReference></value>
</inputParameters>
<isRequired>true</isRequired>
<storeOutputAutomatically>true</storeOutputAutomatically>
<inputsOnNextNavToAssocScrn>UseStoredValues</inputsOnNextNavToAssocScrn>
<styleProperties>…</styleProperties>
</fields>
<isRequired>false</isRequired>
<styleProperties>…</styleProperties>
</fields>
要点:
- 绑定用的 inputParameter 名为
collection,而不是value或source。 - 在 Repeater 内,通过
SourceCollectionName[$EachItem].FieldApiName引用源行——使用记录查找的名称,而不是 Repeater 的名称。此方式适用于DisplayText.fieldText(如{!Get_Source[$EachItem].Id})和组件的value默认值(如<elementReference>Get_Source[$EachItem].Name</elementReference>)。 $EachItem是 Salesforce 在渲染 Repeater 时注入的逐行迭代器。它只会在 Repeater 嵌套的字段中解析。- 下游循环仍按常规方式迭代
Repeater_Name.AllItems——预填充改变的是输入,而不是输出访问器。 - 预填充的行会在提交后作为常规的
.AllItems条目显示;不存在PrepopulatedItems/AddedItems之分(这些访问器会导致部署失败)。
向用户显示 Repeater 条目(Repeater 后的 Loop 屏幕)
要向用户展示其填写的内容(例如审核、确认或逐行详情),请在 Repeater 屏幕后放置一个 Loop,其主体连接到显示屏幕;显示屏幕随后连接回 Loop。Loop 的结束连接器会继续执行下一步。
<loops>
<name>Loop_Through_Repeater</name>
<collectionReference>accountRepeater.AllItems</collectionReference>
<iterationOrder>Asc</iterationOrder>
<nextValueConnector>
<targetReference>Repeater_Output_Screen</targetReference>
</nextValueConnector>
</loops>
<screens>
<name>Repeater_Output_Screen</name>
<connector><targetReference>Loop_Through_Repeater</targetReference></connector>
<fields>
<name>display_info</name>
<fieldType>DisplayText</fieldType>
<fieldText><p>Source Id: {!Loop_Through_Repeater.UniqueField__Id}</p>
<p>Name: {!Loop_Through_Repeater.name.value}</p>
<p>Type: {!Loop_Through_Repeater.description.value}</p></fieldText>
<styleProperties>…</styleProperties>
</fields>
<allowFinish>true</allowFinish>
<showFooter>true</showFooter>
<nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel>
</screens>
循环主体内的访问器规则:
- 用户填写的值——
{!LoopName.nestedFieldName.value}(与其他位置相同,使用.value/.selectedChoiceValues/.isActive等访问器)。 - 预填充行的源记录 Id——
{!LoopName.UniqueField__Id}。这是 Repeater 在每次迭代中公开的合成字段;它只对来自已绑定collection的行携带值(用户添加的新行将为空)。 - 在循环主体内使用循环的名称,而不是 Repeater 的名称——与上述
.AllItems+ Create 规范模式相同。
这种遍历 .AllItems 的显示模式可与 Create/Update 模式组合使用:一个循环用于渲染审核屏幕,后续循环(若顺序允许,也可以是同一个循环)用于 CUD。请记住 CUD 规则——连续的 CUD 节点之间不得插入任何内容(屏幕、获取记录、决策),因此任何审核循环都必须在 CUD 链开始前完全结束。
CUD 规则(平台硬性限制)
- Decision 可以选择从哪个 CUD 链开始(例如,在保存模式决策中选择 Create 或 Update 分支)。但CUD 链一旦开始,连续的 CUD 节点之间不得出现 Decision——部署会失败,并显示
Append multiple Create, Update, or Delete operations only at the end of the flow, in any order。- 对于“仅在有填写内容时创建”的变通方案 → 始终创建(接受空行),或在 CUD 链开始前移动条件逻辑。
- **所有 CUD 都必须位于流程末尾。**任何 CUD 之后都不得有 Get Records 或屏幕。不得包含含有 CUD 的子流程。
- **v260 中循环内 CUD 之后的 Assignment 存在问题。**已在 v262 / API 66 中修复。现在可以安全使用。
计数器参数
<inputParameters><name>min</name><value><numberValue>1.0</numberValue></value></inputParameters>
<inputParameters><name>max</name><value><numberValue>10.0</numberValue></value></inputParameters>
<inputParameters><name>step</name><value><numberValue>1.0</numberValue></value></inputParameters>
<inputParameters><name>minCustomErrorMessage</name><value><stringValue>…</stringValue></value></inputParameters>
<inputParameters><name>maxCustomErrorMessage</name><value><stringValue>…</stringValue></value></inputParameters>
日期参数
<inputParameters><name>minDate</name><value><elementReference>$Flow.CurrentDate</elementReference></value></inputParameters>
<inputParameters><name>maxDate</name><value><dateValue>2027-12-31</dateValue></value></inputParameters>
选项列表紧凑模式
仅当所有标签都 ≤8 个字符且选项不超过 5 个时,才使用 isCompact=true。
查找参数
<inputParameters><name>objectApiName</name><value><stringValue>Asset</stringValue></value></inputParameters>
<inputParameters><name>searchedFields</name><value><stringValue>Name, SerialNumber</stringValue></value></inputParameters>
<inputParameters><name>isMultiSelection</name><value><booleanValue>true</booleanValue></value></inputParameters>
<inputParameters><name>recordIdCollection</name><value><elementReference>v_Ids</elementReference></value></inputParameters>
recordIdCollection — 限定可搜索集合
- 构建器标签: 记录 ID 集合。XML 属性:
recordIdCollection(单数recordId+Collection后缀)。使用recordIds会导致部署失败,并提示We can't find this input attribute: "recordIds"。 - 这是范围筛选器,而非默认预选项。 它会将查找范围限制为仅搜索所提供的字符串 ID 集合。
- 仅在
isMultiSelection=true时生效。 在单选模式下会被静默忽略,用户会看到未筛选的完整对象。 - 标准模式:
recordLookups(限定范围的子集)→loops+assignments(构建字符串 ID 集合)→ 屏幕,设置dcLookup recordIdCollection=v_Ids。可离线使用 Briefcase 预置的数据;约 60k 条记录时目标耗时 < 1s。 - 多选模式下的输出为
{!Lookup.recordIds}(字符串集合);之前使用{!Lookup.recordId}(单数)的可见性规则和 DML 必须遍历该集合或取第一个元素。
dcLookup 显示标签
dcLookup 没有用于指定显示字段的输入参数(没有 displayField/primaryField)。搜索结果和已选标签的标签由对象的主要紧凑布局决定——该布局中的第一个字段优先。若要更改:Setup → Object Manager → 对象 → Compact Layouts → 调整顺序 → 设为 Primary(组织级更改)。searchedFields 控制匹配,不控制显示。
Flow 内的替代方案:将 dcLookup 换成由 dynamicChoiceSets 提供数据的 dcPicklist,并设置 <displayField> / <valueField>。
带 GPS 的地址
<inputParameters><name>useCoordinates</name><value><booleanValue>true</booleanValue></value></inputParameters>
矩阵
<inputParameters><name>questions</name><value><stringValue>["Q1","Q2","Q3"]</stringValue></value></inputParameters>
在 JSON 字符串中,将 & 转义为 &。
双列区段
<fields>
<name>MySection</name>
<fieldText>Section Header</fieldText>
<fieldType>RegionContainer</fieldType>
<fields>
<name>MySection_Col1</name>
<fieldType>Region</fieldType>
<fields></fields>
<inputParameters><name>width</name><value><stringValue>6</stringValue></value></inputParameters>
<isRequired>false</isRequired>
</fields>
<fields>
<name>MySection_Col2</name>
<fieldType>Region</fieldType>
<fields></fields>
<inputParameters><name>width</name><value><stringValue>6</stringValue></value></inputParameters>
<isRequired>false</isRequired>
</fields>
<isRequired>false</isRequired>
<regionContainerType>SectionWithHeader</regionContainerType>
…styleProperties…
</fields>
可见性规则
<visibilityRule>
<conditionLogic>and</conditionLogic>
<conditions>
<leftValueReference>componentName.value</leftValueReference>
<operator>GreaterThan</operator>
<rightValue><numberValue>0.0</numberValue></rightValue>
</conditions>
</visibilityRule>
属性访问器:.value(输入组件,包括 dcCheckbox)、.selectedChoiceValues(选项组件)、.isActive(切换组件)、.firstName / .lastName(Name 组件)、.recordId / .recordIds(Lookup 单选 / 多选)。
相同的访问器也用于 recordCreates / recordUpdates 的 inputAssignments 中,例如 <elementReference>new_Reading.value</elementReference>。
有条件隐藏的必填字段——使用 validationRule,而非 isRequired
IF(TriggerField.selectedChoiceValues = "Yes",
AND(NOT(ISBLANK(value)), value >= 0, value <= 100000),
TRUE)
标准 Decision IsNull 模式
IsNull 右侧接受 booleanValue,而不是 null 字面量:
<conditions>
<leftValueReference>v_ParentId</leftValueReference>
<operator>IsNull</operator>
<rightValue>
<booleanValue>false</booleanValue>
</rightValue>
</conditions>
计算时机(重要)
计算值无法显示在收集输入的同一屏幕上——只有用户点击 Next 后才会运行计算。模式:
Screen N (collect inputs) → recordLookups / assignments / decisions → Screen N+1 (display results)
Screen N 的连接器必须指向计算元素,而非 Screen N+1。所有 Decision 分支最终都必须汇合到 Screen N+1。
DisplayText 公式限制(移动端运行时)
与标准 Flow 相比,DataCaptureFlow 中的 DisplayText 公式支持非常有限——复杂公式可以成功部署并在构建器中预览,但运行时会失败并显示 Error while resolving default value reference。
运行时会失败:
IF(Toggle.isActive, "YES", "NO")— 对组件属性使用任何 IF/CASETEXT(CASE(...))、ADDMONTHS(...)、嵌套日期运算- 在一个
fieldText中混用多个组件引用和公式
可用:
- 简单的单个变量:
{!var_RiskScore} - 简单的组件引用:
{!MyPicklist.selectedChoiceValues}、{!MyNumeric.value} - 全局变量:
{!$Flow.CurrentDate}、{!$User.FirstName}
模式:在 <assignments> 元素中预先计算 → 存入变量 → 在 DisplayText 中引用该变量。
全局变量作为输入 value 默认值
| 默认绑定 | 变量 | 可用? |
|---|---|---|
dcTextInput | $User.Username | ✅ |
dcTextInput | $User.Name | ❌ 类型不匹配错误 |
dcDateTime | $Flow.InterviewStartTime | ✅ |
dcDate | $Flow.CurrentDate | ✅ |
记录选择集(dynamicChoiceSets)——移动端离线注意事项
dynamicChoiceSets 中的 <outputAssignments> 可以成功部署,但在移动端离线 DataCaptureFlow 运行时,无法可靠地填充目标变量。下游屏幕会显示空白。
正确做法:让选择集保持精简(displayField、valueField、filters、object、dataType)。选择屏幕后,通过一个按 Id = {!picklistName.selectedChoiceValues} 进行筛选的 recordLookups 路由,并使用该查找的 outputAssignments 将额外字段存入变量。这也自然符合计算时机规则(查找步骤位于选择屏幕和显示屏幕之间)。
<recordLookups>
<name>gr_SelectedChild</name>
<filters>
<field>Id</field>
<operator>EqualTo</operator>
<value><elementReference>pl_Child.selectedChoiceValues</elementReference></value>
</filters>
<getFirstRecordOnly>true</getFirstRecordOnly>
<object>Child__c</object>
<outputAssignments>
<assignToReference>v_SelectedValue</assignToReference>
<field>Reading_Value__c</field>
</outputAssignments>
</recordLookups>
视觉润色模式(已验证可在移动端运行时正确渲染)
Lightning Design System 中的颜色:#2E844A(绿色)、#0176D3(蓝色)、#FE9339(橙色)、#C9C7C5(中性色)。
主视觉横幅
<div style="background: linear-gradient(135deg, #2E844A 0%, #0176D3 100%); color: white; padding: 20px; border-radius: 10px; text-align: center;">
<p style="margin: 0; font-size: 22px;"><strong>🔧 Site Visit Report</strong></p>
<p style="margin: 4px 0 0 0; font-size: 13px;">Subtitle</p>
</div>
进度条(每个屏幕)
<p><b>Step X of N – 📍 Section Title</b></p>
<div style="width: 100%; height: 8px; background-color: #C9C7C5; border-radius: 4px; margin: 5px 0;">
<div style="width: PERCENT%; height: 100%; background: linear-gradient(90deg, #2E844A 0%, #0176D3 100%); border-radius: 4px;"></div>
</div>
计数器与标题之间的分隔符必须是 en dash –(U+2013),不能用连字符。
提示框
<div style="background-color: #EAF5FE; border-left: 4px solid #0176D3; padding: 10px 14px; border-radius: 4px;">
<p style="margin: 0; color: #014486; font-size: 13px;"><b>ℹ️ Info:</b> …</p>
</div>
<div style="background-color: #FFF4E6; border-left: 4px solid #FE9339; padding: 10px 14px; border-radius: 4px;">
<p style="margin: 0; color: #704D00; font-size: 13px;"><b>⚠️ Heads up:</b> …</p>
</div>
<div style="background-color: #E8F5E9; border-left: 4px solid #2E844A; padding: 10px 14px; border-radius: 4px;">
<p style="margin: 0; color: #1B5E20; font-size: 13px;"><b>✅ Ready:</b> …</p>
</div>
审核卡片(合并前面屏幕中的合并字段)
<div style="border: 1px solid #DDDBDA; border-radius: 8px; padding: 14px 16px; background-color: #FAFAF9;">
<p style="margin: 0 0 8px 0; color: #0176D3; font-size: 14px;"><b>📍 Section</b></p>
<p style="margin: 2px 0;"><b>Name:</b> {!Input_SiteName.value}</p>
<p style="margin: 2px 0;"><b>Contact:</b> {!Input_Contact.firstName} {!Input_Contact.lastName}</p>
<p style="margin: 2px 0;"><b>Priority:</b> {!Input_Priority.selectedChoiceValues}</p>
<p style="margin: 2px 0;"><b>After-hours:</b> {!Input_Toggle.isActive}</p>
</div>
<fieldText> 中的所有 HTML 都必须进行 XML 转义:& → &、" → "、< → <、> → >。
随流程一起部署配套对象
自定义对象和字段部署后,不会自动为 System Administrator 配置文件授予 FLS/CRUD 权限。以管理员身份运行的流程仍无法读取或写入新字段。请一并发布 PermissionSet 并分配给用户:
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
<label>My Object Access</label>
<license>Salesforce</license>
<hasActivationRequired>false</hasActivationRequired>
<objectPermissions>
<allowCreate>true</allowCreate><allowDelete>true</allowDelete>
<allowEdit>true</allowEdit><allowRead>true</allowRead>
<modifyAllRecords>true</modifyAllRecords><viewAllRecords>true</viewAllRecords>
<object>MyObject__c</object>
</objectPermissions>
<fieldPermissions><field>MyObject__c.MyField__c</field><editable>true</editable><readable>true</readable></fieldPermissions>
<tabSettings><tab>MyObject__c</tab><visibility>Visible</visibility></tabSettings>
</PermissionSet>
部署后分配:sf org assign permset --name MyObject_Access --target-org <alias>
CustomObject 注意事项
- 主从关系子对象需要
<sharingModel>ControlledByParent</sharingModel>,否则会出现Must specify a sharing model value。 - Sfdx retrieve 可能会为非标准操作(
Automation、Details)拉取actionOverrides。重新部署前请移除,否则会出现X is not a standard action and cannot be overridden。 - 超过 255 个字符的文本字段必须使用
<type>LongTextArea</type>。Text的最大长度为 255。 - 引用了
required=true字段的 PermissionSet 可以顺利部署,但如果 FLS 在单独的文件中设置,就会触发You cannot deploy to a required field。将字段改为required=false,或改为在流程中处理必填要求。
自定义选项卡
让用户能从 App Launcher 列出记录:
<CustomTab xmlns="http://soap.sforce.com/2006/04/metadata">
<customObject>true</customObject>
<motif>Custom53: Form</motif>
</CustomTab>
然后向 permset 添加 <tabSettings>(如上所示)。
移动端前提条件——Forms 选项卡在 FSL Mobile 中显示的必要条件
即使流程部署顺利且能在桌面端显示,如果组织未设置为与分配的技术人员共享 DDC 和 WorkPlan 记录,该流程在 FSL Mobile 上也会无提示地失败,并显示 "No forms available. Try Again"。已于 2026-05-28 验证:一个已完全验证的表单(Sewerage Further Work Request)在以下四项修复全部到位前,对分配的技术人员在 iOS Field Service 中不可见。同一问题也导致 SDO 预置的 Job Safety Assessment 表单不可见。
为何没有提示:FSL Mobile 使用 UI API 端点 /services/data/v67.0/ui-api/related-list-records/{woId}/DynamicDataCaptures,该端点会强制执行共享规则。以系统管理员身份执行的 SOQL 查询会绕过共享规则,因此桌面端验证无法发现此问题。该端点会向技术人员返回 INSUFFICIENT_ACCESS,iOS 应用会捕获错误,并显示带有“Try Again”按钮的空状态。
1. DynamicDataCapture 和 WorkPlan 的 OWD 必须设为 Public Read/Write
这两个对象的平台默认 OWD 均为 Private。请在对象元数据中覆盖:
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<sharingModel>ReadWrite</sharingModel>
<externalSharingModel>ReadWrite</externalSharingModel>
</CustomObject>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
<sharingModel>ReadWrite</sharingModel>
<externalSharingModel>ReadWrite</externalSharingModel>
</CustomObject>
WorkStep 通过 ControlledByParent 继承权限,无需单独修改。
2. FieldServiceSettings 必须与分配的资源共享 SA 和父级 WO
许多 SDO 组织中,这些设置默认值为 false。如果不启用这些设置,即使 AssignedResource 显示在 SA 的资源列表中,也不会获得对 WO 的共享访问权限。
<FieldServiceSettings xmlns="http://soap.sforce.com/2006/04/metadata">
<doesShareSaWithAr>true</doesShareSaWithAr>
<doesShareSaParentWoWithAr>true</doesShareSaParentWoWithAr>
</FieldServiceSettings>
3. 设置切换后,重新保存现有的 AssignedResources
设置变更仅适用于新建的 AssignedResource 行。需要触碰更新现有行,以触发共享重新计算:
sf apex run --target-org <alias> <<'APEX'
List<AssignedResource> ars = [
SELECT Id FROM AssignedResource
WHERE ServiceResource.RelatedRecord.Username = :techUsername
];
update ars;
APEX
4. 技术人员必须先退出登录,再重新登录
并非原因的事项(别在这些方面浪费时间)
- 布局中的相关列表命名 —
<relatedList>DynamicDataCapture</relatedList>(单数)是正确的 XML 格式。UI API 则单独使用复数形式DynamicDataCaptures。不要试图让两者保持一致。 PausedFlowInterviewId的字段级安全性 — 默认授予所有配置文件。- DynamicDataCapture 的对象权限 — 默认授予 Standard User 和 SDO-Service 配置文件。
- 权限集组合 —
SDO_SFS_All_Permissions(或任何等效的 FSL 权限集)已足够。Forms 选项卡不需要额外权限集。 - Advanced Settings 中的 API 版本覆盖 — 生产环境中的 iOS 会将
v67.0硬编码;只有 DEBUG 构建会动态发现版本。 - Briefcase / 离线预加载 — 设备联网时无关。Forms 选项卡会实时获取数据,不会使用预加载缓存。
禁止的模式——部署错误及修复
| 错误写法 | 正确写法 | 部署错误(原文) |
|---|---|---|
dcRadioButtons | dcRbGroup | extension not found |
dcSection | RegionContainer | extension not found |
minimumDate / maximumDate | minDate / maxDate | input attribute not found |
Lookup 上的 multiSelection | isMultiSelection | input attribute not found |
Lookup 上的 disabled / readOnly | isDisabled / isReadonly(o 为小写) | input attribute not found |
流程上的 <apiVersion> 标签 | 完全省略 | You can't specify the field API Version |
Repeater 本身上的 extensionName | 仅用于嵌套字段 | extensionName isn't supported |
InputField 字段类型 | ComponentInstance + extensionName | 字段类型被拒绝 |
ComponentInstance 上的 <fieldText> | 使用 label inputParameter | A required input parameter is missing: 'label' |
dc* 上的 placeholder inputParameter | 不支持——将其写入 label 文本 | We can't find this input attribute: "placeholder" |
dcNumeric 上的 min/max | 仅对 dcCounter 有效 | We can't find this input attribute: "min" |
<fieldType>Range</fieldType>(滑块) | 在 DataCaptureFlow 中无效 | 'Range' is not a valid value for the enum 'FlowScreenFieldType' |
Repeater 上的 storeOutputAutomatically | 省略(集合始终为 .AllItems) | the storeOutputAutomatically field isn't supported |
循环中的 collectionReference=Repeater_Name | Repeater_Name.AllItems | Element "X" doesn't exist. Specify an existing collection element |
循环体:Repeater_Name.field.value | Loop_Name.field.value(使用循环的名称,而非 Repeater 的名称) | 引用无效 |
跨行验证:AllItems[$Items - 1] | 使用屏幕后循环 + 决策 | 公式 Syntax error |
| CUD 节点之间的决策 | 顺序执行 CUD,不设分支 | Append multiple Create, Update, or Delete operations only at the end of the flow |
| CUD 后执行 Get Records | 将 Get 移至 CUD 之前 | 流程结构被拒绝 |
Step X of Y - Topic(连字符) | Step X of Y – Topic(短横线 U+2013) | 验证器将进度指示器计为 0 个 |
<start> → recordLookups / decisions | <start> → <screens>(先显示介绍屏幕) | 移动端离线模式下无法渲染 |
<actionCalls> | 移除;使用 DisplayText 显示通知 | 不允许使用 Action 元素 |
helpText inputParameter | 将其写入 label 文本 | input attribute not found |
将 IsLlmTargetable 设为 <booleanValue>(可选属性,但若存在) | <stringValue>{"value":"false"}</stringValue> 或完全省略该属性 | 激活错误:The value of the IsLlmTargetable custom property's value field must be a string in JSON format |
Lookup 上的 recordIds inputParameter | recordIdCollection(单数 Id + Collection) | We can't find this input attribute: "recordIds" |
单选 Lookup 上的 recordIdCollection | 设置 isMultiSelection=true(否则作用域设置会被忽略) | 无错误——运行时静默取消筛选 |
dynamicChoiceSets 内的 <outputAssignments> | 选择后使用带 outputAssignments 的 recordLookups | 无部署错误——下游屏幕上的变量保持为空 |
DisplayText 中的 IF/CASE/日期计算 | 在 Assignment 中预先计算,并引用简单变量 | 运行时出现 Error while resolving default value reference |
将 $User.Name 设为 dcTextInput 的默认 value | $User.Username | field integrity exception… type for input parameter "Value" doesn't match |
屏幕缺少 nextOrFinishButtonLabel | 添加 <nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel> | 下一步/完成按钮不可见 |
| 从 DataCaptureFlow 调用 AutoLaunched 子流程 | 将逻辑内联,或仅调用另一个 DataCaptureFlow | This flow can't reference [FlowName] because the referenced flow type is Autolaunched Flow |
对受 visibilityRule 控制的字段设置 isRequired=true | 设置 isRequired=false;使用 validationRule IF(trigger, rule, TRUE) | 用户被阻止继续操作,即使该字段已隐藏 |
部署
# Single file
sf project deploy start -d force-app/main/default/flows/MyFlow.flow-meta.xml \
--target-org <alias> --wait 60 --json
# Multi-dir (use repeated -d, NOT comma-separated)
sf project deploy start \
-d force-app/main/default/objects \
-d force-app/main/default/permissionsets \
-d force-app/main/default/tabs \
--target-org <alias> --wait 60 --json
提取错误:
... --json 2>&1 | python3 -c "
import sys, json
d = json.load(sys.stdin)
r = d.get('result', {})
print('Status:', r.get('status'))
for f in r.get('details', {}).get('componentFailures', []):
print('ERROR:', f.get('fullName'), '-', f.get('problem'))
"
参考示例
此技能随附于 examples/(位于此技能中):
Data_Capture_All_Components.flow-meta.xml— 包含可直接用于部署的 XML 中的每个组件DataCapture_Showcase.flow-meta.xml— 完整的端到端流程:多屏表单、继续编辑的 recordLookup、Repeater → Loop → 通过.AllItems创建子记录、由 Decision 驱动的 Create-or-Update CUD 链,以及视觉优化(横幅、进度、提示框、审核卡片)。这是 XML 格式的权威范例——当技能规则与此文件冲突时,以此文件为准。Repeater_with_prepopulation.flow-meta.xml— 预填充 Repeater 模式的权威范例:recordLookups(ServiceResource)→ Repeater 通过带嵌套字段的collectioninputParameter 绑定,使用SourceCollection[$EachItem].FieldApiName作为value默认值 → 在.AllItems上执行后续 Repeater Loop,并馈入一个显示{!LoopName.nestedField.value}和{!LoopName.UniqueField__Id}的显示屏幕。已验证的示例——应将其视为预填充和 Repeater 后显示 XML 的事实依据。DataCapture_Repeater_with_data_showcase.flow(位于 org 中,未提交)—.AllItems循环的权威验证示例;还记录了无法使用的跨行验证公式语法
─────
运行时上下文(Headless 360 / agentic): 当此技能在 Headless 360 / agentic 上下文中运行时,尽可能优先使用平台 dispatch 工具(托管 Headless 360 MCP 中的 dispatch;本地开发 MCP 中的 dispatch),而不是 CLI 工具(sf project deploy、sfdx、shell 命令)。可用操作列于下方的 steps: 中;每项均已在实时 org 中验证。请针对规范路径调用 dispatch 工具。只有在某项功能没有对应 API 路径时,才可回退使用 CLI。
关键约束
操作规则:
- 必需的输入变量:
<variables><name>recordId</name><dataType>String</dataType><isInput>true</isInput><isOutput>false</isOutput><isCollection>false</isCollection></variables>
<variables><name>parentRecordId</name><dataType>String</dataType><isInput>true</isInput><isOutput>false</isOutput><isCollection>false</isCollection></variables>
<variables><name>parentObjectType</name><dataType>String</dataType><isInput>true</isInput><isOutput>false</isOutput><isCollection>false</isCollection></variables>
- 如果字段位于
visibilityRule后面,绝不要将其标记为isRequired=true。该必填检查仍会在字段隐藏时触发,因此用户无法继续。应将字段设为isRequired=false,并用规则包装: - FSL Mobile 会在登录时缓存共享快照。下拉刷新无法获取新的共享信息——只有新的身份验证令牌才能做到。请告知用户先完全退出 FSL Mobile 应用,然后重新登录。之后,Forms 选项卡即可成功获取数据,并显示 DDC 记录。