Фамилия и инициалы из полного ФИО в 1С 8.3: функция, хранение и пересчёт старых записей

· 10 мин

Содержание

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

Частая путаница: функции СокрЛ, СокрП и СокрЛП в 1С:Предприятии сокращают не слова, а пробелы по краям строки. Готовой функции «ФИО → инициалы» во встроенном языке платформы нет. Её пишут сами или берут из библиотеки типовой конфигурации. Ниже — рабочий код для 8.3, вариант для старых платформ, обработка пустых значений, автоматическое заполнение при вводе и пересчёт уже введённых данных.

Функции работы со строками, которые понадобятся

Весь разбор ФИО строится на шести-семи стандартных функциях. Полезно точно знать, что делает каждая.

ФункцияЧто делаетПример → результат
СокрЛ(Стр)убирает пробелы слева" Иванов" → "Иванов"
СокрП(Стр)убирает пробелы справа"Иванов " → "Иванов"
СокрЛП(Стр)убирает пробелы с обеих сторон" Иванов " → "Иванов"
ПустаяСтрока(Стр)Истина, если в строке только пробелы или ничего" " → Истина
Лев(Стр, N)первые N символовЛев("Иван", 1) → "И"
ВРег(Стр)перевод в верхний регистр"и" → "И"
СтрРазделить(Стр, Разд, ВключатьПустые)массив подстрок"Иванов Иван" → ["Иванов","Иван"]
СтрНайти(Стр, Подстр)позиция подстроки, 0 если нетСтрНайти("И.И.", ".") → 2

📌 Что важно на практике:

  • 🔹 СокрЛП не трогает пробелы внутри строки. "Иванов Иван" с двумя пробелами так и останется с двумя.
  • 🔹 СтрРазделить и СтрНайти появились в платформе 8.3.6. На более ранних используют приём со строками, он описан ниже.
  • 🔹 Третий параметр СтрРазделить ставьте в Ложь. Тогда двойные пробелы не дадут пустых элементов массива.
  • 🔹 Лев от пустой строки возвращает пустую строку, ошибки не будет. Ошибку даст обращение к несуществующему элементу массива, например Части[1], когда в ФИО одно слово.

В конфигурациях на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) уже есть общий модуль ФизическиеЛицаКлиентСервер с функцией ФамилияИнициалы. Прежде чем писать свою, найдите его поиском в дереве конфигурации и посмотрите параметры в своём релизе. В доработках типовой базы лучше вызывать готовую функцию, чем держать вторую копию логики.

Пример кода: как сократить имя и отчество до инициалов

Функцию удобно положить в общий модуль с флагами «Клиент (управляемое приложение)», «Сервер» и «Внешнее соединение». Тогда её можно вызывать и из формы, и из модуля объекта, и из обработки. Назовём модуль СокращениеФИО.

// Возвращает "Иванов И.И." или, при ИнициалыВперед = Истина, "И.И. Иванов".
Функция ФамилияИнициалы(Знач ПолноеИмя, ИнициалыВперед = Ложь) Экспорт

	ПолноеИмя = СтрЗаменить(ПолноеИмя, Символы.НПП, " ");
	ПолноеИмя = СтрЗаменить(ПолноеИмя, Символы.Таб, " ");
	ПолноеИмя = СокрЛП(ПолноеИмя);

	Если ПустаяСтрока(ПолноеИмя) Тогда
		Возврат "";
	КонецЕсли;

	Части = СтрРазделить(ПолноеИмя, " ", Ложь);
	Фамилия = Части[0];
	Инициалы = "";

	Для Индекс = 1 По Части.ВГраница() Цикл
		Часть = Части[Индекс];
		Если ЭтоЧастицаОтчества(Часть) Тогда
			Продолжить;
		ИначеЕсли СтрНайти(Часть, ".") > 0 Тогда
			Инициалы = Инициалы + ВРег(Часть); // уже инициал: "И." или "И.И."
		Иначе
			Инициалы = Инициалы + ИнициалСлова(Часть);
		КонецЕсли;
	КонецЦикла;

	Если ПустаяСтрока(Инициалы) Тогда
		Возврат Фамилия;
	ИначеЕсли ИнициалыВперед Тогда
		Возврат Инициалы + " " + Фамилия;
	Иначе
		Возврат Фамилия + " " + Инициалы;
	КонецЕсли;

