Как сопоставить экземпляры товаров с кодами маркировки, если между ними нет прямой связи по идентификатору

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

В процессе интеграции 1С с внешними сервисами через API часто возникает ситуация, когда мы получаем список конкретных экземпляров продукции (уникальные идентификаторы exemplars_id), но соответствующие им коды маркировки (DataMatrix) хранятся в нашей базе без привязки к этим ID — только в связке с общим product_id. Проанализируем, как можно «подружить» эти таблицы и корректно распределить марки между экземплярами, чтобы избежать дублей и ошибок в учете.

Постановка задачи и анализ исходных данных

Представим, что у нас есть две таблицы. Первая таблица содержит перечень экземпляров, полученных извне. В ней есть product_id (общий код товара) и exemplars_id (уникальный серийный номер или идентификатор упаковки). Поле marka в этой таблице пустое, и его нужно заполнить.

Вторая таблица — это наш внутренний список свободных марок для этого product_id. Главная сложность заключается в том, что маркировка в базе «не знает» о существовании exemplars_id из API. Нам нужно распределить их по принципу «одна марка на один экземпляр».

Решение №1: Программный обход таблиц значений (ТЗ)

Этот метод считается наиболее надежным и простым в реализации, особенно если данные от API приходят в виде объектов, а не хранятся в базе данных. Рассмотрим алгоритм действий:

  1. Выгрузим обе таблицы в объекты ТаблицаЗначений.
  2. Подготовим таблицу с марками: для ускорения поиска можно проиндексировать её по полю product_id.
  3. Запустим цикл по таблице экземпляров. Для каждого product_id найдем подходящую строку во второй таблице.
  4. После того как марка присвоена экземпляру, удалим эту строку из таблицы марок, чтобы она не досталась следующему экземпляру того же товара.

Разберем пример кода, реализующий эту логику:


// Допустим, ТЗ_Экземпляры и ТЗ_Марки уже заполнены
Для Каждого СтрокаЭкземпляра Из ТЗ_Экземпляры Цикл
    
    ПараметрыОтбора = Новый Структура("product_id", СтрокаЭкземпляра.product_id);
    НайденныеМарки = ТЗ_Марки.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеМарки.Количество() > 0 Тогда
        // Берем первую доступную марку
        ТекущаяМарка = НайденныеМарки[0];
        СтрокаЭкземпляра.marka = ТекущаяМарка.marka;
        
        // Удаляем использованную марку из пула доступных
        ТЗ_Марки.Удалить(ТекущаяМарка);
    КонецЕсли;
    
КонецЦикла;

Преимущество этого метода: мы на 100% гарантируем уникальность распределения. Даже если данные в API дублируются, программный метод исключит повторное использование одного и того же кода маркировки.

Решение №2: Использование запроса с нумерацией строк

Рассмотрим ситуацию, когда мы хотим решить задачу исключительно средствами языка запросов 1С. Поскольку в 1С нет встроенной функции ROW_NUMBER(), нам придется имитировать её. Основная идея заключается в том, чтобы пронумеровать строки внутри каждой группы product_id в обеих таблицах, а затем соединить их по двум полям: product_id и ПорядковыйНомер.

Проанализируем шаги построения такого запроса:

  1. Поместим обе таблицы во временные таблицы (ВТ). Обязательно добавим в них поле, по которому можно однозначно упорядочить данные (например, сам exemplars_id или marka).
  2. Создадим ВТ с нумерацией экземпляров. Для этого соединим таблицу саму с собой по условию Т1.product_id = Т2.product_id И Т1.exemplars_id >= Т2.exemplars_id. Сгруппировав по Т1.exemplars_id и рассчитав Количество(Т2.exemplars_id), мы получим порядковый номер.
  3. Аналогично пронумеруем таблицу с марками.
  4. Выполним ЛЕВОЕ СОЕДИНЕНИЕ первой таблицы со второй по полям product_id и НомерСтроки.

Посмотрим на пример структуры запроса для нумерации:


ВЫБРАТЬ
    Т1.product_id,
    Т1.exemplars_id,
    КОЛИЧЕСТВО(Т2.exemplars_id) КАК ПорядковыйНомер
ПОМЕСТИТЬ ВТ_ЭкземплярыСНомером
ИЗ
    ВТ_Экземпляры КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Экземпляры КАК Т2
        ПО Т1.product_id = Т2.product_id
            И Т1.exemplars_id >= Т2.exemplars_id
СГРУППИРОВАТЬ ПО
    Т1.product_id,
    Т1.exemplars_id
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т_Связь.product_id,
    Т_Связь.exemplars_id,
    ВТ_МаркиСНомером.marka
ИЗ
    ВТ_ЭкземплярыСНомером КАК Т_Связь
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаркиСНомером КАК ВТ_МаркиСНомером
        ПО Т_Связь.product_id = ВТ_МаркиСНомером.product_id
            И Т_Связь.ПорядковыйНомер = ВТ_МаркиСНомером.ПорядковыйНомер

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

Обеспечение достоверности: почему простого распределения может быть недостаточно

Выясним причину, по которой простое распределение «любая марка к любому ID» может быть опасным. Если результат этого сопоставления не записывается в базу данных (например, в РегистрСведений), то при повторном формировании запроса или отчета порядок марок может измениться. Это создаст «кашу» в учете, когда один и тот же экземпляр в разных документах будет фигурировать с разными кодами маркировки.

Чтобы избежать подобных проблем, рекомендуем придерживаться следующих правил:

Итоги и рекомендации

Мы рассмотрели два основных способа решения задачи. Если вы работаете с небольшими объемами данных, приходящими из API в реальном времени, выбирайте программный обход Таблицы Значений — это проще в отладке и гарантирует отсутствие ошибок дублирования. Если же вам необходимо обработать огромный массив данных, уже хранящийся в базе, используйте запрос с имитацией ROW_NUMBER(), но помните о нагрузке на сервер.

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

← На главную