MS CRM : Direct status/status reasons updates work in Plugins, not in Workflows or Power Automate

What is Issue?

As developers working with MS Dynamics CRM, we often encounter tricky issues. Recently, I faced a situation where I needed to update the quote status from Draft to Closed and then revise it. To achieve this, I created a CRM Action that directly updated the quote status to Closed. When this action was invoked from a plugin (triggered by an automated process), it worked as expected.

However, another requirement was to create an on‑demand workflow that performed the same process as the automated plugin. While the workflow logic followed the plugin’s behavior, the issue arose when executing the action. Running the on‑demand workflow displayed the error: “Quote status cannot be changed to Closed. Please use the CloseQuoteRequest instead.”. In other words, the status update succeeded when called from a plugin but failed when executed through an on‑demand workflow.

The Reason-

In CRM, on‑demand workflows, plugins, and Power Automate each have their own execution behavior. Additionally, certain actions and status changes—such as Revise Quote, Close Quote, or Win/Lose Opportunity—are governed by CRM‑defined business rules that must be strictly followed.

Solution-

To resolve such issues, it is important to understand the different behaviors of plugins, workflows, and Power Automate flows in CRM.

When an action is executed from a plugin, it runs within the CRM pipeline and in the same transaction, where the update is treated as an UpdateRequest. Plugins can sometimes bypass certain UI validations and allow direct updates. Although plugins run in the calling user’s context, the organization service controls the transaction and leverages SDK messages internally. This makes it possible to perform update operations in special cases.

In contrast, when updates are performed through a workflow or Power Automate flow, they run in the workflow engine, which operates outside the transaction and enforces stricter validations. Additionally, when a workflow or flow is triggered on‑demand by a user, CRM applies UI validations and executes in the actual user context. These validations ensure that actions follow the defined business process sequence.

For example, when revising a quote, CRM closes the current quote and creates a new draft version for editing. It automatically increments the revision ID and maintains audit history.

Even though plugins may allow direct status or status reason updates, it is always recommended to use the appropriate CRM message (such as CloseQuoteRequest) within plugins as well. This ensures consistency and adherence to the same business process across workflows, plugins, and Power Automate flows.

Below are some of the special messages that should be used to avoid issues like this.

Module‑wise Entity List with Special Messages

📞 Service Module

EntityMessage/RequestPurpose
Case (Incident)CloseIncidentRequestCloses a case with resolution details

💼 Sales Module

EntityMessage/RequestPurpose
OpportunityWinOpportunityRequestMarks opportunity as won (requires revenue, date)
OpportunityLoseOpportunityRequestMarks opportunity as lost (requires reason, date)
QuoteCloseQuoteRequestCloses a quote (won/lost)
QuoteReviseQuoteRequestCreates a new version of a quote for editing
Sales OrderCancelSalesOrderRequestCancels a sales order
Sales OrderFulfillSalesOrderRequestMarks a sales order as fulfilled
InvoiceCancelInvoiceRequestCancels an invoice
InvoiceSettleInvoiceRequestMarks an invoice as paid/settled
ContractCancelContractRequestCancels a contract
ContractRenewContractRequestRenews a contract
ContractCloneContractRequestCreates a copy of a contract

⚙️ Common / Cross‑Module

Entity TypeMessage/RequestPurpose
Standard Entities (without dedicated close/cancel messages)SetStateRequestChanges state/status for entities that don’t have a dedicated message
Custom EntitiesUpdateRequestModern alternative for changing state/status safely
Custom EntitiesSetStateRequestUsed for custom entities when no special message exists

This is another key factor to consider when choosing a plugin over a workflow. Check out the rest of this blog for more differences between plugins and workflows.

Hope this will help…
Enjoy MS CRM!!!

Follow on Facebook- FB: MSCRM16Tech

Power Apps : Working with Tables with N:N (Many-to-Many) Relationships

What is Issue?

While working on Canvas Apps in PowerApps, how to deal with Dataverse Tables with N:N relationships ?

The Reason-

In PowerApps, while working with multiple Dataverse tables it is easy to write expressions/formulas with simple columns as well as 1:N (1-to-Many) relationships between tables. But when there are Dataverse tables with N:N (Many-to-Many) relationships, the intersecting table is not available to use directly.

Solution-

Let’s first understand Dataverse tables with N:N (Many-to-Many) relationship.

Suppose, we have 2 tables “Contact” and “Software Service”.

“Software Service” table have lookup “Service Owner” that creates 1:N (1-to-Many) relationship between “Contact” and “Software Service” tables.

“Software Service” also needs multiple “Service Engineers”, So I have created N:N (Many-to-Many) relationship between “Software Service” and “Contact” tables.

Now I have scenario, In Canvas App I have to show list “Software Services” associated with “Contact” as either “Service Owner” or “Service Engineer”. And when it is “Service Engineer”, display his name in “Service Engineer” Column in the list else the “Service Engineer” Column will be blank.

Suppose, “Service 1” is having “Prashant Parkale” (contact record) as added “Service Owner” and contact records “Iron Man” and “Spider Man” added as “Service Engineers”.

If I open “Prashant Parkale” contact record, It should list “Service 1” record with “Service Engineer” column as blank.

If I open “Iron Man” or “Spider Man” contact record, it should list “Service 1” record with “Service Engineer” column as respective contact record name.

