Embedded View + обновление документа с ним

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
Дано:
- документ со всякими полями
- встроенная вьюшка

болезнь:
-получение списка доков во вьюшке
-обновление документа, после действий во вьюшке

костыли больному...
при старте дока, на PO делаем source.EditMode и source.Refresh, получаем (глобально для формы) unid

Симпотомы и последствия:
получить список из встроенной вьюшки можно через...
Код:
Function SelectedDocsEmbedded() As NotesDocumentCollection
    On Error GoTo ErrH
    GoTo Continue
ErrH:
    Error Err, RaiseError
Continue:
    Dim session As New NotesSession
    Dim db As NotesDatabase
    Dim col As NotesDocumentCollection
    Dim doc As NotesDocument
    Set db = session.CurrentDatabase
    Dim NDC As NotesDocumentCollection
    Set NDC= db.CreateDocumentCollection
    Stop
    'Print "Check Unprocrssed"
    Set col=db.UnprocessedDocuments
    Set doc = col.GetFirstDocument
    'preserve docs collection (to avoid delete after db object desctruction)
    While Not doc Is Nothing
        Print doc.UniversalID
        Call NDC.Adddocument(doc)
        Set doc = col.GetNextDocument(doc)
    Wend
    'Print {selected docs count:} NDC.Count
    Set SelectedDocsEmbedded=NDC
End Function
если не создать коллекцию, в либе (из кот. вызывается ф-ция) db.UnprocessedDocuments иcчезнет вместе с "окончанием" ф-ции
Хорошо, коллекцию обработали через кнопку (экшн во встроенной вьюшке), у нас есть текуший док: wks.CurrentDocument, который и является доком, в кот. встроена вьюшка. Нужно обновить поля из экшена!
Вот тут и будет засада - поля обновить можно, НО uidoc.Update вызывает малевича, ибо обновление сносит напрочь объект вcтроенной вьюшки и "все что было в коде" экшена, код для кнопки, кот. обновляет поле в основном доке, по результатам обработки выделенных доков в эмбеде
Код:
Sub Click(Source As Button)
    On Error Goto ErrH
    Goto Begin
ErrH:
    Error Err, RaiseError
Begin:
    Dim wks As New NotesUIWorkspace, uidoc As NotesUIDocument, lst
    Set uidoc=wks.CurrentDocument
    lst=SetDiscountsUI
    If Not uidoc Is Nothing Then
        If (Len(lst(0))>0 And uidoc.Document.GetItemValue({Form})(0)={dProfile}) Then
            Dim doc As NotesDocument
            Set doc=uidoc.Document
            Call doc.Replaceitemvalue({unids}, Fulltrim(Arrayunique(Arrayappend(doc.Getitemvalue({unids}),Split(lst(0),{;})))))
            uidoc.Save
        End If
    End If
 
End Sub

что можно предпринять?
Т.к. придется вызвать сохранение дока, то логично разместить код именно в нем (инмемори вариант невозможен в лоб)
но и там (мы же помним - код вызывается из встроенной вьюшке) низя вызвать uidoc.Update, как и вызвать uidoc.close:wks.EditDocument(true, doc)
потомучта опять - все объекты будут уничтожены (даже если doc получить из БД) и все что в форме, внутри события будет убито после close. Такова специфика вызова из встроенного объекта

Делаем доп. документ! В нем создаем поле с юнид текущего uidoc.Document.UniversalID, форма по вкусу
В форме нового дока может быть код, на QC (потому что открытым нам док не нужен) и вот там... мы запишем:
Код:
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
    Dim unid As String
    unid=Source.Document.GetItemValue({unid2Open})(0)
    Print {Unid to open:}unid
    Dim wks As New NotesUIWorkspace, doc As NotesDocument
    Set doc=wks.CurrentDatabase.Database.GetDocumentByUNID(unid)
    Call wks.EditDocument(True,doc)
    Continie=True
End Sub
а PS основного дока
Код:
Sub Postsave(Source As Notesuidocument)
    On Error Goto ErrH
    Goto Begin
ErrH:
    Error Err, Raiseerror
