Как в КД 2.0 перенести один документ источника в два разных документа приемника?

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

При переходе с старых конфигураций на новые мы часто сталкиваемся с изменением архитектуры хранения данных. Классический пример — перенос остатков взаиморасчетов из "Управление торговлей 10.3" (УТ 10.3) в "Управление торговлей 11" (УТ 11). В УТ 10.3 документ корректировки долга или ввода остатков может содержать несколько табличных частей (например, отдельно "Расчеты" и отдельно "Авансы"), тогда как в УТ 11 архитектура требует разделения этих операций по разным документам или по разным видам операций (Хозяйственным операциям).

Рассмотрим подробно, как реализовать схему переноса "один ко многим" в "Конвертации данных 2.0" (КД 2), избежав типичных ошибок дублирования и затирания данных.

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

Имеем документ-источник в базе 1С 10.3, который содержит две табличные части:

  1. РасчетыСКонтрагентами — отражает долги.
  2. Авансы — отражает предоплаты.

Нам необходимо перенести эти данные в 1С 11.5. Особенность приемника в том, что там используется один вид документа, но с разной ХозяйственнаяОперация для авансов и долгов, либо вообще два разных вида документов. Если попытаться перенести документ "как есть", мы столкнемся с невозможностью корректно разнести данные по разным логическим сущностям.

Анализ распространенной ошибки

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


// ОШИБОЧНЫЙ ПОДХОД
// Выгрузка долгов
Если Источник.РасчетыСКонтрагентами.Количество() > 0 Тогда
    ВыгрузитьПоПравилу(Объект, , , , "ПКО_ВводОстатков_Долг");
КонецЕсли;

// Выгрузка авансов
Если Источник.Авансы.Количество() > 0 Тогда
    ВыгрузитьПоПравилу(Объект, , , , "ПКО_ВводОстатков_Аванс");
КонецЕсли;

Почему это приводит к проблеме? Если использовать стандартный механизм, платформа при выгрузке берет УникальныйИдентификатор (UID) источника и присваивает его объекту в приемнике.

В результате, когда отрабатывает первая выгрузка, в приемнике создается документ с UID_1. Когда отрабатывает вторая выгрузка, система снова пытается создать документ с UID_1. В зависимости от настроек поиска, это приведет либо к ошибке неуникальности, либо (что чаще) второй документ просто "затрет" первый, или данные табличных частей перемешаются. В итоге вы получите один документ вместо двух, или документ с неверными данными.

Правильное решение: Выборка данных запросом в ПВД

Наиболее профессиональный и гибкий способ решения такой задачи — отказаться от стандартной выборки объектов в Правилах Выгрузки Данных (ПВД) и использовать произвольный алгоритм.

Разберем этот метод по шагам.

Шаг 1. Настройка ПВД

В правиле выгрузки данных выбираем "Произвольный алгоритм" (способ выборки). Весь код пишем в обработчике ПередОбработкой. Наша цель — одним запросом собрать данные так, чтобы каждая строка результата запроса представляла собой будущий документ в приемнике.

Сформируем запрос, который объединит данные из двух табличных частей источника, добавив признак операции.


// Примерный алгоритм в ПВД "ПередОбработкой"

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Ост.Ссылка КАК ДокументОснование,
    |   Ост.Организация,
    |   Ост.Контрагент,
    |   Ост.Дата,
    |   ""Долг"" КАК ВидОперацииПереноса,
    |   Ост.РасчетыСКонтрагентами.(
    |       СуммаВзаиморасчетов,
    |       ДоговорКонтрагента
    |   ) КАК ТабличнаяЧастьДанные
    |ИЗ
    |   Документ.ВводНачальныхОстатков КАК Ост
    |ГДЕ
    |   Ост.Дата МЕЖДУ &ДатаНач И &ДатаКон
    |   И НЕ Ост.ПометкаУдаления
    |   И Ост.РасчетыСКонтрагентами.Ссылка ЕСТЬ НЕ NULL
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |   Ост.Ссылка,
    |   Ост.Организация,
    |   Ост.Контрагент,
    |   Ост.Дата,
    |   ""Аванс"",
    |   Ост.Авансы.(
    |       СуммаВзаиморасчетов,
    |       ДоговорКонтрагента
    |   )
    |ИЗ
    |   Документ.ВводНачальныхОстатков КАК Ост
    |ГДЕ
    |   Ост.Дата МЕЖДУ &ДатаНач И &ДатаКон
    |   И НЕ Ост.ПометкаУдаления
    |   И Ост.Авансы.Ссылка ЕСТЬ НЕ NULL";

// Установка параметров периода
Запрос.УстановитьПараметр("ДатаНач", ДатаНачала);
Запрос.УстановитьПараметр("ДатаКон", ДатаОкончания);

