Мастер действия и Лист Согласования

13 9

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

С чего же началось данное решение? В первую очередь из требования Канцелярии и правления, подкреплять все документы несущиеся на подпись - формализованным листом согласования, где было бы все максимально сухо и только по делу. В виду того, что очень многие пользователи Web-клиента, просто и незамысловато печатали всю переписку в полный размер, иногда лист согласования достиг 10 листов в ряд. Для устранения подобных ситуаций, был создан простенький шаблон, который и стал основой для работы с Мастером, которым достаточно легко пользоваться.

С чего мы начинаем разработку мастера? Конечно же с набора параметров:

Пояснения:

1. Задача - соответственно ссылка на задачу с которой мы и будем снимать все согласования для листа;

2. E-mail - Мое решение отсылает Листы согласования по электронной почте, так как основной потребитель это пользователь Web-клиента, а мудрить и усложнять процесс для себя и пользователя не в моих интересах;

3. Инструкция - Ссылка на документ инструкции для Web-Клиента (на случай, если не разберется)

4. СИ - Ссылка на Инструкцию. Можно было бы обойтись и без этого, но я не исключаю, что документ на который делается ссылка, может быть неоднократно изменен, а так же мне он нужен для формы, так что - параметр.

 

С параметрами закончили. Далее, открываем этапы. У меня их всего 2 - более чем достаточно:

Просто как Лом. 

1. Выбор задачи: 

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

2. Задача:

Так же можно ограничить кнопки на излишние переходы. Закончили с Формой. На выходе имеем:

Третий этап, это конечно же вычисления, самое вкусное из всей пачки. Там у нас три этапа:

1. События мастера: До выбора

Params = Wizard.Params
  CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName)   // Вычисляем Инициатора
  Email = НайтиРеквизитПоФИО(CurrentUser.FullName;"Email")                      // Ищем его E-mail (Я 100 раз ранее эту функцию описывал - найдете если что)
  if Assigned(Email)                                                            // Если мыло найдено
     Params.ValueByName('Email').Value = Email                                  // Присваимваем параметру значение почты
  endif
// Получаем текст с ссылкой на инструкцию  
  Params.ValueByName('СИ').Value = "Инструкция по работе с Мастером: " & ГиперссылкаСоздать(Params.ValueByName('Инструкция').Value.ID;"Эл.документ")

2. Выбор Задачи: NEXT

  Params = Wizard.Params
  Задача = Params.ValueByName('Задача').Value     // Смотрим что нам вложили
  Задача = Tasks.GetObjectByID(Задача.ID)         // Ищем ведущую задачу
  if Assigned(Задача.MainTaskID)                  // Если найдена Ведущая
    Params.ValueByName('Задача').Value = Задача.MainTaskID   // Меняем ссылку задачи на ссылку на Ведущую Задачу
  endif

3. Задача: FINISH (тут самый сок)