Begin:
 
    If source.EditMode Then
        Dim doc As NotesDocument
        Set doc=db.CreateDocument()
        Call doc.ReplaceItemValue({unid2Open},unid)
        Call doc.ReplaceItemValue({form},{Refresh})
        Dim uidoc As NotesUIDocument
        Set uidoc=wks.EditDocument(True, doc)
        source.Close
        uidoc.Close
        Exit Sub
        'source.Refresh
    End If
End Sub
"от така х..ня маляты" :) unid - это я глобально для формы задал (в PO иницаализировал)
Получили матрешку, для обхода "ограничений"
все это я проверял на Release 9.0.1FP10 SHF252
 
  • Нравится
Реакции: VladSh

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
ws.viewrefresh
никогда не мучался
не будет работать..., на форме есть поле программно наполняемое на обновление
Код:
Sub Queryrecalc(Source As Notesuidocument, Continue As Variant)
    On Error Goto ErrH
    Goto Continue
ErrH:
    Error Err, RaiseError
Continue:
    Dim doc As NotesDocument, docD As NotesDocument, unids, unidsD
    Set doc=source.Document
    unids=doc.GetItemValue({unids})
    Dim item As NotesItem
    unidsD=GetListByUnids(unids)
    If Isempty(unids) And Not Isempty(doc.GetItemValue({unids})) Then
        doc.RemoveItem({unids})
        doc.Save False, False
    Else
        If (Ubound(doc.GetItemValue({unids}))<>Ubound(unids)) Then
            Print {Unids field->}Ubound(doc.GetItemValue({unids}))
            Call doc.ReplaceItemValue({unids},unids)
            Call doc.Save(False, False)
        End If
    End If
    Set item=doc.ReplaceItemValue({unidsD}, unidsD)
    item.SaveToDisk=False
'    If Isarray(unidsD) Then Print {Result->}Join(unidsD,{;}) Else Print {nothing to display}
End Sub
просто ничего не произойдет - не будет получен новый список
 

ToxaRat

Чёрный маг
Green Team
06.11.2007
3 332
42
BIT
0
предпочитаю работу от обратного - в доке ничего не менять
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
231
Хороший хак!

Только мне не нравится идея принудительного перевода в режим редактирования. Я бы сделал вычитывание unid на PMCh.
И постоянные Save не нравятся. + если нужно обязательное соответствие значений текущего дока и тех данных, что записываются с помощью Save, то лучше бы сделать один Save на PO основного дока, перед "обновлением".
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
Только мне не нравится идея принудительного перевода в режим редактирования.Я бы сделал вычитывание unid на PMCh.
весь смысл в обновлении CFD поля, иначе что-то не срослось у меня
И постоянные Save не нравятся
там только в случаях лажи в самом поле - например удалили док, а unid остался
часть кода к сути не относится (та, где QR save делает) просто не стал дергать куски, Save нужен - где по смыслу не получится иначе (передача из эмбеда)
 

Gandliar

Lotus Team
16.02.2004
564
26
BIT
110
Если во вьюшке нажали кнопку - то workspace.currentDocument - это документ в котором вьюшка, его ж можно поменять после выполнения действий кнопки.
Если во вьюшке открыли док - то на событие QueryOpenDocument вьюшки тоже получим workspace.currentDocument - это документ в котором вьюшка.
Я предположу, что на это событие надо перехватывать открытие дока и открывать через workspace.editDocument и получить uidocNew
В итоге имеем uidoc (в котором вьюшка) и uidocNew (открытый из встроенной вьюшки), запоминаем uidoc в глобальной переменной библиотеки
Затем повесить триггер On Event postSave From uidocNew Call updateUidocParent
где Sub updateUidocParent(Source As NotesUIDocument) - и есть функция обновления дока вьюшки
Вот такая теория :)

Причем чтобы работало on Event, в форме uidocNew должен быть скрипт, я пишу в declaration uidoc as NotesUIDocument, doc as NotesDocument, а в postopen set uidoc = Source и set doc = uidoc.Document
если в форме нет скрипта, то on event не срабатывает.
 
