sf plugins install @salesforce/plugin-bre-to-cml@x.y.z- Please read our Code of Conduct
- Create a new issue before starting your project so that we can keep track of what you are trying to add/fix. That way, we can also offer suggestions or let you know if there is already an effort in progress.
- Fork this repository.
- Build the plugin locally
- Create a topic branch in your fork. Note, this step is recommended but technically not required if contributing using a fork.
- Edit the code in your fork.
- Write appropriate tests for your changes. Try to achieve at least 95% code coverage on any new code. No pull request will be accepted without unit tests.
- Sign CLA (see CLA below).
- Send us a pull request when you are done. We'll review your code, suggest any needed changes, and merge it in.
External contributors will be required to sign a Contributor's License Agreement. You can do so by going to https://cla.salesforce.com/sign-cla.
To build the plugin locally, make sure to have yarn installed and run the following commands:
# Clone the repository
git clone git@github.com:salesforcecli/plugin-bre-to-cml
# Install the dependencies and compile
yarn install
yarn buildTo use your plugin, run using the local ./bin/dev.js or ./bin/dev.cmd file.
# Run using local run file.
./bin/dev cmlThere should be no differences when running via the Salesforce CLI or using the local run file. However, it can be useful to link the plugin to do some additional testing or run your commands from anywhere on your machine.
# Link your plugin to the sf cli
sf plugins link .
# To verify
sf pluginsReview generated files before you import them or apply them to records. Everything this plugin emits — the
.cmlmodel, the association CSV, and the_SurchargeUpdate.json/_UnderwritingUpdate.jsonrecord-update plans — is the output of an automated conversion of your org's BRE data. Verifying that it expresses the logic you intend is your responsibility, not the plugin's. Two things are worth checking in particular. Conversion skips rules it cannot place and reports each one as aSKIPPEDwarning, so a generated model can be incomplete even when the command succeeds. And the import commands change the org:as-expression-setreplaces the active model, andrecord-updateswrites to records, where flippingRuleEngineTypetoConstraintEnginestops the platform evaluating that record's BRE rules. Preview with--dry-run, read the preview rather than confirming past it, and validate the migrated rules behave as expected in a sandbox before running any of this against production.
cml convert surcharge-rules always merges generated surcharge rules into an org's existing
curated ConstraintModel — nesting each rule into its leaf product type with a platform-compatible
pathed rule key — rather than building a fresh, flat model. It requires an existing CML model for the
resolved CML API.
Prerequisite: each surcharge's parent
Surcharge.Codemust be set (non-null). The platform derives the leaf segment of the auto-generatedProductSurcharge.RuleKeyfromSurcharge.Code, and a surcharge with a null Code cannot be flipped toConstraintEngine(the org raises a save-hook error). PopulateSurcharge.Codebefore converting.
After importing the merged CML with sf cml import as-expression-set, apply the emitted _SurchargeUpdate.json (flips RuleEngineType → ConstraintEngine) with sf insurance import record-updates:
$ sf insurance import record-updates --file data/SURCHARGE_CML_SurchargeUpdate.json --target-org myOrg --dry-run
$ sf insurance import record-updates --file data/SURCHARGE_CML_SurchargeUpdate.json --target-org myOrg
The first run previews every planned change and writes nothing. The second prints the same preview and
asks for confirmation before applying; pass --no-prompt to skip the prompt in automation. The command
re-reads each record before writing, verifies its Name against the file, skips records that already
match (so re-running is safe), and reads back the platform-regenerated RuleKey to confirm the
surcharge will actually fire. Import the CML before applying the record updates — the RuleKey is
regenerated against the active model.
The same command applies an _UnderwritingUpdate.json file emitted by sf cml convert underwriting-rules.
You can also apply the flip directly with the Salesforce CLI — run one sf data update record call per
surcharge listed in _SurchargeUpdate.json, using the id from that file to set RuleEngineType →
ConstraintEngine:
$ sf data update record \
--sobject ProductSurcharge \
--record-id <ID_FROM_SURCHARGEUPDATE_JSON> \
--values "RuleEngineType=ConstraintEngine" \
--target-org myOrg
The platform regenerates ProductSurcharge.RuleKey on save, which requires the parent Surcharge.Code
to be non-null (per the prerequisite above).
sf cml convert prod-cfg-rulessf cml convert surcharge-rulessf cml convert underwriting-rulessf cml import as-expression-setsf insurance import record-updates
Converts BRE based Standard Configurator rules represented as JSON to CML and saves it as a pair of CML and association files.
USAGE
$ sf cml convert prod-cfg-rules -o <value> -r <value> -c <value> [--json] [--flags-dir <value>] [--api-version <value>] [-d
<value>] [-x <value>] [-v <value>]
FLAGS
-c, --cml-api=<value> (required) Unique CML API Name to be created.
-d, --workspace-dir=<value> Directory where working files are located, exported rules JSON and where CMLs will
be created.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
-r, --pcr-file=<value> (required) Name of the JSON file that contain exported standard Product
Configuration Rules.
-v, --products-file=<value> Name of the JSON file that contain exported Products from PCM (if not present
products will be fetched automatically).
-x, --additional-products=<value> Comma-separated list of additional product IDs for which CML types should be
generated.
--api-version=<value> Override the api version used for api requests made by this command
GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
DESCRIPTION
Converts BRE based Standard Configurator rules represented as JSON to CML and saves it as a pair of CML and
association files.
Before you execute this command make sure to migrate you PCM or perform migration of rules within the same org.
Export BRE based Standard Configurator rules using sf data export bulk plugin and save results in a JSON file:
sf data export bulk -o breMigOrg --query "SELECT ApiName, ConfigurationRuleDefinition,Description, EffectiveFromDate,
EffectiveToDate, Id, IsDeleted, Name, ProcessScope, RuleSubType, RuleType, Sequence, Status FROM
ProductConfigurationRule WHERE RuleType = 'Configurator' AND ApiName = 'myTestBundle'" --output-file
./data/ProductConfigurationRules.json --result-format json --wait 10 --all-rows
This command executes following logic:
- Read SC Rules from the json file and build a list of `ConfiguratorRuleInput` to mimic the JSON structure of
`ConfigurationRuleDefinition`
- Group rules by non-intersecting `Product2` IDs (CMLs can’t share products)
- For each group:
- Query PCM for related products (for bundle rules) or root definitions (for others)
- Build an in-memory representation of the CML
- Apply logic to build constraints
- Serialize the in-memory CML to a blob for import as an Expression Set, save it in a cml-api.cml file.
- Create `ExpressionSetConstraintObj` association records pointing to the `cml-api` name and write them to
`cml-api_associations.csv` file.
- If multiple CML and association files are produced 1-N number will be appended to the names of files.
EXAMPLES
$ sf cml convert prod-cfg-rules --pcr-file data/ProductConfigurationRules.json --cml-api MY_TEST --workspace-dir data --target-org breMigOrg
Converts BRE-based Product Surcharge dynamic rules to CML eligibility constraints.
USAGE
$ sf cml convert surcharge-rules -o <value> [--json] [--flags-dir <value>] [--api-version <value>] [-c <value>] [-d <value>]
[--update-records] [-f <value>]
FLAGS
-c, --cml-api=<value> CML API Name. If omitted, auto-discovers an existing CML associated with the same root
products.
-d, --workspace-dir=<value> Directory where output files will be written.
-f, --surcharge-file=<value> Optional JSON file with pre-exported ProductSurcharge records. If omitted, records are
queried from the org.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
--api-version=<value> Override the api version used for api requests made by this command
--update-records 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.
GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
DESCRIPTION
Converts BRE-based Product Surcharge dynamic rules to CML eligibility constraints.
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 \_SurchargeUpdate.json file with `sf insurance import
record-updates` (in that order — the platform regenerates ProductSurcharge.RuleKey against the active model when
RuleEngineType flips).
The parent Surcharge's Code field must be set (non-null): the platform derives the leaf segment of the auto-generated
ProductSurcharge.RuleKey from Surcharge.Code, so the converter uses it to build a matching rule key. A surcharge whose
parent Surcharge has a null Code cannot be flipped to ConstraintEngine (the org raises a save-hook error) and will not
convert correctly — populate Surcharge.Code before converting.
EXAMPLES
$ sf cml convert surcharge-rules --cml-api SURCHARGE_CML --target-org myOrg
$ sf cml convert surcharge-rules --cml-api SURCHARGE_CML --surcharge-file path/to/surcharges.json --workspace-dir data --target-org myOrg
Converts BRE-based Insurance Underwriting dynamic rules to CML eligibility constraints.
USAGE
$ sf cml convert underwriting-rules -o <value> [--json] [--flags-dir <value>] [--api-version <value>] [-c <value>] [-d <value>]
[--update-records] [-f <value>]
FLAGS
-c, --cml-api=<value> CML API Name. If omitted, auto-discovers an existing CML associated with the same root
products.
-d, --workspace-dir=<value> Directory where output files will be written.
-f, --uw-file=<value> Optional JSON file with pre-exported UnderwritingRule records. If omitted, records are
queried from the org.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
--api-version=<value> Override the api version used for api requests made by this command
--update-records 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.
GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
DESCRIPTION
Converts BRE-based Insurance Underwriting dynamic rules to CML eligibility constraints.
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 \_UnderwritingUpdate.json file with `sf insurance
import record-updates`.
EXAMPLES
$ sf cml convert underwriting-rules --cml-api UW_CML --target-org myOrg
$ sf cml convert underwriting-rules --cml-api UW_CML --uw-file data/underwriting.json --workspace-dir data --target-org myOrg
Imports CML and associations to the target org
USAGE
$ sf cml import as-expression-set -o <value> -x <value> -c <value> [--json] [--flags-dir <value>] [--api-version <value>] [-d
<value>]
FLAGS
-c, --cml-api=<value> (required) Unique CML API Name to be created.
-d, --workspace-dir=<value> Directory where converted CML and assocciations csv files are located.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
-x, --context-definition=<value> (required) Context Definition name to be assocciated with the CML.
--api-version=<value> Override the api version used for api requests made by this command
GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
DESCRIPTION
Imports CML and associations to the target org
Review CM created by conversion command before doing import.
This command executes following logic:
- Import one CML at a time
- Upsert the Expression Set using the `cml-api` name
- Read and upsert `ExpressionSetConstraintObj` rows from the `cml-api_associations.csv` file (resolving FKs)
- Upload the CML blob
EXAMPLES
$ sf cml import as-expression-set --cml-api MY_TEST --context-definition PricingTransactionCD2 --workspace-dir data --target-org tgtOrg
Applies a record-update file emitted by a cml convert command to the target org.
USAGE
$ sf insurance import record-updates -o <value> -f <value> [--json] [--flags-dir <value>] [--api-version <value>] [-p]
[--dry-run]
FLAGS
-f, --file=<value> (required) Path to the record-update JSON file emitted by a cml convert command.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
-p, --no-prompt Don't prompt for confirmation before applying the changes.
--api-version=<value> Override the api version used for api requests made by this command
--dry-run Show the planned changes and exit without writing anything to the org.
GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
--json Format output as json.
DESCRIPTION
Applies a record-update file emitted by a cml convert command to the target org.
Reads a `<cmlApi>_SurchargeUpdate.json` or `<cmlApi>_UnderwritingUpdate.json` file — the reviewable record-update plan
emitted by `sf cml convert surcharge-rules` / `sf cml convert underwriting-rules` — and applies its changes to the
org. Convert is file-only and never writes to the org; this command is the apply step.
The file is treated as untrusted input: it is structurally validated, every record is re-read from the org before
anything is written, and each record's Name (plus the rule apiName for underwriting rule updates) is cross-checked
against the file so an edited id cannot retarget a different record. Records whose org values already match are
skipped, which makes re-running the command safe and idempotent.
Before applying, the command prints every planned field-level change and asks for confirmation. Use --dry-run to
review the plan without writing, and --no-prompt to apply without confirmation in automation. Changes are applied in
order and are not rolled back, so a mid-apply failure can leave the org partially migrated; re-running is safe because
already-applied records are skipped.
Import the merged CML with `sf cml import as-expression-set` BEFORE applying a surcharge record-update file. The
platform regenerates ProductSurcharge.RuleKey when RuleEngineType flips, and this command verifies that regenerated
key against the key the CML rule was emitted under. That regeneration also requires the parent Surcharge.Code to be
non-null — a surcharge whose parent Surcharge has a null Code cannot be flipped to ConstraintEngine and the org raises
a save-hook error.
EXAMPLES
Review the planned changes without writing anything:
$ sf insurance import record-updates --file data/SURCHARGE_CML_SurchargeUpdate.json --target-org myOrg --dry-run
Apply the changes, confirming the preview interactively:
$ sf insurance import record-updates --file data/SURCHARGE_CML_SurchargeUpdate.json --target-org myOrg
Apply the changes without a confirmation prompt, for automation:
$ sf insurance import record-updates --file data/UW_CML_UnderwritingUpdate.json --target-org myOrg --no-prompt