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

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

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

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

Понимание проблемы: почему срез последних может быть неактуальным?

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

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

Архитектурное решение: получение данных в момент использования, а не хранение

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

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

Более правильным подходом является не хранение "состава обязательных видов документов" непосредственно в табличной части справочника транспортных средств, а получение этой информации динамически в момент ее необходимости.

  1. Динамическое получение данных: Вместо того чтобы обновлять справочник при каждом изменении регистра сведений, мы рекомендуем получать информацию об обязательных видах документов непосредственно в момент ее использования. Например, при формировании путевого листа или при проверке его заполнения.
  2. Преимущества динамического подхода:
    • Актуальность: Запрос к регистру сведений будет выполняться непосредственно в момент открытия или заполнения путевого листа, всегда предоставляя актуальные данные. Вам не нужно будет беспокоиться о синхронизации справочника при каждом изменении регистра сведений, о порядке проведения документов или о проблемах с "срезом последних".
    • Упрощение логики: Избегаем сложной логики в обработчиках проведения документов, которая должна была бы корректировать справочник.
    • Гибкость: Изменения в логике определения обязательных документов (например, добавление нового типа агрегата или условия) не потребуют перепроведения старых документов и обновления справочников. Достаточно будет скорректировать лишь один запрос.

Как это реализовать?

Вместо того, чтобы в обработке проведения документа, который устанавливает/снимает агрегат, пытаться обновить табличную часть справочника ТС, мы рекомендуем: При формировании нового путевого листа или при его проверке, выполнить запрос, который:

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

Примерная логика запроса (псевдокод) при формировании путевого листа:


// Предполагается, что ДатаПутевогоЛиста - это дата, на которую формируется путевой лист
// СсылкаНаТС - это текущее транспортное средство
Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    АгрегатыТС.Агрегат.ВидАгрегата КАК ВидАгрегата
|ИЗ
|    РегистрСведений.АгрегатыТранспортныхСредств.СрезПоследних(&ДатаПутевогоЛиста,
|        ТранспортноеСредство = &СсылкаНаТС) КАК АгрегатыТС";

Запрос.УстановитьПараметр("ДатаПутевогоЛиста", ДатаПутевогоЛиста);
Запрос.УстановитьПараметр("СсылкаНаТС", СсылкаНаТС);

РезультатЗапроса = Запрос.Выполнить().Выгрузить();

// Далее на основании ВидАгрегата и других условий
// определяем список обязательных документов
СписокОбязательныхДокументов = Новый СписокЗначений;
Для Каждого СтрокаАгрегата Из РезультатЗапроса Цикл
    // ... логика определения обязательных документов по виду агрегата ...
    // Например, если ВидАгрегата = Справочники.ВидыАгрегатов.Манипулятор,
    // то добавить 'РазрешениеНаРаботуМанипулятора'
    // ...
КонецЦикла;

// Затем сравниваем СписокОбязательныхДокументов с документами, 
// которые уже есть в путевом листе.

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

Техническое решение 1: Получение среза последних с учетом текущих движений документа

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

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

Примерный код в обработчике проведения:


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

    // 2. Опционально: Блокируем регистр, если необходимо (для исключения конфликтов)
    // БлокировкаДанных = Новый БлокировкаДанных;
    // ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.АгрегатыТранспортныхСредств");
    // ЭлементБлокировки.УстановитьИспользование("ТранспортноеСредство", ЭтотОбъект.ТранспортноеСредство);
    // ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
    // БлокировкаДанных.Заблокировать();

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

    // 4. Теперь получаем срез последних, он будет учитывать только что записанные движения
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    АгрегатыТС.Агрегат.ВидАгрегата КАК ВидАгрегата
    |ИЗ
    |    РегистрСведений.АгрегатыТранспортныхСредств.СрезПоследних(&ДатаДокумента,
    |        ТранспортноеСредство = &СсылкаНаТС) КАК АгрегатыТС";

    Запрос.УстановитьПараметр("ДатаДокумента", ЭтотОбъект.Дата);
    Запрос.УстановитьПараметр("СсылкаНаТС", ЭтотОбъект.ТранспортноеСредство);

    РезультатСреза = Запрос.Выполнить().Выгрузить();

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

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

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

Техническое решение 2: Условное обновление при наличии последовательности документов

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

  1. Проверка наличия последующих документов: В обработчике проведения (или отмены проведения) можно выполнить запрос, чтобы определить, существуют ли другие проведенные документы, которые влияют на тот же объект (например, то же транспортное средство) и имеют более позднюю дату/время, чем текущий документ.
  2. Логика условного обновления:
    • Если последующих документов нет, то смело обновляем данные в справочнике, используя актуальный срез последних (возможно, с применением метода Движения.Записать(), как описано выше).
    • Если последующие документы есть, это может означать, что текущий документ не является "последним" в цепочке событий для данного объекта. В этом случае, возможно, стоит воздержаться от обновления справочника, чтобы не нарушить целостность данных, установленных более поздними документами.

Пример (псевдокод) для ОбработкаПроведения:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // ... (формирование движений и их принудительная запись, как в Техническом решении 1) ...

    // Проверяем наличие более поздних проведенных документов
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |    ДокументСсылка.ТипДокумента.Ссылка
    |ИЗ
    |    Документ.ТипДокумента КАК ДокументСсылка
    |ГДЕ
    |    ДокументСсылка.Проведен = ИСТИНА
    |    И ДокументСсылка.Дата > &ДатаДокумента
    |    И ДокументСсылка.ТранспортноеСредство = &СсылкаНаТС"; // Условие по ТС или другим измерениям

    Запрос.УстановитьПараметр("ДатаДокумента", ЭтотОбъект.Дата);
    Запрос.УстановитьПараметр("СсылкаНаТС", ЭтотОбъект.ТранспортноеСредство);

    ЕстьПоследующиеДокументы = Не Запрос.Выполнить().Пустой();

    Если НЕ ЕстьПоследующиеДокументы Тогда
        // Только если текущий документ "последний" в цепочке для ТС,
        // тогда обновляем справочник.
        // ... Логика обновления справочника, использующая актуальный срез последних ...
    Иначе
        // Есть более поздние документы, справочник не трогаем,
        // чтобы не перетереть актуальные данные, установленные ими.
        // Может потребоваться какое-то сообщение или запись в журнал регистрации.
    КонецЕсли;

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

Этот подход также имеет свои ограничения: он усложняет логику отмены проведения, а также не решает проблему, если документы могут проводиться и распроводиться "не по порядку" (backdating). В таких случаях, именно архитектурное решение (получение данных по требованию) оказывается наиболее надежным.

Менее оптимальное решение: Регламентное задание

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

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

Заключение

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

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

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

← На главную