Как сконвертировать данные из поля ЕдиницаПоКлассификатору в составной тип ЕдиницаИзмерения (Классификатор и Справочник)?

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

При переносе данных между различными конфигурациями «1С:Предприятие» с помощью программного комплекса «1С:Конвертация данных 2.1» разработчики часто сталкиваются с несовпадением архитектуры хранения нормативно-справочной информации. Типичным примером является перенос документов из старых систем (где единица измерения — это простой справочник КлассификаторЕдиницИзмерения) в современные конфигурации на управляемых формах, такие как «Управление нашей фирмой» (УНФ), «Управление торговлей 11» (УТ), «ERP Управление предприятием» или «Комплексная автоматизация».

В этой статье мы подробно разберем, как правильно реализовать конвертацию единиц измерения, когда в источнике это простая ссылка на классификатор, а в приемнике — составной тип данных, включающий как сам классификатор ОКЕИ, так и подчиненный справочник индивидуальных упаковок номенклатуры. Мы проанализируем различные подходы, разберем код из практики реального внедрения и рассмотрим оптимальные с технической точки зрения алгоритмы решения этой задачи.

Понимание архитектуры хранения единиц измерения в источнике и приемнике

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

База-источник:

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

База-приемник (современная конфигурация):

  1. Реквизит ЕдиницаИзмерения в табличных частях документов имеет составной тип данных. Он может содержать ссылку либо на СправочникСсылка.КлассификаторЕдиницИзмерения (ОКЕИ), либо на подчиненный справочник СправочникСсылка.ЕдиницыИзмерения (или УпаковкиЕдиницыИзмерения в УТ/ERP).
  2. В карточке номенклатуры хранится базовая единица (всегда элемент классификатора ОКЕИ).
  3. Все неосновные единицы (упаковки с коэффициентами, отличными от единицы) хранятся в подчиненном справочнике ЕдиницыИзмерения, где поле Владелец жестко связано с конкретной номенклатурой.
  4. Логика проведения документов требует: если товар продается или покупается в базовой единице измерения, в документ подставляется ссылка на общесистемный классификатор. Если же используется альтернативная единица (например, коробка из 10 штук), в документ должна быть записана ссылка на элемент подчиненного справочника ЕдиницыИзмерения, созданный специально для этого товара.

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

Решение №1: Простой разбор логики в обработчике «ПослеЗагрузки» документа

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

Для реализации этого метода в правилах конвертации свойств (ПКС) табличной части документа для реквизита ЕдиницаИзмерения отключается стандартная передача ссылки. Вместо этого значение передается в параметры объекта (например, через структуру ПараметрыОбъекта).

Ниже представлен рабочий программный код, который размещается в обработчике ПослеЗагрузки правила конвертации объектов (ПКО) для переносимого документа:


// Определение корректной ЕдиницыИзмерения в документе приемника
Если ПараметрыОбъекта <> Неопределено Тогда
    ТаблицаПараметровЗапасы = ПараметрыОбъекта["ЗапасыТабличнаяЧасть"];
    Если ТаблицаПараметровЗапасы <> Неопределено Тогда
        Для Каждого СтрокаПараметров Из ТаблицаПараметровЗапасы Цикл
            // Ищем базовую единицу по наименованию в классификаторе приемника
            ЕдиницаПоКлассификатору = Справочники.КлассификаторЕдиницИзмерения.НайтиПоНаименованию(СтрокаПараметров.ЕдИзм, Истина);
            
            ТекущаяСтрокаТЧ = Объект.Запасы[СтрокаПараметров.НомерСтроки];
            ТоварВСтроке = ТекущаяСтрокаТЧ.Номенклатура;
            
            // Если базовая единица номенклатуры совпадает с единицей из источника
            Если ТоварВСтроке.ЕдиницаИзмерения = ЕдиницаПоКлассификатору Тогда
                // Записываем в документ базовую единицу напрямую из классификатора
                ТекущаяСтрокаТЧ.ЕдиницаИзмерения = ЕдиницаПоКлассификатору;
            Иначе
                // Если единицы не совпадают, значит используется альтернативная упаковка.
                // Ищем элемент подчиненного справочника ЕдиницыИзмерения по владельцу (товару) и наименованию.
                ЕдиницаИзмерения = Справочники.ЕдиницыИзмерения.НайтиПоНаименованию(СтрокаПараметров.ЕдИзм, Истина, , ТоварВСтроке);
                
                ТекущаяСтрокаТЧ.ЕдиницаИзмерения = ЕдиницаИзмерения;
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
КонецЕсли;

