Полезные мелочи при работе с карточками ч.1 (Блокировки)

9 8

Блокировка поля в карточке документа или справочника. Не ахти-какая наука, но если у вас 67 реквизитов для 9 групп пользователей при 4 разных ролях, то это весьма упрощает жизнь:

Итак, что нам необходимо для решения этой задачи? Да всего ничего - 2 параметра!

  • Наименование Кнопки или Поля, с которыми нам и работать
  • Параметр на блокировку/разблокировку

Собственно этого хватит, чтобы собрать себе 2 функции для работы. Можно заморочиться и сделать все в одной, но зачем если это требуется не так уж и часто? 

Итак, с чего мы начнем? Если вы никогда не создавали функции, то это дело увлекательное и полезное как в плане работы, так и опыта. Открываем Компоненты - Утилиты разработчика - Функции ISBL. Далее создаем новую Функцию и даем ей имя, и определяем ее группу. Я дал функциям названия ButtonOR и ControlOR где OR - Only Read, и внес их в группу "Работа с правами доступа". Вы можете сделать и иначе.

Далее нам надо создать три параметра:

  1. Object (Можете назвать его как угодно) - Вариантный;
  2. Button или ContrPol ("Кнопка" или "Контрольное_Поле"), можете задавать любые имена - Тип Строка;
  3. OnlRead (Параметр описывающий наши потребности) - тип Логическое - Значение по-умолчанию = False (Это даст нам возможность не заполнять его постоянно). Хотя 

После сохранения нового элемента, мы заходим в раздел "Текст". Теперь мы находимся в закромах нашей функции, в ее "Сердце", где и дадим описание действия.

Для Кнопки, мы должны определить элемент формы при помощи команды: "Form = Object.Form" и "Action = Form.Actions".

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

Наше "определение" указывает на активные элементы формы карточки, но для конкретизации элемента, нам надо указать на его имя (Имя кнопки или Поля), которое мы будем передавать через параметр Button или ContrPol. В них мы будем использовать следующий Код:

Для Кнопки:  Action.FindAction(Button).Visible

Для Поля: Book.Requisites(ContrPol).CanGUIWrite

Теперь, осталось применить наш последний параметр (OnlRead), что по-умолчанию = FALSE (Ложь). Он нам нужен, чтобы было удобнее откатывать наши блокировки назад, простой сменой FALSE на TRUE и обратно. 

Составляем элементарную проверку на равенство FALSE для определения действия: "if OnlRead" и на выходе получаем функцию для "Сокрытия кнопки":

Form = Object.Form
Action = Form.Actions
    if OnlRead
       Action.FindAction(Button).Visible = TRUE  //Если "Правда" (TRUE) - кнопка видимая
    else
       Action.FindAction(Button).Visible = FALSE //Если "Ложь" (FALSE) - кнопка НЕ Видимая
    endif

Или еще короче:

Form = Object.Form
Action = Form.Actions
Action.FindAction(Button).Visible = not OnlRead

И Функцию по блокировки полей карточки:

if OnlRead
    Object.Requisites(ContrPol).CanGUIWrite = True  //Если не TRUE - Поле Блокировано
else
    Object.Requisites(ContrPol).CanGUIWrite = False //Если FALSE - Поле Доступное
endif

Или еще короче и изящнее:

Object.Requisites(ContrPol).CanGUIWrite = OnlRead

Теперь в нашем коде, достаточно написать "ButtonOR(Object;"InsertUpdateDB";TRUE)" для удаления с глаз кнопки, или наоборот "ButtonOR(Object;"InsertUpdateDB")" для ее возвращения на карточку.

И для Полей то же самое "ControlOR(Object;"Дополнение";TRUE)" и "ControlOR(Object;"Дополнение")". 

