Работа с генератором случайных чисел и способы применения в DIRECTUM на примере сценария "Генератор электронных документов"

10 7

Здравствуйте уважаемые читатели.
Не так давно перед нашей командой поставили задачу - разработать механизм автоматического анализа Номенклатуры дел и подготовкой дел к удалению. Использовать стандартные механизмы DIRECTUM не представлялось возможным, т.к. в процессе анализа НД требовалось автоматическое составление актов, согласование актов, проверка ЭД перед удалением и мн. другое. Разработали функционал мы достаточно быстро, но возникли сложности в тестировании данного функционала, т.к. на базе заказчика в одном деле могло содержаться от 2000 до 15000 ЭД. В ручную создавать ЭД в таком количестве - Сизифов труд. Автоматизировать процесс путем копирования документов - документы будут иметь одинаковые названия, что приведет к неудовлетворительному результату при демонстрации функционала заказчику.

В связи с этим нами было принято решение о разработке сценария "Генератор электронных документов".

Текст сценария с комментариями:

//Организуем диалог с пользователем, где запрашиваем ID папки, в которую будут генерироваться документы, и количество документов.
Dial = InputDialog('Введите количество генерируемых документов|Введите ID папки (подпапки вычисляются автоматически)';'10|103750';'Number|Number')
DocNumber = SubString(Dial;'|';1)
StartFolderID = SubString(Dial;'|';2)

//Создаем базу слов, которые будут использоваться для составления наименований электронных документов.
StartWord = 'Договор;Письмо;Заключение;Служебная записка;Приказ;План;Распоряжение;Извещение;Телефонограмма;Котировка;Счет;Акт'
SecondWord = 'на оказании услуг с ;на заключение договора с;на расторжени контракта с;на оформление командировки в;на удаленную работу в;на прокопку ямы в;на проведение работ в;на проведение встречи в;на вывоз мусора в;на ликвидацию проблем в;на арест имущества в;на санитарную обработку в;на поставку товаров в;на отстрел белок в;на отключение воды в;на отключение электроэнергии в;на обучение в;на стажировку в;на проверку исполнения норм ГОСТ в;на запуск проекта интеграции в;на удаление дел в;на попить воды в;на непонятные нужды в;на то что я уже устал писать примеры текста документов в'
ThirdWord = 'Современные технологии;ОДК;СНПЗ;Саратов-Нефтегаз;ВолгаМост;ООО Гроздь;Министерство обороны;Сколково;Администрацию президента;Ростехнологии;Роснано;ЖКО;МВД;ФСБ;Госнаркоконтроль;ИНТЕК;Связь Медиа Сервис;Эхо Москвы;ИА Взгляд;ИА Версия;Завод Зубодробительных станков'

//Формируем пустые списки для дальнейшего заполнения
FolderList = CreateList()        //Список папок всех уровней вложенности, которые вложенны в текущую папку
FullFolderList = CreateList()    //Список папок с нумератором для генератора случайных чисел
StartWordList = CreateList()     //Список слов, которые должны идти первыми в наименовании документа
SecondWordList = CreateList()    //Список слов, которые должны идти вторыми в наименовании документа
ThirdWordList = CreateList()     //Список слов, которые должны идти третьими в наименовании документа

//Заполнение списка слов, которые должны идти первыми в наименовании документа
i=0
Foreach Word in CSubString(StartWord;';')
  StartWordList.Add(i;Word)
  i=i+1
endforeach
StartWordCount=i

//Заполнение списка слов, которые должны идти вторыми в наименовании документа
i=0
Foreach Word in CSubString(SecondWord;';')
  SecondWordList.Add(i;Word)
  i=i+1
endforeach
SecondWordCount=i

//Заполнение списка слов, которые должны идти третьими в наименовании документа
i=0
Foreach Word in CSubString(ThirdWord;';')
  ThirdWordList.Add(i;Word)
  i=i+1
endforeach
ThirdWordCount=i

