При работе с системой 1С:Предприятие, особенно в процессе проведения документов, часто возникает задача получить актуальные данные из регистра сведений. В частности, речь идет о так называемом "срезе последних" — наборе последних записей регистра на определенный момент времени по заданным измерениям. Однако, когда мы пытаемся получить такой срез непосредственно в обработчиках проведения документа, например, в ОбработкаПроведения или ОбработкаУдаленияПроведения, мы сталкиваемся с нюансом: запрос СрезПоследних по умолчанию может не включать движения, сформированные текущим документом.
Давайте выясним, почему так происходит, и как мы можем корректно решить эту задачу, учитывая различные подходы и архитектурные рекомендации.
Виртуальная таблица СрезПоследних регистра сведений предназначена для быстрого получения последней актуальной записи по заданным измерениям на определенную дату. Однако, при вызове этой таблицы в обработке проведения документа, она обращается к уже зафиксированным данным в базе. Движения текущего документа еще не записаны в базу данных на момент выполнения этих обработчиков. Система 1С записывает движения документа в регистры только после успешного завершения всех обработчиков проведения (и подписок на события) и подтверждения транзакции. Соответственно, если движения документа, изменяющие регистр сведений, еще не были записаны, то СрезПоследних их не учтет.
Типичная ситуация, как описал автор темы: при проведении нового документа, запрос к срезу последних в процедурах подписок ОбработкаПроведения или ОбработкаУдаленияПроведения возвращает данные, актуальные на момент до движений текущего документа, то есть данные предыдущего документа.
Прежде чем приступать к техническим способам получения среза последних с учетом текущих движений, давайте проанализируем архитектуру хранения данных. Практика изменения табличной части справочника на основании данных регистра сведений во время проведения документа часто считается неоптимальной и может привести к сложностям в поддержке, дублированию данных и потенциальным ошибкам при некорректном порядке проведения или отмены проведения документов.
Справочники предназначены для хранения нормативно-справочной информации, а регистры сведений — для хранения информации, меняющейся во времени в определенных разрезах. Если у вас есть справочник транспортных средств, в котором хранится табличная часть обязательных видов документов, зависящих от установленных на ТС агрегатов (которые, в свою очередь, учитываются в регистре сведений), то это является примером дублирования данных.
Более правильным подходом является не хранение "состава обязательных видов документов" непосредственно в табличной части справочника транспортных средств, а получение этой информации динамически в момент ее необходимости.
Как это реализовать?
Вместо того, чтобы в обработке проведения документа, который устанавливает/снимает агрегат, пытаться обновить табличную часть справочника ТС, мы рекомендуем: При формировании нового путевого листа или при его проверке, выполнить запрос, который:
Примерная логика запроса (псевдокод) при формировании путевого листа:
// Предполагается, что ДатаПутевогоЛиста - это дата, на которую формируется путевой лист
// СсылкаНаТС - это текущее транспортное средство
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АгрегатыТС.Агрегат.ВидАгрегата КАК ВидАгрегата
|ИЗ
| РегистрСведений.АгрегатыТранспортныхСредств.СрезПоследних(&ДатаПутевогоЛиста,
| ТранспортноеСредство = &СсылкаНаТС) КАК АгрегатыТС";
Запрос.УстановитьПараметр("ДатаПутевогоЛиста", ДатаПутевогоЛиста);
Запрос.УстановитьПараметр("СсылкаНаТС", СсылкаНаТС);
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
// Далее на основании ВидАгрегата и других условий
// определяем список обязательных документов
СписокОбязательныхДокументов = Новый СписокЗначений;
Для Каждого СтрокаАгрегата Из РезультатЗапроса Цикл
// ... логика определения обязательных документов по виду агрегата ...
// Например, если ВидАгрегата = Справочники.ВидыАгрегатов.Манипулятор,
// то добавить 'РазрешениеНаРаботуМанипулятора'
// ...
КонецЦикла;
// Затем сравниваем СписокОбязательныхДокументов с документами,
// которые уже есть в путевом листе.
Этот подход гарантирует, что проверка всегда будет проводиться по самым актуальным данным, независимо от того, как и когда проводились документы, изменяющие регистр сведений.
Если по какой-то причине вы не можете полностью перестроить архитектуру и вам действительно необходимо получить актуальный срез последних данных (включающий движения текущего документа) непосредственно в обработчике проведения, есть способ это сделать. Он основан на том, что движения документа формируются в памяти, и мы можем принудительно записать их в базу данных до запроса среза.
ОбработкаПроведения) перед тем, как выполнять запрос к регистру сведений, вам необходимо принудительно записать движения текущего документа. Для этого используется метод Движения.Записать().Движения.Записать() для регистра сведений, который нас интересует. Это действие сохранит сформированные движения в базу данных, но при этом будет частью текущей транзакции проведения документа. Если проведение документа в целом завершится откатом, то и эти принудительно записанные движения будут отменены.СрезПоследних вернет актуальные данные, включая эти новые записи.Отказ = Истина, что приведет к отмене всей транзакции проведения документа, включая принудительно записанные движения.Примерный код в обработчике проведения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Формируем движения документа
// (логика создания записей в РегистрСведений.АгрегатыТранспортныхСредств)
// Например:
// Движения.АгрегатыТранспортныхСредств.Очистить();
// НоваяЗапись = Движения.АгрегатыТранспортныхСредств.Добавить();
// НоваяЗапись.Период = Дата;
// НоваяЗапись.ТранспортноеСредство = ЭтотОбъект.ТранспортноеСредство;
// НоваяЗапись.Агрегат = ЭтотОбъект.Агрегат;
// ... и так далее для всех нужных движений
// 2. Опционально: Блокируем регистр, если необходимо (для исключения конфликтов)
// БлокировкаДанных = Новый БлокировкаДанных;
// ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.АгрегатыТранспортныхСредств");
// ЭлементБлокировки.УстановитьИспользование("ТранспортноеСредство", ЭтотОбъект.ТранспортноеСредство);
// ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
// БлокировкаДанных.Заблокировать();
// 3. Принудительно записываем движения регистра сведений
Попытка
Движения.АгрегатыТранспортныхСредств.Записать();
Исключение
// Обработка ошибок, если запись не удалась по каким-либо причинам
Сообщить("Ошибка при принудительной записи движений: " + ОписаниеОшибки(), СтатусСообщения.Важное);
Отказ = Истина;
Возврат;
КонецПопытки;
// 4. Теперь получаем срез последних, он будет учитывать только что записанные движения
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| АгрегатыТС.Агрегат.ВидАгрегата КАК ВидАгрегата
|ИЗ
| РегистрСведений.АгрегатыТранспортныхСредств.СрезПоследних(&ДатаДокумента,
| ТранспортноеСредство = &СсылкаНаТС) КАК АгрегатыТС";
Запрос.УстановитьПараметр("ДатаДокумента", ЭтотОбъект.Дата);
Запрос.УстановитьПараметр("СсылкаНаТС", ЭтотОбъект.ТранспортноеСредство);
РезультатСреза = Запрос.Выполнить().Выгрузить();
// 5. Делаем что надо со справочником (если действительно необходимо)
// Например, обновляем табличную часть Справочники.ТранспортныеСредства
// Если Справочник.ТранспортныеСредства.ОбновитьОбязательныеДокументы(ЭтотОбъект.ТранспортноеСредство, РезультатСреза, Отказ) Тогда
// // Успешно
// Иначе
// // Ошибка при обновлении справочника
// Сообщить("Не удалось обновить справочник транспортных средств.", СтатусСообщения.Важное);
// Отказ = Истина;
// КонецЕсли;
КонецПроцедуры
Важно: Этот подход следует использовать с осторожностью. Принудительная запись движений внутри транзакции проведения может влиять на производительность и усложнять отладку, особенно если логика становится сложной. Предпочтительно, если возможно, избегать непосредственного изменения справочников в обработке проведения, следуя архитектурным рекомендациям.
Один из предложенных вариантов на форуме — это проверять, есть ли проведенные документы после текущего. Если такие документы существуют, то не трогать справочник. Этот подход может быть полезен в ситуациях, когда последовательность проведения документов важна, и более поздний документ уже мог установить актуальное состояние или мог быть связан с другим актуальным состоянием регистра сведений.
Движения.Записать(), как описано выше).Пример (псевдокод) для ОбработкаПроведения:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// ... (формирование движений и их принудительная запись, как в Техническом решении 1) ...
// Проверяем наличие более поздних проведенных документов
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ДокументСсылка.ТипДокумента.Ссылка
|ИЗ
| Документ.ТипДокумента КАК ДокументСсылка
|ГДЕ
| ДокументСсылка.Проведен = ИСТИНА
| И ДокументСсылка.Дата > &ДатаДокумента
| И ДокументСсылка.ТранспортноеСредство = &СсылкаНаТС"; // Условие по ТС или другим измерениям
Запрос.УстановитьПараметр("ДатаДокумента", ЭтотОбъект.Дата);
Запрос.УстановитьПараметр("СсылкаНаТС", ЭтотОбъект.ТранспортноеСредство);
ЕстьПоследующиеДокументы = Не Запрос.Выполнить().Пустой();
Если НЕ ЕстьПоследующиеДокументы Тогда
// Только если текущий документ "последний" в цепочке для ТС,
// тогда обновляем справочник.
// ... Логика обновления справочника, использующая актуальный срез последних ...
Иначе
// Есть более поздние документы, справочник не трогаем,
// чтобы не перетереть актуальные данные, установленные ими.
// Может потребоваться какое-то сообщение или запись в журнал регистрации.
КонецЕсли;
КонецПроцедуры
Этот подход также имеет свои ограничения: он усложняет логику отмены проведения, а также не решает проблему, если документы могут проводиться и распроводиться "не по порядку" (backdating). В таких случаях, именно архитектурное решение (получение данных по требованию) оказывается наиболее надежным.
Как вариант, можно использовать регламентное задание, которое будет периодически анализировать изменения в регистре сведений и обновлять соответствующие данные в справочнике. Это может быть применимо для ситуаций, где не требуется немедленная актуализация данных, а допустима некоторая задержка.
В процессе работы с данными в 1С:Предприятии, особенно при взаимодействии регистров сведений и справочников, важно соблюдать принципы архитектурной чистоты и избегать дублирования информации. Наиболее предпочтительным и надежным способом решения задачи получения актуальной информации, зависящей от среза последних регистра сведений, является динамическое получение этой информации в момент ее использования, а не попытки постоянно синхронизировать ее с табличной частью справочника.
Если же архитектурное изменение невозможно или неприемлемо, и вам требуется получить срез последних данных, включающий движения текущего документа, то принудительная запись движений с помощью Движения.Записать() в обработчике проведения является рабочим техническим решением, которое обеспечивает актуальность данных в рамках текущей транзакции.
Всегда анализируйте требования к актуальности и производительности, прежде чем выбирать тот или иной подход. Помните, что чем меньше "магических" синхронизаций и дублирования данных, тем надежнее и проще в поддержке будет ваша система.