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

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

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

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

Решение 1: Проведение документа с последующим откатом транзакции

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

Давайте разберем этот процесс по шагам:

  1. Создание копии документа: Нам потребуется объект документа, который мы хотим провести. Мы можем либо создать новый документ и заполнить его данными, либо скопировать существующий. Работа с копией позволяет нам избежать изменения оригинального документа, если таковой уже существует в базе.
  2. Начало транзакции: Перед выполнением операции записи и проведения мы инициализируем транзакцию. Это гарантирует, что все последующие изменения в базе данных (запись документа, движения по регистрам) будут объединены в одну логическую операцию, которую можно будет либо полностью подтвердить, либо полностью отменить. Для этого мы используем метод НачатьТранзакцию() глобального контекста.
  3. Запись и проведение копии документа: Внутри транзакции мы вызываем метод Записать() для нашей копии документа, указывая режим записи РежимЗаписиДокумента.Проведение. В этом случае система сначала запишет сам объект документа в базу данных, а затем выполнит его проведение, сформировав соответствующие движения по регистрам.
  4. Получение и анализ движений: После успешного проведения документа, его движения становятся доступны через свойство Движения объекта документа. Например, для получения движений по регистру накопления "ОстаткиТоваров", мы можем обратиться к ДокументОбъектКопия.Движения.ОстаткиТоваров. Мы можем перебрать эти движения, проанализировать их, собрать необходимую информацию. В некоторых сценариях для детального исследования структуры полей может быть полезен Универсальный редактор данных (УРД) (поможет универсальный редактор данных (УРД) для 1С), позволяющий наглядно сопоставлять реквизиты и движения.
  5. Откат транзакции: После того как мы получили и обработали все необходимые движения, мы вызываем метод ОтменитьТранзакцию(). Этот метод аннулирует все изменения, которые произошли в рамках текущей транзакции. В результате, ни сам документ, ни его движения не будут сохранены в базе данных permanently. Система "откатит" состояние базы до момента вызова НачатьТранзакцию().
  6. Обработка ошибок: Важно обернуть все операции внутри транзакции в блок Попытка...Исключение...КонецПопытки. Это позволит корректно отловить возможные ошибки при проведении и в любом случае выполнить откат транзакции, чтобы не оставить базу в некорректном состоянии.

Рассмотрим пример кода:


// Предположим, ДокументОбъект - это существующий или созданный документ "Реализация товаров и услуг"
// который мы хотим "провести" без постоянной записи.

// Создаем копии документа в памяти, чтобы не затрагивать оригинал, если он есть
ДокументОбъектКопия = ДокументОбъект.Скопировать();

// Заполняем необходимые реквизиты копии, если они не были заполнены или требуют корректировки
// ДокументОбъектКопия.Дата = ТекущаяДата();
// ДокументОбъектКопия.Организация = Ссылки.Организации.НашаОрганизация;
// ... и так далее, все реквизиты, необходимые для корректного проведения

НачатьТранзакцию();
Попытка
    ДокументОбъектКопия.Записать(РежимЗаписиДокумента.Проведение); // Документ временно записывается и проводится

    // *** Здесь мы получаем и обрабатываем движения ***
    // Например, для регистра накопления "ОстаткиТоваров"
    Для Каждого Движение Из ДокументОбъектКопия.Движения.ОстаткиТоваров Цикл
        Сообщить("Движение по ОстаткамТоваров: " + Движение.Период + ", Номенклатура: " + Движение.Номенклатура + ", Количество: " + Движение.Количество + ", Вид движения: " + Строка(Движение.ВидДвижения));
        // Можем сохранить эти движения в какую-либо временную коллекцию или обработать их
    КонецЦикла;

    // Аналогично для других регистров, например, бухгалтерии или взаиморасчетов
    // Для Каждого Движение Из ДокументОбъектКопия.Движения.Хозрасчетный Цикл
    //     Сообщить("Движение по Хозрасчетному: " + Движение.СчетДт + " - " + Движение.СчетКт + ", Сумма: " + Движение.Сумма);
    // КонецЦикла;

Исключение
    Сообщить("Ошибка при проведении документа: " + ОписаниеОшибки());
    // В случае ошибки, также необходимо откатить транзакцию
    ОтменитьТранзакцию();
    Возврат; // Выходим из процедуры, так как проведение завершилось ошибкой
КонецПопытки;

// После успешного получения движений, откатываем транзакцию.
// Это гарантирует, что ДокументОбъектКопия и его движения не будут сохранены в базе.
ОтменитьТранзакцию();

Сообщить("Движения успешно проанализированы, документ не записан.");

Преимущества этого метода:

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

Недостатки и важные нюансы:

  1. Временная запись в базу: Несмотря на откат транзакции, документ и его движения *временно* записываются в физическую базу данных. В системах с очень высокой нагрузкой это может создавать временную нагрузку на сервер, поэтому стоит изучить вопросы блокировок и минимизации нагрузки при использовании данного метода.
  2. Блокировки: Во время транзакции могут возникать временные блокировки данных, что теоретически может влиять на параллельную работу пользователей, хотя и на очень короткое время.
  3. Использование серверного контекста: Операции с транзакциями, запись документов и работа с движениями должны выполняться в серверном контексте, поскольку они взаимодействуют непосредственно с базой данных.

