Loading...

Reopening Won Quotes in Dynamics 365 Sales Professional / Enterprise (C#)

Reopening Won Quotes in Dynamics 365 Sales Professional / Enterprise (C#)
Featured image of post Reopening Won Quotes in Dynamics 365 Sales Professional / Enterprise (C#)

After you’ve spent any length of time working with the out of the box sales process within Dynamics 365 Sales Professional / Enterprise, you get used to some of the behavioural quirks that can commonly cause you challenges during an implementation. A prime candidate for consideration here concerns the various types of tables used by the application. When we consider these in detail, such as the Opportunity, Quote and Order table, many of the assumptions that we make about how things work are instantly proven wrong, as these tables tend to operate by their own set of rules. For example, the ability to easily map across attributes between the different product line table types (Opportunity Product, Quote Product etc.) becomes a challenge; we must instead revert to using custom code instead to satisfy this requirement. Little things like this can exasperate both new and long-time users of the application and can often be quite frustrating when explaining to organisations using the software. 😅 Notwithstanding these gripes, I still do genuinely believe the Dynamics 365 Sales platform provides a solid base for organisations to leverage out of the box functionality, with the ability to customise further, as needed. And, when we start to bring into the equation more advanced capabilities, via things such as custom pricing plug-ins, you begin to move to an entirely new level when it comes to what we can do with this application.

A particularly annoying behaviour quirk I dealt with recently involved Quotes marked as Won in the system. The organisation I was working with needed to, on occasions, modify details of a Won Quote after the fact. For example, if the salesperson included the wrong item or the customer changed their order after confirming it. As we can observe in the below screenshot, we have no option on the ribbon to reactivate the Quote once Won in the application:

The only “out of the box” way of dealing with this was to create and re-input all details into a new Quote; not a viable and time-effective solution. Fortunately, there is a faster way we can achieve the objective by working through the following outline steps:

  1. First, we need to change the Status & Status Reason of the Quote back to Draft & In Progress.
  2. After we have made modifications to the Quote, it needs to be set as Active again.
  3. Once Active, the Quote then must be closed as Won. Microsoft wraps the logic for this within the WinQuote action, which we can call via the SDK or against the Microsoft Dataverse Web API.

These steps should be achievable via either a classic workflow or a Power Automate cloud flow automation, which would typically be our first preference for a requirement like this. However, in our case, we wanted to call these steps as part of a Custom API definition, which could then be called via a JavaScript action on a Ribbon button, similar to how I’ve described previously on the blog. And, most crucially, we needed all steps to be completed synchronously. So, given these requirements, we had to look at a solution leveraging C# instead.

With all of this in mind, let’s jump into the reason why you’re probably reading this post. 😉 To open a Won Quote using C#, we would need to write and execute the following code:

//TODO: Implement code to generate your IOrganizationService reference. For plug-ins, this would look something like this:
//IOrganizationServiceFactory serviceFactory = serviceProvider.GetService<IOrganizationServiceFactory>();
//IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

Guid quoteID = new Guid("9cc66ad5-2506-4394-854d-f60c35185d96");
Entity quote = new Entity("quote", quoteID);
quote["statecode"] = new OptionSetValue(0); // Draft
quote["statuscode"] = new OptionSetValue(1); // In Progress
service.Update(quote);

Then, once we’re ready to return the Quote to its previous state, we’d then run the following code to re-close the Quote. Note in particular, at this stage, we need to execute two separate actions and ensure they are completed successfully:

//TODO: Implement code to generate your IOrganizationService reference. For plug-ins, this would look something like this:
//IOrganizationServiceFactory serviceFactory = serviceProvider.GetService<IOrganizationServiceFactory>();
//IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

// We execute everything in a transaction, so we can rollback cleanly on failure
ExecuteTransactionRequest transactionRequest = new ExecuteTransactionRequest()
{
    Requests = new OrganizationRequestCollection(),
    ReturnResponses = true,
};
ExecuteTransactionResponse transactionResponse;

Guid quoteID = new Guid("9cc66ad5-2506-4394-854d-f60c35185d96");

// First, we need to Activate the Quote ...
Entity quote = new Entity("quote", quoteID);
quote["statecode"] = new OptionSetValue(1); // Active
quote["statuscode"] = new OptionSetValue(2); // In Progress

UpdateRequest updateRequest = new UpdateRequest()
{
    Target = quote,
};

transactionRequest.Requests.Add(updateRequest);

// ...with this done, now we can re-close the Quote as Won
Entity quoteClose = new Entity("quoteclose");
quoteClose["subject"] = "Quote Close" + DateTime.Now.ToString();
quoteClose["quoteid"] = quote.ToEntityReference();

WinQuoteRequest winQuoteRequest = new WinQuoteRequest()
{
    QuoteClose = quoteClose,
    Status = new OptionSetValue(-1),
};

transactionRequest.Requests.Add(winQuoteRequest);

transactionResponse = (ExecuteTransactionResponse)service.Execute(transactionRequest);
tracer.Trace($"Quote ID {quoteID} closed as won successfully!");

As stated earlier, if there wasn’t a need to execute these actions synchronously, then leveraging Power Automate cloud flows would be something I’d actively encourage instead, as both the ability to update and call action steps are supported via this tool. So before you act too gung-ho with the above, validate your approach against the requirements you are working against.

Regardless of the precise approach you take, it is a minor point of frustration that these types of steps are not natively supported within Dynamics 365 Sales. I understand why the system behaves like this, and there are arguable benefits to having a Quote locked after it’s been approved. However, I imagine the ability to make quick edits to a Won Quote is a common requirement across many different organisations, and expecting users to have to go through raising an entirely new Quote is both impractical and ludicrous in equal measure. It’s good to know that the underlying platform supports us in building workarounds such as this so that we are not left entirely adrift with a system that must fit around the business instead of the other way around.

Published on:

Learn more
The CRM Chap
The CRM Chap

Anything and everything to do with the #PowerPlatform, #MSDYN365, #Azure and more!

Share post:

Related posts

How Generative AI Is Revolutionizing Dynamics 365 Sales (2026 Guide)

Introduction The world of sales has changed dramatically over the past few years. Sales professionals are no longer expected to spend countles...

15 hours ago

Agentic CRM Readiness: What AI-First CRM Means for Microsoft Dynamics 365 Sales Teams

Sales leaders are starting to ask a different kind of CRM question. The question is no longer only whether Microsoft Dynamics 365 Customer Eng...

3 days ago

How AI Agents in Dynamics 365 Sales Are Changing the Way Sales Teams Work

Sales teams today are expected to do more, faster, with less manual effort and less guesswork. That is the exact gap Microsoft is closing with...

5 days ago

Tips to Export Data To Data Lake From MS Dynamics 365 CE And Use It In Power BI.

Integrating Dynamics 365 with Azure Data Lake allows you to continuously sync operational data for cost-effective big data analytics, AI workl...

7 days ago

Dynamics 365 Sales – Enable Automatic CRM Record Linking and Suggested Updates for Accounts, Contacts, and Leads

We are announcing the ability to enable Automatic CRM Record Linking and Suggested Updates for Accounts, Contacts, and Leads in Dynamics 365 S...

7 days ago

Dynamics 365 Sales – Copy opportunities for repeat deals

We are announcing the ability to use the copy opportunity feature to start a new one in Dynamics 365 Sales. This feature will reach general av...

9 days ago

Copilot Cowork and Dynamics 365 Customer Engagement: What It Means for Sales and Service Teams

Microsoft’s Copilot Cowork update matters because it moves AI closer to daily sales and service work inside Dynamics 365. Sellers, service man...

10 days ago

Dynamics 365 Sales – Stay on track at every deal stage with delta-first guidance in Sales Close Agent

We are announcing the ability to stay on track at every deal stage with delta-first guidance in Sales Close Agent in Dynamics 365 Sales. This ...

15 days ago

Dynamics 365 Sales – Chat about sales opportunity agent insights from inside Sales agent

We are announcing the ability to chat about sales opportunity agent insights from inside Sales agent in Dynamics 365 Sales. This feature will ...

15 days ago

Dynamics 365 Sales – Deploy multiple Sales Qualification Agents in a single environment

We are announcing the ability to deploy multiple Sales Qualification Agents in a single environment in Dynamics 365 Sales. This feature will r...

15 days ago
Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!
* Yes, I agree to the privacy policy