Как реализовать выборочный перенос документов из УПП в ТОиР 2.0 с проверкой иерархии объектов ремонта?

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

Перенос данных между конфигурациями "1С:Управление производственным предприятием 1.3" (с подсистемой ТОиР) и современной "ТОиР 2.0 КОРП" часто сопряжен с трудностями из-за кардинального различия в архитектуре. Одной из самых сложных задач является перенос документов Выявленные дефекты, где логика переноса напрямую зависит от того, существуют ли объекты ремонта в новой базе и как они связаны иерархически. Рассмотрим, как решить эту задачу с помощью конфигурации "Конвертация данных 2.0" (КД 2.0).

Разберем архитектурные различия и суть проблемы

В старых версиях ТОиР иерархия объектов ремонта была реализована классическим способом — через родителя в справочнике ОбъектыРемонта. В новой конфигурации "ТОиР 2.0" используется более гибкая, но сложная структура: иерархия вынесена в регистры сведений, такие как торо_ИерархическиеСтруктурыОР и торо_ОбъектыРемонтаГруппы. Это позволяет одному и тому же объекту находиться одновременно в функциональной и пространственной иерархиях.

Перед нами стоит задача: переносить только те строки документа Выявленные дефекты, для которых в базе-приемнике можно однозначно определить связь между "Родительским элементом" (основным объектом ремонта) и "Дочерним элементом" (узлом или деталью). Ситуация осложняется тем, что в базе-источнике у нас есть только код соответствия (например, КодВТоиР20), а реальную структуру иерархии мы можем проверить только в базе-приемнике в момент загрузки.

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

  1. Если оба объекта (родитель и дочерний) уже есть в ТОиР 2.0 — переносим как есть.
  2. Если есть только родитель — дублируем его в поле дочернего элемента.
  3. Если есть только дочерний элемент — пытаемся найти его родителя в базе-приемнике через регистры иерархии. Если нашли — подставляем, если нет — строку не переносим.
  4. Если ни одного кода не проставлено — строку (или весь документ) игнорируем.

Шаг 1: Подготовка данных на стороне источника (ПКС)

Поскольку нам нужно передать в базу-приемник не только ссылки, но и специфические признаки (коды) для выполнения поисковых запросов, воспользуемся механизмом передачи параметров через ОбъектКоллекции. В правилах конвертации свойств (ПКС) для табличной части СписокДефектов мы создадим дополнительные вычисляемые поля, которые будут содержать структуру с необходимыми данными.

Рассмотрим пример кода в обработчике ПередВыгрузкой ПКС (для служебного поля, которое мы назовем, например, КодРодителяСтруктура):


СтруктураПараметров = Новый Структура("ИД, КодРодителя");
СтруктураПараметров.ИД = ОбъектКоллекции.ID; // Уникальный идентификатор строки для поиска в приемнике
СтруктураПараметров.КодРодителя = ОбъектКоллекции.ОбъектРемонта.КодВТОиР20;

// Сериализуем структуру в строку для передачи в XML
Значение = ЗначениеВСтрокуВнутр(СтруктураПараметров);

Такой подход позволяет передать любую дополнительную информацию, не изменяя структуру метаданных в приемнике. Аналогично поступим и для дочернего элемента (узла).

Шаг 2: Реализация логики поиска в базе-приемнике (ПКО)

Основная работа происходит в правиле конвертации объекта (ПКО) документа ВыявленныеДефекты. Нам необходимо использовать обработчик ПослеЗагрузки. На этом этапе объект уже создан в памяти, его реквизиты заполнены из файла, но он еще не записан в базу данных. Это идеальный момент для выполнения запросов и модификации табличной части.

Выясним причину, почему нельзя делать это в ПередЗагрузкой: в тот момент у нас еще нет заполненного объекта Объект, а в ПКС мы не можем управлять отказом от записи всего документа на основании анализа всех строк ТЧ.

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


