Business Rule keeps changing Project task state when Project state changes

Bob
Giga Contributor

I created a new Business Rule to prevent Projects being closed without closing the associated Project Tasks. The BR does stop the update from occurring, but the problem I am running into is that the Project Tasks are then all set to Closed Completed by some ninja BR or UI Action, which I am failing to find.

Things that I have tried:

  • Changing the order of my BR to 0 or 900, it didn't work.
  • I used the debugging tool and tried turning off every BR that I thought might be changing the state of the task to no avail.
  • Looked over all the UI Policies and Client Scripts finding nothing of use.

My overall goal for this change is:

When closing a project (State Change Closed Complete or Incomplete) - Using a BR to complete action

  Look at all the child Project Tasks

            If any project task has an open Time card

                      Stop check

                      Stop update

                      Reset State to previous

                      Alert user

            Else finish update of project

Might be an easier way of tackling this than a couple of BRs:

  1. Prevent closing project if open project tasks or time cards
  2. Prevent closing project tasks if open time cards
1 ACCEPTED SOLUTION

Ok, here's the full client script and script includes. It should be pretty easy to take the code you need:


Script Includes:


var ProjectUtilsAjax = Class.create();


ProjectUtilsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {


     


      getOpenProjectTaskCount: function(parentID) {


              var projectID = this.getParameter('sysparm_project_id');


             


              var projectTaskCount = new GlideRecord('pm_project_task');


              projectTaskCount.addActiveQuery();


              projectTaskCount.addQuery('parent', projectID);


              projectTaskCount.query();


             


              return projectTaskCount.getRowCount();


      },


     


      getProjectCounts: function() {


              var projectID = this.getParameter('sysparm_project_id');


             


              var ptc = this.getRelatedRecordCount(projectID, 'pm_project_task', 'parent');


              var ic   = this.getRelatedRecordCount(projectID, 'issue', 'parent');


              var rc   = this.getRiskCount(projectID);


              var crc = this.getRelatedRecordCount(projectID, 'change_request', 'parent');


              var tcc = this.getTimeCardCount(projectID);


              var ed   = this.getEffortDeviation(projectID);


              var rp   = this.hasResourcePlans(projectID);


              var d = (ed > 10);


             


              gs.log("Variance:   " + d + " = " + ed);


             


              var relatedRecordCounts = {


                      "pm_project_task":ptc,


                      "issue":ic,


                      "risk":rc,


                      "change_request":crc,


                      "time_card":tcc,


                      "effort_deviation":ed,


                      "effort_deviated":d,


                      "open_resource_plan":rp


              };


             


              return JSON.stringify(relatedRecordCounts);


      },


     


      getRelatedRecordCount: function(projectID, tableName, fieldName) {


              var projectTaskCount = new GlideRecord(tableName);


              projectTaskCount.addActiveQuery();


              projectTaskCount.addQuery(fieldName, projectID);


              projectTaskCount.query();


             


              return projectTaskCount.getRowCount();


      },


     


      getRiskCount: function(projectID) {


              var riskGR = new GlideRecord('risk');


              riskGR.addQuery('state', '!=', 'closed');


              riskGR.addQuery('task', projectID);


              riskGR.query();


             


              return riskGR.getRowCount();


      },


     


      getTimeCardCount: function(topTaskID) {


              var timeCardCount = 0;


             


              var projectRecords = new GlideRecord('planned_task');


              projectRecords.addQuery('top_task', topTaskID);


              projectRecords.query();


              while (projectRecords.next()) {


                      var timeCard = new GlideRecord('time_card');


                      timeCard.addQuery('state', '!=', 'Processed');


                      timeCard.addQuery('state', '!=', 'Approved');


                      timeCard.addQuery('task', projectRecords.sys_id);


                      timeCard.query();


                     


                      var tcc = timeCard.getRowCount();


                      timeCardCount += tcc;


                     


              }


             


              return timeCardCount;


      },


     


      getEffortDeviation: function(topTaskID) {


              var pu = new ProjectUtils();


     


              return pu.getEffortDeviation();


      },


     


      hasResourcePlans: function() {


              var projectID = this.getParameter('sysparm_project_id');


              gs.log("hasResourcePlans projectID:   " + projectID);



              var sysIDs = "";


              var projectGR = new GlideRecord('planned_task');


              if (projectGR.get(projectID)) {


                      gs.log("hasResourcePlans short_description:   " + projectGR.short_description);


                      var tree = new SNC.NestedIntervalUtilities("planned_task");


                      gs.log("hasResourcePlans tree:   " + tree);


                      var descendants = tree.getDescendants(projectGR, true);


                      gs.log("hasResourcePlans descendants:   " + descendants);



                      while (descendants.next()) {


                              sysIDs += ", " + descendants.sys_id;


                      }


              }


              gs.log("hasResourcePlans sysIDs:   " + sysIDs);



             


              var resourcePlanGR = new GlideRecord('resource_plan');


              resourcePlanGR.addQuery("task", "IN", sysIDs);


              resourcePlanGR.addQuery("state", "!=", 7);


              resourcePlanGR.addQuery("state", "!=", 8);


              resourcePlanGR.query();


              if (resourcePlanGR.hasNext()) {


                      return true;


              }


             


              return false;


      },


     


      getTaskDescendants: function(task, sysIds) {


              var children = new GlideRecord("task");


              children.addQuery("parent", task.sys_id);


              children.query();


              while (children.next()) {


                      sysIds += ", " + children.sys_id;


                      sysIds = this.getTaskDescendants (children, sysIds);


              }


              return sysIds;


      },


     


      type: 'ProjectUtilsAjax'


});



