@salesforce/afv-skills 1.55.0 → 1.56.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-generate/SKILL.md +8 -0
- package/skills/agentforce-generate/references/actions-reference.md +2 -2
- package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
- package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
- package/skills/agentforce-observe/SKILL.md +13 -9
- package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -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 +32 -1
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
- package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
- package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
- package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
- package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
- package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
- package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
- package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
- package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
- package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
- package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
- package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
- package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
- package/skills/dx-org-analyze/SKILL.md +0 -1
- package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
- package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
- package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
- package/skills/dx-org-manage/SKILL.md +6 -50
- package/skills/dx-org-manage/examples/README.md +10 -16
- package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
- package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
- package/skills/dx-org-shape-manage/SKILL.md +4 -3
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
- package/skills/experience-accessibility-validate/SKILL.md +8 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
- package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
- package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
- package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
- package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
- package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
- package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
- package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
- package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
- package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
- package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
- package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
- package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
- package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
- package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
- package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
- package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
- package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
- package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
- package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
- package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
- package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
- package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
- package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
- package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
- package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
- package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
- package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
- package/skills/mobile-apps-create/SKILL.md +1 -1
- package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
- package/skills/platform-sandbox-configure/SKILL.md +108 -133
- package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
- package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
- package/skills/platform-widget-generate/SKILL.md +26 -18
- package/skills/platform-widget-generate/examples/formulas.json +191 -0
- package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
- package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
- package/skills/sales-call-scoring-configure/SKILL.md +318 -0
- package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
- package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
- package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
- package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
- package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
- package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
- package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
- package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
- package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
- package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
- package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
- package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
- package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
- package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
- package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
- package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
- package/skills/service-de-channel-activate/SKILL.md +6 -5
- package/skills/service-de-channel-create/SKILL.md +3 -3
- package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
- package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
- package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
- package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
- package/skills/service-helpagent-coordinate/SKILL.md +3 -2
- package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
- package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
- package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
- package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
- package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
- package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
- package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
- package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
- package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
- package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
- package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
- package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
- package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
- package/skills/dx-org-manage/references/cli_flags.md +0 -67
- package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
- package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
- package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
- package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
- /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
- /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# Act 2 of 3 — Assign Users to the Sync Configuration
|
|
2
|
+
|
|
3
|
+
Part of the [`consumer-goods-sync-management-configure`](../SKILL.md) skill — the **second of three acts**.
|
|
4
|
+
Runs after [Act 1](act1-setup-sync.md) (it binds a user to the config Act 1
|
|
5
|
+
installed — the admin-confirmed baseline root, e.g. the Retail Execution baseline); followed by
|
|
6
|
+
[Act 3](act3-plan-verify.md).
|
|
7
|
+
|
|
8
|
+
**This Act verifies, checks a precondition, then writes.** It verifies the user (read), confirms
|
|
9
|
+
the user's IOU membership (read — a **precondition**, not a write; the package doesn't own IOU
|
|
10
|
+
membership and this skill does **not** create it), then writes the config assignment. **The IOU
|
|
11
|
+
precondition (A2.2) only matters under the `Default_IOU` strategy** — see below.
|
|
12
|
+
|
|
13
|
+
**Detect the resolution strategy first, because it decides whether the IOU precondition even
|
|
14
|
+
applies.** Read the target config root's `Business_Area_Resolution_Strategy__c` (via
|
|
15
|
+
`GET /v1/syncconfig/configs`). The runtime resolver (`ClientAppMapperService.mapClientApp`) picks
|
|
16
|
+
the root *by strategy, gated on IOU presence*, then resolves the binding **User → Role → Profile**
|
|
17
|
+
from the mapping rows:
|
|
18
|
+
- **`Custom_User_Field` (the default strategy) — no IOU needed.** This root is selected for
|
|
19
|
+
users **without** a default mobility IOU, and the binding is resolved entirely from the
|
|
20
|
+
`Sync_Client_App_Profile_Mapping__c` rows. So a **Role (00E)** or **Profile (00e)** mapping —
|
|
21
|
+
written by A2.3's endpoint — binds every user with that role/profile in one shot, with **no IOU
|
|
22
|
+
membership involved at all**. A2.2 does not apply here — skip it.
|
|
23
|
+
- **`Default_IOU` — IOU membership is the binding, and it's a precondition.** This root is selected
|
|
24
|
+
**only** for users who *have* a default mobility IOU, so that membership is what routes them.
|
|
25
|
+
**This skill reads it, it does not create it:** if the user already has a default mobility IOU,
|
|
26
|
+
proceed; **if the user has no IOU membership, report and stop** — there are no further steps, and
|
|
27
|
+
the skill does **not** write IOU membership (that's a CG Cloud core / RE concern). A Role/Profile
|
|
28
|
+
mapping alone won't rescue an IOU-less user, because this root isn't even evaluated for them.
|
|
29
|
+
|
|
30
|
+
**A missing default mobility IOU is a stop *only* under `Default_IOU` — never under
|
|
31
|
+
`Custom_User_Field`.** Under `Custom_User_Field` the strategy doesn't route by IOU at all, so a user
|
|
32
|
+
without one is **not** blocked: they bind through the mapping row with the **all-scope default**
|
|
33
|
+
(`businessArea` = `'All'`, i.e. all business areas — the `Custom_User_Field` analog of "all IOUs").
|
|
34
|
+
Only under `Default_IOU`, where the IOU *is* the binding, does an absent IOU leave nothing to bind —
|
|
35
|
+
so report-and-stop applies **there and only there**. Do not generalize the `Default_IOU` stop into a
|
|
36
|
+
blanket "no IOU ⇒ stop" rule; that would wrongly halt every `Custom_User_Field` assignment.
|
|
37
|
+
|
|
38
|
+
Choosing the wrong path produces a user who "looks assigned" but whose app resolves no config.
|
|
39
|
+
|
|
40
|
+
## What this Act does
|
|
41
|
+
|
|
42
|
+
### 1. A2.1 — Verify the target user exists — `core`
|
|
43
|
+
|
|
44
|
+
- Generic SOQL via `execute_api`:
|
|
45
|
+
`GET /query?q=SELECT Id, Name, IsActive, Profile.Name FROM User
|
|
46
|
+
WHERE Name = '<name>'` (or by username/email — prefer a unique key). **Versionless** — the
|
|
47
|
+
`execute_api` transport supplies the `/services/data/<api_version>/` base itself (see the
|
|
48
|
+
orchestrator's session state); don't pin a version into a `core` call. This is a
|
|
49
|
+
first-class `purpose: validate` pre-flight query, **not** a bespoke API. `User` is a core object —
|
|
50
|
+
namespace is irrelevant.
|
|
51
|
+
- No package code needed. Report the resolved `User.Id` (and `IsActive`) into session state;
|
|
52
|
+
if the user doesn't exist or is inactive, stop and report — don't assign into a phantom.
|
|
53
|
+
|
|
54
|
+
### 2. A2.2 — Confirm the user's Mobility IOU membership — `core` (read-only precondition)
|
|
55
|
+
|
|
56
|
+
- **Package boundary:** `Sync Management App` only **reads** `InternalOrgUnitUser` /
|
|
57
|
+
`InternalOrganizationUnit` — `ClientAppMapperService` queries the user's default mobility IOU
|
|
58
|
+
(`IsDfltMobIntrOrgUnit = true`) to route them to a `core`-mode `Sync_Config` root, and is
|
|
59
|
+
written (dynamic SOQL + `QueryException` catch) to even *compile* in pure managed-package
|
|
60
|
+
orgs where `InternalOrgUnitUser` doesn't exist. It **never writes** IOU membership, and
|
|
61
|
+
**neither does this skill** — IOU membership is a CG Cloud **core Retail Execution** concern,
|
|
62
|
+
provisioned outside this flow.
|
|
63
|
+
- **Under `Custom_User_Field` (the default) — skip this step entirely.** The binding is resolved
|
|
64
|
+
from the `Sync_Client_App_Profile_Mapping__c` row with no IOU involved, so there is nothing to
|
|
65
|
+
check here; go straight to A2.3. A user with **no** default mobility IOU is **not** a blocker under
|
|
66
|
+
this strategy — the mapping row binds them with the all-scope default (`businessArea` = `'All'`).
|
|
67
|
+
The "report and stop on a missing IOU" rule below is **`Default_IOU`-only**.
|
|
68
|
+
- **Under `Default_IOU` — check the membership as a precondition (read-only):** run the same query
|
|
69
|
+
`ClientAppMapperService.getDefaultIouId()` uses —
|
|
70
|
+
`SELECT InternalOrganizationUnitId FROM InternalOrgUnitUser WHERE UserId = :userId AND
|
|
71
|
+
IsDfltMobIntrOrgUnit = true LIMIT 1` (`core`; `InternalOrgUnitUser` is a core RE object, no
|
|
72
|
+
namespace prefix).
|
|
73
|
+
- **If it returns a row:** the user has a default mobility IOU — carry that
|
|
74
|
+
`InternalOrganizationUnitId` forward (it's the `iouId` scope A2.3 needs) and proceed.
|
|
75
|
+
- **If it returns no row:** the user has **no** Mobility IOU membership — **report and stop.**
|
|
76
|
+
Under `Default_IOU` the config root isn't even evaluated for an IOU-less user, so there is
|
|
77
|
+
nothing further to do: do **not** attempt to create IOU membership, and do **not** proceed to
|
|
78
|
+
A2.3. Say plainly that the user must be given a default mobility IOU (a core / RE task) before
|
|
79
|
+
sync assignment can continue.
|
|
80
|
+
- **In a pure managed-package org** the query throws `QueryException` (the object is absent); the
|
|
81
|
+
package treats that as the MP path (`null`) — degrade the same way rather than hard-failing.
|
|
82
|
+
|
|
83
|
+
### 3. A2.3 — Assign the user to the sync configuration installed in Act 1 — `Sync Management App` (mapping row) / `core` (IOU-implicit)
|
|
84
|
+
|
|
85
|
+
**An assignment binds a User, a Role, OR a Profile to the config** — `mappedRecordId`'s key-prefix
|
|
86
|
+
picks which (`005` = User, `00E` = Role, `00e` = Profile), and the runtime resolver matches in
|
|
87
|
+
priority **User → Role → Profile**. A **Role or Profile** mapping is the high-leverage choice: one
|
|
88
|
+
row binds everyone with that role/profile, no per-user write. Two paths, chosen by the
|
|
89
|
+
`resolution_strategy` you detected up front:
|
|
90
|
+
|
|
91
|
+
#### A2.3 elicitation flow (admin-facing) — what to gather, in order
|
|
92
|
+
|
|
93
|
+
When an admin says "assign the Sync config to a profile / role / user," walk this flow. The
|
|
94
|
+
endpoint (`POST /v1/syncconfig/assignment`) takes a resolved **Id** in `mappedRecordId`
|
|
95
|
+
(`005`=User, `00E`=Role, `00e`=Profile) — never a name — so every "name" the admin gives is
|
|
96
|
+
**resolved to an Id via a `core` SOQL** before the call. Do not skip a confirmation to save a
|
|
97
|
+
round-trip: a wrong config name or an unresolved profile silently produces a no-op binding.
|
|
98
|
+
|
|
99
|
+
1. **Establish the Config to assign to.**
|
|
100
|
+
- If the admin **didn't name a config**, list the deployed ones — `GET /v1/syncconfig/configs`
|
|
101
|
+
(`SyncConfigListEndpoint`) returns `{name, clientAppId, resolutionStrategy}` — and present the
|
|
102
|
+
**`name`** values for the admin to pick. Do not guess.
|
|
103
|
+
- If the admin **named a config**, confirm it's actually deployed: match the name against
|
|
104
|
+
`/configs`. If it's not there, say so and stop (nothing to bind to) — offer the deployed list.
|
|
105
|
+
- Carry forward the chosen config's **`name`** (→ `configPath`) **and** its
|
|
106
|
+
`resolutionStrategy` — the strategy decides whether the second field is an **IOU** (`iouId`,
|
|
107
|
+
`Default_IOU`) or a **Business Area** (`businessArea`, `Custom_User_Field`). `name` ≠
|
|
108
|
+
`clientAppId`; pass the `name`.
|
|
109
|
+
2. **Identify the mapping option: Profile, Role, or User.** Ask the admin if it's not stated.
|
|
110
|
+
3. **Resolve the target name → Id and gather the scope, per option:**
|
|
111
|
+
- **Profile:** get the profile name; if the admin didn't give one, list available profiles
|
|
112
|
+
(`SELECT Id, Name FROM Profile ORDER BY Name`) and ask. Resolve the name → **`00e` Id**
|
|
113
|
+
(`SELECT Id FROM Profile WHERE Name = '<name>'`). Gather the scope (IOU/Business Area — see
|
|
114
|
+
below). Invoke the endpoint with the `00e` Id as `mappedRecordId`.
|
|
115
|
+
- **Role:** get the role name; if not given, list available roles
|
|
116
|
+
(`SELECT Id, Name FROM UserRole ORDER BY Name`) and ask. Resolve the name → **`00E` Id**.
|
|
117
|
+
Gather the scope. Invoke with the `00E` Id as `mappedRecordId`.
|
|
118
|
+
- *Grounding & core-vs-Sync Management App:* these mirror the package's own list fetchers —
|
|
119
|
+
`ClientProfileEditExtension.getProfileIds()` (`SELECT Id, Name FROM Profile`) and
|
|
120
|
+
`getRoleIds()` (`SELECT Id, Name FROM UserRole`) — but `Profile`/`UserRole` are standard
|
|
121
|
+
platform objects (no namespace prefix) and those accessors are VF-controller/`protected`
|
|
122
|
+
(namespace-private, not headless-reachable), so the agent runs the SOQL **directly as `core`**,
|
|
123
|
+
exactly like the IOU reads. Both require the caller to have **view access** on the object.
|
|
124
|
+
- *Role name nuance:* pass the resolved **Id** forward, not the picked name. The service derives
|
|
125
|
+
the persisted mapping-row name itself — and for **roles it uses `DeveloperName`** (the API
|
|
126
|
+
name), not the display `Name` you listed (`SyncConfigAssignmentService.buildRecordName`);
|
|
127
|
+
profiles use `Name` for both. Passing the Id avoids the display-vs-API-name mismatch.
|
|
128
|
+
- **User:** identify the user and get the **`005` Id** from the org
|
|
129
|
+
(`SELECT Id, IsActive FROM User WHERE Username/Email/Name = '<value>'` — reuse A2.1). **If the
|
|
130
|
+
user isn't in the org, inform the admin and stop** — do not bind a phantom. Under `Default_IOU`,
|
|
131
|
+
the user's default mobility IOU was already confirmed in **A2.2** (the precondition check): pass
|
|
132
|
+
that confirmed `InternalOrganizationUnitId` as the scope (`iouId`). Invoke with the `005` Id as
|
|
133
|
+
`mappedRecordId`. (A2.2's read used the exact query `ClientAppMapperService.getDefaultIouId()`
|
|
134
|
+
runs: `SELECT InternalOrganizationUnitId FROM InternalOrgUnitUser WHERE UserId = :userId AND
|
|
135
|
+
IsDfltMobIntrOrgUnit = true LIMIT 1`.)
|
|
136
|
+
- *`IsDfltMobIntrOrgUnit = true` is load-bearing:* it matches **only** the user's *default
|
|
137
|
+
mobility* IOU. A secondary/non-default IOU the user belongs to is deliberately **not**
|
|
138
|
+
matched (the resolver routes such users via the MP path), so do **not** substitute another
|
|
139
|
+
IOU row.
|
|
140
|
+
- *No default mobility IOU → already stopped in A2.2:* if the user had no such membership, the
|
|
141
|
+
A2.2 precondition already reported-and-stopped under `Default_IOU`, so you never reach this
|
|
142
|
+
write. This skill does not create IOU membership to unblock it.
|
|
143
|
+
4. **The "scope" field is strategy-dependent — this is the key reconciliation:**
|
|
144
|
+
- Under **`Default_IOU`**, the admin's "IOU" is passed as **`iouId`** (the InternalOrganizationUnit
|
|
145
|
+
Id). For the **User** path this is exactly the IOU fetched from `InternalOrgUnitUser` in step 3.
|
|
146
|
+
For **Profile/Role**, there is no single user to derive it from, so **list the org's IOUs and
|
|
147
|
+
ask** — `SELECT Id, OrganizationCode, OrganizationName FROM InternalOrganizationUnit ORDER BY
|
|
148
|
+
OrganizationCode` (present **`OrganizationName` alongside `OrganizationCode`** so the admin can
|
|
149
|
+
identify the right record — a bare code like `IOU-001` is hard to recognize; the admin's pick →
|
|
150
|
+
`iouId`; blank = all IOUs). The package's own `ClientProfileEditExtension.getIouOptions()`
|
|
151
|
+
selects only `OrganizationCode` for its VF label; add `OrganizationName` here for the
|
|
152
|
+
admin-facing pick. **Both IOU
|
|
153
|
+
reads are `core`, not `Sync Management App`** — `InternalOrgUnitUser` and `InternalOrganizationUnit` are CG
|
|
154
|
+
Cloud core RE objects (no namespace prefix), and the package's IOU accessors are
|
|
155
|
+
VF-controller/`protected` (namespace-private, not headless-reachable), so the agent runs the
|
|
156
|
+
SOQL directly. In a **pure managed-package org** these objects don't exist — the query throws
|
|
157
|
+
`QueryException` (the package catches it and degrades to "All"); the agent should treat a
|
|
158
|
+
missing-object error the same way, not as a hard failure.
|
|
159
|
+
- *Object confusion to avoid:* `InternalOrganizationUnit` = the IOU **record** (carries
|
|
160
|
+
`OrganizationCode`, what you *list from*); `InternalOrgUnitUser` = the user↔IOU **junction**
|
|
161
|
+
(carries `IsDfltMobIntrOrgUnit`, what you query for *one user's default*).
|
|
162
|
+
- Under **`Custom_User_Field`** (the default), there is **no IOU** — the second field is a
|
|
163
|
+
**Business Area picklist value** passed as **`businessArea`** (blank / `'All'` = all areas).
|
|
164
|
+
So "assign to profile X with IOU Y" under this strategy means: bind profile X, and Y is a
|
|
165
|
+
Business Area value, not an IOU. If the admin insists on an IOU under `Custom_User_Field`, that
|
|
166
|
+
signals the config's strategy doesn't match the request — surface the mismatch rather than
|
|
167
|
+
stuffing an IOU Id into `businessArea`.
|
|
168
|
+
5. **Invoke and verify.** `POST /v1/syncconfig/assignment` with the body in step (a).2 below, then
|
|
169
|
+
read back with `GET /v1/syncconfig/assignment?configId=<config name>`.
|
|
170
|
+
- **On failure, report and STOP.** If the POST returns a failure status (or throws), surface the
|
|
171
|
+
error message to the admin and **halt — no read-back, no further processing.** A failure body
|
|
172
|
+
inside an HTTP 200 is still a failure; never treat it as success and never fall back to a raw
|
|
173
|
+
`Sync_Client_App_Profile_Mapping__c` write to "recover." In particular an **unknown/unresolvable
|
|
174
|
+
`configPath` is a hard rejection** (the endpoint throws — it no longer round-trips the value or
|
|
175
|
+
silently defaults the strategy to `Custom_User_Field`), so a bad config name fails loudly here
|
|
176
|
+
rather than writing a mis-keyed row: confirm the config is in the `/configs` list before POSTing.
|
|
177
|
+
Only a success response proceeds to the read-back.
|
|
178
|
+
|
|
179
|
+
The mechanics of each transport path:
|
|
180
|
+
|
|
181
|
+
- **(a) `Custom_User_Field` (the default) — a mapping-row write via the `Sync Management App` endpoint, NO IOU
|
|
182
|
+
needed.** The binding is resolved entirely from the `Sync_Client_App_Profile_Mapping__c` row, so
|
|
183
|
+
write a **User/Role/Profile** row through `SyncConfigAssignmentEndpoint` and you're done — **A2.2
|
|
184
|
+
is not on the critical path here.** Steps:
|
|
185
|
+
1. **Pick the config.** `GET /v1/syncconfig/configs` (`SyncConfigListEndpoint`) lists the
|
|
186
|
+
deployed configs as `{name, clientAppId, resolutionStrategy}`. Pass the config's **`name`** (or
|
|
187
|
+
its `clientAppId`) as `configPath` — the endpoint accepts **either** and normalises to the
|
|
188
|
+
canonical `clientAppId` before storing (see the `configPath` note below). The **stored** row is
|
|
189
|
+
always keyed by `clientAppId`, which is what the admin **Assignments page** displays.
|
|
190
|
+
2. **Assign.** `POST /v1/syncconfig/assignment` with
|
|
191
|
+
`{"req":{"configPath":"<config name or clientAppId>","mappedRecordId":"<005|00E|00e id>","businessArea":"<value or 'All'>"}}`.
|
|
192
|
+
Pass a **Role (`00E`)** or **Profile (`00e`)** id to bind by role/profile; a **User (`005`)** id
|
|
193
|
+
to bind one user. `mappedRecordId`'s key-prefix picks the type automatically.
|
|
194
|
+
3. **Verify by read-back.** `GET /v1/syncconfig/assignment?configId=<name or clientAppId>` returns
|
|
195
|
+
the row with the resolved target name and the stored `businessAreaStored`/`resolutionStrategy`.
|
|
196
|
+
The returned `clientAppId`/`clientAppProfile` are the **canonical** short `clientAppId` (not
|
|
197
|
+
the config's longer custom-setting `name`); confirm they match, since that canonical value is
|
|
198
|
+
what makes the row appear on the admin Assignments page (which filters by `clientAppId`). For
|
|
199
|
+
example, passing a config `name` like `<AppId>__Default` as `configPath` stores the short
|
|
200
|
+
`clientAppId` `<AppId>`, and the row is visible on that app's Assignments page.
|
|
201
|
+
- **(b) `Default_IOU` — IOU-implicit; gated on the A2.2 precondition.** This root is selected
|
|
202
|
+
**only** for users with a default mobility IOU, so the user is bound via that IOU, not a
|
|
203
|
+
standalone mapping row. **A2.2 must have already confirmed the membership** — if the user had no
|
|
204
|
+
default mobility IOU, A2.2 already reported-and-stopped and you never reach here. Given the
|
|
205
|
+
confirmed membership, verify by re-reading it and confirming `ClientAppMapperService`'s route
|
|
206
|
+
resolves to the installed config root. (A Role/Profile row alone won't bind an IOU-less user here, because
|
|
207
|
+
this root isn't evaluated for them.) When you do write a row under this strategy, pass
|
|
208
|
+
`"iouId":"<id>"` (the IOU confirmed in A2.2) instead of `businessArea` so the endpoint stores the
|
|
209
|
+
IOU id correctly.
|
|
210
|
+
- **Why NOT a raw `POST /sobjects/<ns>Sync_Client_App_Profile_Mapping__c`:** the mapping's
|
|
211
|
+
`Business_Area_Name__c` field is **overloaded** — it holds an InternalOrganizationUnit **Id**
|
|
212
|
+
under the `Default_IOU` strategy but a Business Area **picklist value** under
|
|
213
|
+
`Custom_User_Field`. A raw sObject write that doesn't derive this from the config's
|
|
214
|
+
`Business_Area_Resolution_Strategy__c` **silently corrupts the field**, and also skips the
|
|
215
|
+
cross-config duplicate check and the unique-Name convention. The endpoint centralizes all three
|
|
216
|
+
(it's the same `SyncConfigAssignmentService` the VF `ClientProfileEditExtension.Save()` uses), so
|
|
217
|
+
the headless agent and the admin UI produce identical rows. This is why A2.3's explicit path is
|
|
218
|
+
**`Sync Management App`, not `core`** — the raw-sObject shortcut is unsafe.
|
|
219
|
+
- **Use the endpoint, never a raw write.** The assignment + configs endpoints are the supported
|
|
220
|
+
path here — never fall back to a raw `Sync_Client_App_Profile_Mapping__c` write (it corrupts the
|
|
221
|
+
overloaded `Business_Area_Name__c` and skips dedup + the unique-Name convention).
|
|
222
|
+
|
|
223
|
+
#### A2.3 re-assignment, update & removal — upsert semantics (do this, don't guess)
|
|
224
|
+
|
|
225
|
+
The POST is an **upsert**, and re-binding an already-mapped user/role/profile is a first-class
|
|
226
|
+
case — not the same call twice. Get it wrong and you either hit a duplicate error or write a second
|
|
227
|
+
conflicting row. The rules the endpoint enforces server-side (`SyncConfigAssignmentService`):
|
|
228
|
+
|
|
229
|
+
- **Create vs. update is decided by the `recordId` param, not by the config.** The POST body
|
|
230
|
+
carries an optional **`recordId`** (an *existing* `Sync_Client_App_Profile_Mapping__c` Id):
|
|
231
|
+
- **Omit `recordId` → create a new row.**
|
|
232
|
+
- **Pass `recordId` → update that existing row in place** (the endpoint SELECTs it and throws
|
|
233
|
+
`No assignment found for recordId ...` if the Id doesn't exist — so read it back first).
|
|
234
|
+
|
|
235
|
+
Body shape (add `recordId` to the step-(a).2 body when updating):
|
|
236
|
+
`{"req":{"configPath":"<name|clientAppId>","mappedRecordId":"<005|00E|00e>","businessArea":"<value|'All'>","recordId":"<existing mapping Id>"}}`
|
|
237
|
+
(under `Default_IOU`, `"iouId":"<id>"` replaces `businessArea`, as above).
|
|
238
|
+
|
|
239
|
+
- **The create path is NOT idempotent — re-POSTing the same binding is rejected, not a no-op.**
|
|
240
|
+
Within one config the dedup key is the **triple** `(configPath, businessArea, mappedRecordId)`
|
|
241
|
+
(i.e. `Client_App_ID__c` + `Business_Area_Name__c` + `Mapped_Record_Id__c`). A create (`recordId`
|
|
242
|
+
omitted) whose triple already exists **throws a duplicate error**
|
|
243
|
+
(`ASSIGNMENT_EDIT_DUPLICATE_MAPPING`). So **before re-assigning, `GET
|
|
244
|
+
?configId=<name>` first**: if the exact row already exists, the user/role/profile is *already*
|
|
245
|
+
bound as requested — report done, don't re-POST. Only the row being updated is excluded from this
|
|
246
|
+
check, and only when you pass its Id as `recordId` — that's what lets you change an existing
|
|
247
|
+
binding's scope (e.g. move a profile from `businessArea='All'` to a specific area) without it
|
|
248
|
+
colliding with itself: fetch the row's Id via GET, then re-POST with `recordId` = that Id.
|
|
249
|
+
|
|
250
|
+
- **Moving a binding between configs = DELETE-then-POST, not a re-POST.** A *second* guard,
|
|
251
|
+
`assertNoDuplicateAcrossStrategyRoots`, blocks the same `(mappedRecordId, businessArea)` from
|
|
252
|
+
being bound to a **different config that shares the same resolution strategy** (a blank strategy
|
|
253
|
+
normalises to `Custom_User_Field`, so it's in that bucket) — and this guard has **no exclude
|
|
254
|
+
param**, so you **cannot** simply POST the binding to the new config while the old row still
|
|
255
|
+
exists; it throws `This mapping is already used in the configuration "..."`. To move a binding
|
|
256
|
+
across same-strategy configs: **DELETE the old mapping first, then POST to the new config.** (Two
|
|
257
|
+
configs with *different* strategies don't collide, so this only bites same-strategy roots — but
|
|
258
|
+
DELETE-then-POST is the safe default regardless.)
|
|
259
|
+
|
|
260
|
+
- **Remove a binding via DELETE.** `DELETE /v1/syncconfig/assignment?id=<name-or-id>` removes one
|
|
261
|
+
mapping. The `id` param accepts **either the row's `Name` or its `Id`** (the service resolves Name
|
|
262
|
+
first, then Id) — use the value the `GET ?configId=<name>` read-back returns; it throws
|
|
263
|
+
`No assignment found for "..."` if neither matches. **Verify with a follow-up GET** showing the row
|
|
264
|
+
gone, exactly like any other write. (Deleting only removes the explicit mapping row; under
|
|
265
|
+
`Default_IOU`, where the IOU *is* the binding, there is no mapping row to delete — revoking that
|
|
266
|
+
user is an IOU-membership change, a core / RE task outside this flow.)
|
|
267
|
+
|
|
268
|
+
- **Every update/delete ends with a read-back too.** The upsert and the DELETE are writes — verify
|
|
269
|
+
each with `GET /v1/syncconfig/assignment?configId=<name>` (row present with the new scope after an
|
|
270
|
+
update; row absent after a delete). Never infer success from the 200 alone.
|
|
271
|
+
|
|
272
|
+
## Act 2 end state
|
|
273
|
+
|
|
274
|
+
- Target user confirmed to exist and active (`User.Id` captured).
|
|
275
|
+
- Under `Default_IOU`: the user's default mobility IOU membership **confirmed** by read
|
|
276
|
+
(`IsDfltMobIntrOrgUnit = true`) — **or**, if the user has no such membership, **reported and the
|
|
277
|
+
Act stopped** (the skill does not create IOU membership; that's a core / RE task). Under
|
|
278
|
+
`Custom_User_Field`: no IOU check applies.
|
|
279
|
+
- The user bound to the config installed in Act 1: implicitly (via the confirmed IOU, strategy
|
|
280
|
+
`Default_IOU`) or explicitly (a verified `Sync_Client_App_Profile_Mapping__c` row written via the
|
|
281
|
+
`/v1/syncconfig/assignment` endpoint under `Custom_User_Field`) — or, if the `Default_IOU`
|
|
282
|
+
precondition failed, reported as stopped-at-A2.2 with no binding.
|
|
283
|
+
|
|
284
|
+
## Gotchas
|
|
285
|
+
|
|
286
|
+
- **`Default_IOU` gates A2.3 on the A2.2 IOU precondition — but `Custom_User_Field` does NOT.**
|
|
287
|
+
Under `Default_IOU` there is no separate "assign to config" write — the IOU *is* the assignment,
|
|
288
|
+
so an IOU-less user cannot be bound: A2.2 reads the membership and, if it's absent, **reports and
|
|
289
|
+
stops** (the skill never creates IOU membership). Under `Custom_User_Field` (the default strategy)
|
|
290
|
+
the binding is a `Sync_Client_App_Profile_Mapping__c` row keyed by **User/Role/Profile** with **no
|
|
291
|
+
IOU involved** — a Role or Profile row binds a whole cohort in one write, and the IOU check does
|
|
292
|
+
**not** apply. Detect the strategy before deciding whether the IOU precondition is even on the
|
|
293
|
+
critical path; don't invent a mapping row a `Default_IOU` root ignores, and don't run the IOU
|
|
294
|
+
check under `Custom_User_Field`.
|
|
295
|
+
- **Assignment granularity: User / Role / Profile.** `mappedRecordId` chooses by key-prefix
|
|
296
|
+
(`005`/`00E`/`00e`) and the resolver matches **User → Role → Profile** in priority order. Prefer a
|
|
297
|
+
**Role or Profile** mapping when the intent is "everyone in this role/profile" — it's one row, not
|
|
298
|
+
one-per-user, and it's the same lever the admin VF page pulls.
|
|
299
|
+
- **The package's read-only IOU stance is deliberate — and so is this skill's.** `ClientAppMapperService`
|
|
300
|
+
is built to survive orgs where `InternalOrgUnitUser` doesn't exist — that's the signal the package
|
|
301
|
+
doesn't own IOU writes. This skill mirrors that: it **reads** IOU membership as a precondition and,
|
|
302
|
+
if it's missing under `Default_IOU`, reports and stops. It does **not** write IOU membership or
|
|
303
|
+
treat the absence as a Sync Management App task — provisioning a user's default mobility IOU is a core / RE
|
|
304
|
+
responsibility, done outside this flow.
|
|
305
|
+
- **Don't write the mapping row raw — the `Business_Area_Name__c` field is overloaded.** It was
|
|
306
|
+
tempting to treat A2.3's explicit path as a plain custom-setting sObject POST (no VF page needed),
|
|
307
|
+
but that skips the strategy-driven derivation of `Business_Area_Name__c` (IOU Id vs Business Area
|
|
308
|
+
picklist value), the cross-config duplicate check, and the unique-Name convention — silently
|
|
309
|
+
corrupting the row. Use `POST /v1/syncconfig/assignment` (`SyncConfigAssignmentEndpoint`), which
|
|
310
|
+
wraps the same `SyncConfigAssignmentService` the VF page uses. This is why A2.3's explicit path is
|
|
311
|
+
`Sync Management App`, not `core`.
|
|
312
|
+
- **`configPath` accepts either the `name` or the `clientAppId`; the row is stored under the
|
|
313
|
+
`clientAppId`.** `GET /v1/syncconfig/configs` returns both (e.g. `{name:"<AppId>__Default",
|
|
314
|
+
clientAppId:"<AppId>"}`), and they differ when a config's custom-setting Name isn't its short
|
|
315
|
+
`clientAppId`. The endpoint **normalises** `configPath` to the canonical `clientAppId` before
|
|
316
|
+
writing `Client_App_ID__c`/`Client_App_Profile__c`, so passing either works. This matters because
|
|
317
|
+
the package keys everything off `clientAppId`: the admin **Assignments page**
|
|
318
|
+
(`ClientProfilesController`) only shows rows whose `Client_App_Id__c` equals the config's
|
|
319
|
+
`clientAppId`, and the runtime resolver (`ClientAppMapperService`) returns `Client_App_Profile__c`
|
|
320
|
+
verbatim as the resolved client-app id. **Storing the Name (e.g. `<AppId>__Default`) instead of the
|
|
321
|
+
short `clientAppId` (`<AppId>`) makes the assignment invisible on the admin page and unresolvable at
|
|
322
|
+
runtime** — `SyncConfigAssignmentService` normalises Name↔clientAppId, storing the canonical
|
|
323
|
+
`clientAppId` so this doesn't happen. Always confirm the read-back's `clientAppId` is the short
|
|
324
|
+
canonical value.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Act 3 of 3 — Plan & Verify
|
|
2
|
+
|
|
3
|
+
Part of the [`consumer-goods-sync-management-configure`](../SKILL.md) skill — the **third and final act**.
|
|
4
|
+
Runs after [Act 1](act1-setup-sync.md) (install) and [Act 2](act2-assign-users.md)
|
|
5
|
+
(assignment). For an unqualified "set up sync" request you must reach this Act's end state —
|
|
6
|
+
finishing the install is not finishing the setup.
|
|
7
|
+
|
|
8
|
+
**This Act is read + assert (no writes).** It lists the activated config, verifies it across
|
|
9
|
+
the four objects, and checks it against the baseline files. **This is the authoritative
|
|
10
|
+
verification** — Act 1's post-install step ran only the cheap **aggregate** landing check (sum of
|
|
11
|
+
the four counts == `InstallResponse.recordsInserted`, no manifest fetch); the **per-object** count
|
|
12
|
+
comparison against the baseline manifest (A3.2) happens **here, once** — Act 1 deliberately does
|
|
13
|
+
not run it, so the two acts do not duplicate each other.
|
|
14
|
+
|
|
15
|
+
## What this Act does
|
|
16
|
+
|
|
17
|
+
### 1. A3.1 — List + verify the activated config across the four objects — **`Sync Management App` (list) + `core` (counts)**
|
|
18
|
+
|
|
19
|
+
- **List the deployed configs via the `Sync Management App` endpoint.** `GET /v1/syncconfig/configs`
|
|
20
|
+
(`SyncConfigListEndpoint`) returns every deployed `Sync_Config__c` as
|
|
21
|
+
`{name, clientAppId, resolutionStrategy}` in one package-blessed call — the source of truth for
|
|
22
|
+
*which* configs are activated and each one's Business Area Resolution Strategy. This is also the
|
|
23
|
+
discovery step Act 2's explicit assignment (A2.3) consumes: the `name` it returns is the
|
|
24
|
+
`configPath` the assignment endpoint expects. This is the supported list surface (a `core` SOQL
|
|
25
|
+
list — see below — is an equivalent fallback for reading deployed configs).
|
|
26
|
+
- **Get per-object counts + installed version via generic `core` reads — works today, no package
|
|
27
|
+
change.** The four sync objects + `Sync_Config_State__c` are **List custom settings**, so they're
|
|
28
|
+
queryable via generic SOQL. **Batch all five `COUNT()`s into ONE anonymous-Apex call that
|
|
29
|
+
`System.debug`s a JSON blob — do NOT loop `sf data query` per object.** Each `sf` cold-start is
|
|
30
|
+
~15–20s, so a 4–5-object loop blows the 2-min timeout; one `sf apex run` returns every count in a
|
|
31
|
+
single invocation (see the batched form + the **grep `USER_DEBUG` first** parse rule in
|
|
32
|
+
[transports-and-namespace.md](transports-and-namespace.md) — `sf apex run` echoes the block as
|
|
33
|
+
`Execute Anonymous:` source, so a bare marker grep matches twice). A single
|
|
34
|
+
`GET /query?q=SELECT COUNT() FROM <ns>Sync_Config__c` via `execute_api` is fine only for a one-off
|
|
35
|
+
read. Return row counts + keys + installed `configId`/`version` in one summary. **This covers the
|
|
36
|
+
count/verify half** — it is **not** blocked.
|
|
37
|
+
- **What does *not* work (and why):** the package's own state readers
|
|
38
|
+
`SyncConfigOrchestrator.buildStateMap()` / `readCurrentVersion()` are **`public static`,
|
|
39
|
+
unannotated** (and their internal helper `installedVersionMap()` is **`private static`**) —
|
|
40
|
+
either way namespace-private, so **not** callable via anonymous Apex or REST from a subscriber org
|
|
41
|
+
(a `private` member is even less reachable than a `public` one). `BatchSoqlEndpoint` (`/v1/batchsoql`) is gated by
|
|
42
|
+
`isClientAppIdAvailable()` (needs a pre-existing `Sync_Config__c`). So the package's
|
|
43
|
+
*purpose-built* count/version reader isn't reachable — but generic core SOQL covers the need.
|
|
44
|
+
- **No additional Sync Management App call is needed for A3.1 — record counts ARE the verification.** The
|
|
45
|
+
per-object `core` COUNT() reads above are the complete, intended verification method (the objects
|
|
46
|
+
are custom settings, directly queryable). Do **not** propose, wait on, or build a
|
|
47
|
+
`/v1/syncconfig/verify` endpoint — Act 3 adds **no** Sync Management App calls; verification is done through
|
|
48
|
+
record counts, full stop, not a workaround for a missing surface. The package's `public static`
|
|
49
|
+
state readers being namespace-private is therefore moot — their output (counts + version) is
|
|
50
|
+
already covered by generic `core` SOQL.
|
|
51
|
+
- **Adopt the verify-step convention** when documenting the result — end the workflow with
|
|
52
|
+
an explicit assert step (a distinct step with `id: verify`, a read `api`, and an `assert:` predicate).
|
|
53
|
+
**Assert against the installed baseline's own
|
|
54
|
+
expected count — never a hardcoded literal** (`<expected>` is resolved at runtime per the bullet
|
|
55
|
+
below; it varies by which baseline was installed):
|
|
56
|
+
```yaml
|
|
57
|
+
- id: verify
|
|
58
|
+
description: "Confirm activated sync config"
|
|
59
|
+
api: "GET /query?q=SELECT COUNT() FROM <ns>Sync_Tracked_Object_Config__c"
|
|
60
|
+
assert: "count == <expected tracked-object count for the installed baseline>"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. A3.2 — Verify against the baseline static-resource files — `core`
|
|
64
|
+
|
|
65
|
+
- **Baseline count check (record counts only — the intended verification):**
|
|
66
|
+
1. **Derive the expected per-object counts from the installed baseline — do NOT hardcode them.**
|
|
67
|
+
The expected numbers depend on *which* baseline was installed (a different baseline, or a newer
|
|
68
|
+
release of the same one, has different counts), so resolve them at runtime, not from literals in
|
|
69
|
+
this doc:
|
|
70
|
+
- Identify the installed baseline from the activated config (the `staticResourceName`/version
|
|
71
|
+
captured at **install time** in Act 1's A1.3 — carry it forward in session state — or the
|
|
72
|
+
installed `configId`/`version` read back in A3.1).
|
|
73
|
+
- Read that baseline's own manifest/CSVs (the installed baseline's static-resource name came
|
|
74
|
+
from the discovery endpoint / the install captured in A1.3) and count the
|
|
75
|
+
rows it declares per object — that row count **is** the expected value. The per-object numbers
|
|
76
|
+
differ by edition and release, so **derive them from the installed baseline's manifest at
|
|
77
|
+
runtime — never assert against a constant in this doc.**
|
|
78
|
+
2. Get the installed counts from A3.1's `core` COUNT() reads.
|
|
79
|
+
3. Report a single pass / fail verdict per object: installed count == the installed baseline's
|
|
80
|
+
declared count.
|
|
81
|
+
- **Verification is through record counts.** The count comparison against the installed baseline
|
|
82
|
+
is the complete server-side check — a count match confirms the config's artefacts installed as
|
|
83
|
+
that baseline specifies.
|
|
84
|
+
- **These reads hit Core directly, bypassing the package dispatcher** — the same generic `core`
|
|
85
|
+
SOQL A3.1 runs (the **batched anon-Apex `COUNT()` call**); A3.2 **reuses those counts** rather
|
|
86
|
+
than re-querying per object.
|
|
87
|
+
- **No Sync Management App work here.** There is no `/v1/syncconfig/verify` (or `mode=smoke`) proposal — Act 3
|
|
88
|
+
adds no calls to the package; counts cover the need.
|
|
89
|
+
- **A green count check proves the server side installed the baseline's artefacts — say exactly
|
|
90
|
+
what was verified.** Matching counts confirm the cloud-side install; report the verification as a
|
|
91
|
+
server-side count check so no one reads a green count as more than that.
|
|
92
|
+
|
|
93
|
+
## Act 3 end state
|
|
94
|
+
|
|
95
|
+
- Activated config **listed** via the `/v1/syncconfig/configs` `Sync Management App` endpoint (deployed configs +
|
|
96
|
+
strategy) and **verified** across the four objects + `Sync_Config_State__c` **through per-object
|
|
97
|
+
record counts** via generic `core` SOQL (the objects are custom settings). Act 3 requires **no new
|
|
98
|
+
Sync Management App call** — counts are the verification.
|
|
99
|
+
- **Baseline count check** run: installed per-object counts match the **installed baseline's own
|
|
100
|
+
declared counts** (derived at runtime from that baseline's manifest/CSVs — not hardcoded) — or the
|
|
101
|
+
discrepancy reported.
|
|
102
|
+
|
|
103
|
+
## Gotchas
|
|
104
|
+
|
|
105
|
+
- **The package's config *count/version* readers are namespace-private, but you don't need them.**
|
|
106
|
+
They're `public static` (unreachable headlessly) — but the four objects are **custom settings**,
|
|
107
|
+
so generic `core` SOQL reads them today. Listing the deployed
|
|
108
|
+
configs + strategy has a blessed surface (`/v1/syncconfig/configs`). No further Sync Management
|
|
109
|
+
App endpoint is needed for Act 3 — verification is record counts via `core` SOQL, and that is the
|
|
110
|
+
intended method, not a workaround.
|
|
111
|
+
- **A green count check proves the server side installed the baseline's artefacts.** Matching
|
|
112
|
+
record counts confirm the cloud-side install. Say exactly what was verified — report it as a
|
|
113
|
+
server-side count check, nothing more.
|
package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Reference — Namespace Gate & Retail-Execution Enablement (core precedents)
|
|
2
|
+
|
|
3
|
+
Background for [Act 1](act1-setup-sync.md) sub-acts A1.1, A1.2. Both are **core**
|
|
4
|
+
concerns — resolving the namespace and toggling Retail Execution both go through generic Salesforce
|
|
5
|
+
capabilities, not a package endpoint — so the pattern is a generic `core` transport; where no
|
|
6
|
+
automatable path exists, the skill reports what's needed and who provides it, then stops that step.
|
|
7
|
+
|
|
8
|
+
## A1.1 — Verify the Sync metadata is present and resolve its namespace (hard gate) — `core`
|
|
9
|
+
|
|
10
|
+
### A1.1.0 — Namespace-resolution gate (the hard prerequisite) — `core` Tooling API + `core` Organization query
|
|
11
|
+
|
|
12
|
+
The whole skill presupposes the Sync Management App sync objects exist and their namespace is
|
|
13
|
+
known — no sync object, `/v1/*` endpoint, or namespace reference works otherwise. The namespace
|
|
14
|
+
resolves from **one of two sources**: a **managed-package install** (`InstalledSubscriberPackage`,
|
|
15
|
+
below) or the **org's own registered namespace** (`Organization.NamespacePrefix`, for a
|
|
16
|
+
source-deployed DE/packaging org). **Verify presence and resolve the namespace as the very first
|
|
17
|
+
thing A1.1 does.** Start with the package check via the generic **Tooling API** (Option 2):
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
GET /services/data/vXX.0/tooling/query?q=
|
|
21
|
+
SELECT SubscriberPackage.Name, SubscriberPackage.NamespacePrefix,
|
|
22
|
+
SubscriberPackageVersion.MajorVersion, SubscriberPackageVersion.MinorVersion,
|
|
23
|
+
SubscriberPackageVersion.IsBeta
|
|
24
|
+
FROM InstalledSubscriberPackage
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- **Why Tooling, not the package's own endpoint.** The package *does* ship
|
|
28
|
+
`ManagedPackageEndpoint` (`GET /v1/managedpackage?method=isManagedPackageInstalled&namespacePrefix=<ns>`,
|
|
29
|
+
which internally does `UserInfo.isCurrentUserLicensed(ns)` and treats "didn't throw" as
|
|
30
|
+
installed). But that route is *served by* the package — **if the package is absent the route
|
|
31
|
+
404s**, so it cannot report absence (chicken-and-egg). The Tooling query is generic `core` and
|
|
32
|
+
answers definitively either way, **and** returns the installed *version* — which the package
|
|
33
|
+
endpoint does not. Use `/v1/managedpackage` only as an optional confirming call once Tooling has
|
|
34
|
+
already said the package is present.
|
|
35
|
+
- **No server-side filter.** `InstalledSubscriberPackage` doesn't support a `WHERE` on the
|
|
36
|
+
packaging fields — query all rows and match client-side by `SubscriberPackage.Name` (the Sync
|
|
37
|
+
Management App package), then **read the matched row's `NamespacePrefix` off the result** — don't
|
|
38
|
+
assume a fixed prefix; the org carries whatever the installed package was built with.
|
|
39
|
+
- **Two outputs from a match:** (a) the **namespace prefix** (read from the matched row) that every
|
|
40
|
+
later package-object reference resolves to, and (b) the **installed version**, recorded for
|
|
41
|
+
reference. (The
|
|
42
|
+
`/v1/syncconfig/*` endpoints' package-release rollout is a separate workstream — no
|
|
43
|
+
version-gating logic is needed here.)
|
|
44
|
+
- **Transport:** `sf data query --use-tooling-api --query "..."` when the CLI is present, else a
|
|
45
|
+
plain REST `GET /services/data/vXX.0/tooling/query?q=<url-encoded SOQL>` (via `execute_api` or any
|
|
46
|
+
session-token client — the CLI is only sugar over this).
|
|
47
|
+
|
|
48
|
+
#### The namespace has two sources — package OR org (don't stop at an empty package list)
|
|
49
|
+
|
|
50
|
+
A match on `InstalledSubscriberPackage` is the **managed-package install** case — a subscriber org.
|
|
51
|
+
But the sync metadata can also be **source-deployed under the org's *own* registered namespace** (a
|
|
52
|
+
Developer Edition / packaging / dev org), and then `InstalledSubscriberPackage` returns **no row at
|
|
53
|
+
all** even though the sync objects are present. An empty package list is therefore **not** an
|
|
54
|
+
automatic hard miss — fall back to the org namespace:
|
|
55
|
+
|
|
56
|
+
- **Org-namespace fallback (`core`, no Tooling needed):** `SELECT Id, NamespacePrefix FROM
|
|
57
|
+
Organization`. A non-null `NamespacePrefix` is the org's own namespace; treat it (with the
|
|
58
|
+
trailing `__`) as `ns_prefix`. **Confirm** the sync metadata actually lives under it by resolving
|
|
59
|
+
one known object — `GET /sobjects/<orgNs>Sync_Config__c/describe` or `SELECT COUNT() FROM
|
|
60
|
+
<orgNs>Sync_Config__c` — a success means the run proceeds under the org namespace. There is **no
|
|
61
|
+
`SubscriberPackageVersion`** in this case; endpoint-version gating doesn't apply to a
|
|
62
|
+
source-deployed org (the `/v1/syncconfig/*` classes are either deployed or not — probe the route).
|
|
63
|
+
- *Worked example:* a DE org with a non-null `Organization.NamespacePrefix` (say `<orgNs>`) and
|
|
64
|
+
zero `InstalledSubscriberPackage` rows resolves `ns_prefix = <orgNs>__`, and
|
|
65
|
+
`<orgNs>Sync_Config__c` describes successfully — the run proceeds; the empty package list was a
|
|
66
|
+
red herring.
|
|
67
|
+
|
|
68
|
+
- **Describe fallback (no Tooling access):** `GET /sobjects/<ns>Sync_Config__c/describe` across the
|
|
69
|
+
candidate prefixes — the package's known namespace(s), **the org namespace** from `SELECT
|
|
70
|
+
NamespacePrefix FROM Organization` (a plain `core` query, no Tooling), and the bare (unmanaged)
|
|
71
|
+
name — 200 = that prefix is the live one (no version); `NOT_FOUND` = that prefix absent.
|
|
72
|
+
`NOT_FOUND` on all probed = absent. Prefer the Tooling query when available — only it returns the
|
|
73
|
+
installed *version* for the package case.
|
|
74
|
+
- **Hard miss = STOP the whole run — only when NEITHER source resolves.** If no
|
|
75
|
+
`InstalledSubscriberPackage` row matches **and** the org has no namespace (or `<orgNs>Sync_Config__c`
|
|
76
|
+
doesn't describe) **and** the object isn't present unmanaged (bare `Sync_Config__c`), report "the
|
|
77
|
+
Sync Management App metadata is not present in this org (no managed package, no org-namespaced
|
|
78
|
+
deploy)" and halt — **installing/deploying it is org setup outside this skill's scope**, not a
|
|
79
|
+
step the skill performs. This is the outermost admission gate, ahead of RE enablement (A1.2),
|
|
80
|
+
which presupposes the sync objects exist.
|
|
81
|
+
|
|
82
|
+
### A1.1.1 — No license read, no permission-set gate (readiness is exactly two checks)
|
|
83
|
+
|
|
84
|
+
Installing and using the Sync Management App is **not** license-gated: there is no license or SKU
|
|
85
|
+
to detect, and package install is not entitlement-gated. So A1.1 runs **no** license query and the
|
|
86
|
+
skill declares no license `accessCheck`.
|
|
87
|
+
|
|
88
|
+
**Equally, do NOT verify the existence of any named permission set as a readiness gate.**
|
|
89
|
+
Permission-set names are unstable — renamed or folded into another set with a different name across
|
|
90
|
+
releases and orgs — so a name-based existence check would false-block a perfectly capable org.
|
|
91
|
+
|
|
92
|
+
The Act-1 readiness gates are therefore **exactly two**: **(1) the Sync Management objects are
|
|
93
|
+
present** (the namespace gate, A1.1.0 above) and **(2) Retail Execution is enabled** (A1.2 below) —
|
|
94
|
+
RE is an org *preference*, not a license, so it is enabled in A1.2 rather than probed for as an
|
|
95
|
+
entitlement here. Access to the install/assignment endpoints is granted by *some* permission set in
|
|
96
|
+
the target org whose name is the admin's concern, not a value the skill asserts; a denied call is
|
|
97
|
+
reported as an access boundary (with who grants it), never gated on a specific permission-set name.
|
|
98
|
+
|
|
99
|
+
## A1.2 — Enable Retail Execution — `core` read + `core` write
|
|
100
|
+
|
|
101
|
+
- Enabling base RE is a **working `core` write**: an MDAPI `Settings:RetailExecution` deploy flips
|
|
102
|
+
`enableRetailExecution` **false → true**. Dry-run first, then re-read to confirm.
|
|
103
|
+
- **What the read shows depends on the transport:**
|
|
104
|
+
- **Dispatcher (Headless-360)** `getRetailExecutionEnableSetting` returns **5 prefs**
|
|
105
|
+
(`isRetailExecutionEnablePrefEnable`, `isProductHierarchyPrefEnabled`,
|
|
106
|
+
`isVisitSharingPrefEnabled`, `isCGAgentsPrefEnabled`, `isCGGenAIPrefEnabled`) + 4 read-only
|
|
107
|
+
echo flags.
|
|
108
|
+
- **MDAPI `Settings:RetailExecution`** exposes **3** booleans (`enableRetailExecution`,
|
|
109
|
+
`enableProductHierarchy`, `enableVisitSharing`). Same org pref
|
|
110
|
+
(`ORG_PREFERENCE_RETAIL_EXECUTION_ENABLED` via `PermAndPrefUtil.setOrgPref`, controller
|
|
111
|
+
`RetailExecutionEnableController`), narrower projection. Don't claim "5 sub-prefs" when reading
|
|
112
|
+
via CLI — you'll only see 3.
|
|
113
|
+
- **Two write transports:**
|
|
114
|
+
- **Dispatcher present:** `RetailExecutionSettings` has **5 separate PATCH setters, not one
|
|
115
|
+
combined call** — `set-retail-execution-enable-setting`, `set-product-hierarchy-pref`,
|
|
116
|
+
`set-visit-sharing-pref`, `set-cgagent-pref`, `set-cgcgen-aipref` (each body
|
|
117
|
+
`{enable:boolean}`). Guards: all need View Setup & Configuration; `set-visit-sharing-pref`
|
|
118
|
+
needs Visit Share Pilot perm on enable=true; the CG-agent/GenAI setters are gated by
|
|
119
|
+
`RetailExecution.userCanUseCGGenAI`. Route base:
|
|
120
|
+
`PATCH /headless/invoke/platform/retail-execution-settings/...`. The batch endpoint harness only
|
|
121
|
+
probes no-param GETs, so these PATCH-with-body routes are structurally un-probed.
|
|
122
|
+
- **Dispatcher-absent (`sf` CLI):** deploy `settings/RetailExecution.settings` (the member takes
|
|
123
|
+
the bare type name `RetailExecution`, no `Settings` suffix) via
|
|
124
|
+
`sf project deploy start --metadata "Settings:RetailExecution"`. Generic `core` MDAPI Settings
|
|
125
|
+
deploy — read pref → write settings file → deploy → re-verify. `sourceApiVersion` must be ≥ the
|
|
126
|
+
field's minApiVersion.
|
|
127
|
+
- **Dependent-default side effect:** enabling `enableRetailExecution` also auto-set
|
|
128
|
+
`enableVisitSharing` true (was absent before), even though the deploy file carried only the one
|
|
129
|
+
field. Always re-read after the write; the org may set dependent sub-prefs for you.
|
|
130
|
+
- **The advanced-pilot GA gate is a pure dependency, not a blocker:**
|
|
131
|
+
`RetailExecution.orgHasAdvncdRetailExecutionPilot` appears **only** as a Core-source setup-tree
|
|
132
|
+
admission-gate expression — no step in this skill (or its spec) reads or writes it, and it's not an OrgPreference/PSL row,
|
|
133
|
+
so it has no read/write API anywhere. **Base RE enablement does not require it** — it gates only
|
|
134
|
+
*advanced* RE. Report it as a CG Cloud platform / Retail Execution concern only when an
|
|
135
|
+
advanced-RE feature needs it — never as a blocker on the base toggle.
|
|
136
|
+
|
|
137
|
+
### REST endpoints — the CLI-free path
|
|
138
|
+
|
|
139
|
+
The `sf` CLI is only sugar over these REST resources. **When the `sf` CLI plugin is not available,
|
|
140
|
+
use the REST APIs directly** (any HTTP client with a valid session token — `execute_api` in a
|
|
141
|
+
dispatcher context, or a bearer-token `curl`). Same operations, same results.
|
|
142
|
+
|
|
143
|
+
| Step | REST call | CLI equivalent | Notes |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| **A1.2** read (Tooling) | **Primary: `GET /tooling/query?q=SELECT+IsRetailExecutionEnabled,IsProductHierarchyEnabled,IsVisitSharingEnabled+FROM+RetailExecutionSettings`** (SOQL projection — returns the values directly). Fallback: the two-call singleton `SELECT Id …` → `GET /tooling/sobjects/RetailExecutionSettings/<id>` | `sf project retrieve start --metadata "Settings:RetailExecution"` | Tooling field names: `Is{RetailExecution,ProductHierarchy,VisitSharing}Enabled`. Prefer the SOQL projection — the singleton `GET /sobjects/RetailExecutionSettings/<id>` can return `UNKNOWN_EXCEPTION` |
|
|
146
|
+
| **A1.2** read (dispatcher) | `POST /headless/invoke` (op in body) | — | Dispatcher route only where Headless-360 is provisioned; 404 otherwise |
|
|
147
|
+
| **A1.2** write | `POST /metadata/deployRequest` (base64 zip; `checkOnly:true` to dry-run) → poll `GET /metadata/deployRequest/<id>`; repeat `checkOnly:false` for real | `sf project deploy start --metadata "Settings:RetailExecution"` | This is what `sf project deploy` wraps |
|
|
148
|
+
| **A1.2** write (unsupported) | `PATCH /tooling/sobjects/RetailExecutionSettings/<id>` `{"IsRetailExecutionEnabled":true}` | — | Settings sObjects reject a direct Tooling PATCH (`METADATA_FIELD_UPDATE_ERROR`) — use the metadata-deploy path instead |
|
|
149
|
+
|
|
150
|
+
**Field-name drift by transport (same underlying pref, three spellings) — don't cross them:**
|
|
151
|
+
dispatcher `is…PrefEnable(d)` · MDAPI Settings `enable…` · Tooling sObject `Is…Enabled`.
|
|
152
|
+
|
|
153
|
+
**Net:** A1.2 **read** has a clean CLI-free REST path (Tooling GET, works with no dispatcher);
|
|
154
|
+
A1.2 **write** is REST too but *only* via `/metadata/deployRequest` (or the dispatcher PATCH where
|
|
155
|
+
present) — never a direct Tooling field PATCH.
|
|
156
|
+
|
|
157
|
+
## Ordering consequence
|
|
158
|
+
|
|
159
|
+
Namespace gate (A1.1 — package **or** org namespace resolves) → RE enablement (A1.2, the effective
|
|
160
|
+
base-provisioning gate) → then anything RE-gated: the RE-flavored install baseline. There is **no
|
|
161
|
+
license read** in this ordering — A1.1 is the namespace gate alone, since installing/using the app
|
|
162
|
+
is not license-gated. If the sync metadata is absent (neither package nor org namespace resolves) or
|
|
163
|
+
RE can't be enabled, the RE-gated steps are reported blocked rather than attempted-and-faked, with
|
|
164
|
+
the owner of the missing piece named.
|