Выборка = Запрос.Выполнить().Выбрать();

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

Шаг 2. Использование ВходящихДанных в ПКО

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


// ПКС для Табличной Части
// Источник данных у нас лежит во ВходящиеДанные
Значение = ВходящиеДанные.ТЧ_Данные;

Таким образом, мы отвязываемся от структуры объекта-источника и полностью контролируем процесс формирования данных.

Решение проблемы уникальности (UID)

Самый важный момент при разделении одного документа на два — это уникальный идентификатор. Если мы просто передадим Выборка.ДокументОснование (ссылку) в функцию ВыгрузитьПоПравилу, платформа попытается использовать GUID этого документа.

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

Вариант А: Модификация полей поиска
В ПКО приемника установите галочки поиска не только по номеру и дате, но и по специальному реквизиту, например, Комментарий или ХозяйственнаяОперация. В ПВД, перед выгрузкой, заполняйте этот реквизит:


Если Выборка.ВидОперацииПереноса = "Аванс" Тогда
    ВходящиеДанные.Вставить("Комментарий", "Перенос авансов: " + Строка(Выборка.ДокументОснование.Номер));
Иначе
    ВходящиеДанные.Вставить("Комментарий", "Перенос долгов: " + Строка(Выборка.ДокументОснование.Номер));
КонецЕсли;

Вариант Б: Формирование уникального ключа выгрузки (Рекомендуемый)
В обработчике ПКО ПередВыгрузкой или еще в ПВД измените строковое представление UID, добавив к нему суффикс.


// В ПВД внутри цикла, перед ВыгрузитьПоПравилу
// Генерируем "фейковый" уникальный ключ на основе реального GUID и суффикса
УИД_Строка = Строка(Выборка.ДокументОснование.УникальныйИдентификатор());
Если Выборка.ВидОперацииПереноса = "Аванс" Тогда
    // Меняем последний символ или добавляем префикс, чтобы GUID стал другим, 
    // но воспроизводимым (чтобы при повторной выгрузке не плодились дубли)
    // Внимание: Это упрощенный пример. Правильнее использовать соответствие или алгоритм хеширования
    УникальныйИдентификаторПриемника = Новый УникальныйИдентификатор( ...алгоритм модификации... );
Иначе
    УникальныйИдентификаторПриемника = Выборка.ДокументОснование.УникальныйИдентификатор();
КонецЕсли;

// Передаем этот GUID в параметры выгрузки (параметр Ссылка в ВыгрузитьПоПравилу не передаст GUID напрямую, если объект не найден)
// Надежнее всего управлять поиском через поля поиска.

Однако, самый простой и надежный способ для документов ввода остатков — использовать поля поиска Организация + ВидОперации + ДокументОснование (если есть реквизит) + Дата. В этом случае КД сама поймет, что это разные документы.

Настройка Хозяйственной Операции в УТ 11

В конфигурациях на базе БСП и ERP (УТ 11, КА, ERP) поведение формы и табличных частей документа часто зависит от реквизита ХозяйственнаяОперация. При конвертации его нужно заполнять обязательно.

В нашем ПКО создадим ПКС для свойства ХозяйственнаяОперация. В обработчике ПередВыгрузкой напишем:


// ВходящиеДанные.ВидОперации мы заполнили в ПВД
Если ВходящиеДанные.ВидОперации = "Аванс" Тогда
    Значение = "ВводОстатковАвансовКлиентов"; // Имя предопределенного элемента или перечисления
Иначе
    Значение = "ВводОстатковЗадолженностиКлиентов";
КонецЕсли;

Резюме решения

Давайте подведем итог. Чтобы успешно разделить один документ на два, следуйте этому алгоритму:

  1. Создайте одно ПВД с произвольным алгоритмом выборки.
  2. В ПВД напишите запрос, который возвращает строки данных (одна строка = один будущий документ). Используйте ОБЪЕДИНИТЬ ВСЕ для сбора данных из разных ТЧ источника.
  3. В цикле обхода выборки формируйте структуру ВходящиеДанные, куда помещайте выгрузку табличной части и ключевые поля шапки.
  4. Вызывайте ВыгрузитьПоПравилу, передавая туда структуру ВходящиеДанные.
  5. В ПКО настройте получение свойств из входящих данных и обязательно настройте поля поиска так, чтобы система различала документ с авансами и документ с долгами (например, по типу операции).

Этот метод гарантирует, что вы получите корректные данные в приемнике, избежите пустых документов (так как запрос отсечет пустые ТЧ через ВНУТРЕННЕЕ СОЕДИНЕНИЕ или условия ГДЕ) и будете полностью контролировать процесс миграции.

← На главную