CRM Development – As easy as making a cup of tea?

Last weekend I attended the Dynamics 365 Saturday Summer Bootcamp in London; this was a great event full of opportunities to network, engage and learn, and I am very grateful to the organisers for putting on the event.  Whilst at the event I was speaking to someone who told me that he was currently working as a business analyst but was considering training to become a CRM Functional Consultant and it got me thinking about the importance of business analysis skills to me in my role.

Always ask “Why?”

Before I became a CRM Consultant I studied Law at university, and I’ve trained as an ISO9001 internal auditor in previous job roles.  This background has provided me with a strong ability to analyse and understand business process, though my biggest asset is probably my incessant asking “Why?”.

I’ve spent countless hours developing solutions for CRM that then go unused by the business after deployment, and I could’ve saved so much of this time if I’d just asked “Why” a bit more.

  • Why do you need this solution?
  • Why are you recording this information?
  • Why does the process work like this?
  • Why can’t we use existing functionality?

The key thing is to make sure you have enough detail so that you have a full understanding of the requirements before you even commence development.  Think of it as a modern version of “measure twice, cut once”.

Creating Process Flowcharts

I have a logical mind, so I like to document all of my processes in flowcharts before I get underway with development.  I find a flowchart helps me to keep my thoughts in check and guides me in my development of system updates.  A good flowchart should be comprehensive but clear; it should provide you with all of the steps in the process and should have a clear, logical path to follow.  Anyone who has used Visio in the past can probably attest to the simplicity of creating flowcharts, though I think there is a bit of an art to making a good flowchart.

In order to demonstrate the level of detail I look for in a flowchart, I thought it would be useful to think of it in terms of a process that everyone can understand – making a cup of tea!

How to make a cup of tea

I know what you’re thinking – everyone knows how to make a cup of tea, it’s a waste of time to document the process.  Fortunately, this is a really simple process to document:

Simple Process

So that’s us done right?  If only it were that easy…

I’ve experienced plenty of processes like the above, they are a weak attempt at documenting how a process works, and miss out quite a lot of the detail.  For a start, if you boil a kettle without adding water to it, you’re gonna have a bad time.  I’ve yet to make a cup of tea for a group of people without there being variables involved, some people want milk in their tea, and some people want sugar.  Some want both, some want neither.  So let’s start over and create a process that includes these variables:

Simple Process with variables (1)

Ah, that’s better!

This is a much more detailed process, and accounts for the variables at different steps.  A little bit of extra thought, and asking a few more questions about the process has helped us to capture a lot more information and ensure the process accurately reflects the actual work being completed.

Unfortunately, I think this is still too simple.  There are lots of different types of tea, and they have different preparation processes.  In most businesses, their processes may have multiple divergent paths based on decisions taken at different parts of the process, and this can have a massive knock-on effect to your development if you don’t account for it at the planning stage.  I’ve lost so many hours to poor planning and a lack of understanding of the needs of the business when I’ve been creating my solutions.  Trying to unpick a solution after implementation can be time-consuming, painful and frustrating.

A comprehensive tea making flowchart might look like the below:

Comprehensive Process

This process accounts for multiple variables, divergent paths, and includes a lot of detail that would help me understand what I need to do to make sure my system can account for all of the steps.  It is possible to make this even more detailed if you wish, though you also have to know where to draw the line and not to add complexity for no additional benefit

Conclusions

As I’ve hopefully demonstrated above, it’s really easy to make a simple process, but there are risks involved in basing your system development on poor information.  Spending the time at the start to ensure that you fully understand the needs of the business and the process problem you’re creating a solution for will ensure that your development time will not be wasted.

At the very least, after reading this I hope you know how to make a cup of tea!

 

Designing CRM Forms for User Experience

Introduction

In order to ensure User Adoption of CRM is successful and that the system is delivering results for Users, it is important to ensure that their experience of using the system is as painless as possible. In practice, this means removing unnecessary obstacles to enable them to find the information they need, when they need it. Whilst CRM is, by its nature, a data input system, it is important to focus on the outputs the system can deliver, and the actionable insights it can generate. A smooth and efficient user experience will help ensure that CRM becomes the point of reference for users when they are looking for data, and will drive them to embrace the system rather than relying on Excel spreadsheets and other workarounds that they may have used in the past.

Why Focus on UX?