Params = Wizard.Params
CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName)     // Получаем Инициатора
JobIndex = 1       // Индекс
  Задача = Params.ValueByName('Задача').Value   // Получаем задачу
  Задача = Tasks.GetObjectByID(Задача.ID)       // Формализуем для работы
  DetailJobs = Задача.DetailDataSet(3)          // Полоучаем Job`ы задачи
  NameP = Задача.SYSREQ_ID                      // Снимаем имя задачи в переменную для Листа согласования
  DetailJobs.Index = ArrayOf('EndDate;ASC'; 'Performer;ASC')    // Сортируем по дате и ФИО
  DetailJobs.Indexed = TRUE  
  DetailText = Задача.DetailDataSet(5)  // Получить детальный раздел "Тексты заданий"
  JobsList = CreateList()      // Создать список заданий
  foreach DetailJob in DetailJobs      // Пока мы получаем Job`ы
    JobID = DetailJob.SYSREQ_ID        // ID Job
    AuthorJob = ServiceFactory.GetUserByCode(DetailJob.Performer).FullName      // Ловим автора Задания
    JobText = ""    // Обозначаем переменную
    if DetailText.Locate("JobID"; JobID)     // Формируем строку с данными для дальнейшей работы
      JobText = JobIndex & "|"& AuthorJob & "|" & DetailJob.UTCModified & "|" & DetailText.Text
    endif
    if JobText <<>> ""      // Заполняем лист, индексируем
      JobsList.Add(JobIndex; JobText)
      JobIndex = JobIndex + 1 
    endif                
  endforeach
   
  Excel = CreateObject("Excel.Application")     // Теперь готовим к работе Excel
  ExcelBook = Excel.Workbooks.Add               // Готовим...
  WorkSheet = ExcelBook.WorkSheets(1)           // Готовим...
  WorkSheet.Activate                            // Готовим...
    WorkSheet.Rows(1).Cells(4).Value = 'Лист Согласования сформирован автоматически:'    // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(2).Cells(4).Value = "GUID: " & GetHashInFormatGUID(Задача.ID)         // Заполняем ячейки нужными нам данными
    // На самом деле действие выше, смысла не имеет никакого, но на документе смотриться круто и значимо, куда притянее чем "Написанному - верить"
    WorkSheet.Rows(3).Cells(2).Value = "ID: " & Задача.ID                                // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(4).Cells(2).Value = "ТЕМА: " & Задача.Subject                         // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(1).Value = '№'                                               // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(2).Value = 'Исполнитель'                                     // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(3).Value = 'Когда выполнено'                                 // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(4).Value = 'Содержание ответа'                               // Заполняем ячейки нужными нам данными

    index = 8     // Указываем на строку, с которой начинается таблица
    foreach JobText in JobsList     // Снимаем строки и заполняем таблицу по кусочкам:
        WorkSheet.Rows(Index).Cells(1).Value = SubString(JobText;"|";1)
        WorkSheet.Rows(Index).Cells(2).Value = SubString(JobText;"|";2)
        WorkSheet.Rows(Index).Cells(3).Value = SubString(JobText;"|";3)
        WorkSheet.Rows(Index).Cells(4).Value = Trim(SubString(JobText;"|";4))
        Index = Index + 1
    endforeach    
    WorkSheet.Rows(Index + 1).Cells(2).Value = "Отчет сформировал: " & CurrentUser.FullName  // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(Index + 2).Cells(2).Value = Сейчас()   // Заполняем ячейки нужными нам данными
      
// Наводим красоту с жестким фанатизмом. Как и где найти мануал - отвечаю: https://club.directum.ru/post/773
  Подпись1 = Index + 1
  Подпись2 = Index + 2 
  Край = Index - 1
  
  WorkSheet.Range("B4:D5").Merge
  WorkSheet.Range("B4").WrapText = True
  WorkSheet.Range("D:D").WrapText = True
  WorkSheet.Range("D1").Font.Underline = True
  WorkSheet.Range("D1").Font.Italic = True
  WorkSheet.Range("D2").Font.Underline = True
  WorkSheet.Range("D2").Font.Italic = True
  WorkSheet.Range("D2").Font.Size = 9
  WorkSheet.Rows("6:30").EntireRow.AutoFit
  WorkSheet.Range("B3:B5").Font.Bold = True
         
  WorkSheet.Columns("A:A").ColumnWidth = 2  //EntireColumn.AutoFit
  WorkSheet.Columns("B:B").ColumnWidth = 35 //EntireColumn.AutoFit
  WorkSheet.Columns("C:C").ColumnWidth = 20 //EntireColumn.AutoFit
  WorkSheet.Columns("D:D").ColumnWidth = 60 //EntireColumn.AutoFit
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).Font.Bold = True
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).HorizontalAlignment = -4131
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).VerticalAlignment = -4108
  WorkSheet.Range("A1:D" & Край).HorizontalAlignment = -4131                   
  WorkSheet.Range("A1:D" & Край).VerticalAlignment = -4108
  WorkSheet.Range("A7:D" & Край).Borders(7).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(8).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(9).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(10).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(11).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(12).LineStyle = 1
  WorkSheet.Range("D1").HorizontalAlignment = -4152
  WorkSheet.Range("D2").HorizontalAlignment = -4152
  WorkSheet.Range("A7:D7").HorizontalAlignment = -4108
  WorkSheet.Range("A7:D7").Font.Bold = True
  WorkSheet.PageSetup.Orientation = 2

