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