Множественный выбор в детальном разделе

21 1

Кроме множественного выбора, в статье присутствует информация по:

  • включению\отключению событий реквизитов;
  • пользовательском выборе из справочника.

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

Принцип работы глазами пользователя

1. Есть некий справочник SimpleRef. Откроем карточку записи:

2. Выберем несколько записей справочника "Места появления затрат", затем нажмем Enter:

3. В результате в детальный раздел добавились 4 новые строки, первая строка — изменилась:

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

4. Повторим выбор еще раз, изменив значения полей Канал затрат и *НДС, %:

В итоге: 4-я строка изменилась, 6-9 строки добавились в соответствие с выбранными записями; Сумма с НДС строки 4 разбилась пропорционально между выбранными строками.

 

Постановка задачи

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

  1. При выборе нескольких Мест появления затрат (далее МПЗ) из текущей строки, должно создаваться количество новых строк в табличной части, равное количеству выбранных МПЗ минус 1.
  2. Во вновь создаваемых строках:   
  • Канал затрат — заполняется значением соответствующего поля строки, из которой производился выбор.
  • Сумма с НДС —​ <Сумма с НДС строки, из которой производился множественный выбор> разделить на  <количество выбранных МПЗ>.
  • Сумма без НДС — заполнится только в случае, если будут заполнены поля *НДС, % и Сумма с НДС (сама формула расчета, полагаю, ясна).
  • *НДС, % — заполняется значением соответствующего поля строки, из которой производился выбор.

 

Реализация

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

Процесс реализации условно можно разделить на 4 части:

1. Переопределение механизма выбора (SelectMode smLike\smSelect\smCard), так как стандартный механизм уже не будет работать.

2. Обработка вычислений у других реквизитов, которые срабатывают при добавлении строк и "сбивают" указатель с текущей строки.

3. Выделение из всех реквизитов детального раздела только тех, значения которых нужно передать в новые строки.

4. Заполнение необходимыми данными вновь создаваемых строк.

 

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

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

А вот и код:

EMPTY_STR = ''

// Объект IDataSet
DDS2 = Object.DetailDataSet(2)

// "Запомнить" текущее значение реквизита МПЗ на случай,
// если новое значение не будет выбрано
CostOrigPlaceValue = DDS2.CostOriginationPlaceT2

// Получить набор данных справочника "CostOriginationPlaces"
// со всеми записями
Ref = References.CostOriginationPlaces.GetComponent()

// Получить главное представление справочника
View = Ref.CreateView(Ref.MainViewCode)

// Установить отображения представления в режиме "Выбор"
View.ViewMode = vmSelect 

// Разрешить множественный выбор  
View.MultiSelection = true

// -------------------------------------------------------
// Переопределение пользовательского выбора из справочника

// Наложить фильтр по введенному значению, когда 
// введен какой-то текст и затем нажата 
// клавиша "Enter"
if SelectMode = smLike

  // Недостаток такой фильтрации - она происходит уже
  // на локальном компьютере, т.е. не накладывает 
  // ограничения на данные при запросе
  Ref.Filtered = true

  // Наименование - реквизит, по значениям которого 
  // накладывается фильтр
  Ref.Filter = 'Наименование like "%' & InputValue & '%"'
endif

// Установить признак восстановления значения реквизита 
// МПЗ к прежнему значению, если форма была показана 
// и закрыта без выбора
Flag = true

// smLike при пустом тексте ввода не имеет смысла
if (SelectMode = smLike and InputValue <<>> EMPTY_STR)
 or (SelectMode = smSelect)

  // Показать форму
  View.MainForm.Show
else
  // Так как реквизит МПЗ не является обязательным, 
  // здесь обрабатывается ситуация, когда текст ввода 
  // просто очистили и сохранили карточку.
  if not (SelectMode = smCard) 
    DDS2.CostOriginationPlaceT2 = EMPTY_STR
    Flag = false
  endif  
endif

// Отключить фильтрацию
Ref.Filtered = false