Последнее редактирование:

swyatogor

Green Team
24.02.2014
593
24
BIT
0
эээ... а можно уточнить зачем вообще весь этот геморой??)) чисто с практической точки зрения.. для чего применяется?
 

swyatogor

Green Team
24.02.2014
593
24
BIT
0
-получение списка доков во вьюшке
вообще не проблема -
set view = db.getview("нужный вид");
set dc = view.getalldocumentsbykey(doc.universalid);

как то так) документы во встройке же у Вас категоризированы по какому то признаку? не все подряд же.. в моем примере по UNID родителя..

-обновление документа, после действий во вьюшке
а не нужно во встроенной вьюшке производить никакие действия)) всё делается на стороне документа..
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
вообще не проблема -
set view = db.getview("нужный вид");
set dc = view.getalldocumentsbykey(doc.universalid);

как то так) документы во встройке же у Вас категоризированы по какому то признаку? не все подряд же.. в моем примере по UNID родителя..


а не нужно во встроенной вьюшке производить никакие действия)) всё делается на стороне документа..
интерактив с пользователем, пользователь выделяет доки из встроенного вью...
модальность не подойдет (через пиклист) потому как связанные списки (будут лишние нажатия) и валтузение мышью
и потому нужно произвоидить действия именно в встроенной ьюшке - это гораздор удобнее. В результате получем четкую связь между полями в доке и выделенными документами в эмбеде
1547544931477.png
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
231
Я предположу, что на это событие надо перехватывать открытие дока и открывать через workspace.editDocument и получить uidocNew
В итоге имеем uidoc (в котором вьюшка) и uidocNew (открытый из встроенной вьюшки), запоминаем uidoc в глобальной переменной библиотеки
...
Вот такая теория :)
Это теория. А практика говорит о том, что может быть открыто несколько документов с этим же встроенным видом. Да и просто переключение вкладки в Лотус приводит к разрушению контекста UI-документа, и при последующем NotesUIDcoument.Refresh к малевичу.
Через всё это проходило столько разрабов, что даже страшно вспомнить :)
 

VladSh

начинающий
Lotus Team
11.12.2009
1 797
158
BIT
231
а не нужно во встроенной вьюшке производить никакие действия)) всё делается на стороне документа..
Это Вам не нужно. А вообще возможность обновление UI-документа из действий вида очень удобная вещь. Я когда-то давно пытался делать всё это запуском агента (ToolsRunMacro) и у меня даже получалось оттуда сделать Refresh без малевича, но это было в каком-то очень специфическом случае, не пригодном для повсеместного использования.
На стороне документа это делается только потому, что с действий в виде ни у кого не получалось это сделать.
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
Если во вьюшке нажали кнопку - то workspace.currentDocument - это документ в котором вьюшка, его ж можно поменять после выполнения действий кнопки.
и что это даст?
запоминаем uidoc в глобальной переменной библиотеки
при разрушении контекста - получим невнятные сообщения об ошибках и 0 эффекта вместо желаемого
Затем повесить триггер On Event postSave From uidocNew Call updateUidocParent
где Sub updateUidocParent(Source As NotesUIDocument) - и есть функция обновления дока вьюшки
Вот такая теория
любое изменение как только произойдет любое обновление - будет убит весь конекст эбмеда, со всеми вытекающими последствиями (малевич и т.п.)
 

swyatogor

Green Team
24.02.2014
593
24
BIT
0
интерактив с пользователем, пользователь выделяет доки из встроенного вью...
а предложенный мною метод этого не отменял кстати)
где то на просторах инета (может даже и здесь) нашел одну библу..
Код:
Public Class NotesUIMessageQueue As NotesMessageQueue
Private nTimer As NotesTimer
Private mList List As String
Private mListCount As Integer
Public Property Get Message As String
Dim msg As String
msg=NotesMessageQueue..Message
If msg<>"" Then
mList(CStr(mListCount))=msg
mListCount=mListCount+1
End If
Message=msg
End Property
Public Property Get MessageList As Variant
MessageList=mList
End Property
Public Property Get MessageListCount As Integer
MessageListCount=mListCount
End Property   
' Method for overloading
Public Sub OnMessageAction
Print mName+":", Me.Message
End Sub