Решение 2: Эмуляция проведения через прямые вызовы внутренних модулей конфигурации

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

Мы рассмотрим шаги для реализации этого подхода:

  1. Создание объекта документа в памяти: Нам необходимо создать экземпляр объекта документа, который мы хотим "провести", но не записывать его. Используем метод СоздатьДокумент() менеджера документа.
  2. Заполнение реквизитов документа: Это критически важный шаг. Все реквизиты документа, которые влияют на логику проведения, должны быть максимально полно и корректно заполнены. Чтобы не тратить время на ручное написание кода инициализации, можно использовать инструмент генерация кода по ссылке, который сформирует программное создание объекта со всеми данными в один клик.
  3. Поиск функций проведения в конфигурации: Это самая трудоемкая часть.
    • Модуль объекта документа: В первую очередь следует искать процедуру ОбработкаПроведения() в модуле объекта нужного документа. Часто эта процедура делегирует основную логику проведения в общие модули.
    • Общие модули проведения: Для крупных конфигураций, таких как УТ 11, КА 2, ERP, логика проведения обычно выносится в специализированные общие модули. Используя инструкцию по поиску нужных функций через отладчик, вы сможете быстро определить точку входа в логику формирования движений.
    • Отладка: Лучший способ найти нужную функцию — это открыть конфигуратор, найти процедуру ОбработкаПроведения() в модуле объекта документа и поставить в ней точку останова. Значительно упростить этот процесс и сократить время на постоянные перезапуски среды поможет помощник отладки (мини Vanessa) (удобно через помощник отладки кода в режиме Предприятия).
  4. Вызов функции проведения: После того как мы идентифицировали нужную экспортную функцию (или процедуру) в общем модуле, мы вызываем ее, передавая ей в качестве параметров наш заполненный объект документа. Стоит учитывать, что в некоторых сложных случаях может потребоваться создание движения документа по запросу, если стандартные механизмы проведения слишком жестко завязаны на наличие ссылки в БД.

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

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

Рассмотрим пример кода, демонстрирующий этот подход:


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

// 2. Максимально полно и корректно заполняем все реквизиты документа,
//    которые влияют на логику проведения. Это критически важно!
НовыйДокумент.Дата                 = ТекущаяДата();
НовыйДокумент.Организация          = Ссылки.Организации.НашаОрганизация;
НовыйДокумент.Контрагент           = Ссылки.Контрагенты.Покупатель;
НовыйДокумент.ДоговорКонтрагента   = Ссылки.ДоговорыКонтрагентов.ДоговорСПокупателем;
НовыйДокумент.Склад                = Ссылки.Склады.ОсновнойСклад;
НовыйДокумент.Валюта               = Константы.ВалютаРегламентированногоУчета.Получить();
НовыйДокумент.Курс                 = 1;
НовыйДокумент.Комментарий          = "Тестовое проведение без записи";

// Заполняем табличную часть "Товары"
СтрокаТабличнойЧасти = НовыйДокумент.Товары.Добавить();
СтрокаТабличнойЧасти.Номенклатура = Ссылки.Номенклатура.Товар1;
СтрокаТабличнойЧасти.Количество   = 10;
СтрокаТабличнойЧасти.Цена         = 100;
СтрокаТабличнойЧасти.Сумма        = 1000;
СтрокаТабличнойЧасти = НовыйДокумент.Товары.Добавить();
СтрокаТабличнойЧасти.Номенклатура = Ссылки.Номенклатура.Товар2;
СтрокаТабличнойЧасти.Количество   = 5;
СтрокаТабличнойЧасти.Цена         = 50;
СтрокаТабличнойЧасти.Сумма        = 250;

// Устанавливаем режим проведения (важно для некоторых конфигураций)
РежимПроведения = РежимПроведенияДокумента.Оперативный;

// Инициализируем флаг отказа
Отказ = Ложь;

Попытка
    // 4. Вызываем внутреннюю экспортную функцию общего модуля,
    //    ответственную за формирование движений.
    
    // Допустим, мы нашли функцию:
    ОбщийМодульПроведенияДокумента.СформироватьДвиженияРеализации(НовыйДокумент, Отказ, РежимПроведения);

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

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

    Иначе
        Сообщить("Проведение документа завершилось отказом. Причина: " + НовыйДокумент.Комментарий);
    КонецЕсли;

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

// Важно: НовыйДокумент.Движения.Записать() здесь не вызывается

Преимущества этого метода:

  1. Отсутствие записи в базу: Главное преимущество — ни сам документ, ни его движения не попадают в базу данных даже временно. Это идеальное решение для систем, где важна минимальная нагрузка на базу и чистота данных.
  2. Чистота данных: Отсутствие временных записей исключает любые риски "засорения" базы или необходимости очистки после тестовых операций.

Недостатки и важные нюансы:

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

Заключение: Выбор подходящего решения

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

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

← На главную