При разработке в среде 1С:Предприятие 8 часто возникает задача: внутри модуля документа (обычно в процедуре ОбработкаПроведения) прочитать записи регистра сведений, которые этот же документ только что создал или которые были в нем ранее. Однако начинающие (а иногда и опытные) разработчики сталкиваются с ситуацией, когда стандартный метод Прочитать() возвращает пустой набор или старые данные. Разберем подробно, почему так происходит и как правильно работать с движениями в транзакции.
Прежде всего, проанализируем ситуацию. Когда выполняется процедура ОбработкаПроведения, вся работа происходит внутри одной неявной транзакции. Это означает, что изменения в базе данных физически будут зафиксированы (commit) только после того, как процедура завершится успешно. Если мы попытаемся использовать объект РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей(), платформа обратится напрямую к таблицам базы данных.
Рассмотрим два сценария настройки свойства документа «Удаление движений»:
Прочитать() для нового набора вернет 0 строк.Прочитать() вернет их, но они не будут соответствовать тем данным, которые вы формируете прямо сейчас в алгоритме проведения.Важный момент: данные, которые вы добавляете в коллекцию Движения объекта документа, находятся в оперативной памяти. Пока вы не вызвали метод Записать() для конкретного набора записей или не завершили транзакцию, внешние объекты (включая новые экземпляры наборов записей и запросы к регистру) эти изменения «не видят».
Самый правильный и быстрый способ работы с «собственными» записями документа — обращение к коллекции Движения текущего объекта. Проанализируем пример кода, который позволит нам получить доступ к данным:
// В процедуре ОбработкаПроведения
НаборДвижений = Движения.АФМ_ЗаявкиУПП;
// Если нам нужно прочитать то, что УЖЕ записано в БД для этого документа:
НаборДвижений.Прочитать();
// Теперь мы можем работать с ТЗ или перебором
ТЗ = НаборДвижений.Выгрузить();
Если ТЗ.Количество() > 0 Тогда
// Обработка данных
КонецЕсли;
Важное примечание: Если документ новый, то метод НаборДвижений.Прочитать() все равно вернет пустой набор, так как ссылки в физической таблице базы данных еще не существует. Данные появятся в памяти только после того, как ваш код их туда добавит.
Если ваша логика требует, чтобы данные регистра были доступны для запроса (например, для контроля остатков или сложных расчетов), необходимо использовать так называемую «новую методику проведения». Суть ее заключается в том, чтобы сначала «сбросить» сформированные движения в базу данных в рамках текущей транзакции.
Рассмотрим алгоритм действий:
Движения.ИмяРегистра нужными записями.Движения.ИмяРегистра.Записывать = Истина;.Движения.ИмяРегистра.Записать();.Пример программного кода:
Движение = Движения.АФМ_ЗаявкиУПП.Добавить();
Движение.Период = Дата;
// Заполнение остальных измерений и ресурсов...
// Принудительно записываем движения в БД внутри транзакции
Движения.АФМ_ЗаявкиУПП.Записать();
// Теперь данные доступны для запроса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ * ИЗ РегистрСведений.АФМ_ЗаявкиУПП ГДЕ Регистратор = &Регистратор";
Запрос.УстановитьПараметр("Регистратор", Ссылка);
Результат = Запрос.Выполнить().Выгрузить();
Иногда программисту необходимо сравнить, что было в регистре ДО перепроведения документа и что стало ПОСЛЕ. Внутри ОбработкаПроведения получить «старые» данные сложно, если включено автоматическое удаление движений.
Выясним причину: при автоматическом удалении платформа очищает движения перед входом в ОбработкаПроведения. Поэтому, чтобы сохранить «историю», проанализируем использование события ПередЗаписью:
ПередЗаписью (в модуле документа) создаем набор записей.ДополнительныеСвойства объекта.ОбработкаПроведения извлекаем эти данные и сравниваем с новыми.Посмотрим на пример реализации:
// В модуле документа
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
Набор = РегистрыСведений.АФМ_ЗаявкиУПП.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(Ссылка);
Набор.Прочитать();
// Сохраняем ТЗ со старыми движениями во временное свойство объекта
ДополнительныеСвойства.Вставить("СтарыеДвижения", Набор.Выгрузить());
КонецЕсли;
КонецПроцедуры
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// Извлекаем сохраненные данные
Если ДополнительныеСвойства.Свойство("СтарыеДвижения") Тогда
ТЗ_Старая = ДополнительныеСвойства.СтарыеДвижения;
// Теперь у нас есть данные, которые были в регистре до нажатия кнопки "Провести"
КонецЕсли;
// Формируем новые движения...
КонецПроцедуры
Проанализируем основные грабли, на которые наступают разработчики при работе с подчиненными регистрами:
Движения.ИмяРегистра вместо РегистрыСведений.ИмяРегистра.СоздатьНаборЗаписей(), если работаете внутри модуля самого документа. Это работает быстрее и безопаснее с точки зрения блокировок.Движения.ИмяРегистра.Записать() для пустого набора, вы удалите все существующие записи в базе по этому регистратору. Именно поэтому у автора вопроса в сообщении 4 «движения исчезали» — он обращался к пустой коллекции Движения и, вероятно, она записывалась платформой.Записать()) платформа накладывает исключительную блокировку на эти записи до конца транзакции. Старайтесь делать это как можно ближе к завершению процедуры проведения, чтобы не тормозить работу других пользователей.ЭтоНовый() или наличие ссылки. В новом документе, который еще не был ни разу записан, физически не может быть записей в регистрах.Таким образом, мы выяснили, что ключ к решению проблемы лежит в понимании того, где в конкретный момент времени находятся данные: в оперативной памяти объекта Движения или в физических таблицах базы данных. Используйте коллекцию Движения для работы с текущими изменениями и ДополнительныеСвойства для передачи информации между событиями объекта.