mcpField
F
field-service-data-capture-reference-configure

field-service-data-capture-reference-configure

github.com/forcedotcom开发1.1k73 次安装1 天前更新
代码仓库

关于 field-service-data-capture-reference-configure

面向 Salesforce Field Service 的 Data Capture Flow 编写与部署技能,适用于构建移动端和离线表单的开发者。它提供组件元数据、Repeater、数据写入及部署限制的具体规则,帮助处理复杂表单和部署问题。

主要功能

  1. 01DataCaptureFlow 元数据结构指导
  2. 02移动端输入与选择组件参考
  3. 03Repeater 数据预填、遍历与展示模式
  4. 04记录创建、更新及删除约束
  5. 05部署错误与表单显示问题排查

适用场景

  1. 01编写 Field Service 移动离线表单
  2. 02将 Repeater 条目保存为子记录
  3. 03从现有记录集合预填重复行
  4. 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.xml
DataCapture_Showcase.flow-meta.xml
Repeater_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>{&quot;value&quot;:&quot;false&quot;}</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
短文本dcTextInputComponentInstance
长文本dcLongTextComponentInstance
电子邮件dcEmailComponentInstance
电话dcPhoneComponentInstance
姓名dcNameComponentInstance
数值dcNumericComponentInstance
计数器dcCounterComponentInstance
日期dcDateComponentInstance
日期和时间dcDateTimeComponentInstance
复选框dcCheckboxComponentInstance
开关dcToggleComponentInstance
地址/GPSdcAddressComponentInstance
查找dcLookupComponentInstance
静态图像dcFileViewComponentInstance
上传图像(移动端)dcUpImageComponentInstance
上传文件(移动端)dcUpFileComponentInstance
签名(移动端)dcSignatureComponentInstance
单选选项列表dcPicklistComponentChoice
多选选项列表dcPicklistComponentMultiChoice
单选按钮dcRbGroupComponentChoice
复选框组dcCbGroupComponentMultiChoice
矩阵dcMatrixComponentMultiChoice
显示文本(无)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>&lt;p&gt;Id: {!Get_Source[$EachItem].Id}&lt;/p&gt;</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>&lt;p&gt;Source Id: {!Loop_Through_Repeater.UniqueField__Id}&lt;/p&gt;
&lt;p&gt;Name: {!Loop_Through_Repeater.name.value}&lt;/p&gt;
&lt;p&gt;Type: {!Loop_Through_Repeater.description.value}&lt;/p&gt;</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 字符串中,将 & 转义为 &amp;。

双列区段

<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/CASE
  • TEXT(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 转义:& → &amp;、" → &quot;、< → &lt;、> → &gt;。


随流程一起部署配套对象

自定义对象和字段部署后,不会自动为 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 选项卡会实时获取数据,不会使用预加载缓存。

禁止的模式——部署错误及修复

错误写法正确写法部署错误(原文)
dcRadioButtonsdcRbGroupextension not found
dcSectionRegionContainerextension not found
minimumDate / maximumDateminDate / maxDateinput attribute not found
Lookup 上的 multiSelectionisMultiSelectioninput attribute not found
Lookup 上的 disabled / readOnlyisDisabled / 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 inputParameterA 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_NameRepeater_Name.AllItemsElement "X" doesn't exist. Specify an existing collection element
循环体:Repeater_Name.field.valueLoop_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>{&quot;value&quot;:&quot;false&quot;}</stringValue> 或完全省略该属性激活错误:The value of the IsLlmTargetable custom property's value field must be a string in JSON format
Lookup 上的 recordIds inputParameterrecordIdCollection(单数 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.Usernamefield integrity exception… type for input parameter "Value" doesn't match
屏幕缺少 nextOrFinishButtonLabel添加 <nextOrFinishButtonLabel>Next</nextOrFinishButtonLabel>下一步/完成按钮不可见
从 DataCaptureFlow 调用 AutoLaunched 子流程将逻辑内联,或仅调用另一个 DataCaptureFlowThis 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 通过带嵌套字段的 collection inputParameter 绑定,使用 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 记录。

评论

支持 Markdown