// Извлекаем параметры, переданные из источника через ПКС
ТЧСписокДефектовПараметры = ПараметрыОбъекта["СписокДефектовТабличнаяЧасть"];

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

        // Логика обработки согласно условиям задачи
        Если НЕ ЗначениеЗаполнено(СтруктураПараметровРодителя.КодРодителя) Тогда 
            // Сценарий 3 и 4: Родитель в источнике не указан
            Если ЗначениеЗаполнено(СтруктураПараметровПодчиненного.КодЭлемента) Тогда 
                // Пытаемся найти родителя в иерархии ТОиР 2.0
                Запрос = Новый Запрос;
                Запрос.Текст = 
                "ВЫБРАТЬ
                |   торо_ИерархическиеСтруктурыОР.РодительИерархии КАК РодительОбъекта
                |ИЗ
                |   РегистрСведений.торо_ИерархическиеСтруктурыОР КАК торо_ИерархическиеСтруктурыОР
                |ГДЕ
                |   торо_ИерархическиеСтруктурыОР.ОбъектИерархии = &ОбъектРемонта";
                
                Запрос.УстановитьПараметр("ОбъектРемонта", СтрокаДефектов.ОтказавшийЭлемент);
                
                Результат = Запрос.Выполнить();
                Выборка = Результат.Выбрать();
                
                Если Выборка.Следующий() Тогда
                    // Успех: нашли родителя в приемнике
                    СтрокаДефектов.ОбъектРемонта = Выборка.РодительОбъекта; 
                Иначе
                    // Родитель не найден ни в источнике, ни в приемнике - удаляем строку
                    МассивКУдалению.Добавить(СтрокаДефектов);
                КонецЕсли;    
            Иначе
                // Ничего не заполнено - удаляем строку
                МассивКУдалению.Добавить(СтрокаДефектов);
            КонецЕсли;
        Иначе 
            // Сценарий 1 и 2: Родитель заполнен
            Если НЕ ЗначениеЗаполнено(СтруктураПараметровПодчиненного.КодЭлемента) Тогда 
                // Дочерний не заполнен - дублируем родителя (согласно условию)
                СтрокаДефектов.ОтказавшийЭлемент = СтрокаДефектов.ОбъектРемонта;
            КонецЕсли;
        КонецЕсли;         
    КонецЦикла;
    
    // Очищаем табличную часть от неподходящих строк
    Для Каждого СтрКУдалению Из МассивКУдалению Цикл
        Объект.СписокДефектов.Удалить(СтрКУдалению);
    КонецЦикла;
КонецЕсли;

Шаг 3: Отмена загрузки пустого документа

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


Если Объект.СписокДефектов.Количество() = 0 Тогда
    // Сообщаем пользователю (опционально) и отменяем загрузку объекта
    Сообщить("Документ " + Объект + " пропущен: отсутствуют подходящие объекты ремонта.");
    Отказ = Истина;
КонецЕсли;

Важно помнить, что установка Отказ = Истина в этом обработчике предотвращает вызов метода Записать(), и объект не попадет в информационную базу приемника.

Рекомендации по оптимизации производительности

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

  1. Пакетная обработка: Вместо запроса в цикле соберите все ссылки на ОтказавшийЭлемент в массив и выполните один запрос ко всему регистру иерархии перед началом цикла. Результат запроса выгрузите в Соответствие, где ключом будет ссылка на объект, а значением — его родитель.
  2. Фильтрация структур иерархии: В ТОиР 2.0 регистр иерархии может содержать много разных типов структур (функциональная, организационная). В запросе обязательно добавляйте фильтр по измерению СтруктураИерархии, чтобы не получить случайного "родителя" из другой ветки учета.
  3. Использование кеширования: Если данные об иерархии часто повторяются, сохраняйте результаты поиска в глобальной переменной (в рамках сессии загрузки), чтобы не обращаться к базе данных повторно для одних и тех же объектов.

Проанализировав данную методику, мы видим, что она позволяет гибко управлять данными "на лету", используя мощь встроенного языка 1С и механизмов КД 2.0. Это исключает необходимость предварительной трудоемкой обработки данных в базе-источнике и гарантирует чистоту данных в целевой системе ТОиР 2.0.

← На главную