Onsubmit Client Script on Project Table:


if(typeof gFormAlreadySubmitted == "undefined" ||   typeof gFormAlreadySubmitted == "null")


      gFormAlreadySubmitted = false;



function onSubmit() {


      if (gFormAlreadySubmitted)


              return true;


     


      // If not Close/Skip/Cancel then bail


      var state = g_form.getValue('state');


      if (state != 3 && state != -7 && state != 4)


              return true;


     


      // If New Record bail


      if (g_form.isNewRecord())


              return true;


     


      // Project sys_id


      var projectID = gel('sys_uniqueValue').value;


     


      // Open Risk/Issue/Change Request


      ga = new GlideAjax('ProjectUtilsAjax');


      ga.addParam('sysparm_name', 'getProjectCounts');


      ga.addParam('sysparm_project_id', projectID);


      ga.getXMLWait();


      var projectCounts = ga.getAnswer();


      var pco = JSON.parse(projectCounts);


      var ptc = pco['pm_project_task'];


      var ic   = pco['issue'];


      var rc   = pco['risk'];


      var crc = pco['change_request'];


      var tcc = pco['time_card'];


     


      if (ic != 0 || rc != 0 || crc != 0 || tcc != 0) {


              var msg = "The following records must be closed before the Project can be closed:\n";


              if (ic > 0)


                      msg += pco['issue'] + " Issues\n";


             


              if (rc > 0)


                      msg += pco['risk'] + " Risks\n";


             


              if (crc > 0)


                      msg += pco['change_request'] + " Change Requests\n";


             


              if (tcc > 0)


                      msg += pco['time_card'] + " Time Cards"


             


              alert(msg);


             


              return false;


      }


     


      var ed = pco['effort_deviation'];


      var d   = pco['effort_deviated'];


      jslog("Variance:   " + d + " = " + ed);


      if (d || d == true || d == "true") {


              if (g_form.getValue('u_variance_explanation') == '') {


                      var msg = "Planned and Actual Effort has a variance " + ed +


                              "% which greater than 10%.   Please provide a Variance Explanation.";


                     


                      alert(msg);


                      g_form.setVisible('u_variance_explanation', true);


                      g_form.setMandatory('u_variance_explanation', true);


                     


                      return false;


              }


      }


     


      if (ptc > 0) {


              shouldProjectClose();


             


              return false;


      }


}



function shouldProjectClose() {


      var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;


      var dialog = new dialogClass('glide_confirm_standard');


      dialog.setTitle("Confirmation");


      dialog.setPreference('warning', true);


      dialog.setPreference('title', "All Open Project Tasks will close when the Project is Closed/Cancelled.   Proceed?");


      dialog.setPreference('defaultButton', 'ok_button');


      dialog.setPreference('onPromptComplete',   confirm.bind(this));


      dialog.setPreference('onPromptCancel', cancel.bind(this));


      dialog.render();


}



function confirm() {


      gFormAlreadySubmitted = true;


      if(g_form.getActionName() == 'sysverb_update_and_stay')


              g_form.save();


      else


              g_form.submit();


}