Let’s create Canvas App to embed in Contact form.

  • Create Canvas App with “ModelDrivenFormIntegration” enabled to get current contact record context.
  • Define contactRecord variable in Formula in App.
contactRecord=LookUp(Contacts,Contact= [@ModelDrivenFormIntegration].Item.Contact);
  • Add Gallery control in App Screen
  • Add below formula Items of Gallery control
Filter(
    'Software Services',
    Or(
        'Service Owner'.Contact = contactRecord.Contact,
        'Software Service' in Distinct(
            contactRecord.'Software Services (gen_SoftwareService_Contact_Contact)',
            'Software Service'
        )
    )
)
  • ‘Service Owner’.Contact = contactRecord.Contact will filter out “Software Service” Records where current contact record is added as “Service Owner”
  • contactRecord.’Software Services (gen_SoftwareService_Contact_Contact)’ is retrieving all the “Software Service” records related to current contact record with N:N (Many-to-Many) relationship gen_SoftwareService_Contact_Contact. This I am using as “Service Engineers”. But the fields are not directly accessible in the relationship. So utilizing the Distinct formula to list out all the “Software Service” Id to compare in Filter formula.
  • Please note- when you have to access “Software Service” N:N(Many-to-Many) records for Contact record it can be accessible as contactRecord.’Software Services (gen_SoftwareService_Contact_Contact)’ and if you have to access all contact N:N (Many-to-Many) records for “Software Service” record it can be accessible as serviceRecord.Contacts.
  • Above formula will list out all the “Software Service” records when current contact record when it is added as Service Owner or Service Engineer.
  • Now another requirement to display “Service Engineer” Name for “Software Service” when contact record is associated as “Service Engineer” in N:N (Many-to-Many) relationship.
  • Add below formula in Text for “lbl_serviceEngineer” label added in Gallery control
If(
    contactRecord.Contact in Distinct(
        ThisItem.Contacts,
        Contact
    ),
    contactRecord.'Full Name',
    Blank()
)
  • As explained in Gallery control Item formula, ThisItem.Contacts represents the all N:N (Many-to-Many) “Contact” records related to ThisItem, where ThisItem represent current “Software Service” record.
  • If current opened contact record is listed in all associated “Contacts” to “Software Service” (ThisItem), then show current contact record Full Name, else show blank.
  • In all above formulas, I have used Distinct formula to overcome inaccessibility of related record fields as well as any delegation errors in formula.

In above scenario, we learnt how to access related tables and use related table columns in regular formulas (using Distinct) when tables are in N:N (Many-to-Many) relationships.

Hope this will help…
Enjoy MS CRM!!!

Follow on Facebook- FB: MSCRM16Tech

Power Apps : Form Controls always remains Unsaved

What is Issue?

Power Apps has many useful controls that makes development faster and easier. Form control is one of the control using which user can create, edit or view records. With minimal configuration these forms can be implemented quickly in Power Apps.

I had one scenario that where user want to save form automatically when user moves from one tab to another tab in app. The condition was automatic save will be only if form is having unsaved changes.

When I implemented this, even if user has not changed any value on the form, form was submitted and saved the changes every time.

The Reason-

I used condition If(Form.Unsaved, submitForm(Form1)). But every time Form was submitted and I noticed the form was always unsaved.

Solution-

While working with Edit Forms, You need to consider few things-

  1. Use of Unsaved, Updates and LastSubmit properties of form
    • Unsaved – Form1.Unsaved : This will either true or false. If any field on form updated, It will return true.
    • Updates – Form1.Updates : This holds the local object with updated values of the form1 item before submit.
    • LastSubmit – Form1.LastSubmit : This returns object of form1 item which was last submitted.
  2. Setting defaults on form fields
    • Lots of the fields user want to set with default values manually. But there is side effect on use of Unsaved property of Form if not set properly.
    • Setting default also triggers the OnChange event of the field whenever form item gets changed or Field value is defaulted with some condition.

So In my case, I observed that I set few fields with default values and that caused the form remains in Unsaved always true.

Consider, I have gallery form Account and edit form that allows me to edit the Account selected in gallery.

There is the Label in red that reads “Form has unsaved changed :false”.
For this label I used formula in Text : “Form has unsaved changes : ” & Form1.Unsaved

Initially, I didn’t set any manual default value for Primary Contact field.
That shows, Form1.Unsaved = false

Now, On Account, I want to set the default Primary Contact as Contact with email address “p@p.com”.

As soon as I added default value to the form, Label started showing “Form has unsaved changes : true”

And just to check whether this also triggers the OnChange event of the field, I added Notify(“OnChange event Triggered”);

I ran the App and then changed the gallery selection, It started showing the unsaved changes and OnChange Notification on screen.

To overcome Unsaved changes I modified a DefaultItems property of Primary contact-
If(IsBlank(Parent.Default),First(Filter(Contacts, Email=”p@p.com”)),Parent.Default)

What I am doing here is, if there is already value in Primary Contact field, don’t set Contact with email address “p@p.com”.

Now let’s try changing Gallery Item, No unsaved changes any more, but as form Item is changed, OnChange event of that field will trigger.

So, be careful while adding some logic in OnChange event of field when Form Item is going to change.

Hope this will help…
Enjoy MS CRM!!!

Follow on Facebook- FB: MSCRM16Tech