//Сохраняем Нашу таблицу в PDF и XLS
  FilenamePDF = "C:\temp\Лист_Согласования_ID" & Задача.ID & ".pdf"  
  FilenameXLS = "C:\temp\Temporary.xls"   
  ExcelBook.SaveAs(FilenameXLS)  // Сохраняем таблицу в XLS, этого можно и не делать, но так происходит работе без лишних вопросов   
  ExcelBook.ExportAsFixedFormat(0; FilenamePDF;1; FALSE; FALSE) // Сохраняем таблицу в PDF
  Excel.Quit    // Закрываем Excel без вопросов
  ExcelBook = nil    // Убираемся
  Excel = nil        // Убираемся                           
  УдалитьФайл(FilenameXLS) // Убираемся, так как работа с таблицей закончена
//Отправляем "письма счастья"
  Email = Params.ValueByName('Email').Value    // Получаем наш email из параметра
  Theme = "Лист Согласования по задаче: " & Задача.Subject      // Готовм тему и текст
  TextMail = "Лист согласования находится во вложении." & CR & "Это письмо сформировано автоматически, отвечать на него не нужно."
  ПЧТОтправитьПисьмо(Email;;;Theme;TextMail;;FilenamePDF;)   // отправляем наше письмо с файлом
  УдалитьФайл(FilenamePDF)    // Удаляем файл из-за ненадобности.

Итого. на выходе мы имеем Мастер, который любую Задачу раскручивает на составляющие и выводит в виде документа типа PDF:

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

Наталия Соколова

Добрый день.

Подкину идею на развитие.

