Invalid Use Of Null

beloff

Well-known member
24.07.2012
113
1
#1
Опять завелся какой то демон. Проблема кроется в том что скрипт написан на кнопку в форме, которая вызывается диалогбоксом, т.е. посмотреть дебагером не получится (кроме предпросмотра непосредственно этой формы из дизайнера вызвать её в клиент, но это не вариант, т.к. куча данных берется непосредственно из документа). Ситуация: БД персонал, оформление отпусков, попытка оформить новый отпуск вызывает ошибку invalid use of null в строке
ItemNumTmp = Arraygetindex(vArrCur, Cdat(vDatePeriod0)).
Я так понял что гемор именно с этой функцией (Arraygetindex), т.к. работает она через пень-колоду (встречал в нете). Смысл функции - взять индекс
выбранной даты в массиве, т.к. у человека который работает не первый год может быть куча отпускных периодов и нужно как-то оперировать с данными в массивах с этим индексом. Дополнительная проблема - не выявлена закономерность, т.к. на некоторых работает, на некоторых отказывается, на некоторых сначала отказывается, потом ни с того, ни с сего работает. Ломаю голову уже 3-й день. Может, переписать скрипт вставив вместо этой долбаной функции обычный цикл сравнения одного значения с каждым элементом массива. Вобщем, СОС, ай нид хэлп


<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">скрипт на кнопке</div></div><div class="sp-body"><div class="sp-content">Sub OnAcceptChanges(Byval bNew As Boolean)
Dim StrTmp As String
Dim vNames(0 To 10) As String
Dim ItemNum As Long, i As Long, j As Long, k As Long
Dim vIndex As Variant, vArrCur As Variant, vArrCurCopy As Variant
Dim vDate0 As Variant, vDate1 As Variant
Dim vDatePeriod0 As Variant, vDatePeriod1 As Variant
Dim VacationLen As Long, ItemNumTmp As Long
Dim bRedim As Boolean, bCompens As Boolean

Dim ThisDoc As NotesDocument
Dim Wks As New NotesUIWorkspace

'On Error Goto errmsg

Call Wks.CurrentDocument.Refresh
Set ThisDoc = Wks.CurrentDocument.Document

'// Проверка полей, обязательных для заполнения
Dim str_listfields List As String

StrTmp = Lcase$(Wks.CurrentDocument.FieldGetText("VacKind"))
str_listfields("VacKind") = "Вид отпуска"

If StrTmp Like "*компенсац*" And Wks.CurrentDocument.FieldGetText("VacType")="Трудовой" Then
bCompens = True
Else
bCompens = False
str_listfields("VacBegin") = "Дата начала отпуска"
str_listfields("VacEnd") = "Дата окончания отпуска"
End If

If Lib_CheckMandatoryFields(Wks.CurrentDocument, str_listfields) = False Then Exit Sub

If ThisDoc.HideOrder(0) = "1" Then
vArrCur = ThisDoc.RC_VacType
If vArrCur(0) <> "" And Wks.CurrentDocument.FieldGetText("VacType") <> vArrCur(0) Then
Messagebox "Не допустимо предоставление и трудового и социального отпусков в одной записке.", 16, "Отпуск"
Exit Sub
End If
End If

If Not bCompens Then
vDate0 = Cdat(ThisDoc.GetItemValue("VacBegin")(0))
vDate1 = Cdat(ThisDoc.GetItemValue("VacEnd")(0))
End If

StrTmp = Wks.CurrentDocument.FieldGetText("VacLen")
If StrTmp <> "" And Not Isnumeric(StrTmp) Then
Messagebox "Неверно указана продолжительность отпуска.", 16, "Ошибка ввода"
Exit Sub
Elseif StrTmp = "" And Not bCompens Then
If Cdat(vDate1) >= Cdat(vDate0) Then
StrTmp = Cstr(vDate1 - vDate0)
Call ThisDoc.ReplaceItemValue("VacLen", StrTmp)
End If
End If

