- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 02:13 AM
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 .
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
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 06:29 AM
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' };
}
}
};
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:
If my response helped please mark it correct and close the thread so that it benefits future readers.
Ankur
✨ Certified Technical Architect || ✨ 9x ServiceNow MVP || ✨ ServiceNow Community Leader
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 02:17 AM
Hi @nameisnani
This is more on the education side rather than the platform side. The approve and reject buttons are visible on the left side, so I don’t think any changes are needed on the portal or that it would increase the technical debt.
If my response proves useful, please indicate its helpfulness by selecting " Accept as Solution" and " Helpful." This action benefits both the community and me.
Regards
Dr. Atul G. - Learn N Grow Together
ServiceNow Techno - Functional Trainer
LinkedIn: https://www.linkedin.com/in/dratulgrover
YouTube: https://www.youtube.com/@LearnNGrowTogetherwithAtulG
Topmate: https://topmate.io/atul_grover_lng [ Connect for 1-1 Session]
****************************************************************************************************************
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 02:21 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 02:28 AM
Hi @nameisnani
I understand that this is a client requirement, but as an SN expert, it’s important to guide the customer toward best practices and help avoid technical debt. The issue is that you need to clone the widget, then add the code for the button, and the customer might ask why the same button appears on both sides. So, it’s better to educate them and avoid technical debt.
If my response proves useful, please indicate its helpfulness by selecting " Accept as Solution" and " Helpful." This action benefits both the community and me.
Regards
Dr. Atul G. - Learn N Grow Together
ServiceNow Techno - Functional Trainer
LinkedIn: https://www.linkedin.com/in/dratulgrover
YouTube: https://www.youtube.com/@LearnNGrowTogetherwithAtulG
Topmate: https://topmate.io/atul_grover_lng [ Connect for 1-1 Session]
****************************************************************************************************************
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 03:41 AM
I agree with Atul here and you should train your users about the button positions.
If you still want to do that, then you will have to make changes to widget
1) Clone the OOTB "Approval Record" widget
2) add those buttons
3) then add this newly cloned widget back to approval page
If my response helped please mark it correct and close the thread so that it benefits future readers.
Ankur
✨ Certified Technical Architect || ✨ 9x ServiceNow MVP || ✨ ServiceNow Community Leader