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