Sub New(MQName As String, checkInterval As Integer) , NotesMessageQueue(MQName)
Set nTimer = New NotesTimer(checkInterval)
On Event Alarm From nTimer Call nAlarm
End Sub
Private Sub nAlarm(pTimer As NotesTimer)
pTimer.Enabled=False
If NotesMessageQueue..Messages>0 Then Call Me.OnMessageAction
'     While NotesMessageQueue..Messages>0
'     mList(Cstr(mListCount))=NotesMessageQueue..Message
'     mListCount=mListCount+1
'     Wend

pTimer.Enabled=True
End Sub
End Class
и
Код:
Public Class NotesMessageQueue
Private hMQ As Long
Private mName As String
Private misOwner As Integer
Private mAutoClose As Integer
Public Property Get QueueName As String
QueueName = mName
End Property
Public Property Get isOwner As Integer
isOwner = misOwner
End Property
Public Property Get AutoClose As Integer
AutoClose = mAutoClose
End Property
Public Property Set AutoClose As Integer
mAutoClose = AutoClose
End Property
Public Property Get Messages As Integer
If hMQ<>0 Then Messages=apiMQGetCount(hMQ)
End Property
Public Property Get Message As String
Dim msg As String
Dim ret As Integer
Dim status As Integer
msg = Space(MQ_MAX_MSGSIZE-1) + Chr(0)
apiCall "MQGet:"+mName, apiMQGet(hMQ, msg, MQ_MAX_MSGSIZE, 0, 0, ret)
Message=Left(msg,ret)
End Property
Public Property Set Message As String
If Message<>"" Then apiCall "MQPut:"+mName, apiMQPut(hMQ, NOPRIORITY, Message, LenB(Message), 0 )
End Property
Sub New(MQName As String)
Dim status As Integer
misOwner=True
mAutoClose=True
mName=Trim( mqName)
If Len(mName)>0 Then
status= apiMQCreate(mName,NOPRIORITY,0)
If status=ERR_DUPLICATE_MQ Then misOwner=False
apiCall "MQOpen", apiMQOpen(mName,0,hMQ)
Else
Error 1024+110,"MessageQueue name is empty string…"
End If
End Sub
Sub Delete
If misOwner And mAutoClose And hMQ<>0 Then Call apiMQClose(hMQ,0)
End Sub
End Class
 

swyatogor

Green Team
24.02.2014
593
24
BIT
0
и вот еще..
Код:
' функция обработки ошибок CAPI, содрана и переделана у D. Katz
Private Sub apiCall(apiCallName As String, Status As Integer)
'This function takes the 16-bit status value (set by returning from most api function calls), and returns the "english" version of the error.

Dim Err_Mask As Integer
Dim errorStr As String
If Status = 0 Then Exit Sub
'Strip the two highest-order bits from status (remember, status is only 16 bits total). Those two bits show location of the error (local or remote server, etc.)
'They should be stripped so that the reference number of the error can be looked up appropriately.
Err_Mask = &H3FFF
Status = Status And Err_Mask

'We need to pass the C function a string it can overwrite (so that the memory for the string is already allocated when the OSLoadString is called)
errorStr =String(255,CLng(0))
Status = apiOSLoadString(0, Status, errorStr, Len(errorStr) - 1)
Error Status, apiCallName + "::"+errorStr
End Sub
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 980
611
BIT
427
а предложенный мною метод этого не отменял кстати)
где то на просторах инета (может даже и здесь) нашел одну библу..
Код:
Public Class NotesUIMessageQueue As NotesMessageQueue
Private nTimer As NotesTimer
Private mList List As String
Private mListCount As Integer
Public Property Get Message As String
Dim msg As String
msg=NotesMessageQueue..Message
If msg<>"" Then
mList(CStr(mListCount))=msg
mListCount=mListCount+1
End If
Message=msg
End Property
Public Property Get MessageList As Variant
MessageList=mList
End Property
Public Property Get MessageListCount As Integer
MessageListCount=mListCount
End Property  
' Method for overloading
Public Sub OnMessageAction
Print mName+":", Me.Message
End Sub

