- 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 04:31 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 04:34 AM
I will suggest to start from your side as it will be learning for you as well.
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 04:46 AM
That is true , but service portal complete new , if possible could you please provide starting steps to me ,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-03-2025 05:45 AM
you can use this in HTML to show the buttons, then add logic on what happens when it's clicked
something like this will help you get started
<button ng-click="approve()" class="btn btn-success">Approve</button>
<button ng-click="reject()" class="btn btn-danger">Reject</button>
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 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