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