Enable Advanced Configurator and Constraint Models

RequirementNotes
Revenue Cloud Advanced licenseOrg must already have RCA enabled and the standard Sales Transaction context extended.
Admin or equivalent perm setYou’ll create fields, edit context definitions, and deploy Apex.
Developer Console / VS CodeNeeded for the small Quote Line Item trigger.

 Step 1)  Create Text Area (Long) field called Constraint Engine Node Status to be low three objects.

     i) Quote Line Item

     ii) Order Product

    iii) Asset Action Source

  1. Setup ▸ Object Manager ▸ Quote Line Item ▸ Fields & Relationships ▸ New
  2. Choose Text Area (Long) ▸ Next.
  3. Field Label = Constraint Engine Node Status
  4. Field Name = ConstraintEngineNodeStatus (no underscores)
  5. Length = 5 000 ▸ assign field‑level security ▸ Save.
Step 2)  
  1. Setup ▸ Custom Context Definitions ▸ open your extended SalesTransactionContext_MK context.
  2. Edit Nodes
    • SalesTransactionItem → Add Attribute
      • NameConstraintEngineNodeStatus
      • TypeInput & Output
      • Data TypeString
    • AssetActionSource → Add Attribute
      • NameAssetConstraintEngineNodeStatus
      • TypeInput & Output
      • Data TypeString
  3. Attribute Tags
    • On each node, scroll to the new attribute, click Add Tag, use the same name (Salesforce will append _c).
  4. Save the context.
Step 3) 
  1. In Context Definitions, click  next to Quote Entities Mapping → Edit SObject Mapping.
  2. Map ConstraintEngineNodeStatus (left) ➜ ConstraintEngineNodeStatus__c on Quote Line Item (right).
  3. Verify TransactionType is also mapped.
  4. Save, then repeat for OrderEntitiesMapping & AssetEntitiesMapping  (using the asset‑specific attribute for Asset Action Source).
Don’t forget to Activate the context after all mappings are saved.

Step 4)  Deploy the Quote Line Item Trigger

trigger QuoteItemTrigger on QuoteLineItem (before insert) {
    //collect QuoteActionIds 
    Set<Id> quoteActionIds = new Set<Id>();

    for (QuoteLineItem qi : Trigger.new) {
        if (qi.QuoteActionId != null && qi.ConstraintEngineNodeStatus__c == null) {
            quoteActionIds.add(qi.QuoteActionId);
        }
    }

    if (!quoteActionIds.isEmpty()) {
        // Step 1: Get QuoteAction → SourceAsset
        Map<Id, Id> quoteActionToAssetId = new Map<Id, Id>();
        for (QuoteAction qAction : [
            SELECT Id, SourceAssetId 
            FROM QuoteAction 
            WHERE SourceAssetId != null 
              AND Id IN :quoteActionIds
        ]) {
            quoteActionToAssetId.put(qAction.Id, qAction.SourceAssetId);
        }

        // Step 2: Get AssetActions
        List<AssetAction> assetActions = [
            SELECT Id, AssetId, ActionDate 
            FROM AssetAction 
            WHERE AssetId IN :quoteActionToAssetId.values()
        ];

        // Step 3: Get latest AssetAction per Asset
        Map<Id, AssetAction> assetIdToLatestAction = new Map<Id, AssetAction>();
        for (AssetAction aAction : assetActions) {
            AssetAction existing = assetIdToLatestAction.get(aAction.AssetId);
            if (existing == null || aAction.ActionDate > existing.ActionDate) {
                assetIdToLatestAction.put(aAction.AssetId, aAction);
            }
        }

        // Step 4: Get related AssetActionSource records
        Map<Id, Id> assetIdToActionId = new Map<Id, Id>();
        for (Id assetId : assetIdToLatestAction.keySet()) {
            assetIdToActionId.put(assetId, assetIdToLatestAction.get(assetId).Id);
        }

        List<AssetActionSource> assetActionSources = [
            SELECT ConstraintEngineNodeStatus__c, AssetAction.AssetId 
            FROM AssetActionSource 
            WHERE AssetActionId IN :assetIdToActionId.values() ORDER BY CreatedDate DESC
        ];
        // Step 5: Map AssetId → Status
        Map<Id, String> assetIdToStatus = new Map<Id, String>();
        for (AssetActionSource actionSource : assetActionSources) {
            if (!assetIdToStatus.containsKey(actionSource.AssetAction.AssetId) && 
                actionSource.ConstraintEngineNodeStatus__c != null) {
                assetIdToStatus.put(
                    actionSource.AssetAction.AssetId, 
                    actionSource.ConstraintEngineNodeStatus__c
                );
            }
        }
        List<QuoteLineItem> toUpdate = new List<QuoteLineItem>();
        // Step 6: Set ConstraintEngineNodeStatus__c directly on Trigger.new records
        for (QuoteLineItem qi : Trigger.new) {            
            if (qi.QuoteActionId != null && qi.ConstraintEngineNodeStatus__c == null) {
                Id assetId = quoteActionToAssetId != null ? quoteActionToAssetId.get(qi.QuoteActionId) : null;
                if (assetId != null && assetIdToStatus != null) {
                    String status = assetIdToStatus.get(assetId);
                    if (status != null) {
                       qi.ConstraintEngineNodeStatus__c = status;
                    }
                }
            }
        }
    }
}

Step 5)  Assign Below permissionSet to Required user 

Permission Set Name : Product Configuration Constraints Designer 

Step 6) Enable the Advanced Configurator Engine

  1. Setup ▸ Revenue Settings
  2. Scroll to Set Up Configuration Rules and Constraints with Constraints Engine.
  3. Toggle Enable → Save.

Now you see Constraint Models in the App Launcher. Ensure the tab is visible in relevant profiles.

  1. App Launcher ▸ Constraint Models ▸ New
  2. NameBundle Auto‑Add Demo
  3. Context Definition: select your active context → Save.
  4. In Version 1 → Add Items:
    • Smart Office Bundle
    • Remote Work Bundle
    • (All option products auto‑import.)



Comments

Popular posts from this blog

Configure types Of Products in CPQ and Revenue Cloud ?

My Trigger Scenario's

Steps for Calculate Derived Pricing in RCA ?