@salesforce/afv-skills 1.28.0 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
- package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
- package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
- package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
- package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
- package/skills/dx-code-analyzer-run/SKILL.md +41 -8
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
- package/skills/platform-custom-field-generate/SKILL.md +86 -126
- package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
- package/skills/platform-value-set-generate/SKILL.md +305 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
# Advanced Picklist Reference
|
|
2
|
+
|
|
3
|
+
Detailed rules and worked examples for picklist CustomFields that go beyond a
|
|
4
|
+
simple inline value list. All XML uses the `http://soap.sforce.com/2006/04/metadata`
|
|
5
|
+
namespace with a `<CustomField>` root, exactly like the simple-picklist examples in
|
|
6
|
+
the main skill.
|
|
7
|
+
|
|
8
|
+
Covered here:
|
|
9
|
+
|
|
10
|
+
1. [Value Set References (`<valueSetName>`)](#1-value-set-references-valuesetname)
|
|
11
|
+
2. [Controlling / Dependent Picklists](#2-controlling--dependent-picklists)
|
|
12
|
+
3. [Enhanced Value Attributes (`color`, `isActive`, value-level `<description>`)](#3-enhanced-value-attributes)
|
|
13
|
+
4. [Picklist Validation Rules](#4-picklist-validation-rules)
|
|
14
|
+
5. [Scoping a Picklist to a Record Type](#5-scoping-a-picklist-to-a-record-type)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Value Set References (`<valueSetName>`)
|
|
19
|
+
|
|
20
|
+
A picklist field can either define its values **inline** or **reference an existing
|
|
21
|
+
value set** (a Global Value Set or a Standard Value Set). The shared set is defined
|
|
22
|
+
once and reused across many fields.
|
|
23
|
+
|
|
24
|
+
### ⭐ HARD RULE: `<valueSet>` is EITHER a reference OR inline — never both
|
|
25
|
+
|
|
26
|
+
A `<valueSet>` element must contain **exactly one** of:
|
|
27
|
+
|
|
28
|
+
- `<valueSetName>` — references an existing value set (this section), **OR**
|
|
29
|
+
- `<valueSetDefinition>` — defines values inline (the simple-picklist case).
|
|
30
|
+
|
|
31
|
+
Including both in the same `<valueSet>` is a deployment error.
|
|
32
|
+
|
|
33
|
+
#### ❌ INCORRECT — both reference and inline definition:
|
|
34
|
+
|
|
35
|
+
```xml
|
|
36
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
37
|
+
<fullName>Priority__c</fullName>
|
|
38
|
+
<label>Priority</label>
|
|
39
|
+
<type>Picklist</type>
|
|
40
|
+
<valueSet>
|
|
41
|
+
<restricted>true</restricted>
|
|
42
|
+
<valueSetName>Priority_Levels</valueSetName> <!-- WRONG: reference … -->
|
|
43
|
+
<valueSetDefinition> <!-- WRONG: … AND inline -->
|
|
44
|
+
<sorted>false</sorted>
|
|
45
|
+
<value>
|
|
46
|
+
<fullName>High</fullName>
|
|
47
|
+
<default>false</default>
|
|
48
|
+
<label>High</label>
|
|
49
|
+
</value>
|
|
50
|
+
</valueSetDefinition>
|
|
51
|
+
</valueSet>
|
|
52
|
+
</CustomField>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Error:** `Value set must reference a value set name or define a value set, but not both.`
|
|
56
|
+
|
|
57
|
+
#### ✅ CORRECT — reference a Global Value Set:
|
|
58
|
+
|
|
59
|
+
```xml
|
|
60
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
61
|
+
<fullName>Priority__c</fullName>
|
|
62
|
+
<label>Priority</label>
|
|
63
|
+
<description>Account priority drawn from the shared Priority Levels value set</description>
|
|
64
|
+
<inlineHelpText>Select the priority defined by the central Priority Levels list</inlineHelpText>
|
|
65
|
+
<type>Picklist</type>
|
|
66
|
+
<valueSet>
|
|
67
|
+
<restricted>true</restricted>
|
|
68
|
+
<valueSetName>Priority_Levels</valueSetName>
|
|
69
|
+
</valueSet>
|
|
70
|
+
</CustomField>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Same `<valueSetName>` element — Global vs. Standard value sets
|
|
74
|
+
|
|
75
|
+
The SAME `<valueSetName>` element is used to reference both kinds of value set; only
|
|
76
|
+
the name you put inside it differs.
|
|
77
|
+
|
|
78
|
+
| Referenced set | `<valueSetName>` value | Suffix |
|
|
79
|
+
|----------------|------------------------|--------|
|
|
80
|
+
| **StandardValueSet** (platform-defined, e.g. Industry, LeadSource) | Bare enum name | NO `__c`, NO `__gvs` → `<valueSetName>Industry</valueSetName>` |
|
|
81
|
+
| **GlobalValueSet** | Bare developer name | NO `__c`, **NO `__gvs`** → `<valueSetName>Priority_Levels</valueSetName>` |
|
|
82
|
+
|
|
83
|
+
> **Rule: always use the bare developer name — never add `__gvs`.** In API 57.0+ orgs the
|
|
84
|
+
> platform stores/displays a GlobalValueSet's name with a `__gvs` suffix internally, but the
|
|
85
|
+
> **Metadata API (deploy AND retrieve) uses the bare name** (the suffix came from a patched-out
|
|
86
|
+
> Winter '23 change that broke deploys). So `<valueSetName>Priority_Levels</valueSetName>`, never
|
|
87
|
+
> `Priority_Levels__gvs`. A retrieve showing `__gvs` (or a "returned from org but not found in
|
|
88
|
+
> local project" warning) is expected org-storage display — keep local metadata on the bare name.
|
|
89
|
+
> Never append `__c` to a value-set name either.
|
|
90
|
+
|
|
91
|
+
#### ✅ CORRECT — reference a Standard Value Set (bare name, no suffix):
|
|
92
|
+
|
|
93
|
+
```xml
|
|
94
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
95
|
+
<fullName>Industry__c</fullName>
|
|
96
|
+
<label>Industry</label>
|
|
97
|
+
<description>Industry classification from the standard Industry value set</description>
|
|
98
|
+
<inlineHelpText>Pick the industry that best describes this account</inlineHelpText>
|
|
99
|
+
<type>Picklist</type>
|
|
100
|
+
<valueSet>
|
|
101
|
+
<restricted>true</restricted>
|
|
102
|
+
<valueSetName>Industry</valueSetName>
|
|
103
|
+
</valueSet>
|
|
104
|
+
</CustomField>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### A value-set-backed field is `<restricted>` by design
|
|
108
|
+
|
|
109
|
+
When a field references a value set, its values can only change by editing the value
|
|
110
|
+
set itself — the field cannot define ad-hoc values. Set `<restricted>true</restricted>`
|
|
111
|
+
on these fields. Leaving it unrestricted is meaningless for a referenced set.
|
|
112
|
+
|
|
113
|
+
### Cross-reference: creating the value set itself
|
|
114
|
+
|
|
115
|
+
This skill only **references** an existing value set. **Creating** the GlobalValueSet
|
|
116
|
+
or editing a StandardValueSet (the `.globalValueSet-meta.xml` / `.standardValueSet-meta.xml`
|
|
117
|
+
metadata) is the job of the `platform-value-set-generate` skill. If the value set does not yet
|
|
118
|
+
exist, generate it there first, then reference it here.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 2. Controlling / Dependent Picklists
|
|
123
|
+
|
|
124
|
+
A **dependent** picklist filters its available values based on the selected value of a
|
|
125
|
+
**controlling** field (another picklist, or a checkbox). The dependency lives on the
|
|
126
|
+
**dependent** field via a `<controllingField>` element plus one `<valueSettings>` block
|
|
127
|
+
per (controlling value → dependent value) pair.
|
|
128
|
+
|
|
129
|
+
### ⭐ Use the MODERN API 38.0+ form ONLY
|
|
130
|
+
|
|
131
|
+
| Form | Elements | Status |
|
|
132
|
+
|------|----------|--------|
|
|
133
|
+
| **Modern (API 38.0+)** | `<valueSet>` → `<controllingField>` + `<valueSetDefinition>` + `<valueSettings>` (`<controllingFieldValue>` + `<valueName>`) | ✅ USE THIS |
|
|
134
|
+
| **Legacy (API ≤ 37.0)** | `<picklist>` / `<picklistValues>` / `<controllingFieldValues>` | ❌ DEPRECATED — do NOT generate |
|
|
135
|
+
|
|
136
|
+
Never emit the legacy `<picklist>`, `<picklistValues>`, or `<controllingFieldValues>`
|
|
137
|
+
tags. They are not valid against the modern Metadata API and will fail deployment.
|
|
138
|
+
|
|
139
|
+
### ⭐ HARD RULE: both the controlling and dependent field must be `<restricted>true</restricted>`
|
|
140
|
+
|
|
141
|
+
A field dependency requires a fixed, admin-defined value set on **both** ends. **Always emit
|
|
142
|
+
`<restricted>true</restricted>` inside the `<valueSet>` of the controlling field AND the
|
|
143
|
+
dependent field** — even when the request does not mention "restricted". Omitting it produces
|
|
144
|
+
an unrestricted picklist, which cannot reliably participate in a field dependency and diverges
|
|
145
|
+
from the expected metadata. This is non-negotiable for dependent picklists: if you write a
|
|
146
|
+
`<controllingField>` or `<valueSettings>`, the same `<valueSet>` must also carry
|
|
147
|
+
`<restricted>true</restricted>`.
|
|
148
|
+
|
|
149
|
+
### Structure of a dependent picklist
|
|
150
|
+
|
|
151
|
+
Inside the dependent field's `<valueSet>`, in this order:
|
|
152
|
+
|
|
153
|
+
1. `<controllingField>` — API name of the controlling field (e.g. `Country__c`).
|
|
154
|
+
2. `<valueSetDefinition>` — defines the dependent field's own values (as usual). The dependency
|
|
155
|
+
mapping does **NOT** go in here.
|
|
156
|
+
3. one or more `<valueSettings>` blocks — **siblings** of `<valueSetDefinition>`, NOT nested
|
|
157
|
+
inside it or inside any `<value>`. One block **per (controlling value, dependent value) pair**:
|
|
158
|
+
- `<controllingFieldValue>` — a controlling-field value that enables this dependent value.
|
|
159
|
+
- `<valueName>` — the dependent value that becomes available.
|
|
160
|
+
|
|
161
|
+
> ### ⛔ THE #1 MISTAKE: do NOT put `<controllingFieldValue>` inside `<value>`
|
|
162
|
+
> The mapping lives in **separate `<valueSettings>` blocks**, never as a child of a
|
|
163
|
+
> `<value>` in `<valueSetDefinition>`. Putting `<controllingFieldValue>` inside a `<value>`
|
|
164
|
+
> fails deployment with `Element controllingFieldValue invalid at this location in type CustomValue`.
|
|
165
|
+
>
|
|
166
|
+
> ```xml
|
|
167
|
+
> <!-- ❌ WRONG — controllingFieldValue nested in the value definition -->
|
|
168
|
+
> <valueSetDefinition>
|
|
169
|
+
> <value>
|
|
170
|
+
> <fullName>USA</fullName><label>USA</label><default>false</default>
|
|
171
|
+
> <controllingFieldValue>Americas</controllingFieldValue> <!-- INVALID HERE -->
|
|
172
|
+
> </value>
|
|
173
|
+
> </valueSetDefinition>
|
|
174
|
+
>
|
|
175
|
+
> <!-- ✅ CORRECT — values defined plainly; mapping in SEPARATE valueSettings siblings -->
|
|
176
|
+
> <valueSetDefinition>
|
|
177
|
+
> <value><fullName>USA</fullName><label>USA</label><default>false</default></value>
|
|
178
|
+
> </valueSetDefinition>
|
|
179
|
+
> <valueSettings>
|
|
180
|
+
> <controllingFieldValue>Americas</controllingFieldValue>
|
|
181
|
+
> <valueName>USA</valueName>
|
|
182
|
+
> </valueSettings>
|
|
183
|
+
> ```
|
|
184
|
+
>
|
|
185
|
+
> **One `<valueSettings>` per pair.** With multiple controlling values (Americas→USA,Canada;
|
|
186
|
+
> EMEA→UK,Germany) you emit **one block for each (controllingValue, dependentValue) pair** — four
|
|
187
|
+
> pairs = four `<valueSettings>` blocks. And remember: both fields carry `<restricted>true</restricted>`.
|
|
188
|
+
|
|
189
|
+
### ✅ CORRECT — State dependent on Country (USA → California, Texas)
|
|
190
|
+
|
|
191
|
+
**Controlling field — `Country__c` (a plain restricted picklist):**
|
|
192
|
+
|
|
193
|
+
```xml
|
|
194
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
195
|
+
<fullName>Country__c</fullName>
|
|
196
|
+
<label>Country</label>
|
|
197
|
+
<description>Country used to filter the dependent State picklist</description>
|
|
198
|
+
<inlineHelpText>Select the country first; the State list filters to match</inlineHelpText>
|
|
199
|
+
<type>Picklist</type>
|
|
200
|
+
<valueSet>
|
|
201
|
+
<restricted>true</restricted>
|
|
202
|
+
<valueSetDefinition>
|
|
203
|
+
<sorted>false</sorted>
|
|
204
|
+
<value>
|
|
205
|
+
<fullName>USA</fullName>
|
|
206
|
+
<default>false</default>
|
|
207
|
+
<label>USA</label>
|
|
208
|
+
</value>
|
|
209
|
+
<value>
|
|
210
|
+
<fullName>Canada</fullName>
|
|
211
|
+
<default>false</default>
|
|
212
|
+
<label>Canada</label>
|
|
213
|
+
</value>
|
|
214
|
+
</valueSetDefinition>
|
|
215
|
+
</valueSet>
|
|
216
|
+
</CustomField>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Dependent field — `State__c`, controlled by `Country__c`:**
|
|
220
|
+
|
|
221
|
+
```xml
|
|
222
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
223
|
+
<fullName>State__c</fullName>
|
|
224
|
+
<label>State</label>
|
|
225
|
+
<description>State filtered by the selected Country</description>
|
|
226
|
+
<inlineHelpText>Available states depend on the Country you picked</inlineHelpText>
|
|
227
|
+
<type>Picklist</type>
|
|
228
|
+
<valueSet>
|
|
229
|
+
<controllingField>Country__c</controllingField>
|
|
230
|
+
<restricted>true</restricted>
|
|
231
|
+
<valueSetDefinition>
|
|
232
|
+
<sorted>false</sorted>
|
|
233
|
+
<value>
|
|
234
|
+
<fullName>California</fullName>
|
|
235
|
+
<default>false</default>
|
|
236
|
+
<label>California</label>
|
|
237
|
+
</value>
|
|
238
|
+
<value>
|
|
239
|
+
<fullName>Texas</fullName>
|
|
240
|
+
<default>false</default>
|
|
241
|
+
<label>Texas</label>
|
|
242
|
+
</value>
|
|
243
|
+
</valueSetDefinition>
|
|
244
|
+
<valueSettings>
|
|
245
|
+
<controllingFieldValue>USA</controllingFieldValue>
|
|
246
|
+
<valueName>California</valueName>
|
|
247
|
+
</valueSettings>
|
|
248
|
+
<valueSettings>
|
|
249
|
+
<controllingFieldValue>USA</controllingFieldValue>
|
|
250
|
+
<valueName>Texas</valueName>
|
|
251
|
+
</valueSettings>
|
|
252
|
+
</valueSet>
|
|
253
|
+
</CustomField>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
> Each dependent value gets its own `<valueSettings>` block per enabling controlling
|
|
257
|
+
> value. If California were also valid under a second country, you would add another
|
|
258
|
+
> `<valueSettings>` block with that country's `<controllingFieldValue>` and
|
|
259
|
+
> `<valueName>California</valueName>`.
|
|
260
|
+
|
|
261
|
+
### ❌ INCORRECT — deprecated legacy form:
|
|
262
|
+
|
|
263
|
+
```xml
|
|
264
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
265
|
+
<fullName>State__c</fullName>
|
|
266
|
+
<label>State</label>
|
|
267
|
+
<type>Picklist</type>
|
|
268
|
+
<picklist> <!-- WRONG: deprecated wrapper -->
|
|
269
|
+
<controllingField>Country__c</controllingField>
|
|
270
|
+
<picklistValues> <!-- WRONG: use valueSetDefinition/value -->
|
|
271
|
+
<fullName>California</fullName>
|
|
272
|
+
<controllingFieldValues>USA</controllingFieldValues> <!-- WRONG: use valueSettings -->
|
|
273
|
+
</picklistValues>
|
|
274
|
+
</picklist>
|
|
275
|
+
</CustomField>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Error:** `Element {http://soap.sforce.com/2006/04/metadata}picklist is not allowed` — the
|
|
279
|
+
legacy dependency elements are not valid in the modern `<valueSet>` structure.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 3. Enhanced Value Attributes
|
|
284
|
+
|
|
285
|
+
### ⭐ Value-name fidelity — do NOT underscore picklist value names
|
|
286
|
+
|
|
287
|
+
A **picklist value's `<fullName>` is NOT a field API name** and must NOT be transformed.
|
|
288
|
+
Use the value text **exactly as the user spelled it**, spaces and all. A value the user
|
|
289
|
+
calls `Closed Won` is `<fullName>Closed Won</fullName>` and `<label>Closed Won</label>` —
|
|
290
|
+
**never** `Closed_Won`. Picklist value `<fullName>` permits spaces (and is not required to
|
|
291
|
+
carry `__c`); the space-to-underscore + `__c` rule applies ONLY to the **field**
|
|
292
|
+
`<fullName>` (e.g. field `Total Contract Value` → `Total_Contract_Value__c`), not to the
|
|
293
|
+
values inside it. Underscoring a value name changes the value's identity, diverges from what
|
|
294
|
+
the user asked for, and breaks any RecordType `<picklistValues>` / `<valueSettings>` that
|
|
295
|
+
reference the value by its real name.
|
|
296
|
+
|
|
297
|
+
| Element | Spaces? | `__c` suffix? | Example for "Closed Won" |
|
|
298
|
+
|---|---|---|---|
|
|
299
|
+
| **Field** `<fullName>` | ❌ replace with `_` | ✅ required | (field named) `Status__c` |
|
|
300
|
+
| **Picklist value** `<fullName>` | ✅ keep as written | ❌ never | `Closed Won` |
|
|
301
|
+
| **Picklist value** `<label>` | ✅ keep as written | ❌ never | `Closed Won` |
|
|
302
|
+
|
|
303
|
+
> ❌ `<fullName>Closed_Won</fullName>` ✅ `<fullName>Closed Won</fullName>`
|
|
304
|
+
> When a RecordType filters this value it must also reference `Closed Won` (with the space)
|
|
305
|
+
> in `<values><fullName>` — the names must match exactly across field and record type.
|
|
306
|
+
|
|
307
|
+
Inline `<value>` entries (CustomValue subfields) support more than `<fullName>`,
|
|
308
|
+
`<default>`, and `<label>`. The common extras:
|
|
309
|
+
|
|
310
|
+
| Subfield | Type | Notes |
|
|
311
|
+
|----------|------|-------|
|
|
312
|
+
| `<color>` | Hex string | UI chart/badge color, e.g. `#FF0000`. Include the leading `#`. |
|
|
313
|
+
| `<isActive>` | Boolean | `false` retires a value without deleting it (preserves history). Inactive values still count toward the 1,000-value restricted limit. |
|
|
314
|
+
| `<description>` | String | Value-level documentation, distinct from the field-level `<description>`. |
|
|
315
|
+
|
|
316
|
+
These are independent of `<default>` and `<label>` and may be combined freely.
|
|
317
|
+
|
|
318
|
+
### ✅ CORRECT — Status picklist with colors and an inactive value
|
|
319
|
+
|
|
320
|
+
```xml
|
|
321
|
+
<CustomField xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
322
|
+
<fullName>Status__c</fullName>
|
|
323
|
+
<label>Status</label>
|
|
324
|
+
<description>Lifecycle status of the record</description>
|
|
325
|
+
<inlineHelpText>Select the current lifecycle stage</inlineHelpText>
|
|
326
|
+
<type>Picklist</type>
|
|
327
|
+
<valueSet>
|
|
328
|
+
<restricted>true</restricted>
|
|
329
|
+
<valueSetDefinition>
|
|
330
|
+
<sorted>false</sorted>
|
|
331
|
+
<value>
|
|
332
|
+
<fullName>Cancelled</fullName>
|
|
333
|
+
<default>false</default>
|
|
334
|
+
<label>Cancelled</label>
|
|
335
|
+
<color>#FF0000</color>
|
|
336
|
+
<isActive>true</isActive>
|
|
337
|
+
<description>Work was stopped before completion</description>
|
|
338
|
+
</value>
|
|
339
|
+
<value>
|
|
340
|
+
<fullName>Complete</fullName>
|
|
341
|
+
<default>false</default>
|
|
342
|
+
<label>Complete</label>
|
|
343
|
+
<color>#00FF00</color>
|
|
344
|
+
<isActive>true</isActive>
|
|
345
|
+
<description>All work finished and accepted</description>
|
|
346
|
+
</value>
|
|
347
|
+
<value>
|
|
348
|
+
<fullName>Draft</fullName>
|
|
349
|
+
<default>false</default>
|
|
350
|
+
<label>Draft</label>
|
|
351
|
+
<isActive>false</isActive>
|
|
352
|
+
<description>Legacy draft state, retired from new records</description>
|
|
353
|
+
</value>
|
|
354
|
+
</valueSetDefinition>
|
|
355
|
+
</valueSet>
|
|
356
|
+
</CustomField>
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 4. Picklist Validation Rules
|
|
362
|
+
|
|
363
|
+
The Metadata API rejects malformed picklist values at deploy time. Two common failures:
|
|
364
|
+
|
|
365
|
+
### Duplicate value names
|
|
366
|
+
|
|
367
|
+
Two `<value>` entries with the same `<fullName>` inside one `<valueSetDefinition>` are
|
|
368
|
+
rejected.
|
|
369
|
+
|
|
370
|
+
#### ❌ INCORRECT — duplicate value:
|
|
371
|
+
|
|
372
|
+
```xml
|
|
373
|
+
<valueSetDefinition>
|
|
374
|
+
<sorted>false</sorted>
|
|
375
|
+
<value>
|
|
376
|
+
<fullName>Open</fullName>
|
|
377
|
+
<default>false</default>
|
|
378
|
+
<label>Open</label>
|
|
379
|
+
</value>
|
|
380
|
+
<value>
|
|
381
|
+
<fullName>Open</fullName> <!-- WRONG: duplicate fullName -->
|
|
382
|
+
<default>false</default>
|
|
383
|
+
<label>Open (again)</label>
|
|
384
|
+
</value>
|
|
385
|
+
</valueSetDefinition>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**Error:** `duplicate value found: Open is defined multiple times`
|
|
389
|
+
|
|
390
|
+
### Invalid API-name format
|
|
391
|
+
|
|
392
|
+
A picklist `<value>`'s `<fullName>` must start with a letter and must NOT contain
|
|
393
|
+
hyphens or begin with a digit. Unlike field API names, spaces ARE permitted (e.g.
|
|
394
|
+
`Closed Won`, `United Kingdom`) — see §3 value-name fidelity; do NOT underscore them.
|
|
395
|
+
The `__c` suffix is not required on value `<fullName>` values.
|
|
396
|
+
|
|
397
|
+
The stricter "only alphanumerics and single underscores, no leading digit, no double
|
|
398
|
+
or trailing underscore" rule applies to the *field* `<fullName>` (the `__c`-suffixed
|
|
399
|
+
API name), not to picklist value fullNames.
|
|
400
|
+
|
|
401
|
+
#### ❌ INCORRECT — invalid value API name:
|
|
402
|
+
|
|
403
|
+
```xml
|
|
404
|
+
<value>
|
|
405
|
+
<fullName>1st-Choice</fullName> <!-- WRONG: starts with digit, has hyphen -->
|
|
406
|
+
<default>false</default>
|
|
407
|
+
<label>1st Choice</label>
|
|
408
|
+
</value>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Error:** `Invalid fullName: must begin with a letter and use only alphanumeric characters and underscores`
|
|
412
|
+
|
|
413
|
+
#### ✅ CORRECT:
|
|
414
|
+
|
|
415
|
+
```xml
|
|
416
|
+
<value>
|
|
417
|
+
<fullName>First Choice</fullName> <!-- fixed: letter-first, hyphen removed, space preserved -->
|
|
418
|
+
<default>false</default>
|
|
419
|
+
<label>1st Choice</label>
|
|
420
|
+
</value>
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 5. Scoping a Picklist to a Record Type
|
|
426
|
+
|
|
427
|
+
> **Scope note.** This section covers ONLY the picklist seam between a CustomField and a
|
|
428
|
+
> RecordType — i.e. "expose a subset of *this field's* values for a given record type." It
|
|
429
|
+
> is NOT a general record-type authoring guide. Record types also carry compact layouts,
|
|
430
|
+
> page-layout assignments, branding, and more, which are out of scope here and owned by the
|
|
431
|
+
> record-type / UI metadata area. When a request goes beyond picklist value visibility, say
|
|
432
|
+
> so and defer the broader record-type work rather than guessing.
|
|
433
|
+
|
|
434
|
+
A common follow-on to creating a picklist field is "…and for the *X* record type, only show
|
|
435
|
+
values A and B." That visibility is expressed on the **RecordType**, not the field — the
|
|
436
|
+
field keeps its full value set; the record type filters which values appear.
|
|
437
|
+
|
|
438
|
+
### Picklist value filtering
|
|
439
|
+
|
|
440
|
+
Add one `<picklistValues>` block per filtered field inside the `<RecordType>`:
|
|
441
|
+
|
|
442
|
+
| Element | Requirement | Notes |
|
|
443
|
+
|---|---|---|
|
|
444
|
+
| `<picklistValues>` | One per filtered picklist | Repeat per field you filter |
|
|
445
|
+
| `<picklist>` | Required | The field API name (e.g. `Status__c`, or `StageName` for a standard field) |
|
|
446
|
+
| `<values>` | One per **exposed** value | List ONLY the values this record type should show; omitted values are hidden (NOT deleted from the field) |
|
|
447
|
+
| `<values><fullName>` | Required | The picklist value's API name |
|
|
448
|
+
| `<values><default>` | Required | `true` on exactly one value, `false` on the rest |
|
|
449
|
+
|
|
450
|
+
### The RecordType file always carries its own `<fullName>`
|
|
451
|
+
|
|
452
|
+
Unlike a CustomObject (whose name is derived from the folder/filename), a `<RecordType>` **must
|
|
453
|
+
include a `<fullName>`** element — the record type's developer name (e.g. `<fullName>Internal</fullName>`),
|
|
454
|
+
matching the filename `Internal.recordType-meta.xml`. It's bare (no object prefix); the object
|
|
455
|
+
comes from the `objects/<Object>/` folder path.
|
|
456
|
+
|
|
457
|
+
### ⭐ STEP 1 — Decide if this object needs a BusinessProcess (do this BEFORE writing files)
|
|
458
|
+
|
|
459
|
+
A record type on a **BusinessProcess-gated object — Opportunity, Lead, Case, or Solution —
|
|
460
|
+
will NOT deploy without a `<businessProcess>` reference**, even when it only filters a *custom*
|
|
461
|
+
picklist and never touches the standard status field (`Required field is missing: businessProcess`).
|
|
462
|
+
**Every other object — all custom objects (`*__c`) and other standard objects like Account or
|
|
463
|
+
Contact — needs NO BusinessProcess.** Decide first:
|
|
464
|
+
|
|
465
|
+
| Object | BusinessProcess? | Files to emit |
|
|
466
|
+
|---|---|---|
|
|
467
|
+
| Opportunity / Lead / Case / Solution | **REQUIRED** | BusinessProcess file **+** RecordType that references it (two files) |
|
|
468
|
+
| Custom object (`*__c`), Account, Contact, everything else | **None** | RecordType alone (one file) |
|
|
469
|
+
|
|
470
|
+
For the gated four, a suitable BusinessProcess may already exist in the org — reference its
|
|
471
|
+
developer name instead of generating a new one (confirm via the grounding MCP's `search_metadata`
|
|
472
|
+
if available; otherwise generate a minimal one). For everything else, **do not invent a
|
|
473
|
+
BusinessProcess** — adding one to a custom-object record type is wrong.
|
|
474
|
+
|
|
475
|
+
#### ✅ CORRECT — Opportunity "Enterprise" record type, two deployable files
|
|
476
|
+
|
|
477
|
+
```xml
|
|
478
|
+
<!-- File 1: objects/Opportunity/businessProcesses/Enterprise_Sales_Process.businessProcess-meta.xml -->
|
|
479
|
+
<BusinessProcess xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
480
|
+
<fullName>Enterprise_Sales_Process</fullName> <!-- BARE — see the api-context translation note below -->
|
|
481
|
+
<isActive>true</isActive>
|
|
482
|
+
<values><fullName>Prospecting</fullName></values> <!-- these are Opportunity STAGE (StageName) values, NOT Status__c — the BP governs the standard stage picklist. Do NOT add <default> (fails: "Cannot specify a default on: Opportunity") -->
|
|
483
|
+
<values><fullName>Qualification</fullName></values>
|
|
484
|
+
<values><fullName>Closed Won</fullName></values>
|
|
485
|
+
<values><fullName>Closed Lost</fullName></values>
|
|
486
|
+
</BusinessProcess>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
> ⛔ **`<fullName>` is BARE in the source file — strip the entity prefix that api-context gives you.**
|
|
490
|
+
> The metadata grounding / api-context for `BusinessProcess` reports the fullName in its **API
|
|
491
|
+
> form**, entity-qualified: `Opportunity.Enterprise_Sales_Process`. That is correct for the API —
|
|
492
|
+
> but in the **source/DX-format file you author**, the entity is already conveyed by the
|
|
493
|
+
> `objects/Opportunity/` folder path, so the `<fullName>` element is the **bare process name**:
|
|
494
|
+
> `Enterprise_Sales_Process`, NOT `Opportunity.Enterprise_Sales_Process`. (Same API-vs-source split
|
|
495
|
+
> as the GlobalValueSet `__gvs` suffix in §1.) Writing the entity-qualified form makes the
|
|
496
|
+
> RecordType's bare `<businessProcess>Enterprise_Sales_Process</businessProcess>` reference fail to
|
|
497
|
+
> resolve → `no BusinessProcess named Opportunity.Enterprise_Sales_Process found`. **When you pull
|
|
498
|
+
> the BP name from api-context and it looks like `Opportunity.X`, write `X` in the file.** The BP
|
|
499
|
+
> file's `<fullName>` and the RecordType's `<businessProcess>` must be the identical bare string.
|
|
500
|
+
|
|
501
|
+
```xml
|
|
502
|
+
<!-- File 2: objects/Opportunity/recordTypes/Enterprise.recordType-meta.xml -->
|
|
503
|
+
<RecordType xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
504
|
+
<fullName>Enterprise</fullName>
|
|
505
|
+
<label>Enterprise</label>
|
|
506
|
+
<active>true</active>
|
|
507
|
+
<businessProcess>Enterprise_Sales_Process</businessProcess> <!-- REQUIRED; must precede <picklistValues> -->
|
|
508
|
+
<picklistValues>
|
|
509
|
+
<picklist>Status__c</picklist>
|
|
510
|
+
<values>
|
|
511
|
+
<fullName>Qualified</fullName>
|
|
512
|
+
<default>true</default>
|
|
513
|
+
</values>
|
|
514
|
+
<values>
|
|
515
|
+
<fullName>Closed Won</fullName>
|
|
516
|
+
<default>false</default>
|
|
517
|
+
</values>
|
|
518
|
+
</picklistValues>
|
|
519
|
+
</RecordType>
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### ❌ INCORRECT — BusinessProcess file emitted but NOT referenced (most common failure)
|
|
523
|
+
|
|
524
|
+
```xml
|
|
525
|
+
<!-- File 1 (businessProcesses/Enterprise_Sales_Process...) was generated correctly, BUT -->
|
|
526
|
+
<!-- File 2, the RecordType, FORGOT the <businessProcess> element: -->
|
|
527
|
+
<RecordType xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
528
|
+
<fullName>Enterprise</fullName>
|
|
529
|
+
<active>true</active>
|
|
530
|
+
<label>Enterprise</label>
|
|
531
|
+
<!-- WRONG: no <businessProcess> here → the BP file is orphaned and the deploy fails -->
|
|
532
|
+
<picklistValues> ... </picklistValues>
|
|
533
|
+
</RecordType>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Error:** `Required field is missing: businessProcess`. Generating the BusinessProcess file is
|
|
537
|
+
only half the job — the `<RecordType>` MUST also carry the `<businessProcess>NameMatchingTheFile</businessProcess>`
|
|
538
|
+
element. Both, every time, for Opportunity/Lead/Case/Solution.
|
|
539
|
+
|
|
540
|
+
**BusinessProcess gotchas (these block deployment):**
|
|
541
|
+
- **`<fullName>` is BARE — never object-qualified.** Inside
|
|
542
|
+
`objects/Opportunity/businessProcesses/Enterprise_Sales_Process.businessProcess-meta.xml`, the
|
|
543
|
+
`<fullName>` is just `Enterprise_Sales_Process` — NOT `Opportunity.Enterprise_Sales_Process`.
|
|
544
|
+
The object comes from the folder path. Qualifying it (`<fullName>Opportunity.Enterprise_Sales_Process</fullName>`)
|
|
545
|
+
makes the RecordType's bare `<businessProcess>Enterprise_Sales_Process</businessProcess>`
|
|
546
|
+
reference unresolvable → `no BusinessProcess named Opportunity.Enterprise_Sales_Process found`.
|
|
547
|
+
The BP file's `<fullName>` and the RecordType's `<businessProcess>` value must be the **same
|
|
548
|
+
bare string**.
|
|
549
|
+
- **Two coupled parts** — (1) the `businessProcesses/<Name>.businessProcess-meta.xml` file AND
|
|
550
|
+
(2) a `<businessProcess><Name></businessProcess>` element inside the `<RecordType>`. The
|
|
551
|
+
developer name must match. Doing only one is the #1 deploy failure.
|
|
552
|
+
- **Element order** — `<businessProcess>` must appear **before** `<picklistValues>` inside
|
|
553
|
+
`<RecordType>` (it follows `<active>`). Out-of-order elements fail schema validation.
|
|
554
|
+
- **No `<default>` on Opportunity BP values** — specifying `<default>` on a `<values>` entry
|
|
555
|
+
fails with `Cannot specify a default on: Opportunity`. (Lead / Case / Solution DO allow a
|
|
556
|
+
BP default; Opportunity is the exception.)
|
|
557
|
+
|
|
558
|
+
### Deployment ordering
|
|
559
|
+
|
|
560
|
+
```text
|
|
561
|
+
GlobalValueSet / StandardValueSet (if the field draws from a value set)
|
|
562
|
+
↓
|
|
563
|
+
CustomField (the picklist field, with its full value set)
|
|
564
|
+
BusinessProcess (REQUIRED for Opportunity/Lead/Case/Solution record types)
|
|
565
|
+
↓
|
|
566
|
+
RecordType (filters the field's values; references the BusinessProcess)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
- The CustomField (and ALL the values the record type references) MUST deploy **before** the
|
|
570
|
+
RecordType, or you get `Cannot find the picklist value: <X>`.
|
|
571
|
+
- For Opportunity/Lead/Case/Solution, the `<businessProcess>` must exist (same package or
|
|
572
|
+
already in the org) before the RecordType.
|
|
573
|
+
|
|
574
|
+
### ⭐ UI-sync gotcha — values may not auto-display after API deploy
|
|
575
|
+
|
|
576
|
+
When `<picklistValues>` are loaded via the Metadata API, the values are correctly associated
|
|
577
|
+
under the hood, **but they may not automatically appear as "Selected Values" in the Record
|
|
578
|
+
Type editing screen in Setup.** The API deploy succeeds; this is a platform UI-sync
|
|
579
|
+
limitation, not a deployment error. Tell the user they may need to: **Setup → Object Manager →
|
|
580
|
+
[Object] → Record Types → [Record Type] → Edit next to the picklist → move values into
|
|
581
|
+
Selected Values → Save.** Always call this out when delivering record-type picklist filtering.
|
|
582
|
+
|
|
583
|
+
### Common failures
|
|
584
|
+
|
|
585
|
+
| Error / Symptom | Cause | Fix |
|
|
586
|
+
|---|---|---|
|
|
587
|
+
| `Required field is missing: businessProcess` | Opportunity/Lead/Case/Solution record type without a `<businessProcess>` | Add a BusinessProcess (deploy it first or reference an existing one) |
|
|
588
|
+
| `Cannot specify a default on: Opportunity` | `<default>` set on an Opportunity BusinessProcess value | Remove `<default>` from the Opportunity BP `<values>` |
|
|
589
|
+
| `Cannot find the picklist value: <X>` | A value in `<picklistValues>` doesn't exist on the field | Deploy the field with that value first; check spelling/case |
|
|
590
|
+
| Filtered values still show full set in UI | API-loaded values not promoted to Selected Values | Perform the manual Setup step above |
|