Как просклонять ФИО из родительного падежа обратно в именительный в 1С?

Программист IT и автоматизация бизнеса
← На главную

Разработка печатных форм, отчетов или интеграция с внешними системами в 1С часто требует работы с персональными данными. Одна из самых коварных задач — это обратное склонение ФИО, то есть приведение строки из родительного (или любого другого косвенного) падежа обратно в именительный. Например, когда в базе контрагент или контактное лицо изначально записаны как "Иванова Петра Сергеевича", а в акте или договоре нам нужно вывести его в именительном падеже: "Иванов Петр Сергеевич".

Сложность заключается в том, что большинство стандартных инструментов 1С (включая Библиотеку Стандартных Подсистем, внешнюю компоненту NameDecl.dll и встроенную функцию платформы ПолучитьСклоненияСтроки) работают исключительно в «прямом» направлении: они принимают строку в именительном падеже и склоняют ее по падежам. Попытка передать туда строку в родительном падеже либо вызовет ошибку, либо вернет непредсказуемый результат.

В этой статье мы подробно рассмотрим несколько практических способов решения данной задачи: от простых алгоритмов разбора строк до интеграции с внешними интеллектуальными сервисами.

Метод 1. Ручной разбор и замена окончаний (эвристический алгоритм)

Если задача стоит локально, база работает в закрытом контуре без доступа к интернету, а платформа старая (например, 1С 8.2 или обычные формы УПП), можно применить алгоритм эвристического анализа окончаний. Мы разделяем исходную строку на составляющие (Фамилия, Имя, Отчество) и анализируем окончания фамилии, чтобы преобразовать их по правилам русского языка.

Рассмотрим пример реализации такого алгоритма на встроенном языке 1С:


// Функция разбирает ФИО в родительном падеже и пытается восстановить именительный
Функция ВосстановитьИменительныйПадеж(Знач ИсходноеФИО) Экспорт
    
    ИсходноеФИО = СокрЛП(ИсходноеФИО);
    
    // Выделяем первое слово (предположительно, Фамилия)
    ПозицияПервогоПробела = Найти(ИсходноеФИО, " ");
    Если ПозицияПервогоПробела = 0 Тогда
        Возврат ИсходноеФИО; // Не удалось разделить
    КонецЕсли;
    
    ФамилияРод = СокрЛП(Лев(ИсходноеФИО, ПозицияПервогоПробела));
    ОстатокСтроки = СокрЛП(Сред(ИсходноеФИО, ПозицияПервогоПробела + 1));
    
    // Выделяем второе слово (Имя)
    ПозицияВторогоПробела = Найти(ОстатокСтроки, " ");
    Если ПозицияВторогоПробела = 0 Тогда
        ИмяРод = ОстатокСтроки;
        ОтчествоРод = "";
    Иначе
        ИмяРод = СокрЛП(Лев(ОстатокСтроки, ПозицияВторогоПробела));
        ОтчествоРод = СокрЛП(Сред(ОстатокСтроки, ПозицияВторогоПробела + 1));
    КонецЕсли;
    
    // --- ПРЕОБРАЗОВАНИЕ ФАМИЛИИ ---
    ФамилияИм = ФамилияРод;
    ДлинаФамилии = СтрДлина(ФамилияРод);
    
    Если ДлинаФамилии > 3 Тогда
        // Склонение мужских фамилий на -ов / -ев (Иванова -> Иванов)
        Если Прав(ФамилияРод, 3) = "ова" ИЛИ Прав(ФамилияРод, 3) = "ева" Тогда
            // Проверяем, женщина это или мужчина в род. падеже.
            // Если отчество заканчивается на "на", то это женщина (Именительный = Иванова)
            Если Прав(ОтчествоРод, 2) = "на" Тогда
                ФамилияИм = ФамилияРод; // Оставляем женскую фамилию
            Иначе
                ФамилияИм = Лев(ФамилияРод, ДлинаФамилии - 1); // Мужская (Иванова -> Иванов)
            КонецЕсли;
        // Склонение фамилий на -ого (Белого -> Белый)
        ИначеЕсли Прав(ФамилияРод, 3) = "ого" Тогда
            ФамилияИм = Лев(ФамилияРод, ДлинаФамилии - 3) + "ий";
        // Склонение женских фамилий на -ской (Ковалевской -> Ковалевская)
        ИначеЕсли Прав(ФамилияРод, 5) = "вской" Тогда
            ФамилияИм = Лев(ФамилияРод, ДлинаФамилии - 2) + "ая";
        // Склонение фамилий на -ой (Толстой -> Толстой / Толстая)
        ИначеЕсли Прав(ФамилияРод, 2) = "ой" Тогда
            Если Прав(ОтчествоРод, 2) = "на" Тогда
                ФамилияИм = Лев(ФамилияРод, ДлинаФамилии - 2) + "ая";
            КонецЕсли;
        КонецЕсли;
    КонецЕсли;
    
    // --- ПРЕОБРАЗОВАНИЕ ИМЕНИ ---
    ИмяИм = ИмяРод;
    ДлинаИмени = СтрДлина(ИмяРод);
    Если ДлинаИмени > 2 Тогда
        // Александр -> Александра (в род. падеже), восстанавливаем: Александра -> Александр
        Если Прав(ИмяРод, 1) = "а" И Прав(ОтчествоРод, 3) = "ича" Тогда
            ИмяИм = Лев(ИмяРод, ДлинаИмени - 1);
        // Дмитрий -> Дмитрия, восстанавливаем: Дмитрия -> Дмитрий
        ИначеЕсли Прав(ИмяРод, 1) = "я" И Прав(ОтчествоРод, 3) = "ича" Тогда
            ИмяИм = Лев(ИмяРод, ДлинаИмени - 1) + "й";
        КонецЕсли;
    КонецЕсли;
    
    // --- ПРЕОБРАЗОВАНИЕ ОТЧЕСТВА ---
    ОтчествоИм = ОтчествоРод;
    ДлинаОтчества = СтрДлина(ОтчествоРод);
    Если ДлинаОтчества > 3 Тогда
        // Мужские отчества: Сергеевича -> Сергеевич
        Если Прав(ОтчествоРод, 3) = "ича" Тогда
            ОтчествоИм = Лев(ОтчествоРод, ДлинаОтчества - 1);
        // Женские отчества: Сергеевны -> Сергеевна
        ИначеЕсли Прав(ОтчествоРод, 2) = "ны" Тогда
            ОтчествоИм = Лев(ОтчествоРод, ДлинаОтчества - 1) + "а";
        КонецЕсли;
    КонецЕсли;
    
    Возврат ФильтроватьСтроку(ФамилияИм + " " + ИмяИм + " " + ОтчествоИм);
    
