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

Опубликовано:
14 июня в 07:45
  • 7

При внедрении DIRECTUM на предприятии и в ходе эксплуатации системы к ней регулярно возникают пожелания. В условиях ограниченных ресурсов – сроков, трудоемкости - далеко не все пожелания можно реализовать, однако, можно с помощью небольших «фишек» и волков накормить (пользователей), и овец сберечь (время).

В этой статье поделюсь маленькими «фишками» с последнего проекта, которые облегчили жизнь нашим пользователям. Сразу оговорюсь, что внедряли стандартную версию с минимальными доработками.

1. Согласование с вышестоящими руководителями по иерархии

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

При этом согласование документов, в соответствии с регламентами, должно происходить со всеми вышестоящими руководителями по иерархии, т.е. исполнитель согласует сначала с непосредственным руководителем, далее с руководителем вышестоящего подразделения и т.д. до заместителя директора.

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

Для решения этой задачи мы разработали функцию, которая по исполнителю, запускающему документ на согласование, вычисляет подразделение (стандартный справочник Подразделения) и определяет у него руководителя. Далее проверяем, есть ли у полученного подразделения головное, если есть - повторяем алгоритм, и так далее, пока не добираемся до самого верха иерархии. Если у подразделения нет руководителя (а такое у нас было), просто пропускаем его. На выходе имеем список пользователей – руководителей по иерархии.

В типовой маршрут по согласованию документа добавляем блок «Согласование с вышестоящими руководителями», в котором в качестве исполнителя выступают пользователи из полученного списка. Делаем согласование последовательным.

В итоге, начало всех маршрутов по согласованию у нас совпадало и имело вид:

Пример функции:

  if not Assigned(UserID)
    UserID = EDocuments.CurrentUser.ID
  endif
  
  ManagersList:IUserList = ServiceFactory.GetUserList
  
  EmployeesList = GetEmployeesByUserID(UserID)
  if EmployeesList.Count > 0
    EmployeeCode = EmployeesList.Values(0) 
    DepCode = GetRequisiteValueAsString("РАБ"; EmployeeCode; "ПодразделениеОргСтрук")
    if DepCode <<>> ""
      ManagerCode = GetRequisiteValueAsString("ПОДОРГСТРУК"; DepCode; "Работник")
      if ManagerCode <<>> ""
        ManagerUserCode = GetRequisiteValueAsString("РАБ"; ManagerCode; "Пользователь")
        if ManagerUserCode <<>> ""
          ManagerUserInfo = References.ПОЛ.ObjectInfoByCode(ManagerUserCode)
          ManagerUser = ServiceFactory.GetUserByID(ManagerUserInfo.ID)
          if not ManagersList.Find(ManagerUser)
            ManagersList.Add(ManagerUser)
          endif
        endif  
      endif
      
      // Взять руководителя головного подразделения
      HighDepCode = GetRequisiteValueAsString("ПОДОРГСТРУК"; DepCode; "ПодразделениеОргСтрук")
      while HighDepCode <<>> ""
        DepCode = HighDepCode
        ManagerCode = GetRequisiteValueAsString("ПОДОРГСТРУК"; DepCode; "Работник")
        if ManagerCode <<>> ""
          ManagerUserCode = GetRequisiteValueAsString("РАБ"; ManagerCode; "Пользователь")
          if ManagerUserCode <<>> ""
            ManagerUserInfo = References.ПОЛ.ObjectInfoByCode(ManagerUserCode)
            ManagerUser = ServiceFactory.GetUserByID(ManagerUserInfo.ID)
            if not ManagersList.Find(ManagerUser)
              ManagersList.Add(ManagerUser)
            endif
          endif  
        endif
        
        HighDepCode = GetRequisiteValueAsString("ПОДОРГСТРУК"; DepCode; "ПодразделениеОргСтрук")
      endwhile
    endif
  endif
  
  Result = ManagersList

 

2. Автоматический архив для входящих документов

