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