КонецФункции

Функция ФильтроватьСтроку(Знач Стр)
    Возврат СокрЛП(Стр);
КонецФункции

Преимущества: Работает полностью локально, мгновенно, не требует внешних компонент или платных подписок.

Недостатки: Точность составляет около 80-85%. Нерегулярные, иностранные фамилии или фамилии, не склоняющиеся по стандартным правилам (например, Черных, Шевченко, Гинзбург), алгоритм обработает с ошибками.

Метод 2. Поиск сопоставления по информационной базе

Проанализируем ситуацию: если в нашей системе ФИО заведено в родительном падеже (например, в справочнике КонтактныеЛица), очень вероятно, что физическое лицо с этим именем уже существует в основном справочнике ФизическиеЛица или Сотрудники в правильном именительном падеже.

Мы можем написать поисковый алгоритм сопоставления. Для оптимизации сравнения можно использовать функцию поиска по подстроке или нормализованные индексы.

Разберем пример запроса, который ищет возможное совпадение по первым буквам фамилии и имени:


Функция НайтиИменительныйПадежВБазе(ФИОРодительный) Экспорт
    
    // Разбиваем строку для получения примерного шаблона поиска
    ЧастиФИО = ОбщегоНазначенияКлиентСервер.РазложитьСтрокуВМассивСубстрок(ФИОРодительный, " ");
    Если ЧастиФИО.Количество() < 2 Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Берем основу фамилии (первые 4 символа) и основу имени
    ОсноваФамилии = Лев(ЧастиФИО[0], Мин(СтрДлина(ЧастиФИО[0]), 4));
    ОсноваИмени = Лев(ЧастиФИО[1], Мин(СтрДлина(ЧастиФИО[1]), 3));
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   ФизическиеЛица.Наименование КАК ФИОИменительный
        |ИЗ
        |   Справочник.ФизическиеЛица КАК ФизическиеЛица
        |ГДЕ
        |   ФизическиеЛица.Наименование ПОДОБНО &ШаблонПоиска";
    
    Запрос.УстановитьПараметр("ШаблонПоиска", ОсноваФамилии + "% " + ОсноваИмени + "%");
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Пока Выборка.Следующий() Цикл
            // Используем стандартное прямое склонение найденного объекта 
            // для проверки, совпадет ли оно с нашим исходным родительным падежом
            ФИОИзБазыВРодПадеже = СклонитьФИОСтандартно(Выборка.ФИОИменительный, 2); // 2 - родительный падеж
            
            Если ВРег(ФИОИзБазыВРодПадеже) = ВРег(ФИОРодительный) Тогда
                Возврат Выборка.ФИОИменительный; // Нашли точное совпадение!
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Этот метод дает 100% точность для тех физлиц, которые уже занесены в систему в правильном падеже. Мы используем метод «обратной проверки»: берем кандидата в именительном падеже, склоняем его штатными средствами 1С в родительный и сравниваем с исходной строкой.

Метод 3. Интеграция с внешним облачным API стандартизации (DaData)

