Как исключить или отфильтровать определенные записи из набора записей регистра перед записью в 1С?

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

При разработке конфигураций на платформе 1С:Предприятие часто возникает задача отсечения определенных движений или записей регистров «на лету». Классический сценарий: документ проводит данные по регистру накопления или сведений, но при определенных условиях некоторые строки не должны физически попадать в базу данных. Логичным решением кажется модификация набора записей непосредственно в модуле самого регистра, чтобы не дублировать логику фильтрации в десятках различных документов-регистраторов.

В этой статье мы подробно разберем, как правильно реализовать фильтрацию набора записей в обработчике ПередЗаписью модуля регистра, рассмотрим важные платформенные особенности двухэтапной записи и разберем альтернативные, более чистые архитектурные подходы.

Решение задачи: Фильтрация набора через вспомогательный массив

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

Рассмотрим пример реализации этой логики в модуле набора записей регистра:


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

Функция НеобходимостьЗаписиИсключена(Запись) должна содержать вашу прикладную логику проверки полей записи (измерений, ресурсов или реквизитов).

Важная особенность: Двухэтапная запись зависимого регистра

Проанализируем критически важный нюанс платформы 1С, о котором часто забывают разработчики. При проведении или перепроведении документов запись движений по зависимым регистрам происходит в два этапа:

  1. Очистка старых движений: Платформа записывает пустой набор записей по данному регистратору с включенным флагом Замещение = Истина. В этот момент обработчик ПередЗаписью вызывается в первый раз, при этом коллекция ЭтотОбъект пуста (ЭтотОбъект.Количество() = 0).
  2. Запись новых движений: Платформа формирует и записывает новый заполненный набор движений. Обработчик ПередЗаписью вызывается во второй раз, и коллекция уже содержит новые строки.

Наш код корректно обрабатывает оба этапа: на первом этапе цикл по элементам просто не выполнится, а на втором — успешно отфильтрует новые записи перед их отправкой в СУБД.

Специфика регистров накопления: Свойство Записывать

Если вы работаете с регистром накопления, платформа может оптимизировать процесс записи и проигнорировать изменения, внесенные вами в набор программно в обработчике ПередЗаписью. Чтобы гарантировать, что модифицированный набор дойдет до базы данных, обязательно устанавливайте свойство набора записей: ЭтотОбъект.Записывать = Истина;.

Антипаттерн «Обнуление ресурсов»

Иногда разработчики вместо удаления строк решают пойти по «простому» пути — записывать нулевые значения в ресурсы регистра (например, обнулять количество и сумму). Ни в коем случае не делайте так!

Рассмотрим причины, почему это вредно:

Физическое удаление строк из набора (как показано в примере выше) является единственно верным техническим решением.

Архитектурные риски фильтрации внутри модуля регистра

Несмотря на то, что технически задача фильтрации в обработчике ПередЗаписью решается просто, данный подход таит в себе серьезные архитектурные угрозы:

  1. Рассинхронизация данных: В коде проведения документа коллекция Движения.ИмяРегистра будет содержать исходный (полный) набор записей. Если далее в транзакции проведения выполняются проверки (например, контроль остатков), система будет анализировать неверные «грязные» данные из памяти, хотя в БД запишется урезанный набор. Это прямой путь к логическим ошибкам учета.
  2. Скрытая логика (сложность отладки): Другой разработчик, изучая модуль проведения документа, будет уверен, что движения записываются в полном объеме. Ему придется потратить немало времени на отладку, чтобы обнаружить неявное удаление строк внутри модуля регистра.
  3. Падение производительности: Код фильтрации в модуле регистра будет запускаться абсолютно при любой записи (через обмены, групповые обработки, ручные корректировки), создавая дополнительную нагрузку на сервер.

Рекомендуемые альтернативные подходы

Если перед вами стоит задача централизованной фильтрации движений без жесткой привязки к коду конкретных документов, воспользуйтесь более чистыми архитектурными паттернами:

Вариант 1. Использование подписки на событие документа

Создайте общую подписку на событие ОбработкаПроведения для группы документов. В обработчике подписки вы сможете централизованно анализировать движения документов перед их записью в базу. При таком подходе данные в памяти транзакции останутся согласованными.

Вариант 2. Передача контекста через ДополнительныеСвойства

Если фильтрация должна срабатывать выборочно, передавайте управляющий флаг из документа в набор записей через коллекцию дополнительных свойств:


// В модуле проведения документа:
Движения.ИмяРегистра.ДополнительныеСвойства.Вставить("ВыполнитьФильтрациюСтрок", Истина);

А в обработчике ПередЗаписью набора регистра проверяйте этот флаг:


Если ЭтотОбъект.ДополнительныеСвойства.Свойство("ВыполнитьФильтрациюСтрок") Тогда
    // Выполняем удаление ненужных строк...
КонецЕсли;

Используя эти правила и подходы, мы сможем создавать гибкие, производительные и легко поддерживаемые механизмы управления движениями в системе 1С:Предприятие.

← На главную