В этой статье мы подробно разберем распространенную задачу: загрузку данных из файла Excel в 1С с последующим отображением или обработкой. Мы проанализируем типичные ошибки, возникающие при переходе на управляемые формы и клиент-серверную архитектуру, рассмотрим, почему файл "не виден" на сервере, и исправим ошибки в коде обработки данных. Для решения этого вопроса поможет универсальная загрузка Excel в любые документы 1С.
Часто возникает ситуация, когда необходимо создать обработку, где пользователь выбирает файл Excel (xls или xlsx) на своем компьютере, а система должна прочитать этот файл и вывести данные в табличный документ или загрузить их в справочники/документы. Для этой задачи есть универсальный загрузчик данных Excel для 1С.
В нашем случае мы сталкиваемся с ошибкой: "Ошибка при вызове метода контекста (Прочитать). Ошибка доступа к выбранному файлу" или же табличный документ просто остается пустым. При этом путь к файлу выбран корректно.
Давайте разберем, почему так происходит. В управляемом приложении код разделен на исполняемый &НаКлиенте (на компьютере пользователя) и &НаСервере (где работает сервер 1С). Когда пользователь выбирает файл через диалоговое окно, он получает путь к файлу на своем локальном диске (например, C:\Users\User\Desktop\Report.xls). Если мы передадим этот строковый путь на сервер и попытаемся там выполнить метод чтения, сервер будет искать этот путь на своем локальном диске, где такого файла, разумеется, нет.
Чтобы сервер "увидел" файл, нам необходимо физически передать данные файла с клиента на сервер. В 1С для этого используется механизм Временного хранилища. Для автоматизации этого процесса подойдёт загрузка документов из Excel с распознаванием структуры.
Рассмотрим правильный алгоритм действий:
Давайте напишем корректный код выбора файла и передачи его на сервер.
Вместо того чтобы просто запоминать путь, нам нужно сразу получить двоичные данные файла. Используем объект ДиалогВыбораФайла.
&НаКлиенте
Процедура ПутьКФайлуНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выбор файла Excel";
Диалог.Фильтр = "Excel файлы (*.xls;*.xlsx)|*.xls;*.xlsx";
Диалог.МножественныйВыбор = Ложь;
// Если файл выбран
Если Диалог.Выбрать() Тогда
// Сохраняем путь для отображения пользователю (визуально)
Объект.ПутьКФайлу = Диалог.ПолноеИмяФайла;
КонецЕсли;
КонецПроцедуры
Теперь рассмотрим процедуру, которая запускается по кнопке "Загрузить". Здесь мы помещаем файл в хранилище.
&НаКлиенте
Процедура ПрочитатьФайл(Команда)
Если ПустаяСтрока(Объект.ПутьКФайлу) Тогда
Сообщить("Файл не выбран!");
Возврат;
КонецЕсли;
// Читаем данные файла в двоичный вид на клиенте
Попытка
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(Объект.ПутьКФайлу);
Исключение
Сообщить("Не удалось прочитать файл. Возможно, он открыт в другой программе или нет прав доступа.");
Возврат;
КонецПопытки;
// Помещаем во временное хранилище
// УникальныйИдентификатор передаем, чтобы хранилище жило пока жива форма
АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, УникальныйИдентификатор);
// Вызываем серверную функцию для обработки
ТабДокРезультат = ПрочитатьФайлНаСервере(АдресВоВременномХранилище);
// Отображаем результат, если он есть
Если ТабДокРезультат <> Неопределено Тогда
ТабДокРезультат.Показать("Загруженные данные");
КонецЕсли;
КонецПроцедуры
Теперь перейдем к серверной части. Нам нужно получить файл из хранилища, сохранить его во временную папку сервера и прочитать методом ТабличныйДокумент.Прочитать.
&НаСервере
Функция ПрочитатьФайлНаСервере(АдресДанных)
ТабДок = Новый ТабличныйДокумент;
// Получаем данные из хранилища
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресДанных);
// Создаем имя для временного файла на сервере с правильным расширением
ПутьКФайлуНаСервере = ПолучитьИмяВременногоФайла("xls");
// Записываем данные на диск сервера
ДанныеФайла.Записать(ПутьКФайлуНаСервере);
Попытка
// Читаем Excel в табличный документ 1С
ТабДок.Прочитать(ПутьКФайлуНаСервере, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
// Обязательно выводим понятное описание ошибки
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Ошибка чтения файла: " + ОписаниеОшибки();
Сообщение.Сообщить();
Возврат Неопределено;
КонецПопытки;
// Удаляем временный файл, чтобы не засорять диск сервера
УдалитьФайлы(ПутьКФайлуНаСервере);
// Возвращаем готовый табличный документ на клиент
Возврат ТабДок;
КонецФункции
В исходной теме пользователь пытался обработать прочитанные данные, перебирая строки табличного документа. В этом коде содержалось несколько критических ошибок, которые мы должны разобрать, чтобы не повторять их.
В коде встречалась строка:
// ОШИБКА
СтрокаТаблица.Организация = Справочники.Организации.НайтиПоРеквизиту("ГоловнаяОрганизация");
Метод НайтиПоРеквизиту обязательно требует второго параметра — значения, которое мы ищем. Если мы хотим найти организацию, у которой реквизит "ГоловнаяОрганизация" равен Истина (предполагая, что это Булево), код должен выглядеть так:
// ПРАВИЛЬНО (если ищем булево значение)
СтрокаТаблица.Организация = Справочники.Организации.НайтиПоРеквизиту("ГоловнаяОрганизация", Истина);
Если же "ГоловнаяОрганизация" — это ссылка на другую организацию, то поиск должен осуществляться по другому принципу. Без второго параметра код вызовет ошибку выполнения.
В примере пользователя данные сначала считывались в ТаблицаЗначений, а затем использовался ПостроительОтчета:
ПостроительОтчета = Новый ПостроительОтчета;
ПостроительОтчета.ИсточникДанных = Новый ОписаниеИсточникаДанных(ДанныеФайла);
ПостроительОтчета.Вывести(ТабДок); // Ошибка логики
Проблема здесь в том, что метод Вывести() у построителя отчета полностью очищает переданный ему табличный документ и строит в нем новый отчет на основе источника данных. Если ваша цель — показать пользователю исходный файл Excel, который вы только что прочитали через ТабДок.Прочитать(), то использование построителя отчета сотрет все прочитанные данные и нарисует простую таблицу.
Если же цель — сформировать реестр документов, то использование построителя оправдано, но тогда нет смысла возвращать исходный ТабДок.
При обращении к ячейкам табличного документа по адресу R1C1 (строка, колонка) важно правильно форматировать номера строк.
// Потенциальная ошибка
Адрес = "R" + НомерСтроки + "C" + 2;
В 1С числовое представление по умолчанию включает неразрывные пробелы для разделения триад (групп разрядов). Если НомерСтроки станет больше 999 (например, 1000), строка превратится в "1 000". Адрес ячейки станет "R1 000C2", что является некорректным синтаксисом и вызовет ошибку.
Правильный подход: всегда использовать функцию Формат с отключением группировки:
НомерСтрокиБезПробелов = Формат(НомерСтроки, "ЧГ=0");
Адрес = "R" + НомерСтрокиБезПробелов + "C" + 2;
Ячейка = ТабДок.Область(Адрес);
Если наша задача не просто показать Excel, а программно разобрать данные, то алгоритм будет следующим:
&НаСервере
Функция РазобратьФайлВТаблицу(АдресДанных)
// 1. Получаем файл и читаем в ТабДок (как описано выше)
ТабДок = Новый ТабличныйДокумент;
// ... (код получения файла и чтения, см. выше) ...
// 2. Подготавливаем таблицу для результата
ДанныеФайла = Новый ТаблицаЗначений;
ДанныеФайла.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("Строка"));
ДанныеФайла.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// 3. Обходим строки
ВысотаТаблицы = ТабДок.ВысотаТаблицы;
Для НомерСтр = 2 По ВысотаТаблицы Цикл
// Формируем формат номера строки без пробелов
СтрИндекс = Формат(НомерСтр, "ЧГ=0");
// Получаем значение из 2-й колонки (допустим, ФИО)
ТекстСотрудник = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C2").Текст);
// Получаем значение из 5-й колонки (Сумма)
ТекстСумма = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C5").Текст);
// Если нашли ключевое слово (как в примере автора)
ЧетвертаяКолонка = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C4").Текст);
Если ЗначениеЗаполнено(ТекстСотрудник) Тогда
НоваяСтрока = ДанныеФайла.Добавить();
НоваяСтрока.Сотрудник = ТекстСотрудник;
// Преобразуем сумму. В Excel могут быть неразрывные пробелы
ТекстСумма = СтрЗаменить(ТекстСумма, Символы.НПП, "");
ТекстСумма = СтрЗаменить(ТекстСумма, " ", "");
Попытка
НоваяСтрока.Сумма = Число(ТекстСумма);
Исключение
НоваяСтрока.Сумма = 0;
КонецПопытки;
КонецЕсли;
КонецЦикла;
Возврат ДанныеФайла;
КонецФункции
При работе с загрузкой файлов в управляемых формах 1С всегда помните о клиент-серверном разделе. Файл всегда сначала попадает на Клиент, и ваша задача — передать его на Сервер через Временное хранилище. Также будьте внимательны при парсинге данных: используйте Формат() для адресации ячеек и проверяйте типы данных при записи в ТаблицуЗначений — для этой задачи есть готовый универсальный загрузчик данных из внешних источников.