Как программно выгрузить табличную часть документа 1С в Excel без ошибок?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Выгрузка данных из табличной части (ТЧ) документов в формат Microsoft Excel — одна из наиболее частых задач в практике программиста 1С. Однако при ее реализации разработчики часто сталкиваются с неочевидными техническими проблемами: файлы открываются в режиме «Защищенного просмотра», система выдает непонятные ошибки при сложении чисел, а порядок строк в итоговом документе не совпадает с исходным. В этой статье мы подробно разберем, почему возникают эти проблемы, как исправить скрытые ошибки в коде и как реализовать выгрузку по современным стандартам платформы «1С:Предприятие 8.3».

Разбор скрытых ошибок в коде: почему программа ведет себя некорректно?

Для начала проанализируем ситуацию, с которой столкнулся автор темы, и выясним технические причины возникших сбоев. Это поможет нам избежать подобных ошибок в будущем.

1. Конфликт технологий: COM-объект против Табличного документа

В первоначальном коде автора создавался COM-объект Excel.Application, запускался процесс Excel, создавалась книга, а затем... данные записывались через стандартный механизм платформы 1С — ТабличныйДокумент.Записать(). При этом сам табличный документ (переменная ТаблицаСохранение) оставался абсолютно пустым! Программа создавала пустой файл на диске средствами 1С, затирая или повреждая структуру файла, созданного через COM. Именно поэтому файл Excel открывался пустым.

2. Ошибка «Защищенный просмотр. Обнаружена проблема с этим файлом»

Данное предупреждение безопасности в Microsoft Excel возникает по двум причинам:

  1. Несоответствие формата: При использовании устаревшего формата ТипФайлаТабличногоДокумента.XLS платформа 1С записывает данные в двоичном формате Excel 97-2003 или в виде HTML-разметки с расширением XLS. Современные версии Excel распознают эту подмену и блокируют файл как потенциально опасный.
  2. Локация файла: Если файл сохраняется в сетевую папку или временную директорию операционной системы, Excel автоматически применяет к нему повышенные меры безопасности.

3. Опечатка века: ловушка кириллицы и латиницы в переменных

В процессе обсуждения автор столкнулся с ошибкой: «Преобразование значения к типу Число не может быть выполнено» при попытке инкрементировать счетчик строк (K = K + 1). Проанализируем причину. В коде инициализации была написана строка:

К = 1; // Здесь использована русская буква «К»

А внутри цикла обхода данных было написано:

K = K + 1; // Здесь использована английская буква «K»

Для компилятора 1С русская «К» и английская «K» — это две абсолютно разные переменные. Поскольку английская переменная не была объявлена, ее значение по умолчанию равнялось Неопределено. Попытка сложить Неопределено + 1 как раз и приводит к аварийному завершению работы программы с ошибкой преобразования типов. Чтобы избежать таких ситуаций, настоятельно рекомендуется использовать понятные русскоязычные имена переменных, например, НомерСтроки или СчетчикСтрок.

4. Ошибка контекста при передаче Табличного документа

В процедуре вызова на клиенте автор передавал пустой табличный документ в качестве параметра функции сервера: ВыгрузитьВТБНаСервере(ТабДок). Однако на сервере первой строчкой функции было написано:

ТабДок = Новый ТабличныйДокумент;

Такое присвоение полностью уничтожает связь с объектом, который был передан из клиентского контекста. Серверная функция создавала новый локальный объект, заполняла его, но на клиент возвращался первоначальный пустой документ. Правильный подход — создавать объект непосредственно на сервере и возвращать его как результат работы функции.

Современный стандарт выгрузки данных в Excel

Рассмотрим по шагам, как правильно реализовать выгрузку данных без использования медленной и нестабильной технологии COM (которая к тому же не работает на серверах под управлением ОС Linux). Мы будем использовать встроенный объект ТабличныйДокумент и современный формат XLSX.

Шаг 1. Получение данных на сервере с правильной сортировкой

Чтобы данные в Excel-файле располагались в том же порядке, что и в табличной части документа на форме, в запросе обязательно нужно использовать поле НомерСтроки для упорядочивания. Проанализируем пример правильного серверного кода:


