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

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

Программное изменение табличной части документа в 1С:Предприятии 7.7 — это классическая задача, которая таит в себе несколько подводных камней. Часто начинающие разработчики сталкиваются с ситуацией, когда часть строк не удаляется, данные обновляются некорректно или производительность системы резко падает. Разберем по шагам, почему возникают эти проблемы и как их решить правильно.

Проблема 1: Смещение индексов при удалении строк

Рассмотрим стандартную ситуацию: мы обходим строки документа с помощью цикла ВыбратьСтроки() и ПолучитьСтроку(). Внутри цикла мы проверяем условие и вызываем УдалитьСтроку(). Выясним причину, по которой этот подход часто работает некорректно.

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


Док.ВыбратьСтроки();
Пока Док.ПолучитьСтроку() = 1 Цикл
    Если Док.Товар.НужноУдалить = 1 Тогда
        Док.УдалитьСтроку(); // После этого следующая строка будет пропущена!
    КонецЕсли;
КонецЦикла;

Решение 2: Использование «обратного цикла»

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


КолСтрок = Док.КоличествоСтрок();
Для инд = КолСтрок По 1 Шаг -1 Цикл
    Док.ПолучитьСтрокуПоНомеру(инд);
    Если (Док.Количество = 0) Тогда
        Док.УдалитьСтроку();
    КонецЕсли;
КонецЦикла;

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

Проблема 3: Лишние операции внутри цикла

Разберем ситуацию из сообщения автора темы. В коде выполнялся поиск документа Док.НайтиДокумент() внутри цикла по таблице значений. Это грубая ошибка проектирования. Метод НайтиДокумент() полностью инициализирует объект заново, сбрасывая все текущие изменения. Если вы уже позиционировали объект Док на нужном документе до начала цикла, повторный поиск внутри только замедлит работу и приведет к потере данных.

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

Оптимальный алгоритм обработки строк

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

  1. Сначала позиционируем объект документа один раз.
  2. Обходим таблицу-источник (например, тбПереоценка).
  3. Для каждой позиции ищем строку в документе.
  4. Если нашли — обновляем количество или удаляем, если остаток нулевой.
  5. Если не нашли и остаток положительный — добавляем новую строку.

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


Док = СоздатьОбъект("Документ.ГрупповаяПереоценка");
Если Док.НайтиДокумент(ВыбГрупповаяПереоценка) = 1 Тогда
    тбПереоценка.ВыбратьСтроки();
    Пока тбПереоценка.ПолучитьСтроку() = 1 Цикл
        // Пытаемся найти товар в существующем документе
        Найдено = 0;
        Док.ВыбратьСтроки();
        Пока Док.ПолучитьСтроку() = 1 Цикл
            Если (Док.Товар = тбПереоценка.Товар) И (Док.Склад = тбПереоценка.Склад) Тогда
                Найдено = 1;
                Если тбПереоценка.НаОстатке = 0 Тогда
                    Док.УдалитьСтроку();
                Иначе
                    Док.Количество = тбПереоценка.НаОстатке;
                    Док.СуммаНов = Док.Количество * Док.ЦенаНов;
                КонецЕсли;
                Прервать; // Выходим из поиска в документе, переходим к следующему товару в ТЗ
            КонецЕсли;
        КонецЦикла;

        // Если товара в документе не было, а остаток есть — добавляем новую строку
        Если (Найдено = 0) И (тбПереоценка.НаОстатке > 0) Тогда
            Док.НоваяСтрока();
            Док.Товар = тбПереоценка.Товар;
            Док.Склад = тбПереоценка.Склад;
            Док.Количество = тбПереоценка.НаОстатке;
            // Не забудьте заполнить остальные реквизиты (Цена, Ставка НДС и т.д.)
        КонецЕсли;
    КонецЦикла;
    
    // Записываем и проводим один раз после всех изменений
    Док.Записать();
    Док.Провести();
КонецЕсли;

Метод ускорения через Таблицу Значений

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

Алгоритм действий следующий:

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

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

Рекомендации по безопасности

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

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

Во-вторых, проверяйте, не открыт ли документ другим пользователем. В 1С 7.7 при программной записи открытого документа возникнет ошибка «Объект заблокирован». Рассмотрим простую проверку:


Если Док.НайтиДокумент(ВыбДок) = 1 Тогда
    Попытка
        Док.Записать();
    Исключение
        Предупреждение("Не удалось записать документ! Возможно, он открыт другим пользователем.");
        Возврат;
    КонецПопытки;
КонецЕсли;

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

← На главную