Как удалить конкретную строку из регистра накопления в 1С?

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

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

Рассмотрим подробно, как это сделать программно, какие методы для этого существуют, и обсудим важные архитектурные нюансы, чтобы избежать ошибок в учете.

Суть проблемы: почему нельзя просто удалить?

Регистры накопления (как и бухгалтерии) подчинены регистратору (документу). Это означает, что записи хранятся "наборами". Стандартное поведение платформы при записи движений — это полное замещение. Метод Движения.Очистить(), упомянутый в начале обсуждения, действительно удаляет все записи по данному документу.

Чтобы удалить одну конкретную запись, нам необходимо реализовать алгоритм "Чтение — Модификация — Запись". Разберем этот процесс по шагам.

Алгоритм точечного удаления записи

Для работы с записями регистра без полного пересчета движений документа используется объект НаборЗаписей. Логика действий следующая:

  1. Создать объект НаборЗаписей для нужного регистра.
  2. Установить отбор по регистратору (ссылке на документ).
  3. Прочитать текущие записи из базы данных в память.
  4. Найти и удалить нужную строку (строки).
  5. Записать измененный набор обратно в базу.

Реализация кода: удаление в цикле

Самый важный момент при удалении строк из коллекции (будь то таблица значений или набор записей) — это направление обхода цикла. Если удалять строки в прямом цикле (от 0 до Количество), индексы оставшихся строк смещаются, что приведет либо к пропуску строк, либо к ошибке "Индекс находится за границами массива".

Правильный подход: использование обратного цикла.

Рассмотрим пример кода, адаптированный из полезных сообщений темы:


// Создаем набор записей для конкретного регистра
НаборЗаписей = РегистрыНакопления.ОборотыТоваров.СоздатьНаборЗаписей();

// Устанавливаем отбор по документу-регистратору
НаборЗаписей.Отбор.Регистратор.Установить(СсылкаНаДокумент);

// Считываем движения из базы данных
НаборЗаписей.Прочитать();

// Важно: Обходим коллекцию в обратном порядке!
// Используем конструкцию, которая гарантированно корректно работает с индексами
Для Индекс = 1 - НаборЗаписей.Количество() По 0 Цикл
    
    // Получаем запись по индексу (обратите внимание на минус перед индексом)
    // Либо можно использовать классический обратный цикл:
    // Для Сч = 1 По НаборЗаписей.Количество() Цикл -> ИндексУдаления = НаборЗаписей.Количество() - Сч;
    
    ТекущаяЗапись = НаборЗаписей[-Индекс];
    
    // Проверяем условие, по которому нужно удалить строку
    // Например, если номер строки в ТЧ документа совпадает с номером строки, который мы хотим удалить
    Если ТекущаяЗапись.НомерСтроки = УдаляемыйНомерСтроки Тогда
        НаборЗаписей.Удалить(ТекущаяЗапись);
    КонецЕсли;
    
КонецЦикла;

// Записываем обновленный набор
// Параметр Истина не обязателен, если свойство Записывать = Истина (по умолчанию)
НаборЗаписей.Записать();

Альтернативный способ: через Таблицу Значений

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

  1. Прочитать набор записей (как в примере выше).
  2. Выгрузить его в ТаблицаЗначений методом НаборЗаписей.Выгрузить().
  3. Найти и удалить строку в таблице значений (методом Найти или через построитель запроса).
  4. Загрузить очищенную таблицу обратно в набор методом НаборЗаписей.Загрузить(Таблица).
  5. Выполнить запись набора.

Важные архитектурные замечания

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

1. Проблема перепроведения
Если вы удалите запись из регистра программно, но логика проведения документа (процедура ОбработкаПроведения) останется прежней, то при следующем перепроведении документа или групповом перепроведении (например, при закрытии месяца) удаленная запись появится снова. Механизм проведения полностью перезаписывает движения.

Решение: Логика формирования движений должна быть заложена в самом модуле документа. Удаление "постфактум" работает только до первого перепроведения.

2. Запись движений до проведения
Автор темы упомянул, что запись в регистр происходит "при выборе строки", то есть до проведения документа. Это считается "антипаттерном" (ошибкой проектирования) в 1С по нескольким причинам:

Как сделать правильно?
Для отображения оперативных данных (скидок, бонусов) на форме без записи в базу данных следует использовать Временное хранилище или расчет данных "на лету" в оперативной памяти. Запись в физические таблицы регистров должна происходить строго в одной транзакции при нажатии кнопки "Провести".

Вывод

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

← На главную