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