If Wks.CurrentDocument.FieldGetText("VacType") = "Трудовой" Then
If StrTmp = "" Then
Messagebox "Не задана продолжительность трудового отпуска.", 16, "Ошибка ввода"
Exit Sub
End If
i = Clng(StrTmp)
StrTmp = Wks.CurrentDocument.FieldGetText("VacPeriod")
If StrTmp = "" Or StrTmp = "-" Then
Messagebox "Не задан период трудового отпуска.", 16, "Ошибка ввода"
Exit Sub
End If
vDatePeriod0 = Cdat(Fulltrim(Strleft(StrTmp, "-")))
vDatePeriod1 = Cdat(Fulltrim(Strright(StrTmp, "-")))
If Not bCompens Then
If Cdat(vDate0)+i-1 > Cdat(vDate1) Then
Messagebox "Даты начала и окончания отпуска не согласуются с продолжительностью.", 16, "Ошибка ввода"
Exit Sub
End If
End If

VacationLen = i
vArrCur = ThisDoc.GetItemValue("RC_VacPeriodStart")
ItemNumTmp = Arraygetindex(vArrCur, Cdat(vDatePeriod0))
End If

' имена полей
vNames(0) = "Indexes"
vNames(1) = "RC_VacKind"
vNames(2) = "RC_VacType"
vNames(3) = "RC_VacPeriod"
vNames(4) = "RC_VacLen"
vNames(5) = "RC_VacBegin"
vNames(6) = "RC_VacEnd"
vNames(7) = "RC_VacOrder"
vNames(8) = "RC_VacCache"
vNames(9) = "RC_VacNotes"
vNames(10) = "RC_VacFlags"

' Определение номера текущей записи (редактирование/добавление), 0 - новая запись
StrTmp = Cstr(ThisDoc.GetItemValue("CurItem")(0))
If Not Isnumeric(StrTmp) Then ItemNum = 0 Else ItemNum = Clng(StrTmp)
If bNew Then ItemNum = 0

vIndex = ThisDoc.GetItemValue("Indexes")
If vIndex(0) <> "" And Not bCompens Then
vDatePeriod0 = ThisDoc.GetItemValue("RC_VacBegin")
vDatePeriod1 = ThisDoc.GetItemValue("RC_VacEnd")
For i = 0 To Ubound(vIndex)
If Isdate(vDatePeriod0(i)) And Isdate(vDatePeriod1(i)) Then
If (ItemNum-1) <> i And Not (Cdat(vDate1) < Cdat(vDatePeriod0(i)) Or Cdat(vDate0) > Cdat(vDatePeriod1(i))) Then
Messagebox "Заданный период пересекается с существующим: "+_
Format$(vDatePeriod0(i), "dd.mm.yyyy")+" - "+Format$(vDatePeriod1(i), "dd.mm.yyyy")+Chr$(10)+_
"( № "+Cstr(i+1)+" в списке )", 16, "Ошибка ввода"
Exit Sub
End If
End If
Next i
End If

If ItemNum = 0 Then
'Добавление новой записи
bNew = True
If vIndex(0) <> "" Then
bRedim = True
ItemNum = Ubound(vIndex)+1
Else
bRedim = False
End If
Else
bNew = False
bRedim = False
ItemNum = ItemNum-1
End If

For i = 0 To Ubound(vNames)
vArrCur = ThisDoc.GetItemValue(vNames(i))
If bRedim Then Redim Preserve vArrCur(0 To ItemNum)
If vNames(i) = "Indexes" Then
vArrCur(ItemNum) = Cstr(ItemNum+1)
Elseif vNames(i) = "RC_VacBegin" Or vNames(i) = "RC_VacEnd" Then
If bCompens Then
vArrCur(ItemNum) = "-"
Else
vArrCur(ItemNum) = Format(ThisDoc.GetItemValue(Strright(vNames(i), "_"))(0), "dd.mm.yyyy")
End If
Else
StrTmp = ThisDoc.GetItemValue(Strright(vNames(i), "_"))(0)
If StrTmp = "" Then StrTmp = "-"
vArrCur(ItemNum) = StrTmp
End If
Call ThisDoc.ReplaceItemValue(vNames(i), vArrCur)
Next i
vIndex = ThisDoc.GetItemValue("Indexes")
Call ThisDoc.ReplaceItemValue("CurItem", vIndex(ItemNum))

If VacationLen <> 0 Then
Dim OldLen As Long

