My Approvals Portal page enhancement - Allow approval fro RITM view

nameisnani
Mega Sage

Hi Team ,

 

can anyone please help me on this requirment 

 

Refer attached this is a confusing interface for the user - Please add an Approve and Reject button where the tick .

 

nameisnani_0-1748941929139.png

 

 

 

 

can anyone please provide steps to achieve this requirment

 

please provide configuration screenshots for better understanding .

 

@Ankur Bawiskar  Please help me on this requriment with your solution 

 

Thanks 

1 ACCEPTED SOLUTION

@nameisnani 

here is the working example, my buttons are at different place

You can try to adjust it as per your requirement

Script Include:

var ApprovalHandler = Class.create();
ApprovalHandler.prototype = {
    initialize: function() {},

    processApproval: function(approvalId, action) {
        // Validate action
        if (action != 'approved' && action != 'rejected') {
            return { success: false, message: 'Invalid action' };
        }

        var gr = new GlideRecord('sysapproval_approver');
        if (gr.get(approvalId)) {
            // Check if the current user is the approver or a delegate
            if (gr.approver != gs.getUserID()) {
                var delegateUtil = new global.DelegationUtil();
                if (!delegateUtil.isUserOrDelegate(gr.approver)) {
                    return { success: false, message: 'You are not authorized to approve this request' };
                }
            }
            gr.state = action;
            var result = gr.update();
            if (result) {
                // Optionally trigger workflow or other logic here
                return { success: true, message: 'Approval updated successfully' };
            } else {
                return { success: false, message: 'Failed to update approval' };
            }
        } else {
            return { success: false, message: 'Approval record not found' };
        }
    }
};

AnkurBawiskar_0-1748957000148.png

 

Widget

HTML:

<div ng-if="!data.isValid">
  ${Record not found}
</div>
<div ng-if="data.isValid">
<div class="panel panel-{{::options.color}} b">
  <div class="panel-heading">
    <h2 class="panel-title">${Approval request for {{::task.table}} {{::task.number.display_value}}}</h2>
  </div>

  <div class="panel-body">
    <div ng-if="task.short_description">{{::task.short_description.display_value}}</div>
    <div ng-if="task.opened_by"><label>${Opened by}</label> {{::task.opened_by.display_value}}</div>
    <div ng-if="task.requested_by"><label>{{::task.requested_by_label}}</label> {{::task.requested_by.display_value}}</div>
    <div ng-if="::data.approver"><label>${Approver}</label> {{::data.approver}}</div>
    <div ng-if="task.start_date"><label>${Start}</label> {{::task.start_date.display_value}}</div>
    <div ng-if="task.end_date"><label>${End}</label> {{::task.end_date.display_value}}</div>
    <div ng-if="task.quantity">${Quantity} {{::task.quantity.display_value}}</div>
    <div ng-if="task.price.value > 0"><label>${Price}</label> {{::task.price.display_value}}
      <span ng-if="task.recurring_frequency.value != null"><label>${Recurring price}</label> {{::task.recurring_price.display_value}} {{task.recurring_frequency.display_value}}</span>
      <label ng-if="task.quantity && task.quantity.value > 1"> ${each}</label>
    </div>
    <button ng-click="approve()" class="btn btn-success">Approve</button>
<button ng-click="reject()" class="btn btn-danger">Reject</button>

    
    <div ng-if="data.items.length > 0">
      <h3 class="h4">${Items in this Request}</h3>
      <div ng-repeat="item in data.items">
        <h4>
          {{::item.short_description}}
        </h4>
        <div ng-if="item.price">${Price} {{::item.price}}
          <span ng-if="item.recurring_price">${Recurring price} {{::item.recurring_price}} {{::item.recurring_frequency}}</span>
        </div>
        <sp-widget ng-if="item.variableSummarizerWidget" widget="item.variableSummarizerWidget"></sp-widget>

      </div>
    </div>

    <sp-widget widget="data.variableSummarizerWidget"></sp-widget>
</div>
  <sp-widget widget="data.ticketConversation"></sp-widget>
</div>

Client Controller:

function ($scope, $animate, $rootScope) {
	 var c = this;
  $scope.$watch("data.task", function() {
    $scope.task = $scope.data.task; // copy for shortcuts above
  })

$scope.approve = function() {
        c.server.get({
            action: 'approved',
            approvalId: c.data.sys_id
        }).then(function(response) {
            if (response.data.result.success) {
                alert('Approved successfully!');
                // Optionally reload or redirect
            } else {
                alert(response.data.result.message);
            }
        });
    };

    $scope.reject = function() {
        c.server.get({
            action: 'rejected',
            approvalId: c.data.sys_id
        }).then(function(response) {
            if (response.data.result.success) {
                alert('Rejected successfully!');
                // Optionally reload or redirect
            } else {
                alert(response.data.result.message);
            }
        });
    };

}

