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

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

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

Постановка задачи и анализ ситуации

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

Шаг 1: Подготовка данных из коллекции формы

Разберем типичную проблему, с которой сталкиваются разработчики. При попытке получить массив контрагентов из таблицы на форме кодом ДанныеТФ.ВыгрузитьКолонку("Контрагент") часто возникает ошибка. Выясним причину: переменная на форме является объектом типа ДанныеФормыКоллекция, у которого отсутствует метод ВыгрузитьКолонку.

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


// Преобразуем коллекцию формы в таблицу значений
ТаблицаДанных = ДанныеТФ.Выгрузить();
// Теперь мы можем получить массив уникальных ссылок на контрагентов или договоры
МассивКонтрагентов = ТаблицаДанных.ВыгрузитьКолонку("Контрагент");
МассивДоговоров = ТаблицаДанных.ВыгрузитьКолонку("Договор");

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

Шаг 2: Получение данных из справочника через запрос

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


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

Запрос.УстановитьПараметр("МассивДоговоров", МассивДоговоров);
РезультатЗапроса = Запрос.Выполнить();
ТаблицаУслугИзДоговоров = РезультатЗапроса.Выгрузить();

В данном примере мы выбираем не только данные для заполнения строки (номенклатуру, цену), но и ссылку на договор. Это необходимо для того, чтобы в дальнейшем мы могли сопоставить строки из результата запроса с конкретным документом счета.

Шаг 3: Алгоритм создания документа и заполнения табличной части

Теперь, когда у нас есть общая таблица всех услуг по всем договорам, мы переходим к циклу по основной таблице данных (например, строкам вашей обработки), создаем документы и наполняем их. Для эффективного поиска строк в большой таблице услуг используем метод НайтиСтроки() со структурой отбора.

Разберем этот процесс по шагам:

  1. Обходим исходную таблицу данных в цикле.
  2. Проверяем, не был ли документ уже создан ранее.
  3. Создаем новый объект документа.
  4. Заполняем шапку (Организация, Контрагент, Договор).
  5. Ищем в таблице услуг те строки, которые относятся к текущему договору.
  6. Добавляем найденные строки в документ.

Для Каждого СтрокаТЗ Из ДанныеТФ Цикл
    // Пропускаем строки без пометки или те, где счет уже создан
    Если Не СтрокаТЗ.Пометка Или ЗначениеЗаполнено(СтрокаТЗ.ДокументСчета) Тогда
        Продолжить;
    КонецЕсли;

    Попытка
        // Создаем новый документ
        НовыйСчет = Документы.СчетНаОплатуПокупателю.СоздатьДокумент();
        НовыйСчет.Дата = ТекущаяДата();
        НовыйСчет.Организация = Справочники.Организации.НайтиПоКоду("000000001");
        НовыйСчет.Контрагент = СтрокаТЗ.Контрагент;
        НовыйСчет.ДоговорКонтрагента = СтрокаТЗ.Договор;

        // Отбор строк услуг именно для этого договора
        ПараметрыОтбора = Новый Структура;
        ПараметрыОтбора.Вставить("Договор", СтрокаТЗ.Договор);
        НайденныеСтрокиУслуг = ТаблицаУслугИзДоговоров.НайтиСтроки(ПараметрыОтбора);

        // Заполняем табличную часть документа
        Для Каждого СтрокаУслуги Из НайденныеСтрокиУслуг Цикл
            НоваяСтрокаДокумента = НовыйСчет.Услуги.Добавить();
            // Используем метод ЗаполнитьЗначенияСвойств для быстрого копирования реквизитов
            ЗаполнитьЗначенияСвойств(НоваяСтрокаДокумента, СтрокаУслуги);
        КонецЦикла;

        // Записываем документ
        НовыйСчет.Записать(РежимЗаписиДокумента.Запись);
        
        // Сохраняем ссылку на созданный документ обратно в таблицу формы
        СтрокаТЗ.ДокументСчета = НовыйСчет.Ссылка;
        
    Исключение
        Сообщить("Ошибка при создании счета для " + СтрокаТЗ.Контрагент + ": " + ОписаниеОшибки());
    КонецПопытки;
КонецЦикла;

Дополнительные рекомендации и тонкости

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

1. Использование метода ЗаполнитьЗначенияСвойств. Этот метод значительно сокращает код, копируя значения из колонок таблицы запроса в реквизиты табличной части документа с идентичными именами. Помните: чтобы это сработало, имена колонок в запросе (через КАК) должны в точности совпадать с именами реквизитов табличной части документа.

2. Пересчет налогов и итогов. В типовых конфигурациях (БП 3.0, ERP) простого копирования данных часто недостаточно. При программном заполнении строк могут не рассчитаться суммы НДС или итоговые показатели документа. Рекомендуется после заполнения строк вызывать типовые процедуры пересчета. Посмотрим на пример вызова в БП 3.0:


// Пример для Бухгалтерии предприятия 3.0
СтруктураПараметров = Новый Структура("ПересчитатьВсе", Истина);
ОбработкаТабличныхЧастей.ПриИзмененииРеквизитовВТабличнойЧасти(НовыйСчет.Услуги, СтруктураПараметров);

3. Проверка валют. Проанализируем ситуацию, если договор заключен в валюте, а счет выписывается в рублях. При заполнении из табличной части справочника цены могут перенестись «как есть», без учета курса. Если ваша конфигурация предполагает мультивалютность, добавьте проверку совпадения валют или механизм пересчета цен по актуальному курсу.

4. Оптимизация запроса в расширении. Если вы добавили табличную часть в справочник именно в расширении, убедитесь, что в тексте запроса вы обращаетесь к правильному метаданному. В режиме совместимости некоторых версий платформы может потребоваться использование префикса расширения, если имена объектов пересекаются.

5. Безопасная запись. Использование блока Попытка...Исключение — это правильный подход. Однако старайтесь выводить максимально информативные сообщения об ошибках, чтобы пользователь понимал, на каком именно контрагенте или договоре возникла проблема.

Резюме

Для корректного заполнения табличной части документа из справочника мы выяснили следующее: во-первых, необходимо преобразовать данные формы в таблицу значений; во-вторых, следует использовать один запрос к базе данных для получения всех необходимых строк; и в-третьих, применять метод ЗаполнитьЗначенияСвойств для минимизации кода. Такой подход обеспечивает высокую скорость работы и чистоту программного кода.

← На главную