If Not bNew And Isnumeric(ThisDoc.GetItemValue("VacLen0")(0)) Then
OldLen = Clng(ThisDoc.GetItemValue("VacLen0")(0))
Else
OldLen = 0
End If
VacationLen = VacationLen - OldLen
If VacationLen <> 0 Then
vArrCur = GetFieldNumArray(ThisDoc, "RC_VacPeriodUsed")
vArrCur(ItemNumTmp) = vArrCur(ItemNumTmp)+VacationLen
If vArrCur(ItemNumTmp) < 0 Then ' неверные данные ?
vArrCur(ItemNumTmp) = 0
VacationLen = VacationLen + vArrCur(ItemNumTmp) ' correct delta
End If
Call ThisDoc.ReplaceItemValue("RC_VacPeriodUsed", vArrCur)
vArrCur = GetFieldNumArray(ThisDoc, "RC_VacPeriodUnused")
vArrCur(ItemNumTmp) = vArrCur(ItemNumTmp)-VacationLen
If vArrCur(ItemNumTmp) < 0 Then vArrCur(ItemNumTmp) = 0
Call ThisDoc.ReplaceItemValue("RC_VacPeriodUnused", vArrCur)
Call ThisDoc.ReplaceItemValue("VacLen0", Clng(ThisDoc.VacLen(0)))
End If
End If

' Сортировка записей по дате
vIndex = ThisDoc.GetItemValue("Indexes")
If Ubound(vIndex) > 0 Then
vArrCur = ThisDoc.GetItemValue("RC_VacBegin")
vArrCurCopy = ThisDoc.GetItemValue("RC_VacPeriod")

'Call DoInsertSortByDate(vArrCur, vIndex)
Call DoInsertSortByDateAlt (vArrCur, vArrCurCopy, vIndex)

Call ThisDoc.ReplaceItemValue("RC_VacBegin", vArrCur)
For i = 1 To Ubound(vNames)
If vNames(i) <> "RC_VacBegin" Then
vArrCur = ThisDoc.GetItemValue(vNames(i))
vArrCurCopy = vArrCur
For k = 0 To Ubound(vIndex)
j = Clng(vIndex(k))-1
vArrCur(k) = vArrCurCopy(j)
Next k
Call ThisDoc.ReplaceItemValue(vNames(i), vArrCur)
End If
Next i
For i = 0 To Ubound(vIndex)
j = Clng(vIndex(i))-1
If j = ItemNum Then ItemNumTmp = i
vIndex(i) = Cstr(i+1)
Next i
ItemNum = ItemNumTmp
Call ThisDoc.ReplaceItemValue("Indexes", vIndex)
End If

Call Wks.CurrentDocument.Refresh
Call ThisDoc.ReplaceItemValue("RC_VacListDsp", ThisDoc.GetItemValue("RC_VacList")(ItemNum))

'errmsg :
' Msgbox Erl()
End Sub
 

NickProstoNick

Статус как статус :)
Lotus team
22.08.2008
1 802
21
#2
как-то странно... у кнопки вроде нет такого события как OnAcceptChanges
Ну это так... лирика...
Диалог всегда можно обмануть.
Например в форме диалога при открытии можно инициализировать документ по UNID... ну в общем варианты есть.
Раз определил место ошибки - знач какой-то из твоих массивов пуст. Т.е. даже не инициализирован
 

VladSh

начинающий
Lotus team
11.12.2009
1 260
5
#3
Функция ArrayGetIndex работает прекрасно, надо "уметь готовить".
Она возвращает Null, когда значения нет в массиве. Т.е. результат надо проверять на IsNull.

Конкретно ваша ошибка в том, что переменная ItemNumTmp описана As Long, т.е. когда ArrayGetIndex возвращает Null этот самый Null загнать в Long невозможно. О чём, собственно, оно и пытается вам сказать.

И ещё. Даты я бы не сравнивал со значениями в массиве (см. в хэлпе инфу по автоконвертации массива в стринги), а перегонял бы в строку - CStr.

P.S. Дурной тон вываливать огромный скрипт, большая часть которого тут бесполезна; лучше порезать его, - 3-4 строчки для данного вопроса вполне достаточно.
 

beloff

Well-known member
24.07.2012
113
1
#4
vArrCur = ThisDoc.GetItemValue("RC_VacPeriodStart")
If Ubound(vArrCur) >0 then
ItemNumTmp = Arraygetindex(vArrCur, Cdat(vDatePeriod0))
Elseif ItemNumTmp = 0

