This section provides guidelines to follow while coding the TurboEngines compatible callbacks.

General Coding Guidelines for TurboEngines Callbacks

Naming Conventions

Naming a Class
  • Use Pascal casing standard for naming a class.

    • For example, the class name should be PricingBasePriceCallback instead of pricingBasePriceCallback.

Naming a Method
  • Use Pascal casing standard for naming a method.

    • For example, the method name should be BeforePricingCartAdjustmentAsync instead of beforePricingCartAdjustmentAsync.

Naming a variable or method parameter

  • Use Camel casing standard for naming a variable or method parameter.

    • For example, the variable name should be cartLineItems instead of CartLineItems.

  • Use meaningful, descriptive names for naming a variable or method parameter.

    • For example, the variable name should be cartLineItems instead of crtLnItms

Naming a class property

  • Use Pascal casing standard for naming a class property

    • For example, the property name should be Id instead of id

  • Use meaningful, descriptive names for naming the property

    • For example, the property name should be IsPrimaryLine instead of IsPrmryLn.

Coding Guidelines for TurboEngines Callbacks

You must follow the below guidelines while writing the callback code (C#) for TurboEngines.

When you create a new callback project using TurboEngines callback authoring UI, the sample callback project is auto-generated by the TurboEngine Extensibility framework as per the TurboEngine Callback coding guidelines. You can enhance it further with a custom implementation.
Using Statements
  1. When you are writing the callback code:
    1. Add all the required using statements for the callback code.
    2. Below are the supported using statements in TurboEngine C# callback code:
      1. .NET core framework provided using statements
      2. Newtonsoft.json provided using statements
      3. TurboEngine Extensibility Framework provided using statements.
    3. For your reference, TurboEngine Extensibility Framework provides using statements are as follows. It would help if you used the required using statements.
using Apttus.Lightsaber.Pricing.Common.Callback;
using Apttus.Lightsaber.Pricing.Common.Callback.Entities;
using Apttus.Lightsaber.Pricing.Common.Callback.Messages;
using Apttus.Lightsaber.Pricing.Common.Callback.Enums;
using Apttus.Lightsaber.Pricing.Common.Callback.Models;
using Apttus.Lightsaber.Extensibility.Library;
using Apttus.Lightsaber.Extensibility.Library.Attributes;
using Apttus.Lightsaber.Extensibility.Library.Extension;
using Apttus.Lightsaber.Extensibility.Library.Interface;
using Apttus.Lightsaber.Extensibility.Library.Model;
Namespace naming
  1. Namespace's name must have the following convention:
    1. For example, when writing a pricing callback implementation for Totaling callback for organization X, write the namespace as follows
Class Naming
  1. The main class implementing the callback interface must always inherit from CodeExtensibility.
  2. The naming of the main class must be based on the interface name. So, remove “I“ prefix from the interface name.
    1. For example, pricing callback implementation for Totaling callback must implement IPricingTotallingCallback. Hence, the class name must be PricingTotallingCallback.
  1. Methods should follow async/await pattern implementation.
    1. Following is an example method representation without async/await implementation.

      public void MethodName() {
    2. Following is an example method implementation with async/await implementation.

      public async Task MethodName() {
          await Task.CompletedTask;
  • For better precision, use decimal datatype instead of double datatype. 
Database queries related
  • All the database query invocation must be part of the class named DataAccess under the folder DataAccess.
  • Ensure that all the DB queries for fetching the master data records from TurboEngine Extensibility data source (For example, MongoDB) are written in QueryHelper under the DataAccess folder.
    • This provides a reference and a single place to identify all the queries used by a callback implementation.
  • The respective model class needs to be created as follows to hold the result/response of the query execution for each of the queries executed. When you create a new callback project, a sample model class is already provided in the auto-generated callback project.
    • Create all the model classes under the Models folder.
    • The name of the class must end with QueryModel.
      1. For example, the model class to hold the result/response of Account query execution should be named as AccountQueryModel.
    • Ensure that the field names of QueryModel match the field names used in the query.
    • Ensure that the QueryModel class is annotated with the Entity attribute. The entity name should be spelled the same as it is in Salesforce.
      1. For example, The AccountQueryModel class should have the Entity attribute added as [Entity("Account")]. Here Account is the entity name as available in Salesforce.
  • Ensure that all the general-purpose constants used in the callback implementation are specified in the Constants class.

Custom Fields
  • The classes under the Fields folder must hold the field name constants for any custom fields used in the callback implementation. These class names must end with Field.
    • For example, if the class to hold the proposal object’s custom field must be ProposalField.
    • Generally, you are required to create such classes for the line item, proposal, and cart object fields.
LineItem and Proposal entity model objects
  1. To easily access LineItem and Proposal fields for getting or setting the values in the callback implementation, LineItem and Proposal classes are auto-created under the Entities folder by the TurboEngine Extensibility framework. 

    1. You must use those classes in your callback implementation and add the custom field details to your implementation.

    2. When adding custom field details to LineItem or Proposal class,

      1. provide the relationship field as shown in the following example. For example, If your relationship field is Apttus_Config2__ProductId__r.APTS_field__c then the field name is Apttus_Config2__ProductId__r_APTS_field__c. Here “.“ is replaced with “_” in the name.

      2. In getter methods for the fields, use GetLookupValue<T>() method for relationship fields and use GetValue<T>() method for non-relationship or direct fields.

Extensibility Framework Helpers

For detailed Extensibility Framework Helpers information, refer to the Helper Functions for TurboEngines Callbacks topic.

Best Practices for TurboEngines Callback code

Consider the following practices while writing the TurboEngines compatible C# callback code. 

Whenever you change specific fields within the callback code and send them to the TurboPricing for pricing, It only verifies the change in Net Price and does not verify each field to detect the difference. Hence, if you make any change in callback code that impacts pricing, perform the following. 

TurboEngine uses a digest mechanism to detect if there are any changes to the line items. At the end of every pricing cycle, it recomputes the digest and compares it against the old digest. If they did not match, you could consider that the line item is changed. However, TurboEngines uses only a few selected fields (including NetPrice) to compute the digest but no custom field. Therefore, when the callback code makes any important change on a line item or custom field that TurboEngines might not detect, you must reset digest for that line item to avoid matching the new one. For example, use the following code to reset the digest for a line item with type ILineItemModel:

lineItem.Set("Digest", string.Empty);

Below is the sample pattern to be followed:

List<LineItem> lineItemsToResetDigest = new List<LineItem>();
foreach(var lineItem in allLineItems){
  // business logic that changes line item's field
  // add all such lines to above list

// reset the digest at the end in bulk

General Practices

  1. Use class-level variables only when necessary and try to use the local variables as much as possible.
  2. If you need a class-level variable, use BeforePricingBatchAsync (Base price callback) or BeforePricingCartAdjustmentAsync (Totalling callback) methods to initialize such class level variables.
  3. Avoid using nested for loops.
  4. Do not use Try or Catch in the callback code to explicitly log any unhandled exception. By default, an unhandled exception is logged by the TurboEngine Extensibility framework. However, you can also configure to display the unhandled exception on UI using the “Is Callback Exception Throw Enabled” (is-callback-exception-throw-enabled) feature flag.
  5. For using Extensibility framework helpers (such as DBHelper, LogHelper, and so on) in your callback project, you must create a helper object in the callback implementation class (the main class of callback project which is implementing the callback interface). If you want to access helpers in any other supported class of your callback project, you must pass the helper instances from the main implementation class to such classes.
  6. You must not use the PricingHelper's UpdatePrce() method on line items unless it is necessary.  Therefore, design a callback implementation that can avoid such a method call.
  7. Do not use concrete objects to set as a field value on the line item. In case if you want to set a concrete object as a value to the line items field, then stringify it before setting it on the line item field.
  8. From TurboEngine callback implementation, do not make an SFDC API call using HttpHelper in any scenario. If such a scenario needs to be handled, get in touch with TurboEngine's product or technical team.
  9. In Base price callback, always use batch line items for updating any value on line item as per your custom logic. Ensure that you do not update anything on cart line items when you are in the Base price callback.
  10. Always use the async or await pattern for each method.
  11. To access/update any field in callback code, the field API name/object API name must be spelled the same in Salesforce.
  12. Keep your code formatted for easy readability. Callback authoring UI provides code formatting capability to format the code as per C# standards.
  13. Keep your code to have 0 or minimum code warnings. Callback authoring UI provides the capability that shows you the code warnings per the C# standards for your callback project. Ensure that you have fixed them and incorporated them in your code to have 0 or minimum code warnings.