Calculating Quote.Discount/Percent in CRMScript trigger

Hello everyone,

I want to create a CRMScript trigger which calculates discount and discount percent after saving a product in Quote.

I can observe that the calculated values are saved only if the user actually edits the discount. (Else it is always set to 0. 0%)

Trigger (Sales: After saving in dialog Add/edit product)

#setLanguageLevel 3;

EventData ed = getEventData();

Integer   quantity  = ed.getInputValue("QuoteLine.Quantity").toInteger();
Float     MRR       = ed.getInputValue("QuoteLine.ExtraField4").toFloat();
Float     listPrice = ed.getInputValue("QuoteLine.UnitListPrice").toFloat();
Float     sum       = ed.getInputValue("QuoteLine.SubTotal").toFloat();

// Calculate discount, percentage
Float        discount           = (listPrice * quantity) - (MRR * quantity);
Float        discountPercentage = discount / sum * 100;

NSQuoteAgent agent;
Integer id = ed.getInputValue("QuoteLine.QuoteLineId").toInteger();
NSQuoteLine  line = agent.GetQuoteLine(id);
line.SetDiscountAmount(discount);
line.SetDiscountPercent(discountPercentage);
line = agent.SaveQuoteLine(line);

I'm guessing my code is missing something, but I can't figure out what. Perhaps it has something to do with this method? Or a rule or setting I have to change somewhere?

line.SetUserValueOverride();

Any help is much appreciated.

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Hi Thomas,

All quoteline calculations are performed in the QuoteConnector. In the case of Amount and Percentage discount, I believe it's one or the other and not both.

If you want to override discount percentage, then you set it as you have done and it will not be affected by the QuoteConnectors recalculation routine. Use the SetUserValueOverride(2); to flag the connector that you intend to overriding the percentage.

If you want to override discount amount, then you set it as you have done and it will not be affected by the QuoteConnectors recalculation routine. Use the SetUserValueOverride(3); to flag the connector that you intend to overriding the amount.

public enum ValueOverride : short
{
/// <summary>
/// 0: Discount data is from the ERP system and has not been overridden
/// </summary>
[EnumMember]
None = 0,

/// <summary>
/// 1: The Total amount has been overridden; all other fields should be recalculated
/// </summary>
[EnumMember]
Total = 1,

/// <summary>
/// 2: The discount percentage has been overriden; all other fields should be recalculated
/// </summary>
[EnumMember]
DiscountPercent = 2,

/// <summary>
/// 3: The discount amount has been overridden; all other fields should be recalculated
/// </summary>
[EnumMember]
DiscountAmount = 3,

/// <summary>
/// 4: The earning percent has been overridden; all other fields should be recalculated
/// </summary>
[EnumMember]
EarningPercent = 4,

/// <summary>
/// 5: The earning amount has been overridden; all other fields should be recalculated
/// </summary>
[EnumMember]
EarningAmount = 5,
}

 

Hope this helps!

By: Tony Yates 15 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

That did the trick. Thank you, Tony. :-)

By: Thomas Roesen 15 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Hi,

I was also playing with this, got to the same solution that Tony provided, but without having to retrieve the quote line and saving it seperately. (This is on the Before saving in dialog Add/edit product trigger)

EventData eventData = getEventData();

Integer quantity  = eventData.getInputValue("QuoteLine.Quantity").toInteger();
Float MRR       = eventData.getInputValue("QuoteLine.ExtraField4").toFloat();
Float listPrice = eventData.getInputValue("QuoteLine.UnitListPrice").toFloat();
Float sum       = eventData.getInputValue("QuoteLine.SubTotal").toFloat();

// Calculate discount, percentage
Float discount = (listPrice * quantity) - (MRR * quantity);
Float discountPercentage = discount / sum * 100;

eventData.setOutputValue("QuoteLine.DiscountPercent", discountPercentage.toString(2));

eventData.setOutputValue("QuoteLine.UserValueOverride", "DiscountPercent");

 

By: David Hollegien 15 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Good catch David! I totally skipped over that very important point. 

Thomas, please consider using the Before Saving Quote Trigger and take David's advice and use the setOutputValue method. The Sales client quoteline values will be updated prior to the client calling the SaveQuoteLine method. This avoids calling the SaveQuoteLine method twice, thereby more efficient and performant.

Hope this helps!

By: Tony Yates 15 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Hi,

I have a script that also maybe can benfit from the content in this thread by making it more efficient.

This script is started by the trigger after a sale is saved.

