Welcome to Community Week 2025! Join us to learn, connect, and be recognized as we celebrate the spirit of Community and the power of AI. Get the details  

Making Rejection Comments Mandatory on Approval Record

nameisnani
Mega Sage

Hi Team , 

 

We have requirement that when ever we are rejecting any approval , it should show rejection comments . which we have already achieved that . 

 

SunilKumarPadh_0-1679936056666.png

 

 

If we open the record and try to reject that , at that time rejection comments are not showing .

 

SunilKumarPadh_1-1679936170357.png

 

To achieve this one i have followed this link ( https://www.servicenow.com/community/developer-blog/portal-diaries-service-portal-making-rejection-c... ) But we are confusing we need to update the script . 

 

So , can any one please provide us complete script and steps to achiev this requirment . 

 

This is our current script -

 

HTML 

 

<div class="panel panel-{{::options.color}} b" ng-if="data.showApprovals">
  <div class="panel-heading">
    <h2 class="h4 panel-title">
      <fa ng-if="::options.glyph.length" name="{{::options.glyph}}" class="m-r-sm" ></fa>
      ${My Approvals}
      <label ng-if="data.pagination.showPagination && (data.pagination.from <= data.pagination.to)" class="pull-right text-info">
        <span ng-if="data.pagination.from != data.pagination.to">
          {{data.pagination.from}} ${to_lower}
        </span>
        {{data.pagination.to}} of {{data.pagination.of}}
      </label>
    </h2>
  </div>
  <div class="panel-body" ng-class="{'padder-b-none': data.approvals.length != 0}">
    <div ng-if="data.approvals.length == 0 && data.pagination.of == 0">
      ${You have no pending approvals}
    </div>
    <div ng-if="data.approvals.length == 0 && data.pagination.of != 0">
      ${Loading approvals...}
    </div>
    <div ng-repeat="approval in data.approvals" class="sp-approval m-b-xl">
      <div class="row">
        <div ng-class="contentColClass">
          <div ng-if="approval.task.number || approval.task.short_description">
            <a ng-href="?id=approval&table=sysapproval_approver&sys_id={{::approval.sys_id}}" id="approval_task_{{::approval.sys_id}}"title="{{data.ViewApprovalPageMsg}}">
              <span ng-if="approval.task.number">{{::approval.task.number}}</span>
              <span ng-if="approval.task.number && approval.task.short_description"> - </span>
              <span ng-if="approval.task.short_description">{{::approval.task.short_description}}</span>
            </a>
          </div>
          <div ng-if="approval.task.opened_by"><label>${Requestor}</label> {{::approval.task.opened_by}}</div>
          <div ng-if="approval.task.approver"><label>${Approver}</label> {{::approval.task.approver}}</div>
          <div ng-if="approval.task.start_date"><label>${Start}</label> {{::approval.task.start_date}}</div>
          <div ng-if="approval.task.end_date"><label>${End}</label> {{::approval.task.end_date}}</div>
          <div ng-if="approval.task.quantity"><label>${Quantity}</label> {{::approval.task.quantity}}</div>
          <div ng-if="approval.task.price"><label>${Price}</label> {{::approval.task.price}}
            <span ng-if="approval.task.recurring_price"><label>${Recurring price}</label> {{::approval.task.recurring_price}} {{::approval.task.recurring_frequency}}</span>
            <label ng-if="approval.task.quantity && approval.task.quantity > 1"> ${each}</label>
          </div>
          <div ng-if="approval.items.length == 1">
            <div ng-repeat="item in approval.items">
             <sp-widget ng-if="item.variableSummarizerWidget" widget="item.variableSummarizerWidget"></sp-widget> 
            </div>
          </div>
          <sp-widget ng-if="approval.variableSummarizerWidget" widget="approval.variableSummarizerWidget"></sp-widget>
          
          </div>
        <div ng-if="!options.portal" class="col-sm-4">
          <button name="approve" id="approve_button_{{::approval.sys_id}}" aria-labelledby=" approve_button_{{::approval.sys_id}} approval_task_{{::approval.sys_id}}" ng-if="approval.state == 'requested'" class="btn btn-primary btn-block" style="border-width:1px;" ng-click="approve(approval.sys_id, approval.requireEsigApproval);">${Approve}</button>
          <button name="reject" id="reject_button_{{::approval.sys_id}}" aria-labelledby=" reject_button_{{::approval.sys_id}} approval_task_{{::approval.sys_id}}" ng-if="approval.state == 'requested'" class="btn btn-default btn-block" ng-click="reject(approval.sys_id, approval.requireEsigApproval);">${Reject}</button>
          <button ng-if="approval.state == 'approved'" class="btn btn-success btn-block">{{approval.stateLabel}}</button>
          <button ng-if="approval.state == 'rejected'" class="btn btn-danger btn-block">{{approval.stateLabel}}</button>
          <button ng-if="approval.state != 'requested'" class="btn btn-default btn-block" style="visibility:hidden">{{approval.stateLabel}}</button>
        </div>
        <div ng-if="options.portal && approval.state == 'requested'" class="col-xs-6">
          <button name="reject" id="reject_button_{{::approval.sys_id}}" aria-labelledby=" reject_button_{{::approval.sys_id}} approval_task_{{::approval.sys_id}}" class="btn btn-default btn-block" ng-click="reject(approval.sys_id, approval.requireEsigApproval);">${Reject}</button>
        </div>
        <div ng-if="options.portal && approval.state == 'requested'" class="col-xs-6">
          <button name="approve" id="approve_button_{{::approval.sys_id}}" aria-labelledby=" approve_button_{{::approval.sys_id}} approval_task_{{::approval.sys_id}}" class="btn btn-primary btn-block" ng-click="approve(approval.sys_id, approval.requireEsigApproval);">${Approve}</button>
        </div>
        <div ng-if="options.portal && approval.state != 'requested'" class="col-xs-12">
          <button ng-if="approval.state == 'approved'" class="btn btn-success btn-block">{{approval.stateLabel}}</button>
          <button ng-if="approval.state == 'rejected'" class="btn btn-danger btn-block">{{approval.stateLabel}}</button>
        </div>
      </div>
    </div> <!-- body -->
       
  </div> 
  <div class="panel-footer clearfix" ng-if="data.pagination.showPagination">
    <a id="previous-btn" href="javascript&colon;void(0)" ng-click="previousPage()" ng-show="data.pagination.hasPrevious" class="pull-left btn btn-sm btn-default" aria-label="${Pagination button Previous}">
      <i class="fa fa-arrow-left m-r-sm" aria-hidden="true"></i>${Previous}</a>
    <a id="next_btn" href="javascript&colon;void(0)" ng-click="nextPage()" ng-show="data.pagination.hasNext" class="pull-right btn btn-sm btn-default " aria-label="${Pagination button Next}">
      ${Next}<i class="fa fa-arrow-right m-r-sm col-md-offset-3" aria-hidden="true"></i></a>
</div>
</div>

 

 

Client Script 

 

function ($scope, spUtil, spUIActionsExecuter, spModal) {
	var ESIGNATURE = {
		TYPE: "form",
		APPROVE_SYS: "cbfe291147220100ba13a5554ee4904d",
		REJECT_SYS: "580f711147220100ba13a5554ee4904b"
	};

	if ($scope.options.portal == true || $scope.options.portal == 'true') {
		$scope.contentColClass = "col-xs-12";
		$scope.options.portal = true;
	} else {
		$scope.options.portal = false;
		$scope.contentColClass = "col-sm-8";
	}

	$scope.data.op = "";
	spUtil.recordWatch($scope, "sysapproval_approver", "state=requested^approverIN" + $scope.data.myApprovals.toString(), function(data) {
		// don't double-roundtrip if update came from record just approved/rejected
		if (data.data.sys_id != $scope.data.target)
			spUtil.update($scope);
	});

	function get() {
		spUtil.update($scope);
	}

	$scope.approve = function(id, esigRequired) {
		var requestParams = {
			username: $scope.data.esignature.username,
			userSysId: $scope.data.esignature.userSysId
		};

		if($scope.data.esignature.e_sig_required && esigRequired) {
			spUIActionsExecuter.executeFormAction(ESIGNATURE.APPROVE_SYS, "sysapproval_approver" , id, [] , "", requestParams).then(function(response) {
			});			
		} else {
			$scope.data.op = "approved";
			$scope.data.target = id;
			get();
		}
	}

	$scope.reject = function(id, esigRequired) {

		spModal.open({
			title: 'Reject Approval',
			message: 'Provide a reason for rejecting this approval',
			input: true,
			value: $scope.name
		}).then(function(name) {
			$scope.name = name;

			var requestParams = {
				username: $scope.data.esignature.username,
				userSysId: $scope.data.esignature.userSysId
			};

			if($scope.data.esignature.e_sig_required && esigRequired) {
				spUIActionsExecuter.executeFormAction(ESIGNATURE.REJECT_SYS, "sysapproval_approver" , id, [] , "", requestParams).then(function(response) {
				});
			} else {
				$scope.data.op = "rejected";
				$scope.data.target = id;
				$scope.data.reject_comment = "Rejection reason: " + $scope.name;
				get();
			}
		});
	};

	// pagination
	$scope.previousPage = function() {
		if ($scope.data.pagination.currentPage > 1)
			$scope.data.pagination.currentPage = $scope.data.pagination.currentPage - 1;
		else
			$scope.data.pagination.currentPage = 0;

		get();
	}

	$scope.nextPage = function() {
		$scope.data.pagination.currentPage = $scope.data.pagination.currentPage+1;
		get();
	}

	$scope.getItemDisplay = function(task) {
		if (task.number && task.short_description)
			return task.number + " - " + task.short_description;

		return task.number || task.short_description || "";
	}
}

 

 

 

Server code 

 

 

g_approval_form_request = true;

//we get only a max number of elements to avoid to have a big list of it
var maxNumberOfItemsInTheList = parseInt(options.max_number_of_elements_shown_on_the_list);
//set 10 if maxnumber is undefined, empty or negative value
maxNumberOfItemsInTheList = maxNumberOfItemsInTheList>0 ? maxNumberOfItemsInTheList : 10; 
var initRow = 0;
var lastRow = maxNumberOfItemsInTheList;
var currentPage = 0; //0 is the first page
if (input) {
	// update pagination
	currentPage = input.pagination.currentPage;
	initRow = (currentPage * maxNumberOfItemsInTheList);
	lastRow = initRow + maxNumberOfItemsInTheList;

	if (input.op == 'approved' || input.op == 'rejected') {
		var app = new GlideRecord("sysapproval_approver");
		if (app.get(input.target)) {
			app.state = input.op;
			if(input.op == 'rejected'){
				app.comments = input.reject_comment;
			}

			app.update();

		}
	}
}

data.ViewApprovalPageMsg = gs.getMessage("View approval page");
data.esignature = {
	username:  gs.getUserName(),
	userSysId: gs.getUserID(),
	e_sig_required: GlidePluginManager.isRegistered('com.glide.e_signature_approvals')
};

var esigRequiredMap = {};
if (data.esignature.e_sig_required) {
	var esigRegistryGR = new GlideRecord("e_signature_registry");
	esigRegistryGR.addQuery("enabled", "true");
	esigRegistryGR.query();
	while(esigRegistryGR.next()) {
		esigRequiredMap[esigRegistryGR.getValue("table_name")] = true;
	}
}

var gr = new GlideRecord('sysapproval_approver');
gr.chooseWindow(initRow, lastRow);
var qc1 = gr.addQuery("state", "requested");
//if (input)
//  qc1.addOrCondition("sys_id", "IN", input.ids);
data.myApprovals = getMyApprovals();
gr.addQuery("approver", data.myApprovals);
gr.orderBy("sys_created_on");
gr.query();
var rowCount = gr.getRowCount();
var approvals = [];
var ids = [];
var source_tables = [];

while (gr.next()) {
	var task = getRecordBeingApproved(gr);
	if (!task.isValidRecord())
		continue;

	ids.push(gr.getUniqueValue());
	var t = {};
	t.number = task.getDisplayValue();
	t.short_description = task.short_description.toString();
	if (gr.getValue("approver") != gs.getUserID())
		t.approver = gr.approver.getDisplayValue();
	if (task.isValidField("opened_by") && !task.opened_by.nil())
		t.opened_by = task.opened_by.getDisplayValue();

	// requestor >> opener
	if (task.isValidField("requested_by") && !task.requested_by.nil())
		t.opened_by = task.requested_by.getDisplayValue();

	t.start_date = task.start_date.getDisplayValue();
	t.end_date = task.end_date.getDisplayValue();
	t.quantity = task.quantity.getDisplayValue();
	t.table = task.getLabel();
	if (task.getValue("price") > 0)
		t.price = task.getDisplayValue("price");

	if (task.getValue("recurring_price") > 0)
		t.recurring_price = task.getDisplayValue("recurring_price");

	t.recurring_frequency = task.getDisplayValue("recurring_frequency");

	var items = [];
	var idx = 0;
	var itemsGR = new GlideRecord("sc_req_item");
	itemsGR.addQuery("request", task.sys_id);
	itemsGR.query();
	if (itemsGR.getRowCount() > 1)
		t.short_description = itemsGR.getRowCount() + " requested items";

	while (itemsGR.next()) {
		var item = {};
		item.short_description = itemsGR.short_description.toString();
		if (itemsGR.getValue("price") > 0)
			item.price = itemsGR.getDisplayValue("price");
		if (itemsGR.getValue("recurring_price") > 0) {
			item.recurring_price = itemsGR.getDisplayValue("recurring_price");
			item.recurring_frequency = itemsGR.getDisplayValue("recurring_frequency");
		}
		if (itemsGR.getRowCount() == 1) {
			item.variables = new GlobalServiceCatalogUtil().getVariablesForTask(itemsGR, true);
			item.variableSummarizerWidget = $sp.getWidget('sc-variable-summarizer', {'variables' : item.variables, 'toggle' : false, 'task' :t.number });
			t.short_description = itemsGR.short_description.toString();
		}

		items[idx] = item;
		idx++;
	}

	var j = {};
	j.sys_id = gr.getUniqueValue();
	j.table = gr.getRecordClassName();
	j.approval_source_table = gr.getValue("source_table");
	if (!j.approval_source_table)
		j.approval_source_table = gr.sysapproval.sys_class_name + "";
	j.requireEsigApproval = esigRequiredMap[j.approval_source_table];
	j.task = t;
	if (task) {
		j.variables = new GlobalServiceCatalogUtil().getVariablesForTask(task, true);
		j.variableSummarizerWidget = $sp.getWidget('sc-variable-summarizer', {'variables' : j.variables, 'toggle' : false, 'task': t.number });
	}
	j.items = items;
	j.state = gr.getValue("state");
	j.stateLabel = gr.state.getDisplayValue();
	approvals.push(j);
}

data.ids = ids;
data.approvals = approvals;
data.showApprovals = gs.getUser().hasRole('approver_user');
// for pagination
data.pagination = {};
data.pagination.hasNext = (approvals.length == (parseInt(lastRow) - parseInt(initRow)) && lastRow < rowCount);
data.pagination.hasPrevious = parseInt(initRow) > 0;
data.pagination.from = parseInt(initRow + 1);
data.pagination.to = parseInt(lastRow) < parseInt(rowCount) ? parseInt(lastRow) : parseInt(rowCount);
data.pagination.of = parseInt(rowCount);
data.pagination.showPagination = data.pagination.hasPrevious || data.pagination.hasNext;
data.pagination.currentPage = data.pagination.from > data.pagination.to ? currentPage -1 : currentPage;
delete g_approval_form_request;
function getRecordBeingApproved(gr) {
	if (!gr.sysapproval.nil())
		return gr.sysapproval.getRefRecord();

	return gr.document_id.getRefRecord();
}

 

 

 

 

This is my current script , 

 

can anyone please provide me the exact script , so that it can works in both the places .

 

@Ankur Bawiskar 

@Gunjan Kiratkar 

@OlaN 

Vasantharajan N 

@Vasantharajan N 

 

 

Thanks in advance 

1 ACCEPTED SOLUTION

@nameisnani  - 

Note: This is the customization on OOTB page "approval".  

 

Please use the attached xml of the custom widget.

Step 1: Import the xml into sp_widget table.

Step 2: Update the widget "Approval Info" to "My Approval Info" in page "approval". Please refer the screenshot below for your reference. 

VasantharajanN_0-1679983090984.png

Step 3: Test the change.

 


Thanks & Regards,
Vasanth

View solution in original post

7 REPLIES 7

nameisnani
Mega Sage

Hi @Ankur Bawiskar  @Gunjan Kiratkar  @OlaN 

 

Could you please help me on this request . @Vasantharajan N 

Looping ++ @Harsh Vardhan 

 

 

@nameisnani - Did you import the xml attached in the community link that you used to customize the Approval info widget?


Thanks & Regards,
Vasanth

Yes @Vasantharajan N 

 

I have tried that one as well . not working , could u please check above script .