Задачи бывают разными, но работа с документами - едва ли не в ТОП-3 всех задач в СЭД. Поэтому ничего удивительного в том, что иногда нам приходится с ними сталкиваться. Ну так вот: Ко мне поступила задача - собрать документы для дальнейшего формирования отчета или массового редактирования, для чего надо было сначала все найти. И вот тут как раз и понадобился такой инструмент, как "Поиск" ("Search").
Для первичного выбора, я воспользовался системной функцией "InputDialog" (Хотя вариантов для этого тоже может быть масса):
InputData = InputDialog('Автор|Подразделение|Статус ВНД|С|По';'||||';
'Аналитика:ПОЛ|Аналитика:ПОД|Признак:Действующий,Заменен,Отменен|Дата|Дата';
'Фильтр по Внутренним нормативным документам')
Для начала вполне сносно и позволяет пользователям неплохо прикладывать фильтр к перечню документов. По результатам работы InputDialog, мы получаем строку InputData, состоящую из строк разделенных символом "|", а значит, нам надо их разделить:
AuthorCode = SubString(InputData; '|'; 1)
PodrCode = SubString(InputData; '|'; 2)
Status = SubString(InputData; '|'; 3)
DataS = SubString(InputData; '|'; 4)
DataPo = SubString(InputData; '|'; 5)
Вот. Теперь у нас есть 5 реквизитов в виде отдельных строчек, которые нам будут полезны в нашем поиске, а точнее "фильтрации", и вот с ними можно уже работать. Для упрощения конструкции поиска, я собрал небольшую функцию "ФильтрПоискаПоЗначению":
SearchCriteria = Search.SearchCriteria
if Znach <<>> ''
Criterion = Search.SearchCriteria.Add(Recv)
if Data <<>> ''
Criterion.AddRange(Znach; Data)
else
Criterion.Add(Znach)
endif
endif
Не ахти что, да и не предусмотрен ввод строки с участием условий, но на текущий момент вполне достаточно.
Если же функцию не использовать, конструкция будет выглядеть так:
Search = Searches.CreateNew(ckEdocument)
Criterion = Search.SearchCriteria.Add('ISBEDocKind')
Criterion.Add(VEDCode)
Criterion = Search.SearchCriteria.Add('ISBEDocAuthor')
Criterion.Add(AuthorCode)
Criterion = Search.SearchCriteria.Add('Подразделение')
Criterion.Add(PodrCode)
Criterion = Search.SearchCriteria.Add('СтатусКПО')
Criterion.Add(Status)
Criterion = Search.SearchCriteria.Add('ISBEDocCreateDate')
Criterion.AddRange(DataS; DataPO))
Search.Show(ssmBrowse; FALSE)
И так, у нас есть строки для фильтрации и функция для работы. А значит, можно уже начинать:
Search = Searches.CreateNew(ckEdocument)
ФильтрПоискаПоЗначению(Search;'ISBEDocKind';VEDCode) // Здесь указывается Вид документа
ФильтрПоискаПоЗначению(Search;'ISBEDocAuthor';AuthorCode)
ФильтрПоискаПоЗначению(Search;'Подразделение';PodrCode)
ФильтрПоискаПоЗначению(Search;'СтатусКПО';Status)
ФильтрПоискаПоЗначению(Search;'ISBEDocCreateDate';DataS; DataPO)
Search.Show(ssmBrowse; FALSE)
Альтернативный способ описания поиска, можно описать и иначе (Подробности):
Search = Searches.CreateNew(ckEdocument)
SearchCriteria = Search.SearchCriteria
SearchCriteria.AddWhere = Format('(%0:s.ISBEDocKind = %1:s and
%0:s.ISBEDocAuthor = %2:s and
%0:s.Podrazdelenie = %3:s and
%0:s.StatusKPO = %4:s and
%0:s.ISBEDocCreateDate >= %5:s and %0:s.ISBEDocCreateDate <= %6:s)';
ArrayOf(DOCUMENTS_ALIAS;VEDCode;AuthorCode;PodrCode;Status;FormatSQLDate(DataS);FormatSQLDate(DataPO)))
Search.Show(ssmBrowse; FALSE)
Хотя если уж честно, работать они будут фактически одинаково.
А вот если вы решите перебирать все документы и отсеивать их по приницпу "сито":
Search = Searches.CreateNew(ckEdocument)
SearchJob = Search.Execute
foreach Str in SearchJob
if Str.ISBEDocKind == VEDCode and
Str.ISBEDocAuthor == AuthorCode and
Str.Подразделение == PodrCode and
Str.СтатусКПО == Status and
DateDiff("D"; Str.ISBEDocCreateDate; DataS) <= 0 and
DateDiff("D"; Str.ISBEDocCreateDate; DataPO) >= 0
DocId = AddSubString(Str.ID;DocId;';')
endif
endforeach
То тут диагноз один: "Месье знает толк в извращениях!"
Поиск и выборку документов можно делать различными способами и тут решение за вами, что вам удобнее в вашем случае и как много условий вам надо соблюсти. В справке Директума есть все необходимые указатели и методы реализации ваших затей, так что если все сделать верно, то поиск будет не только точным, но и быстрым. И если все сделаем верно, в результате, мы увидим весь отфильтрованный список документов соответствующих условиям и соберем его в переменную "SearchJob", с которой можно уже начинать работать далее.
SearchJob = Search.Execute // Поиск.Выполнить
Если же вы хотите не только отфильтровать но и посмотреть на результат вашего выбора, тогда добавляем Метод "Show":
Search.Show(ssmBrowse; FALSE) // смотрим и восхищяемся (процесс при этом не тормозит!)
//ИЛИ
Search.Show(ssmSelect; TRUE) // Открываем список и выбираем нужный нам документ!
SearchJob = Search.SelectedContents // Получаем его в переменную "SearchJob"
//ИЛИ
Search.Show(ssmMultiSelect; TRUE) // Выбираем МНОГО!!! документов!
SearchJob = Search.SelectedContents // Получаем их в переменную "SearchJob"
В целом, это все что я хотел сказать - это не исчерпывающая информация, но как пища для размышления, она вполне сгодится. В остальном - пробуйте, экспериментируйте, делитесь успехами!
---------------------------------
При работе с циклами, а тем более с большими массивами данных, иногда хочется понимать, как же долго нам еще ждать и стоит ли вообще это делать. Для этого в СЭД "Директум", существует шикарная системная функция "CreateProgress" и все ей сопутствующее. Как же это работает?
Для начала, нами объявляется некий цикл, который состоит из некоего числа витков. Исходя из примера выше, я могу указать количество витков, как "SearchJob.Count". А значит мы уже можем начать и описать весь процесс, да так, что следить за ним будет не скучно:
Progress = CreateProgress("Минутку, пожалуйста..."; SearchJob.Count) //Создаем прогресс-бар
Progress.Show //Выводим его на свет Божий
//Далее, внутри цикла вносим значение текста, например наименование документа
Progress.Text = Document.Requisites('Наименование').DisplayText
//В конце цикла ставим указатель на новый элемент
Progress.Next
//После цикла указываем на конец
Progress.Hide
Скажем больше: это дело можно еще и останавливать, и даже притормаживать. Всего-то и надо, что добавить параметры в функцию:
Progress = CreateProgress("Минутку, пожалуйста..."; SearchJob.Count; TRUE;'Отменяем?')
/* Параметры:
• Caption – заголовок окна индикатора;
• Max – максимальная позиция полосы прогресса, расположенной в окне индикатора. Если значение не задано, то форма индикатора не будет отображать полосу прогресса;
• IsBreakable – возможность прерывания процесса, ход выполнения которого отображает индикатор: True, если процесс можно будет прервать, нажав на кнопку Прервать, False, если процесс невозможно прервать. Если процесс невозможно прервать, то на форме индикатора не будет кнопки Прервать;
• BreakProcessText – имя кнопки для прерывания процесса;
• TextLineCount – допустимое число строк в метке с текстом, который будет отображаться над полосой прогресса. */
И вуа-ля:
А если нажмем на кнопку "Прервать", то процесс "замрет" до окончательного решения:
Жмем "Ок" и все закончилось, жмем "Отмена" и процесс движется дальше как ни в чем не бывало :)
Конструкция не сложная, но при этом очень помогающая наглядности происходящего процесса и его приблизительный процент выполнения. Это стоит использовать в работе с большими массивами данных.
-----------------------------------------------
В начале своего "Творчества", я уже сталкивался с разными вариантами реализации одних и тех же процессов, но всегда вспоминал слова своей первой преподавательницы по программированию Хиценко Валентины Павловны: "Слишком много действий" (с). И правда, очень часто, стараясь сделать все верно и поставить максимальное кол-во блокировок, мы создаем нереальную тучу лишних действий, которые можно избежать без проблем.
Вот один из примеров:
Ссылаясь на статью выше, я уже расписал как мы получаем документы и как видим прогресс, а вот как мы потом поступаем с этим списком? Давайте допустим, что нам надо собрать из выбранных документов реквизиты по списку и вывести их на экран (Ну, чтобы не расписывать как это можно вбивать в Excel или файл)
Вариант 1 (так делать не надо!):
DocId = ''
Index = 1
All = 0
FullText = Сейчас() & CR
foreach Str in SearchJob
DocId = AddSubString(Str.ID;DocId;';')
All = All + 1
endforeach
Progress = CreateProgress("Минутку, пожалуйста..."; All)
Progress.Show
foreach ReqName in CSubString(DocId; ';')
if Assigned(ReqName)
Docum = EDocuments.GetObjectByID(ReqName)
Progress.Text = Docum.Requisites('ISBEDocName').DisplayText
Per1 = Index
Per2 = Docum.Requisites('ISBEDocName').Value
Per3 = Docum.Requisites('Дата4').Value
if Assigned(Docum.Requisites('Дополнение3').Value)
Per4 = Docum.Requisites('Дополнение3').Value
else
Per4 = ''
endif
if Assigned(Docum.Requisites('Ответственный').Value)
User = ServiceFactory.GetUserByCode(Docum.Requisites('Ответственный').Value)
Per5 = User.FullName
else
Per5 = 'Ответственный не указан'
endif
if Docum.Requisites('ISBEDocName').IsNull
Per2 = ''
endif
if Docum.Requisites('Дата4').IsNull
Per3 = ''
endif
Progress.Next
Index = Index + 1
FullText = FullText & Per1 & '|' & Per2 & '|' & Per3 & '|' & Per4 & '|' & Per5 & CR
endif
endforeach
FullText = FullText & Сейчас()
Progress.Hide
EditText(FullText)
В данном примере, ужасно практически все.
1. Дважды проход по списку Документов, а ведь многие этим грешат!
2. "ALL" - это вообще что? Зачем?
3. Per№ - к чему?
4. Assigned(ReqName) - бессмысленно;
5. проверки на Null - сомнительное удовольствие...
6. FullText из кучи Per`oв - м-да...
В общем это все клише и косяки которые я встречал на практике и смог вспомнить вот так навскидку. Эти мои 6 указателей даже не все, но даже с ними: Легче сжечь и построить заново, чем разбираться в этом мусоре.
Данный образец работал на 150 документов - 15 секунд.
Теперь вариант 2:
Index = 1
FullText = Сейчас()
Progress = CreateProgress("Минутку, пожалуйста..."; SearchJob.Count)
Progress.Show
foreach ReqName in SearchJob
Docum = EDocuments.GetObjectByID(ReqName.ID)
Progress.Text = Docum.Requisites('ISBEDocName').DisplayText
FullText = AddSubString(Index;FullText;CR)
FullText = AddSubString(Docum.Requisites('ISBEDocName').Value;FullText;'|')
FullText = AddSubString(Docum.Requisites('Дата4').AsString;FullText;'|')
FullText = AddSubString(Docum.Requisites('Дополнение3').AsString;FullText;'|')
try
FullText = AddSubString(ServiceFactory.GetUserByCode(Docum.Requisites('Ответственный').Value).FullName;FullText;'|')
except
FullText = AddSubString('Ответственный не указан';FullText;'|')
endexcept
Progress.Next
Index = Index + 1
endforeach
FullText = FullText & Сейчас()
Progress.Hide
EditText(FullText)
Вот все тоже самое, но смотрится и работает иначе: на 150 документов - 9 секунд (сокращение времени на 40%)
Что изменилось:
1. Docum.Requisites('Дата4').AsString - заменила конструкцию на вычисление значения на Null;
2. Избавились от лишних действий;
3. Убрали лишние переменные;
4. Убрали ненужные проверки.
Возможно, я выбрал плохой пример, но он показателен хотя бы тем, что наглядно показывает как часто иногда мы делаем лишние телодвижения в попытке предусмотреть все или избавиться от мимолетной ошибки на манер "Невозможно привести значение NULL к Строке". Так же мы часто перестраховываемся и дробим то, что дробить не нужно, ведь каждая переменная - это ресурс, который хоть и велик, но все же конечен. Будьте внимательнее и чаще смотрите на форум, вполне вероятно, ваш вопрос уже кто-то рассматривал и выписал пути его решение и обхода.
В любом, случае, все это мелочи, но именно из них собираются наши будущие проекты.
Всем удачного дня!
Спасибо за статью! В целях улучшения вашего кода замечу следующие моменты:
1. Используйте функции на русском только если нет английского аналога;
2. При указании параметров функций ставьте пробел после разделителя переменных (; - точки с запятой);
3. Строковые значения оборачивайте в " " - двойные кавычки, а не ' ' - одинарные.
Cтандарты по ведению прикладной разработки в DIRECTUM
Анатолий, ваша ссылка ведёт на закрытый ресурс.
Недостаточно прав для доступа
У вас нет прав на доступ к странице: https://club.directum.ru/post/164842 (403 Forbidden)
Видимо эта информация доступна только для партнеров) Можете попробовать попросить эти материалы в службе поддержки или у закрепленного за вами партнера.
Анатолий Придыбайло, попросить могу, но дадут ли доступ? да и в любом случае - вы написали, а за мной самообучение не заржавеет
Согласен, заголовок статьи выглядит как общеполезный материал для любого разработчика. Может кто-то из "партнёров" может попросить обнародовать материал для обычных разработчиков?)
Анатолий, с первыми двумя пунктами понятно. А почему строки нужно писать в двойных кавычках, а не в одинарных?
>>Анатолий, с первыми двумя пунктами понятно. А почему строки нужно писать в двойных кавычках, а не в одинарных?
Этот стандарт идет из C# и по последним правилам в SQL запросах для строк должны использоваться ' ' - одинарные кавычки, а т.к. в Directum частенько приходится писать запросы к базе, этот стандарт, использовать " " - двойные кавычки оправдан.
Да, в плане стандартов SQL-запроса получается удобнее вложить одинарные в двойные, чем потом экранировать. На моей практике всегда было наоборот.
Для поисков не стоит забывать про ограничения на количество возвращаемых результатов, а для получения просто значений реквизитов на больших объемах данных лучше вообще отказаться от поисков в пользу чистого SQL (учитывать права сложнее, но работает значительно быстрее).
Авторизуйтесь, чтобы написать комментарий