@salesforce/afv-skills 1.1.0 → 1.2.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 +4 -4
- package/skills/agentforce-development/SKILL.md +427 -0
- package/skills/agentforce-development/assets/README-legacy.md +89 -0
- package/skills/agentforce-development/assets/agent-spec-template.md +90 -0
- package/skills/agentforce-development/assets/agents/README.md +45 -0
- package/skills/agentforce-development/assets/agents/hello-world.agent +60 -0
- package/skills/agentforce-development/assets/agents/multi-topic.agent +105 -0
- package/skills/agentforce-development/assets/agents/production-faq.agent +101 -0
- package/skills/agentforce-development/assets/agents/production-faq.bundle-meta.xml +4 -0
- package/skills/agentforce-development/assets/agents/simple-qa.agent +72 -0
- package/skills/agentforce-development/assets/apex/models-api-queueable.cls +225 -0
- package/skills/agentforce-development/assets/bundle-meta.xml +23 -0
- package/skills/agentforce-development/assets/components/apex-action.agent +52 -0
- package/skills/agentforce-development/assets/components/error-handling.agent +58 -0
- package/skills/agentforce-development/assets/components/escalation-setup.agent +169 -0
- package/skills/agentforce-development/assets/components/flow-action.agent +66 -0
- package/skills/agentforce-development/assets/components/n-ary-conditions.agent +110 -0
- package/skills/agentforce-development/assets/components/topic-with-actions.agent +40 -0
- package/skills/agentforce-development/assets/deterministic-routing.agent +166 -0
- package/skills/agentforce-development/assets/escalation-pattern.agent +209 -0
- package/skills/agentforce-development/assets/flow-action-lookup.agent +115 -0
- package/skills/agentforce-development/assets/hub-and-spoke.agent +104 -0
- package/skills/agentforce-development/assets/invocable-apex-template.cls +187 -0
- package/skills/agentforce-development/assets/local-info-agent-annotated.agent +355 -0
- package/skills/agentforce-development/assets/metadata/basic-prompt-template.promptTemplate-meta.xml +109 -0
- package/skills/agentforce-development/assets/metadata/genai-function-apex.xml +92 -0
- package/skills/agentforce-development/assets/metadata/genai-function-flow.xml +57 -0
- package/skills/agentforce-development/assets/metadata/genai-plugin.xml +72 -0
- package/skills/agentforce-development/assets/metadata/http-callout-flow.flow-meta.xml +348 -0
- package/skills/agentforce-development/assets/metadata/record-grounded-prompt.promptTemplate-meta.xml +136 -0
- package/skills/agentforce-development/assets/minimal-starter.agent +42 -0
- package/skills/agentforce-development/assets/patterns/README.md +254 -0
- package/skills/agentforce-development/assets/patterns/action-callbacks.agent +178 -0
- package/skills/agentforce-development/assets/patterns/advanced-input-bindings.agent +141 -0
- package/skills/agentforce-development/assets/patterns/bidirectional-routing.agent +156 -0
- package/skills/agentforce-development/assets/patterns/critical-input-collection.agent +244 -0
- package/skills/agentforce-development/assets/patterns/delegation-routing.agent +89 -0
- package/skills/agentforce-development/assets/patterns/lifecycle-events.agent +127 -0
- package/skills/agentforce-development/assets/patterns/llm-controlled-actions.agent +184 -0
- package/skills/agentforce-development/assets/patterns/multi-step-workflow.agent +282 -0
- package/skills/agentforce-development/assets/patterns/open-gate-routing.agent +286 -0
- package/skills/agentforce-development/assets/patterns/procedural-instructions.agent +273 -0
- package/skills/agentforce-development/assets/patterns/prompt-template-action.agent +188 -0
- package/skills/agentforce-development/assets/patterns/system-instruction-overrides.agent +293 -0
- package/skills/agentforce-development/assets/prompt-rag-search.agent +131 -0
- package/skills/agentforce-development/assets/template-multi-topic.agent +160 -0
- package/skills/agentforce-development/assets/template-single-topic.agent +81 -0
- package/skills/agentforce-development/assets/verification-gate.agent +208 -0
- package/skills/agentforce-development/references/action-prompt-templates.md +164 -0
- package/skills/agentforce-development/references/actions-reference.md +592 -0
- package/skills/agentforce-development/references/agent-access-guide.md +72 -0
- package/skills/agentforce-development/references/agent-design-and-spec-creation.md +1010 -0
- package/skills/agentforce-development/references/agent-metadata-and-lifecycle.md +575 -0
- package/skills/agentforce-development/references/agent-script-core-language.md +1218 -0
- package/skills/agentforce-development/references/agent-topic-map-diagrams.md +323 -0
- package/skills/agentforce-development/references/agent-user-setup.md +526 -0
- package/skills/agentforce-development/references/agent-validation-and-debugging.md +803 -0
- package/skills/agentforce-development/references/known-issues.md +353 -0
- package/skills/agentforce-development/references/minimal-examples.md +67 -0
- package/skills/agentforce-development/references/production-gotchas.md +279 -0
- package/skills/agentforce-development/references/salesforce-cli-for-agents.md +393 -0
- package/skills/agentforce-development/references/version-history.md +23 -0
- package/skills/generate-permission-set/SKILL.md +174 -0
- package/skills/salesforce-custom-application/SKILL.md +1 -2
- package/skills/salesforce-custom-field/SKILL.md +0 -4
- package/skills/salesforce-custom-tab/SKILL.md +84 -8
- package/skills/salesforce-experience-lwr-site/SKILL.md +196 -0
- package/skills/salesforce-experience-lwr-site/docs/bootstrap-template-byo-lwr.md +224 -0
- package/skills/salesforce-experience-lwr-site/docs/configure-content-brandingSet.md +131 -0
- package/skills/salesforce-experience-lwr-site/docs/configure-content-route.md +232 -0
- package/skills/salesforce-experience-lwr-site/docs/configure-content-themeLayout.md +141 -0
- package/skills/salesforce-experience-lwr-site/docs/configure-content-view.md +233 -0
- package/skills/salesforce-experience-lwr-site/docs/configure-guest-sharing-rules.md +42 -0
- package/skills/salesforce-experience-lwr-site/docs/handle-component-and-region-ids.md +27 -0
- package/skills/salesforce-experience-lwr-site/docs/handle-ui-components.md +215 -0
- package/skills/salesforce-flow/SKILL.md +2 -2
- package/skills/salesforce-fragment/SKILL.md +85 -10
- package/skills/salesforce-lightning-app-build/SKILL.md +102 -10
- package/skills/apex-class/SKILL.md +0 -253
- package/skills/apex-class/examples/AccountDeduplicationBatch.cls +0 -148
- package/skills/apex-class/examples/AccountSelector.cls +0 -193
- package/skills/apex-class/examples/AccountService.cls +0 -201
- package/skills/apex-class/templates/abstract.cls +0 -128
- package/skills/apex-class/templates/batch.cls +0 -125
- package/skills/apex-class/templates/domain.cls +0 -102
- package/skills/apex-class/templates/dto.cls +0 -108
- package/skills/apex-class/templates/exception.cls +0 -51
- package/skills/apex-class/templates/interface.cls +0 -25
- package/skills/apex-class/templates/queueable.cls +0 -92
- package/skills/apex-class/templates/schedulable.cls +0 -75
- package/skills/apex-class/templates/selector.cls +0 -92
- package/skills/apex-class/templates/service.cls +0 -69
- package/skills/apex-class/templates/utility.cls +0 -97
- package/skills/apex-test-class/SKILL.md +0 -101
- package/skills/apex-test-class/references/assertion-patterns.md +0 -209
- package/skills/apex-test-class/references/async-testing.md +0 -276
- package/skills/apex-test-class/references/mocking-patterns.md +0 -219
- package/skills/apex-test-class/references/test-data-factory.md +0 -176
- package/skills/deployment-readiness-check/SKILL.md +0 -257
- package/skills/deployment-readiness-check/assets/deployment_checklist.md +0 -286
- package/skills/deployment-readiness-check/references/rollback_procedures.md +0 -308
- package/skills/deployment-readiness-check/scripts/check_metadata.sh +0 -207
- package/skills/salesforce-experience-site/SKILL.md +0 -178
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Selector class for Account queries.
|
|
3
|
-
* Encapsulates all SOQL for Account records.
|
|
4
|
-
* All methods return bulkified results (Lists or Maps).
|
|
5
|
-
* @author Generated by Apex Class Writer Skill
|
|
6
|
-
*/
|
|
7
|
-
public with sharing class AccountSelector {
|
|
8
|
-
|
|
9
|
-
// ─── Field Lists ─────────────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @description Returns the default set of fields to query for Account.
|
|
13
|
-
* Centralizes field references to keep queries DRY.
|
|
14
|
-
* @return Comma-separated field list as a String
|
|
15
|
-
*/
|
|
16
|
-
private static String getDefaultFields() {
|
|
17
|
-
return String.join(
|
|
18
|
-
new List<String>{
|
|
19
|
-
'Id',
|
|
20
|
-
'Name',
|
|
21
|
-
'AccountNumber',
|
|
22
|
-
'Type',
|
|
23
|
-
'Industry',
|
|
24
|
-
'AnnualRevenue',
|
|
25
|
-
'NumberOfEmployees',
|
|
26
|
-
'Phone',
|
|
27
|
-
'Website',
|
|
28
|
-
'OwnerId',
|
|
29
|
-
'CreatedDate',
|
|
30
|
-
'LastModifiedDate'
|
|
31
|
-
},
|
|
32
|
-
', '
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @description Returns fields needed for billing/territory operations
|
|
38
|
-
* @return Comma-separated field list as a String
|
|
39
|
-
*/
|
|
40
|
-
private static String getBillingFields() {
|
|
41
|
-
return String.join(
|
|
42
|
-
new List<String>{
|
|
43
|
-
'Id',
|
|
44
|
-
'Name',
|
|
45
|
-
'BillingStreet',
|
|
46
|
-
'BillingCity',
|
|
47
|
-
'BillingState',
|
|
48
|
-
'BillingPostalCode',
|
|
49
|
-
'BillingCountry',
|
|
50
|
-
'Territory__c'
|
|
51
|
-
},
|
|
52
|
-
', '
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ─── Query Methods ───────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @description Selects Account records by their Ids
|
|
60
|
-
* @param recordIds Set of Account Ids to query
|
|
61
|
-
* @return List of Account records matching the provided Ids
|
|
62
|
-
* @example
|
|
63
|
-
* Set<Id> ids = new Set<Id>{ '001xx000003DGbY' };
|
|
64
|
-
* List<Account> results = AccountSelector.selectByIds(ids);
|
|
65
|
-
*/
|
|
66
|
-
public static List<Account> selectByIds(Set<Id> recordIds) {
|
|
67
|
-
if (recordIds == null || recordIds.isEmpty()) {
|
|
68
|
-
return new List<Account>();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return Database.query(
|
|
72
|
-
'SELECT ' + getDefaultFields() +
|
|
73
|
-
' FROM Account' +
|
|
74
|
-
' WHERE Id IN :recordIds'
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @description Selects Account records as a Map keyed by Id
|
|
80
|
-
* @param recordIds Set of Account Ids to query
|
|
81
|
-
* @return Map of Id to Account
|
|
82
|
-
*/
|
|
83
|
-
public static Map<Id, Account> selectMapByIds(Set<Id> recordIds) {
|
|
84
|
-
return new Map<Id, Account>(selectByIds(recordIds));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @description Selects Accounts with billing address fields for territory assignment
|
|
89
|
-
* @param recordIds Set of Account Ids to query
|
|
90
|
-
* @return List of Account records with billing address fields populated
|
|
91
|
-
*/
|
|
92
|
-
public static List<Account> selectWithBillingAddress(Set<Id> recordIds) {
|
|
93
|
-
if (recordIds == null || recordIds.isEmpty()) {
|
|
94
|
-
return new List<Account>();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return Database.query(
|
|
98
|
-
'SELECT ' + getBillingFields() +
|
|
99
|
-
' FROM Account' +
|
|
100
|
-
' WHERE Id IN :recordIds'
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @description Selects Accounts by Account Type
|
|
106
|
-
* @param accountTypes Set of Account Type values to filter by
|
|
107
|
-
* @return List of matching Account records
|
|
108
|
-
* @example
|
|
109
|
-
* List<Account> prospects = AccountSelector.selectByType(
|
|
110
|
-
* new Set<String>{ 'Prospect', 'Customer - Direct' }
|
|
111
|
-
* );
|
|
112
|
-
*/
|
|
113
|
-
public static List<Account> selectByType(Set<String> accountTypes) {
|
|
114
|
-
if (accountTypes == null || accountTypes.isEmpty()) {
|
|
115
|
-
return new List<Account>();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return Database.query(
|
|
119
|
-
'SELECT ' + getDefaultFields() +
|
|
120
|
-
' FROM Account' +
|
|
121
|
-
' WHERE Type IN :accountTypes' +
|
|
122
|
-
' ORDER BY Name ASC'
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* @description Selects Accounts by Industry with a minimum annual revenue
|
|
128
|
-
* @param industries Set of Industry values to filter by
|
|
129
|
-
* @param minRevenue Minimum AnnualRevenue threshold
|
|
130
|
-
* @return List of matching Account records ordered by revenue descending
|
|
131
|
-
* @example
|
|
132
|
-
* List<Account> techAccounts = AccountSelector.selectByIndustryAndRevenue(
|
|
133
|
-
* new Set<String>{ 'Technology' },
|
|
134
|
-
* 1000000
|
|
135
|
-
* );
|
|
136
|
-
*/
|
|
137
|
-
public static List<Account> selectByIndustryAndRevenue(Set<String> industries, Decimal minRevenue) {
|
|
138
|
-
if (industries == null || industries.isEmpty()) {
|
|
139
|
-
return new List<Account>();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
Decimal revenueThreshold = minRevenue ?? 0;
|
|
143
|
-
|
|
144
|
-
return Database.query(
|
|
145
|
-
'SELECT ' + getDefaultFields() +
|
|
146
|
-
' FROM Account' +
|
|
147
|
-
' WHERE Industry IN :industries' +
|
|
148
|
-
' AND AnnualRevenue >= :revenueThreshold' +
|
|
149
|
-
' ORDER BY AnnualRevenue DESC'
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @description Selects Accounts with their related Contacts (subquery)
|
|
155
|
-
* @param recordIds Set of Account Ids to query
|
|
156
|
-
* @return List of Account records with nested Contacts
|
|
157
|
-
*/
|
|
158
|
-
public static List<Account> selectWithContacts(Set<Id> recordIds) {
|
|
159
|
-
if (recordIds == null || recordIds.isEmpty()) {
|
|
160
|
-
return new List<Account>();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return [
|
|
164
|
-
SELECT Id, Name, Type, Industry,
|
|
165
|
-
(SELECT Id, FirstName, LastName, Email, Title
|
|
166
|
-
FROM Contacts
|
|
167
|
-
ORDER BY LastName ASC)
|
|
168
|
-
FROM Account
|
|
169
|
-
WHERE Id IN :recordIds
|
|
170
|
-
];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ─── Aggregate Queries ───────────────────────────────────────────────
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* @description Returns a count of Accounts grouped by Industry
|
|
177
|
-
* @return List of AggregateResult with Industry and record count
|
|
178
|
-
* @example
|
|
179
|
-
* List<AggregateResult> results = AccountSelector.countByIndustry();
|
|
180
|
-
* for (AggregateResult ar : results) {
|
|
181
|
-
* System.debug(ar.get('Industry') + ': ' + ar.get('cnt'));
|
|
182
|
-
* }
|
|
183
|
-
*/
|
|
184
|
-
public static List<AggregateResult> countByIndustry() {
|
|
185
|
-
return [
|
|
186
|
-
SELECT Industry, COUNT(Id) cnt
|
|
187
|
-
FROM Account
|
|
188
|
-
WHERE Industry != NULL
|
|
189
|
-
GROUP BY Industry
|
|
190
|
-
ORDER BY COUNT(Id) DESC
|
|
191
|
-
];
|
|
192
|
-
}
|
|
193
|
-
}
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Service class for Account business logic.
|
|
3
|
-
* Provides account deduplication, enrichment, and territory assignment.
|
|
4
|
-
* Delegates queries to AccountSelector and SObject manipulation to AccountDomain.
|
|
5
|
-
* @author Generated by Apex Class Writer Skill
|
|
6
|
-
*/
|
|
7
|
-
public with sharing class AccountService {
|
|
8
|
-
|
|
9
|
-
// ─── Constants ───────────────────────────────────────────────────────
|
|
10
|
-
private static final String ERROR_NULL_INPUT = 'Input cannot be null or empty.';
|
|
11
|
-
private static final String ERROR_MERGE_FAILED = 'Account merge failed for master Id: ';
|
|
12
|
-
private static final Integer MAX_MERGE_BATCH = 3;
|
|
13
|
-
|
|
14
|
-
// ─── Public API ──────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @description Merges duplicate Account records into a master record.
|
|
18
|
-
* The master record retains its field values; child records are reparented.
|
|
19
|
-
* @param masterIds Map of master Account Id to Set of duplicate Account Ids to merge
|
|
20
|
-
* @return List of master Account Ids that were successfully merged
|
|
21
|
-
* @throws AccountServiceException if merge processing fails
|
|
22
|
-
* @example
|
|
23
|
-
* Map<Id, Set<Id>> mergeMap = new Map<Id, Set<Id>>{
|
|
24
|
-
* masterAcctId => new Set<Id>{ dupeId1, dupeId2 }
|
|
25
|
-
* };
|
|
26
|
-
* List<Id> mergedIds = AccountService.mergeAccounts(mergeMap);
|
|
27
|
-
*/
|
|
28
|
-
public static List<Id> mergeAccounts(Map<Id, Set<Id>> mergeMap) {
|
|
29
|
-
if (mergeMap == null || mergeMap.isEmpty()) {
|
|
30
|
-
throw new AccountServiceException(ERROR_NULL_INPUT);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Collect all Ids for a single query
|
|
34
|
-
Set<Id> allIds = new Set<Id>();
|
|
35
|
-
allIds.addAll(mergeMap.keySet());
|
|
36
|
-
for (Set<Id> dupeIds : mergeMap.values()) {
|
|
37
|
-
allIds.addAll(dupeIds);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Query all records at once via Selector
|
|
41
|
-
Map<Id, Account> accountMap = AccountSelector.selectMapByIds(allIds);
|
|
42
|
-
|
|
43
|
-
List<Id> successfulMerges = new List<Id>();
|
|
44
|
-
List<String> errors = new List<String>();
|
|
45
|
-
|
|
46
|
-
for (Id masterId : mergeMap.keySet()) {
|
|
47
|
-
Account master = accountMap.get(masterId);
|
|
48
|
-
if (master == null) {
|
|
49
|
-
errors.add('Master account not found: ' + masterId);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
List<Account> duplicates = new List<Account>();
|
|
54
|
-
for (Id dupeId : mergeMap.get(masterId)) {
|
|
55
|
-
Account dupe = accountMap.get(dupeId);
|
|
56
|
-
if (dupe != null) {
|
|
57
|
-
duplicates.add(dupe);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
// Apex merge supports up to 3 records at a time
|
|
63
|
-
for (List<Account> chunk : chunkAccounts(duplicates, MAX_MERGE_BATCH)) {
|
|
64
|
-
Database.merge(master, chunk);
|
|
65
|
-
}
|
|
66
|
-
successfulMerges.add(masterId);
|
|
67
|
-
} catch (DmlException e) {
|
|
68
|
-
errors.add(ERROR_MERGE_FAILED + masterId + ' - ' + e.getMessage());
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!errors.isEmpty()) {
|
|
73
|
-
System.debug(LoggingLevel.WARN, 'Merge errors: ' + String.join(errors, '\n'));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return successfulMerges;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @description Assigns accounts to territories based on Billing State/Country.
|
|
81
|
-
* Uses Custom Metadata Type (Territory_Mapping__mdt) for mappings.
|
|
82
|
-
* @param accountIds Set of Account Ids to assign territories for
|
|
83
|
-
* @return Number of accounts successfully updated
|
|
84
|
-
* @throws AccountServiceException if territory assignment fails
|
|
85
|
-
*/
|
|
86
|
-
public static Integer assignTerritories(Set<Id> accountIds) {
|
|
87
|
-
if (accountIds == null || accountIds.isEmpty()) {
|
|
88
|
-
throw new AccountServiceException(ERROR_NULL_INPUT);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
List<Account> accounts = AccountSelector.selectWithBillingAddress(accountIds);
|
|
92
|
-
Map<String, String> territoryMap = loadTerritoryMappings();
|
|
93
|
-
|
|
94
|
-
List<Account> toUpdate = new List<Account>();
|
|
95
|
-
for (Account acct : accounts) {
|
|
96
|
-
String key = buildTerritoryKey(acct.BillingState, acct.BillingCountry);
|
|
97
|
-
String territory = territoryMap.get(key);
|
|
98
|
-
|
|
99
|
-
if (territory != null && territory != acct.Territory__c) {
|
|
100
|
-
toUpdate.add(new Account(
|
|
101
|
-
Id = acct.Id,
|
|
102
|
-
Territory__c = territory
|
|
103
|
-
));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!toUpdate.isEmpty()) {
|
|
108
|
-
List<Database.SaveResult> results = Database.update(toUpdate, false);
|
|
109
|
-
return countSuccesses(results);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return 0;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ─── Convenience Overloads ───────────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* @description Single-account territory assignment convenience method
|
|
119
|
-
* @param accountId The Account Id to assign a territory for
|
|
120
|
-
* @return 1 if updated, 0 if no change needed
|
|
121
|
-
*/
|
|
122
|
-
public static Integer assignTerritory(Id accountId) {
|
|
123
|
-
return assignTerritories(new Set<Id>{ accountId });
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// ─── Private Helpers ─────────────────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @description Loads territory mappings from Custom Metadata
|
|
130
|
-
* @return Map of territory key (State:Country) to territory name
|
|
131
|
-
*/
|
|
132
|
-
private static Map<String, String> loadTerritoryMappings() {
|
|
133
|
-
Map<String, String> mappings = new Map<String, String>();
|
|
134
|
-
for (Territory_Mapping__mdt mapping : Territory_Mapping__mdt.getAll().values()) {
|
|
135
|
-
String key = buildTerritoryKey(mapping.State__c, mapping.Country__c);
|
|
136
|
-
mappings.put(key, mapping.Territory_Name__c);
|
|
137
|
-
}
|
|
138
|
-
return mappings;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @description Builds a consistent territory lookup key
|
|
143
|
-
* @param state The billing state
|
|
144
|
-
* @param country The billing country
|
|
145
|
-
* @return A normalized key string
|
|
146
|
-
*/
|
|
147
|
-
private static String buildTerritoryKey(String state, String country) {
|
|
148
|
-
return (state ?? '').toUpperCase() + ':' + (country ?? '').toUpperCase();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* @description Chunks a list of Accounts into sublists of the given size
|
|
153
|
-
* @param accounts The accounts to chunk
|
|
154
|
-
* @param chunkSize Maximum chunk size
|
|
155
|
-
* @return List of account sublists
|
|
156
|
-
*/
|
|
157
|
-
private static List<List<Account>> chunkAccounts(List<Account> accounts, Integer chunkSize) {
|
|
158
|
-
List<List<Account>> chunks = new List<List<Account>>();
|
|
159
|
-
List<Account> current = new List<Account>();
|
|
160
|
-
|
|
161
|
-
for (Account acct : accounts) {
|
|
162
|
-
current.add(acct);
|
|
163
|
-
if (current.size() == chunkSize) {
|
|
164
|
-
chunks.add(current);
|
|
165
|
-
current = new List<Account>();
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (!current.isEmpty()) {
|
|
169
|
-
chunks.add(current);
|
|
170
|
-
}
|
|
171
|
-
return chunks;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* @description Counts successful results from a DML operation
|
|
176
|
-
* @param results List of Database.SaveResult
|
|
177
|
-
* @return Count of successful operations
|
|
178
|
-
*/
|
|
179
|
-
private static Integer countSuccesses(List<Database.SaveResult> results) {
|
|
180
|
-
Integer count = 0;
|
|
181
|
-
for (Database.SaveResult sr : results) {
|
|
182
|
-
if (sr.isSuccess()) {
|
|
183
|
-
count++;
|
|
184
|
-
} else {
|
|
185
|
-
for (Database.Error err : sr.getErrors()) {
|
|
186
|
-
System.debug(LoggingLevel.WARN,
|
|
187
|
-
'Update failed for ' + sr.getId() + ': ' + err.getMessage()
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return count;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ─── Exception ───────────────────────────────────────────────────────
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* @description Custom exception for AccountService errors
|
|
199
|
-
*/
|
|
200
|
-
public class AccountServiceException extends Exception {}
|
|
201
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Abstract base class for {describe the family of classes this serves}.
|
|
3
|
-
* Provides common behavior and defines extension points for subclasses.
|
|
4
|
-
* Subclasses must implement the abstract methods to provide specific behavior.
|
|
5
|
-
* @author Generated by Apex Class Writer Skill
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* // Extending this abstract class:
|
|
9
|
-
* public class SalesforceIntegrationService extends {ClassName} {
|
|
10
|
-
* protected override String getEndpoint() {
|
|
11
|
-
* return 'callout:Salesforce_API/services/data/v62.0';
|
|
12
|
-
* }
|
|
13
|
-
*
|
|
14
|
-
* protected override Map<String, String> getHeaders() {
|
|
15
|
-
* return new Map<String, String>{
|
|
16
|
-
* 'Content-Type' => 'application/json'
|
|
17
|
-
* };
|
|
18
|
-
* }
|
|
19
|
-
* }
|
|
20
|
-
*/
|
|
21
|
-
public abstract with sharing class {ClassName} {
|
|
22
|
-
|
|
23
|
-
// ─── Constants ───────────────────────────────────────────────────────
|
|
24
|
-
private static final Integer DEFAULT_TIMEOUT_MS = 30000;
|
|
25
|
-
|
|
26
|
-
// ─── Protected State ─────────────────────────────────────────────────
|
|
27
|
-
protected Integer timeoutMs;
|
|
28
|
-
|
|
29
|
-
// ─── Constructor ─────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @description Initializes the base class with default configuration
|
|
33
|
-
*/
|
|
34
|
-
protected {ClassName}() {
|
|
35
|
-
this.timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─── Abstract Methods (must be implemented by subclasses) ────────────
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @description Returns the endpoint URL for this integration.
|
|
42
|
-
* Subclasses must provide their specific endpoint.
|
|
43
|
-
* @return The endpoint URL as a String
|
|
44
|
-
*/
|
|
45
|
-
protected abstract String getEndpoint();
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @description Returns the HTTP headers for this integration.
|
|
49
|
-
* Subclasses define their own required headers.
|
|
50
|
-
* @return Map of header name to header value
|
|
51
|
-
*/
|
|
52
|
-
protected abstract Map<String, String> getHeaders();
|
|
53
|
-
|
|
54
|
-
// ─── Virtual Methods (can be overridden by subclasses) ───────────────
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @description Hook called before the main operation executes.
|
|
58
|
-
* Override to add pre-processing logic.
|
|
59
|
-
* Default implementation does nothing.
|
|
60
|
-
* @param context Map of contextual data
|
|
61
|
-
*/
|
|
62
|
-
protected virtual void beforeExecute(Map<String, Object> context) {
|
|
63
|
-
// Default: no-op — override in subclass if needed
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @description Hook called after the main operation completes.
|
|
68
|
-
* Override to add post-processing logic.
|
|
69
|
-
* Default implementation does nothing.
|
|
70
|
-
* @param context Map of contextual data
|
|
71
|
-
* @param result The result from the operation
|
|
72
|
-
*/
|
|
73
|
-
protected virtual void afterExecute(Map<String, Object> context, Object result) {
|
|
74
|
-
// Default: no-op — override in subclass if needed
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ─── Template Method (common workflow) ───────────────────────────────
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @description Executes the operation using the template method pattern.
|
|
81
|
-
* Calls beforeExecute → doExecute → afterExecute in sequence.
|
|
82
|
-
* @param context Map of data needed for the operation
|
|
83
|
-
* @return The result of the operation
|
|
84
|
-
*/
|
|
85
|
-
public Object execute(Map<String, Object> context) {
|
|
86
|
-
beforeExecute(context);
|
|
87
|
-
|
|
88
|
-
Object result;
|
|
89
|
-
try {
|
|
90
|
-
result = doExecute(context);
|
|
91
|
-
} catch (Exception e) {
|
|
92
|
-
handleError(e);
|
|
93
|
-
throw e;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
afterExecute(context, result);
|
|
97
|
-
return result;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ─── Protected Helpers ───────────────────────────────────────────────
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @description Core execution logic — override this for the main operation.
|
|
104
|
-
* Default implementation throws — subclass must provide implementation.
|
|
105
|
-
* @param context Map of data needed for the operation
|
|
106
|
-
* @return The result of the operation
|
|
107
|
-
*/
|
|
108
|
-
protected virtual Object doExecute(Map<String, Object> context) {
|
|
109
|
-
throw new UnsupportedOperationException(
|
|
110
|
-
'Subclass must override doExecute()'
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* @description Error handler called when doExecute throws.
|
|
116
|
-
* Override to customize error handling (e.g., logging, retry).
|
|
117
|
-
* @param e The exception that was thrown
|
|
118
|
-
*/
|
|
119
|
-
protected virtual void handleError(Exception e) {
|
|
120
|
-
System.debug(LoggingLevel.ERROR,
|
|
121
|
-
this.toString() + ' error: ' + e.getMessage() + '\n' + e.getStackTraceString()
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ─── Exception ───────────────────────────────────────────────────────
|
|
126
|
-
|
|
127
|
-
public class UnsupportedOperationException extends Exception {}
|
|
128
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Batch Apex class for {describe the batch operation}.
|
|
3
|
-
* Processes {SObject} records in configurable batch sizes.
|
|
4
|
-
* Implements Database.Stateful to track cumulative results across chunks.
|
|
5
|
-
* @author Generated by Apex Class Writer Skill
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* // Execute with default batch size
|
|
9
|
-
* Database.executeBatch(new {ClassName}());
|
|
10
|
-
*
|
|
11
|
-
* // Execute with custom batch size
|
|
12
|
-
* Database.executeBatch(new {ClassName}(), 100);
|
|
13
|
-
*/
|
|
14
|
-
public with sharing class {ClassName} implements Database.Batchable<SObject>, Database.Stateful {
|
|
15
|
-
|
|
16
|
-
// ─── Constants ───────────────────────────────────────────────────────
|
|
17
|
-
private static final Integer DEFAULT_BATCH_SIZE = 200;
|
|
18
|
-
|
|
19
|
-
// ─── Stateful Tracking ───────────────────────────────────────────────
|
|
20
|
-
private Integer totalProcessed = 0;
|
|
21
|
-
private Integer totalErrors = 0;
|
|
22
|
-
private List<String> errorMessages = new List<String>();
|
|
23
|
-
|
|
24
|
-
// ─── Constructor ─────────────────────────────────────────────────────
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @description Default constructor
|
|
28
|
-
*/
|
|
29
|
-
public {ClassName}() {
|
|
30
|
-
// Default configuration
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ─── Batchable Interface ─────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @description Defines the scope of records to process.
|
|
37
|
-
* Uses Database.QueryLocator for efficient large-dataset processing.
|
|
38
|
-
* @param bc The batch context
|
|
39
|
-
* @return QueryLocator for the records to process
|
|
40
|
-
*/
|
|
41
|
-
public Database.QueryLocator start(Database.BatchableContext bc) {
|
|
42
|
-
return Database.getQueryLocator([
|
|
43
|
-
SELECT Id, Name
|
|
44
|
-
// TODO: Add fields needed for processing
|
|
45
|
-
FROM {SObject}
|
|
46
|
-
// TODO: Add WHERE clause to scope the records
|
|
47
|
-
]);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @description Processes each batch of records.
|
|
52
|
-
* Uses Database.update with allOrNone=false for partial success handling.
|
|
53
|
-
* @param bc The batch context
|
|
54
|
-
* @param scope List of {SObject} records in the current batch
|
|
55
|
-
*/
|
|
56
|
-
public void execute(Database.BatchableContext bc, List<{SObject}> scope) {
|
|
57
|
-
List<{SObject}> recordsToUpdate = new List<{SObject}>();
|
|
58
|
-
|
|
59
|
-
for ({SObject} record : scope) {
|
|
60
|
-
// TODO: Apply business logic to each record
|
|
61
|
-
recordsToUpdate.add(record);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!recordsToUpdate.isEmpty()) {
|
|
65
|
-
List<Database.SaveResult> results = Database.update(recordsToUpdate, false);
|
|
66
|
-
processResults(results);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @description Performs post-processing after all batches complete.
|
|
72
|
-
* Logs a summary of the batch execution.
|
|
73
|
-
* @param bc The batch context
|
|
74
|
-
*/
|
|
75
|
-
public void finish(Database.BatchableContext bc) {
|
|
76
|
-
String summary = String.format(
|
|
77
|
-
'{0} completed. Processed: {1}, Errors: {2}',
|
|
78
|
-
new List<Object>{
|
|
79
|
-
{ClassName}.class.getName(),
|
|
80
|
-
totalProcessed,
|
|
81
|
-
totalErrors
|
|
82
|
-
}
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
System.debug(LoggingLevel.INFO, summary);
|
|
86
|
-
|
|
87
|
-
if (!errorMessages.isEmpty()) {
|
|
88
|
-
System.debug(LoggingLevel.ERROR, 'Error details: ' + String.join(errorMessages, '\n'));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// TODO: Send completion notification email or post to chatter if needed
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ─── Private Helpers ─────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* @description Processes Database.SaveResult list, tracking successes and failures
|
|
98
|
-
* @param results List of SaveResult from a DML operation
|
|
99
|
-
*/
|
|
100
|
-
private void processResults(List<Database.SaveResult> results) {
|
|
101
|
-
for (Database.SaveResult result : results) {
|
|
102
|
-
if (result.isSuccess()) {
|
|
103
|
-
totalProcessed++;
|
|
104
|
-
} else {
|
|
105
|
-
totalErrors++;
|
|
106
|
-
for (Database.Error err : result.getErrors()) {
|
|
107
|
-
errorMessages.add(
|
|
108
|
-
'Record ' + result.getId() + ': ' +
|
|
109
|
-
err.getStatusCode() + ' - ' + err.getMessage()
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ─── Static Helpers ──────────────────────────────────────────────────
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @description Convenience method to execute with default batch size
|
|
120
|
-
* @return The batch job Id
|
|
121
|
-
*/
|
|
122
|
-
public static Id run() {
|
|
123
|
-
return Database.executeBatch(new {ClassName}(), DEFAULT_BATCH_SIZE);
|
|
124
|
-
}
|
|
125
|
-
}
|