При работе в 1С пользователи часто сохраняют сформированные печатные формы (документы, отчеты, претензии) на локальный диск в форматах PDF, Excel или MXL. По умолчанию платформа предлагает стандартное имя файла, совпадающее с синонимом макета печатной формы. Например, «Претензия (долг, пени, изъятие)».
Однако на практике бизнесу требуется, чтобы сохраненные файлы имели информативные названия. В имя файла часто необходимо автоматически добавлять номер и дату документа, наименование контрагента, договор или другие реквизиты. Разберем по шагам, как решить эту задачу правильно с точки зрения производительности и стандартов разработки 1С, включая использование механизмов Библиотеки стандартных подсистем (БСП).
Участники обсуждения на форуме пришли к решению изменять третий параметр процедуры вывода печатной формы в модуле менеджера документа. Рассмотрим этот код:
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"ПретензияДолгПениИзъятие",
"Претензия (долг, пени, изъятие) " + ТекКонтрагент + " " + ТекДоговор,
ТабличныйДокумент
);
Хотя этот способ кажется рабочим, он имеет три критических недостатка, которые могут привести к зависанию базы данных, ошибкам при сохранении файлов и некорректной работе при групповой печати:
ВывестиТабличныйДокументВКоллекцию определяет представление печатной формы в коллекции. Если пользователь выделит в списке 10 документов разным контрагентам и нажмет кнопку «Печать», система сформирует единый пакет. Изменение представления макета приведет к тому, что все документы пакета будут пытаться сохраниться под именем первого документа или вызовут ошибку уникальности в коллекции.ТекСсылка.Контрагент. Это классическая проблема производительности «N+1 запрос». При печати 100 документов система сделает 100 отдельных запросов к базе данных, что вызовет сильные задержки в работе сервера.ООО "Рога и Копыта"), а договоры — слэши и знаки номеров (например, Договор №12/34-А). Символы \, /, :, *, ?, ", <, >, | запрещены операционной системой для использования в именах файлов. Попытка записать файл с таким именем вызовет ошибку среды выполнения или сбросит имя файла на стандартное «Таблица.mxl».Проанализируем, как решить эту задачу правильно и обойти все вышеописанные проблемы.
В современных конфигурациях 1С, разработанных на базе БСП, у процедуры УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию есть седьмой (необязательный) параметр — ИмяФайлаПечатнойФормы. Именно он предназначен для динамического переопределения имени файла сохранения.
Этот параметр может принимать не просто строку, а целое Соответствие, где в качестве ключа передается ссылка на конкретный объект печати (документ), а в качестве значения — индивидуально сформированное имя файла. Это полностью решает проблему групповой печати!
Разберем пример правильной реализации в процедуре Печать модуля менеджера документа:
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// 1. Сначала оптимизированно получаем все нужные реквизиты ОДНИМ запросом
ТаблицаДанных = ПолучитьДанныеДляПечати(МассивОбъектов);
// 2. Формируем табличные документы (стандартная логика вашей печатной формы)
// ... (код формирования табличного документа ТабДок)
// 3. Выводим табличный документ в коллекцию
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"ПретензияДолгПениИзъятие",
"Претензия (долг, пени, изъятие)", // Статичное представление в интерфейсе 1С
ТабДок
);
// 4. Настраиваем индивидуальные имена файлов для каждого документа при сохранении
ПечатнаяФорма = УправлениеПечатью.СведенияОПечатнойФорме(КоллекцияПечатныхФорм, "ПретензияДолгПениИзъятие");
Если ПечатнаяФорма <> Неопределено Тогда
ИменаФайлов = Новый Соответствие;
Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
// Формируем красивое и безопасное имя файла
НомерДок = СтрокаДанных.Номер;
ДатаДок = Формат(СтрокаДанных.Дата, "ДФ=dd.MM.yyyy");
КонтрагентНаименование = СтрокаДанных.КонтрагентНаименование;
ДоговорНаименование = СтрокаДанных.ДоговорНаименование;
ШаблонИмени = СтрШаблон("Претензия № %1 от %2 (%3, %4)", НомерДок, ДатаДок, КонтрагентНаименование, ДоговорНаименование);
// Очищаем от недопустимых символов операционной системы
БезопасноеИмяФайла = ОчиститьИмяФайлаОтЗапрещенныхСимволов(ШаблонИмени);
ИменаФайлов.Вставить(СтрокаДанных.Ссылка, БезопасноеИмяФайла);
КонецЦикла;
// Передаем соответствие в БСП
ПечатнаяФорма.ИмяФайлаПечатнойФормы = ИменаФайлов;
КонецЕсли;
КонецПроцедуры
Чтобы избежать сильного падения производительности, данные для формирования имен файлов необходимо получать пакетно. Напишем вспомогательную функцию ПолучитьДанныеДляПечати, которая выполняет один быстрый запрос к базе данных вместо сотен мелких обращений:
Функция ПолучитьДанныеДляПечати(МассивОбъектов)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка КАК Ссылка,
| Номер КАК Номер,
| Дата КАК Дата,
| Контрагент.Наименование КАК КонтрагентНаименование,
| ДоговорКонтрагента.Наименование КАК ДоговорНаименование
|ИЗ
| Документ.Претензия КАК Претензия
|ГДЕ
| Ссылка В(&МассивОбъектов)";
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Если вам требуется получить только простые реквизиты без написания сложных запросов, можно использовать альтернативный метод БСП — функцию общего назначения. Выглядеть это будет следующим образом:
РеквизитыОбъектов = ОбщегоНазначения.ЗначенияРеквизитовОбъектов(МассивОбъектов, "Номер, Дата, Контрагент.Наименование, ДоговорКонтрагента.Наименование");
Этот метод также выполняет всего один запрос к СУБД под капотом и возвращает соответствие значений, исключая зависания системы при больших объемах данных.
Для предотвращения сбоев при сохранении файлов операционной системой обязательно нужно выполнять предварительную валидацию строки. Напишем функцию, которая удаляет или заменяет все недопустимые спецсимволы в имени файла на безопасные:
Функция ОчиститьИмяФайлаОтЗапрещенныхСимволов(ИсходноеИмя) Экспорт
// Если в конфигурации есть БСП, можно использовать готовую функцию:
// Возврат СтроковыеФункцииКлиентСервер.ЗаменитьНедопустимыеСимволыВИмениФайла(ИсходноеИмя, "_");
// Если пишем универсальное решение:
ЗапрещенныеСимволы = "/\:*?""<>|";
Результат = ИсходноеИмя;
Для Индекс = 1 По СтрДлина(ЗапрещенныеСимволы) Цикл
Символ = Сред(ЗапрещенныеСимволы, Индекс, 1);
Результат = СтрЗаменить(Результат, Символ, " "); // Заменяем запрещенный символ на пробел
КонецЦикла;
// Дополнительно удалим двойные пробелы, которые могли образоваться при заменах
Пока Найти(Результат, " ") > 0 Цикл
Результат = СтрЗаменить(Результат, " ", " ");
КонецЦикла;
Возврат СокрЛП(Результат);
КонецФункции
Если вы ведете разработку в полностью самописной конфигурации или старой конфигурации 1С, где отсутствует Библиотека стандартных подсистем, управлять именем файла при сохранении можно напрямую через свойства формируемого табличного документа.
У объекта ТабличныйДокумент существует стандартное свойство ИспользуемоеИмяФайла. Если вы заполните его при формировании макета, платформа 1С автоматически подставит данное значение в диалоговое окно выбора пути сохранения файла при нажатии пользователем кнопки «Сохранить как».
Посмотрим на пример реализации данного подхода:
ТабДок = Новый ТабличныйДокумент;
// ... (Заполнение макета данными)
// Задаем имя файла напрямую в свойствах табличного документа
ИмяДляСохранения = СтрШаблон("Претензия № %1 от %2", СсылкаНаДокумент.Номер, Формат(СсылкаНаДокумент.Дата, "ДФ=dd.MM.yyyy"));
ТабДок.ИспользуемоеИмяФайла = ОчиститьИмяФайлаОтЗапрещенныхСимволов(ИмяДляСохранения);
ТабДок.Показать();
Для реализации качественного механизма кастомизации имен сохраняемых файлов в 1С придерживайтесь следующих правил:
ИмяФайлаПечатнойФормы объекта сведений о печатной форме, передавая туда Соответствие ссылок и подготовленных строк. Это гарантирует корректную работу при групповой печати.