@salesforce/plugin-bre-to-cml 1.1.5 → 1.2.5

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 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 Update source records in the org with RuleEngineType=ConstraintEngine.
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 generates CML
153
- constraints that evaluate surcharge eligibility. Each surcharge rule becomes a named constraint that returns
154
- true/false. The command outputs a .cml file with the constraint model, an \_Associations.csv file for
155
- ExpressionSetConstraintObj records, and a \_RuleKeyMapping.json with the ProductSurcharge ID to RuleKey mapping for
156
- updating records.
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 Update source records in the org with RuleEngineType=ConstraintEngine.
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 \_Associations.csv file for ExpressionSetConstraintObj
194
- records, and a \_RuleKeyMapping.json with the UnderwritingRule ID to RuleKey mapping for updating 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
- protected updateOrgRecords(_records: ProductSurchargeRecord[], ruleKeyMapping: RuleKeyEntry[], conn: Connection): Promise<void>;
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
- async updateOrgRecords(_records, ruleKeyMapping, conn) {
71
- this.log('\nUpdating ProductSurcharge records with RuleEngineType=ConstraintEngine...');
72
- const updates = ruleKeyMapping.map((m) => ({
73
- Id: m.recordId,
74
- RuleEngineType: 'ConstraintEngine',
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":"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"}
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 { Connection } from '@salesforce/core';
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
- protected updateOrgRecords(records: UnderwritingRuleRecord[], ruleKeyMapping: RuleKeyEntry[], conn: Connection): Promise<void>;
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
- async updateOrgRecords(records, ruleKeyMapping, conn) {
73
- const groupIds = new Set();
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
- groupIds.add(record.UnderwritingRuleGroupId);
89
+ groupNames.set(record.UnderwritingRuleGroupId, record.UnderwritingRuleGroup?.Name ?? null);
77
90
  }
78
91
  }
79
- if (groupIds.size > 0) {
80
- this.log(`\nUpdating ${groupIds.size} UnderwritingRuleGroup records with RuleEngineType=ConstraintEngine...`);
81
- const groupUpdates = Array.from(groupIds).map((id) => ({
82
- Id: id,
83
- RuleEngineType: 'ConstraintEngine',
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({ Id: record.Id, DynamicRuleDefinition: JSON.stringify(defn) });
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
- this.log(` Updated ${successCount} rule records, ${failCount} failed`);
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,EAAc,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EACL,2BAA2B,GAE5B,MAAM,6DAA6D,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uDAAuD,CAAC;AAE3F,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,+BAA+B,EAAE,gCAAgC,CAAC,CAAC;AAW1G,2DAA2D;AAC3D,MAAM,CAAC,OAAO,OAAO,2BAA4B,SAAQ,2BAAmD;IACnG,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,CAAU,KAAK,GAAG;QAC7B,GAAG,2BAA2B,CAAC,KAAK;QACpC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC;YACpB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,uBAAuB,CAAC;YACrD,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,IAAI;SACb,CAAC;KACH,CAAC;IAEiB,WAAW,GAAG,kBAAkB,CAAC;IACjC,SAAS,GAAG,IAAI,CAAC;IACjB,eAAe,GAAG,0BAA0B,CAAC;IAC7C,aAAa,GAAG,KAAK,CAAC;IACzC,yFAAyF;IACzF,8FAA8F;IAC3E,IAAI,GACrB,2IAA2I,CAAC;IAEvI,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,UAAU,CAAC;YACrB,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC;YAC9B,UAAU,EAAE,KAAK,CAAC,aAAa,CAAC;YAChC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;YACxB,YAAY,EAAE,KAAK,CAAC,eAAe,CAAC;YACpC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC;YAC3B,aAAa,EAAE,KAAK,CAAC,gBAAgB,CAAC;SACvC,CAAC,CAAC;IACL,CAAC;IAES,WAAW,CAAC,MAA8B;QAClD,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC;YAClC,IAAI,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,IAAI,4BAA4B,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAOtE,CAAC;YACF,OAAO;gBACL,GAAG,GAAG;gBACN,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI;gBAC7B,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI;gBACrD,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW;aAC3B,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,6CAA6C,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACtE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAES,KAAK,CAAC,gBAAgB,CAC9B,OAAiC,EACjC,cAA8B,EAC9B,IAAgB;QAEhB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,uBAAuB,EAAE,CAAC;gBACnC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,cAAc,QAAQ,CAAC,IAAI,wEAAwE,CAAC,CAAC;YAC9G,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACrD,EAAE,EAAE,EAAE;gBACN,cAAc,EAAE,kBAAkB;aACnC,CAAC,CAAC,CAAC;YACJ,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACtF,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YAC9E,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC1D,IAAI,CAAC,GAAG,CAAC,aAAa,cAAc,CAAC,MAAM,gBAAgB,CAAC,CAAC;YAC7D,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,EAAE,IAAI,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/E,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,qBAAqB,CAAC,CAAC;QAEhF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACnD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CACN,cAAc,UAAU,CAAC,MAAM,4EAA4E,CAC5G,CAAC;QACF,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,MAAM,OAAO,GAAyD,EAAE,CAAC;QACzE,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,qBAAqB;gBAAE,SAAS;YAExD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAA4B,CAAC;gBACjF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;gBACvB,IAAI,IAAI,CAAC,qBAAqB,IAAI,OAAO,IAAI,CAAC,qBAAqB,KAAK,QAAQ,EAAE,CAAC;oBAChF,IAAI,CAAC,qBAAiD,CAAC,cAAc,GAAG,kBAAkB,CAAC;gBAC9F,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC/E,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,IAAI,CAAC,+CAA+C,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;gBACxE,SAAS,EAAE,CAAC;YACd,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACvE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;oBACd,YAAY,EAAE,CAAC;gBACjB,CAAC;qBAAM,CAAC;oBACN,SAAS,EAAE,CAAC;oBACZ,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,IAAI,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACpF,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,aAAa,YAAY,kBAAkB,SAAS,SAAS,CAAC,CAAC;IAC1E,CAAC"}
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;