КонецФункции

// "Анна-Мария" -> "А.-М."
Функция ИнициалСлова(Знач Слово)

	Результат = "";
	Для Каждого Кусок Из СтрРазделить(Слово, "-", Ложь) Цикл
		Если ЗначениеЗаполнено(Результат) Тогда
			Результат = Результат + "-";
		КонецЕсли;
		Результат = Результат + ВРег(Лев(Кусок, 1)) + ".";
	КонецЦикла;
	Возврат Результат;

КонецФункции

Функция ЭтоЧастицаОтчества(Знач Слово)

	Слово = НРег(Слово);
	Возврат Слово = "оглы" Или Слово = "кызы" Или Слово = "улы" Или Слово = "уулу";

КонецФункции

Как функция отработает на разных входных строках:

Входная строкаРезультатРезультат с ИнициалыВперед
Иванов Иван ИвановичИванов И.И.И.И. Иванов
петров пётрпетров П.П. петров
Мамедов Эльдар Рашид оглыМамедов Э.Р.Э.Р. Мамедов
Смирнова Анна-Мария ОлеговнаСмирнова А.-М.О.А.-М.О. Смирнова
Сидоров И. И.Сидоров И.И.И.И. Сидоров
пустая строкапустая строкапустая строка

Фамилию функция не трогает: регистр букв в ней остаётся таким, как ввёл пользователь. Если нужна нормализация, добавьте ВРег(Лев(Фамилия, 1)) + НРег(Сред(Фамилия, 2)). Но тогда пострадают фамилии вроде «Мак-Дональд» и «де Бриньи», поэтому без нужды этого лучше не делать.

Порядок «И.О. Фамилия» и неразрывный пробел

В расшифровке подписи по ГОСТ Р 7.0.97-2016 инициалы ставят перед фамилией: И.И. Иванов. Для этого и нужен второй параметр.

В печатной форме между инициалами и фамилией удобно ставить неразрывный пробел Символы.НПП вместо обычного. Тогда при переносе строки в ячейке макета инициалы не оторвутся от фамилии. Достаточно заменить " " на Символы.НПП в двух строках возврата.

Без СтрРазделить: вариант для платформ до 8.3.6

На старой платформе нет СтрРазделить. Классический приём — заменить пробелы переводами строки и читать результат построчно.

Функция ИнициалыСтаройПлатформой(Знач ПолноеИмя)

	Текст = СтрЗаменить(СокрЛП(ПолноеИмя), " ", Символы.ПС);
	Фамилия = "";
	Инициалы = "";

	Для Н = 1 По СтрЧислоСтрок(Текст) Цикл
		Слово = СокрЛП(СтрПолучитьСтроку(Текст, Н));
		Если ПустаяСтрока(Слово) Тогда
			Продолжить; // двойной пробел дал пустую строку
		КонецЕсли;
		Если ПустаяСтрока(Фамилия) Тогда
			Фамилия = Слово;
		Иначе
			Инициалы = Инициалы + ВРег(Лев(Слово, 1)) + ".";
		КонецЕсли;
	КонецЦикла;

	Возврат СокрП(Фамилия + " " + Инициалы);

КонецФункции

Вариант проще основного: он не разбирает двойные имена и частицы «оглы». Для старых баз этого обычно хватает. СокрП в конце убирает хвостовой пробел, если в строке была только фамилия.

Пустые значения и нестандартные ФИО: где ломается сокращение

Код, который работает на «Иванове Иване Ивановиче», часто падает на реальных справочниках. Вот что встречается чаще всего.

