Как правильно удалять или фильтровать строки табличной части документа при проведении в 1С

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

В процессе разработки часто возникает задача: в документе необходимо хранить избыточный набор данных (например, для построения отчетов или инфографики), но при этом движения по регистрам должны формироваться только по тем строкам, которые удовлетворяют определенным условиям. Рассмотрим типичную ситуацию, когда разработчик пытается удалять «лишние» строки непосредственно в процедуре ОбработкаПроведения, и проанализируем, почему это является грубой ошибкой и как реализовать задачу правильно.

Почему нельзя удалять строки в процедуре ОбработкаПроведения

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

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

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

Вариант 1. Очистка данных в событии ПередЗаписью

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

  1. Проверим режим записи документа. Нам нужно, чтобы очистка происходила только при проведении.
  2. Выполним обход табличной части в обратном порядке (с конца), чтобы индексы строк не смещались при удалении.

Посмотрим на пример кода:


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

КонецПроцедуры

Важный нюанс: Этот метод все равно удаляет данные физически. Если они нужны для инфографики, этот способ нам не подходит. Рассмотрим более гибкие решения.

Вариант 2. Фильтрация в алгоритме проведения без удаления строк

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

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

Оптимизация: Избавление от запросов в цикле

В представленном автором коде есть еще одна критическая проблема — использование метода Остатки() внутри цикла Для каждого СтрТЧ из ТабличнаяЧасть1. Это приводит к так называемой проблеме «N+1 запрос», когда при 100 строках в чеке сервер сделает 100 отдельных обращений к СУБД. Это крайне медленно.

Разберем, как оптимизировать этот процесс с помощью одного пакетного запроса:

  1. Выгрузим колонку Код из табличной части в массив.
  2. Передадим этот массив в запрос к виртуальной таблице остатков.
  3. Получим результат одним махом и будем искать данные в полученной таблице значений.

Проанализируем пример оптимизированного кода:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // 1. Подготовка данных
    ТаблицаКодов = ТабличнаяЧасть1.ВыгрузитьКолонку("Код");
    
    // 2. Получаем все остатки одним запросом
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ПотолкиОстатки.Код КАК Код,
        |   ПотолкиОстатки.ЦенаИзделия КАК ЦенаИзделия
        |ИЗ
        |   РегистрНакопления.Потолки.Остатки(, Код В (&МассивКодов)) КАК ПотолкиОстатки";
    
    Запрос.УстановитьПараметр("МассивКодов", ТаблицаКодов);
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Индексируем таблицу для быстрого поиска в цикле
    РезультатЗапроса.Индексы.Добавить("Код");
    
    Движения.Потолки.Записывать = Истина;
    
    // 3. Основной цикл проведения
    Для каждого СтрТЧ Из ТабличнаяЧасть1 Цикл
        
        // Ищем данные по коду в выгруженной таблице остатков
        ПараметрыПоиска = Новый Структура("Код", СтрТЧ.Код);
        НайденныеСтроки = РезультатЗапроса.НайтиСтроки(ПараметрыПоиска);
        
        // Если данные не подошли под условия — просто пропускаем итерацию, не удаляя строку
        Если НайденныеСтроки.Количество() = 0 Тогда
            Продолжить; 
        КонецЕсли;
        
        // Формируем движения на основании найденных данных
        Для каждого СтрокаОстатка Из НайденныеСтроки Цикл
            Если СтрокаОстатка.ЦенаИзделия < 0 Тогда
                Движение = Движения.Потолки.ДобавитьПриход();
            Иначе
                Движение = Движения.Потолки.ДобавитьРасход();
            КонецЕсли;
            // Заполнение реквизитов движения...
        КонецЦикла;
        
    КонецЦикла;

КонецПроцедуры

Вариант 3. Использование флага участия в проведении

Если для инфографики критично понимать, какие именно строки «сработали», а какие нет, мы можем добавить в табличную часть скрытый или доступный только для чтения реквизит УчаствуетВПроведении (тип Булево).

Выясним преимущества такого подхода:

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

Рекомендации по работе с управляемыми блокировками

При проверке остатков (как в исходном примере) важно помнить о конкурентной работе пользователей. Если вы используете схему «сначала проверил остаток — потом списал», обязательно используйте управляемые блокировки.

Разберем, как это сделать:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.Потолки");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.ИсточникДанных = ТабличнаяЧасть1;
ЭлементБлокировки.ИспользоватьИзИсточникаДанных("Код", "Код");
Блокировка.Заблокировать();

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

Итоги

Подведем итог нашему анализу:

  1. Никогда не удаляйте строки ТЧ в ОбработкеПроведения. Это архитектурно неверно и технически небезопасно.
  2. Если строки нужно убрать физически, делайте это в ПередЗаписью, но помните, что данные исчезнут навсегда.
  3. Оптимальный путь — фильтрация внутри алгоритма проведения. Используйте пакетные запросы (избегайте запросов в цикле) и виртуальные таблицы.
  4. Для сохранения данных инфографики используйте дополнительные реквизиты-признаки вместо удаления строк.

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

← На главную