Каждое подразделение имеет свои потоки входящей и исходящей корреспонденции. Письма, служебные записки, распорядительные документы могут быть как инициированы в подразделении – исходящий поток, так и поступить в подразделение извне – входящий поток.

Для удобства хранения и поиска корреспонденции в общей папке проводника системы были созданы папки подразделений. В каждой из них сотрудники хранили свои документы - некий архив или реестр документов подразделения или всего управления. Если нет ограничения по доступу (не конфиденциальный документ), то логичным выглядит пожелание автоматически сохранять ссылки на документы в папки подразделения, чтобы каждый сотрудник мог найти входящий или исходящий документ - т.е. сделать автоматический архив.

Если с исходящей корреспонденцией все более-менее понятно – можно создать папки поиска по видам документов, ограничить по авторам и дате создания (например, текущим годом) - то с входящими документами немного сложнее.

Как правило, входящий документ поступает в подразделение в рамках исполнения поручений по документу, либо для ознакомления. При этом создавать эти документы могут любые сотрудники:

  • входящие документы – делопроизводители;
  • служебные записки, распорядительные документы – остальные сотрудники компании.

Конечно, можно было сделать папку поиска по заданиям в рамках типовых маршрутов (стандартные ТМ «Исполнение поручений», «Ознакомление документа») и ограничить исполнителями задания – сотрудниками текущего подразделения. Но у этого решения есть минусы:

  • в папке ссылки на задания. Нашим пользователям было принципиально видеть сами документы;
  • дубли по одному и тому же письму при исполнении поручений - например, директор расписал на руководителя подразделения (первое задание), руководитель расписал на исполнителя (второе задание). В итоге будет в папке поиска 2 задания по одному и тому же документу.

Мы упростили задачу и взяли в работу только документы, поступающие в рамках исполнения поручений.

  1. Первым делом создаем папки, куда будут копироваться ссылки на входящие документы. Поскольку в каждом управлении свои правила, пользователи сами выбирали на каком уровне будет папка – будут ли это входящие письма конкретного подразделения, группы подразделений, либо всего управления.
  2. Далее создаем простейший справочник соответствия групп пользователей (у нас они совпадали со списками сотрудников по подразделениям) и папки для входящих документов. У нас он выглядел так:


 

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

3. Далее проверяем поручения (стандартный справочник Поручения): смотрим, есть ли по поручению основной документ, если есть, проверяем исполнителей. По каждому исполнителю определяем группу пользователей, в которую он входит. После этого проверяем, задано ли соответствие найденной группы и папки в нашем новом справочнике:


 

4. Для поиска папки мы изменили типовой маршрут «Исполнение поручений». После старта вставили блок с вычислением, где определяли папку (как описано в предыдущем пункте) и вставляли туда ссылку. При этом, если в папке уже имеется ссылка на данный документ, то вставка не осуществлялась.

Пример вычислений на блоке ТМ:

  Params = Object.WorkflowParams
  AssignmentInfo = Params.ValueByName('Поручение').Value

  if not VarIsNull(AssignmentInfo) 
    //Найти основной документа по РКК
    Doc = nil
    DocID = GetRequisiteValueAsString("RRCAssignments"; AssignmentInfo.Code; "ISBIntNumber")
    if Assigned(DocID)
      Doc = EDocuments.GetObjectByID(DocID)
      UserList = Params.ValueByName('ОтветственныеИсполнители').Value

      //Вложить документ в нужную папку
      foreach User in UserList
        Query = "select s.IDSpr
                   from MBAnValR t
                   join MBAnalitSpr a
                     on t.Analit = a.Analit 
                   join MBAnalit s
                     on t.Analit = s.ParentGroup
                     and s.Vid = " & ИДТипСпр("IncomingFoldersSettings") & "
                     and s.Sost = 'Д'
                     and s.XRecStat = '+' 
                   where t.Vid = " & ИДТипСпр("ГПЛ") & "
                     and a.Sost = 'Д'
                     and a.XRecStat = '+' 
                     and t.PolzovatelT = " & User.ID
                     
        foreach FolderID = CSQL(Query)
          Folder = Folders.GetObjectByID(FolderID)
          Folders.PasteToFolder(Folder.Info; Doc.Info)
        endforeach  
                    
      endforeach
    endif
  endif
  
  Doc = nil
  UserList = nil

