Blog

Service Portal: Resolve Incident Button

How to add a "Resolve Incident", “Request Status” and “Cancel” buttons with modal popup for Service Portal (SP).

Please note that there are more than one way to do this, but might help you make new buttons of your own.  

Results

Here is what the final result looks like:

Resolve Button

Modal Popup

INSTRUCTIONS

1. Create Widget

Name: Ticket Actions

Body HTML Template

<div class="panel b" ng-if="data.showWidget">
  <div class="panel-heading bg-primary">Actions</div>
  <div class="panel-body">
    <button type="button" class="btn btn-primary btn-block" ng-click="c.openModalResolve()" ng-if="data.showResolve">Resolve</button>
    <button type="button" class="btn btn-default btn-block" ng-click="c.openModalStatus()" ng-if="data.showStatus">Request Status</button>
    <button type="button" class="btn btn-danger btn-block" ng-click="c.openModalCancel()" ng-if="data.showCancel">Cancel</button>
    <button type="button" class="btn btn-default btn-block" ng-click="c.openModalReopen()" ng-if="data.showReopen">Reopen</button>
  </div>
</div>

<script type="text/ng-template" id="modalTemplateResolve">
	<div class="panel panel-default">
		<div class="panel-heading">
			<h4 class="panel-title">Provide a reason for the resolve</h4>
  </div>
      <div class="panel-body wrapper-xl">
      <form name="modalTemplateResolve" ng-submit="c.uiAction('resolve')">
        <div class="form-group">
          <textarea required sp-autosize="true" ng-required="true" ng-model="data.resolveComments" id="resolveComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
  </div>
        <input class="btn btn-primary" type="submit" />
  </form>
  </div>
  </div>
</script>

<script type="text/ng-template" id="modalTemplateCancel">
	<div class="panel panel-default">
		<div class="panel-heading">
			<h4 class="panel-title">Provide a reason for the cancel</h4>
  </div>
      <div class="panel-body wrapper-xl">
      <form name="modalTemplateResolve" ng-submit="c.uiAction('cancel')">
        <div class="form-group">
          <textarea required sp-autosize="true" ng-required="true" ng-model="data.cancelComments" id="cancelComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
  </div>
        <input class="btn btn-primary" type="submit" />
  </form>
  </div>
  </div>
</script>

<script type="text/ng-template" id="modalTemplateStatus">
	<div class="panel panel-default">
		<div class="panel-heading">
			<h4 class="panel-title">Provide any additional information</h4>
  </div>
      <div class="panel-body wrapper-xl">
      <form name="modalTemplateStatus" ng-submit="c.uiAction('status')">
        <div class="form-group">
          <textarea required sp-autosize="true" ng-required="true" ng-model="data.statusComments" id="statusComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
  </div>
        <input class="btn btn-primary" type="submit" />
  </form>
  </div>
  </div>
</script>

<script type="text/ng-template" id="modalTemplateReopen">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h4 class="panel-title">Provide a reason for reopening the Incident</h4>
  </div>
    <div class="panel-body wrapper-xl">
      <form name="modalTemplateReopen" ng-submit="c.uiAction('reopen')">
        <div class="form-group">
          <textarea required sp-autosize="true" ng-required="true" ng-model="data.reopenComments" id="reopenComments" placeholder="Comments required" class="form-control ng-pristine ng-valid ng-scope ng-empty ng-touched" aria-invalid="false" style="overflow: hidden; word-wrap: break-word; resize: horizontal;"></textarea>
  </div>
        <input class="btn btn-primary" type="submit" />
  </form>
  </div>
  </div>
</script>

Server Script

