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