Если требуется бескомпромиссная точность на любых объемах данных, лучшим решением является использование веб-сервисов стандартизации. Сервис DaData предоставляет метод API «Стандартизация ФИО», который умеет распознавать ФИО, записанное в любом падеже (даже с опечатками и строчными буквами), определять пол и возвращать эталонную запись в именительном падеже.

Рассмотрим, как реализовать отправку HTTP-запроса к API DaData из модулей 1С:


Функция ПросклонятьЧерезDaData(Знач ИсходноеФИО) Экспорт
    
    // Для работы требуется регистрация на dadata.ru и получение ключей API
    Токен = "ВАШ_API_KEY";
    Секрет = "ВАШ_SECRET_KEY";
    
    HTTPСоединение = Новый HTTPСоединение("cleaner.dadata.ru", 443, , , , , Новый ЗащищенноеСоединениеOpenSSL());
    
    HTTPЗапрос = Новый HTTPЗапрос("/api/v1/clean/name");
    HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
    HTTPЗапрос.Заголовки.Вставить("Authorization", "Token " + Токен);
    HTTPЗапрос.Заголовки.Вставить("X-Secret", Секрет);
    
    // Формируем тело запроса в формате JSON (передаем ФИО в массиве)
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, Новый Массив(Новый Структура("source", ИсходноеФИО)));
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8);
    
    Попытка
        Ответ = HTTPСоединение.ОтправитьДляОбработки(HTTPЗапрос);
        Если Ответ.КодСостояния = 200 Тогда
            ЧтениеJSON = Новый ЧтениеJSON;
            ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8));
            РезультатАнализа = ПрочитатьJSON(ЧтениеJSON);
            
            Если ТипЗнч(РезультатАнализа) = Тип("Массив") И РезультатАнализа.Количество() > 0 Тогда
                ДанныеФИО = РезультатАнализа[0];
                
                // DaData возвращает уже структурированные данные в именительном падеже
                // Поля: result (полное ФИО), surname (фамилия), name (имя), patronymic (отчество)
                Если ЗначениеЗаполнено(ДанныеФИО.result) Тогда
                    Возврат ДанныеФИО.result;
                КонецЕсли;
            КонецЕсли;
        Иначе
            ЗаписьЖурналаРегистрации("ИнтеграцияDaData", УровеньЖурналаРегистрации.Ошибка, , , 
                "Ошибка сервиса DaData. Код состояния: " + Ответ.КодСостояния);
        КонецЕсли;
    Исключение
        ЗаписьЖурналаРегистрации("ИнтеграцияDaData", УровеньЖурналаРегистрации.Ошибка, , , 
            "Не удалось связаться с сервером DaData: " + ОписаниеОшибки());
    КонецПопытки;
    
    Возврат ИсходноеФИО; // Если не получилось, возвращаем исходную строку
    
КонецФункции

Преимущества: Абсолютная точность, исправление опечаток в процессе распознавания, разделение на Фамилию/Имя/Отчество и определение пола "из коробки".

Недостатки: Требуется подключение к интернету и регистрация в стороннем сервисе (на бесплатных тарифах действуют ограничения по количеству запросов в сутки).

Метод 4. Поиск в регистре склонений БСП (для баз 1С v8.3)

Если ваша конфигурация разработана на базе БСП (например, 1С:ЗУП, 1С:ERP, 1С:КА), в ней уже существует механизм кэширования склонений. Все рассчитанные ранее формы ФИО записываются в регистр сведений СклоненияПредставленийОбъектов.

Мы можем выполнить быстрый запрос к этому регистру. Если физическое лицо ранее уже выводилось в печатных формах документов в родительном падеже, запись об этом гарантированно есть в кэше базы данных.

Посмотрим на пример структуры запроса:


Функция ПолучитьФИОИзРегистраСклонений(ФИОРодительный) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Склонения.Объект КАК Объект,
        |   Склонения.Объект.Наименование КАК ИменительныйПадеж
        |ИЗ
        |   РегистрСведений.СклоненияПредставленийОбъектов КАК Склонения
        |ГДЕ
        |   Склонения.Представление = &ФИОРодительный";
        
    Запрос.УстановитьПараметр("ФИОРодительный", ФИОРодительный);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.ИменительныйПадеж;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Этот способ крайне эффективен для крупных баз данных (особенно в ЗУП), так как он мгновенно возвращает точные исходные данные без обращения к внешним серверам.

Резюме и рекомендации

Для достижения максимального результата рекомендуется использовать комбинированный подход:

  1. Сначала проверьте кэш склонений БСП (Метод 4), так как это самый быстрый способ получить 100% точные данные локально.
  2. Если кэш пуст, попробуйте найти сопоставление напрямую по справочнику физлиц (Метод 2).
  3. Если точных совпадений в вашей базе нет, обратитесь к внешнему API стандартизации (Метод 3), если позволяет сетевая безопасность вашей компании.
  4. В качестве финального резервного сценария (если внешние сервисы недоступны) используйте программный анализатор окончаний (Метод 1).
← На главную