# Множественный выбор через Btk_ChoiceAbsAvi

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

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

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

## Как работает механизм

Работа механизма состоит из следующих этапов:

1. Прикладной код открывает lookup со списком доступных записей.
2. Пользователь отмечает требуемые записи чекбоксами.
3. Механизм хранит сформированный набор, пока открыт текущий экземпляр формы.
4. Пользователь подтверждает выбор или отменяет форму.
5. После подтверждения прикладной код получает ключи отмеченных записей и выполняет требуемую бизнес-операцию.

### Галочки и выделение строк

Галочки и стандартное выделение строк в таблице выполняют разные задачи:

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

Пользователь может работать с выбором следующими способами:

| Действие в форме | Результат |
|---|---|
| Установить или снять галочку | Добавляет одну запись в итоговый набор или удаляет ее. |
| Выделить строки | Только подготавливает строки для групповой операции; итоговый набор не меняется. |
| Выполнить «Выбрать выделенные записи» | Устанавливает галочки у всех выделенных строк. |
| Выполнить «Снять выбор с выделенных записей» | Снимает галочки со всех выделенных строк. |
| Выполнить «Очистить все» | Снимает все установленные галочки. |

Операция выбора всех записей списка в общий механизм не входит. Если она нужна, ее реализуют в конкретном прикладном представлении.

Пример формы выбора с отмеченными записями:

![Окно выбора «Вид ТМЦ» с отмеченными строками](btkChoiceAbsLookupSelected.png)

## Подключение к списку

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

### Определите ключ записи

Выберите вариант `CheckboxChoice`, соответствующий типу ключа:

| Вариант | Тип ключа | Стандартное имя ключа |
|---|---|---|
| `CheckboxChoiceNLong` | `NLong` | `id` |
| `CheckboxChoiceNGid` | `NGid` | `gidRef` |

Если представление использует другое имя ключа, переопределите `sKeyAttr` в trait списка. Если имя отличается только в одном SQL-запросе, передайте его вторым аргументом `choiceFlagSelect`.

### Подключите механизм к Avi

Подмешайте `Btk_ChoiceAbsAvi` к классу прикладной выборки, а `CheckboxChoiceNLong` или `CheckboxChoiceNGid` — к trait списка в зависимости от типа ключа.

Ниже приведен минимальный шаблон. Замените `ExampleAvi`, `ExampleDvi` и `List_ForChoose` именами прикладных классов. Импорты базовых типов и методы, не относящиеся к механизму выбора, в шаблоне не показаны.

<!-- Начало кода -->
```scala
import ru.bitec.app.btk.sel.service.Btk_ChoiceAbsAvi

object ExampleAvi extends ExampleAvi

class ExampleAvi extends ExampleDvi with Btk_ChoiceAbsAvi {
  def list_ForChoose(): List_ForChoose = {
    new List_ForChoose {
      override def meta: TypeTag = this
    }
  }

  trait Default extends DviDefault

  trait List extends Default with DviList

  trait List_ForChoose extends List with CheckboxChoiceNLong {
    // choiceFlagSelect добавляет признак выбранности для отображения чекбокса.
    override protected def selectStatement: String = {
      s"""
         |select t.*,
         |       ${choiceFlagSelect("t")}
         |from (${super.selectStatement}) t
         |""".stripMargin
    }

    // Возвращает ключ и признак выбора в дополнительном запросе onRefreshExt.
    override protected def onRefreshExt: String =
      s"""WITH t AS (
         |  SELECT $sKeyParamAsAttr
         |)
         |SELECT t.$sKeyAttr,
         |       ${choiceFlagSelect("t")}
         |FROM t
         |""".stripMargin
  }
}
```
<!-- Конец кода -->

### Добавьте поля выбора в запросы

В основной запрос списка добавьте результат `choiceFlagSelect`. В `onRefreshExt` также верните первичный ключ записи и результат `choiceFlagSelect`. Это позволяет механизму сформировать актуальное состояние чекбокса при обновлении данных представления.

В шаблоне выше эти требования выполняют два фрагмента:

