При автоматизации обмена данными часто возникает задача загрузки внешних файлов формата Excel (XLSX) в 1С. Типичный сценарий: файл выгрузки из сторонней системы (например, СБИС) содержит отчет, где заголовками колонок выступают даты (например, "01.09.2023", "02.09.2023" и так далее). При попытке загрузить такой файл через ПостроительЗапроса разработчики часто сталкиваются с неприятным эффектом: заголовки колонок в итоговой ТаблицеЗначений начинают дублироваться, заменяться или вовсе превращаться в нечитаемый набор символов. В этой статье мы разберем, почему это происходит и как правильно обработать данные, чтобы загрузка прошла корректно.
Рассмотрим стандартный механизм, который часто используют для "ленивой" загрузки данных. Разработчик считывает файл в ТабличныйДокумент, а затем передает его область в ОписаниеИсточникаДанных для ПостроителяЗапроса. Проблема кроется в жестких требованиях платформы 1С к именованию колонок в объекте ТаблицаЗначений. Имя колонки должно соответствовать правилам именования переменных:
Когда ПостроительЗапроса анализирует строку заголовков Excel, содержащую даты (например, "11.09.2023"), он пытается автоматически "нормализовать" их. Точки заменяются на подчеркивания, а если в начале стоит цифра, платформа пытается добавить префикс. Однако при использовании метода Прочитать с параметром СпособЧтенияЗначенийТабличногоДокумента.Значение, 1С воспринимает ячейку не как текст, а как тип Дата. Внутренние алгоритмы нормализации при превращении даты в идентификатор могут давать сбой, что приводит к "зацикливанию" имен (например, когда колонки с 11 по 20 число начинают именоваться так же, как с 01 по 10).
Давайте проанализируем исходный код, который вызывает ошибку:
&НаСервере
Процедура ЗагрузитьИзФайлаExcelНаСервере(АдресВХ)
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ДД = ПолучитьИзВременногоХранилища(АдресВХ);
ДД.Записать(ИмяВременногоФайла);
ТабличныйДокумент = Новый ТабличныйДокумент;
// Использование СпособЧтенияЗначенийТабличногоДокумента.Значение приводит к типизации дат
ТабличныйДокумент.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
УдалитьФайлы(ИмяВременногоФайла);
ПЗ = Новый ПостроительЗапроса;
// Построитель пытается сам создать имена колонок из первой строки области
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабличныйДокумент.Область());
ПЗ.ЗаполнитьНастройки();
ПЗ.Выполнить();
ТаблицаЗначений = ПЗ.Результат.Выгрузить();
КонецПроцедуры
В данном примере ТаблицаЗначений на выходе будет содержать некорректные идентификаторы колонок, так как ПостроительЗапроса не справился с автоматическим преобразованием дат в имена переменных.
Чтобы избежать ошибок автоматической трансформации, мы можем программно изменить содержимое первой строки в ТабличномДокументе до того, как передадим его в ПостроительЗапроса. Суть метода заключается в добавлении текстового префикса (например, нижнего подчеркивания) и замене запрещенных символов.
Рассмотрим исправленный алгоритм:
// ... код загрузки файла в ТабличныйДокумент ...
// Проходим циклом по всем колонкам первой строки (заголовкам)
Для НомерКолонки = 1 По ТабличныйДокумент.ШиринаТаблицы Цикл
ТекущееЗначение = ТабличныйДокумент.Область(1, НомерКолонки).Текст;
// Формируем правильное имя переменной: добавляем "_" в начало и убираем точки
НовоеИмя = "Дата_" + СтрЗаменить(ТекущееЗначение, ".", "_");
// Записываем исправленное имя обратно в ячейку заголовка
ТабличныйДокумент.Область(1, НомерКолонки).Текст = НовоеИмя;
КонецЦикла;
// Теперь ПостроительЗапроса увидит корректные строковые идентификаторы
ПЗ = Новый ПостроительЗапроса;
ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабличныйДокумент.Область());
// ... дальнейшая работа с ПЗ ...
Этот способ позволяет сохранить "магию" построителя запросов (быструю типизацию строк), но исключает конфликты именования.
Если ваша платформа версии 8.3.10 и выше, рекомендуем отказаться от ТабличногоДокумента в пользу специализированного объекта ЧтениеДанныхXLSX. Это работает быстрее и дает полный контроль над процессом.
Проанализируем пример реализации:
ЧтениеXLSX = Новый ЧтениеДанныхXLSX;
ЧтениеXLSX.Открыть(ИмяВременногоФайла);
// Читаем первый лист
Страницы = ЧтениеXLSX.ПолучитьСтраницы();
Если Страницы.Количество() > 0 Тогда
ЧтениеXLSX.ВыбратьСтраницу(Страницы[0]);
КонецЕсли;
ТЗ = Новый ТаблицаЗначений;
// Читаем первую строку для создания колонок
Если ЧтениеXLSX.Прочитать() Тогда
Для НомерКолонки = 0 По ЧтениеXLSX.КоличествоКолонок() - 1 Цикл
ИмяКолонки = "Кол_" + НомерКолонки; // Или обработка текста из ЧтениеXLSX.Значение()
ТЗ.Колонки.Добавить(ИмяКолонки, , ЧтениеXLSX.Значение(НомерКолонки));
КонецЦикла;
КонецЕсли;
// Читаем данные построчно
Пока ЧтениеXLSX.Прочитать() Цикл
НоваяСтрока = ТЗ.Добавить();
Для НомерКолонки = 0 По ЧтениеXLSX.КоличествоКолонок() - 1 Цикл
НоваяСтрока[НомерКолонки] = ЧтениеXLSX.Значение(НомерКолонки);
КонецЦикла;
КонецЦикла;
ЧтениеXLSX.Закрыть();
Выясним, какой метод лучше подходит для вашей ситуации:
ТаблицеЗначений и контролируете каждый ТипЗнч.ЗагрузкаДанныхИзФайла. Она уже содержит все необходимые проверки на корректность имен и обработку ошибок.Важный совет: При чтении через ТабличныйДокумент всегда проверяйте параметр СпособЧтенияЗначенийТабличногоДокумента. Если вам важны именно строковые заголовки, используйте значение Текст, чтобы 1С не пыталась преобразовать "01.01.2023" в объект даты прямо в ячейке документа, что часто и становится причиной сбоев в работе ПостроителяЗапроса.