@salesforce/afv-skills 1.62.0 → 1.64.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/agentforce-offline-experimentation-run/SKILL.md +253 -0
- package/skills/agentforce-offline-experimentation-run/scripts/build_report.py +796 -0
- package/skills/agentforce-offline-experimentation-run/scripts/resolve-token.sh +111 -0
- package/skills/agentforce-offline-experimentation-run/scripts/sfap_replay.py +1053 -0
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +21 -11
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +10 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +44 -1
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +132 -124
- package/skills/automation-sandbox-post-copy-configure/references/flow_activation_path.md +151 -0
- package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +55 -9
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +54 -107
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +1 -1
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +82 -5
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +35 -2
- package/skills/consumer-goods-promotion-bo-api-deploy/references/preflight-and-seed-queries.md +149 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +17 -1
- package/skills/dx-devops-conflict-resolve/SKILL.md +1 -5
- package/skills/dx-devops-pipeline-manage/SKILL.md +1 -5
- package/skills/dx-devops-project-manage/SKILL.md +1 -5
- package/skills/dx-devops-promote/SKILL.md +1 -5
- package/skills/dx-devops-request-status/SKILL.md +1 -5
- package/skills/dx-lifecycle-center/SKILL.md +120 -0
- package/skills/dx-lifecycle-center/examples/workflows.md +175 -0
- package/skills/dx-lifecycle-center/references/connect-api.md +123 -0
- package/skills/dx-lifecycle-center/references/connect-org.md +184 -0
- package/skills/dx-lifecycle-center/references/deploy.md +153 -0
- package/skills/dx-lifecycle-center/references/error-handling.md +21 -0
- package/skills/dx-lifecycle-center/references/hub-org.md +37 -0
- package/skills/dx-lifecycle-center/references/list-orgs.md +99 -0
- package/skills/dx-lifecycle-center/references/request-body.md +75 -0
- package/skills/dx-lifecycle-center/references/troubleshooting.md +31 -0
- package/skills/dx-lifecycle-center/scripts/poll-deploy-status.sh +200 -0
- package/skills/dx-lifecycle-center/scripts/resolve-environment.sh +120 -0
- package/skills/dx-org-devhub-configure/SKILL.md +1 -3
- package/skills/dx-org-shape-manage/SKILL.md +1 -5
- package/skills/dx-org-snapshot-manage/SKILL.md +1 -3
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +1 -3
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +1 -3
- package/skills/education-cloud-domain-configure/SKILL.md +1 -3
- package/skills/education-cloud-multi-campus-configure/SKILL.md +1 -3
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +1 -5
- package/skills/experience-ui-bundle-2gp-deploy/SKILL.md +1 -3
- package/skills/experience-ui-bundle-features-generate/SKILL.md +1 -5
- package/skills/experience-ui-bundle-mfa-configure/SKILL.md +1 -3
- package/skills/field-service-data-capture-migrate/SKILL.md +1 -3
- package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +1 -3
- package/skills/marketing-message-test/SKILL.md +1 -3
- package/skills/nonprofit-fundraising-configure/SKILL.md +240 -0
- package/skills/nonprofit-fundraising-configure/references/gotchas.md +26 -0
- package/skills/nonprofit-fundraising-configure/references/mcp-invocation.md +678 -0
- package/skills/nonprofit-fundraising-configure/references/permission-targeting.md +35 -0
- package/skills/nonprofit-volunteer-management-model-get/SKILL.md +166 -0
- package/skills/nonprofit-volunteer-management-model-get/references/entities.md +315 -0
- package/skills/nonprofit-volunteer-management-model-get/references/relationships.md +175 -0
- package/skills/nonprofit-volunteer-management-model-get/references/themes.md +143 -0
- package/skills/nonprofit-volunteer-management-records-create/SKILL.md +121 -0
- package/skills/nonprofit-volunteer-management-records-create/references/field-guide.md +284 -0
- package/skills/nonprofit-volunteer-management-records-create/references/mcp-invocation.md +248 -0
- package/skills/nonprofit-volunteer-management-records-create/references/review-format.md +140 -0
- package/skills/nonprofit-volunteer-management-records-create/scripts/expand-series.py +632 -0
- package/skills/nonprofit-volunteer-management-records-create/scripts/render-review-table.py +419 -0
- package/skills/nonprofit-volunteer-management-records-create/scripts/tests/test_expand_series.py +690 -0
- package/skills/nonprofit-volunteer-management-records-create/scripts/tests/test_render_review_table.py +421 -0
- package/skills/nonprofit-volunteer-management-structure-generate/SKILL.md +119 -0
- package/skills/nonprofit-volunteer-management-structure-generate/references/configuration-decisions.md +159 -0
- package/skills/nonprofit-volunteer-management-structure-generate/references/discovery-interview.md +137 -0
- package/skills/nonprofit-volunteer-management-structure-generate/references/edge-cases.md +48 -0
- package/skills/nonprofit-volunteer-management-structure-generate/references/mcp-invocation.md +75 -0
- package/skills/nonprofit-volunteer-management-structure-generate/references/plan-output.md +141 -0
- package/skills/platform-custom-field-generate/SKILL.md +1 -0
- package/skills/platform-custom-field-generate/references/advanced-picklists.md +5 -5
- package/skills/platform-custom-metadata-type-generate/SKILL.md +3 -0
- package/skills/platform-custom-object-generate/SKILL.md +4 -0
- package/skills/platform-custom-setting-generate/SKILL.md +3 -0
- package/skills/platform-datamask-run/SKILL.md +1 -5
- package/skills/platform-permission-set-generate/SKILL.md +3 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +1 -3
- package/skills/platform-sandbox-configure/SKILL.md +1 -3
- package/skills/platform-value-set-generate/SKILL.md +3 -0
- package/skills/sales-call-scoring-configure/SKILL.md +1 -5
- package/skills/service-agentforce-channel-configure/SKILL.md +1 -3
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +1 -3
- package/skills/service-catalog-template-coordinate/SKILL.md +1 -3
- package/skills/service-concierge-portal-generate/SKILL.md +1 -3
- package/skills/service-email-to-case-configure/SKILL.md +1 -5
- package/skills/service-itsm-agentic-setup-agent-runtime-access-assign/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -5
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +1 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +1 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +1 -3
- package/skills/service-itsm-agentic-setup-uel-user-create/SKILL.md +1 -9
- package/skills/service-itsm-incident-mgmt-configure/SKILL.md +1 -5
- package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -3
- package/skills/service-itsm-slack-itservice-configure/SKILL.md +1 -3
- package/skills/service-itsm-swarming-configure/SKILL.md +1 -3
- package/skills/service-itsm-teams-configure/SKILL.md +1 -3
- package/skills/service-itsm-teams-debug/SKILL.md +1 -3
- package/skills/service-itsm-teams-employee-agent-configure/SKILL.md +1 -3
- package/skills/service-itsm-teams-itdesk-configure/SKILL.md +1 -3
- package/skills/service-itsm-teams-itservice-configure/SKILL.md +1 -3
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +1 -3
- package/skills/service-omni-agent-users-create/SKILL.md +1 -3
- package/skills/service-omni-agent-work-sharing-configure/SKILL.md +1 -3
- package/skills/service-omni-attribute-routing-configure/SKILL.md +1 -3
- package/skills/service-omni-base-settings-configure/SKILL.md +1 -3
- package/skills/service-omni-channel-inventory-analyze/SKILL.md +1 -3
- package/skills/service-omni-channel-limits-analyze/SKILL.md +1 -3
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +1 -3
- package/skills/service-omni-command-center-analyze/SKILL.md +1 -3
- package/skills/service-omni-command-center-configure/SKILL.md +1 -3
- package/skills/service-omni-permission-set-assign/SKILL.md +1 -3
- package/skills/service-omni-presence-status-deploy/SKILL.md +1 -3
- package/skills/service-omni-presence-user-config-deploy/SKILL.md +1 -3
- package/skills/service-omni-queue-deploy/SKILL.md +1 -3
- package/skills/service-omni-queue-members-assign/SKILL.md +1 -3
- package/skills/service-omni-queue-routing-config-deploy/SKILL.md +1 -3
- package/skills/service-omni-routing-flow-deploy/SKILL.md +1 -3
- package/skills/service-omni-service-channel-configure/SKILL.md +1 -3
- package/skills/service-omni-sidebar-configure/SKILL.md +1 -3
- package/skills/service-omni-skills-based-routing-configure/SKILL.md +1 -3
- package/skills/service-omni-supervisor-config-deploy/SKILL.md +1 -3
- package/skills/service-omni-supervisor-permset-assign/SKILL.md +1 -3
- package/skills/service-omni-supervisor-surface-deploy/SKILL.md +1 -3
- package/skills/service-omni-supervisor-users-create/SKILL.md +1 -3
- package/skills/service-omni-work-skill-routing-configure/SKILL.md +1 -3
- package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -47
- package/skills/tableau-next-asset-create/README.md +0 -65
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Volunteer Management Record Creation — Field Guide
|
|
2
|
+
|
|
3
|
+
The field reference for creating Volunteer Management records: which fields exist, what each
|
|
4
|
+
means when creating a record, which to set (the tiers below), and their defaults. This file covers
|
|
5
|
+
how to fill fields; what an object means and how the model connects is in
|
|
6
|
+
`nonprofit-volunteer-management-model-get`.
|
|
7
|
+
|
|
8
|
+
## Where field facts come from
|
|
9
|
+
|
|
10
|
+
- **Standard fields** (API names, required flags, tiers, meaning) come from **this file**. Never
|
|
11
|
+
describe an object to learn its standard fields, and don't make "checking the standard fields" a
|
|
12
|
+
visible step.
|
|
13
|
+
- **A description is used for three things:** required **custom** fields the org added, **live
|
|
14
|
+
picklist values**, and grounding a fix for a write error. When a description is in hand and
|
|
15
|
+
differs from this file (an admin-renamed label, a picklist value, a required flag), the
|
|
16
|
+
description wins; API names don't change on a rename, labels do.
|
|
17
|
+
- **If the user declines the read**, this file alone still covers every standard field. Required
|
|
18
|
+
custom fields and bad picklist values then surface as write errors and are fixed in SKILL.md
|
|
19
|
+
step 9.
|
|
20
|
+
- **Query only fields this file documents.** A column that isn't in an object's table doesn't
|
|
21
|
+
exist for this skill — e.g. Operating Hours has no `Type` field, and selecting one fails the
|
|
22
|
+
whole query with `INVALID_FIELD`.
|
|
23
|
+
- **Every reuse query is bounded** to the planned records (`Name IN (…)`, or `Name LIKE` a
|
|
24
|
+
distinctive stem when the org may word it differently, e.g. `Name LIKE '%Forklift%'`). Never
|
|
25
|
+
read a whole object. Reuse keys:
|
|
26
|
+
- Volunteer Initiative, Location, Competency, Examination, Position: `Name`.
|
|
27
|
+
- Job Position: `Name` + `VolunteerInitiativeId`.
|
|
28
|
+
- Operating Hours: `TimeZone` + the set of Time Slot windows (`DayOfWeek` + `StartTime` +
|
|
29
|
+
`EndTime`). Never the name.
|
|
30
|
+
- Job Position Shift: `JobPositionId + StartDate + EndDate + StartTime + EndTime`.
|
|
31
|
+
|
|
32
|
+
The calls, and the apostrophe rule, are in `references/mcp-invocation.md`.
|
|
33
|
+
|
|
34
|
+
## Tiers
|
|
35
|
+
|
|
36
|
+
The tier codes are internal; the user sees "required", "recommended" or "optional".
|
|
37
|
+
|
|
38
|
+
| Tier | Meaning | Action |
|
|
39
|
+
|------|---------|--------|
|
|
40
|
+
| **T1** | Always set | Every record needs it (required, or an identifier/relationship without which the record is invalid or useless). |
|
|
41
|
+
| **T2** | Set by default; explain if skipping | Set it unless the user opts out, and say what skipping costs. |
|
|
42
|
+
| **T3** | Set only if the program calls for it | Leave blank unless the plan or the user needs it. |
|
|
43
|
+
| **T4** | Never set — system-managed | Filled by the platform, a formula, an auto-number or a Data Processing Engine; writing it errors or is overwritten. |
|
|
44
|
+
| **T5** | Never mention | Belongs to another feature; irrelevant to creation. |
|
|
45
|
+
|
|
46
|
+
**Fill what's known; ask before inventing.** Fill values the plan or the user already gave. For a
|
|
47
|
+
field with no established value, propose one and ask; never invent a value the user didn't give.
|
|
48
|
+
Status may be proposed as `Upcoming`. **Location Type has no default and is always asked** (or
|
|
49
|
+
copied from the paired Location on an Address).
|
|
50
|
+
|
|
51
|
+
**`Description` (every object) — summarize from context.** Summarize what you already know about
|
|
52
|
+
that record (the plan on a chained run, or the conversation). When there's nothing record-specific,
|
|
53
|
+
leave it blank. That's why Volunteer Initiative and Position carry `Description` at T2 (a headline
|
|
54
|
+
record almost always has context) and every other object at T3.
|
|
55
|
+
|
|
56
|
+
**`Req` vs `Tier` differ on purpose.** `Req` is what the platform rejects when blank; `Tier` is what
|
|
57
|
+
this skill fills anyway. A shift with no `StartDate` is useless, so it is T1 though `Req: No`.
|
|
58
|
+
|
|
59
|
+
## Picklists
|
|
60
|
+
|
|
61
|
+
Read every picklist's values from the object description at run time; never assume a set, because
|
|
62
|
+
admins change them. Offer every active value.
|
|
63
|
+
|
|
64
|
+
- **Platform picklists** (Time Zone, Time Slot Day of Week and Type): the values are fixed. A value
|
|
65
|
+
outside them is rejected before the write, never sent.
|
|
66
|
+
- **Every other picklist:** if no live value fits, ask whether to add one. Adding a value needs its
|
|
67
|
+
own yes from the user (`references/mcp-invocation.md` → Adding a picklist value): on headless-360
|
|
68
|
+
the user adds it in Setup, on the Salesforce CLI you add it for them. A value that isn't in the
|
|
69
|
+
list yet is rejected on write, so add it first.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Creation scope
|
|
74
|
+
|
|
75
|
+
This skill creates the **program structure**. The tables below cover every object it writes.
|
|
76
|
+
Per-person and operational records (assignments, availability, a person's qualifications, rollups)
|
|
77
|
+
are out of scope — see [Out of scope](#out-of-scope--never-created).
|
|
78
|
+
|
|
79
|
+
### VolunteerInitiative — the program container (top of the graph)
|
|
80
|
+
|
|
81
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
82
|
+
|-------|------|-----|------|---------|--------|
|
|
83
|
+
| `Name` | Text (Name) | Yes | **T1** | The name of the program — the top-level initiative its job positions, shifts, and assignments roll up under. | — |
|
|
84
|
+
| `Status` | Unrestricted picklist | No | **T2** | The lifecycle status of the program. | Propose `Upcoming` for a new program. |
|
|
85
|
+
| `Description` | Text(255) | No | **T2** | A description of the program. | Set by default; summarize from context. |
|
|
86
|
+
| `StartDate` | Date | No | T3 | The date the program's volunteering starts. | Optional; set together with `EndDate` for a bounded program. |
|
|
87
|
+
| `EndDate` | Date | No | T3 | The date the program's volunteering ends. | Optional; set alongside `StartDate` for a bounded program. |
|
|
88
|
+
| `IsPublished` | Boolean | No | T3 | Whether the program is visible to volunteers in an Experience Cloud site (default `false`). | Nothing out of the box publishes it — set only if publishing now. |
|
|
89
|
+
| `ParentVolunteerInitiative` (`ParentVolunteerInitiativeId`) | Lookup → VolunteerInitiative | No | T3 | The parent program in the initiative hierarchy — engagement rollups aggregate up to parents (max 5 levels, max 1000 child relations). | Write the parent first, then the children in a second call with the parent's **literal Id** — a `@refId` to another Volunteer Initiative in the same call fails with `MALFORMED_ID`. |
|
|
90
|
+
| `TotalVolunteerHours`, `TotalActiveVolunteerCount`, `AttendanceRate`, `OpenAssignmentCount`, `FilledAssignmentCount`, `TotalAssignmentCount` | Number | No | **T4** | Data Processing Engine engagement rollups — hours volunteered, active volunteers, attendance rate, and open/filled/total assignment counts. | Empty containers until you build the automation; writing them is meaningless/overwritten — never set. |
|
|
91
|
+
|
|
92
|
+
### Location — the physical/virtual site
|
|
93
|
+
|
|
94
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
95
|
+
|-------|------|-----|------|---------|--------|
|
|
96
|
+
| `Name` | Text (Name) | Yes | **T1** | The name of the site where the volunteer work happens — what job positions link to and matching compares against. | — |
|
|
97
|
+
| `LocationType` | Unrestricted picklist | Yes | **T1** | The kind of site. | **No default — always ask.** Offer the live values; if none fits, offer to add one. |
|
|
98
|
+
| `TimeZone` | Platform picklist (IANA) | No | **T2** | The site's time zone — it feeds the shift and Operating-Hours time zones used in time-based matching. | Set by default when shifts/matching are time-based — it feeds the shift and Operating-Hours time zones; skipping it breaks time-correct scheduling. |
|
|
99
|
+
| `VisitorAddress` (`VisitorAddressId`) | Lookup → Address | No | **T2** (new site) | A lookup to the site's postal `Address` record. | A Location has **no inline postal fields**, so the street address lives in a paired `Address`: create the **Location first**, then the Address, then back-fill this lookup. See the **Address** entity below for the fill policy and the 3-step circular write. |
|
|
100
|
+
| `Description` | Text | No | T3 | A brief description of the site. | Summarize from context; leave blank if none. |
|
|
101
|
+
|
|
102
|
+
Query for an existing Location by name and reuse it before creating one. A virtual Location has no Address.
|
|
103
|
+
|
|
104
|
+
### Address — the paired postal address for a Location
|
|
105
|
+
|
|
106
|
+
Every **new onsite** Location gets a paired `Address`; that's where the street address lives
|
|
107
|
+
(`Location` itself has none). A virtual Location gets none unless the user asks. A **reused**
|
|
108
|
+
Location: read its `VisitorAddressId` — if it's set, the Location already has an Address, so don't
|
|
109
|
+
create one; if it's empty, offer one.
|
|
110
|
+
|
|
111
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
112
|
+
|-------|------|-----|------|---------|--------|
|
|
113
|
+
| `Parent` (`ParentId`) | Master-detail → Location | Yes | **T1** | The site (Location) this address is the postal address for. | Set to the paired Location's **literal Id**. Master-detail, so it can't be moved to another Location later. See the 3-step write below. |
|
|
114
|
+
| `LocationType` | Unrestricted picklist | Yes | **T1** | The kind of site — mirrors the paired Location's type. | Required, **no default** — **copy the paired Location's `LocationType`**; it must come from the Location, never assumed. |
|
|
115
|
+
| `Street`, `City`, `State`, `PostalCode`, `Country` | Text | No | **T1** (user-supplied) | The site's postal address — the reason this record exists (a Location has no inline street fields). | **Ask the user; never fabricate.** Populate whatever they give; a sub-field they omit stays blank. |
|
|
116
|
+
| `AddressType` | Unrestricted picklist | No | **T2** | The kind of address. | Offer the live values; if none fits, offer to add one or leave it blank. |
|
|
117
|
+
| `TimeZone` | Platform picklist (IANA) | No | **T2** | The site address's time zone. | **Copy the Location's `TimeZone`** — matters for organizations spanning zones. |
|
|
118
|
+
| `Description` | Text | No | T3 | A brief description of the site's address. | Summarize from context; blank if none. |
|
|
119
|
+
| `Latitude`, `Longitude` | Double | No | T3 | The geographic coordinates of the site's address. | Copy from the Location **only if** it carries geo values; else skip. |
|
|
120
|
+
| `DrivingDirections` | Text | No | T3 | Directions to the site. | Copy from the Location if present. |
|
|
121
|
+
| `GeocodeAccuracy` | Picklist | No | **T4** | The accuracy level of the site address's geocode. | Geocoding-managed system value — never hand-set. |
|
|
122
|
+
|
|
123
|
+
**3-step write.** `Address.ParentId` → Location is required and `Location.VisitorAddressId` →
|
|
124
|
+
Address points back, so one call can't create the pair. **(1)** Write the **Location** (no
|
|
125
|
+
`VisitorAddressId`); **(2)** write the **Address** with `ParentId` = the Location's literal Id;
|
|
126
|
+
**(3)** update the Location's `VisitorAddressId` to the Address's Id. Calls in
|
|
127
|
+
`references/mcp-invocation.md`.
|
|
128
|
+
|
|
129
|
+
### OperatingHours — an availability schedule (matching)
|
|
130
|
+
|
|
131
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
132
|
+
|-------|------|-----|------|---------|--------|
|
|
133
|
+
| `Name` | Text (Name) | Yes | **T1** | The name of the availability schedule — the set of weekly windows (its TimeSlots) a volunteer's availability is matched against. | Time zone + a short window-set descriptor — see *Naming* below. |
|
|
134
|
+
| `TimeZone` | Platform picklist (IANA) | Yes | **T1** | The time zone the schedule's TimeSlots are read in; matching uses it to align a volunteer's availability with shift times. | Required and **not safely defaultable** — ask (or take from the Location's time zone). |
|
|
135
|
+
| `Description` | Text | No | T3 | A description of the schedule. | Summarize from context; blank if none. |
|
|
136
|
+
|
|
137
|
+
**Create Operating Hours and Time Slots only when the user explicitly asks.** Nothing links them
|
|
138
|
+
to a Location or Job Position; a volunteer's own availability record (out of scope) points at them
|
|
139
|
+
at runtime.
|
|
140
|
+
|
|
141
|
+
**Reuse.** One Operating Hours can serve many volunteers. Before creating one, query for an
|
|
142
|
+
existing Operating Hours with the same `TimeZone` and the same set of Time Slot windows, and reuse
|
|
143
|
+
it. The name never decides reuse.
|
|
144
|
+
|
|
145
|
+
**Naming.** Time zone + a short window-set descriptor (`America/Los_Angeles — Mornings`,
|
|
146
|
+
`America/New_York — Weekends`), so schedules that share a time zone stay distinct. A site label
|
|
147
|
+
(`Raceway Hours`) is fine as a human-readable tag, but the name never binds the record to a site.
|
|
148
|
+
Use a bare time-zone name only when that zone has a single schedule.
|
|
149
|
+
|
|
150
|
+
### TimeSlot — a weekly availability window (child of OperatingHours)
|
|
151
|
+
|
|
152
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
153
|
+
|-------|------|-----|------|---------|--------|
|
|
154
|
+
| `OperatingHours` (`OperatingHoursId`) | Master-Detail → OperatingHours | Yes | **T1** | The availability schedule this window belongs to. | Set to the parent's **literal Id**. **Write the Operating Hours first, then its Time Slots in a second call.** |
|
|
155
|
+
| `DayOfWeek` | Platform picklist | No | **T1** | The day of the week the volunteer is available (`Sunday`…`Saturday`). | Defaults to `Sunday` when unset — rarely the intended day, so **ask**. |
|
|
156
|
+
| `StartTime` | Time | Yes | **T1** | The time the availability window starts. | No default — ask. |
|
|
157
|
+
| `EndTime` | Time | Yes | **T1** | The time the availability window ends. | Must be ≥ `StartTime`. No default — ask. |
|
|
158
|
+
| `Type` | Platform picklist | No | T3 | The kind of time slot (`Normal` or `Extended`). | Defaults to `Normal`; Volunteer Management doesn't use it — skip unless the user asks for `Extended`. |
|
|
159
|
+
| `TimeSlotNumber` | Auto-number | System | **T4** | The record's auto-generated name (autonumber mask `TS-{0000}` — e.g. `TS-0001`). | Platform-assigned (`createable=false`); never set. |
|
|
160
|
+
|
|
161
|
+
**No duplicate windows.** Within an OperatingHours, don't create a TimeSlot that duplicates an
|
|
162
|
+
existing window (same `DayOfWeek` + `StartTime` + `EndTime`). Reuse the parent OperatingHours and
|
|
163
|
+
its existing TimeSlots instead of re-creating an identical set.
|
|
164
|
+
|
|
165
|
+
### Position — reusable role template (optional)
|
|
166
|
+
|
|
167
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
168
|
+
|-------|------|-----|------|---------|--------|
|
|
169
|
+
| `Name` | Text (Name) | Yes | **T1** | The name of the reusable role template that postings are created from. | — |
|
|
170
|
+
| `Status` | Unrestricted picklist | No | **T2** | The status of the role template. | Propose `Upcoming`. |
|
|
171
|
+
| `Description` | Text(255) | No | **T2** | A description of the role. | Set by default. |
|
|
172
|
+
| `IsInternalApprovalRequired` | Boolean | No | T3 | Whether volunteer signup for postings that use this role requires internal approval (default `false`). | Does nothing until the customer sets up an Approval Process. |
|
|
173
|
+
| `Type` | Unrestricted picklist | No | T3 | The type of role. | Skip unless the org has values or the user asks to add one. |
|
|
174
|
+
| `Code` | Text | No | T3 | A short code for the role. | — |
|
|
175
|
+
|
|
176
|
+
Create a Position only when a role is shared across postings or programs.
|
|
177
|
+
|
|
178
|
+
### PositionQualification — a requirement on the template
|
|
179
|
+
|
|
180
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
181
|
+
|-------|------|-----|------|---------|--------|
|
|
182
|
+
| `Position` (`PositionId`) | Master-Detail → Position | Yes | **T1** | The role template this requirement belongs to; matching checks it for every Job Position that uses the Position. | Set to the Position's literal Id — write the Position first. |
|
|
183
|
+
| `QualificationReferenceRecord` (`QualificationReferenceRecordId`) | Lookup {Competency, Examination} | Yes | **T1** | The Competency or Examination a volunteer must have to fill the role. | Matching uses only Competency requirements; an Examination requirement is informational. |
|
|
184
|
+
| `QualificationLevel` | Integer | No | **T2** | The qualification level the role requires — matching compares a volunteer's level against it. | Set it whenever matching is wanted: a Competency requirement without a level is skipped by matching. |
|
|
185
|
+
| `ProficiencyLevel` | Unrestricted picklist | No | T3 | A descriptive proficiency label for the requirement. | Not used in matching. |
|
|
186
|
+
|
|
187
|
+
### JobPosition — a concrete posting (role × site)
|
|
188
|
+
|
|
189
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
190
|
+
|-------|------|-----|------|---------|--------|
|
|
191
|
+
| `Name` | Text (Name) | Yes | **T1** | The name of the posting volunteers browse and sign up for. | — |
|
|
192
|
+
| `Title` | Text(255) | Yes | **T1** | The title of the posting. | Also required — **both `Name` and `Title` must be set.** Can hold the same value or differ (e.g. an internal-facing name vs. an external/public one). |
|
|
193
|
+
| `Status` | Unrestricted picklist | No | **T2** | The lifecycle status of the posting. | Propose `Upcoming`. |
|
|
194
|
+
| `Location` (`LocationId`) | Lookup → Location | No | **T2** | The site where this posting's work happens. | Optional; set it when the work has a site — it drives time and place matching. |
|
|
195
|
+
| `VolunteerInitiative` (`VolunteerInitiativeId`) | Lookup → VolunteerInitiative | No | **T2** | The parent program this posting rolls up to. | **API name is `VolunteerInitiativeId`** (label "Related Volunteer Initiative") — *not* `RelatedVolunteerInitiative` (that field is on JobPositionAssignment). |
|
|
196
|
+
| `Position` (`PositionId`) | Lookup → Position | No | T3 | The reusable role template this posting is created from. | Only when a Position template is in use. Its qualifications are checked by matching; nothing is copied. |
|
|
197
|
+
| `Description` | Text Area(255) | No | T3 | A description of the posting shown to volunteers. | Summarize from context; blank if none. |
|
|
198
|
+
| `StartDate` / `EndDate` | Date | No | T3 | The posting's active window. | — |
|
|
199
|
+
| `Manager` (`ManagerId`) | Lookup → Account | No | T3 | The posting's manager (an Account). | — |
|
|
200
|
+
| `Code` | Text(255) | No | T3 | A short code for the posting. | — |
|
|
201
|
+
| `LastFilledDate` | Date | No | T3 | The date the posting was last filled. | Nothing fills it automatically. A new posting hasn't been filled, so leave it blank unless the user gives a date. |
|
|
202
|
+
|
|
203
|
+
### JobPositionQualification — a posting-specific requirement
|
|
204
|
+
|
|
205
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
206
|
+
|-------|------|-----|------|---------|--------|
|
|
207
|
+
| `JobPosition` (`JobPositionId`) | Master-Detail → JobPosition | Yes | **T1** | The posting this requirement applies to. | Set to the Job Position's literal Id — write the Job Position first. |
|
|
208
|
+
| `QualificationReferenceRecord` (`QualificationReferenceRecordId`) | Lookup {Competency, Examination} | Yes | **T1** | The Competency or Examination this posting requires, in addition to its Position's requirements. | Matching uses only Competency requirements; an Examination requirement is informational. Ask. |
|
|
209
|
+
| `QualificationLevel` | Integer | No | **T2** | The qualification level this posting requires — matching compares a volunteer's level against it. | Set it whenever matching is wanted: a Competency requirement without a level is skipped by matching. |
|
|
210
|
+
| `ProficiencyLevel` | Unrestricted picklist | No | T3 | A descriptive proficiency label for the requirement. | Not used in matching. |
|
|
211
|
+
| `Description` | Text(255) | No | T3 | A description of the requirement. | From context; blank if none. |
|
|
212
|
+
|
|
213
|
+
Matching uses QualificationLevel on Competency qualifications. A Job Position's own qualifications and its Position's qualifications are checked together, keeping the higher level per Competency. Nothing is copied from the Position to the Job Position — don't add a Job Position Qualification that repeats one on its Position.
|
|
214
|
+
|
|
215
|
+
### JobPositionShift — a dated, time-bounded slot
|
|
216
|
+
|
|
217
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
218
|
+
|-------|------|-----|------|---------|--------|
|
|
219
|
+
| `JobPosition` (`JobPositionId`) | Lookup → JobPosition | Yes | **T1** | The posting this shift is a dated, time-bounded slot of. | Set to the Job Position's literal Id. |
|
|
220
|
+
| `StartDate` | Date | No | **T1** | The date the shift takes place. | **No `StartDateTime` field exists** — date and time are separate. Ask. |
|
|
221
|
+
| `EndDate` | Date | No | **T1** | The shift's end date. | Equal to `StartDate` on a same-day shift; **later when the shift crosses midnight or spans days** (StartDate/EndDate are independent DATE fields). |
|
|
222
|
+
| `StartTime` | Time | No | **T1** | The time the shift starts. | — |
|
|
223
|
+
| `EndTime` | Time | No | **T1** | The time the shift ends. | Must be after `StartTime` **only on a same-day shift** (`EndDate == StartDate`); when the shift ends on a later `EndDate` the end time may be earlier (e.g. 23:00 → 03:00 next day). |
|
|
224
|
+
| `TimeZone` | Platform picklist (IANA) | No | **T1** | The shift's time zone. | No safe default — take it from the Location, or ask. |
|
|
225
|
+
| `Status` | Unrestricted picklist | No | **T2** | The lifecycle status of the shift. | Propose `Upcoming`; `scripts/expand-series.py` writes `Upcoming` when the spec has no `status`. |
|
|
226
|
+
| `MaximumAttendeesCount` | Integer | No | T3 | The maximum number of volunteers for the shift. | Set when the shift has a headcount limit. Capacity is counted, never enforced — blocking a full shift needs a customer validation rule. |
|
|
227
|
+
| `Description` | Text(255) | No | T3 | A description of the shift. | Summarize from context; blank if none. |
|
|
228
|
+
| `Name` | Auto-number | System | **T4** | The record's auto-generated name (autonumber mask `JPS-{0000000000}` — e.g. `JPS-0000000001`). | Platform-assigned (`createable=false`); never set. |
|
|
229
|
+
| `PositionAssignmentCount` | Integer | No | **T4** | The number of volunteers assigned to the shift. | Maintained by the platform as assignments are added — never set. |
|
|
230
|
+
| `RemainingCapacity` | Formula | No | **T4** | The remaining open spots on the shift. | Formula: when both counts are set and the maximum is above 0, `MAX(0, MaximumAttendeesCount − PositionAssignmentCount)`; otherwise blank. Never set. |
|
|
231
|
+
| `RecurrenceSchedule` (`RecurrenceScheduleId`) | Lookup → RecurrenceSchedule | No | **T5** | A lookup to the recurrence schedule the shift belongs to. | Owned by Volunteer Management's own recurrence feature — never create or link; write one shift per occurrence instead. |
|
|
232
|
+
|
|
233
|
+
Bound every series with an end date or a count (`count` in `scripts/expand-series.py`); never an open-ended set.
|
|
234
|
+
|
|
235
|
+
### Competency — a skill a qualification can require
|
|
236
|
+
|
|
237
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
238
|
+
|-------|------|-----|------|---------|--------|
|
|
239
|
+
| `Name` | Text (Name) | Yes | **T1** | The competency name — what qualifications and matching point at. | Ask. |
|
|
240
|
+
| `Category` | Unrestricted picklist | No | T3 | The category of the competency. | Skip unless the org has values or the user asks to add one. |
|
|
241
|
+
| `Description` | Text Area(1000) | No | T3 | A description of the competency. | From context; blank if none. |
|
|
242
|
+
|
|
243
|
+
Query for an existing Competency by name and reuse it before creating one.
|
|
244
|
+
|
|
245
|
+
### Examination — a credential a qualification can require
|
|
246
|
+
|
|
247
|
+
| Field | Type | Req | Tier | Meaning | Policy |
|
|
248
|
+
|-------|------|-----|------|---------|--------|
|
|
249
|
+
| `Name` | Text (Name) | Yes | **T1** | The examination or credential name. | Ask. |
|
|
250
|
+
| `Status` | Unrestricted picklist | No | T3 | The status of the examination. | Optional; offer the live values. |
|
|
251
|
+
| `Description` | Text Area(255) | No | T3 | A description of what the credential verifies. | From context; blank if none. |
|
|
252
|
+
|
|
253
|
+
Examinations are not used in matching; query for an existing one by name and reuse it before creating one.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Out of scope / never created
|
|
258
|
+
|
|
259
|
+
Created at runtime or during volunteer onboarding, or part of the person data model Volunteer
|
|
260
|
+
Management builds on. This skill never writes them; if the user asks, say so.
|
|
261
|
+
|
|
262
|
+
| Entity | Why it's not created here |
|
|
263
|
+
|--------|---------------------------|
|
|
264
|
+
| `RecurrenceSchedule` | Volunteer Management's own recurrence feature owns it; write discrete `JobPositionShift` records instead. |
|
|
265
|
+
| `JobPositionAssignment` | A person doing a shift — operational, created when a volunteer signs up. |
|
|
266
|
+
| `PersonLocationAvailability` | A volunteer's own availability (ties a Location + OperatingHours) — set during onboarding. |
|
|
267
|
+
| `PersonCompetency` / `PersonExamination` | A person's own qualifications — recorded per volunteer. |
|
|
268
|
+
| `ConstituentRole` | Marks a Person Account as a volunteer — per-person onboarding. |
|
|
269
|
+
| `DonorGiftSummary` | A Data Processing Engine rollup target, not authored directly. |
|
|
270
|
+
| `Account` / `Person Account` | The volunteer themselves — a person record created during onboarding, not by program-structure setup. |
|
|
271
|
+
| `PartyRelationshipGroup` | Designates an Account as a household — person data model. |
|
|
272
|
+
| `AccountAccountRelation` / `AccountContactRelation` / `ContactContactRelation` | Relationship links between people and organizations — person data model. |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Creation limits & validation rules
|
|
277
|
+
|
|
278
|
+
Enforce/respect these when generating the graph:
|
|
279
|
+
|
|
280
|
+
- **Volunteer Initiative hierarchy:** max 5 levels of `ParentVolunteerInitiativeId`; max 1000 child relations.
|
|
281
|
+
- **Shift generation:** a tree call takes at most 200 records. Expand every series with `scripts/expand-series.py`; over 200 records it needs `--out-dir`, which writes one body per 200-record batch.
|
|
282
|
+
- **JobPositionShift:** end datetime ≥ start datetime — i.e. `EndDate ≥ StartDate`, with `EndTime > StartTime` required **only when `EndDate == StartDate`** (a shift that crosses midnight or spans days has a later `EndDate`, so its `EndTime` may be earlier); `MaximumAttendeesCount ≥ 0`.
|
|
283
|
+
- **JobPositionAssignment:** end datetime ≥ start datetime.
|
|
284
|
+
- **Capacity:** counted, never enforced — nothing out of the box blocks a signup when a shift is full. A customer validation rule may reject writes; handle its error in SKILL.md step 9.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Org Calls — headless-360 and Salesforce CLI
|
|
2
|
+
|
|
3
|
+
**When to load:** Workflow steps 3–6 (reads) and 8 (writes). Reads run only after the user agrees to the org read; a write runs only after the user confirms that object's records table.
|
|
4
|
+
|
|
5
|
+
Each call is shown for both connections, headless-360 and the Salesforce CLI (with the org's alias); they return the same result. See SKILL.md → Mechanism. Read CLI output from `--json` on stdout only; a warning on stderr mixed into it breaks the JSON.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Volunteer Management check
|
|
10
|
+
|
|
11
|
+
Every call uses API v67.0. The Salesforce CLI forms run `sf api request rest`, `sf sobject describe`, and `sf data query` against `--target-org <alias>`.
|
|
12
|
+
|
|
13
|
+
**1. Org preference**
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
headless-360: dispatch_readonly({"path": "/services/data/v67.0/setup/org/preferences/VolunteerManagementEnabled", "method": "GET"})
|
|
17
|
+
Salesforce CLI: sf api request rest "/services/data/v67.0/setup/org/preferences/VolunteerManagementEnabled" --target-org <alias>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Response: `{"isPreferenceEnabled": true}` (a 360 call may wrap it as `{"status_code": 200, "body": {...}}` — read `body.isPreferenceEnabled`).
|
|
21
|
+
|
|
22
|
+
| Result | Meaning | Do |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `true` | Volunteer Management is on | Continue to the describe |
|
|
25
|
+
| `false` | It's off | Stop. An admin turns it on in Setup → Volunteer Management Settings |
|
|
26
|
+
| 404 `NOT_FOUND` | The org doesn't have the preference (VM not provisioned) | Stop, same message |
|
|
27
|
+
| 403 | The user can't read org settings | Stop and say so; this is not the same as "off" |
|
|
28
|
+
|
|
29
|
+
**2. User access**
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
headless-360: dispatch_readonly({"path": "/services/data/v67.0/sobjects/VolunteerInitiative/describe", "method": "GET"})
|
|
33
|
+
Salesforce CLI: sf sobject describe --sobject VolunteerInitiative --api-version 67.0 --target-org <alias> --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Result | Meaning | Do |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| 200 | The user can see the Volunteer Management objects | Continue |
|
|
39
|
+
| 404 `NOT_FOUND` / `INVALID_TYPE`, or 403 | The user can't see them | Stop. The user needs the Manage Volunteer Data permission set |
|
|
40
|
+
|
|
41
|
+
Don't query permission-set assignments up front; the describe answers the question.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Queries (reuse checks, existing shifts, read-backs)
|
|
46
|
+
|
|
47
|
+
Every query uses the same call:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
headless-360: dispatch_readonly({"path": "/services/data/v67.0/query", "method": "GET", "query_params": {"q": "<SOQL>"}})
|
|
51
|
+
Salesforce CLI: sf data query -q "<SOQL>" --api-version 67.0 --target-org <alias> --json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Apostrophes:** escape an apostrophe inside a SOQL string literal with a backslash — `'Driver\'s License'`, `'O\'Brien Hall'`. Doubling it (`''`) fails with `MALFORMED_QUERY`. Inside a headless-360 JSON argument the backslash itself must be escaped, so write `O\\'Brien` in the JSON string. In the CLI's double-quoted `-q` argument, write `O\'Brien` as is. This applies to every name below.
|
|
55
|
+
|
|
56
|
+
### Reuse checks (step 4)
|
|
57
|
+
|
|
58
|
+
Put only the planned names in the `WHERE`; never query an object without a filter.
|
|
59
|
+
|
|
60
|
+
| Object | SOQL | Reuse when |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Volunteer Initiative | `SELECT Id, Name, Status, ParentVolunteerInitiativeId FROM VolunteerInitiative WHERE Name IN ('Weekly Food Pantry')` | Name matches |
|
|
63
|
+
| Location | `SELECT Id, Name, LocationType, TimeZone, VisitorAddressId FROM Location WHERE Name IN ('Eastside Pantry')` | Name matches. A non-empty `VisitorAddressId` means it already has an Address — don't create one |
|
|
64
|
+
| Competency | `SELECT Id, Name FROM Competency WHERE Name IN ('Forklift Operation')` | Name matches |
|
|
65
|
+
| Examination | `SELECT Id, Name FROM Examination WHERE Name IN ('Driver\'s License')` | Name matches |
|
|
66
|
+
| Position | `SELECT Id, Name, Status FROM Position WHERE Name IN ('Driver')` | Name matches |
|
|
67
|
+
| Job Position | `SELECT Id, Name, Title, VolunteerInitiativeId, LocationId FROM JobPosition WHERE Name IN ('Driver') AND VolunteerInitiativeId = '<Volunteer Initiative Id>'` | Name **and** Volunteer Initiative match. When the Volunteer Initiative is New, no Job Position can match — skip this query |
|
|
68
|
+
| Operating Hours (only when requested) | `SELECT Id, Name, TimeZone, (SELECT DayOfWeek, StartTime, EndTime FROM TimeSlots) FROM OperatingHours WHERE TimeZone = 'America/Chicago'` | Same Time Zone **and** the same set of Time Slot windows. The name never decides reuse |
|
|
69
|
+
|
|
70
|
+
When an exact name finds nothing but the user's wording suggests a near match, widen to a distinctive stem (`WHERE Name LIKE '%Forklift%'`) and ask before reusing a near match.
|
|
71
|
+
|
|
72
|
+
### Existing shifts (step 5)
|
|
73
|
+
|
|
74
|
+
For each reused Job Position, by its Id and the series' start-date range (SOQL date literals are unquoted):
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
SELECT Id, StartDate, EndDate, StartTime, EndTime FROM JobPositionShift
|
|
78
|
+
WHERE JobPositionId = '<reused Job Position Id>' AND StartDate >= 2027-06-05 AND StartDate <= 2027-08-28
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pass the returned rows unchanged as `excludeExisting` to `scripts/expand-series.py`. With a `count` and no end date, use the last date the script would generate as the upper bound (run the script once without `excludeExisting` to see it).
|
|
82
|
+
|
|
83
|
+
### Read-back after a write
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
SELECT Id, Name, <the fields just written> FROM <Object> WHERE Id IN ('<new Id>', ...)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Every written record appears with the values sent → the write is confirmed. A missing record or a different value → report it in the final report; don't re-write automatically.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Object descriptions (step 6)
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
headless-360: dispatch_readonly({"path": "/services/data/v67.0/sobjects/<Object>/describe", "method": "GET"})
|
|
97
|
+
Salesforce CLI: sf sobject describe --sobject <Object> --api-version 67.0 --target-org <alias> --json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
From `fields[]`:
|
|
101
|
+
|
|
102
|
+
- **Required custom field:** `custom == true && createable == true && nillable == false && defaultedOnCreate == false`.
|
|
103
|
+
- **Picklist values:** `picklistValues[]` where `active == true`. A write accepts only these values; a new one has to be added to the list first (Adding a picklist value).
|
|
104
|
+
- **Label:** `label` — use it in the records table (pass it to `render-review-table.py` as `labels`).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Writes (step 8)
|
|
109
|
+
|
|
110
|
+
### Several records of one object — one tree call
|
|
111
|
+
|
|
112
|
+
All records of one object in one call, up to 200 records. The call is all-or-nothing: either every record is created or none is.
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
headless-360: dispatch({"path": "/services/data/v67.0/composite/tree/<Object>", "method": "POST", "body": {"records": [
|
|
116
|
+
{"attributes": {"type": "<Object>", "referenceId": "<Object>_1"}, "Name": "...", ...},
|
|
117
|
+
{"attributes": {"type": "<Object>", "referenceId": "<Object>_2"}, "Name": "...", ...}]}})
|
|
118
|
+
Salesforce CLI: save the same {"records": [...]} body to a file, then
|
|
119
|
+
sf data import tree --files <file>.json --api-version 67.0 --target-org <alias> --json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Result | Meaning | Do |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| 201, `hasErrors: false` | Every record was created | Map each `referenceId` to its returned `id` and feed the Ids to the children |
|
|
125
|
+
| 400, `hasErrors: true` | Nothing in this call was created | Show each `errors[]` entry, fix, re-send the whole call |
|
|
126
|
+
|
|
127
|
+
- `scripts/expand-series.py` output is already this body.
|
|
128
|
+
- A parent written in an earlier call is a literal Id in the child field (`"JobPositionId": "0Jp..."`). A `@refId` reference (`"@JobPosition_1"`) works only for a parent in the same body.
|
|
129
|
+
- Over 200 records: send one call per batch (`expand-series.py --out-dir` writes one body per batch). If a later batch fails, the earlier batches stay; report which records exist.
|
|
130
|
+
|
|
131
|
+
### One record
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
headless-360: dispatch({"path": "/services/data/v67.0/sobjects/<Object>", "method": "POST", "body": {"Name": "...", ...}})
|
|
135
|
+
Salesforce CLI: sf data create record --sobject <Object> --values "Name='...' ..." --api-version 67.0 --target-org <alias> --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
201 with `{"id": "...", "success": true}` → created. 400 → not created; read `errorCode` and `message`.
|
|
139
|
+
|
|
140
|
+
### Location ↔ Address back-fill
|
|
141
|
+
|
|
142
|
+
After the Address exists, point its Location at it:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
headless-360: dispatch({"path": "/services/data/v67.0/sobjects/Location/<Location Id>", "method": "PATCH", "body": {"VisitorAddressId": "<Address Id>"}})
|
|
146
|
+
Salesforce CLI: sf data update record --sobject Location --record-id <Location Id> --values "VisitorAddressId=<Address Id>" --api-version 67.0 --target-org <alias> --json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
204 → updated.
|
|
150
|
+
|
|
151
|
+
### Common write errors
|
|
152
|
+
|
|
153
|
+
| Error | Meaning | Fix |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| `REQUIRED_FIELD_MISSING` | A required field (often a custom one or a validation rule) is empty | Ask the user for the value, re-send |
|
|
156
|
+
| `INVALID_OR_NULL_FOR_RESTRICTED_PICKLIST` | A value isn't in the field's list of values | Offer the live values from the describe, or offer to add the value (Adding a picklist value) |
|
|
157
|
+
| `MALFORMED_ID` | A `@refId` pointed outside its body, or at the same object in the same body | Use the parent's literal Id |
|
|
158
|
+
| `INVALID_FIELD` | A field that doesn't exist on the object | Remove it; only field-guide fields are written |
|
|
159
|
+
| `FIELD_CUSTOM_VALIDATION_EXCEPTION` | A customer validation rule rejected the record | Show the message; ask the user how to proceed |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Adding a picklist value
|
|
164
|
+
|
|
165
|
+
Only for a field in the value-set table below (never Time Zone or Time Slot Day of Week and Type, whose values are fixed), and only after the user says yes to adding that specific value (step 6).
|
|
166
|
+
|
|
167
|
+
How the value gets added depends on the connection:
|
|
168
|
+
|
|
169
|
+
| Connection | Who adds the value | How |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| headless-360 | The user, in Setup | Ask them to add it (Adding it in Setup), wait for "done", then describe the object again |
|
|
172
|
+
| Salesforce CLI | You, for the user | Retrieve, edit, check, deploy, describe again (Command patterns) |
|
|
173
|
+
|
|
174
|
+
Never write a value set through headless-360. It can't read one (`/headless/metadata` returns 400 `UNSUPPORTED_OPERATION` for `StandardValueSet`), and writing one without reading it first drops values.
|
|
175
|
+
|
|
176
|
+
Either way, the new value is used only after a fresh describe shows it in that field's `picklistValues[]`. If it isn't there yet, say so and ask the user to check Setup again or pick one of the live values; never send the write with it.
|
|
177
|
+
|
|
178
|
+
### Value sets
|
|
179
|
+
|
|
180
|
+
Every picklist the skill writes, except the platform ones, keeps its values in a standard value set:
|
|
181
|
+
|
|
182
|
+
| Field | Value set |
|
|
183
|
+
|---|---|
|
|
184
|
+
| Location → Location Type, Address → Location Type | `LocationType` |
|
|
185
|
+
| Address → Address Type | `AddressType` |
|
|
186
|
+
| Competency → Category | `CompetencyCategory` |
|
|
187
|
+
| Examination → Status | `LPIExaminationStatus` |
|
|
188
|
+
| Position → Type | `PositionTypeEnum` |
|
|
189
|
+
| Volunteer Initiative → Status | `VolunteerInitiativeStatus` |
|
|
190
|
+
| Position → Status | `PositionStatus` |
|
|
191
|
+
| Job Position → Status | `JobPositionStatus` |
|
|
192
|
+
| Job Position Shift → Status | `JobPositionShiftStatus` |
|
|
193
|
+
| Position Qualification → Proficiency Level, Job Position Qualification → Proficiency Level | `ProficiencyLevelEnum` |
|
|
194
|
+
|
|
195
|
+
Location Type is one set shared by Location and Address, and Proficiency Level one set shared by both qualification objects, so a value added for one appears on both.
|
|
196
|
+
|
|
197
|
+
### Adding it in Setup (headless-360)
|
|
198
|
+
|
|
199
|
+
Tell the user exactly what to add and where, in one message:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Add "{value}" to {Object} → {Field}:
|
|
203
|
+
Setup → Object Manager → {Object} → Fields & Relationships → {Field} → Values → New
|
|
204
|
+
(/lightning/setup/ObjectManager/{Object}/FieldsAndRelationships/{Field API name}/view)
|
|
205
|
+
Enter "{value}" as the value, leave "Default" unchecked, and save. Reply "done" when it's added.
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
When the value set is shared (Location Type, Proficiency Level), say that the value also appears on the other object. On "done", describe the object again before using the value.
|
|
209
|
+
|
|
210
|
+
### Command patterns (Salesforce CLI)
|
|
211
|
+
|
|
212
|
+
Run from inside a Salesforce project (`sf project generate --name <dir>` makes one).
|
|
213
|
+
|
|
214
|
+
| Intent | Command |
|
|
215
|
+
|---|---|
|
|
216
|
+
| Read the current value set | `sf project retrieve start --metadata StandardValueSet:<value set> --api-version 67.0 --target-org <alias> --json` |
|
|
217
|
+
| Check the edited set without saving it | `sf project deploy start --dry-run --metadata StandardValueSet:<value set> --test-level NoTestRun --api-version 67.0 --target-org <alias> --json` |
|
|
218
|
+
| Deploy the edited set | `sf project deploy start --metadata StandardValueSet:<value set> --test-level NoTestRun --api-version 67.0 --target-org <alias> --json` |
|
|
219
|
+
| Confirm the value is live | `sf sobject describe --sobject <Object> --api-version 67.0 --target-org <alias> --json` — the value appears in that field's `picklistValues[]` |
|
|
220
|
+
|
|
221
|
+
The edit, in `force-app/main/default/standardValueSets/<value set>.standardValueSet-meta.xml`: add one block after the last `<standardValue>` (or before `</StandardValueSet>` when the set is empty) and leave the existing ones unchanged.
|
|
222
|
+
|
|
223
|
+
```xml
|
|
224
|
+
<standardValue>
|
|
225
|
+
<fullName><approved value></fullName>
|
|
226
|
+
<default>false</default>
|
|
227
|
+
<label><approved value></label>
|
|
228
|
+
</standardValue>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
A check reports `"checkOnly": true` and `"success": true` when the set is valid; then deploy.
|
|
232
|
+
|
|
233
|
+
### Rules
|
|
234
|
+
|
|
235
|
+
| Constraint | Rationale |
|
|
236
|
+
|---|---|
|
|
237
|
+
| Retrieve before every deploy and keep every existing value | The deploy replaces the whole value set; a value missing from the file is removed from the org |
|
|
238
|
+
| Add only the value the user approved, with `default` false | Any other change to the set wasn't approved |
|
|
239
|
+
| Check, then deploy, with `--test-level NoTestRun` | A value set carries no Apex; without it an org with low test coverage rejects the deploy |
|
|
240
|
+
| Describe again before using the new value | The write is confirmed only when the value is in `picklistValues[]` |
|
|
241
|
+
|
|
242
|
+
### Troubleshooting
|
|
243
|
+
|
|
244
|
+
| Issue | Resolution |
|
|
245
|
+
|---|---|
|
|
246
|
+
| "`<dir>` does not contain a valid Salesforce DX project" | Create a project with `sf project generate --name <dir>` and run from inside it |
|
|
247
|
+
| The check or deploy fails | Show the error and don't retry with a guessed change; ask the user to pick one of the live values |
|
|
248
|
+
| The user says "done" but the describe doesn't list the value | Say the value isn't there yet; ask them to check that it was saved on the right field, or to pick one of the live values |
|