Cancel Running workflows prior to saving an item.

This is a problem for workflows that delay until a certain date before sending out reminders. The user creates an item with 1/27/2018 as the due date and the workflow starts waiting until 1/27/2018 to send out a reminder. If the user then edits the item and changes the date to 1/27/2018, no new workflow is

Started, and the running workflow is unaware the date has changed.

This is typically resolved by  setting up a workflow to run as part of a retention policy. This can sometimes be troublesome if you want to send out multiple reminders –say  7 days before due and 3 days before due—because you need to set up calculated fields with the reminder dates.

But there is another way.  With a bit of JSOM code, a sharepoint edit form can be made to cancel

the running workflow prior to saving the item to the list. This way a new workflow gets started when you save the item (provided you have the workflow configured to run when and item is added AND when an item is changed.

If you are using an SPFX webpart as your edit form (which is unfortunately not currently supported on modern lists), the following code can be used to cancel the running workflow just prior to saving the new version.

 private async getWorkFlowDefinitionByName(workflowDeploymentService: SP.WorkflowServices.WorkflowDeploymentService, workFlowName: string): Promise {
    let context = workflowDeploymentService.get_context();
    let wfDefinitions = workflowDeploymentService.enumerateDefinitions(true);
    context.load(wfDefinitions);
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
        resolve();
      }, (error) => {
        console.error("an error occured getting workflow definitions");
        console.log(error);
        reject();
      });
    });
    let foundDefinition: SP.WorkflowServices.WorkflowDefinition;
    let defEnum = wfDefinitions.getEnumerator();
    while (defEnum.moveNext()) {
      const wfDefinition = defEnum.get_current();
      if (wfDefinition.get_displayName() === workFlowName) {
        foundDefinition = wfDefinition;
        break;
      }
    }
    return Promise.resolve(foundDefinition);
  }
  private async getWorkFlowSubscriptionByDefinitionIdListId(workflowSubscriptionService: SP.WorkflowServices.WorkflowSubscriptionService, workFlowDefinitionId: string, listId): Promise {
    let context: SP.ClientRuntimeContext = workflowSubscriptionService.get_context();
    let wfSubscriptions: SP.WorkflowServices.WorkflowSubscriptionCollection =
      workflowSubscriptionService.enumerateSubscriptionsByList(listId);
    context.load(wfSubscriptions);
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
        resolve();
      }, (error) => {
        console.error("an error occured gettin workflow subscriptions");
        console.log(error);
        reject();
      });
    });
    if (!wfSubscriptions) {
      alert("Failed to load workflow subscriptsion. Running workflows were not cancelled. This can happen if the Office 365 workflow service is unavailable.");
      console.error("Failed to load Workflow instances.");
      return Promise.reject("Failed to load Workflow instances.");
    }
    let foundSubscription: SP.WorkflowServices.WorkflowSubscription;
    let subscriptionEnum = wfSubscriptions.getEnumerator();
    while (subscriptionEnum.moveNext()) {
      const wfSubscription: SP.WorkflowServices.WorkflowSubscription = subscriptionEnum.get_current();
      if (wfSubscription.get_definitionId().toString().toUpperCase() === workFlowDefinitionId.toString().toUpperCase()) {
        foundSubscription = wfSubscription;
        break;
      }
    }
    return Promise.resolve(foundSubscription);
  }
  private async cancelRunningWorkflows(ItemId: number, listId: string, workflowName: string): Promise {
    if (!workflowName) {
      return Promise.resolve();
    }
    var context = SP.ClientContext.get_current();
    // get all the workflow service managers
    var workflowServicesManager: SP.WorkflowServices.WorkflowServicesManager = SP.WorkflowServices.WorkflowServicesManager.newObject(context, context.get_web());
    var workflowInstanceService: SP.WorkflowServices.WorkflowInstanceService = workflowServicesManager.getWorkflowInstanceService();
    var workflowSubscriptionService: SP.WorkflowServices.WorkflowSubscriptionService = workflowServicesManager.getWorkflowSubscriptionService();
    var workflowDeploymentService: SP.WorkflowServices.WorkflowDeploymentService = workflowServicesManager.getWorkflowDeploymentService();
    //Get all the definitions from the Deployment Service, or get a specific definition using the GetDefinition method.
    let wfDefinition: SP.WorkflowServices.WorkflowDefinition = (await this.getWorkFlowDefinitionByName(workflowDeploymentService, workflowName));
    if (!wfDefinition) {
      console.error("Coold not find workflow Definition for workflow named : " + workflowName);
      alert("Coold not find workflow Definition for workflow named : " + workflowName);
      return Promise.resolve();
    }
    let wfDefinitionId: string = wfDefinition.get_id();
    // get the subscription for the list
    let wfSubscription: SP.WorkflowServices.WorkflowSubscription =
      await this.getWorkFlowSubscriptionByDefinitionIdListId(workflowSubscriptionService, wfDefinitionId, listId);
    if (!wfSubscription) {
      console.error("Could not find a subscription for  workflow named : " + workflowName + " ib the TR List");
      alert("Could not find a subscription for  workflow named : " + workflowName + " ib the TR List");
      return Promise.resolve();
    }
    let wfSubscriptionId: string = wfSubscription.get_id().toString().toUpperCase();
    let wfInstances: SP.WorkflowServices.WorkflowInstanceCollection = workflowInstanceService.enumerateInstancesForListItem(listId, ItemId);
    context.load(wfInstances);
    await new Promise((resolve, reject) => {
      context.executeQueryAsync((x) => {
        resolve();
      }, (error) => {
        console.log(error);
        reject();
      });
    });
    if (!wfInstances) {
      debugger;
      alert("Failed to load workflow instances. Running workflows were not cancelled. This can happen if the Office 365 workflow service is unavailable.");
      console.error("Failed to load Workflow instances.");
      return Promise.resolve();
    }
    var instancesEnum = wfInstances.getEnumerator();
    let runningInstance;
    while (instancesEnum.moveNext()) {
      var instance = instancesEnum.get_current();
      let instanceSubscriptionId = instance.get_workflowSubscriptionId().toString();
      let instanceStatus = instance.get_status();
      if (instanceSubscriptionId.toUpperCase() === wfSubscriptionId && instanceStatus === 1) {
        runningInstance = instance;
      }
    }
    if (runningInstance) {
      workflowInstanceService.terminateWorkflow(runningInstance);
      await new Promise((resolve, reject) => {
        context.executeQueryAsync((x) => {
          console.log("Workflow Termination Successful");
          resolve();
        }, (error) => {
          console.log(error);
          debugger;
          console.error("Failed to terminate workflow.");
          resolve();
        });
      });
    }
  }

With the above methods in place , you just need to call


if (originalReuiredDate != tr.RequiredDate) {
await this.cancelRunningWorkflows(itemId, listId, workflowName).then((x) => {
console.log("Workflow has been terminated");
});
}

prior to saving your list item. If there is an instance of the workflow already running , it will be canceled and a new workflow will start once your item is saved.

Advertisements
This entry was posted in react, spfx, Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s