Пользовательский выбор из справочника. Часть 2

10 1

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

●    введенное значение нужно искать не только в наименовании, но и среди других реквизитов;

●    нужно выбирать из таких системных справочников как Типы справочников, Отчеты и т.д.

Что же делать в этом случае?!

Для начала определим те условия, которые должны выполняться при выборе (функция SelectFromReferenceAction() им полностью соответствует):

●    при очистке значения реквизита справочник не вызывается;

●    при нажатии на клавишу или кнопку «…» справочник вызывается без фильтра по введенному значению;

●    при повторном выборе из справочника указатель записи устанавливается на ту запись, которая уже выбрана;

●    при нажатии на комбинацию клавиш или на кнопку «…» вместе с клавишей вызывается карточка записи, если поле заполнено;

●    если отменили выбор из справочника, то восстанавливается прежнее значение реквизита;

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

Напомню, что в вычислении «Выбор из справочника» есть предопределенные реквизиты, которые будем использовать в коде:

●     SelectMode - тип выбора из справочника, принимает значения:

●            smSelect – показ формы-списка;

●            smLike – показ формы-списка только с теми записями, которые удовлетворяют введенным в поле данным;

●            smCard – показ формы-карточки для введенного в поле значения.

●    InputValue - текст, введенный в управляющий элемент, который связан с реквизитом.

●    Requisite – ссылка на реквизит типа IRequisite, событие Выбор из справочника которого переопределяем.

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

Вот небольшой пример, того как при выборе работника искать не только по наименованию, но и по должности:

  if Assigned(InputValue) or (SelectMode <> smLike)
    EmplRefFactory = References.РАБ
    // Если нажали сочетание Ctrl+F4 для открытия карточки записи
    if SelectMode = smCard
      // Показать карточку
      EmplRec = EmplRefFactory.GetObjectByCode(Requisite.Value)
      EmplRec.Form.Show
    else  
      WhereStr = ''   
      // Если в поле ввели значение и нажали Enter, то найти записи, 
      // удовлетворяющие введенному значению, искать в наименовании и в должности 
      if Assigned(InputValue) and (SelectMode = smLike)
        WhereStr = PreprocQuery(Format('
          (!РАБ!.!РАБ.Наименование! like "%%%0:s%%" or 
          !РАБ!.!РАБ.Строка! like "%%%0:s%%")';
          // Заменить пробелы, чтобы искать по словосочетанию
          Replace(InputValue; ' '; '%')))
      endif
      EmplRef = EmplRefFactory.GetComponent()
      AddWhere = EmplRef.AddWhere(WhereStr)
      EmplRef.Open
      // Если подходит только 1 запись, то сразу заполнить поле, не показывая форму-список справочника   
      if (SelectMode = smLike) and (EmplRef.RecordCount = 1)
        Requisite.Value = EmplRef.SYSREQ_CODE
      else  
        // Показать форму-список справочника
        // Если повторно выбирают запись, то указатель должен быть установлен на выбранной ранее записи
        View = EmplRef.CreateView(EmplRef.MainViewCode)
        if Assigned(Requisite.Value)
          EmplRef.Rules.Rules(SET_FIRST_RECORD_IN_LIST_FORM_RULE_ID).Enabled = 
          not EmplRef.Locate(SYSREQ_CODE; Requisite.Value)
          EmplRef.Refresh  
        endif
        View.ViewMode =  vmSelect 
        View.MultiSelection = FALSE
        View.MainForm.Show
        if View.MainForm.Result = mrOK
          // Сохранить выбранное значение
          EmplID = View.SelectedRecordsID(0)
          Requisite.Value = КодПоAnalit(EmplID)
        endif
      endif
      EmplRef.Close
      EmplRef.DelWhere(AddWhere)
    endif 
  endif
Вячеслав Смирнов

Важно помнить, что метод AddWhere не содержит никаких защитных механизмов, происходит добавление новых строк в секцию where SQL-запроса. Поэтому, InputValue надо обеззараживать перед использованием.

В данном влучае опасным символом является двойная кавычка - она нарушит структуру SQL-запроса. Например, если в поле ввода ввести текст Фамилия"Должность. В качестве решения можно заменить строку кода

Replace(InputValue; ' '; '%')

такой строкой:

Replace(Replace(InputValue; ' '; '%'); '"'; '""')

Тут каждая двойная кавычка заменяется на пару двойных кавычек. А при выполнении SQL-запроса каждая пара будет восприниматься как одна двойная кавычка. То есть на результаты SQL-запроса данная замена не повлияет (проверил).

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

SafeInputValue = Replace(Replace(InputValue; ' '; '%'); '"'; '""')

И ниже использовать уже SafeInputValue.

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