При обновлении конфигурации «Управление производственным предприятием» (УПП, редакция 1.3) многие разработчики и системные администраторы сталкиваются с жестким требованием фирмы «1С»: для полноценного формирования отчетности в ПФР (ныне СФР) по формам СЗВ-М, СЗВ-СТАЖ, СЗВ-КОРР, ОДВ-1 и СЗВ-ИСХ требуется использовать технологическую платформу «1С:Предприятие» версии не ниже 8.3.9.1818. Это связано с тем, что контролирующие органы требуют предоставлять файлы отчетности в кодировке UTF-8 без сигнатуры BOM (Byte Order Mark).
В этой статье мы подробно разберем, почему возникает эта проблема, рассмотрим системные ограничения платформы 8.2, а также изучим несколько практических и элегантных способов обойти это ограничение программным путем, если ваша база по-прежнему вынуждена работать на платформе 8.2.
Прежде чем перейти к коду, давайте разберем физическую суть проблемы. Сигнатура BOM (Byte Order Mark) — это последовательность байтов в самом начале текстового файла (для UTF-8 это три байта: EF BB BF), которая указывает программам-ридерам на то, в каком формате записан текст. Платформа «1С:Предприятие 8.2» при записи файлов в формате UTF-8 всегда принудительно добавляет эти три байта.
Однако приемные шлюзы контролирующих органов жестко настроены на прием файлов UTF-8 строго без BOM. Наличие этих трех байтов в начале XML-документа приводит к ошибке парсинга, и отчетность отклоняется. Начиная с версии 8.3.9, в платформе 1С появился штатный параметр, позволяющий отключать запись BOM, но в 8.2 такого механизма «из коробки» нет.
---
Рассмотрим очень красивое и математически точное решение проблемы. Как известно, кодирование в Base64 переводит бинарные данные в текстовую строку. Поскольку 3 байта сигнатуры BOM в UTF-8 (0xEF 0xBB 0xBF) занимают ровно 24 бита, при преобразовании в Base64 они превращаются ровно в 4 символа в самом начале результирующей строки (строка всегда начинается с символов 77u/).
Таким образом, мы можем записать файл стандартными средствами 1С (с BOM), затем прочитать его как двоичные данные, преобразовать в Base64-строку, отрезать первые 4 символа и записать обратно. Давайте разберем этот алгоритм на примере:
// Создаем текстовый документ и записываем его в UTF-8 (платформа принудительно добавит BOM)
СписокЭкспорт = Новый ТекстовыйДокумент;
СписокЭкспорт.ДобавитьСтроку(ТекстФайлаОтчетности);
СписокЭкспорт.Записать(ИмяФайлаВыгрузки, КодировкаТекста.UTF8);
// Читаем только что записанный файл как бинарные данные
КонвертируемыйФайл = Новый ДвоичныеДанные(ИмяФайлаВыгрузки);
// Кодируем бинарные данные в строку Base64
Строка64 = Base64Строка(КонвертируемыйФайл);
// Отрезаем первые 4 символа (это и есть наши 3 байта BOM)
Строка64БезBOM = Прав(Строка64, СтрДлина(Строка64) - 4);
// Декодируем строку обратно в двоичные данные
ДляЗаписиВФайл = Base64Значение(Строка64БезBOM);
// Перезаписываем исходный файл очищенными бинарными данными
ДляЗаписиВФайл.Записать(ИмяФайлаВыгрузки);
Этот способ крайне надежен, так как не зависит от кодировки операционной системы и работает исключительно на уровне манипуляций с байтами в оперативной памяти.
---
Проанализируем еще один интересный обходной путь. Объект ЗаписьТекста в платформе 1С имеет особенность: если мы открываем файл для записи в режиме дозаписи (параметр Истина), и файл уже физически существует на диске, то платформа не пытается повторно инициализировать заголовок файла и, соответственно, не добавляет в него сигнатуру BOM. Мы можем использовать это поведение искусственно.
Давайте реализуем этот алгоритм по шагам:
ЗаписьТекста, но уже в режиме добавления текста (дозаписи) и пишем туда наши данные в формате UTF-8.Посмотрим на пример реализации данного метода:
// Шаг 1: Создаем пустой файл в кодировке ANSI
ЗТ = Новый ЗаписьТекста(ИмяФайлаВыгрузки, КодировкаТекста.ANSI);
ЗТ.Закрыть();
// Шаг 2: Открываем созданный файл в режиме дозаписи (параметр Истина)
ЗТ = Новый ЗаписьТекста(ИмяФайлаВыгрузки, , , Истина, Символы.ПС);
ЗТ.Записать(ТекстФайлаОтчетности);
ЗТ.Закрыть();
В результате на диске сформируется корректный файл UTF-8, но без сигнатуры BOM в начале. Этот метод значительно быстрее предыдущего, так как не требует двойного чтения файла на диске и тяжелых операций кодирования/декодирования больших строк.
---
Выясним, как интегрировать данное решение непосредственно в код конфигурации УПП редакции 1.3, чтобы пользователи могли беспрепятственно выгружать отчетность кнопками из стандартного интерфейса. Основная логика выгрузки регламентированных отчетов ПФР сосредоточена в общем модуле ПроцедурыПерсонифицированногоУчета, в процедуре ЗаписатьФайлДокументаНаДискету.
Нам необходимо переопределить поведение системы в блоке выгрузки UTF-8. Ниже представлен готовый участок кода, заменяющий стандартный вызов:
// Находим место выгрузки в процедуре ЗаписатьФайлДокументаНаДискету
Если Не ДобавлятьBOM И ТипКодировки = "UTF8" Тогда
// Обходим стандартное предупреждение платформы о необходимости версии 8.3.9
// Код закомментирован для исключения назойливых окон
// Если Не ЕстьПоддержкаУправленияBOM() Тогда ...
Иначе
ДобавлятьBOM = Истина;
КонецЕсли;
Если ВыбиратьКаталог Тогда
Длг = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Длг.Каталог = УправлениеПользователями.ПолучитьЗначениеПоУмолчанию(ТекущийПользователь, "ОсновнойКаталогФайлов");
Длг.Заголовок = "Укажите каталог";
Если Длг.Выбрать() Тогда
ИмяФайла = Длг.Каталог + "\" + ИмяФайла;
Если ДобавлятьBOM Тогда
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.УстановитьТекст(ТекстФайла);
ТекстовыйДокумент.Записать(ИмяФайла, КодировкаФайла);
Иначе
// Наш обходной путь для выгрузки UTF-8 без BOM на платформе 8.2
Если ТипКодировки = "UTF8" Тогда
ЗТ = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.ANSI);
ЗТ.Закрыть();
ЗТ = Новый ЗаписьТекста(ИмяФайла, , , Истина, Символы.ПС);
ЗТ.Записать(ТекстФайла);
ЗТ.Закрыть();
Иначе
Запись = Новый ЗаписьXML;
Запись.ОткрытьФайл(ИмяФайла, ТипКодировки, Ложь);
Запись.ЗаписатьБезОбработки(ТекстФайла);
Запись.Закрыть();
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(ТекстОповещения) Тогда
ТекстОповещения = "Файл записан под именем: ";
КонецЕсли;
Предупреждение(ТекстОповещения + ИмяФайла);
КонецЕсли;
КонецЕсли;
Внедрение этого патча позволит бухгалтерам продолжать сдавать отчетность в штатном режиме без модернизации серверной инфраструктуры до версии 8.3.
---
Существует еще один малоизвестный, но крайне эффективный способ записи текстовых файлов без BOM на старых версиях платформы 1С — это использование кодировки CESU-8.
CESU-8 (Compatibility Encoding Scheme for UTF-16: 8-Bit) во многом идентична стандартной UTF-8, за исключением способа кодирования символов за пределами базовой многоязыковой плоскости (например, эмодзи или редких иероглифов, которые практически никогда не встречаются в отчетности ПФР). Самое важное для нас преимущество заключается в том, что платформа 1С при записи файлов через ЗаписьТекста с указанием КодировкаТекста.CESU-8 генерирует бинарный поток, идентичный UTF-8, но **не добавляет** сигнатуру BOM.
Пример использования:
ЗТ = Новый ЗаписьТекста(ИмяФайлаВыгрузки, КодировкаТекста.CESU-8);
ЗТ.Записать(ТекстФайлаОтчетности);
ЗТ.Закрыть();
Этот способ позволяет обойтись вообще без создания временных файлов или манипуляций с Base64, однако следует помнить, что в редких случаях валидаторы принимающих органов могут проверять XML-декларацию файла (если там принудительно прописано наименование кодировки).
---
Несмотря на то, что программисты успешно находят лазейки для обхода ограничений платформы 8.2, системным администраторам и руководству стоит понимать, что жизненный цикл этой платформы давно завершен. Рассмотрим ключевые проблемы совместимости 1С 8.2 с современным окружением:
Резюме: Если перед вами стоит оперативная задача сдать отчетность "здесь и сейчас" без обновления платформы, используйте предложенные варианты с удалением BOM через ANSI или Base64. Однако параллельно обязательно планируйте переход на платформу 8.3 (в режиме совместимости со старыми конфигурациями она работает превосходно и стабильно) и постепенную миграцию на современные решения уровня 1С:ERP.