@salesforce/plugin-bre-to-cml 1.3.0 → 1.3.1
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 +117 -4
- package/lib/commands/cml/convert/surcharge-rules.d.ts +12 -0
- package/lib/commands/cml/convert/surcharge-rules.js +79 -5
- package/lib/commands/cml/convert/surcharge-rules.js.map +1 -1
- package/lib/commands/cml/convert/underwriting-rules.d.ts +41 -2
- package/lib/commands/cml/convert/underwriting-rules.js +141 -17
- package/lib/commands/cml/convert/underwriting-rules.js.map +1 -1
- package/lib/commands/insurance/import/record-updates.d.ts +66 -0
- package/lib/commands/insurance/import/record-updates.js +239 -0
- package/lib/commands/insurance/import/record-updates.js.map +1 -0
- package/lib/shared/cml-operators.d.ts +9 -1
- package/lib/shared/cml-operators.js +23 -5
- package/lib/shared/cml-operators.js.map +1 -1
- package/lib/shared/insurance/confirm-org-changes.d.ts +49 -0
- package/lib/shared/insurance/confirm-org-changes.js +43 -0
- package/lib/shared/insurance/confirm-org-changes.js.map +1 -0
- package/lib/shared/insurance/insurance-cml-merge.d.ts +100 -13
- package/lib/shared/insurance/insurance-cml-merge.js +355 -54
- package/lib/shared/insurance/insurance-cml-merge.js.map +1 -1
- package/lib/shared/insurance/insurance-context-tags.d.ts +124 -0
- package/lib/shared/insurance/insurance-context-tags.js +210 -0
- package/lib/shared/insurance/insurance-context-tags.js.map +1 -0
- package/lib/shared/insurance/insurance-org.d.ts +12 -0
- package/lib/shared/insurance/insurance-org.js +38 -2
- package/lib/shared/insurance/insurance-org.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-convert-command.d.ts +27 -0
- package/lib/shared/insurance/insurance-rule-convert-command.js +57 -4
- package/lib/shared/insurance/insurance-rule-convert-command.js.map +1 -1
- package/lib/shared/insurance/insurance-rule-generator.d.ts +101 -8
- package/lib/shared/insurance/insurance-rule-generator.js +293 -42
- package/lib/shared/insurance/insurance-rule-generator.js.map +1 -1
- package/lib/shared/insurance/models.d.ts +2 -1
- package/lib/shared/insurance/planned-change-mapper.d.ts +15 -0
- package/lib/shared/insurance/planned-change-mapper.js +51 -0
- package/lib/shared/insurance/planned-change-mapper.js.map +1 -0
- package/lib/shared/insurance/planned-change.d.ts +83 -0
- package/lib/shared/insurance/planned-change.js +74 -0
- package/lib/shared/insurance/planned-change.js.map +1 -0
- package/lib/shared/insurance/record-update-apply.d.ts +100 -0
- package/lib/shared/insurance/record-update-apply.js +303 -0
- package/lib/shared/insurance/record-update-apply.js.map +1 -0
- package/lib/shared/insurance/record-update-plan.d.ts +104 -0
- package/lib/shared/insurance/record-update-plan.js +397 -0
- package/lib/shared/insurance/record-update-plan.js.map +1 -0
- package/messages/cml.convert.surcharge-rules.md +3 -1
- package/messages/cml.convert.underwriting-rules.md +1 -1
- package/messages/insurance.import.record-updates.md +176 -0
- package/oclif.lock +25 -25
- package/oclif.manifest.json +95 -3
- package/package.json +16 -8
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "what is about to change in the org" preview for the insurance import commands
|
|
3
|
+
* (export/review/import design, work item W-23654540 §4): an org-mutating import builds the
|
|
4
|
+
* complete PlannedChange[] from read-only operations, renders it, and only then prompts — so an
|
|
5
|
+
* operator always sees the exact field-level changes before anything is written.
|
|
6
|
+
*
|
|
7
|
+
* Used today by `insurance import record-updates`. Nothing here is specific to record updates, so
|
|
8
|
+
* `cml import as-expression-set` can render its own plan through it when its gate lands (§7 step 12).
|
|
9
|
+
*/
|
|
10
|
+
/** Column values are rendered verbatim, so keep the vocabulary closed and stable. */
|
|
11
|
+
export type PlannedChangeOperation = 'Create' | 'Update' | 'Reuse' | 'Skip (already current)' | 'Create (UNRESOLVED FK)';
|
|
12
|
+
export type PlannedChange = {
|
|
13
|
+
operation: PlannedChangeOperation;
|
|
14
|
+
/** sObject type, e.g. 'ProductSurcharge'. */
|
|
15
|
+
object: string;
|
|
16
|
+
/** Record id, or `NO_ID` for a create (the id does not exist yet). */
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
field: string;
|
|
20
|
+
/** `old → new`, truncated (and, for JSON blobs, reduced to the mutated fields) for display. */
|
|
21
|
+
change: string;
|
|
22
|
+
/**
|
|
23
|
+
* Untruncated current org value, from the same fresh re-read `change` was built from. `change`
|
|
24
|
+
* is a display string, so `--json` consumers need the real values to diff or audit against.
|
|
25
|
+
* Absent for operations that have no current value, e.g. a create.
|
|
26
|
+
*/
|
|
27
|
+
currentValue?: string | null;
|
|
28
|
+
/** Untruncated value that will be written. Absent when the operation writes no single value. */
|
|
29
|
+
newValue?: string;
|
|
30
|
+
};
|
|
31
|
+
/** Placeholder shown in the Id column for records that do not exist yet. */
|
|
32
|
+
export declare const NO_ID = "\u2014";
|
|
33
|
+
/** Placeholder shown when a field currently has no value in the org. */
|
|
34
|
+
export declare const NO_VALUE = "(none)";
|
|
35
|
+
/**
|
|
36
|
+
* Minimal surface the renderer needs, so this module is unit-testable without oclif. Declared with
|
|
37
|
+
* method syntax so an `SfCommand` (whose `table` is generic) satisfies it.
|
|
38
|
+
*/
|
|
39
|
+
export type PlannedChangeRenderer = {
|
|
40
|
+
styledHeader(text: string): void;
|
|
41
|
+
table(options: {
|
|
42
|
+
data: Array<Record<string, unknown>>;
|
|
43
|
+
}): void;
|
|
44
|
+
log(message: string): void;
|
|
45
|
+
warn(message: string): unknown;
|
|
46
|
+
};
|
|
47
|
+
export declare function truncateCell(value: string, max?: number): string;
|
|
48
|
+
/** Renders a single field-level change as `old → new`, with both sides truncated. */
|
|
49
|
+
export declare function formatChange(currentValue: string | null | undefined, newValue: string): string;
|
|
50
|
+
export type PlannedChangeCounts = {
|
|
51
|
+
creates: number;
|
|
52
|
+
updates: number;
|
|
53
|
+
reuses: number;
|
|
54
|
+
skips: number;
|
|
55
|
+
};
|
|
56
|
+
export declare function countPlannedChanges(changes: PlannedChange[]): PlannedChangeCounts;
|
|
57
|
+
export type PlannedChangeText = {
|
|
58
|
+
/** Header line, e.g. `These changes will be applied to <username>`. */
|
|
59
|
+
header: string;
|
|
60
|
+
/** One-line count summary. */
|
|
61
|
+
summary: string;
|
|
62
|
+
/**
|
|
63
|
+
* The explicit non-transactional notice (§4). Changes are applied in order and are NOT rolled
|
|
64
|
+
* back, so a mid-apply failure can leave the org partially migrated — the operator must be told
|
|
65
|
+
* this before confirming, not after a failure.
|
|
66
|
+
*
|
|
67
|
+
* Omit on a path that will not write (e.g. `--dry-run`). A caution about a hazard that cannot
|
|
68
|
+
* occur is noise, and it dilutes the warnings that describe real ones.
|
|
69
|
+
*/
|
|
70
|
+
notTransactional?: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Renders the preview: styled header, one row per field-level change, the count summary, and — only
|
|
74
|
+
* when the caller is on a path that will write — the non-transactional notice.
|
|
75
|
+
*
|
|
76
|
+
* The summary goes to `log`, not `warn`, on purpose. `warn` is not merely a louder `log`: it writes
|
|
77
|
+
* to stderr, so under `--dry-run` (whose entire output IS the summary) redirecting stdout to a file
|
|
78
|
+
* drops it; and `SfCommand.warn` pushes unconditionally to the `warnings` array, so emitting
|
|
79
|
+
* boilerplate through it makes every successful `--json` run return a non-empty `warnings`, which
|
|
80
|
+
* both trips consumers that treat `warnings.length > 0` as a problem signal and buries the genuine
|
|
81
|
+
* warnings (ProductCode drift, per-record save failures) among the routine ones.
|
|
82
|
+
*/
|
|
83
|
+
export declare function renderPlannedChanges(renderer: PlannedChangeRenderer, changes: PlannedChange[], text: PlannedChangeText): void;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/** Placeholder shown in the Id column for records that do not exist yet. */
|
|
17
|
+
export const NO_ID = '—';
|
|
18
|
+
/** Placeholder shown when a field currently has no value in the org. */
|
|
19
|
+
export const NO_VALUE = '(none)';
|
|
20
|
+
/** Table cells are truncated so an opaque blob (e.g. DynamicRuleDefinition) stays readable. */
|
|
21
|
+
const MAX_CELL_LENGTH = 60;
|
|
22
|
+
export function truncateCell(value, max = MAX_CELL_LENGTH) {
|
|
23
|
+
if (value.length <= max)
|
|
24
|
+
return value;
|
|
25
|
+
// `max - 1` goes negative for max < 1, which would slice from the END of the string and return
|
|
26
|
+
// something longer than the budget. The ellipsis alone is the smallest honest rendering.
|
|
27
|
+
const keep = Math.max(max - 1, 0);
|
|
28
|
+
let head = value.slice(0, keep);
|
|
29
|
+
// Never emit the high half of a surrogate pair on its own — it renders as a replacement glyph.
|
|
30
|
+
if (/[\uD800-\uDBFF]$/.test(head))
|
|
31
|
+
head = head.slice(0, -1);
|
|
32
|
+
return `${head}…`;
|
|
33
|
+
}
|
|
34
|
+
/** Renders a single field-level change as `old → new`, with both sides truncated. */
|
|
35
|
+
export function formatChange(currentValue, newValue) {
|
|
36
|
+
const before = currentValue == null || currentValue === '' ? NO_VALUE : currentValue;
|
|
37
|
+
return `${truncateCell(before)} → ${truncateCell(newValue)}`;
|
|
38
|
+
}
|
|
39
|
+
export function countPlannedChanges(changes) {
|
|
40
|
+
return {
|
|
41
|
+
creates: changes.filter((c) => c.operation.startsWith('Create')).length,
|
|
42
|
+
updates: changes.filter((c) => c.operation === 'Update').length,
|
|
43
|
+
reuses: changes.filter((c) => c.operation === 'Reuse').length,
|
|
44
|
+
skips: changes.filter((c) => c.operation === 'Skip (already current)').length,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Renders the preview: styled header, one row per field-level change, the count summary, and — only
|
|
49
|
+
* when the caller is on a path that will write — the non-transactional notice.
|
|
50
|
+
*
|
|
51
|
+
* The summary goes to `log`, not `warn`, on purpose. `warn` is not merely a louder `log`: it writes
|
|
52
|
+
* to stderr, so under `--dry-run` (whose entire output IS the summary) redirecting stdout to a file
|
|
53
|
+
* drops it; and `SfCommand.warn` pushes unconditionally to the `warnings` array, so emitting
|
|
54
|
+
* boilerplate through it makes every successful `--json` run return a non-empty `warnings`, which
|
|
55
|
+
* both trips consumers that treat `warnings.length > 0` as a problem signal and buries the genuine
|
|
56
|
+
* warnings (ProductCode drift, per-record save failures) among the routine ones.
|
|
57
|
+
*/
|
|
58
|
+
export function renderPlannedChanges(renderer, changes, text) {
|
|
59
|
+
renderer.styledHeader(text.header);
|
|
60
|
+
renderer.table({
|
|
61
|
+
data: changes.map((c) => ({
|
|
62
|
+
Operation: c.operation,
|
|
63
|
+
Object: c.object,
|
|
64
|
+
Id: c.id,
|
|
65
|
+
Name: c.name,
|
|
66
|
+
Field: c.field,
|
|
67
|
+
Change: c.change,
|
|
68
|
+
})),
|
|
69
|
+
});
|
|
70
|
+
renderer.log(text.summary);
|
|
71
|
+
if (text.notTransactional !== undefined)
|
|
72
|
+
renderer.warn(text.notTransactional);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=planned-change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planned-change.js","sourceRoot":"","sources":["../../../src/shared/insurance/planned-change.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwCH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,CAAC;AAEzB,wEAAwE;AACxE,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAEjC,+FAA+F;AAC/F,MAAM,eAAe,GAAG,EAAE,CAAC;AAa3B,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,GAAG,GAAG,eAAe;IAC/D,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACtC,+FAA+F;IAC/F,yFAAyF;IACzF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAClC,IAAI,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAChC,+FAA+F;IAC/F,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,OAAO,GAAG,IAAI,GAAG,CAAC;AACpB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,YAAuC,EAAE,QAAgB;IACpF,MAAM,MAAM,GAAG,YAAY,IAAI,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;IACrF,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC/D,CAAC;AASD,MAAM,UAAU,mBAAmB,CAAC,OAAwB;IAC1D,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;QACvE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,MAAM;QAC/D,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,OAAO,CAAC,CAAC,MAAM;QAC7D,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,wBAAwB,CAAC,CAAC,MAAM;KAC9E,CAAC;AACJ,CAAC;AAkBD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAA+B,EAC/B,OAAwB,EACxB,IAAuB;IAEvB,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC;QACb,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxB,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,MAAM,EAAE,CAAC,CAAC,MAAM;SACjB,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAChF,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Org-facing half of `sf insurance import record-updates`
|
|
3
|
+
* (export/review/import design, work item W-23654540 §5).
|
|
4
|
+
*
|
|
5
|
+
* Split into a read phase and an apply phase on purpose: the command must be able to compute and
|
|
6
|
+
* render every planned change using read-only operations, then prompt, and only then write. Keeping
|
|
7
|
+
* both phases here (rather than in the oclif command) makes them unit-testable against a stub
|
|
8
|
+
* Connection.
|
|
9
|
+
*/
|
|
10
|
+
import { Connection } from '@salesforce/core';
|
|
11
|
+
import { RecordUpdate, RecordUpdateField, RecordUpdatePlan } from './models.js';
|
|
12
|
+
/**
|
|
13
|
+
* Why the read phase refused a record. These are not shades of one problem: they need different
|
|
14
|
+
* things from the operator, so the kind travels with the message and the command selects the
|
|
15
|
+
* remediation from it.
|
|
16
|
+
*
|
|
17
|
+
* `recordIdentityMismatch`: the file names a record the org disagrees with (a missing record, or a
|
|
18
|
+
* Name/blob-apiName that does not match). Correcting the file, or regenerating it, is the fix.
|
|
19
|
+
*
|
|
20
|
+
* `unreadableOrgBlob`: the value stored in the org is not parseable JSON. Nothing in the file is
|
|
21
|
+
* wrong and regenerating cannot help — convert would fail to parse the same bytes.
|
|
22
|
+
*
|
|
23
|
+
* `identityCheckUnavailable`: the re-read did not return a field the guard needs. That is a defect
|
|
24
|
+
* in this plugin; no edit to the file can affect it.
|
|
25
|
+
*/
|
|
26
|
+
export type IdentityProblemKind = 'recordIdentityMismatch' | 'unreadableOrgBlob' | 'identityCheckUnavailable';
|
|
27
|
+
/** A blocking problem found before any write, tagged with the kind of remediation it needs. */
|
|
28
|
+
export type IdentityProblem = {
|
|
29
|
+
kind: IdentityProblemKind;
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
/** One field-level change, joined to the org's current value from a fresh re-read. */
|
|
33
|
+
export type PlannedRecordChange = {
|
|
34
|
+
update: RecordUpdate;
|
|
35
|
+
field: RecordUpdateField;
|
|
36
|
+
/** Current org value, from this invocation's re-read. Null when the field is empty. */
|
|
37
|
+
currentValue: string | null;
|
|
38
|
+
/** True when the org already holds the desired value, so the apply skips this field. */
|
|
39
|
+
alreadyCurrent: boolean;
|
|
40
|
+
};
|
|
41
|
+
export type RecordUpdateReadPhase = {
|
|
42
|
+
changes: PlannedRecordChange[];
|
|
43
|
+
/**
|
|
44
|
+
* Blocking problems found before any write: a record that no longer exists, or whose Name (or UW
|
|
45
|
+
* blob apiName) disagrees with the file. Refusing here is what stops an edited id from silently
|
|
46
|
+
* retargeting a valid-but-wrong record.
|
|
47
|
+
*/
|
|
48
|
+
identityErrors: IdentityProblem[];
|
|
49
|
+
/** Non-blocking notices, e.g. ProductCode drift that would desync the platform-generated RuleKey. */
|
|
50
|
+
advisories: string[];
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Re-reads current org state for every record in the plan and returns the resolved field-level
|
|
54
|
+
* changes. Read-only: safe to run under `--dry-run` and before the confirmation prompt.
|
|
55
|
+
*
|
|
56
|
+
* Only regex-validated ids are ever interpolated into SOQL (via `quoteSoqlIdList`); free text from
|
|
57
|
+
* the file — `name`, `value` — never is.
|
|
58
|
+
*/
|
|
59
|
+
export declare function planRecordUpdates(conn: Connection, plan: RecordUpdatePlan): Promise<RecordUpdateReadPhase>;
|
|
60
|
+
export type RecordUpdateFailure = {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
errors: string[];
|
|
64
|
+
/**
|
|
65
|
+
* True when the request failed *after* it was sent, so whether the org applied this record is
|
|
66
|
+
* genuinely unknown — a timeout or a 500 can arrive after the server committed. Distinct from a
|
|
67
|
+
* rejection, where the org is known not to have written it.
|
|
68
|
+
*/
|
|
69
|
+
outcomeUnknown: boolean;
|
|
70
|
+
};
|
|
71
|
+
export type ApplyRecordUpdatesResult = {
|
|
72
|
+
/** Records (not fields) successfully written. */
|
|
73
|
+
applied: number;
|
|
74
|
+
failures: RecordUpdateFailure[];
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Writes the non-skipped changes, one `update` call per sObject type, groups issued in
|
|
78
|
+
* `SOBJECT_APPLY_ORDER`. There is no transaction: successful writes are not rolled back when a
|
|
79
|
+
* later one fails, which is why failures are collected and reported rather than thrown from here.
|
|
80
|
+
*/
|
|
81
|
+
export declare function applyRecordUpdates(conn: Connection, changes: PlannedRecordChange[]): Promise<ApplyRecordUpdatesResult>;
|
|
82
|
+
export type SurchargeVerification = {
|
|
83
|
+
Id: string;
|
|
84
|
+
Name: string;
|
|
85
|
+
RuleEngineType: string | null;
|
|
86
|
+
RuleKey: string | null;
|
|
87
|
+
RuleApiName: string | null;
|
|
88
|
+
};
|
|
89
|
+
export type SurchargeVerificationResult = {
|
|
90
|
+
records: SurchargeVerification[];
|
|
91
|
+
/** Loud but non-fatal: the flip succeeded, yet the surcharge will not fire. */
|
|
92
|
+
warnings: string[];
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Post-flip verification (§5.4). `expectedRuleKey` is never written — the platform regenerates
|
|
96
|
+
* `ProductSurcharge.RuleKey` when `RuleEngineType` flips — so the only way to know the surcharge
|
|
97
|
+
* will actually fire is to read the regenerated key back and compare it to the key the CML rule was
|
|
98
|
+
* emitted under.
|
|
99
|
+
*/
|
|
100
|
+
export declare function verifySurchargeUpdates(conn: Connection, updates: RecordUpdate[]): Promise<SurchargeVerificationResult>;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { fetchProductCodes, quoteSoqlIdList } from './insurance-org.js';
|
|
17
|
+
import { splitProductPath } from './insurance-cml-merge.js';
|
|
18
|
+
import { JSON_BLOB_FIELD, SOBJECT_APPLY_ORDER, dynamicRuleDefinitionApiName, isAlreadyCurrent, } from './record-update-plan.js';
|
|
19
|
+
/**
|
|
20
|
+
* Salesforce rejects a `/composite/sobjects` request carrying more than 200 records, and jsforce
|
|
21
|
+
* only splits the batch itself when `allowRecursive` is set (which `sobject().update()` does not
|
|
22
|
+
* pass). Without chunking here, a 201-record plan fails wholesale and is then misreported as a
|
|
23
|
+
* partial migration.
|
|
24
|
+
*/
|
|
25
|
+
const MAX_DML_CHUNK = 200;
|
|
26
|
+
/** Keeps a generated `Id IN (...)` clause comfortably inside the SOQL statement length limit. */
|
|
27
|
+
const MAX_SOQL_ID_CHUNK = 500;
|
|
28
|
+
function chunk(items, size) {
|
|
29
|
+
return Array.from({ length: Math.ceil(items.length / size) }, (_, i) => items.slice(i * size, i * size + size));
|
|
30
|
+
}
|
|
31
|
+
const asString = (value) => value == null ? null : typeof value === 'string' ? value : String(value);
|
|
32
|
+
/** Groups updates by sObject, in the order the apply must issue them. */
|
|
33
|
+
function groupBySobject(updates) {
|
|
34
|
+
return SOBJECT_APPLY_ORDER.map((sobject) => [sobject, updates.filter((u) => u.sobject === sobject)]).filter(([, group]) => group.length > 0);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Re-reads current org state for every record in the plan and returns the resolved field-level
|
|
38
|
+
* changes. Read-only: safe to run under `--dry-run` and before the confirmation prompt.
|
|
39
|
+
*
|
|
40
|
+
* Only regex-validated ids are ever interpolated into SOQL (via `quoteSoqlIdList`); free text from
|
|
41
|
+
* the file — `name`, `value` — never is.
|
|
42
|
+
*/
|
|
43
|
+
export async function planRecordUpdates(conn, plan) {
|
|
44
|
+
const changes = [];
|
|
45
|
+
const identityErrors = [];
|
|
46
|
+
const advisories = [];
|
|
47
|
+
for (const [sobject, updates] of groupBySobject(plan.updates)) {
|
|
48
|
+
const fieldNames = [...new Set(updates.flatMap((u) => u.fields.map((f) => f.field)))];
|
|
49
|
+
// ProductPath backs the advisory ProductCode drift check below; it is read, never written.
|
|
50
|
+
const extraFields = sobject === 'ProductSurcharge' ? ['ProductPath'] : [];
|
|
51
|
+
// eslint-disable-next-line no-await-in-loop
|
|
52
|
+
const current = await queryCurrentRecords(conn, sobject, updates, [...fieldNames, ...extraFields]);
|
|
53
|
+
for (const update of updates) {
|
|
54
|
+
const record = current.get(update.id);
|
|
55
|
+
if (!record) {
|
|
56
|
+
identityErrors.push({
|
|
57
|
+
kind: 'recordIdentityMismatch',
|
|
58
|
+
message: `${update.sobject} ${update.id} (${update.name}) was not found in the org`,
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const orgName = asString(record.Name);
|
|
63
|
+
if (orgName !== update.name) {
|
|
64
|
+
identityErrors.push({
|
|
65
|
+
kind: 'recordIdentityMismatch',
|
|
66
|
+
message: `${update.sobject} ${update.id} is named '${orgName ?? '<none>'}' in the org but the file expected '${update.name}' — refusing to write to a possibly-wrong record`,
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const blobCheck = findBlobApiNameMismatch(update, record);
|
|
71
|
+
if (blobCheck?.error) {
|
|
72
|
+
identityErrors.push(blobCheck.error);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (blobCheck?.advisory)
|
|
76
|
+
advisories.push(blobCheck.advisory);
|
|
77
|
+
for (const field of update.fields) {
|
|
78
|
+
const currentValue = asString(record[field.field]);
|
|
79
|
+
changes.push({
|
|
80
|
+
update,
|
|
81
|
+
field,
|
|
82
|
+
currentValue,
|
|
83
|
+
alreadyCurrent: isAlreadyCurrent(field.field, currentValue, field.value),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (sobject === 'ProductSurcharge') {
|
|
88
|
+
// eslint-disable-next-line no-await-in-loop
|
|
89
|
+
advisories.push(...(await findProductCodeDrift(conn, updates, current)));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { changes, identityErrors, advisories };
|
|
93
|
+
}
|
|
94
|
+
async function queryCurrentRecords(conn, sobject, updates, fields) {
|
|
95
|
+
const byId = new Map();
|
|
96
|
+
const selected = ['Id', 'Name', ...fields.filter((f) => f !== 'Id' && f !== 'Name')];
|
|
97
|
+
for (const batch of chunk(updates, MAX_SOQL_ID_CHUNK)) {
|
|
98
|
+
const idList = quoteSoqlIdList(batch.map((u) => u.id));
|
|
99
|
+
if (!idList)
|
|
100
|
+
continue;
|
|
101
|
+
// eslint-disable-next-line no-await-in-loop
|
|
102
|
+
const result = await conn.query(`SELECT ${selected.join(', ')} FROM ${sobject} WHERE Id IN (${idList})`);
|
|
103
|
+
for (const record of result.records) {
|
|
104
|
+
const id = asString(record.Id);
|
|
105
|
+
if (id)
|
|
106
|
+
byId.set(id, record);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return byId;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* For a `DynamicRuleDefinition` rewrite, the org blob's own `apiName` is a second identity key
|
|
113
|
+
* alongside Name.
|
|
114
|
+
*
|
|
115
|
+
* The guard fails closed. Previously anything it could not read — a blob the re-read did not
|
|
116
|
+
* select, or one it could not parse — returned "no name" and was indistinguishable from "the names
|
|
117
|
+
* agree", so the second identity key silently stopped protecting the operator. The one case that
|
|
118
|
+
* is genuinely benign is a readable blob that simply carries no `apiName` (convert takes that
|
|
119
|
+
* value from the record's ApiName field, which the blob need not repeat); that is surfaced as an
|
|
120
|
+
* advisory rather than blocking a legitimate migration.
|
|
121
|
+
*/
|
|
122
|
+
function findBlobApiNameMismatch(update, record) {
|
|
123
|
+
if (update.sobject !== 'UnderwritingRule' || !update.apiName)
|
|
124
|
+
return undefined;
|
|
125
|
+
const refusing = 'refusing to write to a possibly-wrong record';
|
|
126
|
+
const prefix = `UnderwritingRule ${update.id} (${update.name})`;
|
|
127
|
+
if (!(JSON_BLOB_FIELD in record)) {
|
|
128
|
+
return {
|
|
129
|
+
error: {
|
|
130
|
+
kind: 'identityCheckUnavailable',
|
|
131
|
+
message: `${prefix}: the org re-read did not return ${JSON_BLOB_FIELD}, so the apiName identity check could not run — ${refusing}`,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const orgApiName = dynamicRuleDefinitionApiName(asString(record[JSON_BLOB_FIELD]));
|
|
136
|
+
if ('failure' in orgApiName) {
|
|
137
|
+
if (orgApiName.failure === 'unparseable') {
|
|
138
|
+
return {
|
|
139
|
+
error: {
|
|
140
|
+
kind: 'unreadableOrgBlob',
|
|
141
|
+
message: `${prefix}: the org's ${JSON_BLOB_FIELD} is not readable JSON, so the apiName identity check could not run — ${refusing}`,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
advisory: `${prefix}: the org's ${JSON_BLOB_FIELD} carries no apiName, so only the record Name could be verified`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (orgApiName.apiName === update.apiName)
|
|
150
|
+
return undefined;
|
|
151
|
+
return {
|
|
152
|
+
error: {
|
|
153
|
+
kind: 'recordIdentityMismatch',
|
|
154
|
+
message: `${prefix} has apiName '${orgApiName.apiName}' in the org but the file expected '${update.apiName}' — ${refusing}`,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Advisory ProductCode drift check. Convert computes the pathed rule key from each product's Code
|
|
160
|
+
* at convert time; the platform regenerates `ProductSurcharge.RuleKey` from the *current*
|
|
161
|
+
* ProductCode/ProductPath when `RuleEngineType` flips. If they drifted in between, the keys diverge
|
|
162
|
+
* silently and the surcharge never fires — so the divergence is surfaced, never blocked on.
|
|
163
|
+
*/
|
|
164
|
+
async function findProductCodeDrift(conn, updates, current) {
|
|
165
|
+
const withCodes = updates.filter((u) => u.productCodes?.length);
|
|
166
|
+
if (withCodes.length === 0)
|
|
167
|
+
return [];
|
|
168
|
+
const productIds = new Set();
|
|
169
|
+
for (const update of withCodes) {
|
|
170
|
+
for (const id of splitProductPath(asString(current.get(update.id)?.ProductPath) ?? ''))
|
|
171
|
+
productIds.add(id);
|
|
172
|
+
}
|
|
173
|
+
if (productIds.size === 0)
|
|
174
|
+
return [];
|
|
175
|
+
// A null ProductCode resolves to Name (then Id), which yields a key that cannot match the
|
|
176
|
+
// platform-generated RuleKey — so the comparison below can be spurious in either direction.
|
|
177
|
+
// The hook exists precisely to make that observable; ignoring it hid the signal.
|
|
178
|
+
const fellBack = new Set();
|
|
179
|
+
const idToCode = await fetchProductCodes(conn, productIds, { onFallback: (id) => fellBack.add(id) });
|
|
180
|
+
const advisories = [];
|
|
181
|
+
if (fellBack.size > 0) {
|
|
182
|
+
advisories.push(`products ${[...fellBack].join(', ')} have no ProductCode; their code was derived from Name or Id, so the ` +
|
|
183
|
+
'ProductCode drift check below cannot be relied on for surcharges on those products');
|
|
184
|
+
}
|
|
185
|
+
for (const update of withCodes) {
|
|
186
|
+
const orgPath = splitProductPath(asString(current.get(update.id)?.ProductPath) ?? '');
|
|
187
|
+
const orgCodes = orgPath.map((id) => idToCode.get(id) ?? id);
|
|
188
|
+
const fileCodes = update.productCodes ?? [];
|
|
189
|
+
if (orgCodes.join('/') !== fileCodes.join('/')) {
|
|
190
|
+
advisories.push(`${update.name} (${update.id}): ProductCodes drifted since convert — file has '${fileCodes.join('/')}', org now resolves '${orgCodes.join('/')}'; the platform-generated RuleKey may not match the converted CML rule key`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return advisories;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Writes the non-skipped changes, one `update` call per sObject type, groups issued in
|
|
197
|
+
* `SOBJECT_APPLY_ORDER`. There is no transaction: successful writes are not rolled back when a
|
|
198
|
+
* later one fails, which is why failures are collected and reported rather than thrown from here.
|
|
199
|
+
*/
|
|
200
|
+
export async function applyRecordUpdates(conn, changes) {
|
|
201
|
+
const pending = changes.filter((c) => !c.alreadyCurrent);
|
|
202
|
+
let applied = 0;
|
|
203
|
+
const failures = [];
|
|
204
|
+
for (const [sobject, updates] of groupBySobject([...new Set(pending.map((c) => c.update))])) {
|
|
205
|
+
const payloads = updates.map((update) => {
|
|
206
|
+
const fields = pending.filter((c) => c.update === update).map((c) => c.field);
|
|
207
|
+
return { Id: update.id, ...Object.fromEntries(fields.map((f) => [f.field, f.value])) };
|
|
208
|
+
});
|
|
209
|
+
// eslint-disable-next-line no-await-in-loop
|
|
210
|
+
const results = await saveAll(conn, sobject, payloads);
|
|
211
|
+
// Indexed by payload, never by result: a result list that does not line up with what was sent
|
|
212
|
+
// must not throw here, because by this point the writes have already landed and the operator
|
|
213
|
+
// would lose the failure report for an org that is now partially migrated.
|
|
214
|
+
updates.forEach((update, i) => {
|
|
215
|
+
const result = results[i];
|
|
216
|
+
if (result?.success)
|
|
217
|
+
applied += 1;
|
|
218
|
+
else {
|
|
219
|
+
failures.push({
|
|
220
|
+
id: update.id,
|
|
221
|
+
name: update.name,
|
|
222
|
+
errors: result?.errors ?? [UNACCOUNTED_RESULT],
|
|
223
|
+
outcomeUnknown: result?.outcomeUnknown ?? true,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return { applied, failures };
|
|
229
|
+
}
|
|
230
|
+
/** Used when the org returned fewer results than the records that were sent. */
|
|
231
|
+
const UNACCOUNTED_RESULT = 'the org returned no result for this record, so whether it was written is unknown';
|
|
232
|
+
/**
|
|
233
|
+
* Issues the update and normalizes jsforce's per-record results. A thrown error (network, invalid
|
|
234
|
+
* session, whole-request rejection) is attributed to every record in the chunk, and marked
|
|
235
|
+
* `outcomeUnknown`: the call site cannot tell a rejected request from a timeout or a 500 that
|
|
236
|
+
* arrived after the server had already committed, and claiming the records were not written is
|
|
237
|
+
* precisely wrong in the second case.
|
|
238
|
+
*/
|
|
239
|
+
async function saveAll(conn, sobject, payloads) {
|
|
240
|
+
const results = [];
|
|
241
|
+
for (const batch of chunk(payloads, MAX_DML_CHUNK)) {
|
|
242
|
+
// Sequential, not parallel: a failing chunk must not race writes the operator may want to stop.
|
|
243
|
+
// eslint-disable-next-line no-await-in-loop
|
|
244
|
+
results.push(...(await saveChunk(conn, sobject, batch)));
|
|
245
|
+
}
|
|
246
|
+
return results;
|
|
247
|
+
}
|
|
248
|
+
async function saveChunk(conn, sobject, payloads) {
|
|
249
|
+
try {
|
|
250
|
+
const raw = await conn.sobject(sobject).update(payloads);
|
|
251
|
+
// A single-record update returns the bare result rather than a one-element array.
|
|
252
|
+
const results = Array.isArray(raw) ? raw : [raw];
|
|
253
|
+
return results.map((r) => ({
|
|
254
|
+
success: Boolean(r.success),
|
|
255
|
+
errors: normalizeErrors(r.errors),
|
|
256
|
+
outcomeUnknown: false,
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
return payloads.map(() => ({ success: false, errors: [e.message], outcomeUnknown: true }));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function normalizeErrors(errors) {
|
|
264
|
+
if (!errors)
|
|
265
|
+
return [];
|
|
266
|
+
const list = Array.isArray(errors) ? errors : [errors];
|
|
267
|
+
return list.map((e) => {
|
|
268
|
+
if (typeof e === 'string')
|
|
269
|
+
return e;
|
|
270
|
+
const message = e?.message;
|
|
271
|
+
return typeof message === 'string' ? message : JSON.stringify(e);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Post-flip verification (§5.4). `expectedRuleKey` is never written — the platform regenerates
|
|
276
|
+
* `ProductSurcharge.RuleKey` when `RuleEngineType` flips — so the only way to know the surcharge
|
|
277
|
+
* will actually fire is to read the regenerated key back and compare it to the key the CML rule was
|
|
278
|
+
* emitted under.
|
|
279
|
+
*/
|
|
280
|
+
export async function verifySurchargeUpdates(conn, updates) {
|
|
281
|
+
const surcharges = updates.filter((u) => u.sobject === 'ProductSurcharge');
|
|
282
|
+
const records = [];
|
|
283
|
+
for (const batch of chunk(surcharges, MAX_SOQL_ID_CHUNK)) {
|
|
284
|
+
const idList = quoteSoqlIdList(batch.map((u) => u.id));
|
|
285
|
+
if (!idList)
|
|
286
|
+
continue;
|
|
287
|
+
// eslint-disable-next-line no-await-in-loop
|
|
288
|
+
const result = await conn.query(`SELECT Id, Name, RuleEngineType, RuleKey, RuleApiName FROM ProductSurcharge WHERE Id IN (${idList})`);
|
|
289
|
+
records.push(...result.records);
|
|
290
|
+
}
|
|
291
|
+
const byId = new Map(records.map((r) => [r.Id, r]));
|
|
292
|
+
const warnings = [];
|
|
293
|
+
for (const update of surcharges) {
|
|
294
|
+
if (!update.expectedRuleKey)
|
|
295
|
+
continue;
|
|
296
|
+
const actual = byId.get(update.id)?.RuleKey ?? null;
|
|
297
|
+
if (actual !== update.expectedRuleKey) {
|
|
298
|
+
warnings.push(`surcharge ${update.name} RuleKey '${actual ?? '<none>'}' does not match the converted CML rule key '${update.expectedRuleKey}'; the surcharge will not fire`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return { records, warnings };
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=record-update-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-update-apply.js","sourceRoot":"","sources":["../../../src/shared/insurance/record-update-apply.ts"],"names":[],"mappings":"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"}
|