Как правильно заполнить табличную часть документа данными из запроса в 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

При разработке функционала заполнения одного документа на основании другого разработчики 1С часто сталкиваются с техническими ошибками: от блокировки изменения ссылок до появления сотен тысяч лишних строк или ошибок передачи данных между клиентом и сервером. В этой статье мы подробно разберем, как корректно получить данные из другого документа с помощью запроса и заполнить ими табличную часть на управляемой форме.

Основные причины ошибок при заполнении табличных частей

Рассмотрим частые проблемы, с которыми можно столкнуться при написании алгоритма заполнения:

  1. Ошибка «Объект недоступен для изменения»: возникает при попытке добавить строки в табличную часть через ссылку ДокументСсылка (например, ТТД = Объект.Ссылка; ТТД.Стоимость.Добавить()). Ссылка предназначена исключительно для чтения. Для изменения данных в управляемой форме необходимо обращаться напрямую к реквизиту формы Объект (тип ДанныеФормыСтруктура) или получать объект информационной базы на сервере с помощью метода ПолучитьОбъект().
  2. Ошибка преобразования данных XDTO (ВыборкаИзРезультатаЗапроса): выборка из результата запроса или сам результат запроса не могут передаваться с сервера на клиент, так как не являются сериализуемыми типами. Выполнять обработку запроса и заполнение табличной части необходимо полностью на сервере в процедуре без возврата выборки на клиент.
  3. Задвоение и разрастание строк (99 999+ записей): если в запросе к табличной части документа не указано условие ГДЕ с отбором по конкретной ссылке на документ-основание, 1С выберет строки из всех существующих в базе документов этого вида. Если же указать несколько таблиц в секции ИЗ через запятую без связи, произойдет декартово произведение (перемножение каждой строки первой таблицы на каждую строку второй).
  4. Незаполненные колонки при вызове ЗаполнитьЗначенияСвойств(): эта процедура копирует данные только между реквизитами с совпадающими именами. Если в документе-источнике колонка называется КоличествоВыработка, а в документе-приемнике — Количество, в запросе необходимо явно задать псевдоним через оператор КАК.

Шаг 1. Составление правильного запроса с отбором

Проанализируем структуру запроса. Чтобы получить строки из табличной части конкретного документа-источника (в нашем примере — уатЗаказГрузоотправителя), необходимо передавать ссылку на него в качестве параметра и устанавливать условие отбора по полю Ссылка:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЗаказУслуги.Номенклатура КАК Номенклатура,
    |	ЗаказУслуги.Содержание КАК Содержание,
    |	ЗаказУслуги.КоличествоВыработка КАК Количество,
    |	ЗаказУслуги.Цена КАК Цена,
    |	ЗаказУслуги.Сумма КАК Сумма,
    |	ЗаказУслуги.СтавкаНДС КАК СтавкаНДС,
    |	ЗаказУслуги.СуммаНДС КАК СуммаНДС
    |ИЗ
    |	Документ.уатЗаказГрузоотправителя.Услуги КАК ЗаказУслуги
    |ГДЕ
    |	ЗаказУслуги.Ссылка = &ДокументОснование";

Запрос.УстановитьПараметр("ДокументОснование", ЗаказГрузоотправителя);
РезультатЗапроса = Запрос.Выполнить();

Обратим внимание на переименование реквизита ЗаказУслуги.КоличествоВыработка КАК Количество. Благодаря назначению псевдонима процедура автоматического заполнения свойств сможет перенести значение в колонку Количество табличной части документа-приемника.

Шаг 2. Заполнение табличной части на сервере

Разберем два основных способа перегрузки данных из результата запроса в табличную часть формы.

Способ 1. Обход выборки запроса в цикле

Этот способ удобен, если перед добавлением строки требуется выполнить дополнительную обработку или проверку условий:


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

Способ 2. Загрузка из таблицы значений (оптимизированный)

Если имена полей в запросе полностью совпадают с именами колонок табличной части приемника, можно выгрузить результат запроса в таблицу значений и применить метод Загрузить(). Это ускоряет выполнение кода за счет отсутствия явного цикла по элементам:


&НаСервере
Процедура ЗаполнитьСтоимостьБыстро(ЗаказГрузоотправителя)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЗаказУслуги.Номенклатура КАК Номенклатура,
        |	ЗаказУслуги.Содержание КАК Содержание,
        |	ЗаказУслуги.КоличествоВыработка КАК Количество,
        |	ЗаказУслуги.Цена КАК Цена,
        |	ЗаказУслуги.Сумма КАК Сумма,
        |	ЗаказУслуги.СтавкаНДС КАК СтавкаНДС,
        |	ЗаказУслуги.СуммаНДС КАК СуммаНДС
        |ИЗ
        |	Документ.уатЗаказГрузоотправителя.Услуги КАК ЗаказУслуги
        |ГДЕ
        |	ЗаказУслуги.Ссылка = &ДокументОснование";
    
    Запрос.УстановитьПараметр("ДокументОснование", ЗаказГрузоотправителя);
    
    // Выгружаем результат запроса напрямую в табличную часть формы
    ТаблицаРезультирующая = Запрос.Выполнить().Выгрузить();
    Объект.Стоимость.Загрузить(ТаблицаРезультирующая);
    
КонецПроцедуры

Шаг 3. Связка клиентского и серверного контекстов

Вызов процедуры заполнения с формы должен производиться по стандартной клиент-серверной схеме. На клиенте обрабатывается нажатие кнопки или команды, после чего вызывается серверная процедура без возврата значений:


&НаКлиенте
Процедура ЗаполнитьНаОснованииЗаказа(Команда)
    ЗаполнитьНаОснованииЗаказаНаСервере();
КонецПроцедуры

Соблюдение этой структуры исключает ошибки сериализации XDTO и обеспечивает корректное обновление данных в интерфейсе пользователя.

← На главную