web
You’re offline. This is a read only version of the page.
close
Skip to main content

Announcements

No record found.

News and Announcements icon
Community site session details

Community site session details

Session Id :
Microsoft Dynamics CRM (Archived)

Quote to Order Workflow prevents Plugin from firing

(0) ShareShare
ReportReport
Posted on by

Hi guys,

I created a custom plugin that calculates the VAT %, Tax and some Discount % in the Quote Product & Order Product entities, and they both work just fine.

When I try to create an Order from a Quote though, I get the error "The product and the unit cannot be updated while the entity is locked." What I get from this error is that because of the built-in workflow that gets the data from the quote and sets it to the "Order", I cannot use my plugin.

What I tried so far is to change the step for execution of the plugin from Post to Pre (Didn't calculate though)Event.

I tried to change the execution mode to "Asynchronous" - This way I was able to create an Order from the Quote, but my plugin in the Quote Product didn't work at all, or calculated wrong values.

I tried the steps in this article too, but without success. - http://nzcrmguy.blogspot.dk/2011/05/c-plugin-update-error-product-and-unit.html

Please, help!

*This post is locked for comments

I have the same question (0)
  • Aileen Gusni Profile Picture
    44,524 on at

    Hi Georgi,

    You create two plugins both are for quote product and order product because there is any possibility that the users can create order directly without quote right?

    If you want to create the order from quote, not directly from order without original quote, so I think you are no need to create the plugin for order product anymore because CRM has its functionality to auto populate copy from the quote

    But because you had another scenario users can key in directly the order and order product without any original code, you can add one more condition before you out your custom code calculating the vat and discount and other things in your current design.

    The condition if you should put if order does not have quote id field, sorry I cannot access the CRM now so dont know the field name, but for example here:

    If(order.Contains("originalquotefieldid"))

    {

     //put your own custom logic here

    }

    Then CRM will skip the calculation for order product with its built in quote to order functionality and will follow exactly in the quote that this order was coming from.

    I hope this can help you.

    Thank you.

  • Community Member Profile Picture
    on at

    Thanks for the reply Aileen!

    Yes, I need the two plugins to be working separately in the quote product and order product, because of business logic.

    What I'm trying to do is to check if the field that indicates whether the entity is locked is true/false, and if it is not locked - calculates and sets the values that I need. I'll paste my code here:

    IPluginExecutionContext context = localContext.PluginExecutionContext;
    
               IOrganizationService service = localContext.OrganizationService;
    
               Guid quoteProductID = (Guid)((Entity)context.InputParameters["Target"]).Id;
    
               ColumnSet set = new ColumnSet();
    
               set.AllColumns = true;
    
               var quote = service.Retrieve("salesorderdetail", quoteProductID, set);
    
               if (context.Depth > 1)
    
               {
    
                   return;
    
               }
    
               else
    
               {
    
                  
    
                   bool check = (bool)quote.Attributes["salesorderispricelocked"];
    
                   if (check)
    
                   {
    
                       if (quote.Attributes.Contains("productid"))
    
                       {
    
                           quote.Attributes.Remove("productid");
    
                       }
    
                       if (quote.Attributes.Contains("uomid"))
    
                       {
    
                           quote.Attributes.Remove("uomid");
    
                       }
    
                       if (quote.Attributes.Contains("priceperunit_base"))
    
                       {
    
                           quote.Attributes.Remove("priceperunit_base");
    
                       }
    
                       if (quote.Attributes.Contains("priceperunit"))
    
                       {
    
                           quote.Attributes.Remove("priceperunit");
    
                       }
    
                       if (quote.Attributes.Contains("quantity"))
    
                       {
    
                           quote.Attributes.Remove("quantity");
    
                       }
    
                       if (quote.Attributes.Contains("new_tldiscount"))
    
                       {
    
                           quote.Attributes.Remove("new_tldiscount");
    
                       }
    
                       if (quote.Attributes.Contains("manualdiscountamount"))
    
                       {
    
                           quote.Attributes.Remove("manualdiscountamount");
    
                       }
    
                       if (quote.Attributes.Contains("baseamount"))
    
                       {
    
                           quote.Attributes.Remove("baseamount");
    
                       }
    
                       if (quote.Attributes.Contains("new_vat"))
    
                       {
    
                           quote.Attributes.Remove("new_vat");
    
                       }
    
                       if (quote.Attributes.Contains("tax"))
    
                       {
    
                           quote.Attributes.Remove("tax");
    
                       }
    
                       service.Update(quote);
    
                   }
    
                   else
    
                   {
    
                       //First I get the base values that I need for the calculations
    
                       var quantity = (decimal)quote["quantity"];
    
                       var priceperunit = (Money)quote["priceperunit"];
    
                       var teamleader = (OptionSetValue)quote["new_tldiscount"];
    
                       //Then I calculate the manual discount and baseamount, for the further calculations
    
                       quote.Attributes["manualdiscountamount"] = new Money((priceperunit.Value * teamleader.Value / 100) * quantity);
    
                       var manualdiscountamount = (Money)quote.Attributes["manualdiscountamount"];
    
                       quote.Attributes["baseamount"] = new Money(priceperunit.Value * quantity);
    
                       var baseamount = (Money)quote["baseamount"];
    
                       //finally I calculate the tax
    
                       var VAT = (OptionSetValue)quote.Attributes["new_vat"];
    
                       var tax = (baseamount.Value - manualdiscountamount.Value) * VAT.Value / 100;
    
                       quote.Attributes["tax"] = new Money(tax); //tax
    
                       //I Update the quote product;
    
                       service.Update(quote);
    
                   }

    Can you tell me what I'm doing wrong?

  • Aileen Gusni Profile Picture
    44,524 on at

    Hi Georgi!!

    Can I recommend you to modifu your code:

    if! (check)

    {

      //Then put your own calculation here

    }

     so you no meed to do amything for the check one.

    And this is you apply in the order product.

    As I know if you have the quote then you create ordee based on the quote, crm will copy all.

    If the check is not working because you set as salesorderpricelocked condition, can you change to check if quoteid is not null or contains quoteid? Sorry I forgot thefield name you can do me a favoe to see in your org schema name.

    Hope this helps!

    Thanks

  • Community Member Profile Picture
    on at

    The fields that show whether the entity is locked are "salesorderispricelocked" for the Order, and "invoiceispricelocked", for the Invoice.

    Thanks for tip Aileen!

    Here is my code for the OrderProduct now:

    bool check = (bool)quote.Attributes["salesorderispricelocked"];
                    if (!check)
                    {
                        //First I get the base values that I need for the calculations
                        var quantity = (decimal)quote["quantity"];
                        var priceperunit = (Money)quote["priceperunit"];
                        var teamleader = (OptionSetValue)quote["new_tldiscount"];
    
                        //Then I calculate the manual discount and baseamount, for the further calculations
                        quote.Attributes["manualdiscountamount"] = new Money((priceperunit.Value * teamleader.Value / 100) * quantity);
                        var manualdiscountamount = (Money)quote.Attributes["manualdiscountamount"];
                        quote.Attributes["baseamount"] = new Money(priceperunit.Value * quantity);
                        var baseamount = (Money)quote["baseamount"];
    
                        //finally I calculate the tax
                        var VAT = (OptionSetValue)quote.Attributes["new_vat"];
                        var tax = (baseamount.Value - manualdiscountamount.Value) * VAT.Value / 100;
                        quote.Attributes["tax"] = new Money(tax); //tax
    
                        //I Update the quote product;
                        service.Update(quote);
                    }


    
    

    But it is still not perfect, because the plugin now runs only for the newly created Orders. If I create a Quote, and make an Order from it, this plugin is not working.

    Can you think of a way to fix that?

  • Aileen Gusni Profile Picture
    44,524 on at

    Hi Georgi,

    Can I ask you, this is how you register your plugin? On create or on update or both and which entity?  Quote product and order product?

    I think if you create an order from quote you dont need to recalculate the price again? Because crm has created for you with the same quote detail, so that I recommend you to use if check is false

  • Community Member Profile Picture
    on at

    They are both on Post-Update step -> when the fields VAT, Tax and Discount are changed. The calculations are on Quote Product, Order Product & Invoice Product.

    I understand that when I create the Quote product, and I do my calculations there, the CRM automatically sets the data to the newly created Order.

    I would like to be able to calculate the taxes/discount values for the quote product, order product and the invoice product, because my client wants it this way.

    I'll keep your advice in mind. Thank you.

  • Aileen Gusni Profile Picture
    44,524 on at

    Georgi! 

    I understand, you want to update all existing not only the newly created order from quote, so that I asked you when you trigger the plugin.

    Can you try to unlock the pricing request first using:

    UnlockSalesOrderPricingRequest

    Before you update the quote?
    Example:http://www.datazx.cn/Fv7p5a/xw-US/ggrgii9c-bi8r-ftxb-srgf-6xtfgfb9fcts/975-tgii-pwyv9z-asyxa-v76x7-uyp01w_nw_4v7p5_6w_9756xoxyvu5xw223rsdfd.html
    I hope this can help you, thanks



  • Community Member Profile Picture
    on at

    I followed the instructions, and I got an error saying "The entity is already unlocked!".

  • Verified answer
    Aileen Gusni Profile Picture
    44,524 on at

    Georgi,

    I think it is because of you put in the post updare.

    Btw I am confused these lines:

    Guid quoteProductID = (Guid)((EntitGuid quoteProductID = (Guid)((Entity)context.InputParameters["Target"]).Id;

              ColumnSet set = new ColumnSet();

              set.AllColumns = true;

              var quote = service.Retrieve("salesorderdetail", quoteProductID, set);y)context.InputParameters["Target"]).Id;

              ColumnSet set = new ColumnSet();

              set.AllColumns = true;

              var quote = service.Retrieve("salesorderdetail", quoteProductID, set);

    You already have the entity context here,  why you still need to retrieve the var quote and the var quote is the salesorderdetail?

    I think you can use pre update, just dont retrieve the var quote again.

    You can just use entity that becomes the context here, and then in the end of your calculation you no need to do service.update(quote)

    Because you are already in that entty.

    You can use quote = (Entity)context.InputParameters["Target"])

    Instead of you retrieve again and again you update, and put it in thw pre update nit post update

    And in the end if your code you no need to service.update quote again.

    Hope that can help you Georgi!

    Thanks

  • Community Member Profile Picture
    on at

    Thanks Aileen! You nailed it here. Everything is working fine now.

    This is my final code

    bool check = (bool)quote.Attributes["invoiceispricelocked"];
    
                   if (check)
    
                   {
    
                       UnlockSalesOrderPricingRequest unlockRequest = new UnlockSalesOrderPricingRequest();
    
                       unlockRequest.SalesOrderId = ((EntityReference)quote.Attributes["salesorderid"]).Id;
    
                       UnlockSalesOrderPricingResponse unlockRequestResponse = (UnlockSalesOrderPricingResponse)service.Execute(unlockRequest);
    
                   }
    
                   var quantity = (decimal)quote["quantity"];
    
                   var priceperunit = (Money)quote["priceperunit"];
    
                   var teamleader = (OptionSetValue)quote["new_tldiscount"];
    
                   //Then I calculate the manual discount and baseamount, for the further calculations
    
                   quote.Attributes["manualdiscountamount"] = new Money((priceperunit.Value * teamleader.Value / 100) * quantity);
    
                   var manualdiscountamount = (Money)quote.Attributes["manualdiscountamount"];
    
                   //quote.Attributes["baseamount"] = new Money(priceperunit.Value * quantity);
    
                   var baseamount = (Money)quote["baseamount"];
    
                   //finally I calculate the tax
    
                   var VAT = (OptionSetValue)quote.Attributes["new_vat"];
    
                   var tax = (baseamount.Value - manualdiscountamount.Value) * VAT.Value / 100;
    
                   quote.Attributes["tax"] = new Money(tax); //tax
    

    Thank you!

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Introducing the 2026 Season 1 community Super Users

Congratulations to our 2026 Super Stars!

Meet the Microsoft Dynamics 365 Contact Center Champions

We are thrilled to have these Champions in our Community!

Congratulations to the April Top 10 Community Leaders

These are the community rock stars!

Leaderboard > 🔒一 Microsoft Dynamics CRM (Archived)

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans