Как зафиксировать дату первого проведения документа в 1С?

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

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

Способ 1. Использование типового механизма версионирования объектов

Если ваша конфигурация построена на базе БСП (Библиотека стандартных подсистем), то в ней, скорее всего, уже включен механизм версионирования. Это самый «чистый» способ с точки зрения архитектуры, так как он не требует внесения изменений в структуру метаданных документа. Разберем, как это работает.

Система при каждом сохранении или проведении документа (в зависимости от настроек) создает запись в регистре сведений ВерсииОбъектов. Чтобы найти дату первого проведения, нам достаточно выполнить запрос к этому регистру. Проанализируем пример логики:

  1. Убедимся, что для нужного типа документа включено версионирование в режиме «При проведении».
  2. При необходимости получения даты мы обращаемся к регистру и ищем самую раннюю версию, где флаг Проведен равен Истина.

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

Способ 2. Добавление реквизита с проверкой в событии ПередЗаписью

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


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

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

КонецПроцедуры

Разберем, почему мы используем именно ПередЗаписью, а не ОбработкаПроведения. В обработке проведения документ уже считается почти записанным, и попытка изменить его реквизиты потребует вызова метода Записать(), что порождает повторный цикл событий и вложенную транзакцию. В событии ПередЗаписью мы просто меняем значение реквизита «в памяти» объекта, и оно записывается в базу вместе со всеми остальными данными в рамках одной транзакции.

Способ 3. Использование дополнительных реквизитов и сведений (БСП)

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

Дополнительные сведения хранятся в отдельном регистре ДополнительныеСведения. Мы можем записать туда дату в момент проведения, используя программный интерфейс БСП. Это позволяет избежать изменения структуры документа, но технически это все равно запись в отдельный регистр сведений, что автор темы изначально хотел минимизировать. Однако это решение гораздо более «типовое», чем создание собственного регистра.

Способ 4. Фиксация даты по изменению конкретного статуса

Часто задача звучит иначе: нужно зафиксировать дату, когда документ впервые стал «К отгрузке». Здесь проверка на Ссылка.Проведен не поможет. Выясним причину: документ может перепроводиться многократно, меняя статусы. Нам нужно отловить именно момент перехода. Посмотрим на пример кода в ПередЗаписью:


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

В этом примере мы используем функцию ЗначениеРеквизитаОбъекта, чтобы не считывать весь объект из базы, что экономит ресурсы. Мы фиксируем дату только в том случае, если новый статус — «К отгрузке», а старый был другим, и дата еще не была заполнена.

Анализ опасных решений: почему нельзя вызывать Записать() в ОбработкеПроведения

Проанализируем ситуацию, предложенную в сообщении 14 форума. Там предлагается внутри ОбработкаПроведения устанавливать значение реквизита и вызывать Записать() с установкой ОбменДанными.Загрузка = Истина. Это крайне вредный совет.

Рассмотрим причины, по которым так делать не стоит:

Способ 5. Использование Журнала регистрации

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

  1. Программно читаем записи журнала регистрации с фильтром по конкретной ссылке на документ.
  2. Ищем самое раннее событие с типом «Документ.Проведение».
  3. Дата этой записи и будет датой первого проведения.

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

Итоги и рекомендации

Мы рассмотрели различные подходы к фиксации даты первого проведения. Подведем итог:

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

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

← На главную