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.
Related
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?
Before this I have post a question regarding my problem but still no answer. I will try again to post question about my problem in here. I will update new code and explaination about it. Before this I have done with dialog box, but I have been asked to change it, just use form in view.
My process start with open a form in view to set a new batch info. the form name is PCBatchInfo. In this form, I have 4 field which is BBatchNo, BInspector, BStart, and BEnd. Then in this form I have a button. When I click this button, it will do three process which is:
Create new batch info and save. This is current open document. I call this "dialogDoc".
Then, create a copy of Computer document. In this copy, it will replace BBatchNo as I created just now. I call this copy document as "newdoc" and original document as "doc"
After that, I create new report document and copy value from Computer document. Then I will get value from set batch no which is BInspector, BStart, and BEnd and insert into this report I call this "repdoc".
Below here my lotusscript in my button
Set db = session.CurrentDatabase
Set uidoc = ws.CurrentDocument
Set dialogDoc = uidoc.Document
Set view = db.GetView("Computer")
Set doc = view.GetFirstDocument
If doc.PStatus(0) = "Lock" Then
Msgbox "Complete PC Inspection first!"
Exit Sub
Else
'--set new batch info--'
dialogDoc.Form = "PCBatchInfo"
Call uidoc.FieldSetText("SaveOptions", "1")
Call uidoc.Save
answer% = Messagebox("Please confirm", 4,"Batch Number")
If answer% = 6 Then
While Not (doc Is Nothing)
If doc.PStatus(0) = "Active" Then
'--create new copy document--'
Set newdoc = doc.CopyToDatabase (db)
newdoc.DocumentId = doc.UniversalID
newdoc.PBatchNo = dialogDoc.BBatchNo(0)
Call newdoc.Save(True, False)
'-- set new acceptance form --'
Set repdoc = New NotesDocument (db)
repdoc.Form = "EmpPCSpec"
repdoc.ABatchNo = doc.PBatchNo(0)
repdoc.AStatus = doc.PStatus(0)
repdoc.ATagNo = doc.PTagNo(0)
repdoc.AFStatus = doc.PFStatus(0)
repdoc.AInspector = dialogDoc.BInspector(0)
repdoc.AStart = dialogDoc.BStart(0)
repdoc.AEnd = dialogDoc.Bend(0)
Call repdoc.ReplaceItemValue("AStatus", "Incomplete")
Call repdoc.ComputeWithForm(False,False)
Call repdoc.save(True,False)
doc.PStatus = "Lock"
Call doc.ComputeWithForm(False,False)
Call doc.save(True,False)
End If
Set doc = view.GetNextDocument(doc)
Wend
End If
End If
All this process I run on one button. So when I run this button, I have success to create new batch info, I can create copy of document, and I can create report document.
But the problem now are in copy document, it did not display BBatchNo. And in report document, it did not display BInspector, BStart, and BEnd that I want to get from PCBatchInfo form. The field left empty. I hope my explaination is clear about my problem. Any help from you guys I appreciated. Thanks!
Update Question
For all information save for new batch info using PCBatchInfo, the view is on (PCBatch). How can I get the value in this view? I only get value from "Computer" where it save Computer document.
I think your problem is right here
Set dialogDoc = New NotesDocument(db)
You carefully got the document object from the current UIDoc and then overwrote it with a new (blank) document.
I have a list of document in view. I also have a button inside view where I can create new document with value from existed document. For this new document, I will use different form for new document created. In my view, document is divide by Status. I also has dialogBox too for me to set batchNo for new documents.
So, the process is like this:
First, I will select document based on it status. So let's say I have 5 documents under "Spoilt" status, I can choose how many document I want. So I choose 2 documents only.
After document selected, I will click button to create one new documents using lotusscript. After button clicked, DialogBox will show. I insert batchNo and click OK.
Then, the code will check that status of documents and create new document by getting value from 3 documents and display into new document.
For example, I need to get value from field "PSerialNo" and "PType" from 2 documents. As you can see below. Value from document1 and document2, I want to insert in new document. So if document1, PSerialNo into WSerialNo1 and PType into WType1. And if document2, PSerialNo into WSerialNo2 and PType into WType2 and so on.
Document 1
Document 2
New Document
This is my code to create new document.
Set doc = dc.GetFirstDocument()
While Not (doc Is Nothing)
If doc.PStatus(0) = "Active" Then
Set newdoc = New NotesDocument(db)
newdoc.Form = "WriteOff"
newdoc.WABatchNo = wDialogDoc.WBatchNo(0)
newdoc.WType = doc.PType(0)
newdoc.WSerialNo = doc.PSerialNo(0)
newdoc.ComputeWithForm(False,False)
newdoc.save(True,False)
End If
doc = dc.GetNextDocument(doc)
Wend
My problem now, if I create new document, and I want to get value from two documents, it not insert into one new document but it insert into two different new document. How can I fix it. Any advice or help I appreciate. Thanks!
It's been more than 10 years since I wrote LotusScript, so I might be wrong.
Set doc = dc.GetFirstDocument()
Dim docCreated As Boolean 'flag a document was created
Dim i As Integer 'index for each document
docCreated = False
i = 0
While Not (doc Is Nothing)
If doc.PStatus(0) = "Active" Then
If Not docCreated Then 'only create a document for first doc
Set newdoc = New NotesDocument(db)
newdoc.Form = "WriteOff"
docCreated = True
End If
i = i + 1
newdoc.WABatchNo = wDialogDoc.WBatchNo(0)
' not sure about this part, but the idea is to set WType1 for first doc, WType2 for 2nd doc, and so on
Call newdoc.ReplaceItemValue("WType" & i, doc.PType(0))
Call newdoc.ReplaceItemValue("WSerialNo" & i, doc.PSerialNo(0))
End If
doc = dc.GetNextDocument(doc)
Wend
If docCreated Then
Call newdoc.ComputeWithForm(False,False)
Call newdoc.save(True,False)
End If
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...
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