Рассмотрим частую задачу, с которой сталкиваются разработчики при написании внешних обработок для конфигурации 1С:Бухгалтерия предприятия 3.0 (и других конфигураций, построенных на базе Библиотеки стандартных подсистем - БСП). Допустим, мы программно создаем новых контрагентов на основе данных из ФНС и нам необходимо записать их юридический или фактический адрес — для этого есть автозаполнение реквизитов контрагента по ИНН.
Зачастую разработчики пытаются подсмотреть, как это реализовано в типовых модулях, копируют код и сталкиваются с ошибками. Проанализируем типичную ситуацию, выясним причины ошибок и разберем по шагам правильные способы программного заполнения контактной информации.
Посмотрим на пример кода, который был скопирован из модуля управляемой формы справочника Контрагенты во внешнюю обработку:
&НаСервере
Процедура ЗаполнитьЭлементКонтактнойИнформации(СпрКонтрагент, ИмяРеквизита, СтруктураДанных)
Если СтруктураДанных = Неопределено Тогда
Возврат;
КонецЕсли;
Отбор = Новый Структура("ИмяРеквизита", ИмяРеквизита);
// Ошибка возникает на следующей строке:
Строки = ЭтотОбъект.КонтактнаяИнформацияОписаниеДополнительныхРеквизитов.НайтиСтроки(Отбор);
ДанныеСтроки = ?(Строки.Количество() = 0, Неопределено, Строки[0]);
Если ДанныеСтроки = Неопределено Тогда
Возврат;
КонецЕсли;
ЗаполнитьЗначенияСвойств(ДанныеСтроки, СтруктураДанных);
ДанныеСтроки.Значение = СтруктураДанных.КонтактнаяИнформация;
ЭтотОбъект[ИмяРеквизита] = СтруктураДанных.Представление;
КонецПроцедуры
При выполнении этого кода во внешней обработке система выдаст ошибку о том, что поле КонтактнаяИнформацияОписаниеДополнительныхРеквизитов не найдено. Разберем, почему это происходит.
Дело в том, что при слепом копировании кода из модуля управляемой формы в серверный модуль фоновой обработки мы теряем интерфейсный контекст. В контексте формы карточки контрагента коллекция КонтактнаяИнформацияОписаниеДополнительныхРеквизитов создается механизмами БСП динамически — она служит исключительно для отрисовки полей адресов и телефонов на экране пользователя.
В модуле внешней обработки переменная ЭтотОбъект ссылается на саму обработку (объект метаданных ВнешняяОбработка), у которой, естественно, нет такой коллекции атрибутов. При фоновой или серверной загрузке записывать данные нужно напрямую в объект справочника (или его табличную часть), а не пытаться манипулировать элементами формы.
Столкнувшись с ошибкой, многие разработчики принимают решение обойти механизмы БСП и записать данные в табличную часть КонтактнаяИнформация напрямую. Посмотрим на пример такого решения:
Об = СпрКонтрагент.ПолучитьОбъект();
Адрес = Об.КонтактнаяИнформация.Добавить();
Адрес.Тип = Перечисления.ТипыКонтактнойИнформации.Адрес;
Адрес.Вид = Справочники.ВидыКонтактнойИнформации.ЮрАдресКонтрагента;
Адрес.Представление = РеквизитыКонтрагента.ЮридическийАдрес.Представление;
Об.Записать();
Важно: в современных конфигурациях 1С, использующих подсистему "Контактная информация" из БСП, такой подход является грубейшей ошибкой!
В этом коде разработчик заполняет только текстовое поле Представление. В карточке контрагента этот адрес действительно появится в виде текста, и визуально все будет казаться нормальным. Однако для алгоритмов системы 1С реальные данные адреса хранятся не в строке представления, а в скрытых реквизитах Значение или ЗначенияПолей. В этих реквизитах адрес хранится в виде строго структурированного сериализованного JSON- или XML-пакета (XDTO).
Если записать адрес "напрямую", заполнив лишь текст, система воспримет такую контактную информацию как некорректную или неполную. Проанализируем последствия, к которым это неизбежно приведет:
Вся работа с контактной информацией при программной генерации объектов должна вестись исключительно через программный интерфейс общего модуля УправлениеКонтактнойИнформацией. В зависимости от того, в каком виде к вам пришли данные из ФНС или другой внешней системы, выберем один из двух подходов — для интеграции подойдёт REST API модуль для создания карточек контрагентов.
Рассмотрим алгоритм действий, если внешняя система вернула нам адрес в разобранном виде: мы отдельно знаем индекс, регион, город, улицу и номер дома — для этого подойдёт заполнение карточек контрагентов данными из госреестров.
Сначала нам нужно получить пустую структуру (шаблон) для заполнения адреса. Для этого вызовем функцию УправлениеКонтактнойИнформацией.ОписаниеНовойКонтактнойИнформации() с указанием нужного типа КИ (Адрес). Затем мы заполним ключи полученной структуры нашими данными.
Посмотрим на пример реализации:
// 1. Получаем пустую структуру для адреса
ПараметрыАдреса = УправлениеКонтактнойИнформацией.ОписаниеНовойКонтактнойИнформации(Перечисления.ТипыКонтактнойИнформации.Адрес);
// 2. Заполняем известные нам поля
ПараметрыАдреса.Вставить("Индекс", "119049");
ПараметрыАдреса.Вставить("КодРегиона", "77"); // Код субъекта РФ
ПараметрыАдреса.Вставить("Город", "Москва");
ПараметрыАдреса.Вставить("Улица", "Крымский Вал");
ПараметрыАдреса.Вставить("Дом", "9");
// Также можно передавать данные в английских ключах структуры (ZIPcode, area, city, street, houseNumber),
// если это предусмотрено версией БСП.
// 3. Получаем объект контрагента
ОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект();
// 4. Записываем КИ через типовой метод
ВидКИ = Справочники.ВидыКонтактнойИнформации.ЮрАдресКонтрагента;
УправлениеКонтактнойИнформацией.ЗаписатьКонтактнуюИнформацию(ОбъектКонтрагент, ПараметрыАдреса, ВидКИ, Перечисления.ТипыКонтактнойИнформации.Адрес);
// 5. Сохраняем справочник
ОбъектКонтрагент.Записать();
Процедура ЗаписатьКонтактнуюИнформацию самостоятельно сформирует правильный JSON/XML пакет, корректно сгенерирует текстовое представление и вставит нужную строку в табличную часть объекта, сохранив историю изменений (если для данного вида КИ включена периодичность).
Разберем ситуацию, когда от ФНС или пользователя нам пришла просто строковая переменная с адресом (например, "г. Москва, ул. Ленина, д. 1"), и мы не можем разбить ее на поля самостоятельно — поможет автоматическое извлечение адреса из текстовой строки.
В этом случае нам поможет специальный метод парсинга, который попытается разобрать строку по адресному классификатору (ФИАС/ГАР) и преобразовать ее во внутренний формат.
Рассмотрим пример кода для этой задачи:
СтрокаАдреса = "125009, г. Москва, Тверская ул, д. 1";
// 1. Пытаемся распарсить строку и получить XDTO объект адреса
// В зависимости от версии БСП методы могут немного отличаться, обычно используется общий модуль со словом "Служебный"
ОбъектXDTO = УправлениеКонтактнойИнформациейСлужебный.КонтактнаяИнформацияXDTOПоПредставлению(СтрокаАдреса, Перечисления.ТипыКонтактнойИнформации.Адрес);
Если ОбъектXDTO <> Неопределено Тогда
// 2. Сериализуем объект XDTO в строку XML/JSON (внутренний формат хранения БСП)
ЗначениеКИ = УправлениеКонтактнойИнформациейСлужебный.КонтактнаяИнформацияXDTOВСтроку(ОбъектXDTO);
ОбъектКонтрагент = СсылкаНаКонтрагента.ПолучитьОбъект();
ВидКИ = Справочники.ВидыКонтактнойИнформации.ФактАдресКонтрагента;
// 3. Записываем подготовленные данные.
// Вместо структуры передаем сериализованное значение
УправлениеКонтактнойИнформацией.ЗаписатьКонтактнуюИнформацию(ОбъектКонтрагент, ЗначениеКИ, ВидКИ, Перечисления.ТипыКонтактнойИнформации.Адрес);
ОбъектКонтрагент.Записать();
КонецЕсли;
Подведем итоги: При программном создании элементов справочников или документов, содержащих контактную информацию, никогда не заполняйте табличные части КонтактнаяИнформация напрямую. Всегда используйте методы общего модуля УправлениеКонтактнойИнформацией — есть расширение для заполнения реквизитов по ИНН. Это гарантирует корректность данных, правильное заполнение скрытых реквизитов XDTO и избавит вас от множества проблем при дальнейшей эксплуатации базы данных (формирование отчетов, работа с кассами и ЭДО).