А если вам необходимо сделать эту операцию не один десяток раз, то вам сильно поможет следующий код:

   RequisitesTabMain = 'LongString;Дата5;' &
      'Дополнение;Депозитарий;ДаНет1;Наименование;ВеселенькиПример'

    foreach ReqName in CSubString(RequisitesTabMain; ';')
      ControlOR(Object;ReqName) // Или ControlOR(Object;ReqName;TRUE)
    endforeach

Данный пример специально расписан немного коряво, чтобы было видно более отчетливо, что наполнение Переменной "RequisitesTabMain" строками (LongString; Дата5; Дополнение; Депозитарий; ДаНет1; Наименование; ВеселенькиПример), может быть очень длинным с переносами и так далее.

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

CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName) // Опреледеляем User`а
//Далее вычислим Роли с повышенными приоритетами, например Финансового Директора и Бухгалтера:
FinDir = ServiceFactory.GetRoleMembers(ServiceFactory.GetRoleByName('ФинДир'); NIL) 
BuchRole = ServiceFactory.GetRoleMembers(ServiceFactory.GetRoleByName('Бухгалтер'); NIL)
// Ну и сразу проверим, вхож ли пользователь в эти роли (Это можно сделать и потом)
IsFinDir = FinDir.Find(CurrentUser)
IsBuch = BuchRole.Find(CurrentUser) 
//Теперь Фильтруем постепенно срезая права в карточке:
if CurrentUser.UserType = utAdministrator             // Проверяем: Админ ли это
  MessageBox("InfoBox";"Добро пожаловать, Админ!")    //По сути, ничего не делаем, это же Админ.
else                                                  // А что если это не Админ?
  ButtonOR(Object;"Delete";TRUE)                         // Сразу срезаем кнопку на удаление  
  if ServiceFactory.GetGroupMembers(ServiceFactory.GetGroupByName('Bro')).Find(CurrentUser)
     // Проверяем, входит ли пользователь в группу BRO, если да, то приветствуем его:
     MessageBox("InfoBox";"Привет, Бро!")
     ButtonOR(Object;"UpdateDate";TRUE) // Бро, конечно нам друг, но даты менять ему нельзя!
  else
     if IsFinDir or IsBuch
         // Проверяем, ФинДир это или Бухгалтер, если да, то просто убираем ненужные им поля:
         ControlOR(Object;"Tdate";TRUE)     // Пример без шутки
         ControlOR(Object;"FinansyD";TRUE)  // Пример без шутки
     else
        // Всем остальным - рубим права без жалости: 
      RequisitesTabMain = 'PMSourcers;T8BookDirection;Работник;ДаНет2;' &
      'PMProductType;ДаНет22;PMConnectionType;PMBook3;LongString;Дополнение;PMGenres1Level;' & 
      'PMEditionType;ИДСпр;PMPublishingHouse;Город;PMYear;LongString2;' & 
      'PMAgeRestrictions;PMGenres2Level;Дата25;Дата26;PMPrintKind;Дата16;' & 
      'ДаНет28;Текст2;Tekst4;ДаНет3;PMLanguages;ДаНет8;PMMultivolumeEdition;Дополнение4'
      foreach ReqName in CSubString(RequisitesTabMain; ';')
          ControlOR(Object;ReqName;TRUE)
      endforeach 
     endif
  endif
endif

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

Но в целом - всего что написано выше, вполне достаточно, чтобы начать работу по разграничению прав в любой из карточек СЭД Директум.

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

9
Авторизуйтесь, чтобы оценить материал.
2
Михаил Тарасов

Самая частая ошибка новичков, на моем опыте, при блокировке полей - это прописывание блокировки, но не прописывание разблокировки

Что то вроде этого (на событии форма показ):

if haveAccess
  Object.Form.Controls.FindControl("ctrl1").ReadOnly = true
endif

Проблема такого кода в том, что у справочников может быть много записей, но форма одна. В буквальном смысле для набора данных с несколькими записями используется один объект формы, в который только подставляются данные. И если мы для первой записи справедливо заблокировали поле, а потом нажмем стрелочку (или pageDown), перейдем на следующую запись, а в ней, например, не нужно блокировать это поле, то оно останется заблокированным. 

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

