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