&НаСервере
Функция СформироватьТабличныйДокументНаСервере()
    
    ТабДок = Новый ТабличныйДокумент;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ВедомостьСписок.НомерСтроки КАК НомерСтроки,
        |   ВедомостьСписок.ФИО.Наименование КАК ФИОНаименование,
        |   ВедомостьСписок.ЛицевойСчет КАК ЛицевойСчет,
        |   ВедомостьСписок.Сумма КАК Сумма,
        |   ВедомостьСписок.Период КАК Период,
        |   ВедомостьСписок.Примечание КАК Примечание
        |ИЗ
        |   Документ.ВедомостьПодотчет.Список КАК ВедомостьСписок
        |ГДЕ
        |   ВедомостьСписок.Ссылка = &Ссылка
        |УПОРЯДОЧИТЬ ПО
        |   НомерСтроки"; // Сохраняем исходную сортировку ТЧ
        
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
    РезультатЗапроса = Запрос.Выполнить();
    
    Выборка = РезультатЗапроса.Выбрать();
    
    // Начнем заполнение со второй строки (первую оставим под шапку таблицы)
    ТекущийНомерСтроки = 2;
    
    // Запишем шапку таблицы для наглядности
    ТабДок.Область("R1C1").Текст = "№";
    ТабДок.Область("R1C2").Текст = "Сотрудник";
    ТабДок.Область("R1C3").Текст = "Лицевой счет";
    ТабДок.Область("R1C4").Текст = "Сумма";
    ТабДок.Область("R1C5").Текст = "Период";
    ТабДок.Область("R1C6").Текст = "Примечание";
    
    Пока Выборка.Следующий() Цикл
        
        // Заполняем ячейки по координатам строки и колонки
        ТабДок.Область("R" + ТекущийНомерСтроки + "C1").Текст = Выборка.НомерСтроки;
        ТабДок.Область("R" + ТекущийНомерСтроки + "C2").Текст = Выборка.ФИОНаименование;
        ТабДок.Область("R" + ТекущийНомерСтроки + "C3").Текст = Выборка.ЛицевойСчет;
        
        // Для числовых полей лучше использовать значение, чтобы в Excel данные были числами, а не текстом
        ТабДок.Область("R" + ТекущийНомерСтроки + "C4").СодержитЗначение = Истина;
        ТабДок.Область("R" + ТекущийНомерСтроки + "C4").ТипЗначения = Новый ОписаниеТипов("Число");
        ТабДок.Область("R" + ТекущийНомерСтроки + "C4").Значение = Выборка.Сумма;
        
        ТабДок.Область("R" + ТекущийНомерСтроки + "C5").Текст = Формат(Выборка.Период, "ДФ=dd.MM.yyyy");
        ТабДок.Область("R" + ТекущийНомерСтроки + "C6").Текст = Выборка.Примечание;
        
        // Увеличиваем счетчик строк на единицу
        ТекущийНомерСтроки = ТекущийНомерСтроки + 1;
        
    КонецЦикла;
    
    // Включаем отображение сетки, чтобы Excel-таблица выглядела аккуратно
    ТабДок.ОтображатьСетку = Истина;
    
    Возврат ТабДок;
    
КонецФункции

Шаг 2. Сохранение файла на клиенте в формате XLSX

Теперь рассмотрим клиентскую процедуру, которая вызывает серверный метод получения данных, предлагает пользователю выбрать путь для сохранения файла и записывает его в современном безопасном формате XLSX.


&НаКлиенте
Процедура ВыгрузитьВExcel(Команда)
    
    // Получаем заполненный табличный документ с сервера
    ТабДок = СформироватьТабличныйДокументНаСервере();
    
    // Инициализируем диалог выбора файла для сохранения
    РежимДиалога = РежимДиалогаВыбораФайла.Сохранение;
    ДиалогВыбора = Новый ДиалогВыбораФайла(РежимДиалога);
    ДиалогВыбора.ПолноеИмяФайла = "Реестр_сотрудников";
    ДиалогВыбора.МножественныйВыбор = Ложь;
    
    // Задаем фильтр только для современного формата XLSX
    Фильтр = "Таблица Excel (*.xlsx)|*.xlsx";
    ДиалогВыбора.Фильтр = Фильтр;
    ДиалогВыбора.Заголовок = "Выберите путь для сохранения файла реестра";
    
    Если ДиалогВыбора.Выбрать() Тогда
        
        ПутьКФайлу = ДиалогВыбора.ПолноеИмяФайла;
        
        Попытка
            // Используем современный тип файла XLSX для исключения ошибок совместимости
            ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
            ПоказатьПредупреждение(, "Файл успешно сохранен по пути: " + ПутьКФайлу);
        Исключение
            ВызватьИсключение "Не удалось записать файл на диск. Возможно, он открыт в другой программе. Системная ошибка: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке());
        КонецПопытки;
        
    Иначе
        ПоказатьПредупреждение(, "Выгрузка отменена пользователем.");
    КонецЕсли;
    
КонецПроцедуры

Использование Макетов: почему это лучше жесткого кодирования ячеек?

Прописывание координат ячеек в виде строк типа "R" + ТекущийНомерСтроки + "C3" — распространенный, но не самый лучший подход. При добавлении новых колонок в отчет или изменении их порядка вам придется переписывать весь программный код и пересчитывать индексы вручную.

Гораздо более гибким решением является использование Макетов табличных документов. Посмотрим на преимущества этого метода:

Рассмотрим пример кода вывода данных с использованием заранее созданного макета с именем МакетРеестра:


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

Использование этого метода существенно ускоряет разработку и упрощает дальнейшую поддержку программного кода. Если пользователю потребуется изменить шрифт, добавить логотип компании или объединить ячейки — вы сделаете это непосредственно в макете конфигуратора за 2 минуты без изменения программного кода выгрузки.

← На главную