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