Creating an Approval action in a ServicePortal Widget
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-28-2025 10:15 AM
Good afternoon folks,
i am creating a new Widget to help with approvals and would like to get someone insight into where i am going wrong.
i created two buttons:
HTML Template:
<div ng-repeat="approval in data.approvals" class="sp-approval m-b-xl">
<button name="approve"
class="btn btn-primary btn-sm"
ng-click="triggerSingleApprovalModal(item, 'approved');">
${Approve}
</button><button name="reject"
class="btn btn-default btn-sm"
ng-click="triggerSingleApprovalModal(item,'rejected');">${Reject}</button>
</div>
in my client script i have this:
function ($scope, spUtil, spUIActionsExecuter, $uibModal, spModal, $timeout) {
// Log when controller loads
console.log("Approval controller initialized");
$scope.triggerSingleApprovalModal = function(approval, op) {
console.log("triggerSingleApprovalModal called with approval:", approval, "op:", op);
if (!approval) {
console.error("No approval object provided!");
return;
}
if (!approval.approvers || approval.approvers.length === 0) {
console.error("Approval object missing approvers array or it is empty:", approval);
return;
}
$scope.data.op = op;
$scope.approval = approval;
if (($scope.options.require_approved_confirmation && op === 'approved') ||
($scope.options.require_rejected_confirmation && op === 'rejected')) {
console.log("Opening confirmation modal for op:", op);
$scope.modalInstance = $uibModal.open({
templateUrl: 'se-single-approval-modal.html',
scope: $scope
});
$scope.modalInstance.closed.then(function() {
console.log("Modal closed, resetting values");
resetValues();
});
} else {
console.log("No confirmation required, proceeding to handleSingleApproval");
$scope.handleSingleApproval(approval, op);
}
};
$scope.handleSingleApproval = function(approval, op) {
console.log("handleSingleApproval called with approval:", approval, "op:", op);
if (!approval.approvers || approval.approvers.length === 0) {
console.error("No approvers found on approval:", approval);
return;
}
var approverSysId = approval.approvers[0];
console.log("Approver sys_id:", approverSysId);
$scope.server.update({
approverSysId: approverSysId,
op: op
}).then(function(response) {
console.log("Server update response:", response);
if (response.data && response.data.message) {
console.log("Success message from server:", response.data.message);
} else if (response.data && response.data.error) {
alert("Error: " + response.data.error);
}
}).catch(function(err){
console.error("Error in server update:", err);
});
};
// Simple reset function
function resetValues() {
console.log("Resetting scope values");
$scope.data.op = null;
$scope.approval = null;
}
}
in my server script i got this:
// Handle pagination from input
if (input && input.approvals && input.approvals.length !== 0 && input.pagination && typeof input.pagination.currentPage === 'number') {
currentPage = input.pagination.currentPage;
initRow = currentPage * maxNumberOfItemsInTheList;
lastRow = initRow + maxNumberOfItemsInTheList;
}
options.initRow = initRow;
options.lastRow = lastRow;
// Handle single or bulk approval actions
if (input && input.action) {
gs.info("Handling action: " + input.action + " with op: " + input.op); // LOG
if (input.action === "single_action" && (input.op === 'approved' || input.op === 'rejected')) {
data.statusObj = portalApprovalUtil.handleSingleApprovalAction(input, options);
} else if (input.action === "bulk_action" && (input.op === "approved" || input.op === "rejected")) {
data.statusObj = portalApprovalUtil.handleBulkApprovalAction(input, options);
} else if (input.action) {
gs.addErrorMessage("Invalid input operation.");
gs.info("Invalid input operation detected."); // LOG
}
}
data.ViewApprovalPageMsg = gs.getMessage("View approval page");
var approvalsPage = portalApprovalUtil.getApprovals(options);
// Load e-signature registry map for use later
var esigRequiredMap = loadEsigRequiredMap();
// Build approvals list with variables and attachments
var approvalsByRitm = {};
var gr = new GlideRecord('sysapproval_approver');
gr.addQuery('approver', gs.getUserID());
gr.addQuery('state', 'requested');
gr.query();
gs.info("Loading approvals for user " + gs.getUserName() + " (" + gs.getUserID() + ")"); // LOG
while (gr.next()) {
var ritmSysId = gr.sysapproval.sys_id.toString();
if (!approvalsByRitm[ritmSysId]) {
var reqItem = new GlideRecord('sc_req_item');
if (reqItem.get(ritmSysId)) {
var loadedData = loadVariablesAndAttachments(reqItem, ritmSysId, esigRequiredMap);
var item = {
task: {
sys_id: reqItem.sys_id.toString(),
number: reqItem.number.toString(),
short_description: reqItem.short_description.toString(),
opened: reqItem.opened_at ? reqItem.opened_at.toString() : ''
},
approvers: [gr.sys_id.toString()],
variables: loadedData.variables,
attachments: loadedData.attachments,
requireEsigApproval: loadedData.requireEsigApproval
};
approvalsByRitm[ritmSysId] = item;
gs.info("Added approval item for RITM: " + ritmSysId); // LOG
} else {
gs.info("sc_req_item record not found for sys_id: " + ritmSysId); // LOG
}
} else {
approvalsByRitm[ritmSysId].approvers.push(gr.sys_id.toString());
}
}
any insight into where i am going wrong with my code will be helpful
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-28-2025 11:44 AM
Hi @Peter Williams -
you use ng-repeat="approval in data.approvals" but pass item to triggerSingleApprovalModal(item, 'approved') or triggerSingleApprovalModal(item, 'rejected')
To start with can you please fix the HTML ng-click to pass approval instead of item.
Also in handleSingleApproval function, you call $scope.server.update({ approverSysId: approverSysId, op: op }), but Script expects input.action to be set (e.g., single_action or bulk_action). As you’re not passing action in the payload, which may cause the server to skip the approval logic or log Invalid input operation.
add a "single_action" to the server.update call.
Thanks,
Tushar
Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-29-2025 07:07 AM
Thank you for getting back
i updated my html to this