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

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

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

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

Оптимизация работы с таблицей формы на сервере

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


&НаСервере
Процедура СоздатьДокументыНаСервере()
    // Получаем таблицу значений напрямую из реквизита формы
    ТаблицаДанных = Объект.Товары.Выгрузить();
    
    // Либо, если это реквизит формы типа ТаблицаЗначений:
    // ТаблицаДанных = ДанныеФормыВЗначение(Элементы.ТаблицаТоваров, Тип("ТаблицаЗначений"));
КонецПроцедуры

Способ 1. Группировка и создание документов с помощью запроса (рекомендуемый)

Рассмотрим наиболее надежный и производительный вариант. Когда в исходной таблице присутствуют строки для разных складов (или контрагентов), наиболее просто и наглядно разделить строки с помощью языка запросов 1С и конструкции ИТОГИ ПО.

Разберем реализацию поэтапно:

  1. Передаем исходную таблицу значений в запрос через временную таблицу.
  2. Группируем результат запроса по реквизиту шапки документа (в нашем примере — Склад).
  3. Выполняем двухуровневый обход: на верхнем уровне создаем документ, на вложенном — заполняем строки табличной части.
  4. Записываем созданный документ в базу данных.

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

Способ 2. Разделение через методы Таблицы Значений

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


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

Важные правила при программном создании документов

Обратим внимание на ключевые особенности платформы 1С:Предприятие 8, которые необходимо учитывать при генерации документов:

  1. Метод создания: в версии 8.х используется метод Документы[ИмяДокумента].СоздатьДокумент() (метод СоздатьОбъект() относился к версии 7.7).
  2. Обязательность сохранения: объект, созданный методом СоздатьДокумент(), находится только в оперативной памяти сервера. Чтобы он появился в базе данных, обязательно нужно вызвать метод Записать() в режиме РежимЗаписиДокумента.Запись или РежимЗаписиДокумента.Проведение.
  3. Транзакционность: при массовом создании документов пакетную операцию рекомендуется оборачивать в транзакцию (НачатьТранзакцию() / ЗафиксироватьТранзакцию()), чтобы в случае ошибки при создании одного из документов не оставалось частично созданных данных.
← На главную