Синхронизация нескольких адресов электронной почты между УТ 11.4 и БП 3.0: устраняем затирание данных

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет IT и автоматизация бизнеса
← На главную

При настройке типовой синхронизации данных между конфигурациями 1С:Управление торговлей 11.4 (УТ) и 1С:Бухгалтерия предприятия 3.0 (БП) через Универсальный формат (EnterpriseData) часто возникает неприятная проблема. Если у одного контрагента зарегистрировано несколько адресов электронной почты (или других однотипных контактных данных, например, телефонов), при обмене переносится только один — самый последний из списка. При этом ранее сохраненные адреса в базе-приемнике бесследно затираются.

В этой статье мы подробно разберем, почему возникает эта ошибка, как устроены внутренние механизмы Библиотеки стандартных подсистем (БСП) в части работы с контактной информацией и как решить эту проблему раз и навсегда с помощью механизмов расширений в обеих базах.

Шаг 1. Проверяем базовые настройки в пользовательском интерфейсе

Прежде чем приступать к доработке программного кода, нам необходимо убедиться в том, что в обеих конфигурациях включена системная возможность хранения нескольких значений для одного вида контактной информации. Без этой настройки СУБД на уровне платформы заблокирует запись дублирующих видов КИ.

Выполним проверку по следующему алгоритму в каждой из баз (УТ и БП):

  1. Перейдем в раздел НСИ и администрирование (или Администрирование в БП) — Общие настройки.
  2. Раскроем группу Контактная информация и перейдем по гиперссылке Виды контактной информации.
  3. В появившемся списке найдем группу Контрагенты (а в УТ — также группу Партнеры).
  4. Откроем карточку нужного нам вида КИ (в нашем случае это Электронная почта или Email).
  5. Убедимся, что установлен флаг Разрешить ввод нескольких значений. Если флаг отсутствует, включим его и сохраним изменения.

Если этот флаг не установлен хотя бы в одной из баз, стандартная функция БСП ЗапрещенВводНесколькихЗначений() будет принудительно отсекать все адреса, кроме первого, даже если мы исправим код обмена.

Шаг 2. Разбор проблемы затирания данных при загрузке в 1С:Бухгалтерию

Проанализируем файл выгрузки XML, который формирует база-источник. Мы увидим, что в пакете данных передаются абсолютно все почтовые ящики, привязанные к контрагенту. Однако при загрузке в БП сохраняется только последний. Почему так происходит?

Обратимся к программному коду конфигурации БП 3.0. В модуле менеджера обмена МенеджерОбменаЧерезУниверсальныйФормат13 (или актуальной версии формата в вашей конфигурации) за импорт контактной информации отвечает процедура ЗаполнитьКонтактнуюИнформациюВПолученныхДанных. В самом конце этой процедуры вызывается стандартный метод БСП:


УправлениеКонтактнойИнформацией.ДобавитьКонтактнуюИнформацию(ПолученныеДанные, СокрЛП(СтрокаXDTO.ЗначенияПолей), ТекВидКИ);

Посмотрим на сигнатуру метода ДобавитьКонтактнуюИнформацию в общем модуле БСП:


Процедура ДобавитьКонтактнуюИнформацию(СсылкаИлиОбъект, ЗначениеИлиПредставление, ВидКонтактнойИнформации, Дата = Неопределено, Замещать = Истина) Экспорт

Обратим внимание на последний параметр: Замещать = Истина. По умолчанию метод работает в режиме жесткого замещения. Это означает, что при обработке каждой строки из входящего пакета XML система сначала полностью очищает всю контактную информацию данного вида у объекта, а затем записывает новую. В результате циклического обхода коллекции КИ каждая последующая строка просто затирает предыдущую, и в базе остается только самая последняя запись.

Для исправления этой логики в БП нам нужно явно передавать Ложь в параметр Замещать. Реализуем это через расширение конфигурации.

Создадим расширение для БП и добавим в него процедуру загрузки с директивой &Вместо:


&Вместо("ЗаполнитьКонтактнуюИнформациюВПолученныхДанных")
Процедура Расш_ЗаполнитьКонтактнуюИнформациюВПолученныхДанных(ПолученныеДанные, ДанныеXDTO)
    // Копируем стандартный код подготовки данных из типовой процедуры...
    // В месте вызова записи КИ меняем параметр "Замещать" на Ложь:
    УправлениеКонтактнойИнформацией.ДобавитьКонтактнуюИнформацию(ПолученныеДанные, СокрЛП(СтрокаXDTO.ЗначенияПолей), ТекВидКИ, , Ложь);
КонецПроцедуры

Теперь при обмене новые адреса электронной почты будут добавляться в табличную часть контрагента, не уничтожая уже имеющиеся записи.

Шаг 3. Исправление загрузки контактной информации в 1С:Управление торговлей

В конфигурации УТ 11.4 ситуация аналогична, но имеет свою специфику. Нам необходимо скорректировать общий модуль МенеджерОбменаЧерезУниверсальныйФормат, переопределив процедуру ЗагрузитьКонтактнуюИнформацию.

Создадим расширение для УТ 11.4 и добавим следующий код, заменяющий стандартный метод загрузки КИ:


&Вместо("ЗагрузитьКонтактнуюИнформацию")
Процедура Расш_ЗагрузитьКонтактнуюИнформацию(ДанныеXDTO, ПолученныеДанные, ГруппаВидовКИ)
    Если Не (ДанныеXDTO.Свойство("КонтактнаяИнформация") И ЗначениеЗаполнено(ДанныеXDTO.КонтактнаяИнформация)) Тогда
        Возврат;
    КонецЕсли;
    
    Для Каждого СтрокаXDTO Из ДанныеXDTO.КонтактнаяИнформация Цикл
        ВидКИСтрокой = СокрЛП(СтрокаXDTO.ВидКонтактнойИнформации.Значение);
        НаименованиеКИ = СокрЛП(СтрокаXDTO.НаименованиеКонтактнойИнформации);
        ТекВидКИ = ВидКонтактнойИнформацииИзСтроки(ВидКИСтрокой, НаименованиеКИ, ГруппаВидовКИ);
        
        Если Не ЗначениеЗаполнено(ТекВидКИ) Тогда
            Продолжить;
        КонецЕсли;
        
        // Вызываем метод БСП с отключенным замещением (параметр Замещать = Ложь)
        УправлениеКонтактнойИнформацией.ДобавитьКонтактнуюИнформацию(ПолученныеДанные, СокрЛП(СтрокаXDTO.ЗначенияПолей), ТекВидКИ, , Ложь);
    КонецЦикла;
КонецПроцедуры

Этот код успешно отработает и запишет все адреса в базу данных. Мы можем легко проверить это с помощью консоли запросов — все записи физически появятся в табличной части КонтактнаяИнформация справочника Контрагенты. Однако здесь нас поджидает еще одна архитектурная особенность УТ, из-за которой в интерфейсе карточки контрагента по-прежнему будет отображаться только один email.

Шаг 4. Устранение логической ошибки синхронизации «Партнеры — Контрагенты» в УТ 11

Выясним причину, почему при наличии записей в базе данных интерфейс УТ упорно скрывает вторую и последующие почты. В отличие от БП, где сущность контрагента является единственной, в УТ реализовано разделение на:

Контактная информация хранится параллельно как у Партнера, так и у Контрагента. При изменении или загрузке контрагента запускается отложенный обработчик ОтложеннаяОбработка_Контрагенты, который синхронизирует КИ контрагента с КИ связанного с ним партнера.

Проанализируем типовой код этой процедуры. Разработчики "1С" допустили логическую ошибку, объединив очистку старых данных партнера и запись новых в один цикл:


