Загрузка данных из внешних файлов Excel — одна из самых частых задач, с которыми сталкивается начинающий программист 1С. Особенно это актуально для конфигурации «Управление торговлей 10.3», работающей на обычных формах. В процессе реализации этой задачи новички часто наступают на одни и те же «грабли»: данные перезаписываются, номенклатура не находится, а строки в документе не создаются. В этой статье мы подробно разберем, как правильно организовать чтение файла Excel и перенос данных в табличную часть документа, проанализируем ошибки и оптимизируем код.
Рассмотрим типичную ситуацию: при выполнении кода в таблицу значений (ТЗ) или табличную часть документа попадает только одна строка (обычно последняя из файла). Выясним причину этой ошибки. Она кроется в неправильном расположении метода Добавить(). Если вызвать метод создания строки до начала цикла, программа будет лишь перезаписывать значения в одной и той же созданной строке.
Проанализируем правильный подход. Для того чтобы каждая строка из Excel стала новой строкой в 1С, вызов метода Добавить() должен находиться внутри цикла Для ... Цикл. Посмотрим на пример кода:
// НЕПРАВИЛЬНО:
НоваяСтрока = ТЗ.Добавить();
Для Стр = 1 ПО 10 Цикл
НоваяСтрока.Номенклатура = Эксель.Cells(Стр, 1).Value;
КонецЦикла;
// ПРАВИЛЬНО:
Для Стр = 1 ПО 10 Цикл
НоваяСтрока = ТЗ.Добавить(); // Строка создается на каждой итерации
НоваяСтрока.Номенклатура = Эксель.Cells(Стр, 1).Value;
КонецЦикла;
После того как данные прочитаны из Excel в промежуточную таблицу значений, их нужно перенести в сам документ (например, в «Поступление товаров и услуг»). Распространенная ошибка здесь — попытка использовать свойство ТекущиеДанные элементов формы. Это свойство предназначено для интерактивной работы пользователя и не подходит для программного заполнения.
Разберем, как сделать это правильно через объект документа. В обычных формах (УТ 10.3) мы обращаемся к коллекции табличной части напрямую через ЭтотОбъект или просто по имени табличной части, если код пишется в модуле формы. Рассмотрим по шагам:
Добавить() у табличной части документа (например, Товары).
Процедура ПереборСтрокТЗ()
Для Каждого СтрокаТЗ Из ТЗ Цикл
// Создаем новую строку непосредственно в документе
НоваяСтрокаДокумента = ЭтотОбъект.Товары.Добавить();
// Заполняем данные
НоваяСтрокаДокумента.Номенклатура = СтрокаТЗ.Номенклатура;
НоваяСтрокаДокумента.Количество = СтрокаТЗ.Количество;
НоваяСтрокаДокумента.Цена = СтрокаТЗ.Цена;
НоваяСтрокаДокумента.Сумма = СтрокаТЗ.Сумма;
КонецЦикла;
КонецПроцедуры
Важно понимать, что в Excel хранятся текстовые названия или артикулы, а в табличной части документа 1С поле Номенклатура имеет тип СправочникСсылка.Номенклатура. Мы не можем просто присвоить текстовую строку ссылочному полю. Если мы так сделаем, поле останется пустым или возникнет ошибка типа.
Рассмотрим, как правильно искать номенклатуру по наименованию, которое мы получили из Excel. Для этого используем метод НайтиПоНаименованию(). Также крайне желательно использовать функцию СокрЛП(), чтобы убрать лишние пробелы, которые часто копируются из таблиц.
// В цикле чтения Excel
НазваниеИзФайла = СокрЛП(Excel.Cells(Стр, 1).Value);
НайденнаяСсылка = Справочники.Номенклатура.НайтиПоНаименованию(НазваниеИзФайла);
Если НайденнаяСсылка.Пустая() Тогда
Сообщить("Товар не найден: " + НазваниеИзФайла);
Иначе
НоваяСтрока.Номенклатура = НайденнаяСсылка;
КонецЕсли;
Важный момент: попытка присвоить значение напрямую в свойство Наименование (например, Стр.Номенклатура.Наименование = "Монитор") приведет к ошибке, так как наименование у существующей ссылки доступно только для чтения. Всегда сначала ищем ссылку, а затем присваиваем её полю документа.
В конфигурации «Управление торговлей 10.3» простого заполнения номенклатуры и количества недостаточно для корректного проведения документа. У каждой номенклатуры есть базовая единица измерения и ставка НДС. Рассмотрим, что еще нужно добавить в код для полноценной работы:
СтавкаНДС.Проанализируем пример заполнения сопутствующих реквизитов:
НоваяСтрокаДокумента = ЭтотОбъект.Товары.Добавить();
НоваяСтрокаДокумента.Номенклатура = СтрокаТЗ.Номенклатура;
// Заполняем единицу измерения из карточки номенклатуры
НоваяСтрокаДокумента.ЕдиницаИзмерения = СтрокаТЗ.Номенклатура.ЕдиницаХраненияОстатков;
НоваяСтрокаДокумента.Коэффициент = НоваяСтрокаДокумента.ЕдиницаИзмерения.Коэффициент;
// Подтягиваем ставку НДС
НоваяСтрокаДокумента.СтавкаНДС = СтрокаТЗ.Номенклатура.СтавкаНДС;
НоваяСтрокаДокумента.Количество = СтрокаТЗ.Количество;
НоваяСтрокаДокумента.Цена = СтрокаТЗ.Цена;
// Сумму можно рассчитать программно, чтобы не полагаться только на файл
НоваяСтрокаДокумента.Сумма = НоваяСтрокаДокумента.Количество * НоваяСтрокаДокумента.Цена;
При работе через COMОбъект("Excel.Application") часто возникает проблема "висящих" процессов в диспетчере задач. Если процесс не закрыть правильно, файл может оказаться заблокированным для других пользователей, а оперативная память сервера будет переполнена. Выясним, как гарантированно закрыть приложение.
Недостаточно просто вызвать Quit(). Профессиональный подход подразумевает закрытие книги без сохранения изменений и обнуление переменной 1С.
// Закрываем книгу без вопросов о сохранении
Excel.ActiveWorkbook.Close(Ложь);
// Выходим из приложения
Excel.Quit();
// Очищаем переменную для освобождения памяти
Excel = Неопределено;
Если в вашем файле Excel не 10 строк, а 1000 или более, обращение к каждой ячейке через Cells(Стр, Кол).Value будет работать крайне медленно. Каждое такое обращение — это вызов через медленный интерфейс COM. Рассмотрим способ ускорения в десятки раз: чтение всего диапазона в массив за один вызов.
// Получаем данные всего листа в двумерный массив 1С
// Предположим, у нас 4 колонки и 1000 строк
ДанныеМассива = Excel.Range(Excel.Cells(1, 1), Excel.Cells(1000, 4)).Value;
// Теперь работаем с обычным массивом, это мгновенно
Для Стр = 1 По 1000 Цикл
ТекНоменклатура = ДанныеМассива.GetValue(Стр, 1);
ТекКоличество = ДанныеМассива.GetValue(Стр, 2);
// ... дальнейшая логика
КонецЦикла;
Подводя итог, скажем, что успешная загрузка данных из Excel в 1С требует внимательного отношения к типам данных и структуре циклов. Всегда проверяйте, создается ли новая строка внутри цикла, очищайте строки от пробелов и не забывайте закрывать COM-объекты после завершения работы. Следуя этим правилам, вы создадите надежный и быстрый инструмент для импорта данных.