// Выбрана хотя бы одна запись
if View.MainForm.Result = mrOK

  // Заполнить реквизит МПЗ текущей строки значением 
  // первой из выбранных строк
  IDRec = View.SelectedRecordsID(0)
  DDS2.CostOriginationPlaceT2 = 
  References.CostOriginationPlaces.ObjectInfo(IDRec).Code    
	
  // ***Отключить события "При изменении реквизита"***
  // Отключить события всех реквизитов и событий, у 
  // которых есть собственные вычисления, нарушающие 
  // механизм работы данного вычисления.
  // Сами реквизиты и события определяются опытным путем. 
  // Альтернативный вариант - изменить сами вычисления 
  // на этих реквизитах и\или в событиях.

  // "Сумма с НДС" 
  DDS2.Requisites('Цена2Т2').Events.Events(reOnChange).Enabled = false

  // "*НДС, %"
  DDS2.Requisites('NDSProcentT2').Events.Events(reOnChange).Enabled = false

  DDS2Events = DDS2.Events

  // Отключить событие "Добавление После" для детального раздела 2
  DDS2Events.Events(dseAfterInsert).Enabled = False


  // Проверить обязательный реквизит "*НДС, %" на означеность    
  if Assigned(DDS2.NDSProcentT2)
    NDSRef = References.NDSProcents.GetObjectByCode(DDS2.NDSProcentT2)
  else
    NDSRef = EMPTY_STR
  endif 

  NewSumNDS = null      
  NewSumOutNDS = null
   
  // Получить новое значение реквизита "Сумма с НДС",
  // разделив его на количество выбранных записей, 
  // если записей > 1
  if Assigned(DDS2.Requisites('Цена2Т2').Value) 
    and (View.SelectedRecordCount > 1)
    NewSumNDS = DDS2.Requisites('Цена2Т2').Value / View.SelectedRecordCount
    DDS2.Requisites('Цена2Т2').Value = NewSumNDS

    // Заполнить реквизит "Сумма без НДС"
    // Рассчитать "вручную", так как событие отключено
    if Assigned(NDSRef)
      NewSumOutNDS = (NewSumNDS * 100) / (NDSRef.Цена1 + 100)
      DDS2.Requisites('АmountT2').Value = NewSumOutNDS
   endif
  endif 

  AccountRefID = References.SimpleRef.GetComponent().ComponentID

  // Получить наименования реквизитов детального раздела 2
  // справочника.
  // Такие системные реквизиты, как: XRecID, Vid, Analit,
  // NumStr, XRecStat в выборку не попадают
  QueryText = Format("select Kod 
                      from MBVidAnRecv 
		      where Vid = %s and Razd = %s";
		      ArrayOf(AccountRefID; "'С'")  // "С" - Таблица 2
		    )

  QuerySelectList = CreateQuery()
  QuerySelectList.CommandText = QueryText

  // Открыть набор данных запроса
  QuerySelectList.Open

  // Сделать текущей первую запись набора данных
  QuerySelectList.First

  // Создать список, в котором будет храниться информация 
  // вида (наименование реквизита ; значение реквизита)
  RecvList = CreateList()

  // Записать данные текущей строки: 
  // наименование реквизита - из SQL-запроса
  // значение реквизита - из детального раздела
  while not QuerySelectList.EOF
    RecvList.Add(QuerySelectList.Kod; DDS2.Requisites(QuerySelectList.Kod).Value)
    QuerySelectList.Next
  endwhile

  QuerySelectList.Close    

  Index = 1 
	
  while Index < View.SelectedRecordCount

    // При множественном выборе добавить в 
    // детальный раздел 2 (количество выбранных строк - 1)
    // и скопировать в них значения реквизитов 
    // текущей строки
    RecvListItem = 0
  
    // Добавить запись в конец набора данных и открыть ее
    DDS2.Append
  
    // "Пробежаться" по всем, выбранным SQL-запросом,
    // реквизитам и заполнить соответствующими данными
    while RecvListItem <= (RecvList.Count - 1)
      DDS2.Requisites(RecvList.Names(RecvListItem)).Value = RecvList.Values(RecvListItem)
      RecvListItem = RecvListItem + 1
    endwhile 
  
    // Заполнить реквизиты, значения которых необходимо переопределить
    IDRec = View.SelectedRecordsID(Index)
    DDS2.CostOriginationPlaceT2 = References.CostOriginationPlaces.ObjectInfo(IDRec).Code
    DDS2.Requisites('Цена2Т2').Value = NewSumNDS
    DDS2.Requisites('АmountT2').Value = NewSumOutNDS
  
    Index = Index + 1
  endwhile

  // Включить события
  DDS2.Requisites('Цена2Т2').Events.Events(reOnChange).Enabled = true
  DDS2.Requisites('NDSProcentT2').Events.Events(reOnChange).Enabled = true
  DDS2Events.Events(dseAfterInsert).Enabled = true  
 
  NDSRef = nil
else

  // Восстановить значение реквизита МПЗ, если не было выбрано новое
  if Flag
    DDS2.CostOriginationPlaceT2 = CostOrigPlaceValue
  endif
endif

// Обработка режима выбора формы-карточки(Ctrl+F4)
if SelectMode = smCard
  SelectFromReferenceAction('CostOriginationPlaces'; SelectMode; InputValue; DDS2.Requisites('CostOriginationPlaceT2'))
endif           

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

21
Авторизуйтесь, чтобы оценить материал.
2
Евгения Мамаева

Андрей, великодушно благодарю за статью! Очень помогли!

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