The only possible event, from my understanding, is for this script to be executed in the context of a user saving a sale.

The reason is that the users create a project just before order creation and the ERP system requires a valid project number on all quote lines. In many cases the project doesn't exist until it is time to create the order, and in this case the already created quote lines needs to get populated with project number.

The reason for the after event and not the before, is because it needs to respond to the case when the sale gets connected to a project, and I haven't been able to read the updated project data from the current context (i.e. when the user connects the sale to a project with the project dropdown on the sale's main window or changes the project connected to the sale from one project to another project).

The issue I'm having with making the code below more efficient with the approach in this thread is that I can't find the values needed in the EventData object. Since the context I'm in is related to the sale.

Is there a way to change the current context within the script or some other smart way of taking advantage of the setOutputValue method for the script below?

#setLanguageLevel 3;

EventData eventData;
NSProjectAgent projectAgent;
NSProjectEntity projectEntity;
NSSaleAgent saleAgent;
NSSale sale;
NSQuoteAgent quoteAgent;
NSQuote quote;
NSQuoteAlternative[] quoteAlternatives;
NSQuoteLine[] quoteLines;
String[] output;

eventData = getEventData();
sale = saleAgent.GetSale( eventData.getInputValue( "SaleEntity.SaleId" ).toInteger() );
projectEntity = projectAgent.GetProjectEntity( sale.GetProjectId() );

if ( sale.GetProjectId() == 0 )
{
  output.pushBack( "Salg " + sale.GetSaleId().toString() + " er ikke lagt inn i prosjekt. Avslutter." );
  return;
}
else
{
  output.pushBack( "Salg " + sale.GetSaleId().toString() + " ligger under prosjekt " + sale.GetProjectId().toString() + "..." );
  if ( projectEntity.GetActiveErpLinks() < 1 )
  {
    eventData.setBlockExecution( true );
    eventData.setMessage( "Prosjektet er ikke koblet til ERP." );

    output.pushBack( "Prosjekt " + sale.GetProjectId().toString() + " ikke koblet til ERP. Avslutter." );
    return;
  }
  else
  {
    output.pushBack( "Prosjekt " + sale.GetProjectId().toString() + " koblet til ERP..." );

    if( quoteAgent.GetQuoteFromSaleId( sale.GetSaleId() ).GetQuoteId().toString().isEmpty() )
    {
      output.pushBack( "Tilbud ikke opprettet for salg " + sale.GetSaleId().toString() + ", dette er mest sannsynlig et \"direktesalg\". Avslutter." );
      return;
    }

    quote = quoteAgent.GetQuoteFromSaleId( sale.GetSaleId() );
    output.pushBack( "Tilbud " + quote.GetQuoteId().toString() + " ligger under salg " + sale.GetSaleId().toString() + "..." );

    quoteAlternatives = quoteAgent.GetQuoteAlternatives( quote.GetActiveQuoteVersionId() );

    for( Integer i = 0; i < quoteAlternatives.length(); i++ )
    {
      quoteLines = quoteAgent.GetQuoteLines( quoteAlternatives[ i ].GetQuoteAlternativeId() );
      if( quoteLines.length() == 0 )
      {
        output.pushBack( "Tilbudsalternativ " + i.toString() + " inneholder ingen tilbudslinjer." );
      }
      else
      {
        for( Integer j = 0; j < quoteLines.length(); j++ )
        {
          quoteLines[ j ].SetExtraField1( projectEntity.GetProjectNumber() );
          quoteAgent.SaveQuoteLine( quoteLines[ j ] );
        }
      }
    }
  }
}

Best regards.

By: Chris-Anton Eriksen 17 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Hi Chris-Anton!

To prevent a save from happening, i.e. when a project is not set, use the setValidationMessage method to tell the user what needs to be done. See this thread for an example.

This should be done in the Before Save... in order t prevent it from being saved in the first place.

Hope this helps!

By: Tony Yates 17 Sep 2020

RE: Calculating Quote.Discount/Percent in CRMScript trigger

Hi Tony,

Thanks for your reply.

I have experimented a bit with the before/after functionality and have observed that this is indeed what is happening with a "before" trigger. I will look further into it by reading up on the thread you referred to, thanks!

The main question im trying to get answered here though is if there's a way to change the current context within the script or some other smart way of taking advantage of the setOutputValue method for the script in my original reply to this thread? Instead of using the SaveQuoteLine method which was both discussed earlier in this thread and what is currently used in the script in my original reply.

By: Chris-Anton Eriksen 17 Sep 2020