Тарас Асачёв

Ну в принципе логично, если предполагать изменение прав. В примере показана ситуация:

1. Можно все и всем

2. Если ты Админ, то все так и остается

3. Если ты не Админ - будем срезать ваши права.

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

Михаил Тарасов

Блокировки, конечно же, используются шире, чем: "Если не админ, то срезаем права"

Например, разрешить автору менять свою карточку, а остальным дать только просмотр - весьма распространенный кейс. 

Дан Олейников

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

Александр Куклин

Делал как-то раз подобный функционал. Задача состояла в том, чтобы менять состояния контролов на форме в зависимости от нескольких одновременно влияющих факторов, как то: роль пользователя в процессе, этап согласования, комбинации данных в карточке, наличие/отсутствие замещения.

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

  • Создаются списки реквизитов, которым должны быть назначены одинаковые права доступа в зависимости от некоторых условий и роли/группы пользователя. Например автор документа должен иметь доступ к реквизитам А,Б,В,Г,Д при создании документа, к реквизитам А,Б,В в дальнейшем, а финансист - к реквизитам Г,Д,Е. Получается три списка: Г,Д - общий список для обеих ролей, А,Б,В и Д,Е - частные списки для каждой роли.
  • Задаётся массив правил, зависящих от реквизитов карточки, для дальнейшего расчета логических условий. Например - вхождение стадии ЖЦ документа в некий список стадий, является ли пользователь, который запустил согласование, текущим пользователем, соответствует ли значение такого-то реквизита таким-то значениям и т.д.
  • Для тех ролей/групп, для которых нужен расчет доступности реквизитов, задаются комбинации правил и наборов реквизитов.

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

Тарас Асачёв

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

Тарас Асачёв

Я еще забыл такую полезную штуку как официальное сокрытие информации (CanGUIRead ):

Object.Requisites("Oklad").CanGUIRead = FALSE  // Скрываем из виду и не скрываем этого! 

После такого, поле будет выглядеть так:

Тарас Асачёв

А так да, как писал выше Михаил Тарасов:

Кейс на открытые данные для Автора, Админа и конкретной группы - 

CurrentUser = ServiceFactory.GetUserByName(Application.Connection.UserName) 

// Автор данного документа
Author = (CurrentUser.FullName == Object.Requisites(SYSREQ_EDOC_AUTHOR).DisplayText)                            
// Группа по подбору персонала
PodborPers = ServiceFactory.GetGroupMembers(ServiceFactory.GetGroupByName('PodborPers')).Find(CurrentUser)      
// Администраторы
Admin = ServiceFactory.GetGroupMembers(ServiceFactory.GetGroupByName('Administrators')).Find(CurrentUser)  

// Если это автор Заявки или сотрудник по подбору персонала, то...
if Author or PodborPers or Admin                                                
   //MessageBox("Совинформбюро";"Доступ не ограниченный.")
else

    Object.Requisites('LongString').CanGUIWrite = FALSE      // Блокируем! 
    Object.Requisites('Персона').CanGUIWrite = FALSE         // Блокируем! 
    Object.Requisites('Подразделение').CanGUIWrite = FALSE   // Блокируем! 
    Object.Requisites('String').CanGUIWrite = FALSE          // Блокируем! 
    Object.Requisites('LongString2').CanGUIWrite = FALSE     // Блокируем! 
    Object.Requisites('PriznakNP').CanGUIWrite = FALSE       // Блокируем! 
    Object.Requisites('String2').CanGUIWrite = FALSE         // Блокируем!
    Object.Requisites('Obosnovanie').CanGUIWrite = FALSE     // Блокируем!
    Object.Requisites('Prichina').CanGUIWrite = FALSE        // Блокируем!
    Object.Requisites("Oklad").CanGUIRead = FALSE            // Скрываем из виду!  

endif

 

Тарас Асачев: обновлено 12.09.2018 в 10:37

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