В процессе разработки в 1С часто возникает задача, когда необходимо перенести данные из одного документа в другой, причем данные в источнике распределены по нескольким табличным частям. Например, в документе Заявка есть список продукции и связанные с ними технологии, а в документе ОтчетСотрудника нужно выбрать конкретную технологию по конкретной продукции. Разберем пошагово, как реализовать этот механизм максимально эффективно и удобно для пользователя.
Представим ситуацию: у нас есть основной документ Заявка. В нем три табличные части (ТЧ): одна главная (например, Заявка с перечнем продукции) и две подчиненные (например, Технологии). Нам нужно в другом документе, ОтчетСотрудника, при нажатии кнопки Добавить в его ТЧ Работа вызывать список выбора. Этот список должен содержать комбинированные данные: Продукция + Технология + Характеристики, чтобы пользователь мог выбрать нужную строку одним кликом.
Рассмотрим типичную ошибку начинающих разработчиков — выполнение запросов внутри цикла. Проанализируем ситуацию: если мы сначала получаем список продукции, а потом в цикле для каждой продукции ищем технологии, это создает лишнюю нагрузку на базу данных. Вместо этого мы используем Левое соединение в одном запросе. Это позволит нам сразу получить готовую таблицу со всеми возможными комбинациями.
Выясним, как составить правильный запрос, который объединит данные из двух табличных частей документа-источника:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаявкаТехнологии.Технологии КАК Технология,
| ЗаявкаЗаявка.Продукция КАК Продукция,
| ЗаявкаЗаявка.РазмерФормат КАК РазмерФормат,
| ЗаявкаТехнологии.Количество КАК Количество
|ИЗ
| Документ.Заявка.Заявка КАК ЗаявкаЗаявка
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.Заявка.Технологии КАК ЗаявкаТехнологии
| ПО ЗаявкаЗаявка.Ссылка = ЗаявкаТехнологии.Ссылка
| И ЗаявкаЗаявка.Продукция = ЗаявкаТехнологии.Продукция
|ГДЕ
| ЗаявкаЗаявка.Ссылка = &СсылкаЗаявка";
Запрос.УстановитьПараметр("СсылкаЗаявка", Объект.ДокументОснование);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
В данном примере мы соединяем таблицы по ссылке на документ и по полю Продукция. Это гарантирует, что мы получим только те технологии, которые относятся к конкретным позициям в данной заявке.
После того как данные получены в таблицу значений РезультатЗапроса, нам нужно предоставить пользователю возможность выбора. Рассмотрим простейший и эффективный метод для обычных форм — ВыбратьСтроку(). Этот метод открывает стандартное окно со списком, где пользователь может найти нужную запись.
Проанализируем программную реализацию выбора:
ВыбраннаяСтрока = РезультатЗапроса.ВыбратьСтроку("Выберите технологию из списка");
Если ВыбраннаяСтрока <> Неопределено Тогда
// Пользователь выбрал строку, переходим к заполнению
НоваяСтрока = Объект.Работа.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыбраннаяСтрока);
КонецЕсли;
Использование метода ЗаполнитьЗначенияСвойств значительно упрощает код. Он автоматически копирует данные из колонок таблицы запроса в колонки табличной части документа, если их имена совпадают (например, Технология, Количество и т.д.).
Часто требуется, чтобы список выбора был не общим, а зависел от того, какая продукция уже выбрана в текущей строке документа. Посмотрим, как это реализовать. Если в табличной части Работа уже указана продукция, мы можем передать её в параметры запроса, чтобы ограничить выбор.
Разберем вариант с фильтрацией:
// Допустим, мы вызываем это из события НачалоВыбора или ПриИзменении
ТекущиеДанные = Элементы.Работа.ТекущиеДанные;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаявкаТехнологии.Технологии КАК Технология,
| ЗаявкаТехнологии.Количество КАК Количество
|ИЗ
| Документ.Заявка.Технологии КАК ЗаявкаТехнологии
|ГДЕ
| ЗаявкаТехнологии.Ссылка = &СсылкаЗаявка
| И ЗаявкаТехнологии.Продукция = &Продукция";
Запрос.УстановитьПараметр("СсылкаЗаявка", Объект.Заявка);
Запрос.УстановитьПараметр("Продукция", ТекущиеДанные.Продукция);
ТаблицаДляВыбора = Запрос.Выполнить().Выгрузить();
ВыбраннаяТехнология = ТаблицаДляВыбора.ВыбратьСтроку();
Если ВыбраннаяТехнология <> Неопределено Тогда
ТекущиеДанные.Технология = ВыбраннаяТехнология.Технология;
ТекущиеДанные.Количество = ВыбраннаяТехнология.Количество;
КонецЕсли;
Если ваша конфигурация работает на управляемых формах (интерфейс "Такси"), использование ВыбратьСтроку() может быть неудобным или недоступным (из-за модальности). В таком случае мы рекомендуем использовать СписокЗначений с методом ПоказатьВыборЭлемента().
Рассмотрим, как сформировать красивое представление для выбора:
СписокВыбора = Новый СписокЗначений;
Для Каждого СтрокаТЗ Из ТаблицаДляВыбора Цикл
Представление = СтрШаблон("%1 (%2) - %3 шт.",
СтрокаТЗ.Продукция,
СтрокаТЗ.Технология,
СтрокаТЗ.Количество);
// В значение кладем структуру или саму строку ТЗ для последующего разбора
СписокВыбора.Добавить(СтрокаТЗ, Представление);
КонецЦикла;
// В управляемом приложении используем описание оповещения для немодального вызова
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораТехнологии", ЭтотОбъект);
СписокВыбора.ПоказатьВыборЭлемента(ОписаниеОповещения, "Выберите технологию");
В этом случае пользователь увидит понятный текст, такой как "Вывеска из акрила (Гравировка) - 1 шт.", что сводит к минимуму риск ошибки при вводе данных.
Для того чтобы ваша система работала стабильно и быстро, придерживайтесь следующих правил:
КАК) точно так же, как они называются в табличной части. Это позволит использовать ЗаполнитьЗначенияСвойств максимально эффективно.Заявка (ссылка-основание). Если ссылка пустая, запрос не вернет данных, и пользователю стоит вывести предупреждение.Заявка очень много и в каждой ТЧ сотни строк, убедитесь, что поля, по которым идет соединение (например, Продукция), проиндексированы в метаданных (свойство "Индексировать").ВременноеХранилище. Это избавит систему от повторных обращений к базе данных при каждом нажатии кнопки "Добавить".Таким образом, мы проанализировали ситуацию и нашли оптимальное решение: использование единого запроса с соединением таблиц и интерактивный метод выбора строки. Это позволяет быстро и корректно заполнять документы сложной структуры, сохраняя удобство для конечного пользователя.