Most documents have a life cycle, or a series of stages that they go through before they are considered complete. The life cycle of a document in a workflow should closely mirror the business process that it is automating. There is usually a field that identifies the status of the document at each step of the way. Often, when the status of the document changes, certain actions are required from specific individuals. Email is the means of notifying those individuals of the need for action on their part. A change in a document's status is the event that triggers the email.
Identification of these triggers should take place during the analysis phase and is usually refined during prototyping and testing. The process flow is often diagrammed using a flowchart. It is also useful to create a table listing the triggers, any significant fields and document conditions, and the recipients and the messages. Combined with the process flowchart, this is an excellent means of communicating with the client, as well as a good testing tool. As the application is tested , you can check the behavior with the table and flowchart for accuracy. Table 24.6 is a sample table for a change request application. The message text in this table is abbreviated to save space in the illustration, but this should give you the general idea. The actual mechanism of getting triggers to work is simple.
Table 24.6. Email Triggers for the Change Request Form
|Trigger||Form Logic||Recipients||Part of the Message Text|
|New||cStatus = "New"||Document owner||A new change request has been submitted. Please review.|
|In Review||cStatus = "In Review"||Reviewers||The document owner has released a change request for review.|
|Accepted||cStatus = "Accepted"||All||The change request has been accepted and will be processed as a change order.|
|Rejected||cStatus = "Rejected"||All||The change request has been rejected for the following reasons.|
There are two principal means of sending mail notifications when the triggers have been established. First, you can include a field on the form that executes @MailSend (usually with parameters) when the document is saved. Second, you can create agents that send the mail notifications.
A distinct disadvantage exists when using a field on a form to send mail notifications. @MailSend doesn't work for Web clients , and neither does LotusScript. If you have Web clients participating in a workflow application, using a field on the form to generate email notifications will fail.
You can easily work around this limitation by moving the code to send mail notifications out of the form and into an agent. Because an agent runs on the server, it does not depend on the type of client accessing the application. It can execute whenever a document is created or modified, or on a set schedule, and can test for the trigger conditions. If the conditions for the trigger are met, it can then send email notifications to the appropriate person(s). For obvious reasons, this is the preferred method of mail-enabling an application.
Using a Field to Send Mail
Typically, @MailSend with parameters is used to send email notifications based on the triggers established. For example, @IsNewDoc returns a Boolean True at the time the document is composed . Used in a computed field to send mail with @MailSend() , @IsNewDoc sends mail unexpectedly when the document is first created, not when it is saved:
REM "This sends mail when the document is new" ; @If(@IsNewDoc; @Success; @Return("")); @MailSend("Steve Kern/ndur6";"";"";"Change Request"; "A new change request has been added. Please review!"; ""; [IncludeDocLink])
Adding @IsDocBeingSaved to the formula solves that problem. A typical formula for sending email when a document is first composed and saved follows :
REM "Test for a new doc and if the doc is being saved" ; REM "and send e-mail. @Return stops the execution of the "; REM "formula if either of the two conditions is not true" ; @If(@IsNewDoc & @IsDocBeingSaved ; @Success; @Return("")) ; REM +"Send a doclink to this document" ; @MailSend("Steve Kern/ndur6";"";"";"Change Request"; "A new change request has been added. Please review!" ;""; [IncludeDocLink])
To use a field to trigger the routing, take advantage of the order in which formulas are evaluated: from left to right and from top to bottom. Email triggers cannot rely on the value of the Status field to send mail but must instead detect whether the status has changed. To accomplish this, you can use hidden fields at the bottom of the form. One stores the value of the Status field. This field is usually named something such as cOldStatus and has a formula of this:
@If(@IsNewDoc; "New"; cStatus)
Another field actually sends the mail. If this field is positioned before the cOldStatus field, the value of cStatus can be compared to the value of cOldStatus. If they are not the same, an appropriate email can be sent. How does this work? It's simple. The cStatus and the Mail Send fields are above cOldStatus in the form and are evaluated before cOldStatus. Therefore, if a user changes cStatus, the value of cOldStatus is computed after the Mail Send field. cOldStatus retains the value of the status field before the user changed it.
REM "Compare the values of cStatus and cOldStatus" ; REM "If they are different, send e-mail" ; @If(cStatus = "New"; @Return(""); @Success); SendTo := "Steve Kern/ndur6"; Subject := "Status Change"; Remark := "The Approval Status of your Change Request # " + cCRNumber + " has changed from " + cOldStatus + " to " + cStatus + ". Double-Click this document link to review ==> "; @If(@IsDocBeingSaved & cOldStatus != cStatus; @MailSend(SendTo; ""; ""; Subject; Remark; ""; [IncludeDoclink]); "")
Although using fields to route mail works, it is not the preferred method. The preferred method is to use agents, which are discussed next .
Using Agents in Workflow
With the advent of Web browsers as an alternative client for Domino applications, the use of agents to mail-enable an application has become very important. A Web client does not have the capability to send mail, whereas the Notes client does. For this reason, you should consider avoiding the use of mail functions in forms.
One of the goals of a workflow application is to reduce the amount of time it takes to complete a given task. This is also known as cycle time. Often a set period is allotted to process a document, after which it is considered past due. After an initial notification, reminders are sent on a daily basis to reviewers. When a document is past due, the application logic might then route it to the database administrator or the reviewer's supervisor for further action. Agents are used to send notifications at appropriate times, thereby compressing the cycle time.
You can implement this workflow by creating agents for each status trigger. For example, you create an agent to send notifications when a document is created, and the status is New. You can create another agent to send reminders on a daily basis. When the number of reminders reaches a certain level, another agent can be used to escalate the request to the next person. Of course, you'll want to create an agent to notify the appropriate persons whenever the document is approved or denied ”some sort of final notification. Other agents can be created to meet the needs of your application.
You could always create a single agent to handle all possible status conditions for a document. However, it is much simpler to create a single agent for each notification trigger. The logic is less complex, so it's easier to debug any problems that might arise.
It is a good idea to log the activity of both users and agents. You can track the activity in multivalue fields. Here is a list of typical tracking fields:
The agent in Listing 24.1 is used to remind reviewers that a document is past due for their approvals . It incorporates the techniques discussed and the fields listed previously:
Listing 24.1 Reminder Agent
REM "Send reminder of new requests to the Approver"; REM "Schedule is daily, not on weekends"; SELECT Form = "OSR2" & cDocStatus = "New"; REM "Initialize and increment Reminder counter"; jnReminders := @If(@IsAvailable(nReminders); nReminders + 1; 1) ; FIELD nReminders := jnReminders ; REM "Write doc history"; jcDocHistory := "Reminder #" + @ Trim(@Text(jnReminders)) + " of Office Supply Requisition emailed to " + @Name([Abbreviate];cApprover) + " on " + @Text(@Now;"S2") ; @SetField("cDocHistory"; cDocHistory : jcDocHistory) ; REM "Add agent tracking fields." ; FIELD dNotified := dNotified ; @SetField("dNotified"; @If(dNotified = ""; @Now; dNotified : @Now)); FIELD cLastAgent := cLastAgent ; jcLastAgent := "Remind Approver" ; @SetField("cLastAgent"; @If(cLastAgent = ""; jcLastAgent; cLastAgent : jcLastAgent)); REM "Email reminder"; jcSendTo :=@Name([Abbreviate]; cApprover); jcCC := "" ; REM "For testing, add a blind carbon copy" ; jcBCC := ""; jcSubject := "Reminder: Requisition for " + cAssociate ; jcBody := "You have not responded to the Requisition for " + cAssociate + ". Click the doclink at the bottom of this message!" ; @MailSend(jcSendTo; jcCC; jcBCC; jcSubject; jcBody; ""; [IncludeDoclink])
Listing 24.2 shows an escalation agent. This agent uses a value stored in a profile document as a late trigger. It compares that value with the value in the nReminders field and sends notification to the supervisor.
Listing 24.2 Escalation Agent
REM "Escalate new Requests that have a value in nReminders"; REM "that is greater than the late trigger." ; REM "Schedule is daily, not on weekends"; jnLateTrigger := @GetProfileField("GPF"; "nLateTrigger") ; SELECT Form = "OSR2" & cDocStatus = "New" & nReminders > jnLateTrigger; REM "Add agent tracking fields." ; FIELD dNotified := dNotified ; @SetField("dNotified"; @If(dNotified = ""; @Now; dNotified : @Now)); FIELD cLastAgent := cLastAgent ; jcLastAgent := "Escalate Approval" ; @SetField("cLastAgent"; @If(cLastAgent = ""; jcLastAgent; cLastAgent : jcLastAgent)); jcAdmin := @GetProfileField("GPF"; "cAppManager") ; jcSendTo := @Name([Abbreviate];jcAdmin) ; REM "Set up the Body message" ; jcRemark := @Name([CN];cApprover) + " has not responded to this request. Please review this request, and take appropriate action. Thanks!" + @NewLine + "Click this doclink to open the request --> " ; REM "Set up the Subject field" ; jcSubject := "Request approval past due!" ; REM "Use @MailSend with parameters to notify recipients"; @MailSend( jcSendTo ; "" ; "" ; jcSubject ; jcRemark ; "" ; [IncludeDoclink] ) ; REM "Add a line to the document history field." ; jcDocHistory := "Approval escalation notice emailed to " + jcSendTo + " on " + @Text(@Now; "S2") ; @SetField("cDocHistory" ; cDocHistory : jcDocHistory)
You can use the techniques in these two examples to build applications with a complex workflow. Using the Formula language is entirely appropriate for this kind of mail notification. It is far simpler to create an agent such as this using the Formula language than it is to use LotusScript. LotusScript requires many more lines of code to accomplish the same result. However, LotusScript should be considered when the logic is more complex. An example of an agent with more complex logic involved in notifications can be found in the next section.
Part I. Introduction to Release 6
Whats New in Release 6?
The Release 6 Object Store
The Integrated Development Environment
Part II. Foundations of Application Design
Advanced Form Design
Using Shared Resources in Domino Applications
Using the Page Designer
Adding Framesets to Domino Applications
Automating Your Application with Agents
Part III. Programming Domino Applications
Using the Formula Language
Real-World Examples Using the Formula Language
Writing LotusScript for Domino Applications
Real-World LotusScript Examples
Writing Java for Domino Applications
Real-World Java Examples
Enhancing Domino Applications for the Web
Part IV. Advanced Design Topics
Accessing Data with XML
Accessing Data with DECS and DCRs
Security and Domino Applications
Creating Workflow Applications
Analyzing Domino Applications
Part V. Appendices
Appendix A. HTML Reference
Appendix B. Domino URL Reference