Т.к. у вас форма отчета полупостоянная (т.е. есть шаблон, таблица "растет в длину"), для формирования отчета можно использовать xsd-схему (https://club.directum.ru/post/145). Так формирование отчета происходит быстрее.

Юлия Литвинюк

Подскажите, почему не подошел стандартный отчет Лист согласования по задачам?

Еще я заметила опечатку в заголовке отчета "Когда выплонено"

Тарас Асачёв

По поводу схемы - вариант отличный, но мы стремились все же к единому стандарту - минимум правок и инноваций. Да и потом - это только начало, думаю вскоре начнется волна "хотелок" и правок.

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

"Выплонено" -   и ведь никто за неделю не заметил...

Тарас Асачев: обновлено 11.06.2019 в 12:20
Тарас Асачёв

Прикреплен файл: Wizards.rar
Собственно сам мастер

Тарас Асачёв

Небольшие правки направленные на улучшение вычисления реального Исполнителя:

Task = Tasks.GetObjectByID(ID_Param)
CurrentUser = Task.Requisites("Author").DisplayText    
CurrentCode = Task.Requisites("Author").Value
USERL = ServiceFactory.GetUserByCode(CurrentCode)      
JobIndex = 1      

  Задача = Tasks.GetObjectByID(Task.ID)      
  DetailJobs = Задача.DetailDataSet(3)          
  NameP = Задача.SYSREQ_ID                      
  DetailJobs.Index = ArrayOf('EndDate;ASC'; 'Performer;ASC')  
  DetailJobs.Indexed = TRUE  
  DetailText = Задача.DetailDataSet(5)  
  JobsList = CreateList()              
  foreach DetailJob in DetailJobs      
    JobID = DetailJob.SYSREQ_ID  
    Query = Format("select prtcl.UserID from SBTaskProtocol prtcl where prtcl.ActionType = 'X' and prtcl.JobId = %s"; JobID)
    UserID = SQL(Query)
    if Assigned(UserID)
      AuthorJob = ServiceFactory.GetUserById(UserID).FullName 
    else
      AuthorJob = ''
    endif  
    JobText = ""  
    if DetailText.Locate("JobID"; JobID)    
      JobText = JobIndex & "|"& IfThen(Assigned(AuthorJob);AuthorJob;'') & "|" &
       IfThen(Assigned(DetailJob.EndDate);DetailJob.EndDate;'') & "|" &
       IfThen(Assigned(DetailText.Text);DetailText.Text;'')
    endif
    if JobText <<>> ""     
      JobsList.Add(JobIndex; JobText)
      JobIndex = JobIndex + 1 
    endif                
  endforeach

Далее ничего не меняется. 

Дмитрий Зайцев

День добрый! А при работе через веб-клиент корректно отрабатывает мастер действий?  При работе мастера действий через веб-клиент получаем ошибку "Приложению Microsoft Excel не удается открыть или сохранить документы из-за нехватки памяти или места на диске..."  

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

Дмитрий Зайцев: обновлено 16.09.2020 в 12:43
Дмитрий Зайцев: обновлено 16.09.2020 в 16:12
Тарас Асачёв

Дмитрий, Если у Вас на сервере где происходит выполнение запроса мало прав или места, то эта ошибка понятна. У нас это все завернуто на сервере APP, где у пользователя из-под которого происходят вычисления прав на все каталоги достаточная. 

Тарас Асачёв

Upgrade for 4.9.1

Params = Wizard.Params
CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName)     // Получаем Инициатора
JobIndex = 1       // Индекс
  Задача = Params.ValueByName('Задача').Value   // Получаем задачу
  Задача = Tasks.GetObjectByID(Задача.ID)       // Формализуем для работы
  DetailJobs = Задача.DetailDataSet(3)          // Получаем Job`ы задачи
  NameP = Задача.SYSREQ_ID                      // Снимаем имя задачи в переменную для Листа согласования
  DetailJobs.Index = ArrayOf('EndDate;ASC'; 'Performer;ASC')    // Сортируем по дате и ФИО
  DetailJobs.Indexed = TRUE  
  DetailText = Задача.DetailDataSet(5)  // Получить детальный раздел "Тексты заданий"
  JobsList = CreateList()      // Создать список заданий
  foreach DetailJob in DetailJobs      // Пока мы получаем Job`ы
    JobID = DetailJob.SYSREQ_ID        // ID Job
    Query = Format("select prtcl.UserID from SBTaskProtocol prtcl where prtcl.ActionType = 'X' and prtcl.JobId = %s"; JobID)   //prtcl.ActionType = X and
    UserID = SQL(Query)
    if Assigned(UserID)
      if ServiceFactory.GetUserByCode(DetailJob.Performer).FullName <<>> ServiceFactory.GetUserById(UserID).FullName
        AuthorJob = ServiceFactory.GetUserById(UserID).FullName & ' за ' & ServiceFactory.GetUserByCode(DetailJob.Performer).FullName
      else
        AuthorJob = ServiceFactory.GetUserById(UserID).FullName
      endif      
    else
      AuthorJob = ''
    endif
    //AuthorJob = ServiceFactory.GetUserByCode(DetailJob.Performer).FullName      // Ловим автора Задания
    JobText = ""    // Обозначаем переменную
    if DetailText.Locate("JobID"; JobID)     // Формируем строку с данными для дальнейшей работы
      if FindSubString('Обратитесь к администратору системы';DetailText.Text;' ') = 0
        JobText = JobIndex & "|"& IfThen(Assigned(AuthorJob);AuthorJob;'') & "|" &
        IfThen(Assigned(DetailJob.EndDate);DetailJob.EndDate;'') & "|" &
        IfThen(Assigned(DetailText.Text);DetailText.Text;'')
      endif
    endif
    if JobText <<>> ""      // Заполняем лист, индексируем
      JobsList.Add(JobIndex; JobText)
      JobIndex = JobIndex + 1 
    endif                
  endforeach
   
  Excel = CreateObject("Excel.Application")     // Теперь готовим к работе Excel
  ExcelBook = Excel.Workbooks.Add               // Готовим...
  WorkSheet = ExcelBook.WorkSheets(1)           // Готовим...
  WorkSheet.Activate                            // Готовим...
    WorkSheet.Rows(1).Cells(4).Value = 'Лист Согласования сформирован автоматически:'    // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(2).Cells(4).Value = "GUID: " & GetHashInFormatGUID(Задача.ID)         // Заполняем ячейки нужными нам данными
    // На самом деле действие выше, смысла не имеет никакого, но на документе смотриться круто и значимо, куда притянее чем "Написанному - верить"
    WorkSheet.Rows(3).Cells(2).Value = "ID: " & Задача.ID                                // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(4).Cells(2).Value = "ТЕМА: " & Задача.Subject                         // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(1).Value = '№'                                               // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(2).Value = 'Исполнитель'                                     // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(3).Value = 'Когда выполнено'                                 // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(4).Value = 'Содержание ответа'                               // Заполняем ячейки нужными нам данными

    index = 8     // Указываем на строку, с которой начинается таблица
    foreach JobText in JobsList     // Снимаем строки и заполняем таблицу по кусочкам:
        WorkSheet.Rows(Index).Cells(1).Value = SubString(JobText;"|";1)
        WorkSheet.Rows(Index).Cells(2).Value = SubString(JobText;"|";2)
        WorkSheet.Rows(Index).Cells(3).Value = SubString(JobText;"|";3)
        WorkSheet.Rows(Index).Cells(4).Value = Trim(SubString(JobText;"|";4))
        Index = Index + 1
    endforeach    
    WorkSheet.Rows(Index + 1).Cells(2).Value = "Отчет сформировал: " & CurrentUser.FullName  // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(Index + 2).Cells(2).Value = Сейчас()   // Заполняем ячейки нужными нам данными
      
