Agent for UnprocessedDocuments After New Mail Arrives - lotus-notes

I wish to modify some documents when they arrive in my mail-in application. (I need to remove the $REF field which then allows dragging and dropping in to folders, but that's not the point.)
I have my agent set to After New Mail Arrives and to select unprocessed documents. Designer Help for Unprocessed documents states...
With the agent properties as ....
But the agent selects any document in the Inbox which has been modified or edited rather than just the new arriving email. There is also a delay of up to a minute before the agent runs.
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim collection As NotesDocumentCollection
Dim doc As NotesDocument
Set db = session.CurrentDatabase
Set collection = db.UnprocessedDocuments
Set doc = collection.GetFirstDocument()
While Not(doc Is Nothing)
If doc.Hasitem("$REF") Then
doc.Subject= "($Ref) " & doc.Subject(0) 'Only testing here will remove item if exists
Else
doc.Subject = "(No Ref) " & doc.Subject(0)
End If
Call doc.save(True,False)
' Call session.UpdateProcessedDoc( doc ) 'This doesn't appear to make any difference.
Set doc = collection.GetNextDocument(doc)
Wend
End Sub
Any help in running this type of script on only new mail and faster than at present, as users may edit the doc before the agent has run, would be appreciated.
Thanks

You might want to use trigger "Before new mail arrives" instead of "After new mail has arrived".
"Before new mail arrives" gets executed for every single new email immediately.
You get access to email document with
Dim session as New NotesSession
Dim doc as NotesDocument
Set doc = session.DocumentContext
You can find a good comparison of both triggers here.

The delay is normal. The agent manager doesn't guarantee immediate execution.
What you might be seeing is a "first-time effect". I.e., the first time you run the agent, all of the messages are "new" because they are newer compared to Last-Run timestamp on the agent - which defaults to day zero in the distant past. Have you tried this multiple times in the same database to see if what is in unprocessed documents on the second run?

Related

Lock current document using lotusscript

I have a document and a copy of the document. I want to create a document lock for the document using LotusScript.
I have separately put the current document inside Computer view and copy document inside Draft view. Below here the action to create a copy.
Create copy
Sub Click(Source As Button)
Dim ns As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim newdoc As NotesDocument
Dim dc As NotesDocumentCollection
Set db= ns.CurrentDatabase
Set dc= db.UnprocessedDocuments
If dc.Count<>1 Then
Messagebox "No or too many documents selected"
Exit Sub
End If
Set doc= dc.GetFirstDocument
Set newdoc= doc.CopyToDatabase(db)
Call newdoc.ReplaceItemValue("PStatus", "Draft")
Call newdoc.Save(True, False)
End Sub
Save Button
Sub Click(Source As Button)
Dim session As New NotesSession
Dim workspace As New NotesUIWorkspace
Dim db As NotesDatabase
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Dim view As NotesView
Set uidoc = workspace.CurrentDocument
Set db = session.CurrentDatabase
Set view = db.GetView("Draft")
Set doc = view.GetDocumentByKey("Draft", True)
vpswd = Inputbox$("Pls input code to save :")
If vpswd = "o" Then
uidoc.EditMode = True
Set doc = uidoc.Document
Set doc = view.GetFirstDocument
If doc.PStatus(0) = "Draft" Then
Set newdoc= doc.CopyToDatabase(db)
Call newdoc.ReplaceItemValue("PStatus", "Active")
Call newdoc.Save(True, False)
End If
Call uidoc.FieldSetText("SaveOptions" , "1")
Call uidoc.Save
Call uidoc.Close
End If
End Sub
How can I lock a current document every time I create a copy of the document? E.g current document will lock when the copy document created.
Another question is, for copy document, after I saved, I want to replace the current document as a copy document and a copy document will be a live document. While the current document will not be deleted from the database but deleted from Computer view and displayed in the "Archived" view.
And inside Draft view, copy document will be deleted from view. Any help will be appreciated. Thanks!
Locking a document should be implicit. Make sure that you use the Status field in all documents. E.g. when you create the copy, set the status of the copied document to "Copied" and the nw documof nt to "Draft". You have to cover all different status changes. Best is also to add one field that contains a unique document Id that can never be changed. Maybe also add a version number.
Examples of statuses:
Draft: document under construction (only one, cannot be copied)
Current: valid document (only one, can be copied)
Copied: Current document that has been copied (only one, cannot be copied)
Archived: document that has been replaced (more than one, cannot be copied)
It might help to create a STD, a State Transition Diagram.
I have done something similar. When a new version, as I call it, is created, it is the "draft" document. The previous document is an "approved" document. User get to see only the approved until the new version is approved, then the older approved document is archived out of the db. If you want all versions to stay in the db, but only the newer document brought up, you could use what I call a HistoryID field. Basically I have a field on every document that has up to three values in it:
TheUNID
ParentUNID:TheUNID:Parent
TheUNID:ChildUNID:Child
The "theUNID" field is computed when composed so it never changes. If a new version or draft is created, the HistoryID would now have two values:
TheUNID
TheUNID:ChildUNID:Parent
This let's you know you now have a new draft and the document you are on is actually a parent. The "ChildUNID" is actually "theUNID" of the draft document you just created.
On the child document or the new draft document you put in the HistoryID:
TheUNID
ParentUNID:TheUNID:Parent
This way your draft knows who it's parent is.
In your code in your db, you just have any document that gets opened check the HistoryID field first. If there is a child, you take the user to the child. It doesn't matter what parent gets opened or how far back the generations go, your code continues to iterate through the HistoryID until the current child document is found that doesn't have a parent. Sound good?
Let me know if you need clarification.

Object variable not set issue while adding text in to a notes document field

I did create a new form, where I have a table with 2 column's.
The last row of this table is the status.
I would like to do this: While my Notes Agent is running, it will go trough few action stepts. If one of the steps done, it should add the result of this action into a cell that I have definded.
Something like this:
----------------------------------------
| First action was done successfully. |
| Second action was done successfully. |
| Third action was done successfully. |
----------------------------------------
I tried the following code:
Sub Initialize
Dim sess As NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim item As NotesItem
Set db = sess.CurrentDatabase
Set doc = db.GetDocumentById("100B")
'do some code stuff.....
Set item = doc.AppendItemValue( "field_Status", "First action was done successfully." & Chr(13) )
Call doc.Save( False, True )
'do some code stuff.....
Set item = doc.AppendItemValue( "field_Status", "Second action was done successfully." )
Call doc.Save( False, True )
End Sub
Unfortunately I get always this message:
Object variable not set
Im not sure what is missing and if this is actually the right way how to realize my idea in code. I hope you have an idea / hint for me. Thank you.
First of all: NEVER write even one line of code without error handler.
That said: The easiest error handler would be
On Error Goto ErrorHandler
...your complete code goes here
EndOfRoutine:
Exit Sub
ErrorHandler:
Messagebox Err & "," & Error & " in line " & Erl
Resume EndOfRoutine
This would have given you this error line:
Set db = sess.CurrentDatabase
But wait: WHICH Object variable is not set?
Its "sess", as you only wrote:
Dim sess as NotesSession
Assigned, but not set... you need either:
Dim sess as New NotesSession
Or if you want to keep your line then just before the assignment of db:
Set sess = New NotesSession
Additional advice: finding a document via its NoteID is not a good Idea, and hardcoding it is even worse: the NoteId changes in different replicas. The same document will have a different NoteId in another replica of the database (e.g. A local replica or in a cluster). And if someone deletes that document and recreates it, then you need to update your code. Use a sorted view and GetDocumentByKey with a specific key you set in your document or a Profile document to store this information. Or at least use DocumentUniqueId, as this Is the same in every replica (though the recreation issue stays the same)

How can Lotus Notes detect whether a document is currently being opened?

As title.
How can Lotus Notes detect whether a document is currently being opened?
The solution should without "Document locking" because of User's User needs.
I have 1 maindoc and 1 subdoc, but subdoc and maindoc are not parents.
I use the "IsUIDocOpen" but it just worked at currentdocument.
Is there any other way to do this?
If you ask just for one client, then this is doable without document locking, but it needs some advanced techniques:
You can use NotesUIWorkspace to get a currently open document for any given backend document if you set parameter "newInstance" to false.
To get the currently open document (as uidocument, but of course you can use .Document property to get NotesDocument from it) you use the following code. If it returns nothing then the document is not open:
Dim ses as New NotesSession
Dim ws as New NotesUIWorkspace
Dim docToGetFrontendFor as NotesDocument
Dim uidoc as NotesDocument
Set docToGetFrontendFor = .... 'somehow get the document you wanna have the frontend for
Call ses.SetEnvironmentvar( "PreventOpen" , "TRUE" )
Set uidoc = ws.EditDocument( False, docToGetFrontendFor, False, "", True, False )
If not uidoc is Nothing then '-document was open already
'- do whatever with the frontend- document
Why the ses.SetEnvironmentvar( "PreventOpen" , "TRUE" )?
The EditDocument opens the document regardless of it being open already or not.
You need to prevent the document from opening, if it is not already open. Therefor you manipulate the "QueryOpen"- Event of the Form of the document:
Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
Dim ses as New NotesSession
Dim strPrevent as String
strPrevent = ses.GetEnvironmentstring( "PreventOpen" )
Call ses.SetEnvironmentVar( "PreventOpen" , "" )
If strPrevent = "TRUE" Then Continue = False
End Sub
So: The document does NOT open, if PreventOpen is set, so if it is not already open it will stay closed.
This approach has one big downside: The Notes Client has a "BUG": If you open a document and save it and then open it again with my code, then it will Open in a second window DESPITE the parameter "newInstance" set to false unless you closed and reopened that document.
Explained:
Create Document
Save document
Close document
Reopen document
Use my code
==> Works as the code just "reuses" the window
Create Document
Save document
Use my code
==> Will try to open a second instance for the document and then return NOTHING as this new instance does not open because of the code...

$Ref flag reappears even after deletion

We have an Xpage based Web mail-in application. Some emails are coming with $Ref field (response document). We wanted to convert them into normal document by removing $Ref field so that it will no longer a response one. We are able to delete the $ref field but it reappears again after saving the documents from web interface(xpage) and it makes all such documents again response document. This started happening recently and working well earlier. Can you please suggest what is the root cause? How to stop it?
Sub Initialize
On Error GoTo ErrorHandler
Dim s As New NotesSession
Dim db As NotesDatabase
Dim coll As NotesDocumentCollection
Dim doc As NotesDocument
Dim count As Long
Set db = s.Currentdatabase
Set coll = db.Unprocesseddocuments
MessageBox "Count : " & coll.Count
'Exit Sub
count = 1
Set doc = coll.Getfirstdocument()
While Not doc Is Nothing
If doc.Hasitem("$Ref") Then
Call doc.Removeitem("$Ref")
Call doc.Save(false, false)
End If
Print count
count = count + 1
Set doc = coll.Getnextdocument(doc)
Wend
Exit Sub
ErrorHandler:
MessageBox "Error " & Error & " at line " & Erl
Exit Sub
End Sub
Try changing your save method to Call doc.Save(true, false).
This will force the document save, and also prevent creating a response.
Also verify that you are running the agent on a server not accessable to active users. Also verify that you are only running your agent once, and that it isn't enabled on multible replicas.
Have you tried using If(doc.isResponse) Then instead of If doc.Hasitem("$Ref") Then
Note: I am looking in the java editor, but assume that the isResponse method is also in LS.
Wheather a document is a response is controlled by a property of the used form. As long as you do not assign another form that is a "Document" and not a "Response", you will most probably not be able to remove the "isresponse"- flag. As already told: most probably your xpage saves the "frontend" document after your backend manipulation and resets the state (form validation enabled in xpage properties). But without source code of the xpage one cannot tell.
Check the form name. Emails can use either the "Memo" form or the "Reply" form. Emails using the Reply form are response documents. You may also need to change the form to "Memo".

Getting values from profile document

I have some problem getting the value from the profile document. For creation, I followed these instructions:
Creating a profile form
1.Create a form with fields to hold the values you want to store in profile documents.
2.Choose Design - Form Properties and deselect "Include in Menu. and "Include in Search Builder"
3.Save the form.
4.Do not include the form in any view.
In the form I have only one field that is computed when composed (a number), with a computed value "0"
But with this code I can't retreive the field value:
Dim session as New NotesSession
Dim db as NotesDatabase
Dim doc as NotesDocument
Set db=session.CurrentDatabase
Set doc=db.GetProfileDocument("nameofprofiledoc")
dim number as integer
number=doc.fieldname(0)
Isprofile return true, but number is always "". For some reason it never gets the value
Perhaps it is because you haven't saved the document into the database yet. You can create an action button that let's you edit the document and then save it:
#Command( [EditProfile] ; formname );
Computed field formulas are not executed when you call GetProfileDocument. That's because GetProfileDocument is a "back-end" method. The form, with its various field definitions and formulas is used by the "front-end". (There's one exception: there's a ComputeWithForm method available in the back-end classes.) Anyhow. GetProfileDocument either loads a previously saved profile doc, or creates a new one that is essentially empty. #Ken's answer tells you how to manually create the profile document and save it so your code will find it. As an alternative, you can do the initialization in your code like this:
if ! doc.hasItem("fieldname") then
doc.replaceItemValue("fieldname",0)
end if
number=doc.fieldname(0)
That way you won't be dependent on someone manually creating the profile document. But if you are accessing the profile document from multiple places in code, and you can't predict which code might execute first, then you're probably going to want to create a script library with functions (or a class) and wrap GetProfileDocument in your own function (or method) to insure that all code paths will do the initialization properly.
As Richard mentions, when you call the method GetProfileDocument, the profile is created as a blank document, if not found in the database, (it will only have some initial fields that hold the profile name, conflict action, last editor)..
To initialize the profile, you might try to either:
1) Program the Form's QuerySave event to copy all items to the profile (then cancel the Save, to avoid storing the data in a regular document).
Sub Querysave(Source As Notesuidocument, Continue As Variant)
Dim db As NotesDatabase
Dim curdoc As NotesDocument
Dim profile As NotesDocument
Set curdoc = Source.Document
Set db = curodc.ParentDatabase
Set profile = db.GetProfileDocument(curdoc.Form(0))
Call curdoc.CopyAllItems(profile, True)
Call profile.Save(True, False)
Continue = False 'Don't allow the form to be saved to a regular document.
End Sub
Or:
2) Call GetProfileDocument to create a new profile or get an old one (you can tell the difference by checking the IsNewNote property. Then assign it a Form name, and compute it with this form.
In an Agent or an Action:
Sub InitializeProfile
Dim s As New NotesSession
Dim db As NotesDatabase
Dim profile As NotesDocument
Set db = s.CurrentDatabase
Set profile = db.GetProfileDocument("MyForm")
If profile.IsNewNote Then
Call profile.ReplaceItemValue("Form", "MyForm")
Call profile.ComputeWithForm(False, False) 'This will create the computed fields with their default values in the profile.
Call profile.Save(True, False)
End If
End Sub

Resources