Customize the MarginValidation Interface Implementation

This interface implementation is called by the default Margin Range Loader interface implementation. You can overwrite this class to customize the margin validation.

  • Using the Load Margin Ranges interface implementation, the pricing service queries the named calculation procedure to find the margin ranges for the product specified in the line item and passes it to the Margin Validation interface implementation.

  • Next, the Margin Validation interface implementation compares the line item's margin to determine whether the range is within the specified limits.

  • If the margin is outside the valid range, it returns an error message in the CPQ pricing message field to the Industries CPQ Cart.

Note
Note

This step is optional. Skip it if you want to use the default MarginValidation interface implementation.

Name

Properties

Interface Name

Margin Validation

Active Implementation Class

DefaultMarginValidationImplementation

Purpose

Introduced in the Winter '19 release, this interface implementation is used to validate the margin range on individual line items.

  1. Specify your input and output parameters.

    Input Map Key

    Datatype

    Description

    Input Parameters

    parent

    sObject

    For order, opportunity, or quote

    lineItemList

    List<sObject>

    For OrderItem, QuoteLineItem, or OpportunityLineItem

    lineItemIdToMarginRangeMap

    Map<Id, Map <String, Object>>

    For each line item, a map with the following keys:

    • OneTimeMarginLowerBound

    • OneTimeMarginUpperBound

    • RecurringMarginLowerBound

    • RecurringMarginUpperBound

    Output Parameters: None

  2. Specify an error message in the 'CpqPricingMessage__c' field of the line items. Error messages for one-time margins and recurring margins are displayed for each line item in the Industries CPQ Cart when the margin ranges are violated.

    For example, the one-time margin must be between 5.0% to 10.0% is displayed in the cart for a line item when the validation.

  3. Customize the margin validation implementation using the sample implementation code.
    global class CustomMarginValidationImplementation implements vlocity_cmt.VlocityOpenInterface
    {
        private static String ONE_TIME_MARGIN_PREFIX = 'OneTimeMargin';
        private static String RECURRING_MARGIN_PREFIX = 'RecurringMargin';
        private static String nsp = 'vlocity_cmt__';
        /*
         * invoke method.
         */
        public Boolean invokeMethod(String methodName, Map<String, Object> input, Map<String, Object> output,
                                    Map<String, Object> options)
        {
            if (methodName.equals('validateMarginRange'))
            {
                validateMarginRange(input, output, options);
                return true;
            }
            return false;
        }
     
        /*
         * validate margin ranges.
         *
         */
        private void validateMarginRange(Map<String, Object> input, Map<String, Object> output,
                                         Map<String, Object> options)
        {
            List<SObject> lineItemList = (List<SObject>) input.get('lineItemList');
            //map, which has margin range details.
            Map<Id, Map<String, Object>> itemIdTomarginRangeMap = (Map<Id, Map<String, Object>>)
                    input.get('lineItemIdToMarginRangeMap');
            for (SObject item : lineItemList)
            {
                String oneTimeMarginMessage = null;
                String recurringMarginMessage = null;
                Id lineItemId = (Id) item.get('Id');
                //margin range map also has all output columns of martix.
                Map<String, Object> marginRangeMap = itemIdTomarginRangeMap.get(lineItemId);
                if (marginRangeMap != null)
                {
                    Decimal oneTimeMargin = (Decimal) item.get(nsp + 'OneTimeMargin__c');
                    Decimal recurringMargin = (Decimal) item.get(nsp + 'RecurringMargin__c');
                    Decimal oneTimeMarginLowerBound = (Decimal) marginRangeMap.get('OneTimeMarginLowerBound');
                    Decimal oneTimeMarginUpperBound = (Decimal) marginRangeMap.get('OneTimeMarginUpperBound');
                    Decimal recurringMarginLowerBound = (Decimal) marginRangeMap.get('RecurringMarginLowerBound');
                    Decimal recurringMarginUpperBound = (Decimal) marginRangeMap.get('RecurringMarginUpperBound');
                    if (oneTimeMarginLowerBound != null && oneTimeMarginUpperBound != null &&
                            (oneTimeMargin > oneTimeMarginUpperBound || oneTimeMargin < oneTimeMarginLowerBound))
                    {
                        oneTimeMarginMessage = ONE_TIME_MARGIN_PREFIX + ':' + 'One time margin must be between ' + oneTimeMarginLowerBound +
                                               ' and ' + oneTimeMarginUpperBound + '.';
                    }
                    if (recurringMarginLowerBound != null && recurringMarginUpperBound != null &&
                            (recurringMargin > recurringMarginUpperBound || recurringMargin < recurringMarginLowerBound))
                    {
                        recurringMarginMessage = RECURRING_MARGIN_PREFIX + ':' + 'Recurring margin must be between ' + recurringMarginLowerBound
                                                 + ' and ' + recurringMarginUpperBound + '.';
                    }
                }
     
                //read previous message and update
                String message = (String) item.get(nsp + 'CpqPricingMessage__c');
                List<String> errorList = new List<String>();
                if (String.isNotBlank(message))
                {
                    List<String> messageList = message.split('\\|');
                    for (String m : messageList)
                    {
                        List<String> mList = m.split(':');
                        if (mList.size() > 1)
                        {
                            if (!mList[0].equals(ONE_TIME_MARGIN_PREFIX) &&
                                    !mList[0].equals(RECURRING_MARGIN_PREFIX))
                            {
                                errorList.add(m);
                            }
                        }
                    }
                }
                if (oneTimeMarginMessage != null)
                {
                    errorList.add(oneTimeMarginMessage);
                }
                if (recurringMarginMessage != null)
                {
                    errorList.add(recurringMarginMessage);
                }
                //update pricing message.
                if (errorList.isEmpty())
                {
                    item.put(nsp + 'CpqPricingMessage__c', null);
                }
                else
                {
                    item.put(nsp + 'CpqPricingMessage__c', String.join(errorList, '|'));
                }
            }
        }
    }