@salesforce/plugin-bre-to-cml 1.1.4 → 1.2.4
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/README.md +27 -9
- package/lib/commands/cml/convert/surcharge-rules.d.ts +19 -3
- package/lib/commands/cml/convert/surcharge-rules.js +94 -13
- package/lib/commands/cml/convert/surcharge-rules.js.map +1 -1
- package/lib/commands/cml/convert/underwriting-rules.d.ts +16 -4
- package/lib/commands/cml/convert/underwriting-rules.js +37 -40
- package/lib/commands/cml/convert/underwriting-rules.js.map +1 -1
- package/lib/shared/insurance/insurance-cml-merge.d.ts +91 -0
- package/lib/shared/insurance/insurance-cml-merge.js +569 -0
- package/lib/shared/insurance/insurance-cml-merge.js.map +1 -0
- package/lib/shared/insurance/insurance-org.d.ts +19 -1
- package/lib/shared/insurance/insurance-org.js +9 -1
- package/lib/shared/insurance/insurance-org.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-convert-command.d.ts +46 -2
- package/lib/shared/insurance/insurance-rule-convert-command.js +213 -22
- package/lib/shared/insurance/insurance-rule-convert-command.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-generator.d.ts +38 -1
- package/lib/shared/insurance/insurance-rule-generator.js +165 -6
- package/lib/shared/insurance/insurance-rule-generator.js.map +1 -1
- package/lib/shared/insurance/models.d.ts +25 -0
- package/messages/cml.convert.insurance-shared.md +9 -1
- package/messages/cml.convert.surcharge-rules.md +1 -1
- package/messages/cml.convert.underwriting-rules.md +1 -1
- package/oclif.lock +294 -706
- package/oclif.manifest.json +5 -5
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -58,6 +58,13 @@ sf plugins
|
|
|
58
58
|
|
|
59
59
|
## Commands
|
|
60
60
|
|
|
61
|
+
### Insurance: merging surcharge rules into an existing model
|
|
62
|
+
|
|
63
|
+
`cml convert surcharge-rules` **always** merges generated surcharge rules into an org's existing
|
|
64
|
+
curated `ConstraintModel` — nesting each rule into its leaf product type with a platform-compatible
|
|
65
|
+
pathed rule key — rather than building a fresh, flat model. It requires an existing CML model for the
|
|
66
|
+
resolved CML API.
|
|
67
|
+
|
|
61
68
|
<!-- commands -->
|
|
62
69
|
|
|
63
70
|
- [`sf cml convert prod-cfg-rules`](#sf-cml-convert-prod-cfg-rules)
|
|
@@ -140,7 +147,9 @@ FLAGS
|
|
|
140
147
|
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
|
|
141
148
|
configuration variable is already set.
|
|
142
149
|
--api-version=<value> Override the api version used for api requests made by this command
|
|
143
|
-
--update-records
|
|
150
|
+
--update-records REMOVED. Convert no longer writes to the org; passing this flag now errors. Convert
|
|
151
|
+
instead emits a reviewable record-update file enumerating the org-record changes, which
|
|
152
|
+
you apply to the org separately.
|
|
144
153
|
|
|
145
154
|
GLOBAL FLAGS
|
|
146
155
|
--flags-dir=<value> Import flag values from a directory.
|
|
@@ -149,11 +158,15 @@ GLOBAL FLAGS
|
|
|
149
158
|
DESCRIPTION
|
|
150
159
|
Converts BRE-based Product Surcharge dynamic rules to CML eligibility constraints.
|
|
151
160
|
|
|
152
|
-
Reads ProductSurcharge records from the org (or a JSON file), parses their RuleDefinition, and
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
Reads ProductSurcharge records from the org (or a JSON file), parses their RuleDefinition, and merges the generated
|
|
162
|
+
surcharge rules into the org's existing curated ConstraintModel for the resolved CML API. Each surcharge rule is
|
|
163
|
+
nested into its leaf product type with a platform-compatible pathed rule key (matching the RuleKey the platform
|
|
164
|
+
auto-generates), so the rule actually fires for nested products instead of being silently dropped. The command is
|
|
165
|
+
file-only and never writes to the org. It requires an existing CML model to merge into and outputs a .cml file with
|
|
166
|
+
the full merged model, a header-only \_Associations.csv file, a \_RuleKeyMapping.json with the ProductSurcharge ID to
|
|
167
|
+
RuleKey mapping, and a \_SurchargeUpdate.json file enumerating the org-record changes. Review the files, then apply
|
|
168
|
+
the CML with `sf cml import as-expression-set` and apply the org-record changes enumerated in the
|
|
169
|
+
\_SurchargeUpdate.json file to the org separately (in that order).
|
|
157
170
|
|
|
158
171
|
EXAMPLES
|
|
159
172
|
$ sf cml convert surcharge-rules --cml-api SURCHARGE_CML --target-org myOrg
|
|
@@ -179,7 +192,9 @@ FLAGS
|
|
|
179
192
|
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
|
|
180
193
|
configuration variable is already set.
|
|
181
194
|
--api-version=<value> Override the api version used for api requests made by this command
|
|
182
|
-
--update-records
|
|
195
|
+
--update-records REMOVED. Convert no longer writes to the org; passing this flag now errors. Convert
|
|
196
|
+
instead emits a reviewable record-update file enumerating the org-record changes, which
|
|
197
|
+
you apply to the org separately.
|
|
183
198
|
|
|
184
199
|
GLOBAL FLAGS
|
|
185
200
|
--flags-dir=<value> Import flag values from a directory.
|
|
@@ -190,8 +205,11 @@ DESCRIPTION
|
|
|
190
205
|
|
|
191
206
|
Reads UnderwritingRule records from the org (or a JSON file), parses their DynamicRuleDefinition, and generates CML
|
|
192
207
|
constraints that evaluate underwriting eligibility. Each rule becomes a named constraint that returns true/false. The
|
|
193
|
-
command outputs a .cml file with the constraint model, an
|
|
194
|
-
records,
|
|
208
|
+
command is file-only and never writes to the org: it outputs a .cml file with the constraint model, an
|
|
209
|
+
\_Associations.csv file for ExpressionSetConstraintObj records, a \_RuleKeyMapping.json with the UnderwritingRule ID
|
|
210
|
+
to RuleKey mapping, and a \_UnderwritingUpdate.json file enumerating the org-record changes. Review the files, then
|
|
211
|
+
apply the CML with `sf cml import as-expression-set` and apply the org-record changes enumerated in the
|
|
212
|
+
\_UnderwritingUpdate.json file to the org separately.
|
|
195
213
|
|
|
196
214
|
EXAMPLES
|
|
197
215
|
$ sf cml convert underwriting-rules --cml-api UW_CML --target-org myOrg
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Connection } from '@salesforce/core';
|
|
2
|
-
import { ParsedRuleDefinition, RuleKeyEntry, RuleRecord } from '../../../shared/insurance/models.js';
|
|
3
|
-
import { InsuranceRuleConvertCommand, InsuranceRuleConvertResult } from '../../../shared/insurance/insurance-rule-convert-command.js';
|
|
2
|
+
import { ParsedRuleDefinition, RecordUpdate, RuleKeyEntry, RuleRecord } from '../../../shared/insurance/models.js';
|
|
3
|
+
import { InsuranceRuleConvertCommand, InsuranceRuleConvertContext, InsuranceRuleConvertResult, ParsedRuleEntry } from '../../../shared/insurance/insurance-rule-convert-command.js';
|
|
4
4
|
type ProductSurchargeRecord = RuleRecord & {
|
|
5
5
|
RuleDefinition: string | null;
|
|
6
6
|
};
|
|
@@ -21,9 +21,25 @@ export default class CmlConvertSurchargeRules extends InsuranceRuleConvertComman
|
|
|
21
21
|
protected readonly keyPrefix = "SC";
|
|
22
22
|
protected readonly constraintLabel = "Surcharge eligibility";
|
|
23
23
|
protected readonly apiNamePrefix = "SC_";
|
|
24
|
+
protected readonly ruleType = "InsuranceSurchargeRule";
|
|
24
25
|
protected readonly soql = "SELECT Id, Name, RuleDefinition, ProductPath FROM ProductSurcharge WHERE RuleApiName != null";
|
|
26
|
+
protected readonly recordUpdateKind: "surcharge-update";
|
|
27
|
+
private readonly surchargeProductCodes;
|
|
25
28
|
run(): Promise<CmlConvertSurchargeRulesResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Merge mode: read the org's existing ConstraintModel, compute each surcharge's pathed rule key
|
|
31
|
+
* (matching the platform's auto-generated RuleKey), and nest the `rule(...)` statement into the
|
|
32
|
+
* correct existing leaf `type` block — instead of overwriting the curated model with a flat one.
|
|
33
|
+
*/
|
|
34
|
+
protected runMergeConvert(_ctx: InsuranceRuleConvertContext, conn: Connection, records: ProductSurchargeRecord[], ruleDefs: ParsedRuleEntry[], productIdToCode: Map<string, string>, api: string, safeApi: string, workspaceDir: string): Promise<InsuranceRuleConvertResult>;
|
|
26
35
|
protected parseRecord(record: ProductSurchargeRecord): ParsedRuleDefinition | null;
|
|
27
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Pure transform — the file-only successor to the old live updateOrgRecords. Mirrors it exactly:
|
|
38
|
+
* one ProductSurcharge per placed rule, flipping RuleEngineType to ConstraintEngine. The
|
|
39
|
+
* expectedRuleKey (the convert-computed pathed key) and productCodes are advisory — they are NOT
|
|
40
|
+
* written to the org (the platform regenerates ProductSurcharge.RuleKey on the flip); the apply
|
|
41
|
+
* uses them to verify the surcharge will actually fire and to flag ProductCode/ProductPath drift.
|
|
42
|
+
*/
|
|
43
|
+
protected buildRecordUpdates(_records: ProductSurchargeRecord[], ruleKeyMapping: RuleKeyEntry[]): RecordUpdate[];
|
|
28
44
|
}
|
|
29
45
|
export {};
|
|
@@ -17,6 +17,7 @@ import { Flags } from '@salesforce/sf-plugins-core';
|
|
|
17
17
|
import { Messages } from '@salesforce/core';
|
|
18
18
|
import { InsuranceRuleConvertCommand, } from '../../../shared/insurance/insurance-rule-convert-command.js';
|
|
19
19
|
import { decodeHtmlEntities } from '../../../shared/insurance/insurance-rule-generator.js';
|
|
20
|
+
import { buildPathedSurchargeRules, fetchExistingConstraintModel, fetchProductTypeTags, mergeSurchargeRules, splitProductPath, } from '../../../shared/insurance/insurance-cml-merge.js';
|
|
20
21
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
21
22
|
const messages = Messages.loadMessages('@salesforce/plugin-bre-to-cml', 'cml.convert.surcharge-rules');
|
|
22
23
|
// eslint-disable-next-line sf-plugin/only-extend-SfCommand
|
|
@@ -36,7 +37,14 @@ export default class CmlConvertSurchargeRules extends InsuranceRuleConvertComman
|
|
|
36
37
|
keyPrefix = 'SC';
|
|
37
38
|
constraintLabel = 'Surcharge eligibility';
|
|
38
39
|
apiNamePrefix = 'SC_';
|
|
40
|
+
// Emit surcharge eligibility as a CML `rule(decl, "InsuranceSurchargeRule", "<ruleKey>", "True")`.
|
|
41
|
+
ruleType = 'InsuranceSurchargeRule';
|
|
39
42
|
soql = 'SELECT Id, Name, RuleDefinition, ProductPath FROM ProductSurcharge WHERE RuleApiName != null';
|
|
43
|
+
recordUpdateKind = 'surcharge-update';
|
|
44
|
+
// Populated during runMergeConvert (the only place the ProductPath→ProductCode mapping is known)
|
|
45
|
+
// and consumed by buildRecordUpdates, which the base calls to serialize the update file. Maps a
|
|
46
|
+
// ProductSurcharge Id to its ordered ProductCode path — advisory drift-detection for the apply.
|
|
47
|
+
surchargeProductCodes = new Map();
|
|
40
48
|
async run() {
|
|
41
49
|
const { flags } = await this.parse(CmlConvertSurchargeRules);
|
|
42
50
|
return this.runConvert({
|
|
@@ -46,8 +54,79 @@ export default class CmlConvertSurchargeRules extends InsuranceRuleConvertComman
|
|
|
46
54
|
workspaceDir: flags['workspace-dir'],
|
|
47
55
|
inputFile: flags['surcharge-file'],
|
|
48
56
|
updateRecords: flags['update-records'],
|
|
57
|
+
// Surcharge conversion ALWAYS merges into the org's existing curated ConstraintModel —
|
|
58
|
+
// the flat-overwrite build path is intentionally unreachable here (it silently drops nested
|
|
59
|
+
// surcharges via non-pathed rule keys). There is no flag to opt out.
|
|
60
|
+
mergeWithOrg: true,
|
|
49
61
|
});
|
|
50
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Merge mode: read the org's existing ConstraintModel, compute each surcharge's pathed rule key
|
|
65
|
+
* (matching the platform's auto-generated RuleKey), and nest the `rule(...)` statement into the
|
|
66
|
+
* correct existing leaf `type` block — instead of overwriting the curated model with a flat one.
|
|
67
|
+
*/
|
|
68
|
+
async runMergeConvert(_ctx, conn, records, ruleDefs, productIdToCode, api, safeApi, workspaceDir) {
|
|
69
|
+
const existing = await fetchExistingConstraintModel(conn, api);
|
|
70
|
+
if (!existing?.cmlText.trim()) {
|
|
71
|
+
this.error(`surcharge-rules merges into an existing ConstraintModel for CML API '${api}', but none was found. Create the curated model first (e.g. via the underwriting/prod-cfg converters or by importing a baseline), then re-run this command.`);
|
|
72
|
+
}
|
|
73
|
+
const productIds = new Set();
|
|
74
|
+
for (const { record } of ruleDefs) {
|
|
75
|
+
// [Fix #11] Route every ProductPath parse through one helper so trim + drop-empty semantics
|
|
76
|
+
// never diverge between the convert layer and the merge module.
|
|
77
|
+
for (const id of splitProductPath(record.ProductPath))
|
|
78
|
+
productIds.add(id);
|
|
79
|
+
}
|
|
80
|
+
const productIdToType = await fetchProductTypeTags(conn, productIds);
|
|
81
|
+
// Capture the ProductCode path per surcharge so the emitted update file can carry it (same
|
|
82
|
+
// mapping buildPathedSurchargeRules uses for the rule key). Drift here would desync the platform
|
|
83
|
+
// RuleKey, so it is recorded for the apply-time check rather than discarded.
|
|
84
|
+
this.surchargeProductCodes.clear();
|
|
85
|
+
for (const { record } of ruleDefs) {
|
|
86
|
+
// [Fix #11] Same single helper.
|
|
87
|
+
const codes = splitProductPath(record.ProductPath).map((id) => productIdToCode.get(id) ?? id);
|
|
88
|
+
this.surchargeProductCodes.set(record.Id, codes);
|
|
89
|
+
}
|
|
90
|
+
const rules = buildPathedSurchargeRules(this.keyPrefix, ruleDefs, productIdToCode, productIdToType);
|
|
91
|
+
rules.forEach((r) => this.log(` -> ${r.recordName} => ${r.ruleKey} (type: ${r.typeName ?? 'UNRESOLVED'})`));
|
|
92
|
+
const { mergedCml, placements, skips, attributeWarnings } = mergeSurchargeRules(existing.cmlText, rules);
|
|
93
|
+
this.log(`\nMerge summary: ${placements.filter((p) => p.status === 'inserted').length} inserted, ${placements.filter((p) => p.status === 'replaced').length} updated in place, ${skips.length} skipped.`);
|
|
94
|
+
for (const s of skips)
|
|
95
|
+
this.warn(` SKIPPED ${s.rule.recordName}: ${s.reason}`);
|
|
96
|
+
for (const w of attributeWarnings)
|
|
97
|
+
this.warn(` ATTRIBUTE ${w}`);
|
|
98
|
+
// [Fix #9] When skips are present, log a high-signal final summary that buckets reasons —
|
|
99
|
+
// an operator scanning hundreds of per-rule warnings should not have to grep to learn whether
|
|
100
|
+
// they're staring at one duplicate or fifty missing-type-tag rules. Buckets match the merge
|
|
101
|
+
// module's skip-reason vocabulary; anything that doesn't match a known reason falls into "other".
|
|
102
|
+
if (skips.length > 0) {
|
|
103
|
+
const counts = { duplicate: 0, emptyPath: 0, noTypeTag: 0, typeBlockMissing: 0, typeBlockAmbiguous: 0, other: 0 };
|
|
104
|
+
for (const s of skips) {
|
|
105
|
+
if (s.reason.startsWith('duplicate pathed rule key'))
|
|
106
|
+
counts.duplicate += 1;
|
|
107
|
+
else if (s.reason.startsWith('empty ProductPath'))
|
|
108
|
+
counts.emptyPath += 1;
|
|
109
|
+
else if (s.reason.startsWith('no CML type tag'))
|
|
110
|
+
counts.noTypeTag += 1;
|
|
111
|
+
else if (s.reason.includes('not found in existing model'))
|
|
112
|
+
counts.typeBlockMissing += 1;
|
|
113
|
+
else if (s.reason.includes('is ambiguous'))
|
|
114
|
+
counts.typeBlockAmbiguous += 1;
|
|
115
|
+
else
|
|
116
|
+
counts.other += 1;
|
|
117
|
+
}
|
|
118
|
+
this.log(`Skip breakdown: ${counts.duplicate} duplicate-key, ${counts.emptyPath} empty-ProductPath, ` +
|
|
119
|
+
`${counts.noTypeTag} no-type-tag, ${counts.typeBlockMissing} type-block-missing, ` +
|
|
120
|
+
`${counts.typeBlockAmbiguous} type-block-ambiguous, ${counts.other} other`);
|
|
121
|
+
}
|
|
122
|
+
const ruleKeyMapping = placements.map((p) => ({
|
|
123
|
+
recordId: p.rule.recordId,
|
|
124
|
+
name: p.rule.recordName,
|
|
125
|
+
ruleKey: p.rule.ruleKey,
|
|
126
|
+
}));
|
|
127
|
+
const recordUpdateFile = await this.writeRecordUpdateFile(records, ruleKeyMapping, api, safeApi, workspaceDir);
|
|
128
|
+
return this.writeMergedOutputFiles(mergedCml, ruleKeyMapping, safeApi, workspaceDir, api, recordUpdateFile);
|
|
129
|
+
}
|
|
51
130
|
parseRecord(record) {
|
|
52
131
|
if (!record.RuleDefinition) {
|
|
53
132
|
this.warn(`Skipping ${record.Name}: no RuleDefinition`);
|
|
@@ -67,20 +146,22 @@ export default class CmlConvertSurchargeRules extends InsuranceRuleConvertComman
|
|
|
67
146
|
return null;
|
|
68
147
|
}
|
|
69
148
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Pure transform — the file-only successor to the old live updateOrgRecords. Mirrors it exactly:
|
|
151
|
+
* one ProductSurcharge per placed rule, flipping RuleEngineType to ConstraintEngine. The
|
|
152
|
+
* expectedRuleKey (the convert-computed pathed key) and productCodes are advisory — they are NOT
|
|
153
|
+
* written to the org (the platform regenerates ProductSurcharge.RuleKey on the flip); the apply
|
|
154
|
+
* uses them to verify the surcharge will actually fire and to flag ProductCode/ProductPath drift.
|
|
155
|
+
*/
|
|
156
|
+
buildRecordUpdates(_records, ruleKeyMapping) {
|
|
157
|
+
return ruleKeyMapping.map((m) => ({
|
|
158
|
+
sobject: 'ProductSurcharge',
|
|
159
|
+
id: m.recordId,
|
|
160
|
+
name: m.name,
|
|
161
|
+
fields: [{ field: 'RuleEngineType', value: 'ConstraintEngine' }],
|
|
162
|
+
expectedRuleKey: m.ruleKey,
|
|
163
|
+
productCodes: this.surchargeProductCodes.get(m.recordId),
|
|
75
164
|
}));
|
|
76
|
-
const results = await conn.sobject('ProductSurcharge').update(updates);
|
|
77
|
-
const list = Array.isArray(results) ? results : [results];
|
|
78
|
-
const successes = list.filter((r) => r.success);
|
|
79
|
-
const failures = list.filter((r) => !r.success);
|
|
80
|
-
this.log(` Updated ${successes.length} records successfully`);
|
|
81
|
-
for (const f of failures) {
|
|
82
|
-
this.warn(` Failed to update ${f.id ?? 'unknown'}: ${JSON.stringify(f.errors)}`);
|
|
83
|
-
}
|
|
84
165
|
}
|
|
85
166
|
}
|
|
86
167
|
//# sourceMappingURL=surcharge-rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surcharge-rules.js","sourceRoot":"","sources":["../../../../src/commands/cml/convert/surcharge-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,EAAc,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EACL,2BAA2B,
|
|
1
|
+
{"version":3,"file":"surcharge-rules.js","sourceRoot":"","sources":["../../../../src/commands/cml/convert/surcharge-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ParsedRuleDefinition, RuleKeyEntry, RuleRecord } from '../../../shared/insurance/models.js';
|
|
1
|
+
import { ParsedRuleDefinition, RecordUpdate, RuleKeyEntry, RuleRecord } from '../../../shared/insurance/models.js';
|
|
3
2
|
import { InsuranceRuleConvertCommand, InsuranceRuleConvertResult } from '../../../shared/insurance/insurance-rule-convert-command.js';
|
|
4
3
|
type UnderwritingRuleRecord = RuleRecord & {
|
|
5
4
|
ApiName: string | null;
|
|
6
5
|
DynamicRuleDefinition: string | null;
|
|
7
6
|
RuleKey: string | null;
|
|
8
7
|
UnderwritingRuleGroupId: string | null;
|
|
8
|
+
UnderwritingRuleGroup: {
|
|
9
|
+
Name: string | null;
|
|
10
|
+
} | null;
|
|
9
11
|
};
|
|
10
12
|
export type CmlConvertUnderwritingRulesResult = InsuranceRuleConvertResult;
|
|
11
13
|
export default class CmlConvertUnderwritingRules extends InsuranceRuleConvertCommand<UnderwritingRuleRecord> {
|
|
@@ -24,9 +26,19 @@ export default class CmlConvertUnderwritingRules extends InsuranceRuleConvertCom
|
|
|
24
26
|
protected readonly keyPrefix = "UW";
|
|
25
27
|
protected readonly constraintLabel = "Underwriting eligibility";
|
|
26
28
|
protected readonly apiNamePrefix = "UW_";
|
|
27
|
-
protected readonly soql = "SELECT Id, Name, ApiName, DynamicRuleDefinition, ProductPath, RuleKey, UnderwritingRuleGroupId FROM UnderwritingRule WHERE RuleKey = null";
|
|
29
|
+
protected readonly soql = "SELECT Id, Name, ApiName, DynamicRuleDefinition, ProductPath, RuleKey, UnderwritingRuleGroupId, UnderwritingRuleGroup.Name FROM UnderwritingRule WHERE RuleKey = null";
|
|
30
|
+
protected readonly recordUpdateKind: "underwriting-update";
|
|
28
31
|
run(): Promise<CmlConvertUnderwritingRulesResult>;
|
|
29
32
|
protected parseRecord(record: UnderwritingRuleRecord): ParsedRuleDefinition | null;
|
|
30
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Pure transform — the file-only successor to the old live updateOrgRecords. Produces the exact
|
|
35
|
+
* org-record changes convert previously applied: (1) each UnderwritingRuleGroup flipped to
|
|
36
|
+
* RuleEngineType=ConstraintEngine, then (2) each BRE UnderwritingRule's DynamicRuleDefinition blob
|
|
37
|
+
* rewritten with the converted ruleKey (and the nested underwritingRuleGroup.ruleEngineType, when
|
|
38
|
+
* present). Groups are emitted first so a faithful apply mirrors the original ordering. The blob
|
|
39
|
+
* rewrite uses a RAW JSON.parse of the org's stored value (NOT decodeHtmlEntities) — byte-for-byte
|
|
40
|
+
* the same mutation the live path performed.
|
|
41
|
+
*/
|
|
42
|
+
protected buildRecordUpdates(records: UnderwritingRuleRecord[], ruleKeyMapping: RuleKeyEntry[]): RecordUpdate[];
|
|
31
43
|
}
|
|
32
44
|
export {};
|
|
@@ -38,7 +38,8 @@ export default class CmlConvertUnderwritingRules extends InsuranceRuleConvertCom
|
|
|
38
38
|
apiNamePrefix = 'UW_';
|
|
39
39
|
// DynamicRuleDefinition is a non-filterable (long text) field, so it can't appear in the
|
|
40
40
|
// WHERE clause; records with a null DynamicRuleDefinition are skipped during parsing instead.
|
|
41
|
-
soql = 'SELECT Id, Name, ApiName, DynamicRuleDefinition, ProductPath, RuleKey, UnderwritingRuleGroupId FROM UnderwritingRule WHERE RuleKey = null';
|
|
41
|
+
soql = 'SELECT Id, Name, ApiName, DynamicRuleDefinition, ProductPath, RuleKey, UnderwritingRuleGroupId, UnderwritingRuleGroup.Name FROM UnderwritingRule WHERE RuleKey = null';
|
|
42
|
+
recordUpdateKind = 'underwriting-update';
|
|
42
43
|
async run() {
|
|
43
44
|
const { flags } = await this.parse(CmlConvertUnderwritingRules);
|
|
44
45
|
return this.runConvert({
|
|
@@ -69,38 +70,41 @@ export default class CmlConvertUnderwritingRules extends InsuranceRuleConvertCom
|
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Pure transform — the file-only successor to the old live updateOrgRecords. Produces the exact
|
|
75
|
+
* org-record changes convert previously applied: (1) each UnderwritingRuleGroup flipped to
|
|
76
|
+
* RuleEngineType=ConstraintEngine, then (2) each BRE UnderwritingRule's DynamicRuleDefinition blob
|
|
77
|
+
* rewritten with the converted ruleKey (and the nested underwritingRuleGroup.ruleEngineType, when
|
|
78
|
+
* present). Groups are emitted first so a faithful apply mirrors the original ordering. The blob
|
|
79
|
+
* rewrite uses a RAW JSON.parse of the org's stored value (NOT decodeHtmlEntities) — byte-for-byte
|
|
80
|
+
* the same mutation the live path performed.
|
|
81
|
+
*/
|
|
82
|
+
buildRecordUpdates(records, ruleKeyMapping) {
|
|
83
|
+
const updates = [];
|
|
84
|
+
// (1) UnderwritingRuleGroup flips — union of every referenced group, name resolved from the
|
|
85
|
+
// queried relationship so the apply-time identity guard has something to cross-check.
|
|
86
|
+
const groupNames = new Map();
|
|
74
87
|
for (const record of records) {
|
|
75
88
|
if (record.UnderwritingRuleGroupId) {
|
|
76
|
-
|
|
89
|
+
groupNames.set(record.UnderwritingRuleGroupId, record.UnderwritingRuleGroup?.Name ?? null);
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}));
|
|
85
|
-
const groupResults = await conn.sobject('UnderwritingRuleGroup').update(groupUpdates);
|
|
86
|
-
const groupList = Array.isArray(groupResults) ? groupResults : [groupResults];
|
|
87
|
-
const groupSuccesses = groupList.filter((r) => r.success);
|
|
88
|
-
const groupFailures = groupList.filter((r) => !r.success);
|
|
89
|
-
this.log(` Updated ${groupSuccesses.length} group records`);
|
|
90
|
-
for (const f of groupFailures) {
|
|
91
|
-
this.warn(` Failed to update group ${f.id ?? 'unknown'}: ${JSON.stringify(f.errors)}`);
|
|
92
|
+
for (const [groupId, groupName] of groupNames) {
|
|
93
|
+
if (!groupName) {
|
|
94
|
+
// Without a Name the apply can't run its identity guard; skip rather than write blind.
|
|
95
|
+
this.warn(`Skipping UnderwritingRuleGroup ${groupId}: no Name resolved (cannot verify identity on apply)`);
|
|
96
|
+
continue;
|
|
92
97
|
}
|
|
98
|
+
updates.push({
|
|
99
|
+
sobject: 'UnderwritingRuleGroup',
|
|
100
|
+
id: groupId,
|
|
101
|
+
name: groupName,
|
|
102
|
+
fields: [{ field: 'RuleEngineType', value: 'ConstraintEngine' }],
|
|
103
|
+
});
|
|
93
104
|
}
|
|
105
|
+
// (2) UnderwritingRule DynamicRuleDefinition rewrites — same subset and same mutation as live.
|
|
94
106
|
const ruleKeyMap = new Map(ruleKeyMapping.map((m) => [m.recordId, m.ruleKey]));
|
|
95
107
|
const breRecords = records.filter((r) => !r.RuleKey && r.DynamicRuleDefinition);
|
|
96
|
-
if (breRecords.length === 0) {
|
|
97
|
-
this.log('\nNo BRE rules to update with RuleKey.');
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
this.log(`\nUpdating ${breRecords.length} UnderwritingRule DynamicRuleDefinition with ruleKey and ruleEngineType...`);
|
|
101
|
-
let successCount = 0;
|
|
102
|
-
let failCount = 0;
|
|
103
|
-
const updates = [];
|
|
104
108
|
for (const record of breRecords) {
|
|
105
109
|
const ruleKey = ruleKeyMap.get(record.Id);
|
|
106
110
|
if (!ruleKey || !record.DynamicRuleDefinition)
|
|
@@ -111,26 +115,19 @@ export default class CmlConvertUnderwritingRules extends InsuranceRuleConvertCom
|
|
|
111
115
|
if (defn.underwritingRuleGroup && typeof defn.underwritingRuleGroup === 'object') {
|
|
112
116
|
defn.underwritingRuleGroup.ruleEngineType = 'ConstraintEngine';
|
|
113
117
|
}
|
|
114
|
-
updates.push({
|
|
118
|
+
updates.push({
|
|
119
|
+
sobject: 'UnderwritingRule',
|
|
120
|
+
id: record.Id,
|
|
121
|
+
name: record.Name,
|
|
122
|
+
apiName: record.ApiName ?? undefined,
|
|
123
|
+
fields: [{ field: 'DynamicRuleDefinition', value: JSON.stringify(defn) }],
|
|
124
|
+
});
|
|
115
125
|
}
|
|
116
126
|
catch {
|
|
117
127
|
this.warn(` Failed to parse DynamicRuleDefinition for ${record.Name}`);
|
|
118
|
-
failCount++;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (updates.length > 0) {
|
|
122
|
-
const results = await conn.sobject('UnderwritingRule').update(updates);
|
|
123
|
-
for (const r of Array.isArray(results) ? results : [results]) {
|
|
124
|
-
if (r.success) {
|
|
125
|
-
successCount++;
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
failCount++;
|
|
129
|
-
this.warn(` Failed to update ${r.id ?? 'unknown'}: ${JSON.stringify(r.errors)}`);
|
|
130
|
-
}
|
|
131
128
|
}
|
|
132
129
|
}
|
|
133
|
-
|
|
130
|
+
return updates;
|
|
134
131
|
}
|
|
135
132
|
}
|
|
136
133
|
//# sourceMappingURL=underwriting-rules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"underwriting-rules.js","sourceRoot":"","sources":["../../../../src/commands/cml/convert/underwriting-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACpD,OAAO,
|
|
1
|
+
{"version":3,"file":"underwriting-rules.js","sourceRoot":"","sources":["../../../../src/commands/cml/convert/underwriting-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Connection } from '@salesforce/core';
|
|
2
|
+
import { ParsedRuleDefinition, RuleRecord } from './models.js';
|
|
3
|
+
/**
|
|
4
|
+
* MERGE MODE (insurance-only): instead of building a fresh single-type CML and PATCH-replacing
|
|
5
|
+
* the org's curated "Gold Standard" ConstraintModel, this module reads the existing model text,
|
|
6
|
+
* computes each surcharge's PLATFORM-COMPATIBLE pathed rule key, and inserts/updates the
|
|
7
|
+
* `rule(...)` statement inside the correct existing leaf `type` block. The result is the full
|
|
8
|
+
* merged model, written to the same `${cmlApi}.cml` path the common import reads — so the import
|
|
9
|
+
* still does a whole-file PATCH, but the file now contains the curated model + the surcharge rules
|
|
10
|
+
* nested correctly rather than a flat overwrite.
|
|
11
|
+
*
|
|
12
|
+
* Why pathed keys: when a ProductSurcharge is persisted as ConstraintEngine the platform
|
|
13
|
+
* auto-generates `RuleKey` as `SC` + sanitize(ProductCode) of every segment in ProductPath (in
|
|
14
|
+
* order) + sanitize(apiName), joined by `__`. The Core surcharge engine matches the fired CML rule
|
|
15
|
+
* key against that auto-generated RuleKey by exact string, so the CML rule key MUST be pathed too.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SURCHARGE_RULE_ACTION = "InsuranceSurchargeRule";
|
|
18
|
+
export type PathedSurchargeRule = {
|
|
19
|
+
recordId: string;
|
|
20
|
+
recordName: string;
|
|
21
|
+
apiName: string;
|
|
22
|
+
/** Full pathed key: SC__<code-of-each-path-segment>__<apiName>. */
|
|
23
|
+
ruleKey: string;
|
|
24
|
+
/**
|
|
25
|
+
* Ordered ProductCodes for every ProductPath segment used to build {@link ruleKey}. Empty when the
|
|
26
|
+
* source ProductPath was blank/whitespace-only — the merge guards on this to refuse the replace
|
|
27
|
+
* path for malformed input (an empty-path rule degenerates to `SC__<apiName>` which could
|
|
28
|
+
* coincidentally match a curated short-keyed line).
|
|
29
|
+
*/
|
|
30
|
+
pathProductCodes: string[];
|
|
31
|
+
/** Leaf CML type name (ConstraintModelTag of the LAST ProductPath segment). */
|
|
32
|
+
typeName: string | undefined;
|
|
33
|
+
/** The generated `rule(<decl>, "InsuranceSurchargeRule", "<ruleKey>", "True");` statement. */
|
|
34
|
+
statement: string;
|
|
35
|
+
/** Sanitized attribute names referenced by the rule declaration (for visibility warnings). */
|
|
36
|
+
referencedAttributes: string[];
|
|
37
|
+
};
|
|
38
|
+
export type MergePlacement = {
|
|
39
|
+
rule: PathedSurchargeRule;
|
|
40
|
+
status: 'inserted' | 'replaced';
|
|
41
|
+
};
|
|
42
|
+
export type MergeSkip = {
|
|
43
|
+
rule: PathedSurchargeRule;
|
|
44
|
+
reason: string;
|
|
45
|
+
};
|
|
46
|
+
export type MergeResult = {
|
|
47
|
+
mergedCml: string;
|
|
48
|
+
placements: MergePlacement[];
|
|
49
|
+
skips: MergeSkip[];
|
|
50
|
+
attributeWarnings: string[];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Builds the pathed surcharge rule key that mirrors the platform's auto-generated
|
|
54
|
+
* `ProductSurcharge.RuleKey`: prefix + every path-segment product code + apiName.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildPathedRuleKey(prefix: string, pathProductCodes: string[], apiName: string, stageTransition?: string): string;
|
|
57
|
+
/** Splits a `ProductPath` into its ordered Product2 ids (slash-separated). */
|
|
58
|
+
export declare function splitProductPath(productPath: string): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Reads the org's existing ConstraintModel text for the given CML API name, mirroring the lookup
|
|
61
|
+
* the common import uses (ExpressionSetDefinition.DeveloperName -> ExpressionSetDefinitionVersion).
|
|
62
|
+
* Returns undefined when no model exists yet (caller should then fall back to the build path).
|
|
63
|
+
*/
|
|
64
|
+
export declare function fetchExistingConstraintModel(conn: Connection, cmlApiName: string): Promise<{
|
|
65
|
+
versionId: string;
|
|
66
|
+
cmlText: string;
|
|
67
|
+
} | undefined>;
|
|
68
|
+
/**
|
|
69
|
+
* Resolves Product2 id -> CML type name from the authoritative source: ExpressionSetConstraintObj
|
|
70
|
+
* rows with ConstraintModelTagType='Type' (ReferenceObjectId is the Product2 id, ConstraintModelTag
|
|
71
|
+
* is the CML type name). This is how the curated model binds products to type blocks, so it's the
|
|
72
|
+
* correct way to find which existing `type` block a surcharge's leaf product nests into.
|
|
73
|
+
*/
|
|
74
|
+
export declare function fetchProductTypeTags(conn: Connection, productIds: Set<string>): Promise<Map<string, string>>;
|
|
75
|
+
/** Builds the `rule(...)` statement for a surcharge, using the same constraint generator as build mode. */
|
|
76
|
+
export declare function buildSurchargeRuleStatement(declaration: string, ruleKey: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Prepares the pathed-rule descriptors for a set of parsed surcharge records.
|
|
79
|
+
* `productIdToCode` and `productIdToType` must already cover every ProductPath segment.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildPathedSurchargeRules(prefix: string, ruleDefs: Array<{
|
|
82
|
+
record: RuleRecord;
|
|
83
|
+
ruleDef: ParsedRuleDefinition;
|
|
84
|
+
}>, productIdToCode: Map<string, string>, productIdToType: Map<string, string>): PathedSurchargeRule[];
|
|
85
|
+
/**
|
|
86
|
+
* Merges the pathed surcharge `rule(...)` statements into the existing CML text. If the rule key is
|
|
87
|
+
* already present, the existing statement line is replaced in place (idempotent). Otherwise the
|
|
88
|
+
* statement is inserted just before the closing brace of the leaf `type` block. Rules whose leaf type
|
|
89
|
+
* block can't be found are skipped (reported, never silently dropped).
|
|
90
|
+
*/
|
|
91
|
+
export declare function mergeSurchargeRules(existingCml: string, rules: PathedSurchargeRule[]): MergeResult;
|