Thursday, June 26, 2025

Moving BPF Stages Backward in Dynamics 365 with JavaScript

A step-by-step guide to dynamically control BPF stages using client-side JavaScript

Introduction

Business Process Flows (BPFs) in Microsoft Dynamics 365 and Dataverse guide users through predefined stages, but moving backward isn’t supported natively in the BPF designer. For example, moving from stage 2 to stage 1 based on a field value like "No" requires custom logic. JavaScript, executed as a Web Resource in a model-driven app form, provides a client-side solution to update the activestageid field. In this post, we’ll show you how to create a JavaScript Web Resource to move BPF stages dynamically.

Why This Matters: JavaScript enables real-time, client-side BPF stage control, offering immediate feedback to users without server-side dependencies.

The Requirement

For an account entity with a BPF named "Account Process Flow," we need to:

  • Monitor a choice field (new_decisionfield).
  • If "Yes," move from stage 2 to stage 3.
  • If "No," move back to stage 1.

We’ll use JavaScript to detect field changes and update the BPF instance’s activestageid using the Web API.

Step-by-Step Guide

Step 1: Gather Required Information

Before writing the JavaScript, collect these details:

  • BPF Stage IDs: Find the GUIDs for stage 1 and stage 3 in the processstages table using XrmToolBox or FetchXML. Example:
    • Stage 1: 12345678-1234-1234-1234-1234567890ab
    • Stage 3: 98765432-4321-4321-4321-9876543210ba
  • BPF Instance Table: Identify the logical name (e.g., accountprocessflows).
  • Field Values: Confirm the new_decisionfield OptionSet values (Yes = 100000000, No = 100000001).

Step 2: Create the JavaScript Web Resource

Create a JavaScript file to handle the onchange event of new_decisionfield and update the BPF stage:

var BPFStageMover = BPFStageMover || {};

BPFStageMover.moveBPFStage = function (executionContext) {
    var formContext = executionContext.getFormContext();

    // Get the decision field value
    var decisionField = formContext.getAttribute("new_decisionfield");
    if (!decisionField) {
        console.log("Decision field not found.");
        return;
    }

    var decisionValue = decisionField.getValue();
    if (decisionValue === null) {
        console.log("Decision field is null.");
        return;
    }

    // Define stage GUIDs (replace with actual stage IDs)
    var stage1Id = "12345678-1234-1234-1234-1234567890ab"; // Stage 1 GUID
    var stage3Id = "98765432-4321-4321-4321-9876543210ba"; // Stage 3 GUID

    // Determine new stage based on decision value
    var newStageId = decisionValue === 100000000 ? stage3Id : stage1Id; // Yes: 100000000, No: 100000001

    // Get the account ID
    var accountId = formContext.data.entity.getId().replace(/[{}]/g, "");

    // Retrieve the BPF instance
    Xrm.WebApi.retrieveMultipleRecords("accountprocessflow", `?$select=activestageid&$filter=regardingobjectid eq ${accountId}`)
        .then(
            function success(result) {
                if (result.entities.length === 0) {
                    console.log("No BPF instance found for account: " + accountId);
                    return;
                }

                var bpfInstance = result.entities[0];
                var currentStageId = bpfInstance.activestageid;

                // Only update if the stage is different
                if (currentStageId !== newStageId) {
                    var updateData = {
                        "activestageid@odata.bind": `/processstages(${newStageId})`
                    };

                    // Update the BPF instance
                    Xrm.WebApi.updateRecord("accountprocessflow", bpfInstance.accountprocessflowid, updateData)
                        .then(
                            function success() {
                                console.log("BPF stage updated to: " + newStageId);
                                formContext.data.refresh(true); // Refresh form to reflect stage change
                            },
                            function error(error) {
                                console.error("Error updating BPF stage: " + error.message);
                                Xrm.Utility.alertDialog("Error updating BPF stage: " + error.message);
                            }
                        );
                } else {
                    console.log("No stage change needed.");
                }
            },
            function error(error) {
                console.error("Error retrieving BPF instance: " + error.message);
                Xrm.Utility.alertDialog("Error retrieving BPF instance: " + error.message);
            }
        );
};

Step 3: Register the Web Resource

Add the JavaScript to your Dynamics 365 form:

  1. Upload Web Resource:
    • In your solution, create a Web Resource named new_moveBPFStage (Type: JavaScript).
    • Upload the moveBPFStage.js file.
  2. Add to Form:
    • Open the account form in the form editor.
    • Add new_moveBPFStage to the form libraries.
    • Register the BPFStageMover.moveBPFStage function on the onchange event of new_decisionfield, passing the execution context.
  3. Publish: Save and publish the form and solution.

Step 4: Test the JavaScript

Open an account record, change new_decisionfield to "Yes" or "No," and verify the BPF stage moves to stage 3 or stage 1. Use browser developer tools (F12) to check console logs for errors.

Pro Tip: Use the browser’s developer tools to debug JavaScript errors. Add console logs for key variables (e.g., decisionValue, currentStageId) to trace execution.

Key Notes

  • Replace GUIDs: Update stage GUIDs in the JavaScript with actual values from your processstages table.
  • BPF Table: Use the correct logical name for your BPF instance table (e.g., accountprocessflows).
  • Option Set Values: Adjust 100000000 (Yes) and 100000001 (No) to match your field’s values.
  • Permissions: Ensure the user has read/write access to the accountprocessflows table.

Conclusion

Using JavaScript in a Dynamics 365 form, you can dynamically move BPF stages backward or forward, overcoming the designer’s limitations. This client-side approach provides real-time feedback and is ideal for interactive user experiences. Try it out, and share your feedback or questions in the comments!

© 2025 Your Name. All rights reserved.

No comments:

Post a Comment

Power Automate Optimization: Filter Rows vs Trigger Conditions - When and Why to Use Each

Filter Rows vs Trigger Conditions in Power Automate Filter Rows vs Trigger Conditions in Power Automate Why your flow...