DFFS Example: Training application list with approval

This example show a training application list with multi level approval and email alerts – all handled within one list and powered by DFFS.

The requester fill in the form by selecting training or conference from a autocomplete lookup (DFFS Plugin) in another list of training activities and conferences (screenshot at the bottom of this article).

When  selected, the script pulls in the event name, start and end date, cost and number of hours.

When the “Program” is selected from another autocomplete lookup, the program name and the manager for this program is pulled in from another list (screenshot at the bottom of this article).

Use this as a guide to make your own  customized approval workflow in DFFS, and post any questions of feedback in the forum.

The training application list has these fields:

The status field that controls the workflow has these options:

  • Draft
  • Awaiting approval by manager
  • Rejected by manager
  • Awaiting approval by finance
  • Rejected by finance
  • Fully approved

In the email messages in this example the “manager”, “finance” and the end user are all the same person.

Please let me know if you are missing some steps, or you need some more details.

NewForm

Detail view on the SPJS Autocomplete plugin.

Tabs

Rules

Email

Custom JS

// Autocomplete on training / conference
spjs.ac.textField({
 "applyTo":"Title",
 "helpText":"Type to search open training and conferences",
 "loadText":"",
 "listGuid":"Conference_Training",
 "listBaseUrl":"/DFFS/DFFS_Demo",
 "showField":"Title",
 "searchFields":["Title"],
 "filterCAML":"",
 "useREST":true,
 "filterREST":"Status eq 'Open'",
 "optionDetailFields":["StartDate","EndDate","NumberOfHours","Cost"],
 "optionDetailPrefix":["Start: ","End: ","Number of hours: ","Cost ($): "],
 "enforceUniqueValues":true,
 "rowLimit":15,
 "listOptionsOnFocus":false,
 "minLengthBeforeSearch":3,
 "reValidateOnLoad":false,
 "allowAddNew":false,
 "isLookupInSelf":false,
 "addNewAdditionalFields":[],
 "multiselect":false, 
 "multiselectSeparator":"; ",
 "orderBy":[],
 "setFields":[
 {
 "fromFIN":"StartDate",
 "toFIN":"StartDate",
 "parseFunction":"parseDateFunc"
 },
 {
 "fromFIN":"EndDate",
 "toFIN":"EndDate",
 "parseFunction":"parseDateFunc"
 },
 {
 "fromFIN":"Cost",
 "toFIN":"Cost"
 },
 {
 "fromFIN":"NumberOfHours",
 "toFIN":"NumberOfHours"
 }
 ],
 "debug":false
});

function parseDateFunc(a){
 // Set the desired format in the variable "f"
 var f = "MM/dd/yyyy hh:mm" , d = new Date(a);
 f = f.replace("MM",(d.getMonth()+1) < 10 ? "0"+(d.getMonth()+1) : (d.getMonth()+1));
 f = f.replace("dd",d.getDate() < 10 ? "0"+d.getDate() : d.getDate());
 f = f.replace("yyyy",d.getFullYear());
 f = f.replace("yy",d.getFullYear().toString().substring(2));
 f = f.replace("hh",d.getHours() < 10 ? "0"+d.getHours() : d.getHours());
 f = f.replace("mm",d.getMinutes() < 10 ? "0"+d.getMinutes() : d.getMinutes());
 return f.split(/ |:/);
}

// Autocomplete on program
spjs.ac.textField({
 "applyTo":"Program",
 "helpText":"Type to search programs",
 "loadText":"",
 "listGuid":"Programs",
 "listBaseUrl":"/DFFS/DFFS_Demo",
 "showField":"Title",
 "searchFields":["Title"],
 "filterCAML":"",
 "useREST":true,
 "filterREST":"Status eq 'Open'",
 "optionDetailFields":["Manager/Title"],
 "optionDetailPrefix":["Manager: "],
 "enforceUniqueValues":true,
 "rowLimit":15,
 "listOptionsOnFocus":false,
 "minLengthBeforeSearch":3,
 "reValidateOnLoad":false,
 "allowAddNew":false,
 "isLookupInSelf":false,
 "addNewAdditionalFields":[],
 "multiselect":false, 
 "multiselectSeparator":"; ",
 "orderBy":[],
 "setFields":[
 {
 "fromFIN":"Manager/Name",
 "toFIN":"Manager",
 "parseFunction":""
 }
 ],
 "debug":false
});

