При переходе с старых конфигураций на новые мы часто сталкиваемся с изменением архитектуры хранения данных. Классический пример — перенос остатков взаиморасчетов из "Управление торговлей 10.3" (УТ 10.3) в "Управление торговлей 11" (УТ 11). В УТ 10.3 документ корректировки долга или ввода остатков может содержать несколько табличных частей (например, отдельно "Расчеты" и отдельно "Авансы"), тогда как в УТ 11 архитектура требует разделения этих операций по разным документам или по разным видам операций (Хозяйственным операциям).
Рассмотрим подробно, как реализовать схему переноса "один ко многим" в "Конвертации данных 2.0" (КД 2), избежав типичных ошибок дублирования и затирания данных.
Имеем документ-источник в базе 1С 10.3, который содержит две табличные части:
РасчетыСКонтрагентами — отражает долги.Авансы — отражает предоплаты.Нам необходимо перенести эти данные в 1С 11.5. Особенность приемника в том, что там используется один вид документа, но с разной ХозяйственнаяОперация для авансов и долгов, либо вообще два разных вида документов. Если попытаться перенести документ "как есть", мы столкнемся с невозможностью корректно разнести данные по разным логическим сущностям.
Давайте разберем подход, который интуитивно выбирают многие разработчики и который приводит к ошибкам. Логика часто строится так: создается два Правила Конвертации Объектов (ПКО) и в коде выгрузки вызывается метод ВыгрузитьПоПравилу дважды для одного и того же объекта-источника.
// ОШИБОЧНЫЙ ПОДХОД
// Выгрузка долгов
Если Источник.РасчетыСКонтрагентами.Количество() > 0 Тогда
ВыгрузитьПоПравилу(Объект, , , , "ПКО_ВводОстатков_Долг");
КонецЕсли;
// Выгрузка авансов
Если Источник.Авансы.Количество() > 0 Тогда
ВыгрузитьПоПравилу(Объект, , , , "ПКО_ВводОстатков_Аванс");
КонецЕсли;
Почему это приводит к проблеме? Если использовать стандартный механизм, платформа при выгрузке берет УникальныйИдентификатор (UID) источника и присваивает его объекту в приемнике.
В результате, когда отрабатывает первая выгрузка, в приемнике создается документ с UID_1. Когда отрабатывает вторая выгрузка, система снова пытается создать документ с UID_1. В зависимости от настроек поиска, это приведет либо к ошибке неуникальности, либо (что чаще) второй документ просто "затрет" первый, или данные табличных частей перемешаются. В итоге вы получите один документ вместо двух, или документ с неверными данными.
Наиболее профессиональный и гибкий способ решения такой задачи — отказаться от стандартной выборки объектов в Правилах Выгрузки Данных (ПВД) и использовать произвольный алгоритм.
Разберем этот метод по шагам.
В правиле выгрузки данных выбираем "Произвольный алгоритм" (способ выборки). Весь код пишем в обработчике ПередОбработкой. Наша цель — одним запросом собрать данные так, чтобы каждая строка результата запроса представляла собой будущий документ в приемнике.
Сформируем запрос, который объединит данные из двух табличных частей источника, добавив признак операции.
// Примерный алгоритм в ПВД "ПередОбработкой"
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ост.Ссылка КАК ДокументОснование,
| Ост.Организация,
| Ост.Контрагент,
| Ост.Дата,
| ""Долг"" КАК ВидОперацииПереноса,
| Ост.РасчетыСКонтрагентами.(
| СуммаВзаиморасчетов,
| ДоговорКонтрагента
| ) КАК ТабличнаяЧастьДанные
|ИЗ
| Документ.ВводНачальныхОстатков КАК Ост
|ГДЕ
| Ост.Дата МЕЖДУ &ДатаНач И &ДатаКон
| И НЕ Ост.ПометкаУдаления
| И Ост.РасчетыСКонтрагентами.Ссылка ЕСТЬ НЕ NULL
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| Ост.Ссылка,
| Ост.Организация,
| Ост.Контрагент,
| Ост.Дата,
| ""Аванс"",
| Ост.Авансы.(
| СуммаВзаиморасчетов,
| ДоговорКонтрагента
| )
|ИЗ
| Документ.ВводНачальныхОстатков КАК Ост
|ГДЕ
| Ост.Дата МЕЖДУ &ДатаНач И &ДатаКон
| И НЕ Ост.ПометкаУдаления
| И Ост.Авансы.Ссылка ЕСТЬ НЕ NULL";
// Установка параметров периода
Запрос.УстановитьПараметр("ДатаНач", ДатаНачала);
Запрос.УстановитьПараметр("ДатаКон", ДатаОкончания);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Подготовим структуру входящих данных для ПКО
ВходящиеДанные = Новый Структура;
ВходящиеДанные.Вставить("ВидОперации", Выборка.ВидОперацииПереноса);
ВходящиеДанные.Вставить("Организация", Выборка.Организация);
ВходящиеДанные.Вставить("Контрагент", Выборка.Контрагент);
// Выгружаем табличную часть в список/таблицу значений во входящие данные
ВходящиеДанные.Вставить("ТЧ_Данные", Выборка.ТабличнаяЧастьДанные.Выгрузить());
// Используем ВходящиеДанные для передачи в правило
// Важно: Первым параметром передаем Неопределено или Ссылку (для генерации GUID),
// но данные берем из ВходящихДанных
ВыгрузитьПоПравилу(Выборка.ДокументОснование, , ВходящиеДанные, , "ПКО_ВводОстатков_Приемник");
КонецЦикла;
Теперь настроим Правило Конвертации Объектов (ПКО). Поскольку мы передаем данные через структуру ВходящиеДанные, в настройках ПКО (Правила конвертации свойств — ПКС) нужно указать, откуда брать значения.
ПередВыгрузкой этого свойства указываем источник данных:
// ПКС для Табличной Части
// Источник данных у нас лежит во ВходящиеДанные
Значение = ВходящиеДанные.ТЧ_Данные;
Таким образом, мы отвязываемся от структуры объекта-источника и полностью контролируем процесс формирования данных.
Самый важный момент при разделении одного документа на два — это уникальный идентификатор. Если мы просто передадим Выборка.ДокументОснование (ссылку) в функцию ВыгрузитьПоПравилу, платформа попытается использовать GUID этого документа.
Чтобы создать два разных документа, нам нужно манипулировать ключом поиска или идентификатором.
Вариант А: Модификация полей поиска
В ПКО приемника установите галочки поиска не только по номеру и дате, но и по специальному реквизиту, например, Комментарий или ХозяйственнаяОперация.
В ПВД, перед выгрузкой, заполняйте этот реквизит:
Если Выборка.ВидОперацииПереноса = "Аванс" Тогда
ВходящиеДанные.Вставить("Комментарий", "Перенос авансов: " + Строка(Выборка.ДокументОснование.Номер));
Иначе
ВходящиеДанные.Вставить("Комментарий", "Перенос долгов: " + Строка(Выборка.ДокументОснование.Номер));
КонецЕсли;
Вариант Б: Формирование уникального ключа выгрузки (Рекомендуемый)
В обработчике ПКО ПередВыгрузкой или еще в ПВД измените строковое представление UID, добавив к нему суффикс.
// В ПВД внутри цикла, перед ВыгрузитьПоПравилу
// Генерируем "фейковый" уникальный ключ на основе реального GUID и суффикса
УИД_Строка = Строка(Выборка.ДокументОснование.УникальныйИдентификатор());
Если Выборка.ВидОперацииПереноса = "Аванс" Тогда
// Меняем последний символ или добавляем префикс, чтобы GUID стал другим,
// но воспроизводимым (чтобы при повторной выгрузке не плодились дубли)
// Внимание: Это упрощенный пример. Правильнее использовать соответствие или алгоритм хеширования
УникальныйИдентификаторПриемника = Новый УникальныйИдентификатор( ...алгоритм модификации... );
Иначе
УникальныйИдентификаторПриемника = Выборка.ДокументОснование.УникальныйИдентификатор();
КонецЕсли;
// Передаем этот GUID в параметры выгрузки (параметр Ссылка в ВыгрузитьПоПравилу не передаст GUID напрямую, если объект не найден)
// Надежнее всего управлять поиском через поля поиска.
Однако, самый простой и надежный способ для документов ввода остатков — использовать поля поиска Организация + ВидОперации + ДокументОснование (если есть реквизит) + Дата. В этом случае КД сама поймет, что это разные документы.
В конфигурациях на базе БСП и ERP (УТ 11, КА, ERP) поведение формы и табличных частей документа часто зависит от реквизита ХозяйственнаяОперация. При конвертации его нужно заполнять обязательно.
В нашем ПКО создадим ПКС для свойства ХозяйственнаяОперация. В обработчике ПередВыгрузкой напишем:
// ВходящиеДанные.ВидОперации мы заполнили в ПВД
Если ВходящиеДанные.ВидОперации = "Аванс" Тогда
Значение = "ВводОстатковАвансовКлиентов"; // Имя предопределенного элемента или перечисления
Иначе
Значение = "ВводОстатковЗадолженностиКлиентов";
КонецЕсли;
Давайте подведем итог. Чтобы успешно разделить один документ на два, следуйте этому алгоритму:
ОБЪЕДИНИТЬ ВСЕ для сбора данных из разных ТЧ источника.ВыгрузитьПоПравилу, передавая туда структуру ВходящиеДанные.Этот метод гарантирует, что вы получите корректные данные в приемнике, избежите пустых документов (так как запрос отсечет пустые ТЧ через ВНУТРЕННЕЕ СОЕДИНЕНИЕ или условия ГДЕ) и будете полностью контролировать процесс миграции.