В "чистой" системе Driectum RX в полях "Почтовый адрес" можно указывать произвольный текст, который не всегда заполняется корректно и может отсутствовать в федеральной информационной адресной системе. Интеграция с ФИАС позволяет заполнять поля почтовых адресов корректными данными, в правильном формате.
Поле "Почтовый адрес" используется во многих сущностях системы. Это и справочник "Компании", и справочник "Персоны, и "Наши организации". Кроме того, в некоторых готовых решениях также присутствуют поля с подобным названием, например, решение "Модуль обращений граждан", в котором в карточке обращения на вкладке "Заявитель" есть поле "Почтовый адрес" заявителя.
Как правило почтовый адрес заявителя заполняется из полученного обращения, которое составлял сам заявитель, и соответственно, адрес может быть заполнен некорректно. В таком случае сотрудник, регистрирующий входящее обращение, корректирует почтовый адрес. Для внесение корректных данных, регистратор должен найти адрес в корректном виде и отредактировать его в карточке обращения.
Для более удобного заполнения полей "Почтовый адрес" корректно сформированными адресами, хранящимися в базе ФИАС, можно использовать любой API-сервер ФИАС, например https://kladr-api.ru/docs .
Ниже будет представлен вариант реализации.
Что было сделано на нашем проекте:
Созданы константы, хранящие адрес API-сервера ФИАС (В нашем случае на стороне заказчика развернут свой API-сервер с актуальной базой ФИАС`а) и максимальное количество результатов для отображения пользователю;
Реализовано диалоговое окно для пользователя;
Добавлена дополнительная кнопка, открывающая диалоговое окно подбора адресов;
Описана структура данных, возвращаемых API-сервером ФИАС`а.
При разработке, мы столкнулись с тем, что нельзя из серверного метода передавать в клиентский код и обратно список IList структур. Для преодоления этого ограничения было добавлено 2 связанные структуры. Первая структура хранит текстовое представление адреса в ФИАС и его GUID. Вторая структура хранит список ILits первых структур и текст ошибки, если API вернуло ошибку.
Ниже представлены структуры:
/// <summary>
/// Структура связывает адрес в ФИАС и его GUID.
/// Параметры с маленькой буквы т.к. заполняются десериализацией json данных, в которых параметры приходят тоже с маленькой буквы.
/// </summary>
[Public]
partial class AddressFIAS
{
public string address { get; set; }
public string ao_guid { get; set; }
}
/// <summary>
/// Структура хранит список связей адресов и их GUIDов.
/// </summary>
[Public]
partial class AddressesFIAS
{
public System.Collections.Generic.IList<GD.MainSolution.Module.CitizenRequests.Structures.Module.AddressFIAS> AddressFIAS { get; set; }
public string Error { get; set; }
}
/// <summary>
/// Структура для заполнения полей в карточке персоны или обращения.
/// </summary>
[Public]
partial class FIASResult
{
public string Address { get; set; }
public string FIASguidSC { get; set; }
public bool FilledFromFIASSC { get; set; }
}
Вызов диалогового окна и последующее заполнение полей карточки реализованные в клиентском коде:
/// <summary>
/// Открыть диалоговое окно для заполнения почтового адреса с ФИАС
/// </summary>
public void OpenDialogFIAS()
{
var result = GD.PTOEDMS.PublicFunctions.Module.OpenDialogFIAS(_obj.PostalAddress);
if (!string.IsNullOrEmpty(result.Address))
{
_obj.PostalAddress = result.Address;
_obj.FIASguidSC = !string.IsNullOrEmpty(result.FIASguidSC) ? result.FIASguidSC : null;
_obj.FilledFromFIASSC = result.FilledFromFIASSC;
}
}
Реализация работы диалогового окна представляет собой следующую последовательность действий:
Получение пользовательских настроек или настроек заданных в константах, если не заполнены пользовательские;
Объявляются используемые переменные и структуры;
Если поле «Почтовый адрес» не было пустым, то выполняется первичный запрос к API с данными из этого поля для предзаполнения полей диалогового окна;
Создается диалоговое окно;
Добавляются обработчики изменения значения поля «Адрес в ФИАС» и чек-бокса « Отсутствует адрес в ФИАС»;
При нажатии «Ок» заполняются поля результирующей структуры и метод возвращает эту структуру.
Метод работы диалогового окна реализованный в клиентском коде с модификатором доступа Public:
/// <summary>
/// Диалоговое окно работы с адресами ФИАС
/// </summary>
[Public]
public GD.MainSolution.Module.CitizenRequests.Structures.Module.IFIASResult OpenDialogFIAS(string postalAddress)
{
var settings = GD.MainSolution.Module.CitizenRequests.PublicFunctions.Module.Remote.GetUrlFIAS();
var url = settings[0];
var count = int.Parse(settings[1]);
var token = settings[2];
var result = GD.MainSolution.Module.CitizenRequests.Structures.Module.FIASResult.Create(string.Empty, string.Empty, false);
var addressStruct = GD.MainSolution.Module.CitizenRequests.Structures.Module.AddressesFIAS.Create();
var addressesString = new string[count];
if (!string.IsNullOrEmpty(postalAddress))
{
addressStruct = GD.MainSolution.Module.CitizenRequests.PublicFunctions.Module.Remote.RefreshAdress(addressStruct, postalAddress, url, count.ToString(), token);
if (!string.IsNullOrEmpty(addressStruct.Error))
{
Logger.DebugFormat("!!! FIAS Error - {0}", addressStruct.Error);
addressStruct.Error = string.Empty;
}
else
{
addressesString = addressStruct.AddressFIAS.Take(count).Select(a => a.address).ToArray();
}
}
// Создать диалог ввода.
var dialog = Dialogs.CreateInputDialog(GD.MainSolution.People.Resources.SearchAddressFIAS);
if (addressesString.Length >= count)
{
dialog.Text = GD.MainSolution.People.Resources.AddressesMoreWhenSettingFormat(count);
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat(addressesString.Length.ToString());
}
// Добавить поле для выбора адреса
var addressString = dialog.AddString(GD.MainSolution.People.Resources.PostalAddress, true, postalAddress);
var addressField = dialog.AddSelect(GD.MainSolution.People.Resources.AddressFIAS, true, 0).From(addressesString);
var missingFIASAddress = dialog.AddBoolean(GD.MainSolution.People.Resources.MissingFIASAddress, false);
dialog.Width = 1000;
addressString.SetOnValueChanged((x) =>
{
if (x.NewValue != x.OldValue && !string.IsNullOrEmpty(x.NewValue) && !missingFIASAddress.Value.Value)
{
if (addressStruct.AddressFIAS != null)
{
addressStruct.AddressFIAS.Clear();
}
addressStruct = GD.MainSolution.Module.CitizenRequests.PublicFunctions.Module.Remote.RefreshAdress(addressStruct, x.NewValue,
url, count.ToString(), token);
if (!string.IsNullOrEmpty(addressStruct.Error))
{
Logger.DebugFormat("!!! FIAS Error - {0}", addressStruct.Error);
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat("0");
addressField.From(string.Empty);
addressField.Value = string.Empty;
addressStruct.Error = string.Empty;
}
else
{
addressesString = addressStruct.AddressFIAS.Take(count).Select(a => a.address).ToArray();
if (addressesString.Length > 0)
{
if (addressesString.Length >= count)
{
dialog.Text = GD.MainSolution.People.Resources.AddressesMoreWhenSettingFormat(count);
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat(addressesString.Length.ToString());
}
addressField.From(addressesString);
addressField.ValueIndex = 0;
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat("0");
addressField.From(string.Empty);
addressField.Value = string.Empty;
}
}
}
});
missingFIASAddress.SetOnValueChanged((x) =>
{
addressField.IsEnabled = !x.NewValue.Value;
addressField.IsRequired = !x.NewValue.Value;
if (x.NewValue.Value == false && !string.IsNullOrEmpty(addressString.Value))
{
if (addressStruct.AddressFIAS != null)
{
addressStruct.AddressFIAS.Clear();
}
addressStruct = GD.MainSolution.Module.CitizenRequests.PublicFunctions.Module.Remote.RefreshAdress(addressStruct, addressString.Value,
url, count.ToString(), token);
if (!string.IsNullOrEmpty(addressStruct.Error))
{
Logger.DebugFormat("!!! FIAS Error - {0}", addressStruct.Error);
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat("0");
addressField.From(string.Empty);
addressField.Value = string.Empty;
addressStruct.Error = string.Empty;
}
else
{
addressesString = addressStruct.AddressFIAS.Take(count).Select(a => a.address).ToArray();
if (addressesString.Length > 0)
{
if (addressesString.Length >= count)
{
dialog.Text = GD.MainSolution.People.Resources.AddressesMoreWhenSettingFormat(count);
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat(addressesString.Length.ToString());
}
addressField.From(addressesString);
addressField.ValueIndex = 0;
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat("0");
addressField.From(string.Empty);
addressField.Value = string.Empty;
}
}
}
else
{
dialog.Text = GD.MainSolution.People.Resources.AvailableAddressesFIASFormat("0");
addressField.From(string.Empty);
addressField.Value = string.Empty;
}
});
if (dialog.Show() == DialogButtons.Ok)
{
// Сформировать строку из результатов ввода.
if (missingFIASAddress.Value.Value)
{
result.Address = addressString.Value;
return result;
}
else
{
result.Address = addressField.Value;
var currentAddress = addressStruct.AddressFIAS.Where(a => a.address == addressField.Value).FirstOrDefault();
result.FIASguidSC = currentAddress != null ? currentAddress.ao_guid : string.Empty;
result.FilledFromFIASSC = currentAddress != null ? true : false;
return result;
}
}
return result;
}
Взаимодействие с API реализовано GET-запросами по протоколу http:
/// <summary>
/// Обновить адреса в выпадающем списке
/// </summary>
[Public, Remote(IsPure = true)]
public GD.MainSolution.Module.CitizenRequests.Structures.Module.IAddressesFIAS RefreshAdress(GD.MainSolution.Module.CitizenRequests.Structures.Module.IAddressesFIAS addressesFias,
string address, string url, string count, string token)
{
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", token);
var builder = new UriBuilder(url);
builder.Query = string.Format("term={0}", address);
url = builder.ToString();
var response = client.GetAsync(url).Result;
if (response.StatusCode == HttpStatusCode.OK)
{
var result = response.Content.ReadAsStringAsync().Result;
addressesFias.AddressFIAS = JsonConvert.DeserializeObject<IList<Structures.Module.AddressFIAS>>(result);
return addressesFias;
}
else
{
addressesFias.Error = response.Content.ReadAsStringAsync().Result;
return addressesFias;
}
}
catch (Exception ex)
{
addressesFias.Error = ex.Message;
return addressesFias;
}
}
}
Переменная token на нашем проекте используется для авторизации на сервере API заказчика. При использовании другого API могут потребоваться другие способы авторизации. Также авторизация может отсутствовать.
Внешний вид вкладки "Заявитель" карточки обращения стал выглядеть так:
Поле "Почтовый адрес" сделали не редактируемым в самой карточке обращения, но ввести адрес вручную можно в диалоговом окне подбора адресов.
Для подбора адреса нужно нажать кнопку "Адрес в ФИАС". Откроется диалоговое окно с подбором адреса.
Диалоговое окно содержит краткую информацию о том, как выполнять поиск адресов, количество доступных адресов для выбора из выпадающего списка, поле для ввода адреса в произвольном виде, выпадающий список, который возвращает доступные для выбора адреса и чек-бокс,
Содержание диалогового окна:
Выбор доступных адресов выполняется следующим образом:
Пользователь вписывает адрес в произвольном формате в поле "Почтовый адрес";
Пользователь снимает фокус с текстового поля, например, переходя к выпадающему списку (Обязательное условие срабатывания метода SetOnValueChanged());
СЭД отправляет строку из поля "Почтовый адрес" на API-сервер ФИАС и затем, получив ответ, десериализует ответ в структуру;
Из полученной структуры формируем список адресов доступных для выбора в выпадающем списке, а количество этих адресов выводим в информационном сообщении о количестве доступных адресов для выбора;
Пользователь выбирает нужный адрес из списка предложенных, либо, если нужного адреса нет, дополняет текстовое поле "Почтовый адрес" уточнениями и повторяются пункты 2-5;
Пользователь нажимает кнопку "Ок". В этот момент нужный нам адрес подставляется в поле "Почтовый адрес" карточки обращения, заполняется чек-бокс "Заполнено из ФИАС" и происходят дополнительные действия.
Итак, что же даёт нам взаимодействие с ФИАС:
Спасибо за статью.
У меня следующий вопрос. А разве сейчас налоговая не перешла на ГАР, вместо ФИАС? Не лучше ли использовать именно ГАР ?
Евгений, Добрый день! Все верно. И в данный момент ФИАС использует базу данных ГАР.
ФИАС является адресной системой, а ГАР - это конкретная база данных (Адресный реестр).
На сайте ФИАС (https://fias.nalog.ru/Updates#) основная БД указана как раз ГАР.
Помню когда-то делали такой же проект на Directum 5. Здорово что подобный кейс реализовали на DirectumRX ! Спасибо за статью!
Оставлю это тут:
Репозиторий с шаблоном разработки «Загрузка данных из xlsx-файлов с обложки» в котором реализована загрузка ФИАС в систему https://github.com/DirectumCompany/rx-template-upload-data-ui
Авторизуйтесь, чтобы написать комментарий