//Создание прогресса для отображения пользователям
BarFolders = CreateProgress('Создание списка папок обнаруженных корневой папке';;FALSE)
BarFolders.Show
s=''
      //Вызов корневой папки
      EnterFldObject = Folders.GetObjectByID(StartFolderID)
      EnterFld = EnterFldObject.Info
      //Создание контента папок и добавление в него корневой папки
      ContentFldList = CreateList()
      ContentFldList.Add(EnterFld.ID;EnterFld.ID)
      //Добавление корневой папки в общий список папок
      FolderList.Add(EnterFld.ID;EnterFld.Name)
      N = 0
      //Добавление корневой папки в список папок с нумератором. 
      FullFolderList.Add(N;EnterFld.ID)
      //Смена заголовка процесса.
      BarFolders.Title = 'Поиск папок '
      //Начинаем цикл, обнаруживающий вложенные папки до исчезновения содержимого списка контента папки
      While (ContentFldList.Count > 0)
        //Получение первой папки из текущей записи списка контента
        Fold = Folders.GetObjectByID(ContentFldList.Values(0))
        //Получение содержимого папки
        CFld = Fold.Info.AllContents
        //Начало анализа содержимого папки
        Foreach Fld in CFld
          //Если текущее содержимое папки является папкой то.....
          if (fld.ComponentType = ctFolder)
            //Если обнаруженная папка уже раннее обнаруживалась(перекресные ссылки) 
            //И папка является пользовательской папкой
            //И папка не является папкой поиска
            if (not Assigned(FolderList.FindItem(Fld.ID))) and (Fld.FolderType = ftUserFolder) and (Fld.FolderType <> ftSearch) 
              //Добавляем данные о найдейной папки в список контента
              ContentFldList.Add(Fld.ID;Fld.ID)
              //Добавляем данные о найденой папке в общий список папок
              FolderList.Add(Fld.ID;Fld.Name)
              //Добавляем данные о найденой папке в нумеруемы список папок
              FullFolderList.Add(N;Fld.ID)
              //Добавление обнаруженной папки в титул процесса
              BarFolders.Text = Fld.Name
              //Увеличение нумератора на еденицу
              N = N + 1
            Endif
          Endif
        Endforeach
        //Удаление первой записи списка контента
        ContentFldList.Delete(0)
      endwhile

//Установка нумератора для цикла в первоначальное положение    
num = 0
//Установка заголовка процесса
BarFolders.Title = 'Генерация документов '
//Начало цикла для генерации документов с Num по количество документов, указанные пользователем
While Num <= DocNumber
  //Генерация случайного числа для выбора папки, в которую будет сохранен текущий сгенерированный документ
  FolderNum = Abs(SQL('Select Rand()') * N-1)
  //Генерация случайного числа для первого слова в наименовании документа
  StartWordNum = Abs(SQL('Select Rand()') * StartWordCount-1)
  //Генерация случайного числа для второго слова в наименовании документа
  SecondWordNum = Abs(SQL('Select Rand()') * SecondWordCount-1)
  //Генерация случайного числа для третьего слова в наименовании документа
  ThirdWordNum = Abs(SQL('Select Rand()') * ThirdWordCount-1)
  //Составление имени ЭД на основе сгенерированных данных
  EName = StartWordList.Values(StartWordNum) & ' ' & SecondWordList.Values(SecondWordNum) & ' ' & ThirdWordList.Values(ThirdWordNum)
  //Помещение имени ЭД в заголовок процесса
  BarFolders.Text = EName
  //Получение обьекта папки, в которую будет помещен текущий ЭД
  Fold = Folders.GetObjectByID(FullFolderList.Values(FolderNum))
  //Создание документа из шаблона
  Edoc = Edocuments.CreateNewFromTemplate('ПЭА';'Г000034';'Д000019')
  //Установка наименования ЭД и сохранение
  Edoc.ISBEDocName = EName & ' (' & Num & ')'
  Edoc.Save
  //Помещение ЭД в папку и увеличение шага цикла
  Folders.PasteToFolder(Fold.Info; Edoc.info)
  Num = Num + 1
endwhile

В итоге данный сценарий создаст случайные ЭД со случайным наименованием в указанную папку. 30000 документов сценарий создал за 2 часа работы.

Основным элементом данного сценария является конструкция:

//N - конец диапозона случайных чисел.
//в итоге получаем дробное число в диапозоне от 0 до N
RandomInt = Abs(SQL('Select Rand()') * N)

