Как собрать иерархию упаковок при сканировании любого штрихкода в 1С?

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

В современных складских и логистических системах на базе 1С часто встречается задача организации «матрешки». Это структура, в которой единица товара вкладывается в коробку, коробка — в палету, а палета — в контейнер. Технически это реализуется через справочник ШтрихкодыУпаковок, где в табличной части каждого элемента хранятся ссылки на вложенные элементы. Основная сложность возникает, когда при сканировании штрихкода единичного товара нам нужно мгновенно «размотать» всю цепочку вверх и понять, в какой палете и в каком контейнере он находится. Для автоматизации процессов сканирования и учета маркировки на складе можно использовать мобильное решение для ТСД и смартфонов с интеграцией в 1С.

В этой статье мы подробно разберем несколько профессиональных подходов к реализации этой задачи: от простых рекурсивных запросов до архитектурных решений, используемых в высоконагруженных системах маркировки.

Метод 1. Использование механизма «Критерии отбора»

Первый и наиболее штатный для платформы 1С способ — использование объекта метаданных КритерииОтбора. Разберем, почему это эффективно. Когда мы описываем вложенность через табличную часть, поиск «кто является родителем для этой коробки» обычным запросом потребует сканирования всей таблицы. КритерииОтбора создают на уровне СУБД индекс, который позволяет мгновенно найти все ссылки на конкретный объект в указанных табличных частях.

Рассмотрим по шагам, как это настроить:

  1. Создадим объект КритерийОтбора.Склад_ВладелецУпаковки.
  2. На вкладке «Данные» укажем тип значения (например, СправочникСсылка.ШтрихкодыУпаковок).
  3. На вкладке «Состав» выберем табличную часть ВложенныеУпаковки этого же справочника.

Теперь, чтобы получить родителя, нам не нужно писать сложные запросы. Мы можем использовать следующий код:


Функция ПолучитьРодителя(ТекущаяУпаковка)
    Выборка = КритерииОтбора.Склад_ВладелецУпаковки.Выбрать(ТекущаяУпаковка);
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Ссылка;
    КонецЕсли;
    Возврат Неопределено;
КонецФункции

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

Метод 2. Реализация через Регистр сведений (Транзитивное замыкание)

Если глубина вложенности велика или дерево упаковок строится очень часто (например, в рабочем месте кладовщика с высокой интенсивностью), использование критериев отбора может быть недостаточно быстрым из-за рекурсии. В таких случаях мы рекомендуем использовать вспомогательный Регистр сведений СвязиУпаковок.

В этом регистре мы будем хранить структуру в плоском виде. Разберем структуру регистра:

При записи элемента справочника ШтрихкодыУпаковок необходимо обновлять данные в этом регистре. Рассмотрим преимущество: мы можем получить всю иерархию одним запросом, используя конструкцию В ИЕРАРХИИ, если структура справочника позволяет, либо выполнив один пакетный запрос. Однако чаще всего этот регистр используется для быстрого поиска «в один шаг» без обращения к тяжелым табличным частям.

Метод 3. Хранение «Строки иерархии» (Path Indexing)

Проанализируем еще один интересный подход, который часто применяется в веб-технологиях, но вполне реализуем в 1С. Это хранение полного пути в самом элементе справочника. В справочник добавляется реквизит ПолныйПутьИерархии (строка неограниченной длины, индексируемая).

При включении коробки в палету мы записываем в палету путь: /ID_Палеты/. А для коробки путь обновляется на /ID_Палеты/ID_Коробки/. Таким образом, при сканировании любого штрихкода мы сразу считываем это поле и мгновенно получаем идентификаторы всех родителей без дополнительных запросов к БД.

Посмотрим на пример кода, который может разбирать такую строку:


// Допустим, мы получили строку: "/84db-00155d01/a2bf-00155d02/c3d1-00155d03/"
МассивИдентификаторов = СтрРазделить(СтрокаИерархии, "/", Ложь);
// Теперь у нас есть список всех родителей в порядке вложенности
Для Каждого ИД Из МассивИдентификаторов Цикл
    СсылкаРодителя = Справочники.ШтрихкодыУпаковок.ПолучитьСсылку(Новый УникальныйИдентификатор(ИД));
    // Добавляем в дерево визуализации
КонецЦикла;

Метод 4. Рекурсивные запросы в современных версиях платформы

Начиная с версии платформы 8.3.23, разработчикам стали доступны рекурсивные запросы (CTE). Если ваша база работает на актуальной версии, мы можем получить всю структуру одним запросом к табличной части, не создавая дополнительных регистров.

Рассмотрим, как мог бы выглядеть такой запрос (схематично):


ВЫБРАТЬ
    ТЧ.Ссылка КАК Родитель,
    ТЧ.Штрихкод КАК Вложенный
ИЗ
    Справочник.ШтрихкодыУпаковок.ВложенныеУпаковки КАК ТЧ
ГДЕ
    ТЧ.Упаковка = &СканируемыйШтрихкод

// Далее используется рекурсивное объединение с этой же таблицей

Это самый технологичный метод, так как он перекладывает всю работу на плечи СУБД, минимизируя трафик между сервером приложений и базой данных.

Важный аспект: Контроль уникальности вхождения

Выясним важную деталь, упомянутую в обсуждении: может ли один товар входить в две палеты? В логике «матрешки» это недопустимо. Чтобы система работала корректно, нам необходимо добавить проверку при записи упаковки.

Рассмотрим пример кода в модуле объекта справочника:


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

Визуализация структуры: Дерево значений

После того как мы собрали массив ссылок (от сканируемого объекта до самого верхнего родителя), нам нужно красиво показать это пользователю. Лучшим инструментом для этого является ДеревоЗначений на управляемой форме.

Алгоритм построения будет следующим:

  1. Получаем массив всех связанных ссылок (иерархию).
  2. Запросом выбираем дополнительные данные (наименование, текущий статус, количество вложений) для всех ссылок из массива.
  3. Программным обходом заполняем ДеревоЗначений, начиная с самого верхнего родителя (корня).

Такой подход исключает проблему «запросов в цикле» при отрисовке интерфейса, так как данные извлекаются одним пакетом, а затем просто распределяются по веткам дерева в памяти сервера приложений.

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

← На главную