В практике программирования на платформе 1С:Предприятие 8.3 часто возникают задачи, связанные с распределением ресурсов или сопоставлением объектов по определенным критериям. Одной из таких классических задач является подбор подходящей упаковки (конвертов) для содержимого (открыток). В этой статье мы детально разберем, как создать отчет, который будет автоматически подбирать конверты для открыток, учитывая их размеры, возможность поворота и ограниченное количество доступных материалов.
Рассмотрим ситуацию: у нас есть два документа с табличными частями — «Открытки» и «Конверты». В обеих таблицах указаны длина и ширина. Основная ошибка начинающих разработчиков при решении этой задачи заключается в попытке использовать фиксированные области табличного документа (например, Область("Конверт1")). Такой подход делает отчет негибким и не позволяет вывести список произвольной длины.
Основные проблемы исходного кода:
Для эффективной работы нам необходимо выгрузить данные из документов в универсальную коллекцию — ТаблицаЗначений. Это позволит нам динамически изменять данные, добавлять служебные колонки и удалять уже использованные позиции.
Проанализируем процесс подготовки коллекций:
// Выгружаем данные из документов в таблицы значений
ТЗ_Открытки = ДокументОткрытки.ТабличнаяЧастьОткрытки.Выгрузить();
ТЗ_Конверты = ДокументКонверты.ТабличнаяЧастьКонверты.Выгрузить();
// Добавляем колонку для пометки того, что конверт уже занят
ТЗ_Конверты.Колонки.Добавить("Использован", Новый ОписаниеТипов("Булево"));
Выясним причину, по которой простое сравнение «Длина к Длине» не всегда работает. Открытка размером 10х20 см прекрасно поместится в конверт 21х11 см, если её повернуть. Следовательно, наше условие должно проверять оба варианта ориентации.
Разберем по шагам логику сопоставления в цикле:
Для Каждого СтрокаОткрытка Из ТЗ_Открытки Цикл
Для Каждого СтрокаКонверт Из ТЗ_Конверты Цикл
// Проверяем, не занят ли уже этот конверт другой открыткой
Если СтрокаКонверт.Использован Тогда
Продолжить;
КонецЕсли;
// Условие соответствия с учетом поворота
МеньшаяСторонаОткрытки = Мин(СтрокаОткрытка.Длина, СтрокаОткрытка.Ширина);
БольшаяСторонаОткрытки = Макс(СтрокаОткрытка.Длина, СтрокаОткрытка.Ширина);
МеньшаяСторонаКонверта = Мин(СтрокаКонверт.Длина, СтрокаКонверт.Ширина);
БольшаяСторонаКонверта = Макс(СтрокаКонверт.Длина, СтрокаКонверт.Ширина);
Если МеньшаяСторонаОткрытки <= МеньшаяСторонаКонверта И БольшаяСторонаОткрытки <= БольшаяСторонаКонверта Тогда
// Если подошло, фиксируем связь и помечаем конверт
СтрокаКонверт.Использован = Истина;
// Здесь будет код вывода в табличный документ
// ...
// Прерываем внутренний цикл, так как конверт для текущей открытки найден
Прервать;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Чтобы отчет заполнялся корректно и выглядел профессионально, воспользуемся объектом Макет. Вместо того чтобы прописывать адреса ячеек вручную, мы создадим именованную область «Строка» и будем выводить её столько раз, сколько пар у нас получится.
Рассмотрим алгоритм вывода данных:
ЭтотОбъект.ПолучитьМакет("Макет").Вывести().Посмотрим на пример кода формирования отчета:
ТабДок = Новый ТабличныйДокумент;
Макет = Отчеты.РаспределениеОткрыток.ПолучитьМакет("Макет");
// Выводим шапку
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
НомерПП = 0;
Для Каждого СтрокаОткрытка Из ТЗ_Открытки Цикл
// ... (тут логика поиска конверта из Шага 2)
Если НашлиПодходящийКонверт Тогда
НомерПП = НомерПП + 1;
ОбластьСтрока.Параметры.Номер = НомерПП;
ОбластьСтрока.Параметры.Открытка = СтрокаОткрытка.Наименование;
ОбластьСтрока.Параметры.Конверт = СтрокаКонверт.Наименование;
ТабДок.Вывести(ОбластьСтрока);
КонецЕсли;
КонецЦикла;
Проанализируем ситуацию, упомянутую опытными разработчиками: если мы будем класть маленькие открытки в самые большие конверты, нам может не хватить больших конвертов для соответствующих открыток. Это задача на минимизацию неотправленных писем.
Рекомендации по оптимизации:
Пример предварительной сортировки ТаблицыЗначений:
// Сортируем открытки по убыванию площади (или максимальной стороны)
ТЗ_Открытки.Колонки.Добавить("Площадь", Новый ОписаниеТипов("Число"));
Для Каждого Стр Из ТЗ_Открытки Цикл
Стр.Площадь = Стр.Длина * Стр.Ширина;
КонецЦикла;
ТЗ_Открытки.Сортировать("Площадь Убыв");
Подведем итоги. Для решения задачи распределения в 1С необходимо отказаться от жесткой привязки к ячейкам таблицы и перейти к использованию коллекций данных (ТаблицаЗначений) и механизмов макета (Вывести, Параметры). Использование флагов (Использован = Истина) и правильная сортировка данных позволяют превратить простой перебор в эффективный алгоритм, решающий бизнес-задачу с минимальными потерями ресурсов.
Помните, что использование метода Прервать во внутреннем цикле поиска критически важно — это не только ускоряет работу программы, но и гарантирует, что одной открытке будет назначен ровно один конверт.