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