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