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