// ТИПОВОЙ ОШИБОЧНЫЙ КОД:
Для Каждого СтрокаКИКонтрагента Из Объект.КонтактнаяИнформация Цикл
    ВидКИПартнера = ВидКИПартнераПоВидуКИКонтрагента(СтрокаКИКонтрагента.Вид, ИспользоватьПартнеровКакКонтрагентов);
    Если ВидКИПартнера = Неопределено Тогда
        Продолжить;
    КонецЕсли;
    
    // На каждой итерации ищется и удаляется КИ партнера данного вида!
    СтрокиКИПартнера = ПартнерОбъект.ContactИнформация.НайтиСтроки(Новый Структура("Вид", ВидКИПартнера));
    Для Каждого СтрокаКИПартнера Из СтрокиКИПартнера Цикл
        ПартнерОбъект.КонтактнаяИнформация.Удалить(СтрокаКИПартнера);
    КонецЦикла;
    
    СтрокаКИПартнера = ПартнерОбъект.КонтактнаяИнформация.Добавить;
    ЗаполнитьЗначенияСвойств(СтрокаКИПартнера, СтрокаКИКонтрагента);
    СтрокаКИПартнера.Вид = ВидКИПартнера;
КонецЦикла;

В чем заключается ошибка? Когда у контрагента есть два email-адреса, на первой итерации цикла система находит у партнера старый email, удаляет его и добавляет первый новый email. На второй итерации цикла система обрабатывает второй email контрагента, снова ищет у партнера КИ данного вида, находит только что добавленный на первом шаге первый email, благополучно удаляет его и записывает второй email. В итоге у связанного партнера всегда остается только одна (последняя) запись!

Чтобы исправить это поведение, нам нужно разделить алгоритм на два независимых этапа:

  1. Сначала полностью очистить у партнера все записи тех видов контактной информации, которые присутствуют в пакете изменений контрагента.
  2. Только после завершения полной очистки запустить второй цикл и добавить все новые записи.

Добавим в наше расширение для УТ 11 доработанный вариант процедуры отложенной обработки:


&Вместо("ОтложеннаяОбработка_Контрагенты")
Процедура Расш_ОтложеннаяОбработка_Контрагенты(Объект, КомпонентыОбмена, ОбъектМодифицирован)
    
    // ... [Здесь должен быть стандартный подготовительный код процедуры] ...
    
    // ЭТАП 1: Предварительно очищаем всю существующую КИ у партнера по видам КИ контрагента
    Для Каждого СтрокаКИКонтрагента Из Объект.КонтактнаяИнформация Цикл
        ВидКИПартнера = ВидКИПартнераПоВидуКИКонтрагента(СтрокаКИКонтрагента.Вид, ИспользоватьПартнеровКакКонтрагентов);
        Если ВидКИПартнера = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        СтрокиКИПартнера = ПартнерОбъект.КонтактнаяИнформация.НайтиСтроки(Новый Структура("Вид", ВидКИПартнера));
        Для Каждого СтрокаКИПартнера Из СтрокиКИПартнера Цикл
            ПартнерОбъект.КонтактнаяИнформация.Удалить(СтрокаКИПартнера);
        КонецЦикла;
    КонецЦикла;
    
    // ЭТАП 2: Переносим все новые адреса КИ без риска взаимного удаления строк
    Для Каждого СтрокаКИКонтрагента Из Объект.КонтактнаяИнформация Цикл
        ВидКИПартнера = ВидКИПартнераПоВидуКИКонтрагента(СтрокаКИКонтрагента.Вид, ИспользоватьПартнеровКакКонтрагентов);
        Если ВидКИПартнера = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        СтрокаКИПартнера = ПартнерОбъект.КонтактнаяИнформация.Добавить;
        ЗаполнитьЗначенияСвойств(СтрокаКИПартнера, СтрокаКИКонтрагента);
        СтрокаКИПартнера.Вид = ВидКИПартнера;
    КонецЦикла;
    
    // ... [Здесь должен быть стандартный завершающий код процедуры, включая запись ПартнерОбъект] ...
    
КонецПроцедуры

Благодаря такому разделению циклов, механизм отложенной синхронизации партнеров работает корректно, сохраняя всю структуру переданных адресов электронной почты.

Резюме и альтернативные пути решения

Мы подробно разобрали, как решить проблему синхронизации множественных email-адресов на уровне исходного кода конфигураций 1С. Описанный метод через расширения является наиболее быстрым и эффективным для баз, находящихся на поддержке с возможностью изменения.

Однако, если вы не хотите вносить изменения в конфигурации программно, у вас есть альтернативные пути:

← На главную