@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 +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 +291 -649
- package/oclif.manifest.json +5 -5
- package/package.json +10 -10
|
@@ -60,6 +60,68 @@ export function generateRuleKey(prefix, productCode, apiName, stageTransition) {
|
|
|
60
60
|
parts.push(sanitizeName(apiName));
|
|
61
61
|
return parts.join('__');
|
|
62
62
|
}
|
|
63
|
+
// Relational operators (<, <=, >, >=) ALWAYS interpolate their RHS unquoted, regardless of
|
|
64
|
+
// dataType. Equals/NotEquals interpolate unquoted whenever the resolved cmlDataType is NOT
|
|
65
|
+
// CML_DATA_TYPES.STRING (the shared emitter only quotes when dataType === 'string'). Either way an
|
|
66
|
+
// unquoted, attacker-influenced value (e.g. `2020) || evil(`) would reach the curated model
|
|
67
|
+
// verbatim, so every value on an unquoted-emission condition must be a bare safe literal.
|
|
68
|
+
const ALWAYS_UNQUOTED_OPERATORS = new Set([
|
|
69
|
+
'LessThan',
|
|
70
|
+
'LessThanOrEquals',
|
|
71
|
+
'GreaterThan',
|
|
72
|
+
'GreaterThanOrEquals',
|
|
73
|
+
]);
|
|
74
|
+
// Operators that emit a value via doubleQuotedIfNeeded — unquoted unless cmlDataType === STRING.
|
|
75
|
+
const VALUE_EQUALITY_OPERATORS = new Set(['Equals', 'NotEquals']);
|
|
76
|
+
function isSafeNumericLiteral(value) {
|
|
77
|
+
return /^-?\d+(\.\d+)?$/.test(value.trim());
|
|
78
|
+
}
|
|
79
|
+
function isSafeBooleanLiteral(value) {
|
|
80
|
+
return /^(true|false)$/.test(value.trim());
|
|
81
|
+
}
|
|
82
|
+
// Bare date / datetime literal: YYYY-MM-DD optionally followed by an ISO-8601 time component. No
|
|
83
|
+
// parens, operators, or whitespace that could break out of the unquoted slot.
|
|
84
|
+
function isSafeDateLiteral(value) {
|
|
85
|
+
return /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/.test(value.trim());
|
|
86
|
+
}
|
|
87
|
+
// A value is safe to emit unquoted only if it is a bare literal of the target CML type. Anything
|
|
88
|
+
// that isn't (including strings, which must never reach an unquoted slot) is rejected.
|
|
89
|
+
function isSafeUnquotedLiteral(value, cmlDataType) {
|
|
90
|
+
switch (cmlDataType) {
|
|
91
|
+
case CML_DATA_TYPES.INTEGER:
|
|
92
|
+
case CML_DATA_TYPES.DECIMAL:
|
|
93
|
+
return isSafeNumericLiteral(value);
|
|
94
|
+
case CML_DATA_TYPES.BOOLEAN:
|
|
95
|
+
return isSafeBooleanLiteral(value);
|
|
96
|
+
case CML_DATA_TYPES.DATE:
|
|
97
|
+
return isSafeDateLiteral(value);
|
|
98
|
+
default:
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// A value destined for a string-quoted slot must be safely single-line quotable. The shared
|
|
103
|
+
// escapeQuotes escapes ' and " but NOT backslash, so a value containing a backslash (e.g. ending
|
|
104
|
+
// in `\`, or a `\"` sequence) can escape its own closing quote and break out into raw CML. Reject
|
|
105
|
+
// any backslash, plus newlines that would split the single-line literal.
|
|
106
|
+
function isSafeQuotableString(value) {
|
|
107
|
+
return !/[\\\r\n]/.test(value);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether a product Name is safe to emit as a Type association's reference value
|
|
111
|
+
* (`$Product2ReferenceId`). That value travels two unescaped hops we do NOT own and cannot change:
|
|
112
|
+
* the naive comma-joined `_Associations.csv` column (a comma shifts every later column), and the
|
|
113
|
+
* common `cml import as-expression-set` resolver's single-quoted SOQL `WHERE Name IN ('<value>')`
|
|
114
|
+
* (a single quote breaks out of the literal; a backslash or newline corrupts the row).
|
|
115
|
+
* A Name failing this guard is dropped by the convert layer (it falls back to the ProductCode and
|
|
116
|
+
* warns) rather than silently producing a corrupt CSV / injecting SOQL. An empty / whitespace-only
|
|
117
|
+
* Name is also rejected — it can never match a real Product2 by name. Mirrors the reject-don't-escape
|
|
118
|
+
* stance of {@link isSafeQuotableString} for condition values.
|
|
119
|
+
*/
|
|
120
|
+
export function isSafeAssociationReferenceValue(value) {
|
|
121
|
+
if (value.trim().length === 0)
|
|
122
|
+
return false;
|
|
123
|
+
return !/[,'"\\\r\n]/.test(value);
|
|
124
|
+
}
|
|
63
125
|
function buildConditionExpression(condition) {
|
|
64
126
|
if (!isKnownOperator(condition.operator))
|
|
65
127
|
return null;
|
|
@@ -67,8 +129,24 @@ function buildConditionExpression(condition) {
|
|
|
67
129
|
if (operatorRequiresValues(op) && (!condition.values || condition.values.length === 0)) {
|
|
68
130
|
return null;
|
|
69
131
|
}
|
|
70
|
-
const
|
|
132
|
+
const values = condition.values ?? [];
|
|
71
133
|
const cmlDataType = dataTypeToCml(condition.dataType);
|
|
134
|
+
// Determine whether this operator/dataType pair emits its RHS UNQUOTED. Relational operators
|
|
135
|
+
// always do; Equals/NotEquals do whenever the cmlDataType is not STRING. On the unquoted path we
|
|
136
|
+
// require every value to be a bare safe literal; otherwise the value would land in the curated
|
|
137
|
+
// model verbatim and could inject CML or produce a type-unsafe comparison. A failing value drops
|
|
138
|
+
// the condition (the caller filters nulls), exactly as an unknown operator or missing value does.
|
|
139
|
+
const emitsUnquoted = ALWAYS_UNQUOTED_OPERATORS.has(op) || (VALUE_EQUALITY_OPERATORS.has(op) && cmlDataType !== CML_DATA_TYPES.STRING);
|
|
140
|
+
if (emitsUnquoted) {
|
|
141
|
+
if (!values.every((v) => isSafeUnquotedLiteral(v, cmlDataType))) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (!values.every(isSafeQuotableString)) {
|
|
146
|
+
// String-quoted path: reject values the shared escaper cannot safely contain (backslash, etc.).
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const attrName = sanitizeName(condition.attributeName ?? condition.contextTagName ?? 'unknown');
|
|
72
150
|
return convertToCmlExpression(attrName, op, condition.values, cmlDataType);
|
|
73
151
|
}
|
|
74
152
|
function buildCriteriaExpression(criteria) {
|
|
@@ -106,11 +184,83 @@ export function collectAttributes(ruleDefs) {
|
|
|
106
184
|
}
|
|
107
185
|
return attrs;
|
|
108
186
|
}
|
|
109
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Collects only the attributes that actually reach the emitted CML — i.e. those on conditions whose
|
|
189
|
+
* `buildConditionExpression` returned a non-null expression. Conditions dropped by the safe-literal
|
|
190
|
+
* guard, an unknown operator, or missing values do NOT contribute their attribute. This is the
|
|
191
|
+
* companion to `collectAttributes` for merge-mode attribute-presence warnings: warning about an
|
|
192
|
+
* attribute the declaration never emitted (because the guard dropped its condition) is spurious
|
|
193
|
+
* noise on exactly the inputs the guard sanitized. Unlike `collectAttributes`, names are returned
|
|
194
|
+
* sanitized to match how they appear in the emitted declaration.
|
|
195
|
+
*/
|
|
196
|
+
export function collectEmittedAttributes(ruleDefs) {
|
|
197
|
+
const attrs = new Set();
|
|
198
|
+
for (const { ruleDef } of ruleDefs) {
|
|
199
|
+
for (const criteria of ruleDef.ruleCriteria ?? []) {
|
|
200
|
+
for (const cond of criteria.conditions ?? []) {
|
|
201
|
+
if (buildConditionExpression(cond) === null)
|
|
202
|
+
continue;
|
|
203
|
+
const name = cond.attributeName ?? cond.contextTagName;
|
|
204
|
+
if (name)
|
|
205
|
+
attrs.add(sanitizeName(name));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return attrs;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Derives the real CML data type for each collected attribute from its condition `dataType`.
|
|
213
|
+
* When an attribute appears with more than one distinct CML type (a conflict), or its type is
|
|
214
|
+
* unknown, it falls back to STRING. Returned alongside (not replacing) the Set from
|
|
215
|
+
* collectAttributes, which merge mode still depends on.
|
|
216
|
+
*/
|
|
217
|
+
export function collectAttributeTypes(ruleDefs) {
|
|
218
|
+
const types = new Map();
|
|
219
|
+
const conflicting = new Set();
|
|
220
|
+
for (const { ruleDef } of ruleDefs) {
|
|
221
|
+
for (const criteria of ruleDef.ruleCriteria ?? []) {
|
|
222
|
+
for (const cond of criteria.conditions ?? []) {
|
|
223
|
+
const name = cond.attributeName ?? cond.contextTagName;
|
|
224
|
+
if (!name)
|
|
225
|
+
continue;
|
|
226
|
+
const cmlType = dataTypeToCml(cond.dataType);
|
|
227
|
+
const existing = types.get(name);
|
|
228
|
+
if (existing === undefined) {
|
|
229
|
+
types.set(name, cmlType);
|
|
230
|
+
}
|
|
231
|
+
else if (existing !== cmlType) {
|
|
232
|
+
conflicting.add(name);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const name of conflicting) {
|
|
238
|
+
types.set(name, CML_DATA_TYPES.STRING);
|
|
239
|
+
}
|
|
240
|
+
return types;
|
|
241
|
+
}
|
|
242
|
+
export function buildCmlModel(ruleDefs, productIdToCode, keyPrefix, constraintLabel,
|
|
243
|
+
// When set, eligibility is emitted as a CML `rule(decl, "<ruleType>", "<ruleKey>", "True")`
|
|
244
|
+
// statement instead of a `constraint NAME = (decl, "label");`. Surcharge passes
|
|
245
|
+
// 'InsuranceSurchargeRule'; underwriting leaves it undefined to keep the constraint form.
|
|
246
|
+
ruleType,
|
|
247
|
+
// Maps a root product id to its Product2 Name. The common `cml import as-expression-set` resolves
|
|
248
|
+
// each Type association's Product2 by NAME (`WHERE Name IN (<$Product2ReferenceId>)`), so the Name
|
|
249
|
+
// — not the ProductCode — must land in the association reference value or the importer silently
|
|
250
|
+
// drops the binding. Optional + last so the legacy (code-as-reference) call sites stay valid; the
|
|
251
|
+
// convert layer supplies only Names that passed isSafeAssociationReferenceValue, hence the
|
|
252
|
+
// unconditional ProductCode fallback below for any product missing a safe Name.
|
|
253
|
+
productIdToName) {
|
|
110
254
|
const cmlModel = new CmlModel();
|
|
111
255
|
const rulesByProduct = new Map();
|
|
112
256
|
for (const entry of ruleDefs) {
|
|
113
|
-
|
|
257
|
+
// Trim the root segment so leading/trailing whitespace can't split one product into two
|
|
258
|
+
// groups, and skip a rule with a blank ProductPath (it can't be nested under any product)
|
|
259
|
+
// instead of materializing an empty-named type. Mirrors the trimming in
|
|
260
|
+
// collectAllProductIds / collectRootProductIds.
|
|
261
|
+
const rootProductId = entry.record.ProductPath.split('/')[0]?.trim();
|
|
262
|
+
if (!rootProductId)
|
|
263
|
+
continue;
|
|
114
264
|
if (!rulesByProduct.has(rootProductId)) {
|
|
115
265
|
rulesByProduct.set(rootProductId, []);
|
|
116
266
|
}
|
|
@@ -122,14 +272,19 @@ export function buildCmlModel(ruleDefs, productIdToCode, keyPrefix, constraintLa
|
|
|
122
272
|
const typeName = sanitizeName(productCode);
|
|
123
273
|
const productType = new CmlType(typeName, undefined, undefined);
|
|
124
274
|
const attrs = collectAttributes(productRules);
|
|
275
|
+
const attrTypes = collectAttributeTypes(productRules);
|
|
125
276
|
for (const attrName of attrs) {
|
|
126
|
-
|
|
277
|
+
const cmlType = attrTypes.get(attrName) ?? CML_DATA_TYPES.STRING;
|
|
278
|
+
productType.addAttribute(new CmlAttribute(null, sanitizeName(attrName), cmlType));
|
|
127
279
|
}
|
|
128
280
|
for (const { record, ruleDef } of productRules) {
|
|
129
281
|
const apiName = ruleDef.apiName ?? record.Name;
|
|
130
282
|
const stageTransition = buildStageTransition(ruleDef.underwritingRuleGroup);
|
|
131
283
|
const ruleKey = generateRuleKey(keyPrefix, productCode, apiName, stageTransition);
|
|
132
|
-
const
|
|
284
|
+
const declaration = buildConstraintDeclaration(ruleDef);
|
|
285
|
+
const constraint = ruleType
|
|
286
|
+
? CmlConstraint.createRuleConstraint(declaration, ruleType, ruleKey, 'True')
|
|
287
|
+
: new CmlConstraint(CONSTRAINT_TYPES.CONSTRAINT, declaration, `"${constraintLabel}: ${record.Name}"`);
|
|
133
288
|
// Mirror generateRuleKey: include the stage transition so two rules that share an
|
|
134
289
|
// apiName under the same product (gated on different transitions) don't collide.
|
|
135
290
|
constraint.name = sanitizeName(stageTransition ? `${apiName}_${stageTransition}` : apiName);
|
|
@@ -137,7 +292,11 @@ export function buildCmlModel(ruleDefs, productIdToCode, keyPrefix, constraintLa
|
|
|
137
292
|
ruleKeyMapping.push({ recordId: record.Id, name: record.Name, ruleKey });
|
|
138
293
|
}
|
|
139
294
|
cmlModel.addType(productType);
|
|
140
|
-
|
|
295
|
+
// tag/type name stay ProductCode-derived (the CML doc keys off them); only the reference value
|
|
296
|
+
// — what the common importer resolves the Product2 by — becomes the Name. Fall back to the
|
|
297
|
+
// ProductCode when no safe Name was supplied (preserves legacy behavior for code-only callers).
|
|
298
|
+
const referenceValue = productIdToName?.get(rootProductId) ?? productCode;
|
|
299
|
+
cmlModel.addAssociation(new Association(null, typeName, ASSOCIATION_TYPES.TYPE, rootProductId, 'Product2', referenceValue));
|
|
141
300
|
}
|
|
142
301
|
return { cmlModel, ruleKeyMapping };
|
|
143
302
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insurance-rule-generator.js","sourceRoot":"","sources":["../../../src/shared/insurance/insurance-rule-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAUtG,SAAS,aAAa,CAAC,QAAiB;IACtC,MAAM,KAAK,GAA2B;QACpC,MAAM,EAAE,cAAc,CAAC,OAAO;QAC9B,OAAO,EAAE,cAAc,CAAC,OAAO;QAC/B,OAAO,EAAE,cAAc,CAAC,OAAO;QAC/B,QAAQ,EAAE,cAAc,CAAC,OAAO;QAChC,OAAO,EAAE,cAAc,CAAC,OAAO;QAC/B,IAAI,EAAE,cAAc,CAAC,IAAI;QACzB,QAAQ,EAAE,cAAc,CAAC,IAAI;KAC9B,CAAC;IACF,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK;SACT,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiC;IACpE,IAAI,CAAC,SAAS,EAAE,SAAS,IAAI,CAAC,SAAS,EAAE,OAAO;QAAE,OAAO,SAAS,CAAC;IACnE,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,WAAmB,EACnB,OAAe,EACf,eAAwB;IAExB,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC;IAClD,IAAI,eAAe;QAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,wBAAwB,CAAC,SAAwB;IACxD,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtD,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC;IAC9B,IAAI,sBAAsB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,
|
|
1
|
+
{"version":3,"file":"insurance-rule-generator.js","sourceRoot":"","sources":["../../../src/shared/insurance/insurance-rule-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,3 +42,28 @@ export type RuleKeyEntry = {
|
|
|
42
42
|
name: string;
|
|
43
43
|
ruleKey: string;
|
|
44
44
|
};
|
|
45
|
+
export type RecordUpdateField = {
|
|
46
|
+
/** sObject field API name to set, e.g. 'RuleEngineType' or 'DynamicRuleDefinition'. */
|
|
47
|
+
field: string;
|
|
48
|
+
/** Value to write. JSON-blob fields (DynamicRuleDefinition) are stringified verbatim. */
|
|
49
|
+
value: string;
|
|
50
|
+
};
|
|
51
|
+
export type RecordUpdate = {
|
|
52
|
+
sobject: 'UnderwritingRuleGroup' | 'UnderwritingRule' | 'ProductSurcharge';
|
|
53
|
+
/** 15/18-char Salesforce Id (re-validated on apply). */
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
apiName?: string;
|
|
57
|
+
fields: RecordUpdateField[];
|
|
58
|
+
expectedRuleKey?: string;
|
|
59
|
+
productCodes?: string[];
|
|
60
|
+
};
|
|
61
|
+
export type RecordUpdatePlan = {
|
|
62
|
+
schemaVersion: 1;
|
|
63
|
+
kind: 'underwriting-update' | 'surcharge-update';
|
|
64
|
+
/** Raw CML api name (matches the .cml / RuleKeyMapping for traceability). */
|
|
65
|
+
cmlApi: string;
|
|
66
|
+
/** ISO timestamp, advisory (drift-detection aid). */
|
|
67
|
+
generatedAt: string;
|
|
68
|
+
updates: RecordUpdate[];
|
|
69
|
+
};
|
|
@@ -8,4 +8,12 @@ Directory where output files will be written.
|
|
|
8
8
|
|
|
9
9
|
# flags.update-records.summary
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
REMOVED. Convert no longer writes to the org; passing this flag now errors. Convert instead emits a reviewable record-update file enumerating the org-record changes, which you apply to the org separately.
|
|
12
|
+
|
|
13
|
+
# error.updateRecordsRemoved
|
|
14
|
+
|
|
15
|
+
convert no longer writes to the org. It now always emits a `<cmlApi>_{Underwriting,Surcharge}Update.json` file for review.
|
|
16
|
+
|
|
17
|
+
# error.updateRecordsRemoved.actions
|
|
18
|
+
|
|
19
|
+
- Review the emitted `<cmlApi>_{Underwriting,Surcharge}Update.json` file and apply its org-record changes to the target org separately.
|
|
@@ -4,7 +4,7 @@ Converts BRE-based Product Surcharge dynamic rules to CML eligibility constraint
|
|
|
4
4
|
|
|
5
5
|
# description
|
|
6
6
|
|
|
7
|
-
Reads ProductSurcharge records from the org (or a JSON file), parses their RuleDefinition, and
|
|
7
|
+
Reads ProductSurcharge records from the org (or a JSON file), parses their RuleDefinition, and merges the generated surcharge rules into the org's existing curated ConstraintModel for the resolved CML API. Each surcharge rule is nested into its leaf product type with a platform-compatible pathed rule key (matching the RuleKey the platform auto-generates), so the rule actually fires for nested products instead of being silently dropped. The command is file-only and never writes to the org. It requires an existing CML model to merge into and outputs a .cml file with the full merged model, a header-only \_Associations.csv file, a \_RuleKeyMapping.json with the ProductSurcharge ID to RuleKey mapping, and a \_SurchargeUpdate.json file enumerating the org-record changes. Review the files, then apply the CML with `sf cml import as-expression-set` and apply the org-record changes enumerated in the \_SurchargeUpdate.json file to the org separately (in that order).
|
|
8
8
|
|
|
9
9
|
# examples
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@ Converts BRE-based Insurance Underwriting dynamic rules to CML eligibility const
|
|
|
4
4
|
|
|
5
5
|
# description
|
|
6
6
|
|
|
7
|
-
Reads UnderwritingRule records from the org (or a JSON file), parses their DynamicRuleDefinition, and generates CML constraints that evaluate underwriting eligibility. Each rule becomes a named constraint that returns true/false. The command outputs a .cml file with the constraint model, an \_Associations.csv file for ExpressionSetConstraintObj records,
|
|
7
|
+
Reads UnderwritingRule records from the org (or a JSON file), parses their DynamicRuleDefinition, and generates CML constraints that evaluate underwriting eligibility. Each rule becomes a named constraint that returns true/false. The command is file-only and never writes to the org: it outputs a .cml file with the constraint model, an \_Associations.csv file for ExpressionSetConstraintObj records, a \_RuleKeyMapping.json with the UnderwritingRule ID to RuleKey mapping, and a \_UnderwritingUpdate.json file enumerating the org-record changes. Review the files, then apply the CML with `sf cml import as-expression-set` and apply the org-record changes enumerated in the \_UnderwritingUpdate.json file to the org separately.
|
|
8
8
|
|
|
9
9
|
# examples
|
|
10
10
|
|