Server Side Script:

(function() {
	// g_approval_form_request is for approval summarizer ACLs
	// that let user read a record they need to approve. This global
	// variable is then deleted at the bottom of the script
	g_approval_form_request = true;
	
	
	
	var gr = $sp.getRecord();
	if (gr == null || !gr.isValid()) {
		data.isValid = false;
		return;
	}
	if (gr.getValue("approver") != gs.getUserID())
		data.approver = gr.approver.getDisplayValue();
	data.isValid = true;
	var task = getRecordBeingApproved(gr);
	if (task == null) {
		data.isValid = false;
		return;
	}

	var t = {};
	t = $sp.getFieldsObject(task, 'number,short_description,opened_by,requested_by,start_date,end_date,quantity,price,recurring_price,recurring_frequency');
	t.table = task.getLabel();

	var items = [];
	var idx = 0;
	var itemsGR = new GlideRecord("sc_req_item");
	itemsGR.addQuery("request", task.sys_id);
	itemsGR.query();
	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) {
		  item.variables = new GlobalServiceCatalogUtil().getVariablesForTask(itemsGR, true);
		  item.variableSummarizerWidget = $sp.getWidget('sc-variable-summarizer', {'variables' : item.variables, 'toggle' : false, 'task': t.number.value});
		}
	  items[idx] = item;
	  idx++;
	}

	data.items = items;
	data.sys_id = gr.getUniqueValue();

	t.requested_by_label = gs.getMessage('Requestor');
	if(t.requested_by && t.requested_by.type== 'glide_date')
		t.requested_by_label = t.requested_by.label;

	data.task = t;
	if (task) {
		data.variables = new GlobalServiceCatalogUtil().getVariablesForTask(task, true);
		data.variableSummarizerWidget = $sp.getWidget('sc-variable-summarizer', {'variables' : data.variables, 'toggle' : true, 'task': t.number.value});
	}

	function getRecordBeingApproved(gr) {
		var approvalTargetRecord;
	  if (!gr.sysapproval.nil())
			approvalTargetRecord = gr.sysapproval.getRefRecord();
		else
			approvalTargetRecord = gr.document_id.getRefRecord();

		return (approvalTargetRecord.canRead()) ? approvalTargetRecord : null;
	}

	var ticketConversationOptions = {
		placeholder: gs.getMessage("Type your message here..."),
		placeholderNoEntries: gs.getMessage("Start a conversation..."),
		btnLabel: gs.getMessage("Send")
	};

	if (options.use_approval_record_activity_stream === true ||
			options.use_approval_record_activity_stream === "true") {
		ticketConversationOptions.sys_id = gr.getUniqueValue();
		ticketConversationOptions.table  = gr.getRecordClassName();
		ticketConversationOptions.title  = gs.getMessage("Activity Stream for Approval");
	} else {
		ticketConversationOptions.sys_id = task.getUniqueValue();
		ticketConversationOptions.table  = task.getRecordClassName();
		ticketConversationOptions.title  = gs.getMessage("Activity Stream for {0}", task.getLabel());
	}
	data.ticketConversation = $sp.getWidget('widget-ticket-conversation', ticketConversationOptions);
	delete g_approval_form_request;
	
	if (input && input.action) {
        var handler = new ApprovalHandler();
        var result = handler.processApproval(input.approvalId, input.action);
        data.result = result;
        return;
    }
	
})();

Output:

approval widget copy buttons.gif

If my response helped please mark it correct and close the thread so that it benefits future readers.

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader

View solution in original post

16 REPLIES 16

@Ankur Bawiskar 

 

Thank you so much @Ankur Bawiskar  ,  trying now 

@Ankur Bawiskar  Is script include client callable ?

@Ankur Bawiskar  

 

After approving also still buttons are showing 

@Ankur Bawiskar  could you please help me here

nameisnani_0-1748959116851.png

 

@nameisnani 

you can update such that those buttons are gone when it's not requested

I believe I have answered your question with the correct outcome and you can enhance it from here.

Please mark my response as correct and close the thread.

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader

@nameisnani 

I believe I have answered your question and you can mark my response as correct and close the thread.

Regards,
Ankur
Certified Technical Architect  ||  9x ServiceNow MVP  ||  ServiceNow Community Leader