3. Вставка шрихкода в документы, созданные из файла

Если компания использует штрихкоды для быстрой идентификации документов в системе, то как правило:

  • для входящих бумажных документов на документ приклеивается наклейка, распечатанная с помощью принтера наклеек
  • для исходящих и внутренних документов штрихкод вставляется в текст документа при создании из приложений Microsoft Office Word или Microsoft Excel

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

Т.е. при создании документа в системе исполнитель должен был вставить в него штрихкод.

Как мы знаем, документ можно создать из шаблона и из файла. В шаблоне сразу можно настроить вставку штрихкода в колонтитулы, в этом случае пользователю останется ввести только сам текст документа.

Но часто бывает, что пользователи создают документы из файла: на абсолютно все случаи шаблоны не создашь, да это и не нужно. А у пользователя, скажем, уже есть на рабочем месте заготовка письма или предыдущее письмо на ту же тему (тому же корреспонденту), в котором достаточно изменить пару предложений.

Импортируя документ из файла, для вставки штрихкода необходимо открыть документ и через меню интеграции вставить штрихкод. Это не всегда удобно, например, когда документ импортируется полностью готовым к согласованию или же в документе настроены разные колонтитулы для разных страниц (а штрихкод должен вставляться на все страницы). Т.е. пользователю приходится делать дополнительные действия перед отправкой готового к согласованию документа.

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

  1. Создает документ из файла, указывает вид и тип карточки.
  2. Заполняет карточку документа, нажимает кнопку штрихкод и получает уведомление от системы, что вставка произведена успешно.
  3. Отправляет документ на согласование по одноименной кнопке из карточки.