function showApprovalStatus(){
 var b = [], label, labelColor, status, spjsStatusArr = [
 {"label":"Draft","icon":"&#9312;","color":"#107c10"},
 {"label":"Awaiting approval by manager","icon":"&#9313;","color":"#107c10"},
 {"label":"Rejected by manager","icon":"&#9314;","color":"#e81123"},
 {"label":"Awaiting approval by finance","icon":"&#9315;","color":"#107c10"},
 {"label":"Rejected by finance","icon":"&#9316;","color":"#e81123"},
 {"label":"Fully approved","icon":"&#9317;","color":"#107c10"}
 ];
 status = getFieldValue("Status");
 jQuery.each(spjsStatusArr,function(i,c){
 b.push("<div title='"+c.label+"' class='statusBannerIcon'");
 if(status === c.label){
 b.push(" style='color:"+c.color+"'");
 label = c.label;
 currStatusIndex = i;
 labelColor = c.color;
 }
 b.push("'>"+c.icon+"</div>");
 });
 jQuery(".approvalWfStatus").html(b.join(""));
 jQuery(".approvalWfLabel").html("<span style='color:"+labelColor+"'>Current status: "+label+"</span>");
}
setTimeout(function(){
 showApprovalStatus();
},500);

Custom CSS

.ms-formlabel{
 width:250px;
}
.statusBannerIcon{
 display:inline-block;
 font-size:35px;
 margin:5px;
 color:#cccccc;
}
.approvalWfLabel{
 font-size:18px;
 font-weight:bold;
 border-bottom:1px silver solid;
 padding:0 0 10px 5px;
}
.customHeading{
 background-color: #1B619B;
 color: #EFF2F6;
 font-size:16px;
 padding:5px 10px;
}

DispForm

Tabs

Custom JS

function showApprovalStatus(){
 var b = [], label, labelColor, status, spjsStatusArr = [
 {"label":"Draft","icon":"&#9312;","color":"#107c10"},
 {"label":"Awaiting approval by manager","icon":"&#9313;","color":"#107c10"},
 {"label":"Rejected by manager","icon":"&#9314;","color":"#e81123"},
 {"label":"Awaiting approval by finance","icon":"&#9315;","color":"#107c10"},
 {"label":"Rejected by finance","icon":"&#9316;","color":"#e81123"},
 {"label":"Fully approved","icon":"&#9317;","color":"#107c10"}
 ];
 status = getFieldValue("Status");
 jQuery.each(spjsStatusArr,function(i,c){
 b.push("<div title='"+c.label+"' class='statusBannerIcon'");
 if(status === c.label){
 b.push(" style='color:"+c.color+"'");
 label = c.label;
 currStatusIndex = i;
 labelColor = c.color;
 }
 b.push("'>"+c.icon+"</div>");
 });
 jQuery(".approvalWfStatus").html(b.join(""));
 jQuery(".approvalWfLabel").html("<span style='color:"+labelColor+"'>Current status: "+label+"</span>");
}
setTimeout(function(){
 showApprovalStatus();
},500);

Custom CSS

.ms-formlabel{
 width:250px;
}
.statusBannerIcon{
 display:inline-block;
 font-size:35px;
 margin:5px;
 color:#cccccc;
}
.approvalWfLabel{
 font-size:18px;
 font-weight:bold;
 border-bottom:1px silver solid;
 padding:0 0 10px 5px;
}
.customHeading{
 background-color: #1B619B;
 color: #EFF2F6;
 font-size:16px;
 padding:5px 10px;
}

