Category Archives: DFFS

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.

DFFS package updated

I have made some further enhancements to the require.js loading method for “Custom JS files” in the Custom JS tab. Based on the feedback,  I’m fairly confident it will work as it did before Microsoft decided to include require.js in SharePoint online.

Read the change log to see all changes.

Please post any feedback on this version in the comments below, or in the forum.

Best regards,
Alexander

DFFS v4.4.3.5 – hopefully sorted the custom js loading files issue

Thanks for the patience!

I have now released a new version with a few changes, and hopefully a final fix for the loading custom js files problem. See change log and additional help text in the Custom JS tab in DFFS.

In addition to the hopefully last fix on the load custom js files, I have made a a hopefully significant improvement on the load time in large forms. Please report back how the load time changes with this new version.

Post your findings below, or use the forum.

Best regards,
Alexander

Know issue with DFFS v4.4.3.3

There is unfortunately one issue with the latest version related to loading of custom js files and the custom js textarea.

If you have a rule triggering onload that calls on a function loaded in custom js, this rule will fail with an error message telling that the function isn’t defined.

The reason for this is that the loading of the require.js script that’s used for loading all other dependencies will defer the loading of custom js for a few milliseconds – enough for the rules to trigger.

I have tried to fix it, and a new version of the DFFS_frontend_min.js file can be downloaded here.

Please let me know if this fixes the issue, and I’ll wrap up a full package of files with this patch.

Sorry for the inconvenience,

Alexander

DFFS v4.4.3.3 published

I have fixed a few bugs and changed a few things based on feedback on the previous release – you find the change log here.

I have updated the DFFS user manual with the latest changes, but please let me know if I have forgotten something.

To all that has reported issues with the latest version: Please test to see if the issues have been fixed, and either reply to this post, or follow up on the forum post where the issue was first raised.


I want to thank all who has provided feedback on bugs and enhancement proposals, and I want to send a special big thanks to Rudolf Vehring for his help with testing this and previous version – and for providing valuable feedback and suggestions.

Alexander

DFFS v4.4.3.0

After a loooong BETA period, and quite a few modification to DFFS and the plugins, I’m finally ready with a new production release.

There are a few nice additions like better backup / restore functionality with support for creating restore points, and new loader functionality that lets you load different DFFS versions side by side in the same site. This way you can do a gradual upgrade of DFFS  – one form at the time.

Please read the full change log to see all changes. You find the change log here: https://spjsblog.com/dffs/dffs-change-log/

Follow the download link here to get the new package.

Please note that the user manual hasn’t been updated yet, but I’ll do my best to update the manual within a few days.

Please post questions in the appropriate forum: https://spjsblog.com/forums/

Best regards,
Alexander