
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
XML fields are a great way to save form content and apply it later. You may already be familiar with this concept from the Propose Changes functionality. If you haven't used this, here's a quick refresher. At some point in the change process, you right click on an a CI listed in the affected CIs related list, make some changes on the form, then click the Save Proposed Changes UI action, and all your changes are saved in an XML field to be applied later when you click "Apply Proposed Changes." Recently, a customer had me build a cancelation process that did something similar. This is what I came up with…
The key requirements were:
- The user canceling the request must provide a mandatory reason (u_reason choice list) and descriptive text (close_notes)
- For compliance reasons, the user must provide eSignature validation that they are closing the request
- The cancel option is only available in states Draft, Assess & Schedule, Approval, and Implement (up until the time tasks begin), not in Review and not in Close.
To make the Cancel user experience consistent with the rest of the process, I used a Glide Dialog Window to pop up and prompt for the two mandatory fields.
My initial implementation borrowed the eSignature logic from the Approval form's UI actions and put it on the pop-up's Update button. When the form was filled in, the user clicks Update and another pop up appears prompting for login and password, then continues to cancel the request.
That worked fine until the customer required single sign on. Once enabled, the update button still prompted for login and password, unfortunately it took any valid credentials, not necessarily the currently logged in user. So I went back to the drawing board.
After lots of pictures on my sunroom windows using whiteboard markers (my modus operandi), I came up with this design:
- User chooses the Cancel UI action (available in states indicated above and if no other cancelations are pending)
- The Glide dialog window pops up and requires the mandatory fields for u_reason and close_notes as before.
- The user clicks Update
- A (before) business rule:
- Saves the desired field/values to a (hidden) XML field
- Creates a stand alone approval - this not connected to the change workflow.
- Restores the previous values to the form
If the approval is rejected, the XML field is cleared and the process goes on unaffected. If the approval is approved, another business rule applies the contents of the XML field to the change, thus closing the request.
Creating an XML field is much like any other field. You can do this from the form via right-click Personalize> Form Layout, or create a new dictionary entry directly. If you create the field via the form, go back to the form and right click on the new field and choose Personalize Dictionary. Be sure to check the "XML view" attribute on the dictionary entry. Note: you may have to personalize your dictionary form to have it show up.
Checking the XML view on the dictionary gives you this little orange XML tag next to the field label. True, I said this field was hidden, but during debug it was helpful to expose it on the form.
Clicking the XML tag pops a window with a nicely formatted XML tree for easier reading.
So now we have a field to store stuff in, let's take a look at the part that does the actual work. After the Update button on the Glide Dialog Window is clicked, a business rule is run that is triggered by two things, the status going to Canceled, and the XML field being empty. The business rule script looks like this:
- Name: Save cancel values
- Table: Change Request [change_request]
- Order: 50
- Active: true
- When: before
- Insert: true
- Update: true
- Condition: current.u_status.changesTo('930') && current.u_xml.nil()
Note: 930 is the value of status "Canceled". Status (u_status) is a choice list dependent on state.
saveValues();
createApproval();
restoreValues();
/*
* saveValues - save the current S/S/R+res notes to the XML field
*
* @param - none
* @return - none
*
*/
function saveValues() {
var xml = '<change_request>\n';
xml += '\t<state>' + current.state + '</state>\n';
xml += '\t<u_status>' + current.u_status + '</u_status>\n';
xml += '\t<u_reason>' + current.u_reason + '</u_reason>\n';
xml += '\t<close_notes>' + current.close_notes + '</close_notes>\n';
xml += '</change_request>\n';
current.u_xml = xml;
}
/*
* createApproval - create a standalone approval and connect it to the WF activity 'Cancel Request'
*
* @param - none
* @return - none
*
*/
function createApproval() {
var wf = new GlideRecord('wf_context');
wf.addQuery('table', 'change_request');
wf.addQuery('id', current.sys_id);
wf.setLimit(1);
wf.query();
if (wf.next()){
var activity = new GlideRecord('wf_activity');
activity.addQuery('name', 'Cancel Request');
activity.addQuery('workflow_version', wf.workflow_version);
activity.setLimit(1);
activity.query();
if (activity.next()) {
var app = new GlideRecord('sysapproval_approver');
// Create approval
app.initialize();
app.wf_activity = activity.sys_id;
app.sysapproval = current.sys_id;
app.approver = gs.getUserID();
app.state = 'requested';
app.u_element = 'chg_cancel';
app.insert();
// Approval created
}
}
}
/*
* restoreValues - restore the previous values to SSR so the process continues uninterrupted
*
* @param - none
* @return - none
*
*/
function restoreValues() {
var pr = new GlideRecord('change_request');
if (pr.get(current.sys_id)) {
current.state = pr.state;
current.u_status = pr.u_status;
current.u_reason = pr.u_reason;
current.close_notes = pr.close_notes;
}
}
A couple notes about the script:
- I could have used the serializer to capture the entire record rather than hand build the XML. I did it this way for simplicity. (For more information about the serializer take a look at some of the OOB script includes that use it.)
- You may also notice that the createApproval() function appears more complex that it needs to be. This was done because the customer wanted the workflow activity (wf_activity) field to reflect "Cancel Request". Remember, earlier when I said that this was a stand alone approval and not part of the of the workflow. Well, partially. I have an activity on the workflow, but it's not connected to anything. The only value it has is to provide a reference to that activity so the workflow activity name displays like any other approval. If you don't need that, just use the part of the function from "// Create approval" to "// Approval created"
- In the restoreValues() function, the "previous" object isn't available in a before script. pr.get(current.sys_id) is used to retrieve the old values
If the approval is rejected, the following business rule cleans things up:
- Name: Change Cancel - Rejected
- Table: Approval [sysapproval_approver]
- Order: 100
- Active: true
- When: after
- Update: true
- Condition: current.sysapproval.sys_class_name == 'change_request' && current.u_element == 'chg_cancel' && current.state.changesTo('rejected')
Script:
var chg = new GlideRecord('change_request');
if (chg.get(current.sysapproval.sys_id)) {
// If it's rejected, clear the XML field to allow the UI action to appear again
chg.u_xml = '';
chg.update();
}
(FYI - the u_element field was added to sysapproval_approver for another purpose. It provides a way to differentiate this approval record from any other associated with this change)
If the approval is approved (confirming the cancelation) then this business rule is run to finally apply the saved data in the XML field to the change request:
- Name: Change Cancel - Approved
- Table: Approval [sysapproval_approver]
- Order: 100
- Active: true
- When: after
- Update: true
- Condition: current.sysapproval.sys_class_name == 'change_request' && current.u_element == 'chg_cancel' && current.state.changesTo('approved')
Script:
// Get the change record associated with this approval
// Get the values from the XML field and put them in the appropriate fields
var chg = new GlideRecord('change_request');
if (chg.get(current.sysapproval.sys_id)) {
chg.state = gs.getXMLText(chg.u_xml, '//state');
chg.u_status = gs.getXMLText(chg.u_xml, '//u_status');
chg.u_reason = gs.getXMLText(chg.u_xml, '//u_reason');
chg.close_notes = gs.getXMLText(chg.u_xml, '//close_notes');
chg.active = false;
chg.update();
}
This was a pretty specific example of saving information to an XML field, but it gives you an idea of how you can use XML fields to store information and retrieve it later. The customer was happy because the user experience didn't change and they gained improved audit evidence for cancelations while maintaining all the key requirements.
- 1,380 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.