Pricing Callback Class
The Pricing Callback class enables you to add pricing logic to your cart that cannot be achieved by out-of-the-box pricing mechanisms, such as Price Rulesets and Price Matrices. The Pricing Callback is executed for each bundled product or standalone in the cart. A separate transaction call is initiated for each product in the cart. The Pricing Callback is invoked when you are on the cart page. The pricing callback is invoked during pricing stages and executes the various methods in the BASEPRICE and ADJUSTMENT modes. This feature enables dynamic pricing adjustments based on specific conditions.
IBasePricing Callback
IBasePricing callback is invoked during different pricing stages and executes the various methods in the BASEPRICE and ADJUSTMENT modes.
Adjustment Line Item Callback can be achieved with IBasePricing callback using its method.
To use the Pricing Callback you must create a custom C# class that implements the following interfaces:
Interface | Description |
IBasePricing | Provides a mechanism to define custom logic to be executed during different pricing stages. |
Feature Flag
is-pricing-callback-enabled is the feature flag that enables the Product Filter callback.
The following methods are available in the IBasePricing callback interface:
Method | Signature | Description |
BeforePricingBatchAsync () | public async Task BeforePricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken token); | You can use this method to define custom logic that must be executed before Base Price is calculated. |
OnPricingBatchAsync() | public async Task OnPricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken token); | You can use this method to define custom logic that must be executed during the Base Price calculation. You can use the price list items to write the custom logic. |
AfterPricingBatchAsync() | public async Task AfterPricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken token); | You can use this method to define custom logic that must be executed after the Base Price is calculated. |
OnProductOptionPriceAsync() | public async Task OnProductOptionPriceAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IProductOptionPrice> productOptionPrice, CancellationToken token); | You can use this extension point to modify product option prices for the given line item. |
OnPriceMatrixAsync() | public async Task OnPriceMatrixAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IPriceMatrixEntry>> priceMatrixEntries, CancellationToken token); | You can use this extension point to modify resolved price matrix for the given line item if required. |
OnPriceRuleAsync() | public async Task OnPriceRuleAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IPriceRuleEntry>> priceRuleEntries, CancellationToken token); | You can use this extension point to modify the resolved price rule entries for the given line item if required. |
OnPipelinePriceRuleAsync() | Task OnPipelinePriceRuleAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IPriceRuleEntry>> pipelinePriceRuleEntries) | You can use this extension point to modify the resolved price pipeline rule entries for the given line item if required |
OnPriceEscalatorAsync() | public async Task OnPriceEscalatorAsync(IBatchPriceRequest batchPriceRequest, List<IPriceEscalator> priceEscalators, CancellationToken token); | You can use this extension point to modify the resolved price escalators if required. |
OnWaterfallRuleAsync() | public async Task OnWaterfallRuleAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IWaterfallRuleEntry>> waterfallRuleEntries, CancellationToken token); |
Usecases
Following are some examples of BasePricing Callback methods.
CancellationToken has been introduced in custom code developed within the Conga Advantage Platform. If you are still using the older approach, you must mark those legacy methods as obsolete. This means applying the [Obsolete] attribute to methods that do not support tokens. For example: [Obsolete("Use version with CancellationToken")]. For more information, see CancellationToken Implementation Guidelines.
BeforePricingBatchAsync
This method is called before pricing gets started for the batch. Any logic that is needed before the pricing is started can be written here. In below example the code will set cart Line start and end date same as cart start and end date.
public async Task BeforePricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingBasePriceCallback.BeforePricingBatchAsync Started");
var cartLines = batchPriceRequest.GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
var cart = batchPriceRequest.GetCart();
var cartStartDate = cart.GetEntity().ExpectedStartDate;
var cartEndDate = cart.GetEntity().ExpectedEndDate;
foreach (ILineItemModel lineItem in cartLines)
{
lineItem.StartDate = cartStartDate;
lineItem.EndDate = cartEndDate;
}
logHelper?.LogInformation("PricingBasePriceCallback.BeforePricingBatchAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnPricingBatchAsync
This extension point is called on pricing, after PLI is resolved for the batch.
public async Task OnPricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingBasePriceCallback.OnPricingBatchAsync Started");
var cartLines = batchPriceRequest.GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
foreach (ILineItemModel lineItem in cartLines)
{
var PLI_ListPrice = lineItem.GetLookupValue<decimal?>("PriceListItem.ListPrice");
lineItem.ListPrice_custom = PLI_ListPrice;
}
logHelper?.LogInformation("PricingBasePriceCallback.OnPricingBatchAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}AfterPricingBatchAsync
This extension point is called once pricing is completed for the batch.
public async Task AfterPricingBatchAsync(IBatchPriceRequest batchPriceRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingBasePriceCallback.AfterPricingBatchAsync Started");
var cartLines = batchPriceRequest.GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
foreach (ILineItemModel lineItem in cartLines)
{
var lineCost = lineItem.GetEntity().Cost;
var lineNetUnitPrice = lineItem.GetEntity().NetUnitPrice;
lineItem.NetPrice_custom = lineCost * lineNetUnitPrice;
}
logHelper?.LogInformation("PricingBasePriceCallback.AfterPricingBatchAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnWaterfallRuleAsync
public async Task OnWaterfallRuleAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IWaterfallRuleEntry>> waterfallRuleEntries, CancellationToken token);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnProductOptionPriceAsync
public async Task OnProductOptionPriceAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IProductOptionPrice> productOptionPrice, CancellationToken token);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnPriceMatrixAsync
public async Task OnPriceMatrixAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IPriceMatrixEntry>> priceMatrixEntries, CancellationToken token);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnPriceRuleAsync
public async Task OnPriceRuleAsync(IBatchPriceRequest batchPriceRequest, IDictionary<string, IEnumerable<IPriceRuleEntry>> priceRuleEntries, CancellationToken token);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}
OnPriceEscalatorAsync
public async Task OnPriceEscalatorAsync(IBatchPriceRequest batchPriceRequest, List<IPriceEscalator> priceEscalators, CancellationToken token);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}ITotallingPricing Callback
ITotalingPricing Callback class enables you to add pricing totaling logic to your cart that cannot be achieved by out-of-the-box pricing mechanisms. The Pricing Totaling Callback is invoked when you are on the cart page. The pricing totaling callback is invoked during different pricing totaling stages and executes the various methods in the BASEPRICE and ADJUSTMENT modes. This feature enables dynamic pricing adjustments based on specific conditions.
Adjustment Spread Callback can be achieved with ITotallingPricing Callback using its method.
Example:
Customer has a use case where they want to capture Total QTY (sum of QTY of all Similar Products in cart) against the Line Item. Also, they wanted to Capture Total Product Group QTY (Sum of QTY belonging to Similar Product Group). This intern was used to drive Pricing using Price Rules. In this case ITotalingPricing triggers to calculate Total QTY and Total Product Group QTY.
Interface | Description |
ITotalingPricing | Provides a mechanism to define custom logic to be executed before, during, and after adjustment Calculation |
The following methods are available in the ITotallingPricing interface:
Method | Signature | Description |
BeforePricingCartAdjustmentAsync | public Task BeforePricingCartAdjustmentAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken); | You can use this method to define custom logic that must be executed before Base Price is calculated. |
AfterPricingCartAdjustmentAsync | public Task AfterPricingCartAdjustmentAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken); | You can use this method to define custom logic that must be executed after the Base Price is calculated. |
OnCartPricingCompleteAsync | public Task OnCartPricingCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken); | You can use this extension point to modify product option prices for the given line item. |
OnDealMaximizerCompleteAsync() | public async Task OnDealMaximizerCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken); | |
OnApprovalCheckCompleteAsync() | public async Task OnApprovalCheckCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken); |
Usecases
Following are some examples of TotallingPricing Callback methods.
CancellationToken has been introduced in custom code developed within the Conga Advantage Platform. If you are still using the older approach, you must mark those legacy methods as obsolete. This means applying the [Obsolete] attribute to methods that do not support tokens. For example: [Obsolete("Use version with CancellationToken")]. For more information, see CancellationToken Implementation Guidelines.
BeforePricingCartAdjustmentAsync
This extension point is called before cart adjustment starts.
public async Task BeforePricingCartAdjustmentAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingTotallingCallback.BeforePricingCartAdjustmentAsync Started");
var cartLines = aggregateCartRequest.GetCartContext().GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
foreach (ILineItemModel lineItem in cartLines)
{
if (lineItem.GetEntity().LocationId == "Loc0001")
{
lineItem.GetEntity().AdjustmentType = "% Discount";
lineItem.GetEntity().AdjustmentAmount = 15;
}
}
logHelper?.LogInformation("PricingTotallingCallback.BeforePricingCartAdjustmentAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}AfterPricingCartAdjustmentAsync
This extension point is called after cart adjustment is complete.
public async Task AfterPricingCartAdjustmentAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingTotallingCallback.AfterPricingCartAdjustmentAsync Started");
var cart = aggregateCartRequest.GetCartContext().GetCart();
var cartLines = aggregateCartRequest.GetCartContext().GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
var cartTotalNetPrice = cartLines.Sum(x => x.GetEntity().NetPrice);
cart.Set("CartTotalNetPrice_custom", cartTotalNetPrice);
logHelper?.LogInformation("PricingTotallingCallback.AfterPricingCartAdjustmentAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnCartPricingCompleteAsync
This extension point is called after adjustment/totalling is complete. This is the last method in the pricing cycle.
public async Task OnCartPricingCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken)
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
logHelper?.LogInformation("PricingTotallingCallback.OnCartPricingCompleteAsync Started");
var cartLines = aggregateCartRequest.GetCartContext().GetLineItems().SelectMany(x => x.GetChargeLines()).ToList();
foreach (ILineItemModel lineItem in cartLines)
{
}
logHelper?.LogInformation("PricingTotallingCallback.OnCartPricingCompleteAsync Ended");
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnDealMaximizerCompleteAsync
public async Task OnDealMaximizerCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}OnApprovalCheckCompleteAsync
public async Task OnApprovalCheckCompleteAsync(IAggregateCartRequest aggregateCartRequest, CancellationToken cancellationToken);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}Additional Tip:
Creating PriceRamp should be done from IPricingTotallingCallback.OnCartPricingCompleteAsync see:
string url = string.Format("/api/cart/v1/carts/{0}/items/{1}/auto-ramps", cartId, lineItemId);
var response = await GetHttpHelper(new Conga.Platform.Extensibility.CustomCode.Library.Models.InternalApiAttributes()).PostAsync(url, payload);IRelatedPricing Callback
IRelatedPricing Callback allows you to apply custom logic to calculate the related pricing. This callback is invoked while CPQ is calculating related pricing after the main pricing is calculated.
To use the Related Pricing Callback you must create a custom C# class that implements the IRelatedPricing callback interface and register the custom C# class with Related Pricing Callback Class. You must write your custom logic in the custom C# class.
If you have defined Pricing Callback class, Related Pricing Callback is always invoked after the Pricing Callback is executed.
Interface | Description |
IRelatedPricing | Provides a mechanism to define custom logic for calculating the pricing for related product line items |
The following methods are available in the IRelatedPricingCallback interface:
Method | Signature | Description |
|---|---|---|
ComputeBasePriceBatchAsync() | public Task<List<IRelatedPricingBatchResponse>> ComputeBasePriceBatchAsync(IRelatedPricingBatchRequest relatedPricingBatchRequest, CancellationToken cancellationToken); | You can use this method to apply custom logic for related pricing calculation and override the default CPQ related pricing logic. |
CancellationToken has been introduced in custom code developed within the Conga Advantage Platform. If you are still using the older approach, you must mark those legacy methods as obsolete. This means applying the [Obsolete] attribute to methods that do not support tokens. For example: [Obsolete("Use version with CancellationToken")]. For more information, see CancellationToken Implementation Guidelines.
public Task<List<IRelatedPricingBatchResponse>> ComputeBasePriceBatchAsync(IRelatedPricingBatchRequest relatedPricingBatchRequest, CancellationToken cancellationToken);
{
ThrowIfCancellationRequested(cancellationToken);
var logHelper = GetLogHelper();
try
{
//add your logic here
}
catch (Exception ex)
{
logHelper?.LogError("Exception Message=" + ex.Message + "stacktrace =" + ex.StackTrace);
}
await Task.CompletedTask;
}