Microsoft produced a set of User Experience guidelines in 2013, and the diagram below shows how designing an efficient User Experience benefits the business:

UX Principles
Aligning the purpose of CRM towards the end user’s needs leads to better user perception as the users consider CRM tobe giving them value by making them more efficient.  If the CRM system can be seen to be delivering value to the users it leads to wider user adoption which naturally generates a greater quantity of higher quality data.

A CRM system that has a lot of quality data can be used to generate meaningful insights and actionable intelligence, and this can drive business decisions. If the business is able to see a valuable impact from the system, then they will be more likely to sustain and increase their investment in the system development as they will recognise the return on investment they are receiving.

Microsoft have summed this up in the paper as follows:

The key take away here is this: It’s very important to focus on the value CRM adds to the end users’ day-to-day activities and how it helps them achieve their goals and objectives. If this shared purpose isn’t established early and the focus isn’t enforced through design and implementation decisions, poor adoption and overall project failure will likely follow.

In order to demonstrate these principles in practice, I’ll use some sample issues I’ve encountered in my environment in the Accounts entity, though the conclusions reached can be equally applied across any system entity.

Note: the screenshots below are from a CRM 2016 (v8.1) system, and some of the issues encountered in this version of CRM can be addressed with the upgrade to Dynamics 365.  Nevertheless, I hope it still provides valuable food for thought.

Sample Issues

For the purposes of this section, I’ll be using the screenshot below as a reference

AccountFormSample.PNG

Long lists of fields

According to Miller’s Law, humans can only understand and process information in a maximum “chunks” of 7. In practice in CRM this means that, where possible, we should try to avoid long lists of fields as they become difficult and confusing to process, unless they can be broken into smaller sections and groups that help the users to consume the data.

On the Account form above, you can see that the Account Information section on the left is comprised of a long list of over 20 fields.  The list of fields contains a number of different field types, there is not a logical flow to the fields, and they’re not grouped in a way that new and existing System Users will be able to understand easily.

Having a large list of unrelated fields also creates issues when you take into consideration the likelihood of the fields being filled in.  In the example above, different fields ralte to different Account Types (i.e. clients, prospects, etc.).  If there are large lists of empty fields this can be visually jarring for users, and it can reinforce negative behaviours e.g. not filling in fields when information is known.

Visual Clutter

The sample Account form is also quite cluttered visually. For example, a large amount of screen real-estate is dedicated to the Notes & Activities feed; the information in this section, whilst useful, is not accessed regularly and is usually of interest only in certain circumstances.

Similarly, though it is not visible on the screenshot, underneath the “Address” section there is a map that highlights the location of the selected address. This map is rarely used, often incorrect, and therefore consumes more space on the screen that could be utilised for more relevant information.

Utilisation of Sub-Grids vs. Associated Grids

There are a number of Subgrids on the Account form, however there is limited screen space to ensure all relevant data from the related entities can be displayed. For example, the Contacts subgrid in the screenshot above is situated on the right hand side of the screen, but there are a few potential issues with it:

  1. It only displays six contacts at a time. If there are a lot of contacts on the account Users would need to navigate multiple pages to find the specific contact they wanted.
  2. The list is organised alphabetically; whilst this makes logical sense, it is typically not the most useful form of sorting. For example, if the contacts were organised by Job Role, or by the amount of times contacted, etc. this would probably be more useful for Users
  3. The subgrid only displays two columns, and is therefore missing other potentially useful information that would assist users. They will have to open the Associated Grid or the individual Contacts to find the information they need

There is nothing inherently wrong with using subgrids, however if the information is used infrequently, or the amount of information that needs to be conveyed is more than can be displayed in a small grid, then it is recommended to use the Associated Grids.

Field Positions

Whilst recognising that there is a need to make space on forms for fields that are used infrequently, the position of these fields could be considered to create logical flow through the form. In an ideal scenario, the most useful fields will be closer to the top of the form, and the lesser used fields will be relocated further down. This ensures the User Experience can be optimised to minimise excessive scrolling or searching for information.

For example, the Company Profile section contains fields for Number of Employees and Annual Revenue, however they are not completed on many Accounts. The SIC code fields are also too small to display all of the information. The Primary Contact field is similarly not filled in on a lot of the records.

 

Multiple Forms with Similar Data