Пример вычислений на кнопке:

  DocumentID = Object.ID
  Versions = Object.Versions
  VersionNumber = ""
  VersionNote = ""
  EDocExt = ""
  foreach Version in Versions
    VersLock = Version.GlobalLock
    if VersLock.Locked  
      Exit("Текст документа на настоящий момент сохраняется и заблокирован, подождите некоторое время перед повторной попыткой вставки штрихкода")
    endif
     
    VCS = Version.CurrentState 
    if VCS = 2
      VersionNumber = Version.Number
      VersionNote = Version.Note
      EDocExt = Version.Editor.Extension  
      exitfor  
    endif  
  endforeach
  
  if Assigned(VersionNumber)    
    // Символ slash
    SLASH_SYMBOL = "\"
    // Символ подчёркивания
    UNDERLINE_SYMBOL = "_"
    // Массив запрещённых при создании папок символов
    BANNED_FOR_FILE_NAME_SYMBOLS = ArrayOf("\"; "/"; ":"; "|"; "*"; "?"; '"'; "<"; ">"; "'"; Char(145); Char(146); Char(147);
      Char(148); Char(9))
    // Шаблон формирования имени временного файла
    EXPORTED_TEMP_DOCUMENT_TEMPLATE = "%s\%s_%s.%s"
    // Сгенерировать и сохранить картинку со штрихкодом
    BARCODE_ORIENTATION_NUMBER = 3
    BARCODE_STRING_INDEX = 0
        
    EDocumentName = Object.Name
    // Заменить все запрещённые символы в названии файла на знак подчёркивания
    foreach BannedSymbol in CArrayElement(BANNED_FOR_FILE_NAME_SYMBOLS)
      EDocumentName = Replace(EDocumentName; BannedSymbol; UNDERLINE_SYMBOL)
    endforeach
        
    // Получить путь к временной папке текущего пользователя
    CurrentUserTempFolder = GetTempFolder()
    // Взять последний символ пути к временной папке пользователя
    LastTempFolderChar = Copy(CurrentUserTempFolder; Length(CurrentUserTempFolder); 1) 
    // Если последний символ - "\"
    if LastTempFolderChar == SLASH_SYMBOL
      CurrentUserTempFolder = Copy(CurrentUserTempFolder; 0; Length(CurrentUserTempFolder) - 1)
    endif    

    // Сформировать путь к файлу, в который будет экспортирован документ        
    InputFilePath = Format(EXPORTED_TEMP_DOCUMENT_TEMPLATE; ArrayOf(CurrentUserTempFolder; DocumentID; EDocumentName; EDocExt))
    
    // Экспортировать электронный документ без блокировки
    Object.Export(VersionNumber; InputFilePath; False)     
    
    // Получить строку штрихкода    
    BarCodeStringAndType = GetDocumentBarcodeString(DocumentID)
    BarCodeString = BarCodeStringAndType[BARCODE_STRING_INDEX]
  
    // Определить путь для сохранения временного файла со штрихкодом
    // Шаблон для формирования имени файла со штрихкодом
    BARCODE_FILE_PATH_TEMPLATE = "%s\barcode_%s.wmf"    
    BarCodeDestinationPath = Format(BARCODE_FILE_PATH_TEMPLATE; ArrayOf(CurrentUserTempFolder; DocumentID))
  
    // Создать файл штрихкода на диске во временной папке пользователя
    GenerateBarCode(BarCodeString;; '';BarCodeDestinationPath)
         
   // вставить штрихкод в колонтитул документа
   // с помощью ОМ Word
    wdSeekCurrentPageFooter = 10
    wdAlignParagraphRight = 2   
    wdHeaderFooterFirstPage = 2
    Word = CreateObject("Word.Application")
    DocW = Word.Documents.Open(InputFilePath)  
    DocW.ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageFooter
    Insh = DocW.Sections(1).Footers(1).Range.InlineShapes.AddPicture(BarCodeDestinationPath;False;True)
    Insh.Height = 32
    Insh.Width = 107
    DocW.Sections(1).Footers(1).Range.ParagraphFormat.Alignment = wdAlignParagraphRight
   
   // Если установлен признак "Особый колонтитул для первой страницы",
   // то вставить ШК туда отдельно 
    If DocW.PageSetup.DifferentFirstPageHeaderFooter
      InSh1 = DocW.Sections(1).Footers(wdHeaderFooterFirstPage).Range.InlineShapes.AddPicture(BarCodeDestinationPath;False;True)    
      InSh1.Height = 32
      InSh1.Width = 107
      DocW.Sections(1).Footers(wdHeaderFooterFirstPage).Range.ParagraphFormat.Alignment = wdAlignParagraphRight
    EndIf
      
    DocW.SaveAs(InputFilePath)    // куда сейвим
    DocW.Close(false)    // закрываем док
    Word.Quit()
        
   // импортировать документ уже со штрихкодом обратно в текущую версию
    Object.ImportFromFile(VersionNumber; VersionNote; InputFilePath)
    Object.Save
     
    ShowMessage("Штрихкод вставлен в документ")
  else
    ShowMessage("У документа нет ни одной действующей версии.")     
  endif

На этом у меня все. Пишите в комментариях свои примеры маленьких, но полезных фишек.

21
Подписаться

Комментарии

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

Не бывает у них случаев, когда согласование должно идти по схеме Непосредственный руководитель - Доп. согласующие - Прочие руководители по иерархии?

Гульназ, спасибо!

У данного клиента таких случаев нет. Но можно для вашего примера добавить в схему блок согласования с непосредственным руководителем (например, взять из стандартного ТМ "Согласование официальных документов"), а функцию переделать, чтобы она сначала вычисляла подразделение работника, у этого подразделения искала головное и в головном вычисляла руководителя. Или же оставить как есть, но в ТМ добавить вычисление по удалению непосредственного руководителя из списка вышестоящих руководителей 

Есть несколько вопросов по вычислению руководителей:

1. Вычисления происходят до генерального директора или генеральный -1 подразделение? Бывают случаи, что даже согласование с замом лишнее, т.к. он может подписывать документ.

2. Если мы еще раз входим в блок, например при доработке, снова вычисляем иерархию? Если за время доработки сменился руководитель ему придет задание или "старому" руководителю?

По штрихкодам интересное решение, но тоже пару вопросов)

1. Правильно я понимаю, что текст смещается после вставки колонтитула и его придется редактировать? А если в документе уже есть колонтитул? А если нажать дважды?)

2. Если несколько версий у документа, в какую вставится штрихкод?

Андрей:

1. Здесь гибкая настройка - если вам надо, чтобы генеральный или его зам участвовал в согласовании, тогда создавайте подразделение, например Администрация и делайте его головным для остальных подразделений. Если не надо - тогда не заполняйте подразделение у таких работников. Функция "останавливается" когда у подразделения нет головного. У этого клиента согласование по иерархии должно было доходить до уровня начальников управлений. Замы и сам директор  - подписывающие документ, а значит отдельный блок в согласовании ТМ. Расскажу на примере, я сотрудник бюро комплектации, мое бюро входит в сметный отдел, а он в свою очередь в проектное управление. Выше него главный инженер и далее директор. У бюро комплектации я указываю головным  подразделением сметный отдел, у сметного отдела - проектное управление. А у проектного управления поле головное подразделение оставляю пустым - тогда список вышестоящих руководителей будет состоять только из 3х человек.

2. Если функцию включить в вычисления на блоке, тогда да. А мы делали это на старте ТМ, поэтому в нашем случае ответ нет, список статичный

3.  >> Правильно я понимаю, что текст смещается после вставки колонтитула и его придется редактировать? - нет, текст не смещается, потому что место под колонтитулы в word изначально "заложены" ниже них текст не уйдет. Даже если специально удалить колонтитулы, то удаляется содержание в колонтитуле (текст, нумерация, таблицы), а само место остается

>> А если в документе уже есть колонтитул?  - вставит ШК на него. Здесь никак не обрабатывали, т.е. придется сдвигать ШК, чтобы видно было и текст в колонтитуле и ШК

>> А если нажать дважды?) - два раза вставит) 10 раз нажмете - 10 раз вставит)))

4. Здесь изначально кейс другой был: работник создает новый документ в системе из файла. а не импортирует в новую версию уже имеющегося документа. Если документ уже есть в системе, то новые его версии, как правило создаются на основе старых. Поэтому других вариантов даже не рассматривали. Но да, Ваш кейс тоже имеет место быть, поэтому проверила в тестовой базе - вставляет только в одну версию. В первую :)

А вообще, здорово, что столько вопросов!) Значит надо вам брать код и пробовать самостоятельно экспериментировать ;)

Автоматический архив для входящих документов.

В версии 5.6 появились управляемые папки документов, которые решают эту задачу: можно сделать папку поиска по заданиям в рамках типовых маршрутов (стандартные ТМ «Исполнение поручений», «Ознакомление документа») и ограничить исполнителями задания – сотрудниками текущего подразделения. При этом пользователи видят документы, а не задания

Верно! Для меня расширение функционала папок - это очень долгожданная фича. Новые колонки в папках - это же и сортировка и группировка и фильтрация по всем нужным полям. Наконец-то не надо изобретать отчеты или сценарии поиска, чтобы работать с документами также как и со справочниками.

Да и вообще версия 5.6 хороша - одно только автоматическое создание поручений из протокола чего стоит! :)

Ксения Иванова: обновлено 20.06.2018 в 15:19

любопытно, что автоматическое создание поручений по протоколу совещаний уже было в 5.1, но несколько в ином виде: в модуле "совещания и заседания" на основе повестки формировался протокол в виде табличной части в карточке совещания, уточнялись пункты и исполнители, а дальше формировался уже документ в ворде и отправлялся на согласование, затем расходились поручения.

в 5.2 этот функционал упростили, табличной части с пунктами протокола не стало

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