End If

Teamstudio Analyzer
 

savl

Lotus team
28.10.2011
2 131
102
#5
beloff

Сделай ItemNumTmp Variant'ом и проверяй на IsNull или:
Код:
dim k as variant
k = Arraygetindex(vArrCur, Cdat(vDatePeriod0))
if IsNull(k) then
ItemNumTmp = 0
else
ItemNumTmp = k
end if
 

beloff

Well-known member
24.07.2012
113
1
#6
savl
спасибо, мне как раз ночью это и приснилось. Чуть повыше практически то же самое и вписал. Все равно спасибо
 

beloff

Well-known member
24.07.2012
113
1
#7
все равно что-то не то по сути. Проверка на isnull это просто попытка обойти проблему.

Итак: массив берется и становится эквивалентным полю по размерности (vArrCur = ThisDoc.GetItemValue("RC_VacPeriodStart"))
например в поле 6 значений (тип поля Time/Date List). В поле "RC_VacPeriodStart" следующие значения
03.01.2008 01:00:00 ZE3
26.01.2008 01:00:00 ZE3
03.01.2009 01:00:00 ZE3
03.01.2010 01:00:00 ZE3
03.01.2011 00:00:00 ZE3
03.01.2012 00:00:00 ZE3

Спрашиваем msgbox'om значения каждого элемента массива и вот что он нам выдает (на каждый элемент массива перечисленный выше)
03.01.2008
26.01.2008
03.01.2009
03.01.2010
02.01.2011 23:00:00
02.01.2012 23:00:00

я думаю теперь понятно почему он выдает NULL, т.к. я ищу значение 02.01.2012 в указанном массиве, а там такого естественно нету. Как я и думал тут какая-то херня с типами данных в массиве. Тут раньше был совет по поводу конвертации в String, только я ничего не понял (сорри за ламерство, у меня был большой перерыв в программинге на Лотусе, кое-что просто подзабыл)
 

VladSh

начинающий
Lotus team
11.12.2009
1 260
5
#10
Сделай ItemNumTmp Variant'ом и проверяй на IsNull
Ну и зачем? А "на подумать" что-то оставить?) Оно и так следует из моего сообщения. Тем более что приснилось) я иногда код тоже во сне пишу))

Код:
if IsNull(k) then
ItemNumTmp = 0
else
ItemNumTmp = k
end if
ItemNumTmp = 0 логически неверно, т.к. тем самым мы говорим, что значение в массиве всё-таки есть, и находится в 0-м элементе.

все равно что-то не то по сути. Проверка на isnull это просто попытка обойти проблему.
Если в массиве не будет значения, даже со всеми преобразованиями, то будет ошибка, потому в тему. Если не верите, можно оставить ItemNumTmp As Long :) Т.о. это первая часть проблемы.
Спасибо, что отписались по 2-й, интересное наблюдение.
DateValue делаем передаваемой строке в ArrayGetIndex или отказались от него и цикл с перебором по значениям item'а?
 

savl

Lotus team
28.10.2011
2 131
102
#11
VladSh
про =0 согласен, лохонулся. Обычно пишу -1, тогда точно знаю, что элемента нет.
 

beloff

Well-known member
24.07.2012
113
1
#12
VladSh
DateValue делаем передаваемой строке в ArrayGetIndex или отказались от него и цикл с перебором по значениям item'а?
перед поиском написал цикл чтобы перебором всех элементов в массиве каждый конвертировался в нормальную дату (т.е. через DateValue)

vArrCur = ThisDoc.GetItemValue("RC_VacPeriodStart")
For a = 0 To Ubound(vArrCur)
'Msgbox vArrCur(a)
vArrCur(a) = Datevalue(vArrCur(a))
'Msgbox vArrCur(a)
Next

ItemNumTmp = Arraygetindex(vArrCur, Cdat(vDatePeriod0))
 

VladSh

начинающий
Lotus team
11.12.2009
1 260
5
#13
beloff
Я бы строку с GetItemValue и цикл заменил на
Код:
Const IN_DT = "RC_VacPeriodStart"
arrDt = Evaluate({@Text(} + IN_DT + {; "D0T0")}, nd)