Approval Widget Cancel button change status and add comment to work note

BenjaminY
Tera Contributor

I'm currently working on extending a custom approval widget in ServiceNow to include a "Cancel" button alongside the existing "Approve" and "Reject" options.

The goal is:

  • When a user selects Cancel, a comment is required.

  • Upon submission, the widget should:

    • Update the record_status field on custom table (on both the approval and related custom task record) to 'cancelled'.

    • Add the user's comment as a work note on the related custom task record (e.g., x_g_dh5_ccht_ccht_task).

      The reject button is changing the status to the record but not adding the comment once submitted.

      HTML:

      <div class="panel panel-{{::c.options.color}} b">
        <div class="panel-heading">
          <h4 class="panel-title" ng-if="c.data.state == 'requested'">${This {{c.data.label}} requires your approval}</h4>
          <h4 class="panel-title" ng-if="c.data.state == 'approved'">${Approved} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>
          <h4 class="panel-title" ng-if="c.data.state == 'rejected'">${Rejected} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>
          <h4 class="panel-title" ng-if="c.data.state == 'cancelled'">${Cancelled} <sn-time-ago timestamp="::c.data.sys_updated_on" /></h4>
       
        </div>  
        <div class="panel-body">
          <form ng-submit="$event.preventDefault()" class="form-horizontal">
            <div ng-if="c.data.fields.length > 0">
              <div ng-repeat="field in c.data.fields" class="m-b-xs" ng-if="field.value">
                <label class="m-n">{{field.label}}</label>
                <span ng-switch="field.type">
                  <div ng-switch-when="glide_date_time" title="{{field.display_value}}"><sn-time-ago timestamp="::field.value" /></div>
                  <div ng-switch-default >{{field.display_value}}</div>
                </span>
              </div>
            </div>
            <div ng-if="c.data.state == 'requested'" class="question">
              <button type="button" name="approve" class="btn btn-success btn-question" ng-click="c.action('approved')">${Approve}</button>
              <div class="spacer"></div>
              <button type="button" class="btn btn-default btn-question" ng-click="c.openModal('rejected')">${Reject}</button>
      <div style="height: 20px;"></div>
        <button type="button" class="btn btn-default btn-question" ng-click="c.openModal('cancelled')">${Cancel Request}</button>
        </div>
          </form>
        </div>  
      </div>
       
      <!--Setting Window for Comments JWD -->
      <script type="text/ng-template" id="modalTemplate">
        <div class="panel panel-default">
          <div class="panel-body">
            <div class="form-group">
              <label>
                {{ c.modalAction === 'cancelled' ? 'Justification for Cancelling Request' : 'Comments required when rejecting' }}
              </label>
              <input class="form-control" ng-model="c.data.comments" required />
            </div>
            <div class="form-group">
              <button 
                ng-disabled="!c.data.comments" 
                class="btn btn-primary btn-sm" 
                ng-click="c.action(c.modalAction); c.closeModal();">
                ${Submit}
              </button>
            </div>
            <div class="panel-footer text-right">
              <button class="btn btn-default" ng-click="c.closeModal()">Cancel</button>
            </div>
          </div>
        </div>
      </script>

      Client Script:
      function ($scope, $uibModal) {
      var c = this;
      c.data.comments = "";
      c.action = function(state) {
      c.data.op = state;
      c.data.state = state;
      c.server.update();
      }
      //Opens the pop-up for comments. JWD
      c.openModal = function(actionType) {
      c.modalAction = actionType; // either 'cancelled' or 'rejected'
      c.modalInstance = $uibModal.open({
      templateUrl: 'modalTemplate',
      scope: $scope
      });
      }
       
      c.closeModal = function() {
      c.modalInstance.close();
      }
       
      }

      Server Script:

      var gr = $sp.getRecord();
      if (input && input.op && gr) {
      gs.info("[WIDGET] Received comments: " + input.comments);
      gs.info("[WIDGET] Updating approval record: " + gr.getTableName() + " | " + gr.getUniqueValue());

      gr.comments = input.comments;
      gr.state = input.op;
      gr.update();

      // Update related task (x_g_dh5_ccht_ccht_task)
      var targetRecord = getRecordBeingApproved(gr);
      gs.info("[WIDGET] Related task record: " + (targetRecord ? targetRecord.getUniqueValue() : 'No related record'));

      if (targetRecord && targetRecord.isValidRecord()) {
      gs.info("[WIDGET] Updating related task record: " + targetRecord.getTableName() + " | " + targetRecord.getUniqueValue());
      targetRecord.setValue('state', input.op);

      // Check if work_notes field is valid and update it
      if (targetRecord.isValidField('work_notes')) {
      targetRecord.setValue('work_notes', input.comments); // Set comments in work_notes
      } else {
      gs.info("[WIDGET] work_notes field is not valid on related task record.");
      }

      targetRecord.update();
      } else {
      gs.info("[WIDGET] No valid related task record found or not accessible.");
      }
      }

      var fields = $sp.getFields(gr, 'state,sys_created_on');
      if (gr) {
      if (gr.sys_mod_count > 0)
      fields.push($sp.getField(gr, 'sys_updated_on'));

      data.fields = fields;
      data.state = gr.state.toString();
      data.sys_updated_on = gr.sys_updated_on.toString();
      data.sys_id = gr.getUniqueValue();
      data.table = gr.getTableName();

      var related = getRecordBeingApproved(gr);
      if (related && related.isValidRecord()) {
      data.label = related.getLabel();
      } else {
      data.label = gr.getLabel();
      }
      }

      function getRecordBeingApproved(gr) {
      if (!gr.sysapproval.nil()) return gr.sysapproval.getRefRecord();
      return gr.document_id.getRefRecord();
      }



      BenjaminY_0-1747074522223.pngBenjaminY_1-1747074625983.pngBenjaminY_2-1747074706495.png

       

 

3 REPLIES 3

Rajat Gupta6
Tera Guru

try 

gr.comments.setJournalEntry(input.comments.toString());

 

Please accept my solution if it works for you and thumps up.

This seems to only work within global applications

Rajat Gupta6
Tera Guru

Then gr.comments = input.comments.toString(); should work fine for scoped apps.