При разработке отчетов, обработок выгрузки данных (например, для договоров ДМС или банковских реестров) часто возникает задача извлечь контактную информацию физических лиц: адрес регистрации, проживания, домашний или мобильный телефон. В конфигурациях 1С данные сведения обычно хранятся в регистре сведений КонтактнаяИнформация (в ряде современных конфигураций — в табличных частях справочников). Рассмотрим подробнее, как правильно составить запрос, избежать распространенных ошибок при соединении таблиц и корректно обработать результат.
Разберем типичные ошибки, с которыми сталкиваются разработчики при попытке получить одновременно несколько видов контактной информации (например, адрес и телефон) для одного физического лица:
ПО: попытка написать в одном левом соединении условие вида (КонтактнаяИнформация.Вид = &ВидАдрес) И (КонтактнаяИнформация.Вид = &ВидТелефон). Поскольку одна запись регистра не может одновременно иметь два разных вида, такое условие всегда вернет ЛОЖЬ, и поля контактной информации останутся пустыми.КонтактнаяИнформацияАдрес и КонтактнаяИнформацияТелефон). Если в условиях соединения или в секции ВЫБРАТЬ обратиться к исходному имени таблицы, возникнет ошибка Поле не найдено.Поле1 по Поле9 несут разную смысловую нагрузку в зависимости от типа записи. Например, для адреса Поле3 хранит район, а для телефона в отдельных структурах может хранить номер без кода. Если попытаться извлечь номер телефона из неверно соединенной таблицы адреса через Поле3, в выборку попадет район населенного пункта. В большинстве случаев готовый строковый номер или адрес содержится в ресурсе Представление.Проанализируем правильный подход: для каждого вида контактной информации мы подключаем регистр сведений КонтактнаяИнформация отдельным левым соединением с уникальным псевдонимом и отбором по соответствующему виду.
Посмотрим на пример текста запроса, который выбирает паспортные данные, адрес регистрации и номер телефона физических лиц по массиву сотрудников:
ВЫБРАТЬ
ЕСТЬNULL(ФИОФизЛицСрезПоследних.Фамилия, "") КАК Фамилия,
ЕСТЬNULL(ФИОФизЛицСрезПоследних.Имя, "") КАК Имя,
ЕСТЬNULL(ФИОФизЛицСрезПоследних.Отчество, "") КАК Отчество,
СотрудникиОрганизаций.Физлицо.ДатаРождения КАК ДатаРождения,
СотрудникиОрганизаций.Физлицо.МестоРождения КАК МестоРождения,
СотрудникиОрганизаций.ДатаНачала КАК ДатаНачала,
ЕСТЬNULL(ПаспортныеДанныеФизЛицСрезПоследних.ДокументСерия + " " + ПаспортныеДанныеФизЛицСрезПоследних.ДокументНомер, "") КАК НомерДокумента,
ЕСТЬNULL(ПаспортныеДанныеФизЛицСрезПоследних.ДокументКемВыдан, "") КАК ДокументКемВыдан,
ЕСТЬNULL(ПаспортныеДанныеФизЛицСрезПоследних.ДокументДатаВыдачи, ДАТАВРЕМЯ(1, 1, 1)) КАК ДокументДатаВыдачи,
ЕСТЬNULL(ПаспортныеДанныеФизЛицСрезПоследних.ДокументКодПодразделения, "") КАК ДокументКодПодразделения,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле1, "") КАК Индекс,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле4, "") КАК Город,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле6, "") КАК Улица,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле7, "") КАК НомерДома,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле8, "") КАК Корпус,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Поле9, "") КАК НомерКвартиры,
ЕСТЬNULL(КонтактнаяИнформацияАдрес.Представление, "") КАК АдресПредставление,
ФизическиеЛица.ИНН КАК ИНН,
ЕСТЬNULL(КонтактнаяИнформацияТелефон.Представление, "") КАК ТелефонФизЛица
ИЗ
Справочник.СотрудникиОрганизаций КАК СотрудникиОрганизаций
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФИОФизЛиц.СрезПоследних(&Дата, ) КАК ФИОФизЛицСрезПоследних
ПО СотрудникиОрганизаций.Физлицо = ФИОФизЛицСрезПоследних.ФизЛицо
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПаспортныеДанныеФизЛиц.СрезПоследних(&Дата, ) КАК ПаспортныеДанныеФизЛицСрезПоследних
ПО СотрудникиОрганизаций.Физлицо = ПаспортныеДанныеФизЛицСрезПоследних.ФизЛицо
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформацияАдрес
ПО СотрудникиОрганизаций.Физлицо = КонтактнаяИнформацияАдрес.Объект
И (КонтактнаяИнформацияАдрес.Вид = &ВидАдресаРегистрации)
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформацияТелефон
ПО СотрудникиОрганизаций.Физлицо = КонтактнаяИнформацияТелефон.Объект
И (КонтактнаяИнформацияТелефон.Вид = &ВидТелефонФизЛица)
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица КАК ФизическиеЛица
ПО СотрудникиОрганизаций.Физлицо = ФизическиеЛица.Ссылка
ГДЕ
СотрудникиОрганизаций.Ссылка В(&МассивСотрудников)
Разберем по шагам, как правильно инициализировать параметры запроса в программном коде. Виды контактной информации являются элементами справочника ВидыКонтактнойИнформации:
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
// Установка видов контактной информации
ВидАдреса = Справочники.ВидыКонтактнойИнформации.ЮрАдресФизЛица;
ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонФизЛица;
Запрос.УстановитьПараметр("ВидАдресаРегистрации", ВидАдреса);
Запрос.УстановитьПараметр("ВидТелефонФизЛица", ВидТелефона);
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Запрос.УстановитьПараметр("МассивСотрудников", МассивСотрудников);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Обратите внимание: вместо передачи параметров через УстановитьПараметр для предопределенных элементов справочников и перечислений в тексте запроса можно использовать конструкцию ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.ТелефонФизЛица).
Часто в базе данных кадровики заполняют контактные телефоны с ошибками: в одно поле могут внести как мобильный, так и городской номер, добавить внутренний добавочный номер или комментарий. В языке запросов 1С отсутствуют средства для подсчета длины строки и регулярные выражения, поэтому проверку и нормализацию выполняют во встроенном языке при обходе выборки.
Для проверки длины строки используется функция СтрДлина. Рассмотрим пример функции, которая очищает строку телефона от посторонних символов (пробелов, тире, скобок) и определяет тип телефона по количеству цифр и коду:
Функция ОчиститьНомерОтСимволов(Знач ИсходныйНомер)
РазрешенныеСимволы = "0123456789";
ОчищенныйНомер = "";
Для Индекс = 1 По СтрДлина(ИсходныйНомер) Цикл
Символ = Сред(ИсходныйНомер, Индекс, 1);
Если Найти(РазрешенныеСимволы, Символ) > 0 Тогда
ОчищенныйНомер = ОчищенныйНомер + Символ;
КонецЕсли;
КонецЦикла;
Возврат ОчищенныйНомер;
КонецФункции
// Пример использования при обходе выборки
Пока Выборка.Следующий() Цикл
ТелефонСтр = Выборка.ТелефонФизЛица;
ЦифрыТелефона = ОчиститьНомерОтСимволов(ТелефонСтр);
ДлинаНомера = СтрДлина(ЦифрыТелефона);
Если ДлинаНомера = 11 И (Лев(ЦифрыТелефона, 2) = "79" Или Лев(ЦифрыТелефона, 2) = "89") Тогда
// Номер является мобильным телефоном РФ
МобТелефон = ТелефонСтр;
ДомТелефон = "";
ИначеЕсли ДлинаНомера > 0 Тогда
// Номер определен как городской/домашний
МобТелефон = "";
ДомТелефон = ТелефонСтр;
Иначе
МобТелефон = "";
ДомТелефон = "";
КонецЕсли;
КонецЦикла;
Такой комбинированный подход позволяет гибко и безошибочно выгружать данные физлиц в любые внешние форматы, исключая дублирование строк и искажение информации.