По аналогичному принципу работы с генератором случайных чисел можно обеспечивать следующий функционал:
1) Автоматическое наполнение БД тестовыми данными (как документами, так и записями справочников)

2) Организовывать систему случайных задач (например задачи с текстом, для поднятия настроения сотрудников)

3) Организовывать внутриорганизационные лотереи.

4) Динамическое назначение исполнителей в ТМ (например для выбора одного из равноправных сотрудников для выполнения работы в случайном порядке)

5) и многое другое, на что может хватить фантазии программиста.

 

"Компьютер может все! Но он никогда не научится творчески мыслить" (С) сообщество разработчиков ПО.

Может это не так?

10
Авторизуйтесь, чтобы оценить материал.
1
Рустам Валиуллин

RandomInt = Abs(SQL('Select Rand()') * N)
Антон, но данная строка не вернет целое число. Если только вы не используете что-то типа Precision. Поясните сообществу, что за магию вы используете?

Антон Чурсин

Рустам, добрый день. Вы правы. Тут допущена ошибка в тексте комментария.
Для организации целого числа необходимо использовать конструкцию:

RandomInt = Round((SQL('Select Rand()') * N);0)

Но, в текущей ситуации, на работоспособность сценария наличие дробного числа никак не влияет.

Антон Чурсин

Текст статьи поправил в соответствии с Вашим замечанием.

Сергей Ромашев

Антон, а как решаете вопрос с остальными обязательными реквизитами, которые могут быть в карточке? Для каждого типа правите сценарий?

Антон Чурсин

Сергей, данный сценарий создавался для наполнения базы ЭД с типом карточки "Документы произвольной формы" для видимого наполнения базы. В данной статье он приведен как пример эффективного использования генератора случайных чисел.

При необходимости его можно можно модифицировать и усложнить логику, например:

1) Добавить генерацию выбора случайного типа карточки ЭД.

2) Добавить генерацию возможных реквизитов ЭД.

3) Разработать функции создания ЭД на основе переданных параметров.

4) Применить первые 3 пункта по логике выполнения функций для конкретных типов карточки ЭД.

Сергей Ромашев

Мы тоже сталкиваемся с подобными задачами. Особенно при подготовке демостендов по новым решениям, когда неоткуда больше взять данные, кроме как создать в базе новые.

Интересно было бы обсудить способы решения этих задач, какие узкие моменты каким способом решили. Обязательные реквизиты в зависимости от типа карточки, определение правил их заполнения и списков возможных значений, наличие вычислений на реквизите - от этих параметров очень сильно зависит результат. Мы в конечном итоге пришли к универсальному решению в виде справочника, в котором задаются все параметры сценария генерации документов, вплоть до словарей возможных значений. Оказался вполне удобным помощником в этом деле. По крайней мере больше не нужно просить разработчиков написать сценарий конкретно под твою ситуацию)

Антон Чурсин

Сергей, Ваша идея замечательна.

Но для полного автоматического заполнения базы я предложил бы использовать не один справочник, а как минимум 3:

1) Перечень критериев для генерации отдельных ЭД. (словари, тип карточек, а так же DetailDataSet с реквизитами ЭД, типом реквизита(справочник,строка,признак), словарь результатов для реквизита)

2) Перечень критерий генерации записей справочников. (тип справочника, а так же DetailDataSet с реквизитами справочника, типом реквизита(справочник,строка,признак), словарь результатов для реквизита)

3) Генерации (Наименование генерации,а так же DetailDataSet с перечнем критериев генерации для справочника или ЭД, количество генераций, папка для размещения ЭД, признак использования всех вложенных папок для размещения генерации.)

Таким образом Ваш сценарий будет генерировать не только ЭД, но и записи справочников. При наличии базы типовых перечней критерий генерации, вы сможете выгружать данные из одного решения в другое стандартными механизмами Directum и за считанные минуты настраивать генерацию в новой базе просто заполняя справочник "Генерации" уже имеющимися данными в заранее созданную иерархию папок, регулируя, нужно ли заполнять вложенные папки для каждой указанной папки текущей генерации.
Кстати можно еще добавить и критерии генерации иерархии папок. :)

Авторизуйтесь, чтобы написать комментарий