При разработке внешних печатных форм для кадровых документов нередко возникает ситуация: в документе указано несколько сотрудников, однако в печатную форму выводятся данные только по первому человеку либо запрос возвращает ошибку несовместимости типов. Разберем типичные ошибки, проанализируем причины их возникновения и рассмотрим правильные варианты реализации выборки данных.
Посмотрим на исходный фрагмент кода, вызвавший затруднение:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФИОФизЛицСрезПоследних.Фамилия КАК Фамилия,
| ФИОФизЛицСрезПоследних.Имя КАК Имя,
| ФИОФизЛицСрезПоследних.Отчество КАК Отчество
| ИЗ
| РегистрСведений.ФИОФизЛиц.СрезПоследних(&Дата, ) КАК ФИОФизЛицСрезПоследних
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПриемНаРаботуВОрганизацию КАК ПриемНаРаботуВОрганизацию
| ПО ФИОФизЛицСрезПоследних.ФизЛицо = ПриемНаРаботуВОрганизацию.РаботникиОрганизации.ФизЛицо
|ГДЕ
| ПриемНаРаботуВОрганизацию.РаботникиОрганизации.ФизЛицо = &ФизЛицо";
Запрос.УстановитьПараметр("ФизЛицо", СтрДокП.Сотрудник.Физлицо);
Запрос.УстановитьПараметр("Дата", ТекущаяДата());
Выясним причину некорректной работы такого запроса. Здесь допущено сразу несколько грубых архитектурных ошибок:
Документ.ПриемНаРаботуВОрганизацию обращается к объекту (шапке) документа, а не к его строкам. Попытка связать поле через ПриемНаРаботуВОрганизацию.РаботникиОрганизации.ФизЛицо некорректна, так как табличная часть — это отдельная подчиненная таблица базы данных.&ФизЛицо устанавливается из конкретной переменной СтрДокП. Если этот код выполняется вне цикла или переменная указывает на первую строку, запрос физически не сможет вернуть данные по остальным сотрудникам.Ссылка = &Ссылка платформа выдает ошибку: «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов». В параметр &Ссылка необходимо передавать ссылку на сам документ (СсылкаНаОбъект), а не на его табличную часть.Подвал заполняются в цикле выборки, а сама область выводится в результирующий табличный документ после окончания цикла, в итоговый печатный документ попадут данные исключительно последней итерации.Рассмотрим по шагам правильное построение запроса. Для того чтобы получить актуальные ФИО всех сотрудников, указанных в документе, необходимо обращаться непосредственно к табличной части РаботникиОрганизации и соединять ее со срезом последних регистра сведений ФИОФизЛиц.
Посмотрим на пример правильного текста запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПриемТЧ.НомерСтроки КАК НомерСтроки,
| ПриемТЧ.ФизЛицо КАК ФизЛицо,
| ФИОФизЛицСрезПоследних.Фамилия КАК Фамилия,
| ФИОФизЛицСрезПоследних.Имя КАК Имя,
| ФИОФизЛицСрезПоследних.Отчество КАК Отчество
|ИЗ
| Документ.ПриемНаРаботуВОрганизацию.РаботникиОрганизации КАК ПриемТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФИОФизЛиц.СрезПоследних(&ДатаСреза, ) КАК ФИОФизЛицСрезПоследних
| ПО ПриемТЧ.ФизЛицо = ФИОФизЛицСрезПоследних.ФизЛицо
|ГДЕ
| ПриемТЧ.Ссылка = &Ссылка
|УПОРЯДОЧИТЬ ПО
| ПриемТЧ.НомерСтроки";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Запрос.УстановитьПараметр("ДатаСреза", СсылкаНаОбъект.Дата);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Заполнение параметров области строки или подвала
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
Фамилия = Выборка.Фамилия;
Имя = Выборка.Имя;
Отчество = Выборка.Отчество;
// Формирование инициалов
ФИОИнициалы = "";
Если ЗначениеЗаполнено(Имя) Тогда
ФИОИнициалы = Лев(Имя, 1) + ".";
КонецЕсли;
Если ЗначениеЗаполнено(Отчество) Тогда
ФИОИнициалы = ФИОИнициалы + " " + Лев(Отчество, 1) + ".";
КонецЕсли;
ФИОИнициалы = СокрЛП(ФИОИнициалы + " " + Фамилия);
ОбластьСтрока.Параметры.ФИОСотрудника = ФИОИнициалы;
ТабличныйДокумент.Вывести(ОбластьСтрока);
КонецЦикла;
Важные замечания по оптимизации и логике:
ТекущаяДата() для исторических документов. Если сотрудник сменил фамилию после приема на работу, использование текущей даты приведет к искажению данных в печатной форме за прошлый период. Срез необходимо делать на дату документа (СсылкаНаОбъект.Дата) либо на дату приема на работу конкретного сотрудника.ЛЕВОЕ СОЕДИНЕНИЕ, чтобы при случайном отсутствии записи в регистре сведений строка документа не пропадала из печатной формы полностью.ТабличныйДокумент.Вывести() должен вызываться на каждой итерации цикла по выборке.Разберем вариант, когда строку с фамилией и инициалами требуется сформировать сразу на уровне базы данных. В языке запросов 1С для выделения первых букв имени и отчества применяется функция ПОДСТРОКА.
Посмотрим на пример запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПриемТЧ.НомерСтроки КАК НомерСтроки,
| ВЫБОР
| КОГДА ФИОФизЛицСрезПоследних.Фамилия ЕСТЬ NULL
| ТОГДА ПриемТЧ.ФизЛицо.Наименование
| ИНАЧЕ
| ФИОФизЛицСрезПоследних.Фамилия +
| ВЫБОР
| КОГДА ПОДСТРОКА(ФИОФизЛицСрезПоследних.Имя, 1, 1) <> """"
| ТОГДА "" "" + ПОДСТРОКА(ФИОФизЛицСрезПоследних.Имя, 1, 1) + "".""
| ИНАЧЕ """"
| КОНЕЦ +
| ВЫБОР
| КОГДА ПОДСТРОКА(ФИОФизЛицСрезПоследних.Отчество, 1, 1) <> """"
| ТОГДА "" "" + ПОДСТРОКА(ФИОФизЛицСрезПоследних.Отчество, 1, 1) + "".""
| ИНАЧЕ """"
| КОНЕЦ
| КОНЕЦ КАК ФИОСИнициалами
|ИЗ
| Документ.ПриемНаРаботуВОрганизацию.РаботникиОрганизации КАК ПриемТЧ
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФИОФизЛиц.СрезПоследних(&ДатаСреза, ) КАК ФИОФизЛицСрезПоследних
| ПО ПриемТЧ.ФизЛицо = ФИОФизЛицСрезПоследних.ФизЛицо
|ГДЕ
| ПриемТЧ.Ссылка = &Ссылка
|УПОРЯДОЧИТЬ ПО
| ПриемТЧ.НомерСтроки";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Запрос.УстановитьПараметр("ДатаСреза", СсылкаНаОбъект.Дата);
РезультатЗапроса = Запрос.Выполнить();
Такой подход избавляет от необходимости дополнительной строковой обработки параметров в цикле вывода макета.
Проанализируем альтернативный путь: в типовых конфигурациях 1С уже реализованы готовые экспортные методы, избавляющие разработчика от ручного написания запросов к периодическим регистрам сведений.
Для форматирования строки представления физического лица доступна функция общего модуля:
ПредставлениеФИО = ОбщегоНазначенияЗК.ФамилияИнициалыФизЛица(Выборка.ФизЛицоНаименование);
Если требуется получить структуру с отдельными полями (Фамилия, Имя, Отчество) на нужную дату, используется метод:
СтруктураФИО = УправлениеПерсоналом.ПолучитьФИОФизЛица(ФизЛицо, ДатаДокумента);
В современных конфигурациях структура регистра называется ФИОФизическихЛиц, а преобразование строки выполняется общими модулями БСП:
// Получение фамилии с инициалами из полного ФИО
Инициалы = ФизическиеЛицаКлиентСервер.ФамилияИнициалы(ПолноеФИО);
Использование типовых функций снижает риск появления ошибок при изменении структуры метаданных и гарантирует единообразное форматирование во всех печатных формах программы.