(function() {

 // Get table & sys_id
 data.table = input.table || $sp.getParameter("table");
 data.sys_id = input.sys_id || $sp.getParameter("sys_id");

 // Valid GlideRecord
 gr = new GlideRecord(data.table);
 if (!gr.isValid())
   return;

 // Valid sys_id
 if (!gr.get(data.sys_id))
   return;

  //Button Visibility
  if(data.table == 'incident' && gr.active == true && gr.incident_state != 6 && (gr.caller_id == gs.getUserID() ||  gs.hasRole("admin"))){
    data.showWidget = true;
    data.showResolve = true;
    data.showCancel = true;
    data.showStatus = true;
    data.showReopen = false;
  }
  else if(data.table == 'incident' && gr.incident_state == 6 && (gr.caller_id == gs.getUserID() ||  gs.hasRole("admin"))){
    data.showWidget = true;
    data.showResolve = false;
    data.showCancel = false;
    data.showStatus = false;
    data.showReopen = true;
  }
  else if(data.table == 'sc_req_item' && gr.active == true && (gr.request.requested_for == gs.getUserID() ||  gs.hasRole("admin"))){
    data.showWidget = true;
    data.showResolve = false;
    data.showCancel = true;
    data.showStatus = true;
    data.showReopen = false;
  }
  else {
    data.showWidget = false;
    data.showResolve = false;
    data.showCancel = false;
    data.showStatus = false;
    data.showReopen = false;
  }

 //input
 if (input && input.action) {
   var action = input.action;

   //Incident table
   if (data.table == 'incident') {
     if (action == 'resolve' && input.resolveComments !='') {
			 gr.setValue('incident_state', 6);
			 gr.setValue('state', 6);
			 gr.setValue('resolved_by', gs.getUserID());
			 gr.setValue('close_code', 'Closed/Resolved by Caller');
			 gr.setValue('close_notes', "Closed by caller with comment: " + input.resolveComments);
			 //gr.work_notes = 'Closed by caller with comment: ' + input.resolveComments;
			 gr.update();
     }
     if (action == 'status' && input.statusComments !='') {
       gr.comments.setJournalEntry("Status request by user: "+ input.statusComments);
       gr.setValue('u_request_status',gr.u_request_status++);
       gr.update();
     }
     if (action == 'cancel') {
       gr.setValue('incident_state', 8);
       gr.setValue('state', 8);
       gr.setValue('resolved_by', gs.getUserID());
			 gr.setValue('close_code','Customer Cancelled');
       gr.setValue('close_notes','Cancelled by caller with comment: '+ input.cancelComments);
			 //gr.work_notes = 'Cancelled by caller with comment: ' + input.resolveComments;
       gr.update();
     }
     if (action == 'reopen') {
       gr.setValue('incident_state', 9);
       gr.setValue('state', 9);
       gr.setDisplayValue('comments', 'Reopened by caller with comment: '+ input.reopenComments);
       gr.update();
     }
   }

   //Requested Item table
   if (data.table == 'sc_req_item' && input.cancelComments !='') {
     if (action == 'cancel') {
       var workflow = new Workflow();
       workflow.cancel(gr);
       gr.setValue('approval','withdrawn');
       gr.setValue('stage','Request Cancelled');
       gr.setValue('state','4');
       gr.setValue('active','false');
			 gr.comments.setJournalEntry("Closed by Requested For with comment: "+ input.cancelComments);
       gr.update();
     }
     if (action == 'status' && input.statusComments !='') {

			 gr.comments.setJournalEntry("Status request by user: "+ input.statusComments);
       gr.update();
     }
   }

 }
})();

Client Controller

function($uibModal, $scope, spUtil) {
	var c = this;

	$scope.$on('record.updated', function(name, data) {
		c.data.cancelComments = '';
		c.data.statusComments = '';
		c.data.resolveComments = '';
		c.data.reopenComments = '';
		spUtil.update($scope);
	})

	c.uiAction = function(action) {
		c.data.action = action;
		c.server.update().then(function() {
			c.data.action = undefined;
		})
		c.modalInstance.close();
	}
	c.openModalResolve = function() {
		c.modalInstance = $uibModal.open({
			templateUrl: 'modalTemplateResolve',
			scope: $scope
		});
	}
	c.openModalCancel = function() {
		c.modalInstance = $uibModal.open({
			templateUrl: 'modalTemplateCancel',
			scope: $scope
		});
	}
	c.openModalStatus = function() {
		c.modalInstance = $uibModal.open({
			templateUrl: 'modalTemplateStatus',
			scope: $scope
		});
	}
	c.openModalReopen = function() {
		c.modalInstance = $uibModal.open({
			templateUrl: 'modalTemplateReopen',
			scope: $scope
		});
	}
	c.closeModal = function() {
		c.modalInstance.close();
	}
}

2. Drop Widget on Form

  1. Go to the Ticket Page in SP, go look at an existing incident.

  2. Ctrl-Right click and select "Page in Designer

  3. On the Widgets Sidebar drag-n-drop the "Ticket Actions" widget on the form.

CTRL-Right click Options

Page Designer