- `${choiceFlagSelect("t")}` добавляет служебные поля выбора в основной запрос и `onRefreshExt`;
- `t.$sKeyAttr` возвращает первичный ключ строки для сопоставления данных `onRefreshExt` с основным списком.

Если ключ выбора `sKeyAttr` отличается от первичного ключа строки, верните в `onRefreshExt` оба значения: первичный ключ с именем `id`, `gid` или `gidRef` и ключ выбора.

### Настройте AVM

Объявите `bChoice_dz` в блоке `<attributes>`, как показано [в данном разделе](https://help.globalerp.ru/books/GlobalServerAppGuide/SNAPSHOT/html/040_selection/030_%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0_%D0%B8_%D0%BE%D1%84%D0%BE%D1%80%D0%BC%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%D0%BA%D0%B8.html#id12), если нужно изменить наименование или порядок колонки. Без явного объявления механизм использует наименование `" - "` и порядок `-100`.

Для групповых операций включите [мультиселект](https://help.globalerp.ru/books/GlobalServerAppGuide/SNAPSHOT/html/040_selection/030_%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%82%D0%BA%D0%B0_%D0%B8_%D0%BE%D1%84%D0%BE%D1%80%D0%BC%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D1%8B%D0%B1%D0%BE%D1%80%D0%BA%D0%B8.html#id5) в отображении и таблице. Параметры `doubleClickOperation.createFormMode` и `doubleClickOperation.lookupMode` задают операцию, которую форма выполняет по двойному нажатию в соответствующем режиме. Операция `changeSelectedByClick` переключает состояние выбора текущей строки. Для обычного изменения чекбокса эти параметры не требуются.

<!-- Начало кода -->
```xml
<representation name="List_ForChoose"
                editMode="edit"
                isMultiSelect="true"
                doubleClickOperation.createFormMode="changeSelectedByClick"
                doubleClickOperation.lookupMode="changeSelectedByClick">
    <layout>
        <tabComposer>
            <frame>
                <grid isMultiSelectEnabled="true"/>
            </frame>
        </tabComposer>
    </layout>
    <attributes>
        <attr name="bChoice_dz" caption="Выбор" order="-100"/>
    </attributes>
</representation>
```
<!-- Конец кода -->

### Откройте lookup и получите результат

Откройте представление как lookup и передайте результат тому же представлению для разбора:

<!-- Начало кода -->
```scala
// Открывает список с чекбоксами и встроенными групповыми операциями.
val listRep = ExampleAvi.list_ForChoose()
val data = listRep.newForm().openLookup()

// Возвращает ключи записей, отмеченных пользователем.
val idavSelected = listRep.chosenKeys(data)

idavSelected.foreach { idvSelected =>
  processSelected(idvSelected)
}
```
<!-- Конец кода -->

`chosenKeys` возвращает типизированный список ключей только для подтвержденного результата lookup. При отмене формы он возвращает пустой список. Если бизнес-логике нужно различать отмену и подтверждение без выбранных записей, дополнительно проверьте результат закрытия формы.

## Дополнительные настройки

### Предварительный выбор

Передайте параметр `saPresetValues#`, если часть записей должна быть отмечена при открытии формы. Ключи укажите одной строкой через разделитель `;`.

<!-- Начало кода -->
```scala
val listRep = ExampleAvi.list_ForChoose()
val data = listRep
  .newForm()
  .params(Map("saPresetValues#" -> idavPreset.mkString(";")))
  .openLookup()

val idavSelected = listRep.chosenKeys(data)
```
<!-- Конец кода -->

## Ограничения

- Основной запрос и `onRefreshExt` должны возвращать первичный ключ с именем `id`, `gid` или `gidRef`, а также ключ выбора `sKeyAttr`, если он отличается от первичного.
- Порядок ключей в результате не гарантируется. Если порядок важен для бизнес-операции, вызывающий код формирует его самостоятельно.
- `Btk_ChoiceAbsAvi` не проверяет предметные ограничения и права на последующее изменение данных и не управляет транзакцией бизнес-операции. Эти задачи выполняет вызывающий код.