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