// Наводим красоту с жестким фанатизмом. Как и где найти мануал - отвечаю: https://club.directum.ru/post/773
  Подпись1 = Index + 1
  Подпись2 = Index + 2 
  Край = Index - 1
  
  WorkSheet.Range("B4:D5").Merge
  WorkSheet.Range("B4").WrapText = True
  WorkSheet.Range("D:D").WrapText = True
  WorkSheet.Range("D1").Font.Underline = True
  WorkSheet.Range("D1").Font.Italic = True
  WorkSheet.Range("D2").Font.Underline = True
  WorkSheet.Range("D2").Font.Italic = True
  WorkSheet.Range("D2").Font.Size = 9
  WorkSheet.Rows("6:30").EntireRow.AutoFit
  WorkSheet.Range("B3:B5").Font.Bold = True
         
  WorkSheet.Columns("A:A").ColumnWidth = 2  //EntireColumn.AutoFit
  WorkSheet.Columns("B:B").ColumnWidth = 35 //EntireColumn.AutoFit
  WorkSheet.Columns("C:C").ColumnWidth = 20 //EntireColumn.AutoFit
  WorkSheet.Columns("D:D").ColumnWidth = 60 //EntireColumn.AutoFit
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).Font.Bold = True
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).HorizontalAlignment = -4131
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).VerticalAlignment = -4108
  WorkSheet.Range("A1:D" & Край).HorizontalAlignment = -4131                   
  WorkSheet.Range("A1:D" & Край).VerticalAlignment = -4108
  WorkSheet.Range("A7:D" & Край).Borders(7).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(8).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(9).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(10).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(11).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(12).LineStyle = 1
  WorkSheet.Range("D1").HorizontalAlignment = -4152
  WorkSheet.Range("D2").HorizontalAlignment = -4152
  WorkSheet.Range("A7:D7").HorizontalAlignment = -4108
  WorkSheet.Range("A7:D7").Font.Bold = True
  WorkSheet.PageSetup.Orientation = 2

//Сохраняем Нашу таблицу в PDF и XLS
  FilenamePDF = GetTempFolder() & "Лист_Согласования_ID" & Задача.ID & ".pdf"  
  FilenameXLS = GetTempFolder() & "Temporary" & Задача.ID & ".xls"   
  ExcelBook.SaveAs(FilenameXLS)  // Сохраняем таблицу в XLS, этого можно и не делать, но так происходит работе без лишних вопросов   
  ExcelBook.ExportAsFixedFormat(0; FilenamePDF;1; FALSE; FALSE) // Сохраняем таблицу в PDF
  Excel.Quit    // Закрываем Excel без вопросов
  ExcelBook = nil    // Убираемся
  Excel = nil        // Убираемся                           
  УдалитьФайл(FilenameXLS) // Убираемся, так как работа с таблицей закончена
//Отправляем "письма счастья"
  Email = Params.ValueByName('E-mail').Value    // Получаем наш email из параметра
  Theme = "Лист Согласования по задаче: " & Задача.Subject      // Готовим тему и текст
  TextMail = "Лист согласования находится во вложении." & CR & "Это письмо сформировано автоматически, отвечать на него не нужно."
  try
    //ФайлОткрыть(FilenamePDF)
    ПЧТОтправитьПисьмо(Email;;;Theme;TextMail;;FilenamePDF;)   // отправляем наше письмо с файлом
    УдалитьФайл(FilenamePDF)    // Удаляем файл из-за ненадобности.
  except
    ФайлОткрыть(FilenamePDF)
  endexcept  
  //УдалитьФайл(FilenamePDF)    // Удаляем файл из-за ненадобности.
  

 

Тарас Асачёв

С подзадачами:

Params = Wizard.Params
CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName)     // Получаем Инициатора
JobIndex = 1       // Индекс
  Задача = Params.ValueByName('Задача').Value   // Получаем задачу