Sub New(MQName As String, checkInterval As Integer) , NotesMessageQueue(MQName)
Set nTimer = New NotesTimer(checkInterval)
On Event Alarm From nTimer Call nAlarm
End Sub
Private Sub nAlarm(pTimer As NotesTimer)
pTimer.Enabled=False
If NotesMessageQueue..Messages>0 Then Call Me.OnMessageAction
'     While NotesMessageQueue..Messages>0
'     mList(Cstr(mListCount))=NotesMessageQueue..Message
'     mListCount=mListCount+1
'     Wend

pTimer.Enabled=True
End Sub
End Class
и
Код:
Public Class NotesMessageQueue
Private hMQ As Long
Private mName As String
Private misOwner As Integer
Private mAutoClose As Integer
Public Property Get QueueName As String
QueueName = mName
End Property
Public Property Get isOwner As Integer
isOwner = misOwner
End Property
Public Property Get AutoClose As Integer
AutoClose = mAutoClose
End Property
Public Property Set AutoClose As Integer
mAutoClose = AutoClose
End Property
Public Property Get Messages As Integer
If hMQ<>0 Then Messages=apiMQGetCount(hMQ)
End Property
Public Property Get Message As String
Dim msg As String
Dim ret As Integer
Dim status As Integer
msg = Space(MQ_MAX_MSGSIZE-1) + Chr(0)
apiCall "MQGet:"+mName, apiMQGet(hMQ, msg, MQ_MAX_MSGSIZE, 0, 0, ret)
Message=Left(msg,ret)
End Property
Public Property Set Message As String
If Message<>"" Then apiCall "MQPut:"+mName, apiMQPut(hMQ, NOPRIORITY, Message, LenB(Message), 0 )
End Property
Sub New(MQName As String)
Dim status As Integer
misOwner=True
mAutoClose=True
mName=Trim( mqName)
If Len(mName)>0 Then
status= apiMQCreate(mName,NOPRIORITY,0)
If status=ERR_DUPLICATE_MQ Then misOwner=False
apiCall "MQOpen", apiMQOpen(mName,0,hMQ)
Else
Error 1024+110,"MessageQueue name is empty string…"
End If
End Sub
Sub Delete
If misOwner And mAutoClose And hMQ<>0 Then Call apiMQClose(hMQ,0)
End Sub
End Class
это "просторы" от Дмитрия Акулова
и нотусовый таймер - еще тот кастыль ;)
 

swyatogor

Green Team
24.02.2014
593
24
BIT
0
на Onselect встроенного вида вешаем обработку:
Код:
        Dim  MQ As New NotesMessageQueue("SZ_ID_Doc")
    '' очищаем.. а то копятся..
        For i =1 To     MQ.Messages
            stroka = MQ.Message
        Next
        
        MQ.Message = source.CaretNoteID
        MQ.Autoclose = False

а на документе уже обрабатываем по полученному
 

Gandliar

Lotus Team
16.02.2004
564
26
BIT
110
Ну не знаю.

У меня работает без Малевича следующая конструкция:

В документе А (в режиме редактирования) есть кнопка "Добавить контрагента", по нажатию на которую создается документ Б.
Если в документе Б нажали кнопку сохранить, то на событие postsave, в документ А заполняются данные по контрагенту и документ А рефрешится.
Все происходит через конструкцию On Event postSave From uidocNew Call bPartnerPostSaveAfterAdd
 

swyatogor

Green Team
24.02.2014
593
24
BIT
0
В документе А (в режиме редактирования) есть кнопка "Добавить контрагента", по нажатию на которую создается документ Б.
Если в документе Б нажали кнопку сохранить, то на событие postsave, в документ А заполняются данные по контрагенту и документ А рефрешится.
Все происходит через конструкцию On Event postSave From uidocNew Call bPartnerPostSaveAfterAdd
это не тот случай - описанный в задании у топик стартера)
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!