При разработке конфигураций в системе 1С:Предприятие 8.3 начинающие разработчики часто сталкиваются с задачей автоматизации ввода данных. Типичный пример — подстановка единицы измерения (штуки, метры, комплекты) сразу после того, как пользователь выбрал товар в табличной части документа. Разберем подробно, как реализовать эту логику, исправим типичные синтаксические ошибки и рассмотрим лучшие практики проектирования метаданных.
Часто новички пытаются обращаться к данным документа через менеджер объекта, используя конструкцию типа Документы.Приход.ТабличнаяЧасть.... Выясним причину, почему это не работает. В языке 1С объект Документы.Приход — это менеджер всех документов данного вида, а не конкретный экземпляр, который открыт у пользователя на экране.
Для работы с данными «здесь и сейчас» в управляемой форме необходимо использовать коллекцию ЭтотОбъект (в модуле объекта) или данные формы. Если мы работаем в модуле формы, нам нужно получить конкретную строку, в которой произошло изменение. Проанализируем правильный подход к получению текущей строки:
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
// Получаем строку, с которой в данный момент работает пользователь
СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
// Теперь мы можем обращаться к реквизитам этой строки
// Например: СтрокаТабличнойЧасти.Номенклатура
КонецПроцедуры
В современных управляемых формах код разделен на клиентский (выполняется на компьютере пользователя) и серверный (выполняется на сервере приложений). Рассмотрим ситуацию: реквизит Номенклатура — это ссылка. На клиенте мы видим только ее текстовое представление, но не можем «заглянуть внутрь» объекта, чтобы узнать его единицу измерения или другие свойства.
Чтобы получить данные из справочника, нам необходимо «сбегать» на сервер. Разберем по шагам, как это реализовать с помощью функции-помощника:
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверную функцию, чтобы получить данные о номенклатуре
ДанныеНоменклатуры = ПолучитьДанныеНоменклатурыНаСервере(ТекущаяСтрока.Номенклатура);
// Заполняем реквизиты в строке документа
ТекущаяСтрока.ЕдИзм = ДанныеНоменклатуры.ЕдиницаИзмерения;
ТекущаяСтрока.Цена = ДанныеНоменклатуры.Цена;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатурыНаСервере(СсылкаНаНоменклатуру)
// Создаем структуру для передачи нескольких значений за один вызов
Результат = Новый Структура("ЕдиницаИзмерения, Цена");
// Получаем данные прямо из ссылки
Результат.ЕдиницаИзмерения = СсылкаНаНоменклатуру.ЕдиницаИзмерения;
// Допустим, цену мы берем из регистра сведений
// Результат.Цена = РегистрыСведений.ЦеныНоменклатуры.ПолучитьПоследнее(ТекущаяДата(), Новый Структура("Номенклатура", СсылкаНаНоменклатуру)).Цена;
Возврат Результат;
КонецФункции
Важный момент: использование директивы &НаСервереБезКонтекста предпочтительнее, так как она не передает на сервер всю форму целиком, что значительно ускоряет работу программы.
Если в вашей конфигурации используется архитектура с несколькими справочниками (например, «ВходныеДвери», «МежкомнатныеДвери», «Ламинат»), и реквизит Номенклатура имеет составной тип, нам нужно научиться определять, к какому справочнику относится выбранный элемент. Рассмотрим, как это сделать с помощью функции ТипЗнч и метаданных.
Вместо перечисления наименований, проанализируем ситуацию с использованием типов:
&НаСервере
Функция ОпределитьЕдиницуПоТипу(Ссылка)
// Проверяем тип объекта через функцию ТипЗнч
Если ТипЗнч(Ссылка) = Тип("СправочникСсылка.ВхДвери")
ИЛИ ТипЗнч(Ссылка) = Тип("СправочникСсылка.МкДвери") Тогда
Возврат Справочники.КлассификаторЕдиницИзмерения.НайтиПоНаименованию("шт");
ИначеЕсли ТипЗнч(Ссылка) = Тип("СправочникСсылка.Ламинат") Тогда
Возврат Справочники.КлассификаторЕдиницИзмерения.НайтиПоНаименованию("м2");
КонецЕсли;
КонецФункции
Однако такой подход считается «жестким кодированием» (hardcode) и усложняет поддержку системы. Если завтра добавится справочник «Линолеум», придется менять программный код.
Профессиональный подход к разработке в 1С предполагает минимизацию условий Если...Тогда в пользу универсальных механизмов. Рассмотрим два способа оптимизации вашей базы данных:
Справочники.Номенклатура. Чтобы различать товары, используйте реквизит ВидНоменклатуры. Для каждого вида (Двери, Погонаж, Плитка) можно настроить свои наборы дополнительных реквизитов и свойств. В этом случае единица измерения будет просто одним из реквизитов карточки товара.ЕдиницаИзмерения (тип — СправочникСсылка.КлассификаторЕдиницИзмерения). Тогда код станет максимально простым: ТекущаяСтрока.ЕдИзм = ТекущаяСтрока.Номенклатура.ЕдиницаИзмерения;.Характеристик. Это позволит не плодить сотни элементов в справочнике (например, «Дверь белая 800», «Дверь белая 900»), а иметь одну модель с разными свойствами.Если ваша конфигурация создана на основе современных типовых решений, в ней наверняка есть модуль ОбщегоНазначения. Он позволяет получать реквизиты объектов без написания собственных серверных функций. Посмотрим на пример:
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
Строка = Элементы.Товары.ТекущиеДанные;
// Получаем реквизит единицы измерения одним вызовом через БСП
Строка.ЕдИзм = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Строка.Номенклатура, "ЕдиницаИзмерения");
КонецПроцедуры
Этот метод является стандартом де-факто в современной разработке, так как он автоматически обрабатывает кэширование и оптимизирует запросы к базе данных.
Подведем итог: для реализации автоматической подстановки единиц измерения всегда используйте событие ПриИзменении, работайте с коллекцией ТекущиеДанные и не забывайте разделять код на клиентскую и серверную части. Стремитесь к тому, чтобы данные (единицы измерения, цены) хранились внутри справочника номенклатуры, а не прописывались условиями в коде документа.