В процессе разработки в 1С:ERP 2.4 или ЗУП 3.1 часто возникает задача выгрузки персональных данных сотрудников (ФИО, серия и номер паспорта) во внешние системы через текстовые файлы. Рассмотрим, как эффективно решить эту задачу, избежав типичных ошибок производительности и используя возможности программного интерфейса (API) типовых решений.
Для реализации выгрузки нам необходимо пройти по строкам табличной части (например, «Реестр»), получить ссылки на сотрудников или физических лиц, извлечь их персональные данные из регистров сведений и сформировать строку определенного формата для записи в файл. В современных конфигурациях 1С существует два основных подхода к решению этой задачи:
В конфигурациях на базе 1С:ЗУП 3.1 и 1С:ERP 2.4 предусмотрен мощный инструмент — программный модуль КадровыйУчет. Функция КадровыеДанныеСотрудников позволяет получить все необходимые сведения одним запросом сразу по всему массиву сотрудников. Это избавляет от необходимости вручную описывать связи между таблицами физических лиц и их документов.
Разберем алгоритм реализации на сервере:
// Сначала соберем всех сотрудников из табличной части в массив
МассивСотрудников = Объект.Реестр.ВыгрузитьКолонку("Сотрудник");
// Определим список полей, которые нам нужны
// ДокументСерия, ДокументНомер — это предопределенные поля в API
Поля = "Фамилия, Имя, Отчество, ДокументСерия, ДокументНомер";
// Получаем таблицу значений с данными на текущую дату (или дату документа)
ДатаСведений = ТекущаяДата();
ТаблицаДанных = КадровыйУчет.КадровыеДанныеСотрудников(Истина, МассивСотрудников, Поля, ДатаСведений);
// Теперь у нас есть ТаблицаЗначений, которую можно быстро обработать
Для каждого СтрокаДанных Из ТаблицаДанных Цикл
// Формируем строку для файла
ТекстСтроки = СтрСоединить(Новый Массив, "*");
// Далее логика сборки строки...
КонецЦикла;
Важное преимущество: Метод КадровыеДанныеСотрудников автоматически проверяет права пользователя на чтение персональных данных. Если у пользователя нет прав на просмотр паспортов, поля будут пустыми, что предотвращает утечку конфиденциальной информации.
Если по каким-то причинам использование API невозможно, мы можем составить запрос к регистру сведений ДокументыФизическихЛиц. Проанализируем ситуацию: данные о паспортах хранятся в срезе последних этого регистра.
Внимание: Никогда не выполняйте запрос внутри цикла Для каждого. Это создает избыточную нагрузку на сервер (проблема N+1). Правильно — передать весь список физлиц в запрос один раз.
Рассмотрим пример правильного построения запроса:
&НаСервере
Функция ПолучитьТаблицуПаспортов(МассивФизЛиц)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизическиеЛица.Ссылка КАК ФизЛицо,
| ФизическиеЛица.Фамилия КАК Фамилия,
| ФизическиеЛица.Имя КАК Имя,
| ФизическиеЛица.Отчество КАК Отчество,
| Документы.Серия КАК Серия,
| Документы.Номер КАК Номер
|ИЗ
| Справочник.ФизическиеЛица КАК ФизическиеЛица
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДокументыФизическихЛиц.СрезПоследних(&ДатаСреза, ВидДокумента = &ВидДокумента) КАК Документы
| ПО Документы.Физлицо = ФизическиеЛица.Ссылка
|ГДЕ
| ФизическиеЛица.Ссылка В(&МассивФизЛиц)";
Запрос.УстановитьПараметр("МассивФизЛиц", МассивФизЛиц);
Запрос.УстановитьПараметр("ДатаСреза", ТекущаяДата());
// Обязательно указываем вид документа, чтобы не выгрузить СНИЛС или водительские права
Запрос.УстановитьПараметр("ВидДокумента", Справочники.ВидыДокументовФизическихЛиц.ПаспортРФ);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
В данном запросе мы используем ЛЕВОЕ СОЕДИНЕНИЕ, чтобы в выборку попали даже те сотрудники, у которых паспортные данные не заполнены. Параметр ВидДокумента критически важен, так как в регистре может храниться множество типов документов для одного человека.
После получения данных необходимо собрать их в текстовый документ. Рассмотрим процесс создания файла. Традиционно используется объект ТекстовыйДокумент, но для повышения производительности и корректной работы с кодировками рекомендуется использовать ЗаписьТекста.
Проанализируем процедуру выгрузки на клиенте:
&НаКлиенте
Процедура ВыгрузитьДанные(Команда)
// 1. Получаем данные с сервера (одним вызовом)
ТаблицаСведений = ПолучитьДанныеДляВыгрузкиСервер();
// 2. Определяем путь к файлу
ПутьКФайлу = "C:\Export\StaffData.txt"; // Пример статичного пути
// 3. Записываем данные
ФайлЗаписи = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.ANSI);
Для каждого Строка Из ТаблицаСведений Цикл
// Собираем массив полей для удобного соединения через разделитель "*"
МассивПолей = Новый Массив;
МассивПолей.Добавить(Строка.Фамилия);
МассивПолей.Добавить(Строка.Имя);
МассивПолей.Добавить(Строка.Отчество);
// Обработка пустых значений паспорта
Серия = ?(ЗначениеЗаполнено(Строка.Серия), Строка.Серия, "");
Номер = ?(ЗначениеЗаполнено(Строка.Номер), Строка.Номер, "");
МассивПолей.Добавить(Серия + " " + Номер);
// Формируем финальную строку
СтрокаДляЗаписи = СтрСоединить(МассивПолей, "*");
ФайлЗаписи.ЗаписатьСтроку(СтрокаДляЗаписи);
КонецЦикла;
ФайлЗаписи.Закрыть();
ПоказатьПредупреждение(, "Выгрузка завершена успешно!");
КонецПроцедуры
При работе с персональными данными и текстовыми файлами следует учитывать несколько моментов, которые часто упускают начинающие разработчики:
NULL. Перед записью в файл или конкатенацией строк обязательно преобразуйте его в пустую строку, иначе возникнет ошибка типов.ANSI (Windows-1251). Если же файл будет открываться в современных веб-сервисах, используйте UTF-8 (с BOM или без).СтрСоединить, вы гарантируете, что количество разделителей (звездочек) в строке будет всегда одинаковым, даже если у сотрудника нет отчества.ЗаписьЖурналаРегистрации, чтобы зафиксировать, какой пользователь и когда выгрузил список сотрудников в файл.Выясним причину, почему использование ЗаписьТекста предпочтительнее ТекстовыйДокумент. Объект ТекстовыйДокумент хранит все строки в оперативной памяти до момента вызова метода Записать(). Если в вашем реестре тысячи сотрудников, это может привести к значительному потреблению памяти. Объект ЗаписьТекста работает в режиме потока, записывая данные на диск сразу после вызова ЗаписатьСтроку(), что гораздо эффективнее для больших объемов данных.
Разберем финальный совет по оптимизации: если табличная часть документа очень большая, передавайте на сервер не всю таблицу, а только массив идентификаторов (ссылок) на объекты. Это сократит объем данных, передаваемых между клиентом и сервером.