В практике разработки на платформе 1С:Предприятие 8 часто возникает задача управления движениями документа "на лету". Обычная очистка всех движений перед проведением — операция стандартная, но иногда логика требует более тонкого подхода: например, сохранить часть записей, которые уже были выставлены или защищены от изменений, и пересчитать только "оперативные" данные. Рассмотрим, как правильно реализовать частичную очистку движений регистра накопления, избежав при этом типичных ошибок начинающих разработчиков.
Прежде чем приступать к написанию кода в процедуре ОбработкаПроведения, нам необходимо проанализировать настройки самого объекта конфигурации. Поведение системы при проведении документа во многом определяется свойством «Удаление движений» (вкладка «Логические»).
Разберем два варианта настройки:
ОбработкаПроведения, так как на момент выполнения кода записей в базе уже нет.Важно: Выбирая режим «Не удалять автоматически», мы берем на себя полную ответственность за актуальность движений. Если мы забудем очистить ненужные записи, в системе могут возникнуть «фантомные» остатки.
Наиболее производительным и безопасным способом частичной очистки является работа через промежуточную таблицу значений. Рассмотрим этот алгоритм по шагам:
1. Прочитаем существующий набор записей регистра для данного документа.
2. Выгрузим его в таблицу значений.
3. Удалим из таблицы те строки, которые подлежат обновлению (по нашему условию).
4. Загрузим оставшиеся строки обратно в коллекцию движений.
Посмотрим на пример кода, реализующего этот подход:
// В процедуре ОбработкаПроведения
// 1. Читаем текущие движения из базы данных
Движения.МойРегистрНакопления.Прочитать();
// 2. Выгружаем движения в таблицу значений
ТаблицаДвижений = Движения.МойРегистрНакопления.Выгрузить();
// 3. Отбираем строки, которые нужно ОСТАВИТЬ (например, ФлагВыставлен = Истина)
МассивСтрокДляУдаления = Новый Массив;
Для Каждого СтрокаДвижения Из ТаблицаДвижений Цикл
Если СтрокаДвижения.ФлагВыставлен = Ложь Тогда
МассивСтрокДляУдаления.Добавить(СтрокаДвижения);
КонецЕсли;
КонецЦикла;
// Удаляем ненужные строки из ТЗ
Для Каждого СтрокаДляУдаления Из МассивСтрокДляУдаления Цикл
ТаблицаДвижений.Удалить(СтрокаДляУдаления);
КонецЦикла;
// 4. Очищаем коллекцию в памяти и загружаем отфильтрованные данные
Движения.МойРегистрНакопления.Очистить();
Движения.МойРегистрНакопления.Загрузить(ТаблицаДвижений);
// 5. Устанавливаем флаг записи, чтобы платформа сохранила результат
Движения.МойРегистрНакопления.Записывать = Истина;
Этот метод хорош тем, что мы минимизируем количество транзакций и обращений к базе данных, работая с данными в оперативной памяти.
Проанализируем ситуацию, когда разработчик пытается удалять записи напрямую из набора движений, используя простой обход Для Каждого. Рассмотрим пример, который нельзя использовать в «чистом» виде:
// ОШИБОЧНЫЙ ПРИМЕР
Для Каждого Запись Из Движения.МойРегистрНакопления Цикл
Если Запись.ФлагВыставлен = Ложь Тогда
Движения.МойРегистрНакопления.Удалить(Запись); // ОШИБКА: индексы сместятся!
КонецЕсли;
КонецЦикла;
Выясним причину ошибки: когда вы удаляете элемент из коллекции во время обхода, индексы всех последующих элементов уменьшаются на единицу. В результате цикл перескакивает через одну запись, и часть строк, соответствующих условию, останется неудаленной.
Как сделать правильно, если вы всё же хотите использовать цикл? Нужно выполнять обход с конца в начало. Разберем этот пример:
КоличествоЗаписей = Движения.МойРегистрНакопления.Количество();
// Обходим коллекцию с последнего индекса до нуля
Для Счетчик = 1 По КоличествоЗаписей Цикл
Индекс = КоличествоЗаписей - Счетчик;
Запись = Движения.МойРегистрНакопления[Индекс];
Если Запись.ФлагВыставлен = Ложь Тогда
Движения.МойРегистрНакопления.Удалить(Индекс);
КонецЕсли;
КонецЦикла;
Такой подход гарантирует, что смещение индексов не повлияет на еще не обработанные элементы.
При работе в процедуре ОбработкаПроведения важно помнить, что коллекция Движения является частью транзакции проведения. Если вы явно вызываете метод Записать() внутри кода, как показано в некоторых советах, это может привести к избыточной нагрузке.
Рассмотрим правила эффективной записи:
Записать() вручную без крайней необходимости. Платформа сама запишет все наборы, у которых свойство Записывать установлено в Истина, в самом конце процедуры проведения.ГДЕ ФлагВыставлен = Ложь.Если задача стоит в «заморозке» определенных данных, стоит подумать, является ли регистр накопления лучшим местом для этого. Иногда эффективнее разделить данные на два регистра:
ФлагВыставлен принимает значение Истина.Это полностью исключает риск случайного удаления важных данных при технических сбоях или ошибках в коде перепроведения.
Для реализации очистки движений с условием в 1С 8.3:
Записывать = Истина, предоставляя платформе возможность зафиксировать транзакцию один раз в конце проведения.