Проанализируем этот алгоритм подробнее. На первый взгляд решение выглядит простым и логичным. Однако у него есть существенные недостатки, которые могут проявиться при промышленной эксплуатации правил обмена на больших объемах данных:

Решение №2: Использование стандартных механизмов КД 2.1 через ПКО и входящие данные (Best Practice)

Для того чтобы избежать узких мест первого решения, мы рекомендуем задействовать штатный функционал конфигурации «Конвертация данных 2.1». Программа позволяет динамически определять правило конвертации (ПКО) для выгружаемого свойства непосредственно в момент выгрузки. Это избавляет от необходимости писать тяжелый код постобработки при загрузке.

Давайте разберем по шагам, как настроить этот механизм:

Шаг 1. Создание ПКО для единиц измерения

В настройках правил обмена создаем два правила конвертации объектов:

  1. ПКО КлассификаторЕдиницИзмерения (для базовых единиц).
  2. ПКО ЕдиницыИзмерения (для подчиненных альтернативных упаковок).

Для ПКО ЕдиницыИзмерения настраиваем поиск по полям Владелец (Номенклатура) и ЕдиницаПоКлассификатору (или Наименование). Для этих свойств в правилах конвертации свойств (ПКС) взводим флаг «Получить из входящих данных».

Шаг 2. Динамический выбор правила в документе

В правиле конвертации свойств (ПКС) для реквизита ЕдиницаИзмерения табличной части документа в обработчике ПередВыгрузкой пишем следующий код:


// Определяем, совпадает ли единица измерения в документе источника с базовой единицей номенклатуры
Если Источник.ЕдиницаИзмерения = Источник.Номенклатура.БазоваяЕдиницаИзмерения Тогда
    // Если это базовая единица, выгружаем её по правилу классификатора
    ИмяПКО = "КлассификаторЕдиницИзмерения";
Иначе
    // Если это альтернативная упаковка, используем подчиненный справочник
    ИмяПКО = "ЕдиницыИзмерения";
    
    // Формируем структуру входящих данных для корректного поиска и создания элемента в приемнике
    ИсходящиеДанные = Новый Структура;
    ИсходящиеДанные.Вставить("Владелец", Источник.Номенклатура);
    ИсходящиеДанные.Вставить("ЕдиницаПоКлассификатору", Источник.ЕдиницаИзмерения);
    ИсходящиеДанные.Вставить("Наименование", СокрЛП(Источник.ЕдиницаИзмерения));
    
    // Если в источнике в ТЧ хранится коэффициент упаковки, передаем его
    Если НаличиеРеквизитаКоэффициент Тогда
        ИсходящиеДанные.Вставить("Коэффициент", Источник.Коэффициент);
    Иначе
        ИсходящиеДанные.Вставить("Коэффициент", 1);
    КонецЕсли;
КонецЕсли;

В чем преимущества данного подхода?

Во-первых, платформа «1С:Предприятие» при загрузке автоматически попытается найти нужный элемент подчиненного справочника по полям поиска. Во-вторых, если элемент не будет найден, конфигурация загрузки автоматически создаст его на лету, заполнив владельца и классификатор из переданных входящих данных. Это гарантирует стопроцентную целостность данных в информационной базе приемника.

Решение №3: Оптимизированный поиск через запрос в «ПослеЗагрузки» документа

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

Проанализируем оптимизированный вариант обработчика ПослеЗагрузки для документа:


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

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

Резюме и рекомендации

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

  1. Всегда старайтесь переносить альтернативные единицы измерения (упаковки) на этапе конвертации справочника Номенклатура. Если все упаковки созданы заранее, сопоставить их в документах будет значительно проще.
  2. Для реализации гибкой логики составных типов отдавайте предпочтение **Решению №2** с использованием входящих данных и динамическим выбором ПКО (ИмяПКО). Это самый надежный способ, минимизирующий написание сложного программного кода на стороне загрузки.
  3. Если вы вынуждены писать код в обработчиках загрузки документов, никогда не используйте поиск элементов (НайтиПоНаименованию, НайтиПоРеквизиту) внутри циклов. Всегда кэшируйте данные или выбирайте их одним пакетным запросом, как показано в **Решении №3**.
← На главную