Как прочитать записи регистра сведений, подчиненного регистратору, в процессе проведения документа

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

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

Причина проблемы: Транзакции и видимость данных

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

Рассмотрим два сценария настройки свойства документа «Удаление движений»:

  1. Удалять автоматически: Платформа при начале проведения физически очищает старые записи в БД. Метод Прочитать() для нового набора вернет 0 строк.
  2. Не удалять автоматически: В базе данных все еще лежат «старые» записи. Метод Прочитать() вернет их, но они не будут соответствовать тем данным, которые вы формируете прямо сейчас в алгоритме проведения.

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

Способ 1. Использование коллекции Движения

Самый правильный и быстрый способ работы с «собственными» записями документа — обращение к коллекции Движения текущего объекта. Проанализируем пример кода, который позволит нам получить доступ к данным:


// В процедуре ОбработкаПроведения
НаборДвижений = Движения.АФМ_ЗаявкиУПП;

// Если нам нужно прочитать то, что УЖЕ записано в БД для этого документа:
НаборДвижений.Прочитать(); 

// Теперь мы можем работать с ТЗ или перебором
ТЗ = НаборДвижений.Выгрузить();
Если ТЗ.Количество() > 0 Тогда
    // Обработка данных
КонецЕсли;

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

Способ 2. Явная запись в транзакции (Новая методика)

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

Рассмотрим алгоритм действий:

  1. Заполняем коллекцию Движения.ИмяРегистра нужными записями.
  2. Устанавливаем свойство Движения.ИмяРегистра.Записывать = Истина;.
  3. Явно вызываем Движения.ИмяРегистра.Записать();.
  4. После этого любой Запрос внутри этой же обработки проведения увидит эти данные, так как они попадут во временные таблицы транзакции БД.

Пример программного кода:


Движение = Движения.АФМ_ЗаявкиУПП.Добавить();
Движение.Период = Дата;
// Заполнение остальных измерений и ресурсов...

// Принудительно записываем движения в БД внутри транзакции
Движения.АФМ_ЗаявкиУПП.Записать();

// Теперь данные доступны для запроса
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ * ИЗ РегистрСведений.АФМ_ЗаявкиУПП ГДЕ Регистратор = &Регистратор";
Запрос.УстановитьПараметр("Регистратор", Ссылка);
Результат = Запрос.Выполнить().Выгрузить();

Способ 3. Работа с «прошлыми» данными в событии ПередЗаписью

Иногда программисту необходимо сравнить, что было в регистре ДО перепроведения документа и что стало ПОСЛЕ. Внутри ОбработкаПроведения получить «старые» данные сложно, если включено автоматическое удаление движений.

Выясним причину: при автоматическом удалении платформа очищает движения перед входом в ОбработкаПроведения. Поэтому, чтобы сохранить «историю», проанализируем использование события ПередЗаписью:

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

Посмотрим на пример реализации:


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

Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // Извлекаем сохраненные данные
    Если ДополнительныеСвойства.Свойство("СтарыеДвижения") Тогда
        ТЗ_Старая = ДополнительныеСвойства.СтарыеДвижения;
        // Теперь у нас есть данные, которые были в регистре до нажатия кнопки "Провести"
    КонецЕсли;
    
    // Формируем новые движения...
КонецПроцедуры

Типичные ошибки и советы

Проанализируем основные грабли, на которые наступают разработчики при работе с подчиненными регистрами:

Таким образом, мы выяснили, что ключ к решению проблемы лежит в понимании того, где в конкретный момент времени находятся данные: в оперативной памяти объекта Движения или в физических таблицах базы данных. Используйте коллекцию Движения для работы с текущими изменениями и ДополнительныеСвойства для передачи информации между событиями объекта.

← На главную