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

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

В практике разработчика 1С часто возникает задача, когда данные из внешнего источника (например, файла Excel или результата запроса) попадают в Таблицу Значений (ТЗ), и на их основе необходимо сформировать пакет документов. Сложность заключается в том, что в одной таблице могут находиться данные для нескольких разных документов, и их нужно правильно сгруппировать, чтобы каждая строка ТЗ попала в соответствующую табличную часть «нужного» объекта.

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

Способ 1. Последовательный обход отсортированной таблицы

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

Предположим, у нас есть таблица ТЗ_Начисление, где колонка НомерРегистратора определяет принадлежность строки к конкретному документу.


// Сначала обязательно сортируем таблицу по группирующему полю
ТЗ_Начисление.Сортировать("НомерРегистратора");

ТекНомер = Неопределено;
ТекДок   = Неопределено;

Для Каждого Стр Из ТЗ_Начисление Цикл
    
    // Если номер изменился, значит начались данные для нового документа
    Если Стр.НомерРегистратора <> ТекНомер Тогда
        
        // Если это не первая итерация, записываем предыдущий созданный документ
        Если ТекДок <> Неопределено Тогда
            ТекДок.Записать();
        КонецЕсли;
        
        // Создаем новый объект документа
        ТекДок = Документы.НужныйДокумент.СоздатьДокумент();
        ТекДок.Дата = ТекущаяДата();
        // Заполняем основные реквизиты шапки из текущей строки
        ТекДок.Организация = Стр.Организация; 
        
        ТекНомер = Стр.НомерРегистратора;
    КонецЕсли;
    
    // Добавляем строку в табличную часть текущего документа
    НоваяСтрокаТЧ = ТекДок.ВыполненныеРаботы.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаТЧ, Стр);
    
КонецЦикла;

// Не забываем записать самый последний документ после завершения цикла
Если ТекДок <> Неопределено Тогда
    ТекДок.Записать();
КонецЕсли;

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

Способ 2. Использование вспомогательного списка уникальных ключей

Если логика создания документа сложная, удобнее сначала получить список всех уникальных документов, которые нужно создать, а затем для каждого из них выбирать подходящие строки. Проанализируем, как это реализовать с помощью метода Скопировать() и Свернуть().


// 1. Получаем таблицу только с уникальными идентификаторами документов
ТаблицаДокументов = ТЗ_Начисление.Скопировать();
ТаблицаДокументов.Свернуть("НомерРегистратора, Организация"); // Указываем все поля шапки

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

Рассмотрим подробнее: метод НайтиСтроки() работает достаточно быстро, и такой код более читаем, так как четко разделяет создание «шапки» документа и заполнение его «табличной части».

Способ 3. Группировка данных через запрос к ТЗ

Профессиональный подход подразумевает использование механизма запросов даже для временных таблиц. Это позволяет использовать мощный инструмент ИТОГИ, который автоматически сгруппирует данные по документам и их содержимому.

  1. Поместим Таблицу Значений в менеджер временных таблиц.
  2. Выполним запрос с предложением ИТОГИ ПО.
  3. Обойдем результат запроса по группировкам.

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

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

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

Оптимизация и важные нюансы

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

Использование транзакций: Если вам нужно создать более 10–20 документов за раз, обязательно оберните цикл записи в транзакцию. Это значительно ускорит процесс, так как база данных не будет фиксировать каждое изменение на диске по отдельности.


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

Метод ЗаполнитьЗначенияСвойств: Вместо того чтобы писать вручную СтрокаТЧ.Номенклатура = СтрокаТЗ.Номенклатура для каждого поля, используйте функцию ЗаполнитьЗначенияСвойств(Приемник, Источник). Она автоматически сопоставит имена колонок ТЗ и реквизитов ТЧ и перенесет значения.

Проверка на существование: Если вы загружаете данные повторно (например, из обновленного Excel), выясним причину возможного дублирования. Перед вызовом СоздатьДокумент() попробуйте найти уже существующий документ по ключевым полям (номер, дата, контрагент) с помощью метода НайтиПоРеквизиту или запроса. Если документ найден, используйте ПолучитьОбъект() вместо создания нового.

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

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

← На главную