Вводные
В конфигурации УТ 11 присутствует функционал заполнения адресов и конрагентов через сервис ИТС, автодополнения ФИО нет.
Заполнение контрагентов работает при подписке ПРОФ или наличии 1С:Контрагент, адреса работают при активной подписке ИТС.
Данная реализация позволяет получить схожий функционал бесплатно с порогом 10к обращений в день, что для средних фирм более чем хватает.
Документация.
Получение ключа https://dadata.ru/suggestions/usage/
Работа с API https://dadata.ru/api/suggest/
Реализация
Реализовывал через расширение, настройки храню в выделенном справочнике.
dd_ОбщийМодульПовторноеИспользование
Функция ПолучитьНастройкиПодсистемы() Экспорт
УстановитьПривилегированныйРежим(Истина);
СтруктураНастроек = Новый Структура;
СтруктураНастроек.Вставить("ИспользованиеСервиса", Ложь);
СтруктураНастроек.Вставить("КлючДоступа", "");
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
| dd_НастройкиПодсистемы.ИспользованиеСервиса КАК ИспользованиеСервиса,
| dd_НастройкиПодсистемы.КлючДоступа КАК КлючДоступа
|ИЗ
| Справочник.dd_НастройкиПодсистемы КАК dd_НастройкиПодсистемы
|ГДЕ
| НЕ dd_НастройкиПодсистемы.ПометкаУдаления";
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
СтруктураНастроек.ИспользованиеСервиса = Выборка.ИспользованиеСервиса;
СтруктураНастроек.КлючДоступа = Выборка.КлючДоступа;
КонецЕсли;
Возврат СтруктураНастроек;
КонецФункции
Обращения к сервису
Обращения к сервису вынес в общий модуль, реализовывал на клиентской части, возможно имеет смысл переносить на сервер, чтобы все обращения были с него.
Функция ПолучитьСоответствиеПодсказок(Текст, ВидПодсказки) Экспорт
НастройкиПодсистемы = dd_ОбщийМодульПовторноеИспользование.ПолучитьНастройкиПодсистемы();
СоответствиеОтвет = Новый Соответствие;
Если НЕ НастройкиПодсистемы.ИспользованиеСервиса Тогда
Возврат СоответствиеОтвет;
КонецЕсли;
СоответствиеДанные = Новый Соответствие;
СоответствиеДанные.Вставить("query", Текст);
СтрокаДанные = В_JSON(СоответствиеДанные);
АдресРесурса = "/suggestions/api/4_1/rs";
Если ВидПодсказки = "Адрес" Тогда
АдресРесурса = АдресРесурса + "/suggest/address";
ИначеЕсли ВидПодсказки = "ФИО" Тогда
АдресРесурса = АдресРесурса + "/suggest/fio";
ИначеЕсли ВидПодсказки = "ОрганизацияНаименование" Тогда
АдресРесурса = АдресРесурса + "/suggest/party";
ИначеЕсли ВидПодсказки = "ОрганизацияИНН" Тогда
АдресРесурса = АдресРесурса + "/findById/party";
Иначе
Возврат СоответствиеОтвет;
КонецЕсли;
ДанныеОтвет = POST(АдресРесурса, СтрокаДанные);
Если ДанныеОтвет.КодСостояния = 200 Тогда
СтрокаДанныеОтвет = ДанныеОтвет.ПолучитьТелоКакСтроку();
СоответствиеОтвет = ИЗ_JSON(СтрокаДанныеОтвет);
КонецЕсли;
Возврат СоответствиеОтвет;
КонецФункции
Обвязку HTTP запроса не выкладываю, каждый пишет на свой вкус.
Для работы с адресами нужно преобразовать формат Dadata в формат адресов 1С.
Преобразование формата адреса
Функция СформироватьКИФормата1С(СоответствиеАдресДаДата) Экспорт
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Комментарий", "");
СтруктураДанных.Вставить("КонтактнаяИнформация", "");
СтруктураДанных.Вставить("Корректный", Неопределено);
СтруктураДанных.Вставить("Представление", "");
СтруктураДанных.Представление = СоответствиеАдресДаДата.Получить("unrestricted_value");
АдресДаДатаДанные = СоответствиеАдресДаДата.Получить("data");
Результат = РаботаСАдресамиКлиентСервер.ОписаниеНовойКонтактнойИнформации(ПредопределенноеЗначение("Перечисление.ТипыКонтактнойИнформации.Адрес"));
Результат.Вставить("Country", "Россия");
Результат.Вставить("CountryCode", "643");
Результат.Вставить("Value", СоответствиеАдресДаДата.Получить("unrestricted_value"));
Результат.AddressType = РаботаСАдресамиКлиентСервер.АдминистративноТерриториальныйАдрес();
Результат.ZIPCode = Строка(АдресДаДатаДанные.Получить("postal_code"));
Результат.OKTMO = Строка(АдресДаДатаДанные.Получить("oktmo"));
Результат.OKATO = Строка(АдресДаДатаДанные.Получить("okato"));
Результат.Area = Строка(АдресДаДатаДанные.Получить("region"));
Результат.AreaType = Строка(АдресДаДатаДанные.Получить("region_type"));
Результат.District = Строка(АдресДаДатаДанные.Получить("area"));
Результат.DistrictType = Строка(АдресДаДатаДанные.Получить("area_type"));
Результат.City = Строка(АдресДаДатаДанные.Получить("city"));
Результат.CityType = Строка(АдресДаДатаДанные.Получить("city_type"));
Если Результат.City = "Москва" Тогда
Результат.City = "";
Результат.CityType = "";
КонецЕсли;
Результат.Locality = Строка(АдресДаДатаДанные.Получить("settlement"));
Результат.LocalityType = Строка(АдресДаДатаДанные.Получить("settlement_type"));
Результат.Street = Строка(АдресДаДатаДанные.Получить("street"));
Результат.StreetType = Строка(АдресДаДатаДанные.Получить("street_type"));
Результат.CityDistrict = Строка(АдресДаДатаДанные.Получить("city_district"));
Результат.CityDistrictType = Строка(АдресДаДатаДанные.Получить("city_district_type"));
Если Не ПустаяСтрока(АдресДаДатаДанные.Получить("house")) Тогда
house_type = АдресДаДатаДанные.Получить("house_type");
Если house_type = "д" Тогда
Результат.HouseType = "Дом";
Результат.HouseNumber = АдресДаДатаДанные.Получить("house");
ИначеЕсли house_type = "влд" Тогда
Результат.HouseType = "Владение";
Результат.HouseNumber = АдресДаДатаДанные.Получить("house");
ИначеЕсли house_type = "двлд" Тогда
Результат.HouseType = "Домовладение";
Результат.HouseNumber = АдресДаДатаДанные.Получить("house");
ИначеЕсли house_type = "к" Тогда
Результат.Buildings.Добавить(УправлениеКонтактнойИнформациейКлиентСервер.ЗначениеСтроенияИлиПомещения("Корпус", АдресДаДатаДанные.Получить("house")));
КонецЕсли;
КонецЕсли;
Если Не ПустаяСтрока(АдресДаДатаДанные.Получить("block")) Тогда
block_type = АдресДаДатаДанные.Получить("block_type");
Если block_type = "стр" Тогда
Тип = "Строение";
ИначеЕсли block_type = "к" Тогда
Тип = "Корпус";
ИначеЕсли block_type = "сооружение" Тогда
Тип = "Сооружение";
ИначеЕсли block_type = "литер" Тогда
Тип = "Литер";
Иначе
Тип = "";
КонецЕсли;
Результат.Buildings.Добавить(УправлениеКонтактнойИнформациейКлиентСервер.ЗначениеСтроенияИлиПомещения(Тип, АдресДаДатаДанные.Получить("block")));
КонецЕсли;
Если Не ПустаяСтрока(АдресДаДатаДанные.Получить("flat")) Тогда
flat_type = АдресДаДатаДанные.Получить("flat_type");
Если block_type = "оф" Тогда
Тип = "Офис";
ИначеЕсли block_type = "кв" Тогда
Тип = "Квартира";
ИначеЕсли block_type = "эт" Тогда
Тип = "Этаж";
ИначеЕсли block_type = "пом" Тогда
Тип = "Помещение";
Иначе
Тип = "";
КонецЕсли;
Результат.Apartments.Добавить(УправлениеКонтактнойИнформациейКлиентСервер.ЗначениеСтроенияИлиПомещения(Тип, АдресДаДатаДанные.Получить("flat")));
КонецЕсли;
СтруктураДанных.КонтактнаяИнформация = dd_ЗапросыКСервису.В_JSON(Результат);
Возврат СтруктураДанных;
КонецФункции
В преобразовании не переносятся коды ФИАС, в текущей реализации нет необходимости, можно доработать.
Работа с формами, ФИО
На мой взгляд оптимумом является связка АвтоПодбор + ОбработкаВыбора
В расширении формы (например спр. "КонтактныеЛицаПартнеров") к полю наименование добавляем обработчики:
Обработчики на форме работа с ФИО
&НаКлиенте
Процедура dd_НаименованиеОбработкаВыбораВместо(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
dd_ОбщийМодульКлиент.ОбработкаВыбора_СтрокаФИО(Элемент, ВыбранноеЗначение, СтандартнаяОбработка, ЭтаФорма, Объект, Параметры);
КонецПроцедуры
&НаКлиенте
Процедура dd_НаименованиеАвтоПодборВместо(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
dd_ОбщийМодульКлиент.АвтоПодбор_СтрокаФИО(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка, ЭтаФорма, Объект, Параметры);
КонецПроцедуры
Реализация вынес в общий модуль для возможности переиспользования
Обработчики работы с ФИО, общий модуль
Процедура АвтоПодбор_СтрокаФИО(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка, Форма, Объект, Параметры) Экспорт
Если Параметры.НаименованиеИзменено
ИЛИ
СтрДлина(Текст) < 3
ИЛИ Прав(Текст, 1) = " " Тогда
Параметры.НаименованиеИзменено = Ложь;
Возврат;
КонецЕсли;
СтандартнаяОбработка = Ложь;
СоответствиеОтвет = dd_ЗапросыКСервису.ПолучитьСоответствиеПодсказок(Текст, "ФИО");
Если СоответствиеОтвет.Количество() = 0 Тогда
Возврат;
КонецЕсли;
СписокЗначений = Новый СписокЗначений;
Для каждого СтрТ Из СоответствиеОтвет.Получить("suggestions") Цикл
СписокЗначений.Добавить(СтрТ.Получить("value"));
КонецЦикла;
ДанныеВыбора = СписокЗначений;
КонецПроцедуры
Процедура ОбработкаВыбора_СтрокаФИО(Элемент, ВыбранноеЗначение, СтандартнаяОбработка, Форма, Объект, Параметры) Экспорт
СтандартнаяОбработка = Ложь;
Если ВыбранноеЗначение = Элемент.ТекстРедактирования Тогда
Возврат;
КонецЕсли;
Объект.Наименование = ВыбранноеЗначение;
Форма.ОбновитьОтображениеДанных(Форма.Элементы.Наименование);
Позиция = СтрДлина(Элемент.ТекстРедактирования) + 1;
Элемент.УстановитьГраницыВыделения(Позиция, Позиция);
Форма.Модифицированность = Истина;
Параметры.НаименованиеИзменено = Истина;
КонецПроцедуры
Работа с формами, Адрес
Поля адресов (контактной информации) на форме выводится кодом и обработчики вынесены в общий модуль. В обработку редактирования адреса не лез, изменил работу заполнения по строке.
УправлениеКонтактнойИнформациейКлиент
&Вместо("АвтоПодборАдреса")
Процедура dd_АвтоПодборАдреса(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка) Экспорт
Если СтрДлина(Текст) < 3 ИЛИ Прав(Текст, 1) = " " Тогда
Возврат;
КонецЕсли;
НастройкиПодсистемы = dd_ОбщийМодульПовторноеИспользование.ПолучитьНастройкиПодсистемы();
Если НЕ НастройкиПодсистемы.ИспользованиеСервиса Тогда
ПродолжитьВызов(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка);
Возврат;
КонецЕсли;
СоответствиеОтвет = dd_ЗапросыКСервису.ПолучитьСоответствиеПодсказок(Текст, "Адрес");
Если СоответствиеОтвет.Количество() = 0 Тогда
Возврат;
КонецЕсли;
СписокЗначений = Новый СписокЗначений;
Для каждого СтрТ Из СоответствиеОтвет.Получить("suggestions") Цикл
СписокЗначений.Добавить(СтрТ, СтрТ.Получить("value"));
КонецЦикла;
ДанныеВыбора = СписокЗначений;
СтандартнаяОбработка = Ложь;
КонецПроцедуры
&Вместо("ОбработкаВыбора")
Процедура dd_ОбработкаВыбора(Знач Форма, Знач ВыбранноеЗначение, Знач ИмяРеквизита, СтандартнаяОбработка = Ложь) Экспорт
НастройкиПодсистемы = dd_ОбщийМодульПовторноеИспользование.ПолучитьНастройкиПодсистемы();
Если НастройкиПодсистемы.ИспользованиеСервиса Тогда
ДанныеАдреса = dd_ЗапросыКСервису.СформироватьКИФормата1С(ВыбранноеЗначение);
Представление = ВыбранноеЗначение.Получить("Value");
ВыбранноеЗначение = Новый Структура;
ВыбранноеЗначение.Вставить("Адрес", ДанныеАдреса.КонтактнаяИнформация);
ВыбранноеЗначение.Вставить("Представление", ДанныеАдреса.Представление);
КонецЕсли;
ПродолжитьВызов(Форма, ВыбранноеЗначение, ИмяРеквизита, СтандартнаяОбработка);
Если НастройкиПодсистемы.ИспользованиеСервиса Тогда
Форма.ОбновитьОтображениеДанных(Форма.Элементы[ИмяРеквизита]);
Позиция = СтрДлина(Форма.Элементы[ИмяРеквизита].ТекстРедактирования) + 1;
Форма.Элементы[ИмяРеквизита].УстановитьГраницыВыделения(Позиция, Позиция);
КонецЕсли;
КонецПроцедуры
Установки курсора как в случае с ФИО нет, для начала такой вариант вполне рабочий.
Работа с формами, ИНН
Работа с ИНН ведется из форм спр. Контрагенты, спр. Партнеры, формы "Помощник ввода нового", для спр. Контрагенты:
//-- в общем модуле
Процедура АвтоПодбор_СтрокаИНН(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка) Экспорт
Если СтрДлина(Текст) < 10 Тогда
Возврат;
КонецЕсли;
СтандартнаяОбработка = Ложь;
СоответствиеОтвет = dd_ЗапросыКСервису.ПолучитьСоответствиеПодсказок(Текст, "ОрганизацияИНН");
Если СоответствиеОтвет.Количество() = 0 Тогда
Возврат;
КонецЕсли;
СписокЗначений = Новый СписокЗначений;
Для каждого СтрТ Из СоответствиеОтвет.Получить("suggestions") Цикл
СписокЗначений.Добавить(СтрТ.Получить("data"), СтрТ.Получить("value"));
КонецЦикла;
ДанныеВыбора = СписокЗначений;
КонецПроцедуры
//-- в форме
&НаКлиенте
Процедура dd_ИННПриИзмененииВместо(Элемент)
Если НЕ Параметры.КонтрагентВыбран Тогда
ПродолжитьВызов(Элемент);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура dd_ИННОбработкаВыбораВместо(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
РеквизитыКонтрагента = dd_ОбщийМодульКлиент.ПолучитьРеквизитыКонтрагента(ВыбранноеЗначение);
Объект.ИНН = РеквизитыКонтрагента.ИНН;
Объект.КПП = РеквизитыКонтрагента.КПП;
Объект.Наименование = РеквизитыКонтрагента.Наименование;
Объект.НаименованиеПолное = РеквизитыКонтрагента.НаименованиеПолное;
ВидКонтактнойИнформацииЮрАдрес = ПредопределенноеЗначение("Справочник.ВидыКонтактнойИнформации.ЮрАдресКонтрагента");
ПартнерыИКонтрагентыКлиент.ЗаполнитьЭлементКонтактнойИнформации(
ЭтотОбъект,
ВидКонтактнойИнформацииЮрАдрес,
РеквизитыКонтрагента.ЮридическийАдрес);
//--
Отбор = Новый Структура("Вид", ВидКонтактнойИнформацииЮрАдрес);
НайденныеСтроки = ЭтаФорма.КонтактнаяИнформацияОписаниеДополнительныхРеквизитов.НайтиСтроки(Отбор);
ИмяРеквизита = "";
ИмяЭлементаДляРазмещения = "";
ХранитьИсториюИзменений = Истина;
Для каждого СтрокаКонтактнойИнформации Из НайденныеСтроки Цикл
Если НЕ СтрокаКонтактнойИнформации.ЭтоИсторическаяКонтактнаяИнформация Тогда
ИмяРеквизита = СтрокаКонтактнойИнформации.ИмяРеквизита;
ИмяЭлементаДляРазмещения = СтрокаКонтактнойИнформации.ИмяЭлементаДляРазмещения;
ХранитьИсториюИзменений = СтрокаКонтактнойИнформации.ХранитьИсториюИзменений;
КонецЕсли;
ЭтаФорма.КонтактнаяИнформацияОписаниеДополнительныхРеквизитов.Удалить(СтрокаКонтактнойИнформации);
КонецЦикла;
ДанныеСтроки = ЭтаФорма.КонтактнаяИнформацияОписаниеДополнительныхРеквизитов.Добавить();
ДанныеСтроки.Вид = ВидКонтактнойИнформацииЮрАдрес;
ДанныеСтроки.Тип = ПредопределенноеЗначение("Перечисление.ТипыКонтактнойИнформации.Адрес");
ДанныеСтроки.Значение = РеквизитыКонтрагента.ЮридическийАдрес.КонтактнаяИнформация;
ДанныеСтроки.Представление = РеквизитыКонтрагента.ЮридическийАдрес.Представление;
ДанныеСтроки.Комментарий = РеквизитыКонтрагента.ЮридическийАдрес.Комментарий;
ДанныеСтроки.ИмяЭлементаДляРазмещения = ИмяЭлементаДляРазмещения;
ДанныеСтроки.ДействуетС = Неопределено; //Дата(1970,1,1,1,0,0) + ВыбранноеЗначение.Получить("ogrn_date") / 1000;
ДанныеСтроки.ИмяРеквизита = ИмяРеквизита;
ДанныеСтроки.ЭтоИсторическаяКонтактнаяИнформация = Ложь;
ДанныеСтроки.ХранитьИсториюИзменений = Ложь;
//--
Модифицированность = Истина;
Параметры.КонтрагентВыбран = Истина;
КонецПроцедуры
В данным сразу получаю адрес, его преобразую в формат 1С и фиксирую на форме.
В типовой реализации отслеживается обработка выбора с созданием фонового задания её блокирую используя ключевой параметр формы.
Проект на GitHub https://github.com/malikov-pro/dadata_suggestions_ut11
Благодарю за внимание.
Если нужна готовая сборка или есть предложения по доработке - пишите в комментарии.