Как реализовать алгоритм распределения открыток по конвертам в отчете 1С

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Анализ исходной проблемы и типичных ошибок

Рассмотрим ситуацию: у нас есть два документа с табличными частями — «Открытки» и «Конверты». В обеих таблицах указаны длина и ширина. Основная ошибка начинающих разработчиков при решении этой задачи заключается в попытке использовать фиксированные области табличного документа (например, Область("Конверт1")). Такой подход делает отчет негибким и не позволяет вывести список произвольной длины.

Основные проблемы исходного кода:

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

Шаг 1: Подготовка данных с помощью Таблицы Значений

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

Проанализируем процесс подготовки коллекций:


// Выгружаем данные из документов в таблицы значений
ТЗ_Открытки = ДокументОткрытки.ТабличнаяЧастьОткрытки.Выгрузить();
ТЗ_Конверты = ДокументКонверты.ТабличнаяЧастьКонверты.Выгрузить();

// Добавляем колонку для пометки того, что конверт уже занят
ТЗ_Конверты.Колонки.Добавить("Использован", Новый ОписаниеТипов("Булево"));

Шаг 2: Реализация алгоритма подбора с учетом поворота

Выясним причину, по которой простое сравнение «Длина к Длине» не всегда работает. Открытка размером 10х20 см прекрасно поместится в конверт 21х11 см, если её повернуть. Следовательно, наше условие должно проверять оба варианта ориентации.

Разберем по шагам логику сопоставления в цикле:


Для Каждого СтрокаОткрытка Из ТЗ_Открытки Цикл
    Для Каждого СтрокаКонверт Из ТЗ_Конверты Цикл
        
        // Проверяем, не занят ли уже этот конверт другой открыткой
        Если СтрокаКонверт.Использован Тогда
            Продолжить;
        КонецЕсли;
        
        // Условие соответствия с учетом поворота
        МеньшаяСторонаОткрытки = Мин(СтрокаОткрытка.Длина, СтрокаОткрытка.Ширина);
        БольшаяСторонаОткрытки = Макс(СтрокаОткрытка.Длина, СтрокаОткрытка.Ширина);
        
        МеньшаяСторонаКонверта = Мин(СтрокаКонверт.Длина, СтрокаКонверт.Ширина);
        БольшаяСторонаКонверта = Макс(СтрокаКонверт.Длина, СтрокаКонверт.Ширина);
        
        Если МеньшаяСторонаОткрытки <= МеньшаяСторонаКонверта И БольшаяСторонаОткрытки <= БольшаяСторонаКонверта Тогда
            // Если подошло, фиксируем связь и помечаем конверт
            СтрокаКонверт.Использован = Истина;
            
            // Здесь будет код вывода в табличный документ
            // ...
            
            // Прерываем внутренний цикл, так как конверт для текущей открытки найден
            Прервать; 
        КонецЕсли;
        
    КонецЦикла;
КонецЦикла;

Шаг 3: Формирование отчета через макет

Чтобы отчет заполнялся корректно и выглядел профессионально, воспользуемся объектом Макет. Вместо того чтобы прописывать адреса ячеек вручную, мы создадим именованную область «Строка» и будем выводить её столько раз, сколько пар у нас получится.

Рассмотрим алгоритм вывода данных:

  1. Получим макет отчета через ЭтотОбъект.ПолучитьМакет("Макет").
  2. Получим область, содержащую заголовки (например, «Шапка»).
  3. В цикле будем получать область «Строка», заполнять её параметры и выводить в результирующий табличный документ методом Вывести().

Посмотрим на пример кода формирования отчета:


ТабДок = Новый ТабличныйДокумент;
Макет = Отчеты.РаспределениеОткрыток.ПолучитьМакет("Макет");

// Выводим шапку
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ТабДок.Вывести(ОбластьШапка);

ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
НомерПП = 0;

Для Каждого СтрокаОткрытка Из ТЗ_Открытки Цикл
    // ... (тут логика поиска конверта из Шага 2)
    Если НашлиПодходящийКонверт Тогда
        НомерПП = НомерПП + 1;
        ОбластьСтрока.Параметры.Номер = НомерПП;
        ОбластьСтрока.Параметры.Открытка = СтрокаОткрытка.Наименование;
        ОбластьСтрока.Параметры.Конверт = СтрокаКонверт.Наименование;
        ТабДок.Вывести(ОбластьСтрока);
    КонецЕсли;
КонецЦикла;

Шаг 4: Оптимизация (Стратегия «Наилучшего соответствия»)

Проанализируем ситуацию, упомянутую опытными разработчиками: если мы будем класть маленькие открытки в самые большие конверты, нам может не хватить больших конвертов для соответствующих открыток. Это задача на минимизацию неотправленных писем.

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

Пример предварительной сортировки ТаблицыЗначений:


// Сортируем открытки по убыванию площади (или максимальной стороны)
ТЗ_Открытки.Колонки.Добавить("Площадь", Новый ОписаниеТипов("Число"));
Для Каждого Стр Из ТЗ_Открытки Цикл
    Стр.Площадь = Стр.Длина * Стр.Ширина;
КонецЦикла;
ТЗ_Открытки.Сортировать("Площадь Убыв");

Заключение

Подведем итоги. Для решения задачи распределения в 1С необходимо отказаться от жесткой привязки к ячейкам таблицы и перейти к использованию коллекций данных (ТаблицаЗначений) и механизмов макета (Вывести, Параметры). Использование флагов (Использован = Истина) и правильная сортировка данных позволяют превратить простой перебор в эффективный алгоритм, решающий бизнес-задачу с минимальными потерями ресурсов.

Помните, что использование метода Прервать во внутреннем цикле поиска критически важно — это не только ускоряет работу программы, но и гарантирует, что одной открытке будет назначен ровно один конверт.

← На главную