function cancel() {


      // Reset State to Original Value


      g_form.setValue('state', g_scratchpad.projectState);


}


View solution in original post

6 REPLIES 6

Michael Fry1
Kilo Patron

When closing the Project, check from Open Project Tasks and non-approved Time Cards. What about open Risks, Issues, and/or Change Requests? We use a client script to do the check, which I can share, but just wanted to confirm all the different pieces in play here.


Bob
Giga Contributor

Thanks for the reply Michael.




Our PMO is mostly concerned about having these Time Cards not complete, because we pipe it over into another system to resolve billing. The absolute biggest concern is there. Would love to check out the script as it probably applies!



Thank you very much!


Ok, here's the full client script and script includes. It should be pretty easy to take the code you need:


Script Includes:


var ProjectUtilsAjax = Class.create();


ProjectUtilsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {


     


      getOpenProjectTaskCount: function(parentID) {


              var projectID = this.getParameter('sysparm_project_id');


             


              var projectTaskCount = new GlideRecord('pm_project_task');


              projectTaskCount.addActiveQuery();


              projectTaskCount.addQuery('parent', projectID);


              projectTaskCount.query();


             


              return projectTaskCount.getRowCount();


      },


     


      getProjectCounts: function() {


              var projectID = this.getParameter('sysparm_project_id');


             


              var ptc = this.getRelatedRecordCount(projectID, 'pm_project_task', 'parent');


              var ic   = this.getRelatedRecordCount(projectID, 'issue', 'parent');


              var rc   = this.getRiskCount(projectID);


              var crc = this.getRelatedRecordCount(projectID, 'change_request', 'parent');


              var tcc = this.getTimeCardCount(projectID);


              var ed   = this.getEffortDeviation(projectID);


              var rp   = this.hasResourcePlans(projectID);


              var d = (ed > 10);


             


              gs.log("Variance:   " + d + " = " + ed);


             


              var relatedRecordCounts = {


                      "pm_project_task":ptc,


                      "issue":ic,


                      "risk":rc,


                      "change_request":crc,


                      "time_card":tcc,


                      "effort_deviation":ed,


                      "effort_deviated":d,


                      "open_resource_plan":rp


              };


             


              return JSON.stringify(relatedRecordCounts);


      },


     


      getRelatedRecordCount: function(projectID, tableName, fieldName) {


              var projectTaskCount = new GlideRecord(tableName);


              projectTaskCount.addActiveQuery();


              projectTaskCount.addQuery(fieldName, projectID);


              projectTaskCount.query();


             


              return projectTaskCount.getRowCount();


      },


     


      getRiskCount: function(projectID) {


              var riskGR = new GlideRecord('risk');


              riskGR.addQuery('state', '!=', 'closed');


              riskGR.addQuery('task', projectID);


              riskGR.query();


             


              return riskGR.getRowCount();


      },


     


      getTimeCardCount: function(topTaskID) {


              var timeCardCount = 0;


             


              var projectRecords = new GlideRecord('planned_task');


              projectRecords.addQuery('top_task', topTaskID);


              projectRecords.query();


              while (projectRecords.next()) {


                      var timeCard = new GlideRecord('time_card');


                      timeCard.addQuery('state', '!=', 'Processed');


                      timeCard.addQuery('state', '!=', 'Approved');


                      timeCard.addQuery('task', projectRecords.sys_id);


                      timeCard.query();


                     


                      var tcc = timeCard.getRowCount();


                      timeCardCount += tcc;


                     


              }


             


              return timeCardCount;


      },


     


      getEffortDeviation: function(topTaskID) {


              var pu = new ProjectUtils();


     


              return pu.getEffortDeviation();


      },


     


      hasResourcePlans: function() {


              var projectID = this.getParameter('sysparm_project_id');


              gs.log("hasResourcePlans projectID:   " + projectID);



              var sysIDs = "";


              var projectGR = new GlideRecord('planned_task');


              if (projectGR.get(projectID)) {


                      gs.log("hasResourcePlans short_description:   " + projectGR.short_description);


                      var tree = new SNC.NestedIntervalUtilities("planned_task");


                      gs.log("hasResourcePlans tree:   " + tree);


                      var descendants = tree.getDescendants(projectGR, true);


                      gs.log("hasResourcePlans descendants:   " + descendants);



                      while (descendants.next()) {


                              sysIDs += ", " + descendants.sys_id;


                      }


              }


              gs.log("hasResourcePlans sysIDs:   " + sysIDs);



             


              var resourcePlanGR = new GlideRecord('resource_plan');


              resourcePlanGR.addQuery("task", "IN", sysIDs);


              resourcePlanGR.addQuery("state", "!=", 7);


              resourcePlanGR.addQuery("state", "!=", 8);


              resourcePlanGR.query();


              if (resourcePlanGR.hasNext()) {


                      return true;


              }


             


              return false;


      },


     


      getTaskDescendants: function(task, sysIds) {


              var children = new GlideRecord("task");


              children.addQuery("parent", task.sys_id);


              children.query();


              while (children.next()) {


                      sysIds += ", " + children.sys_id;


                      sysIds = this.getTaskDescendants (children, sysIds);


              }


              return sysIds;


      },


     


      type: 'ProjectUtilsAjax'


});