⚠️ Источники ошибок и некрасивых результатов:

  • 🔸 Пустое ФИО. Обращение Части[0] к пустому массиву вызывает ошибку «Значение индекса выходит за пределы диапазона». Поэтому проверка ПустаяСтрока стоит до разбиения.
  • 🔸 Только фамилия или фамилия и имя. Обращение Части[2] «в лоб» даёт ту же ошибку. Цикл по ВГраница() от неё защищает.
  • 🔸 Неразрывный пробел и табуляция. Попадают при копировании из Excel и из писем. СокрЛП их не считает разделителями слов, и ФИО остаётся одним словом. Отсюда две замены в начале функции.
  • 🔸 Уже сокращённое ФИО вида Иванов И.И.. Без проверки на точку получится Иванов И.: второй инициал потеряется.
  • 🔸 Частицы «оглы», «кызы». Без фильтра получится лишняя буква: Мамедов Э.Р.О.
  • 🔸 Значение типа не «Строка». Если в функцию попадёт ссылка или Неопределено, СтрЗаменить выдаст ошибку. На входе передавайте строку, например Строка(Ссылка) или реквизит Наименование.
Диагностика: выберите симптом

Автоматическое сокращение ФИО при вводе

Если краткое ФИО нужно видеть в списках и отборах, его хранят в отдельном реквизите и заполняют сами. Есть два места, где это можно сделать.

В форме: обработчик ПриИзменении

Пользователь меняет полное ФИО — краткое пересчитывается сразу, на клиенте:

&НаКлиенте
Процедура НаименованиеПриИзменении(Элемент)
	Объект.КраткоеФИО = СокращениеФИО.ФамилияИнициалы(Объект.Наименование);
КонецПроцедуры

Минус: при записи из обработки, загрузки или обмена форма не открывается, и реквизит останется старым.

В модуле объекта: ПередЗаписью

Надёжнее заполнять реквизит в модуле объекта. Он срабатывает при любой записи, а не только из формы:

Процедура ПередЗаписью(Отказ)
	Если ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	КраткоеФИО = СокращениеФИО.ФамилияИнициалы(Наименование);
КонецПроцедуры

Проверка ОбменДанными.Загрузка пропускает пересчёт при загрузке по обмену: там значение уже приходит из другой базы.

В типовой конфигурации не добавляйте реквизиты и не правьте модули напрямую: снятие с поддержки усложнит каждое обновление. Используйте расширение. Обработчик модуля объекта переопределяется в нём аннотацией &После("ПередЗаписью").

Регистр сведений для хранения сокращений

Когда справочник типовой и реквизит в него добавлять не хочется, краткие ФИО удобно держать в своём регистре сведений. Его можно создать прямо в расширении.

Структура регистра СокращенияФИО (непериодический, независимый):

ЭлементВидТип
ФизическоеЛицоизмерениессылка на справочник физических лиц
ФамилияИнициалыресурсСтрока, 60
ИнициалыФамилияресурсСтрока, 60

Запись при сохранении физлица — в расширении, в модуле объекта заимствованного справочника:

&После("ПриЗаписи")
Процедура Расш1_ПриЗаписи(Отказ)

	Если Отказ Или ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;

	Запись = РегистрыСведений.СокращенияФИО.СоздатьМенеджерЗаписи();
	Запись.ФизическоеЛицо  = Ссылка;
	Запись.ФамилияИнициалы = СокращениеФИО.ФамилияИнициалы(Наименование);
	Запись.ИнициалыФамилия = СокращениеФИО.ФамилияИнициалы(Наименование, Истина);
	Запись.Записать(Истина);

КонецПроцедуры

Главный выигрыш регистра — запросы. Разбить строку на слова средствами языка запросов неудобно, а готовое значение из регистра берётся обычным соединением:

ВЫБРАТЬ
	Физлица.Ссылка КАК ФизическоеЛицо,
	ЕСТЬNULL(Сокр.ФамилияИнициалы, Физлица.Наименование) КАК КраткоеФИО
ИЗ
	Справочник.ФизическиеЛица КАК Физлица
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СокращенияФИО КАК Сокр
		ПО Сокр.ФизическоеЛицо = Физлица.Ссылка

ЕСТЬNULL подставит полное ФИО, если записи в регистре ещё нет. Отчёт не покажет пустую строку.

