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

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Разберем, как устроена работа с объектами и ссылками в платформе 1С:Предприятие 7.7, почему возникает ошибка «Не выбран документ!» и как правильно реализовать пакетное проведение документов с использованием коллекций значений.

Разница между объектом и ссылкой на документ в 1С 7.7

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

  1. Объект документа — экземпляр, созданный через функцию СоздатьОбъект("Документ.ИмяДокумента"). Только этот объект представляет собой рабочий буфер в памяти, обладает методами модификации данных, такими как Новый(), Записать(), Провести(), Удалить() и доступом к реквизитам шапки и табличной части для записи.
  2. Ссылка на документ (значение типа «Документ») — идентификатор уже существующей записи в базе данных, возвращаемый методом ТекущийДокумент() или при получении значения из реквизита другого объекта / списка значений. Ссылка доступна только для чтения и не имеет методов Записать() или Провести().

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

Пошаговый алгоритм: сохранение ссылок и последующее проведение

Рассмотрим стандартный и надежный способ решения задачи, когда документы реализации сначала записываются без проведения, их ссылки сохраняются в СписокЗначений, а после проведения поступления выполняется проведение всех реализаций.

  1. В цикле обработки исходных данных создаем каждую реализацию, заполняем реквизиты и вызываем метод Записать(). Документ появляется в информационной базе как непроведенный.
  2. Получаем ссылку на сохраненный документ с помощью метода ТекущийДокумент() и помещаем ее в СписокЗначений.
  3. Формируем, записываем и проводим документ поступления.
  4. Создаем отдельный объект для проведения реализаций, перебираем сохраненный СписокЗначений, позиционируем объект на нужной накладной методом НайтиДокумент() и вызываем Провести().

Посмотрим на пример программного кода:


// 1. Создание и запись реализаций без проведения
СЗСписокРеализаций = СоздатьОбъект("СписокЗначений");
ДокРеализация = СоздатьОбъект("Документ.Реализация");

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

// 2. Создание и проведение документа Поступления
ДокПоступление = СоздатьОбъект("Документ.ПоступлениеТМЦ");
ДокПоступление.Новый();
// ... Заполнение реквизитов поступления по накопленным данным ...
ДокПоступление.Записать();
ДокПоступление.Провести();

// 3. Проведение всех созданных реализаций из Списка Значений
ТекДок = СоздатьОбъект("Документ.Реализация");

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

Альтернативные способы решения задачи

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

Способ 1. Управление временем документов

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

  1. Метод УстановитьВремя(<Часы>, <Минуты>, <Секунды>) позволяет явно указать хронологическую позицию (например, задать для поступления время на одну секунду раньше первой реализации).
  2. Метод АвтоВремяНачалоДня() автоматически позиционирует документ при записи в самое начало дня перед всеми остальными документами.
  3. Метод АвтоВремяПослеТекущего() или АвтоВремяКонецДня() позволяет выстроить цепочку расходных документов после документа поступления.

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

Вместо того чтобы создавать незаписанные или непроведенные документы в базе данных, правильнее буферизовать сами исходные данные в объекте ТаблицаЗначений:

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

Надежность пакетной обработки и транзакции

При создании связанных пакетов «приход — расход» обязательно защищайте операцию транзакцией. Это исключит ситуацию, когда поступление создано, а одна из реализаций не провелась из-за ошибки в данных или нехватки остатка:


НачатьТранзакцию();

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

Использование явной транзакции обеспечивает целостность данных в базе и ускоряет запись большого объема документов за счет снижения числа обращений к дисковой подсистеме.

← На главную