Как реализовать хранение истории изменения статусов документа в регистре сведений

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

В процессе разработки на платформе 1С часто возникает задача не просто изменять текущий статус документа (например, «Заказ клиента»), а сохранять всю хронологию его изменений. Это необходимо для анализа бизнес-процессов: сколько времени заказ находился в производстве, кто и когда перевел его в статус «Доставлен» и так далее. Рассмотрим подробно, как реализовать этот механизм правильно, чтобы записи в истории не перезаписывались, а добавлялись последовательно.

Выбор архитектуры: Регистр сведений

Первым делом проанализируем, какой объект метаданных лучше всего подходит для этой задачи. Нам необходимо хранить набор записей, привязанных ко времени. Оптимальный выбор — Регистр сведений. Разберем по шагам настройки регистра:

  1. Периодичность: Установим значение В пределах секунды. Это позволит нам автоматически использовать поле Период для сортировки и получения «среза последних» данных.
  2. Режим записи: Рекомендуется использовать Независимый. Это даст гибкость: мы сможем добавлять записи программно, не привязываясь жестко к движениям документа, что полезно, если статус меняется не только при проведении.
  3. Измерения:
    • Заказ (тип: ДокументСсылка.Заказ). Обязательно установим свойство Ведущее. Это обеспечит автоматическое удаление записей истории при удалении документа и позволит видеть историю прямо из формы заказа.
  4. Ресурсы:
    • Статус (тип: ПеречислениеСсылка.СтатусыЗаказов). Именно здесь будет храниться само значение статуса.
  5. Реквизиты (опционально):
    • Автор (тип: СправочникСсылка.Пользователи). Полезно для понимания, кто именно внес изменения.

Реализация логики записи при проведении

Теперь разберем программную часть. Основная задача — сделать так, чтобы новая запись в регистр добавлялась только в том случае, если статус действительно изменился. Если пользователь просто перепроводит документ без смены статуса, плодить дублирующие записи в истории не нужно.

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


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

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

Механизм записи данных

Для непосредственной записи новой строки в независимый регистр сведений удобнее всего использовать МенеджерЗаписи. Проанализируем вспомогательную процедуру:


Процедура ЗаписатьСтатусВРегистр()
    
    МенеджерЗаписи = РегистрыСведений.ИсторияСтатусов.СоздатьМенеджерЗаписи();
    
    // Заполняем ключевые поля (Период + Измерения)
    МенеджерЗаписи.Период = ТекущаяДатаСеанса();
    МенеджерЗаписи.Заказ  = Ссылка;
    
    // Заполняем ресурсы
    МенеджерЗаписи.Статус = ЭтотОбъект.Статус;
    
    // Если добавляли реквизит Автор:
    // МенеджерЗаписи.Автор = ПараметрыСеанса.ТекущийПользователь;
    
    Попытка
        // Записываем с параметром Замещать = Ложь, 
        // хотя для периодического регистра с уникальным периодом это не критично
        МенеджерЗаписи.Записать(Истина); 
    Исключение
        // Обработка ошибки, если, например, две записи попали в одну секунду
        Инфо = ИнформацияОбОшибке();
        ЗаписьЖурналаРегистрации("Ошибка записи статуса", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(Инфо));
    КонецПопытки;
    
КонецПроцедуры

Важные нюансы и оптимизация

Рассмотрим несколько критических моментов, которые помогут избежать ошибок в промышленной эксплуатации:

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

2. Проблема "одной секунды":
Поскольку периодичность регистра обычно составляет одну секунду, при программном изменении статуса (например, в цикле обработки) может возникнуть ситуация, когда для одного заказа пытаются создаться две записи с одинаковым периодом. Платформа выдаст ошибку уникальности. Чтобы этого избежать, можно добавить в измерения регистра дополнительный реквизит (например, УникальныйИдентификатор или ЧисловойНомер) или использовать периодичность По позиции регистратора (если регистр подчинен документу).

3. Использование "Истории данных":
Выясним, стоит ли всегда писать свой код. В современных версиях платформы (8.3.11+) существует системный механизм История данных. Если включить его для документа и его реквизита Статус, платформа сама будет протоколировать изменения. Однако создание собственного регистра сведений предпочтительнее, если вам нужно выводить эту историю в отчеты или использовать ее логику в других алгоритмах программы, так как программный доступ к системной истории данных технически сложнее.

4. Свойство "Ведущее":
Не пренебрегайте установкой свойства Ведущее для измерения Заказ. Это не только упрощает навигацию для пользователя, но и гарантирует целостность базы данных. Если администратор удалит "битый" заказ, система автоматически очистит таблицу истории статусов, не оставляя "сиротских" записей в базе.

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

← На главную