It still remains the Won Quote there…
*Before Close the Opportunity
*After Close the Opportunity as Lost
*As you can see the Quotes are still having Won Status..Which our customer does want.
Sometimes we have requirement to Close All Quotes as Lost as well if the Opportunity was Lost in order to keep in sync each other.
*Remember you cannot close Opportunity if you still have Draft or Active Quotes
So here is the code to Close All Quotes as Lost or Canceled when an Opportunity was Lost
#region variables Guid guidOpportunityId; List<Quote> quoteToBeCanceled = new List<Quote>(); #endregion if (context.InputParameters.Contains("OpportunityClose") && context.InputParameters["OpportunityClose"] is Entity) { //create entity context Entity entity = (Entity)context.InputParameters["OpportunityClose"]; if (entity.LogicalName != OpportunityClose.EntityLogicalName) { return; } try { //create target entity as early bound OpportunityClose TargetEntity = entity.ToEntity<OpportunityClose>(); #region Close all Quotes if Opportunity was Lost //get the Opportunity Id guidOpportunityId = TargetEntity.OpportunityId.Id; //get all quote related to this Opportunity quoteToBeCanceled = GetQuoteToBeCanceled(baseContext, guidOpportunityId); if (quoteToBeCanceled != null) { foreach (Quote quote in quoteToBeCanceled) { if (quote.StateCode.Value == QuoteState.Won || quote.StateCode.Value == QuoteState.Closed) { //have to be deactivated first as Draft ConvertToDraftQuote(service, quote); //have to be activated first before it closes the Quote ActivateDraftQuote(service, quote); } if (quote.StateCode.Value == QuoteState.Draft) { //have to be activated first ActivateDraftQuote(service, quote); } //finally Close the Quote (Active Quote will be directly closed //You can Choose Canceled or Lost //as Canceled ExecuteCloseQuoteAsCanceled(service, quote, quote.QuoteNumber); //as Lost ExecuteCloseQuoteAsLost(service, quote, quote.QuoteNumber); } } #endregion } catch (Exception ex) { throw new InvalidPluginExecutionException(ex.Message); } }
Query to Get All related Quotes from Opportunity
//return all of the Quote from related Opportunity to be Canceled/Lost //I am using LINQ here.. public List<Quote> GetQuoteToBeCanceled(KonicaBaseContext baseContext, Guid guidOpportunityId) { //get the Quote to be canceled in same Opportunity, exclude the wonQuote var quoteToBeCanceled = from x in baseContext.QuoteSet orderby x.CreatedOn descending where x.OpportunityId.Id == guidOpportunityId select x; if (quoteToBeCanceled.ToList().Count > 0) { return quoteToBeCanceled.ToList<Quote>(); } else { return null; } }
And the Helper Code…
Which you can use this code (C# code) for other purposes, such as How to Activate a Draft Quote, and so on..
The helper code consists of:
1. Convert Quote back to Draft
2. Activate a Draft Quote
3. Close the Quote as Closed - Canceled
4. Close the Quote as Closed – Lost
Convert Quote back to Draft
//make the Quote as Draft first public void ConvertToDraftQuote(IOrganizationService service, Quote erQuote) { // Activate the quote SetStateRequest activateQuote = new SetStateRequest() { EntityMoniker = erQuote.ToEntityReference(), State = new OptionSetValue((int)QuoteState.Draft), Status = new OptionSetValue((int)1) //in progress }; service.Execute(activateQuote); }
Activate a Draft Quote
//activate the Draft Quote public void ActivateDraftQuote(IOrganizationService service, Quote erQuote) { // Activate the quote SetStateRequest activateQuote = new SetStateRequest() { EntityMoniker = erQuote.ToEntityReference(), State = new OptionSetValue((int)QuoteState.Active), Status = new OptionSetValue((int)2) //in progress }; service.Execute(activateQuote); }
Close the Quote as Closed - Canceled
//And finally Close the Quotes as Canceled public void ExecuteCloseQuoteAsCanceled(IOrganizationService service, Quote erQuote, string strQuoteNumber) { CloseQuoteRequest closeQuoteRequest = new CloseQuoteRequest() { QuoteClose = new QuoteClose() { Subject = String.Format("Quote Closed (Canceled) - {0} - {1}", strQuoteNumber, DateTime.Now.ToString()), QuoteId = erQuote.ToEntityReference() }, Status = new OptionSetValue(6) }; service.Execute(closeQuoteRequest); }
Close the Quote as Closed - Lost
public void ExecuteCloseQuoteAsLost(IOrganizationService service, Quote erQuote, string strQuoteNumber) { CloseQuoteRequest closeQuoteRequest = new CloseQuoteRequest() { QuoteClose = new QuoteClose() { Subject = String.Format("Quote Closed (Opportunity Lost) - {0} - {1}", strQuoteNumber, DateTime.Now.ToString()), QuoteId = erQuote.ToEntityReference() }, Status = new OptionSetValue(5) }; service.Execute(closeQuoteRequest); }
Register the Plugin:
Message: Lose
Primary Entity: opportunity
Event: Post-operation
Execution Mode: Synchronous
Result:
Hope this helps you!
Thanks.
This doesn't quite work.. Even if you register this plugin, the 'close as lost' still shows you the warning. When, exactly, will this code execute?
ReplyDeleteDid you ever receive a reply to your post or get this to work?
DeleteHi,
ReplyDeleteI have created script to close all the quotes of opportunity. please refer the below link.
https://venkykolagani.wordpress.com/2015/06/10/close-all-related-quotes-of-opportunity-as-lost-crm-201120132015-javascript/?preview_id=14
Hope it will help you.
Thanks,
Venky.
Great post!
ReplyDeleteWill this work in CRM 2016/365?
Also, we are trying to get an addition to this behaviour.
If an opportunity is Closed as Won, all Won Quotes stay untouched. Open Quotes are closed as Lost. Would that be possible?
Great post!
ReplyDeleteشركة مكافحة النمل الابيض بالرياض
This plugin is not firing in D365 . Is there any addition we need to do ?
ReplyDeleteInstead at the first place getting the error message provided in the post.
ReplyDeleteI'm glad to have found your website and this piece of writing is excellent. I didn't find a lot of unique content on your blog. I'm certain I'll return to check out some of your other significant future postings.
ReplyDelete