Мы часто сталкиваемся с необходимостью импортировать данные из внешних файлов, таких как Excel, в информационную базу 1С. Одним из распространенных сценариев является загрузка данных из одной или нескольких колонок Excel-файла напрямую в табличное поле формы.
На первый взгляд задача кажется простой, однако при попытке записать данные напрямую в свойство Значение ячейки табличного поля, мы можем столкнуться с ошибкой "поле значение не доступно для записи". Давайте подробно разберем, почему это происходит и как правильно реализовать загрузку данных, обеспечивая корректное заполнение как текстовых, так и значащих полей.
Прежде чем мы приступим к заполнению табличного поля, нам необходимо обеспечить выбор файла пользователем и прочитать его содержимое. В современных конфигурациях 1С (управляемые формы) этот процесс обычно включает клиент-серверное взаимодействие. Для обычных форм процесс может быть проще и выполняться на клиенте или сервере, в зависимости от контекста.
ТабличныйДокумент для чтения содержимого выбранного файла Excel.Рассмотрим пример кода для данного этапа, включая клиент-серверное взаимодействие, которое актуально для управляемых форм, но принципы чтения файла будут одинаковы:
&НаКлиенте
Процедура ЗагрузитьИзExcel(Команда)
// Создаем параметры диалога для выбора файла
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл Excel для загрузки";
ПараметрыДиалога.Фильтр = "Таблица Excel (*.xls;*.xlsx)|*.xls;*.xlsx";
ПараметрыДиалога.Расширения = "xls,xlsx";
ПараметрыДиалога.МножественныйВыбор = Ложь; // Предполагаем выбор одного файла
// Помещаем файл на сервер асинхронно
Сообщить("Выполняется выбор файла Excel...", СтатусСообщения.Информация);
ОписаниеФайла = Ждать ПоместитьФайлНаСерверАсинх(,,,ПараметрыДиалога);
Если ОписаниеФайла <> Неопределено Тогда
Сообщить("Файл '" + ОписаниеФайла.Имя + "' выбран. Приступаем к чтению...", СтатусСообщения.Информация);
// Вызываем серверную процедуру для чтения файла
ПрочитатьExcelНаСервере(ОписаниеФайла.Адрес, ОписаниеФайла.Имя, ОписаниеФайла.СсылкаНаФайла.Расширение);
Иначе
Сообщить("Выбор файла отменен пользователем.", СтатусСообщения.Важное);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПрочитатьExcelНаСервере(АдресДанных, ИмяФайла, РасширениеФайла)
// Получаем временное имя файла на сервере
ПутьКФайлу = ПолучитьИмяВременногоФайла(РасширениеФайла);
// Получаем данные файла из временного хранилища
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресДанных);
// Записываем данные во временный файл
ДанныеФайла.Записать(ПутьКФайлу);
// Создаем объект ТабличныйДокумент для чтения Excel
ТабДок = Новый ТабличныйДокумент;
Попытка
// Читаем файл Excel в ТабличныйДокумент
// Используем СпособЧтенияЗначенийТабличногоДокумента.Значение,
// чтобы платформа пыталась распознать типы данных (числа, даты)
// иначе все будет прочитано как Строка
ТабДок.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Сообщить("Файл '" + ИмяФайла + "' успешно прочитан в ТабличныйДокумент. Высота таблицы: " + ТабДок.ВысотаТаблицы, СтатусСообщения.Информация);
Исключение
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Не удалось прочитать файл Excel '" + ИмяФайла + "': " + ОписаниеОшибки();
Сообщение.Сообщить();
// Удаляем временный файл
УдалитьФайлы(ПутьКФайлу);
Возврат;
КонецПопытки;
// Удаляем временный файл после чтения
УдалитьФайлы(ПутьКФайлу);
// Теперь у нас есть ТабДок с данными.
// Дальнейшая обработка и загрузка в табличное поле будет здесь.
// Передаем ТабДок для загрузки на форму, если это требуется,
// или загружаем данные в реквизит формы типа ТаблицаЗначений
// уже на сервере.
ЗагрузитьДанныеВТабличноеПоле(ТабДок);
КонецПроцедуры
Обратите внимание, что мы используем СпособЧтенияЗначенийТабличногоДокумента.Значение при чтении файла. Это позволяет 1С автоматически попытаться определить тип данных в ячейках Excel (например, число, дата) и сохранить их в свойство Значение ячейки ТабличногоДокумента. Если мы используем СпособЧтенияЗначенийТабличногоДокумента.Текст, все данные будут прочитаны как строки.
Текст и Значение ячейки ТабличногоДокументаПонимание того, как работают свойства Текст и Значение у объекта ОбластьЯчеекТабличногоДокумента (которую мы получаем методом ТабДок.Область()), является критически важным. Именно здесь кроется одна из основных причин недопониманий при работе с данными.
Текст: Это свойство всегда имеет тип Строка. Оно содержит строковое представление данных ячейки. Если в ячейке было число, дата или булево значение, оно будет преобразовано в строку при доступе к свойству Текст. По умолчанию, при чтении Excel, данные часто попадают именно в Текст, если не указан иной способ чтения или если 1С не смогла однозначно определить тип.Значение: Это свойство может хранить данные любого типа (число, дата, булево, ссылка на объект, и т.д.). Однако, доступ к свойству Значение контролируется другим свойством – СодержитЗначение.Важное правило: Свойства Текст и Значение не могут быть активны одновременно для одной и той же ячейки ТабличногоДокумента. Их доступность определяется свойством СодержитЗначение:
Область.СодержитЗначение = Истина, то мы можем работать со свойством Область.Значение. Свойство Область.Текст в этом случае недоступно для записи и содержит строковое представление Значения.Область.СодержитЗначение = Ложь, то мы можем работать со свойством Область.Текст. Свойство Область.Значение в этом случае недоступно.Когда мы читаем файл Excel с помощью ТабДок.Прочитать(..., СпособЧтенияЗначенийТабличногоДокумента.Значение), платформа 1С пытается установить СодержитЗначение = Истина для тех ячеек, где она смогла распознать нетекстовые данные (числа, даты). Для остальных ячеек СодержитЗначение останется Ложь.
После того, как мы прочитали данные из Excel в объект ТабличныйДокумент, нам нужно перенести их в ТабличноеПоле на форме. ТабличноеПоле на форме обычно отображает данные из реквизита формы, который может быть типа ТаблицаЗначений, ДинамическийСписок или табличной частью какого-либо объекта.
В контексте исходной темы, автор работал с ЭлементыФормы.ТаблицаКлючей, что указывает на ТабличноеПоле, отображающее, скорее всего, реквизит формы типа ТаблицаЗначений или ТабличнуюЧасть объекта (например, обработки или документа) в обычных формах.
Этот метод подразумевает итеративное прохождение по строкам ТабличногоДокумента и создание новых строк в реквизите формы, с последующим присвоением значений колонкам этих строк. Это наиболее прямолинейный подход, но для больших объемов данных он может быть менее производительным.
Давайте проанализируем исходный код автора из первого сообщения:
// ...
КолСтр = ТабДок.ВысотаТаблицы;
Для Сч = 2 по КолСтр Цикл
// ... прочие переменные ...
пр= Строка(ТабДок.ПолучитьОбласть("R" + Формат(Сч, "ЧГ=0") + "C" + 1).ТекущаяОбласть.Текст);
// ...
ТекОбласть = ЭлементыФормы.ТаблицаКлючей.Область(НачНомерСтроки, 1); // Ошибка: обращение к ячейке табличного поля по номеру
Если ТекОбласть.СодержитЗначение Тогда
ТекОбласть.Значение=пр;
Иначе
ТекОбласть.Текст=пр;
КонецЕсли;
ТекОбласть.Текст=пр;
// ...
КонецЦикла;
Здесь мы видим несколько проблем:
ТабличногоПоля на форме методом ЭлементыФормы.ТаблицаКлючей.Область(НачНомерСтроки, 1). Этот метод предназначен для работы с макетами табличного документа или элементами формы типа ПолеHTMLДокумента / ПолеКартинки в некоторых контекстах, но не для прямого заполнения данных в ТабличноеПоле, связанное с реквизитом формы (например, ТаблицаЗначений). Чтобы заполнить ТабличноеПоле, мы должны работать с его источником данных (например, ТаблицаЗначений или табличной частью объекта).НачНомерСтроки без должного изменения внутри цикла приводит к тому, что все попытки записи идут в одну и ту же строку или в несвязанные с текущей итерацией строки.ТекОбласть.Текст=пр; после блока Если/Иначе излишне и может перезаписать результат предыдущей логики.Правильный подход к построчному копированию: Мы должны добавлять новые строки в реквизит формы, связанный с ТабличнымПолем, а затем присваивать значения колонкам этих строк.
Предположим, у нас есть реквизит формы ТаблицаКлючей типа ТаблицаЗначений, и у этой таблицы есть колонка ЗначениеКлюча.
&НаСервере
Процедура ЗагрузитьДанныеВТабличноеПоле(ТабДок)
// Очищаем существующие данные в таблице на форме
ТаблицаКлючей.Очистить(); // ТаблицаКлючей - реквизит формы типа ТаблицаЗначений
// Добавляем колонки в ТаблицуЗначений, если их нет или они могут отличаться.
// Это важно, если Excel-файл может иметь разную структуру.
// Или убедитесь, что колонки уже есть в ТаблицеЗначений.
Если ТаблицаКлючей.Колонки.Найти("ЗначениеКлюча") = Неопределено Тогда
ТаблицаКлючей.Колонки.Добавить("ЗначениеКлюча", Новый ОписаниеТипов("Строка"));
// Если планируем хранить другие типы, указываем их:
// ТаблицаКлючей.Колонки.Добавить("ЧисловоеЗначение", Новый ОписаниеТипов("Число"));
КонецЕсли;
Для Сч = 2 По ТабДок.ВысотаТаблицы Цикл // Начинаем со второй строки, если первая - заголовок
НоваяСтрока = ТаблицаКлючей.Добавить();
// Получаем ячейку из ТабличногоДокумента
ТекущаяЯчейкаТабДок = ТабДок.Область(Сч, 1); // 1 - это номер колонки Excel
// Определяем, какое свойство использовать (Текст или Значение)
// и присваиваем его в колонку нашей таблицы значений.
// Если знаем, что данные всегда текстовые, можно всегда использовать Текст.
// Если же данные могут быть разных типов (числа, даты),
// то лучше использовать Значение, если оно доступно.
Если ТекущаяЯчейкаТабДок.СодержитЗначение Тогда
НоваяСтрока.ЗначениеКлюча = ТекущаяЯчейкаТабДок.Значение;
Иначе
НоваяСтрока.ЗначениеКлюча = ТекущаяЯчейкаТабДок.Текст;
КонецЕсли;
КонецЦикла;
Сообщить("Данные успешно загружены в табличное поле.", СтатусСообщения.Информация);
КонецПроцедуры
Важно: Если ТабличноеПоле на форме отображает табличную часть объекта (например, документа), то вместо ТаблицаКлючей.Добавить() мы будем использовать метод добавления строки в табличную часть объекта (например, Объект.ИмяТабличнойЧасти.Добавить()).
ТаблицуЗначений с помощью ПостроителяЗапросаЭтот метод является более продвинутым и эффективным для больших объемов данных, поскольку позволяет выполнить загрузку данных из ТабличногоДокумента в ТаблицуЗначений одним запросом, а затем присвоить эту ТаблицуЗначений реквизиту формы.
&НаСервере
Процедура ЗагрузитьДанныеВТабличноеПолеЧерезПостроитель(ТабДок)
// Очищаем существующие данные в таблице на форме
ТаблицаКлючей.Очистить(); // ТаблицаКлючей - реквизит формы типа ТаблицаЗначений
Попытка
// Создаем ПостроительЗапроса
ПостроительЗапроса = Новый ПостроительЗапроса;
// Указываем в качестве источника данных весь ТабличныйДокумент
ПостроительЗапроса.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
// Автоматически заполняем настройки запроса на основе структуры ТабличногоДокумента
ПостроительЗапроса.ЗаполнитьНастройки();
// Если нам нужна только первая колонка, можно явно указать поля
// (ПостроительЗапроса автоматически создаст поля вида "Поле1", "Поле2"...)
Если ПостроительЗапроса.ВыбираемыеПоля.Найти("Поле1") = Неопределено Тогда
ПостроительЗапроса.ВыбираемыеПоля.Добавить("Поле1");
КонецЕсли;
// Можно переименовать поля для удобства:
ПостроительЗапроса.ВыбираемыеПоля.Найти("Поле1").Имя = "ЗначениеКлюча";
ПостроительЗапроса.ВыбираемыеПоля.Найти("ЗначениеКлюча").ТекстЗаголовка = "Значение Ключа из Excel";
// Выполняем запрос
ПостроительЗапроса.Выполнить();
// Выгружаем результат в ТаблицуЗначений
ТаблицаЗначенийИзExcel = ПостроительЗапроса.Результат.Выгрузить();
// Если первая строка Excel была заголовком, удаляем ее из ТаблицыЗначений
Если ТаблицаЗначенийИзExcel.Количество() > 0 Тогда
ТаблицаЗначенийИзExcel.Удалить(0); // Удаляем первую строку (индекс 0)
КонецЕсли;
// Загружаем полученную ТаблицуЗначений в реквизит формы
ТаблицаКлючей.Загрузить(ТаблицаЗначенийИзExcel);
// Обновляем колонки ТабличногоПоля на форме, если это необходимо
// (особенно если ТаблицаЗначенийИзExcel может иметь разный состав колонок)
ЭлементыФормы.ТаблицаКлючей.СоздатьКолонки();
Сообщить("Данные успешно загружены через ПостроительЗапроса.", СтатусСообщения.Информация);
Исключение
Сообщить("Ошибка при загрузке данных через ПостроительЗапроса: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
КонецПопытки;
КонецПроцедуры
Этот метод особенно полезен, когда нужно загрузить не одну, а несколько колонок, и когда требуется более сложная фильтрация или преобразование данных прямо во время выгрузки.
Excel.ApplicationЕсли на сервере или клиентской машине (в зависимости от места выполнения кода) установлен Microsoft Excel, мы можем напрямую работать с Excel как с COM-объектом. Этот способ дает максимальный контроль над файлом Excel, но требует наличия установленного Excel и может быть медленнее для очень больших файлов.
&НаСервере
Процедура ЗагрузитьДанныеЧерезCOM(ПутьКФайлу)
// Очищаем существующие данные
ТаблицаКлючей.Очистить();
Попытка
// Создаем COM-объект Excel
Excel = Новый COMОбъект("Excel.Application");
Excel.Visible = Ложь; // Скрываем Excel, чтобы он не мелькал перед пользователем
// Открываем книгу Excel
ExcelBook = Excel.Workbooks.Open(ПутьКФайлу);
Лист = ExcelBook.Sheets(1); // Выбираем первый лист
// Определяем количество заполненных строк (например, по первой колонке)
// Cells(1, 1).SpecialCells(11) находит последнюю заполненную ячейку
КоличествоСтрок = Лист.Cells(1, 1).SpecialCells(11).Row;
Для НомерСтроки = 2 По КоличествоСтрок Цикл // Начинаем со второй строки, пропуская заголовок
НоваяСтрока = ТаблицаКлючей.Добавить();
// Читаем значение из ячейки Excel
НоваяСтрока.ЗначениеКлюча = Лист.Cells(НомерСтроки, 1).Value; // .Value возвращает значение ячейки
КонецЦикла;
// Закрываем книгу и Excel
ExcelBook.Close(Ложь); // False - не сохранять изменения
Excel.Quit();
Сообщить("Дандые успешно загружены через COM-объект Excel.", СтатусСообщения.Информация);
Исключение
Сообщить("Ошибка работы с COM-объектом Excel: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
Если Excel <> Неопределено Тогда
Excel.Quit(); // Попытаемся закрыть Excel в случае ошибки
КонецЕсли;
КонецПопытки;
КонецПроцедуры
Использование COM-объекта Excel более ресурсоемко и менее универсально, поскольку требует предустановленного Excel, но может быть полезно для сложных манипуляций с файлом.
Это ключевой момент, который вызвал затруднения у автора темы. Когда мы пытаемся присвоить данные в свойство Значение ячейки табличного поля на форме (не путать с ячейкой ТабличногоДокумента), мы можем столкнуться с ошибкой, если для соответствующей колонки табличного поля не установлено свойство СодержитЗначение.
На форме ТабличноеПоле состоит из колонок. Каждая колонка может отображать либо Текст, либо Значение из соответствующего поля данных. Чтобы иметь возможность записи в Значение колонки ТабличногоПоля, необходимо:
СодержитЗначение для колонки ТабличногоПоля на форме в Истина. Это можно сделать как в конфигураторе (для статических колонок), так и программно.ТаблицеЗначений или табличной части) должен соответствовать типу данных, которые мы пытаемся записать. Например, если колонка предназначена для чисел, а мы пытаемся записать строку, будет ошибка преобразования типа.В конфигураторе для колонки ТабличногоПоля перейдите в свойства и найдите пункт "Содержит значение". Установите его в "Истина".
Программно это можно сделать так (в контексте обычных форм, как в исходной теме):
// Предположим, у нас есть колонка "ЗначениеКлюча" в ТабличномПоле "ТаблицаКлючей"
// и нам нужно, чтобы она могла хранить Значения (например, числа или ссылки).
// Получаем элемент управления "ТаблицаКлючей"
ЭлементТабличноеПоле = ЭлементыФормы.ТаблицаКлючей;
// Ищем нужную колонку по имени
КолонкаТабличногоПоля = ЭлементТабличноеПоле.Колонки.Найти("ЗначениеКлюча");
Если КолонкаТабличногоПоля <> Неопределено Тогда
// Устанавливаем свойство СодержитЗначение в Истина
КолонкаТабличногоПоля.СодержитЗначение = Истина;
Сообщить("Для колонки 'ЗначениеКлюча' установлено СодержитЗначение = Истина.", СтатусСообщения.Информация);
Иначе
Сообщить("Колонка 'ЗначениеКлюча' не найдена в табличном поле.", СтатусСообщения.Предупреждение);
КонецЕсли;
После установки СодержитЗначение = Истина для колонки, вы сможете использовать свойство Значение для ячеек этой колонки в ТабличномПоле.
Итоговый код автора, который, вероятно, привел его к решению, после того как он установил СодержитЗначение и исправил перепутанные таблицы, мог выглядеть примерно так (адаптировано для ясности, используя правильный подход, описанный выше для построчного копирования):
&НаСервере
Процедура ЗагрузитьДанныеИзExcelВТаблицуКлючей(ТабДок)
// Убедимся, что реквизит формы ТаблицаКлючей существует и имеет нужную структуру
Если ТаблицаКлючей = Неопределено Тогда
ТаблицаКлючей = Новый ТаблицаЗначений;
КонецЕсли;
Если ТаблицаКлючей.Колонки.Найти("Ключ") = Неопределено Тогда
ТаблицаКлючей.Колонки.Добавить("Ключ", Новый ОписаниеТипов("Строка")); // Добавляем колонку, если ее нет
КонецЕсли;
// Если нам нужны числовые значения, то тип колонки должен быть Число
// Если ТаблицаКлючей.Колонки.Найти("ЧисловойКлюч") = Неопределено Тогда
// ТаблицаКлючей.Колонки.Добавить("ЧисловойКлюч", Новый ОписаниеТипов("Число"));
// КонецЕсли;
ТаблицаКлючей.Очистить(); // Очищаем таблицу перед загрузкой
Для Сч = 2 По ТабДок.ВысотаТаблицы Цикл // Начинаем со второй строки, пропуская заголовок
НоваяСтрока = ТаблицаКлючей.Добавить();
// Получаем ячейку из ТабличногоДокумента
// Предполагаем, что данные находятся в первой колонке Excel
ТекущаяЯчейкаТабДок = ТабДок.Область(Сч, 1);
// Присваиваем значение колонке "Ключ" новой строки
// Если в Excel были числа или другие типы, то используем Значение
Если ТекущаяЯчейкаТабДок.СодержитЗначение Тогда
НоваяСтрока.Ключ = ТекущаяЯчейкаТабДок.Значение;
Иначе
// В противном случае, берем Текст
НоваяСтрока.Ключ = ТекущаяЯчейкаТабДок.Текст;
КонецЕсли;
КонецЦикла;
// В управляемых формах, для обновления отображения ТабличногоПоля
// связанного с ТаблицейЗначений, достаточно изменить саму ТаблицуЗначений.
// В обычных формах, если ЭлементыФормы.ТаблицаКлючей напрямую отображает ТаблицуЗначений,
// то после заполнения реквизита ТаблицаКлючей, отображение должно обновиться.
Сообщить("Загрузка данных в ТаблицуКлючей завершена.", СтатусСообщения.Информация);
КонецПроцедуры
Мы рассмотрели различные подходы к загрузке данных из Excel-файла в табличное поле 1С. Ключевыми моментами, на которые следует обратить внимание, являются:
ТабличныйДокумент, желательно с использованием СпособЧтенияЗначенийТабличногоДокумента.Значение.Текст и Значение ячеек ТабличногоДокумента и ТабличногоПоля.СодержитЗначение для колонки ТабличногоПоля, чтобы обеспечить возможность записи данных в свойство Значение.ПостроительЗапроса, COM-объект Excel).Следуя этим рекомендациям, вы сможете эффективно реализовать импорт данных из Excel в ваши формы 1С, избегая распространенных ошибок и обеспечивая надежную работу системы.