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