В Бухгалтерии и ЗУП полное ФИО с историей изменений хранится в периодическом регистре ФИОФизическихЛиц. Если человек сменил фамилию, сокращение для документов прошлых периодов берите из записи на дату документа, а не из текущего наименования.

Где держать краткое ФИО в вашей базе?

Массовое обновление: как сократить ФИО у уже введённых записей

Новый реквизит или регистр пуст для всех записей, созданных раньше. Их заполняют разовой обработкой. Запускайте её на копии базы, затем на рабочей — сначала сделайте резервную копию.

&НаСервереБезКонтекста
Процедура ЗаполнитьСокращенияНаСервере()

	Запрос = Новый Запрос(
	"ВЫБРАТЬ
	|	Физлица.Ссылка КАК Ссылка,
	|	Физлица.Наименование КАК Наименование
	|ИЗ
	|	Справочник.ФизическиеЛица КАК Физлица
	|ГДЕ
	|	НЕ Физлица.ЭтоГруппа"); // уберите условие, если групп в справочнике нет

	Выборка = Запрос.Выполнить().Выбрать();
	Счетчик = 0;

	НачатьТранзакцию();
	Попытка
		Пока Выборка.Следующий() Цикл
			Запись = РегистрыСведений.СокращенияФИО.СоздатьМенеджерЗаписи();
			Запись.ФизическоеЛицо  = Выборка.Ссылка;
			Запись.ФамилияИнициалы = СокращениеФИО.ФамилияИнициалы(Выборка.Наименование);
			Запись.ИнициалыФамилия = СокращениеФИО.ФамилияИнициалы(Выборка.Наименование, Истина);
			Запись.Записать(Истина);

			Счетчик = Счетчик + 1;
			Если Счетчик % 500 = 0 Тогда
				ЗафиксироватьТранзакцию();
				НачатьТранзакцию();
			КонецЕсли;
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;

КонецПроцедуры

Почему так:

  1. Записи фиксируются пачками по 500. Одна транзакция на 50 000 физлиц долго держит блокировки, а запись по одной без транзакций идёт медленнее.
  2. При ошибке откатывается только текущая пачка. Уже записанное остаётся, и обработку можно запустить повторно: Записать(Истина) перезапишет строки без дублей.
  3. Регистр не меняет сами элементы справочника. Поэтому не срабатывают их обработчики и не меняется дата изменения объекта.

Если краткое ФИО хранится в реквизите справочника, а не в регистре, в цикле придётся получать объект через Выборка.Ссылка.ПолучитьОбъект() и записывать его. Это заметно медленнее. Флаг ОбменДанными.Загрузка = Истина ускорит запись, но отключит все проверки и обработчики объекта. Включайте его, только если понимаете, что именно он пропустит в вашей конфигурации.

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

Частые вопросы

Можно ли сократить ФИО прямо в запросе?
Средствами языка запросов разбить строку на слова неудобно и ненадёжно. Практичнее хранить готовое сокращение в реквизите или регистре сведений и брать его соединением.
Почему СокрЛП не убирает двойной пробел между фамилией и именем?
Эта функция удаляет пробелы только в начале и в конце строки. Пробелы внутри остаются. Их обходят третьим параметром СтрРазделить, равным Ложь.
Как получить только имя или только отчество из полного ФИО?
Разбейте строку через СтрРазделить и возьмите элемент с индексом 1 для имени и 2 для отчества. Перед этим проверьте ВГраница(), чтобы не получить ошибку на коротком ФИО.
Ставить ли пробел между инициалами: «И. И.» или «И.И.»?
В деловых документах принят вариант без пробела между инициалами и с пробелом перед фамилией: «И.И. Иванов». Функция выше формирует именно его.
Как сократить ФИО в макете печатной формы без кода?
В параметре макета можно указать выражение, но надёжнее заполнить параметр в модуле менеджера печати вызовом функции сокращения. Так одна логика работает во всех формах.

Источники

  • ГОСТ Р 7.0.97-2016 «Организационно-распорядительная документация. Требования к оформлению документов»