One of the great flexibilities of the CRM system is the ability to create multiple forms for entities, however lazy development can quickly lead to issues with this.  When creating a new Form, Microsoft “helpfully” pre-populate the form with tabs, sections and fields for you.  In order to ensure the Forms serve a purpose you should only add fields to the form if they’re required, and remove everything else.  The Account entity in this example had 7 forms available to all system users.  Whilst each form was intended to serve a purpose, the form design meant that there were a lot of repeated fields across all the forms, leading to confusion for Users about which Form they should be using.

Related Records Navigation

related Records Navigation.PNG

An oft-overlooked aspect of CRM Form design is the related records navigation section.  It’s easy to forget to update this section, and there is a limit to the customisation capabilities for this section.  In the example above the list of Related Records is not optimised to deliver best results for Users.

To reduce navigational clutter, any related records that are not likely to be used should be removed, e.g. Import Logs, Feedback, etc. The relationships should be specifically named to ensure there is no confusion (i.e. there are two “Opportunities” relationships currently visualised, but they don’t have specific descriptions.  You should also group the related records into common sections to make it easier to navigate for Users.

Recommendations

In order to address the issues highlighted above, I would recommend designing your Forms to follow, where relevant, the Microsoft guidelines for user experience design.

Some of the recommendations I would implement are:

  1. Field Label Width – set to ensure the full label of the fields are visible and not cut off
  2. Tab and Section based grouping – using Form Tabs to group related fields, and sub-grouping the data into sections within the tabs
  3. Smart Form Design – where necessary, using business rules and JavaScript functions to hide/show fields as required, and using Field Security to restrict access to fields to specific security roles
  4. Removing Duplication – on occasions where additional forms are required, ensuring they are not simply a duplicate of other forms, and are designed for a specific business need.

An example of how these recommendations have been implemented can be seen below:

NewAccountForm

For the example above, I’ve implemented the following changes:

  1. Grouped fields into common sections to make it easier to find relevant information
  2. Used the Advanced Multiselect solution to add multiselect options
  3. Removed the Address composite field.  I’m really not a fan of the composite fields, though I appreciate they can serve a purpose.
  4. Changed the “Country” field from Single Line of Text to an Optionset to ensure consistent data input
  5. Used JavaScript and business rules to show/hide fields based on selected account type
  6. Moved the Notes and Activities feed to a dedicated tab to enable it to be collapsed to save screen real estate
  7. Removed sub-grids where they were not serving a useful purpose
  8. Removed unnecessary duplicate forms
  9. Cleaned up the related records navigation (see below)

New Related Records Navigation.PNG

 

I think this approach has made the Form more usable, and it’s much easier to digest from a visual perspective.  Taking the time to understand the User’s needs helps to ensure they appreciate the system and feel that it is working for them, rather than against them.

If you’ve got any thoughts on how to improve the User Experience please add your comments below, or get in touch with me

Update an Account in CRM with the latest Stock Price using North52

I recently installed North52 in my CRM environment and I’ve been having great fun exploring the capabilities of the solution to replace some existing plugins, and to develop some new functionality.  North52 is an amazing solution that enables functional consultants like me to develop technical solutions using a simple point-and-click editor.  North52 is probably worth several blog posts all by itself, but for the sake of brevity I’m going to focus on the WebFusion service.

What is WebFusion?

WebFusion allows you to integrate your CRM system with any services that expose a REST API.  It’s really straightforward to develop an integration and there are a number of cool features:

WebFusion Features.PNG

Getting Stock Prices

I work for a financial services company, and we have a number of publicly listed clients and prospects.  In order to equip the staff with as much information as possible I thought it would be useful to provide them with the latest stock prices for those companies.  A bit of google-fu led me to Alpha Vantage, which has a free API to return real-time and historical stock data in JSON and CSV format.  I was interested in using the Time Series Data, which offers a number of different data points.  The Alpha Vantage website explains this better than I can:

Time Series Data provides realtime and historical equity data in 4 different temporal resolutions: (1) intraday, (2) daily, (3) weekly, and (4) monthly. Daily, weekly, and monthly time series contain up to 20 years of historical data. The intraday time series typically spans the last 10 to 15 trading days.

I wanted to get the daily close price for any of the clients/prospects that had been identified, so I was using the Time Series Daily API call.  For an example JSON file, see this link.  I’ve included a sample in the screenshot below:

Sample JSON

Now that the first step is out of the way, the challenge was to see how I could get this information into CRM.

Creating the North52 Formula

One of the things that I really like about North52 is the simplicity of creating formulas.  For this formula I only wanted it to be fired if there was a Stock Code on the record so I initiated the formula with an If(ContainsData) function. If this was successful then I wanted to use the CallRestAPI function.

Getting the CallRestAPI function to return the information I needed in the formula was pretty straightforward, however I did run into a little bit of trouble trying to convert the returned data into a format I needed to be able to add it to my record.  Luckily for me, the guys at North52 are super helpful; John Grace responded to me really quickly with the updates I needed and the resultant formula looks like this:


if( ContainsData([account.stockexchange]) ,

CallRestAPI(
SetRequestBaseURL('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol='+[account.stockexchange]+'&apikey='+xCacheGet('Alpha Vantage API Key')),
SetRequestResource(),
SetRequestDetails('GET'), 
SetRequestHeaders(),
SetRequestParams(), 
SetRequestAuthenticationBasic(),
SetRequestFiles(),
SetRequestExpected('OK'),
SetRequestActionPass(
 SmartFlow(
 SetVar('responsecontent', Replace(GetVar('responsecontent'), '. ', '_ ') ), 
 SetVar('responsecontent', Replace(GetVar('responsecontent'), '(', '') ),
 SetVar('responsecontent', Replace(GetVar('responsecontent'), ')', '') ),
 SetVar('LastRefreshed', GetVarJsonValue('Meta Data.3_ Last Refreshed') ),

 UpdateRecord('account', [account.accountid], 
 SetAttribute('hr_lateststockprice', GetVarJsonValue('Time Series Daily.' + GetVar('LastRefreshed') + '.4_ close') ),
 SetAttribute('hr_lateststockretrievaldate', GetVar('LastRefreshed') ) 
 ) 
 )
 ),
SetRequestActionFail(ThrowError('Fail')
)
)
, 'NoOp')

The main steps that John added were as follows:

  1. Change periods (“.”) to underscores (“_”) in the JSON results
  2. Remove open parentheses (“(“) and close parenthese (“)”) from the JSON results
  3. Create a variable to hold the Last Refreshed date for use later in the formula

Once we had these updates, all that was left to do was use an UpdateRecord step to add the output to my CRM records.

Whilst the Formula code above can look a little bit daunting to a non-developer, its actually really straightforward to add, and there are wizards for most formulas to make it even easier.

Extending the Functionality

As the Stock Price changes every day, I wanted to ensure that it was updated daily on the CRM record, and North52 has a great Scheduler that makes this easy to achieve.

I also wanted to make it possible for Users to trigger this on-demand if needed (e.g. if they had just added the Stock Code but didn’t want to wait for the formula to run at 2am).  This was achieved through the use of a North52 Quick Button.

Update Stock Quick Button

Additional Considerations

If the stock you are searching for is listed on the London Stock Exchange, you need to suffix the stock code with ‘.l’ (without the inverted commas).  I believe the syntax for the stock code symbols follows the Yahoo Finance system, so it’s worth checking on there to ensure you’re using the correct code if you’re having issues.

It’s probably also worth noting that this is a free API, and therefore the service could change at any time.

Conclusions

This was a fun project for me to work on, and I’m pleased with the results.  It shows the power of the North52 solution and it’s enabled me to develop an integration that would otherwise have been extremely difficult and time-consuming to achieve as a non-developer.  My next challenge is to see if I can achieve the same kind of functionality with the Companies House API…

 

Long Date Formatting for Date fields – A Different Way

I recently read a great blog post by Megan Walker regarding the formatting of a long date field in CRM.  Megan has explained really simply how to achieve this using JavaScript, and I’d thoroughly recommend reading her blog to see how easy it is.

I know that some people are uncomfortable even with using simple scripts so I wanted to highlight an alternative way of achieving this without using JavaScript, for those who may wish to avoid it.

I’ve previously discussed the Workflow Elements solution by Aiden Kaskela in my blog about adding team members to another team, and this is another perfect opportunity to utilise it.

Overview of date formatting workflow

One of the options in Workflow Elements is Date – Convert to Custom Text, and this allows you to build a custom text version of any date field in your entity using up to 20 components.  The components available comprise options for Year, Month, Day, Hour and Minute, as well as options for AM/PM designators, time zone display and separators.

Kaskela Date Format Options

Building your date

Creating a custom text date couldn’t be easier, you simply add the Date – Convert to Custom Text step to your workflow, then open it and select the date you’d like to convert, pick the time zone for display and then build it from the options identified above:

Buidling the Date

Once you’re happy with the custom date options you’ve selected, save and close the window then add an Update Record step to your workflow.  In the Update Record step, find the field you’d like to update with the Custom Date, then in the Form Assistant select the previous step output under local values, then select the Formatted Date option and add it to your field.

Add to workflow.png

The output of the workflow looks great and is easy to configure:

Date

 

I still wouldn’t recommend using this for every date field in your environment, but this is a handy and simple alternative to JavaScript and it doesn’t require you to open and save the record for it be triggered.  The configuration options available give you significant control over the output and can cater to most requirements.

CRM Power Pane – Review

One of my favourite tools in my CRM Administrator Toolkit (*not an actual thing) is the Dynamics CRM Power Pane extension for Google Chrome and Mozilla Firefox.

The Power Pane add-on has been developed to help developers, testers and power users to accomplish tasks that they may require in their day-to-day role.  Installation of the add-on is as simple as installing the extension in your browser – no need to install any solutions.

Once you have the extension installed an orange lightning bolt icon will appear in the top-left corner of your Nav bar whenever you open CRM, and clicking this will enable the actions for you.

I should caveat at the top that this tool is very much not for end-users, and I’d caution against even making them aware that it exists.  It can undo the security settings you have put in place, and render your customisations meaningless.

With that out of the way, let’s have a look at what it does.   The CRM Power-Pane add-on is comprised of 3 sets of tools:

  • Record Actions
  • Form Actions
  • Navigation Tools

I’ll give an overview of each of these below:

Record Actions

There are 5 options under the Record Actions section

  1. Entity Info – opens a pop-up window with the Entity logical name and the Entity Type Code
  2. Record ID – opens a pop-up window with the GUID of the current record
  3. Record URL – opens a pop-up window with the direct URL of the current record
  4. Clone Record – creates an exact clone of the current record
  5. Record Properties – opens the record properties window

I particularly like the Record URL options – no more clicking “Email a Link” and then copying the link so I can send it to someone in Skype.  I also get a lot of use out of the Record ID option, it’s come in handy more times than I can count.

 

Form Actions

The Form Actions are where this tool really comes into it’s own, and where I think most other developers/administrators will get use out of it.  There are 12 options under Form Actions:

  1. Enable All Fields – makes all read-only fields editable
  2. Show Hidden Fields – unhides any fields that have been set as hidden
  3. Disable Field Requirement – removes any business recommended or business required options on fields, enabling you to save it without filling them in
  4. Schema Names as Label – changes the field labels to show the logical/schema name
  5. Scheme Name Copy Mode – enables the option to copy the schema name for a field by clicking on the field name
  6. Show Optionset Values – prefixes the optionset options with their value
  7. Show Field Value – opens a pop-up window to allow you to input the schema name for a field, and returns the value in the field, and the field type.  Depending on the type of field additional values will also be returned, e.g. for a lookup field you will get the text value, the record GUID, the lookup record Entity Name and Entity Type Code.
  8. Find Field in Form – opens a pop-up window where you can input the schema name for a field, and then moves the focus to that field on the form and highlights the field
  9. Highlight Dirty Fields – adds a highlight to any fields that have been changed since the form was loaded
  10. Refresh Ribbon – refreshes the command ribbon on the form.  This can be useful if you’re testing the visibility of buttons that appear/disappear based on field values
  11. Refresh Form – refreshes the CRM form, without the need to refresh the whole window.

I use a lot of these tools on almost a daily basis, in particular the Schema Names as Label  and Scheme Name Copy Mode.  If you’re writing any code then I’m sure you’ll find these useful too.  Similarly, being able to unhide and unlock fields has saved me numerous times when I’ve been carrying out testing.

As I said at the start, if your Users find this tool it could cause no end of headaches for you, but it also serves as a stark reminder that CRM is just a series of web pages, so if you want a field to be completely secure don’t put it on a Form that is accessible by Users. Reece Campbell wrote a great blog about CRM Forms and security recently, so I’d recommend it for more reading.

Navigations

The last set of tools in CRM Power Pane are the Navigations tools. There are 6 tools here:

  1. Go to record by ID – enables you to open any record by specifying the entity name and the record GUID
  2. Entity Editor – makes it really easy to open the Entity customisation in the default solution.  This defaults to the entity you’re currently in, but you can specify which one you’d like to open
  3.  CRM Diagnostics – opens the CRM diagnostics page to allow you to evaluate network performance
  4. Performance Center – opens the CRM Performance Center to allow you to evaluate the performance of form loading in CRM
  5. Mobile Express – opens the Mobile Express version of your CRM environment
  6. Mobile Client – opens the mobile version of your CRM environment

Conclusion

The Dynamics CRM Power Pane is an incredible useful tool, and I find myself using it pretty much every day.  The array of tools it offers are varied, and they deliver some much needed added abilities for me as a system developer/administrator.  I’d recommend installing it and seeing how you get on with it, just remember not to let your Users know about it.

Some of you may be aware of another Chrome extension called Level Up for Dynamics CRM/365, developed by Natraj Yegnaraman.  Level Up does a number of similar funcitons to CRM Power Pane, and has some additional options.  I have both installed in my browser, and have used them both extensively.  If you’d like to read more about Level Up, Kylie Kiser recently wrote a review of it and I’d recommend you have a look at her blog

Add Team Members to another Team

I’ve been working on a problem that’s been plaguing me for months and now, thanks to Microsoft Business Solutions MVP Aiden Kaskela and his Workflow Elements solution, I’ve finally managed to get it sorted.

The Problem

I wanted to be able to conditionally add members of one Team to another with a workflow, without hard coding the specific Users into the workflow.

My specific scenario was as follows:

If an Opportunity meets certain criteria, an Owner Team is automatically created and linked to the record.  The new Owner Team should then be updated to include Users who are in another Owner Team (the Proposals Team in my scenario).

Dynamically Add Users to Opportunity Team

Why couldn’t we just link the Proposals Team to the Opportunity I hear you ask?  Good question, it is because the Proposals Team are the minimum members of the New Opportunity Team and each Opportunity Team may have multiple other Users added to it from across the business.

We use the Teams as part of our custom integration with SharePoint; adding a User to the Team automatically assigns them specific permissions in SharePoint so we needed specific Teams per Opportunity.

I went round in circles for a long time trying to work out the most efficient solution for this, but I kept running into issues.  I was able to achieve steps 1-4 from the image above, but could never quite complete the process.  The closest I came was using the “Add User to Record Team” N:N associate step from Andrii Butenko’s Ultimate Workflow Toolkit, however this still required me to add a step per user and hard-code their name into the Workflow, which meant I would also then have to deactivate the workflow and update it if the composition of the Proposals Team changed.

I asked in the CRM Community Forum to see if anyone else could help but still ran into the same issues.  I reached out to Aiden Kaskela about a month ago to see if he could help and today he’s delivered in spectacular style

The Solution

Aiden has updated his Workflow Elements solution to include a new step – “Relationship – Associate From Query” (available from V2.1.0) which makes my scenario really simple to solve

Kaskela Workflow Solutions

Getting this step to work couldn’t be easier.  You add it to your workflow, then select the N:N Relationship Name, and then you have the option of using a System View, Personal View or FetchXML query to select the records to be associated.  For my scenario, this was triggered on the Team entity, and used a Personal View on the System User entity to find the members of the Proposals Team

Relationship - Associate from Query

I love the simplicity of this workflow step, and I can envisage a number of additional scenarios that this could be used for in my environment.  It makes it really easy to develop complex, dynamic association workflows.

Conclusion

Solving this problem has demonstrated two things to me:

  1. Dynamics CRM/365 is an amazing platform, and the flexibility it offers developers and customisers to deliver on much-needed functionality is so useful.  The system gets better with every release, and it makes it a pleasure to work with
  2. More importantly, the CRM/365 Community is incredible.  There are so many developers who create tools and plugins that they make available for free for us all to use, and they make my job so much easier.  Their creativity and generosity astounds me, and I am so grateful to them for everything they provide.

I could not recommend the Kaskela Workflow Elements solution enough.  I use it for so many applications, and this latest release makes it even better.  Please go and visit his website, download the solution and try it out; I guarantee you’ll love it.