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