Onsubmit Client Script on Project Table:


if(typeof gFormAlreadySubmitted == "undefined" ||   typeof gFormAlreadySubmitted == "null")


      gFormAlreadySubmitted = false;



function onSubmit() {


      if (gFormAlreadySubmitted)


              return true;


     


      // If not Close/Skip/Cancel then bail


      var state = g_form.getValue('state');


      if (state != 3 && state != -7 && state != 4)


              return true;


     


      // If New Record bail


      if (g_form.isNewRecord())


              return true;


     


      // Project sys_id


      var projectID = gel('sys_uniqueValue').value;


     


      // Open Risk/Issue/Change Request


      ga = new GlideAjax('ProjectUtilsAjax');


      ga.addParam('sysparm_name', 'getProjectCounts');


      ga.addParam('sysparm_project_id', projectID);


      ga.getXMLWait();


      var projectCounts = ga.getAnswer();


      var pco = JSON.parse(projectCounts);


      var ptc = pco['pm_project_task'];


      var ic   = pco['issue'];


      var rc   = pco['risk'];


      var crc = pco['change_request'];


      var tcc = pco['time_card'];


     


      if (ic != 0 || rc != 0 || crc != 0 || tcc != 0) {


              var msg = "The following records must be closed before the Project can be closed:\n";


              if (ic > 0)


                      msg += pco['issue'] + " Issues\n";


             


              if (rc > 0)


                      msg += pco['risk'] + " Risks\n";


             


              if (crc > 0)


                      msg += pco['change_request'] + " Change Requests\n";


             


              if (tcc > 0)


                      msg += pco['time_card'] + " Time Cards"


             


              alert(msg);


             


              return false;


      }


     


      var ed = pco['effort_deviation'];


      var d   = pco['effort_deviated'];


      jslog("Variance:   " + d + " = " + ed);


      if (d || d == true || d == "true") {


              if (g_form.getValue('u_variance_explanation') == '') {


                      var msg = "Planned and Actual Effort has a variance " + ed +


                              "% which greater than 10%.   Please provide a Variance Explanation.";


                     


                      alert(msg);


                      g_form.setVisible('u_variance_explanation', true);


                      g_form.setMandatory('u_variance_explanation', true);


                     


                      return false;


              }


      }


     


      if (ptc > 0) {


              shouldProjectClose();


             


              return false;


      }


}



function shouldProjectClose() {


      var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;


      var dialog = new dialogClass('glide_confirm_standard');


      dialog.setTitle("Confirmation");


      dialog.setPreference('warning', true);


      dialog.setPreference('title', "All Open Project Tasks will close when the Project is Closed/Cancelled.   Proceed?");


      dialog.setPreference('defaultButton', 'ok_button');


      dialog.setPreference('onPromptComplete',   confirm.bind(this));


      dialog.setPreference('onPromptCancel', cancel.bind(this));


      dialog.render();


}



function confirm() {


      gFormAlreadySubmitted = true;


      if(g_form.getActionName() == 'sysverb_update_and_stay')


              g_form.save();


      else


              g_form.submit();


}



function cancel() {


      // Reset State to Original Value


      g_form.setValue('state', g_scratchpad.projectState);


}


Bob
Giga Contributor

Michael, thank you for your generosity in sharing this include and client script. It is fantastic. I'll get it implemented and tweaked and report back on how it all worked out.



Very Appreciatively,