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