if Params.ValueByName('Подзадачи').Value
  OsnovnID = Задача.ID 
  SQL = 'SELECT XrecID
      FROM SBTask
      where maintaskID = ' & OsnovnID
      ZDID = SQL(SQL;;';')
  if Assigned(ZDID)
    OsnovnID = AddSubString(ZDID;OsnovnID;';')
  endif  
else
  OsnovnID = Задача.ID 
endif
 JobText = ""    // Обозначаем переменную
 JobsList = CreateList()               // Создать список заданий
 foreach IdTask in CSubString(OsnovnID;';')
  Задача = Tasks.GetObjectByID(IdTask)          // Формализуем для работы
  DetailJobs = Задача.DetailDataSet(3)          // Полоучаем Job`ы задачи
  NameP = Задача.SYSREQ_ID                      // Снимаем имя задачи в переменную для Листа согласования
  DetailJobs.Index = ArrayOf('EndDate;ASC'; 'Performer;ASC')    // Сортируем по дате и ФИО
  DetailJobs.Indexed = TRUE  
  DetailText = Задача.DetailDataSet(5)  // Получить детальный раздел "Тексты заданий"  
  foreach DetailJob in DetailJobs       // Пока мы получаем Job`ы
    JobID = DetailJob.SYSREQ_ID         // ID Job
    Query = Format("select prtcl.UserID from SBTaskProtocol prtcl where prtcl.ActionType = 'X' and prtcl.JobId = %s"; JobID)   //prtcl.ActionType = X and
    UserID = SQL(Query)
    if Assigned(UserID)
      if ServiceFactory.GetUserByCode(DetailJob.Performer).FullName <<>> ServiceFactory.GetUserById(UserID).FullName
        AuthorJob = ServiceFactory.GetUserById(UserID).FullName & ' за ' & ServiceFactory.GetUserByCode(DetailJob.Performer).FullName
      else
        AuthorJob = ServiceFactory.GetUserById(UserID).FullName
      endif      
    else
      AuthorJob = ''
    endif
    //AuthorJob = ServiceFactory.GetUserByCode(DetailJob.Performer).FullName      // Ловим автора Задания
    if DetailText.Locate("JobID"; JobID)     // Формируем строку с данными для дальнейшей работы
      if FindSubString('Обратитесь к администратору системы';DetailText.Text;' ') = 0
        if Assigned(AuthorJob)
          JobText = IfThen(Assigned(AuthorJob);AuthorJob;'') & "|" &       // JobIndex & "|"& 
          IfThen(Assigned(DetailJob.EndDate);DetailJob.EndDate;'') & "|" &
          IfThen(Assigned(DetailText.Text);DetailText.Text;'')
        endif
      endif
    endif
    if JobText <<>> ""      // Заполняем лист, индексируем
      NameGUID = GetHashInFormatGUID(JobText)
      JobsList.SetVar(NameGUID; JobText)
      //JobIndex = JobIndex + 1 
    endif                
  endforeach
 endforeach
   
  Excel = CreateObject("Excel.Application")     // Теперь готовим к работе Excel
  ExcelBook = Excel.Workbooks.Add               // Готовим...
  WorkSheet = ExcelBook.WorkSheets(1)           // Готовим...
  WorkSheet.Activate                            // Готовим...
    WorkSheet.Rows(1).Cells(4).Value = 'Лист Согласования сформирован автоматически:'    // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(2).Cells(4).Value = "GUID: " & GetHashInFormatGUID(Задача.ID)         // Заполняем ячейки нужными нам данными
    // На самом деле действие выше, смысла не имеет никакого, но на документе смотриться круто и значимо, куда притянее чем "Написанному - верить"
    WorkSheet.Rows(3).Cells(2).Value = "ID: " & Задача.ID                                // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(4).Cells(2).Value = "ТЕМА: " & Задача.Subject                         // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(1).Value = '№'                                               // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(2).Value = 'Исполнитель'                                     // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(3).Value = 'Когда выполнено'                                 // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(7).Cells(4).Value = 'Содержание ответа'                               // Заполняем ячейки нужными нам данными

    index = 8     // Указываем на строку, с которой начинается таблица
    Perech = 1
    foreach JobText in JobsList     // Снимаем строки и заполняем таблицу по кусочкам:
        WorkSheet.Rows(Index).Cells(1).Value = Perech //SubString(JobText;"|";1)
        WorkSheet.Rows(Index).Cells(2).Value = SubString(JobText;"|";1)
        WorkSheet.Rows(Index).Cells(3).Value = SubString(JobText;"|";2)
        WorkSheet.Rows(Index).Cells(4).Value = Trim(SubString(JobText;"|";3))
        Index = Index + 1
        Perech = Perech + 1
    endforeach    
    WorkSheet.Rows(Index + 1).Cells(2).Value = "Отчет сформировал: " & CurrentUser.FullName  // Заполняем ячейки нужными нам данными
    WorkSheet.Rows(Index + 2).Cells(2).Value = Сейчас()   // Заполняем ячейки нужными нам данными
      
