Рассмотрим распространенную проблему при настройке правил обмена в "Конвертации данных 2.0/2.1" (КД). Ситуация, с которой часто сталкиваются разработчики, выглядит следующим образом: при выгрузке документа (например, "Ввод начальных остатков" или "Поступление материалов") из базы-источника данные уходят корректно. Однако в базе-приемнике, несмотря на то, что документ создается, поля счетов в табличных частях (ТЧ) оказываются пустыми.
Это приводит к критической ошибке при попытке записи движений документа (хотя сама выгрузка движений документа XML может проходить без ошибок). В журнале регистрации или окне сообщений мы видим ошибку:
Ошибка при вызове метода контекста (Записать): Не удалось записать: "Журнал проводок (ЕПСБУ)"!
Проводка 1. Не заполнен счет дебета
Разберем подробно причины этого поведения, почему отладчик может вводить в заблуждение, и рассмотрим два способа решения (по методике конвертации данных 2): "быстрый" (через код в обработчиках) и "правильный" (через передачу параметров).
В описанной ситуации (особенно характерной для бюджетного учета — БГУ, план счетов ЕПСБУ) происходит следующее:
ПослеЗагрузки, можно увидеть, что в объекте документа в ТЧ "Материалы" поле "Счет" заполнено объектом типа ПланСчетовСсылка.Причина кроется в механизме поиска ссылок (ПКО).
Когда система загружает данные, она пытается найти счет в базе-приемнике. В правилах конвертации объектов (ПКО) для плана счетов обычно стоят флаги поиска "По коду" и/или "По наименованию".
ПередЗаписью, которые принудительно очищают некорректно заполненные счета учета, если не заполнены зависимые реквизиты (КФО, КПС, КЭК).Если стандартный поиск по ПКО не срабатывает из-за различий в планах счетов (например, разная структура субсчетов или предопределенных элементов), можно явно указать соответствие счетов в обработчике ПослеЗагрузкиОбъекта в правилах для Документа.
Этот метод требует написания кода, но позволяет жестко контролировать, какой счет будет подставлен.
Рассмотрим пример кода, который нужно разместить в ПКО документа "Ввод начальных остатков" на вкладке Обработчики событий -> После загрузки.
// Определяем переменную для хранения найденного счета
СчетЕПСБУ = ПланыСчетов.ЕПСБУ.ПустаяСсылка();
// Логика сопоставления.
// Объект.Счет - это счет из шапки документа (обычно групповой, например 105.00)
// Нам нужно определить детальный счет для строк (например 105.36)
Если Объект.Счет.Код = "105.00" Тогда
// Ищем конкретный счет в базе-приемнике
СчетЕПСБУ = ПланыСчетов.ЕПСБУ.НайтиПоКоду("105.36");
ИначеЕсли Объект.Счет.Код = "07" Тогда
СчетЕПСБУ = ПланыСчетов.ЕПСБУ.НайтиПоКоду("07.2");
ИначеЕсли Объект.Счет.Код = "01.32" Тогда
// Если коды совпадают, все равно ищем явно, чтобы исключить ошибки ссылок
СчетЕПСБУ = ПланыСчетов.ЕПСБУ.НайтиПоКоду("01.32");
КонецЕсли;
// Если счет найден, заполняем табличную часть
Если ЗначениеЗаполнено(СчетЕПСБУ) Тогда
// 1. Заполняем табличную часть "Материалы"
Для каждого СтрокаТЧ Из Объект.Материалы Цикл
// Можно заполнять всегда или только если пусто
Если НЕ ЗначениеЗаполнено(СтрокаТЧ.Счет) Тогда
СтрокаТЧ.Счет = СчетЕПСБУ;
КонецЕсли;
КонецЦикла;
// 2. Исправляем движения (проводки), так как ошибка возникала именно там
// Выгружаем набор записей в таблицу значений для обработки
ТаблицаДвижений = Объект.Движения.ЖурналПроводокЕПСБУ.Выгрузить();
Для каждого Проводка Из ТаблицаДвижений Цикл
// Заполняем счет дебета (или кредита, в зависимости от операции)
Проводка.СчетДт = СчетЕПСБУ;
КонецЦикла;
// Загружаем исправленные движения обратно в набор
Объект.Движения.ЖурналПроводокЕПСБУ.Загрузить(ТаблицаДвижений);
КонецЕсли;
Важно: При таком подходе необходимо установить у документа режим записи, который не перезаписывает движения автоматически, либо убедиться, что ваша логика выполняется после формирования движений системой.
Более продвинутый способ — не "хардкодить" счета в приемнике (ведь коды могут меняться), а передать реальные коды счетов из базы-источника в базу-приемник через параметры конвертации.
Этот метод позволяет передать произвольную таблицу данных вместе с объектом.
В ПКО документа на стороне источника, в событии ПередВыгрузкой, соберем коды счетов из табличной части в структуру и поместим её в ИсходящиеДанные.
// Создаем таблицу для передачи информации о счетах
ТЗ_Счета = Новый ТаблицаЗначений;
ТЗ_Счета.Колонки.Добавить("НомерСтроки");
ТЗ_Счета.Колонки.Добавить("КодСчета");
// Обходим табличную часть источника
Для Каждого Стр Из Источник.Материалы Цикл
НовСтр = ТЗ_Счета.Добавить();
НовСтр.НомерСтроки = Стр.НомерСтроки;
// Нам важен именно текстовый код, так как ссылка не перенесется напрямую
НовСтр.КодСчета = Стр.Счет.Код;
КонецЦикла;
// Передаем таблицу в параметры, которые уйдут в XML
// В КД 2 можно использовать ИсходящиеДанные, если настроено ПКС,
// либо передавать через параметры узла, но надежнее через свойства.
// Создадим фиктивное свойство (см. ниже) или используем ВходящиеДанные приемника.
ВходящиеДанные = Новый Структура("ТаблицаСчетовМатериалов", ТЗ_Счета);
Чтобы ВходящиеДанные передались, в правилах конвертации свойств (ПКС) документа можно создать фиктивное свойство. Но проще воспользоваться механизмом ПараметрыОбъекта.
В обработчике После загрузки в приемнике параметры из файла обмена доступны через структуру ПараметрыОбъекта (если они были туда помещены при выгрузке).
Теперь в базе-приемнике мы можем прочитать эти коды и найти соответствующие счета.
// Проверяем, передали ли мы таблицу
Если ПараметрыОбъекта <> Неопределено И ПараметрыОбъекта.Свойство("ТаблицаСчетовМатериалов") Тогда
ТЗ_Счета = ПараметрыОбъекта.ТаблицаСчетовМатериалов;
Для Каждого СтрТЧ Из Объект.Материалы Цикл
// Ищем строку в переданной таблице по номеру строки
НайденнаяСтрока = ТЗ_Счета.Найти(СтрТЧ.НомерСтроки, "НомерСтроки");
Если НайденнаяСтрока <> Неопределено Тогда
КодСчетаИсточника = НайденнаяСтрока.КодСчета;
// Пытаемся найти счет в текущей базе по коду
// Важно: Поиск должен быть точным
НайдСчет = ПланыСчетов.ЕПСБУ.НайтиПоКоду(КодСчетаИсточника);
Если ЗначениеЗаполнено(НайдСчет) Тогда
СтрТЧ.Счет = НайдСчет;
Иначе
// Если по полному коду не нашли, можно искать родителя или аналог
// Сообщить("Не найден счет: " + КодСчетаИсточника);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Аналогично обновляем движения, если они уже сформированы
// ... см. код из Решения 1 для движений ...
КонецЕсли;
Основная ошибка "Не заполнен счет дебета" возникает из-за того, что при конвертации ссылка на счет либо не находится, либо не соответствует типу счета (группа/элемент) в приемнике, и платформа очищает её перед записью набора записей регистра.
Для исправления используйте один из методов:
ПослеЗагрузки, если структура планов счетов в базах различается.