EditForm

The first step in EditForm is “Approval by manager”, then “Approval by finance”. Both steps can either be approved or rejected.

Approval tab.

If the manager approves, an email is sent to “finance”.

If the manager rejects, an email is sent to the author and the status set to “Rejected by manager”.

If it is approved, the finance representative follows the link in the email to this form:

Approval tab.

Finance can reject or approve. If it is rejected, this email will be sent to the author of the request form:

If it is approved, this email is sent:

Tabs

Rules

Email

Please note that the checkbox “Use custom list with workflow to send E-Mails” is checked. This is necessary to be able to postpone the sending of the email “eventFollowUp” that has this “Send date”:

{EndDate}+14

If you use the REST email option (by not checking this checkbox) your email will be sent immediately.

Please refer the user installation manual for details on the DFFS_Email list and workflow.

Custom JS

function showApprovalStatus(){
 var b = [], label, labelColor, status, spjsStatusArr = [
 {"label":"Draft","icon":"&#9312;","color":"#107c10"},
 {"label":"Awaiting approval by manager","icon":"&#9313;","color":"#107c10"},
 {"label":"Rejected by manager","icon":"&#9314;","color":"#e81123"},
 {"label":"Awaiting approval by finance","icon":"&#9315;","color":"#107c10"},
 {"label":"Rejected by finance","icon":"&#9316;","color":"#e81123"},
 {"label":"Fully approved","icon":"&#9317;","color":"#107c10"}
 ];
 status = getFieldValue("Status");
 jQuery.each(spjsStatusArr,function(i,c){
 b.push("<div title='"+c.label+"' class='statusBannerIcon'");
 if(status === c.label){
 b.push(" style='color:"+c.color+"'");
 label = c.label;
 currStatusIndex = i;
 labelColor = c.color;
 }
 b.push("'>"+c.icon+"</div>");
 });
 jQuery(".approvalWfStatus").html(b.join(""));
 jQuery(".approvalWfLabel").html("<span style='color:"+labelColor+"'>Current status: "+label+"</span>");
}
setTimeout(function(){
 showApprovalStatus();
},500);

Custom CSS

.ms-formlabel{
 width:250px;
}
.statusBannerIcon{
 display:inline-block;
 font-size:35px;
 margin:5px;
 color:#cccccc;
}
.approvalWfLabel{
 font-size:18px;
 font-weight:bold;
 border-bottom:1px silver solid;
 padding:0 0 10px 5px;
}
.customHeading{
 background-color: #1B619B;
 color: #EFF2F6;
 font-size:16px;
 padding:5px 10px;
}

Lookup lists for the Autocomplete plugin

The datasource for “Training/Conference” and “Program” is is two separate lists in the same site.

16 thoughts on “DFFS Example: Training application list with approval”

  1. This looks really good but for a novice like me there is a lot not explained that I’m not sure about, like where are the status icons 1 to 6 in circles? are these built in or is this all up to me to figure out … it’s OK if it is; after all I’m not expecting you to give me a crash course in web development 🙂

  2. Sorry for the delay – these are unicode “icons” defined in this array in the Custom JS section:

    spjsStatusArr = [
     {"label":"Draft","icon":"①","color":"#107c10"},
     {"label":"Awaiting approval by manager","icon":"②","color":"#107c10"},
     {"label":"Rejected by manager","icon":"③","color":"#e81123"},
     {"label":"Awaiting approval by finance","icon":"④","color":"#107c10"},
     {"label":"Rejected by finance","icon":"⑤","color":"#e81123"},
     {"label":"Fully approved","icon":"⑥","color":"#107c10"}
     ];

    Look here: http://www.fileformat.info/info/unicode/char/2460/index.htm and use the “next character” navigation in the top right corner to see the next one.

    PS: The HTML code for the icons are turned into actual icons in the above snippet.

    Alexander

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.