// Наводим красоту с жестким фанатизмом. Как и где найти мануал - отвечаю: https://club.directum.ru/post/773
  Подпись1 = Index + 1
  Подпись2 = Index + 2 
  Край = Index - 1
  
  WorkSheet.Range("B4:D5").Merge
  WorkSheet.Range("B4").WrapText = True
  WorkSheet.Range("D:D").WrapText = True
  WorkSheet.Range("D1").Font.Underline = True
  WorkSheet.Range("D1").Font.Italic = True
  WorkSheet.Range("D2").Font.Underline = True
  WorkSheet.Range("D2").Font.Italic = True
  WorkSheet.Range("D2").Font.Size = 9
  WorkSheet.Rows("6:30").EntireRow.AutoFit
  WorkSheet.Range("B3:B5").Font.Bold = True
         
  WorkSheet.Columns("A:A").ColumnWidth = 2  //EntireColumn.AutoFit
  WorkSheet.Columns("B:B").ColumnWidth = 35 //EntireColumn.AutoFit
  WorkSheet.Columns("C:C").ColumnWidth = 20 //EntireColumn.AutoFit
  WorkSheet.Columns("D:D").ColumnWidth = 60 //EntireColumn.AutoFit
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).Font.Bold = True
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).HorizontalAlignment = -4131
  WorkSheet.Range("B" & Подпись1 & ":B" & Подпись2).VerticalAlignment = -4108
  WorkSheet.Range("A1:D" & Край).HorizontalAlignment = -4131                   
  WorkSheet.Range("A1:D" & Край).VerticalAlignment = -4108
  WorkSheet.Range("A7:D" & Край).Borders(7).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(8).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(9).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(10).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(11).LineStyle = 1
  WorkSheet.Range("A7:D" & Край).Borders(12).LineStyle = 1
  WorkSheet.Range("D1").HorizontalAlignment = -4152
  WorkSheet.Range("D2").HorizontalAlignment = -4152
  WorkSheet.Range("A7:D7").HorizontalAlignment = -4108
  WorkSheet.Range("A7:D7").Font.Bold = True
  WorkSheet.PageSetup.Orientation = 2

//Сохраняем Нашу таблицу в PDF и XLS
  FilenamePDF = GetTempFolder() & "Лист_Согласования_ID" & Задача.ID & ".pdf"  
  FilenameXLS = GetTempFolder() & "Temporary" & Задача.ID & ".xls"   
  ExcelBook.SaveAs(FilenameXLS)  // Сохраняем таблицу в XLS, этого можно и не делать, но так происходит работе без лишних вопросов   
  ExcelBook.ExportAsFixedFormat(0; FilenamePDF;1; FALSE; FALSE) // Сохраняем таблицу в PDF
  Excel.Quit    // Закрываем Excel без вопросов
  ExcelBook = nil    // Убираемся
  Excel = nil        // Убираемся                           
  УдалитьФайл(FilenameXLS) // Убираемся, так как работа с таблицей закончена
//Отправляем "письма счастья"
  Email = Params.ValueByName('E-mail').Value    // Получаем наш email из параметра
  Theme = "Лист Согласования по задаче: " & Задача.Subject      // Готовм тему и текст
  TextMail = "Лист согласования находится во вложении." & CR & "Это письмо сформировано автоматически, отвечать на него не нужно."
  try
    //ФайлОткрыть(FilenamePDF)
    ПЧТОтправитьПисьмо(Email;;;Theme;TextMail;;FilenamePDF;)   // отправляем наше письмо с файлом
    УдалитьФайл(FilenamePDF)    // Удаляем файл из-за ненадобности.
  except
    ФайлОткрыть(FilenamePDF)
  endexcept  
  //УдалитьФайл(FilenamePDF)    // Удаляем файл из-за ненадобности.

 

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