Загрузка данных из внешних источников, таких как файлы Excel, в табличные части документов 1С — это одна из самых распространенных задач, с которой сталкиваются пользователи и разработчики. Она позволяет значительно ускорить процесс ввода информации, особенно при работе с большим объемом однотипных данных, например, при поступлении товаров или формировании заказов.
В этом руководстве мы подробно рассмотрим, как создать внешнюю обработку для 1С:Управление торговлей 11.4 (или аналогичных конфигураций на управляемых формах), которая будет считывать данные из файла Excel и заполнять табличную часть документа «ПоступлениеТоваровУслуг». Мы разберем как классический подход с использованием COM-объектов, так и более современный, основанный на механизмах Библиотеки Стандартных Подсистем (БСП), а также уделим внимание важному аспекту поиска номенклатуры.
Предположим, что у нас есть файл Excel с колонками: Номенклатура (наименование), Количество, Цена, Сумма. Наша цель — создать обработку, которую можно будет открыть прямо из документа «ПоступлениеТоваровУслуг» для удобства работы.
Прежде чем углубляться в детали, давайте наметим основные шаги, которые нам предстоит пройти:
COM-объект Excel.Application (или альтернативный метод на БСП). Важно понимать, что работа с COM-объектами должна происходить на клиентской стороне, так как на сервере 1С они обычно недоступны.Начнем с создания новой внешней обработки. Откройте конфигуратор, выберите «Файл» -> «Новый» -> «Внешняя обработка». Сохраните ее в удобном месте.
Далее нам понадобится форма обработки. Перейдите в свойства обработки, на вкладке «Формы» добавьте новую форму, например, «Форма». На этой форме нам потребуется разместить следующие элементы:
ОбъектДокумента типа ДокументСсылка.ПоступлениеТоваровУслуг: Этот реквизит будет хранить ссылку на документ, из которого была вызвана обработка. Он нам понадобится для дальнейшего заполнения табличной части.Для чтения Excel-файлов в среде 1С чаще всего используется COM-объект Excel.Application. Важно помнить, что работа с этим объектом возможна только на стороне клиента (то есть на компьютере пользователя), поскольку на сервере 1С приложения Microsoft Office обычно не установлены или их использование блокируется по соображениям безопасности и производительности.
Рассмотрим процедуру, которая будет вызываться при нажатии на кнопку «Загрузить из Excel».
&НаКлиенте
Процедура ЗагрузитьИзExcel(Команда)
// 1. Открываем диалог выбора файла
РежимДиалога = РежимДиалогаВыбораФайла.Открытие;
ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалога);
ДиалогВыбораФайла.ПолноеИмяФайла = "*.xls;*.xlsx"; // Фильтр по расширениям Excel-файлов
ДиалогВыбораФайла.Заголовок = "Выберите файл Excel для загрузки";
Если ДиалогВыбораФайла.Выбрать() Тогда // Если пользователь выбрал файл
ПутьКФайлу = ДиалогВыбораФайла.ПолноеИмяФайла;
Попытка
// 2. Создаем COM-объект Excel.Application
Excel = Новый COMОбъект("Excel.Application");
// Установим видимость Excel, если хотим видеть процесс открытия файла.
// В продакшене лучше оставить Ложь.
Excel.Visible = Ложь;
// 3. Открываем книгу Excel
ExcelКнига = Excel.Workbooks.Open(ПутьКФайлу);
// 4. Выбираем первый лист книги (индексация начинается с 1)
ExcelЛист = ExcelКнига.Sheets(1);
// 5. Создаем ТаблицуЗначений для хранения считанных данных
ДанныеДляЗагрузки = Новый ТаблицаЗначений;
ДанныеДляЗагрузки.Колонки.Добавить("НоменклатураНаименование");
ДанныеДляЗагрузки.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ДанныеДляЗагрузки.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
ДанныеДляЗагрузки.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
// 6. Итерируемся по строкам листа Excel
// Начинаем со 2 строки, предполагая, что первая строка содержит заголовки
Для НомерСтроки = 2 По ExcelЛист.UsedRange.Rows.Count Цикл
НоваяСтрока = ДанныеДляЗагрузки.Добавить();
// Читаем значения ячеек по номеру строки и столбца (индексация с 1)
НоваяСтрока.НоменклатураНаименование = ExcelЛист.Cells(НомерСтроки, 1).Value;
НоваяСтрока.Количество = ExcelЛист.Cells(НомерСтроки, 2).Value;
НоваяСтрока.Цена = ExcelЛист.Cells(НомерСтроки, 3).Value;
НоваяСтрока.Сумма = ExcelЛист.Cells(НомерСтроки, 4).Value;
КонецЦикла;
// 7. Закрываем книгу и выходим из приложения Excel
ExcelКнига.Close(Ложь); // Ложь - не сохранять изменения
Excel.Quit();
// 8. Передаем полученные данные на сервер для дальнейшей обработки
// ОбъектДокумента - это реквизит формы, куда мы получим ссылку на документ
ЗагрузитьДанныеНаСервере(ДанныеДляЗагрузки, ОбъектДокумента);
Исключение
// 9. Обработка ошибок
Сообщить("Ошибка при чтении файла Excel: " + ОписаниеОшибки());
// Важно корректно закрыть COM-объекты даже в случае ошибки
Если ExcelКнига <> Неопределено Тогда ExcelКнига.Close(Ложь); КонецЕсли;
Если Excel <> Неопределено Тогда Excel.Quit(); КонецЕсли;
КонецПопытки;
КонецЕсли;
КонецПроцедуры
В этом коде мы последовательно выполняем все необходимые действия для взаимодействия с Excel. Мы используем метод Excel.Workbooks.Open() для открытия файла, ExcelКнига.Sheets(1) для доступа к первому листу и ExcelЛист.Cells(НомерСтроки, НомерКолонки).Value для чтения содержимого ячеек. Обратите внимание на использование ExcelЛист.UsedRange.Rows.Count, которое позволяет определить количество заполненных строк на листе, исключая пустые. Важный аспект — корректное закрытие объектов ExcelКнига и Excel.Application через Close() и Quit() для освобождения ресурсов.
После того как данные успешно считаны на клиенте и помещены в ТаблицаЗначений, их необходимо передать на сервер. Это происходит вызовом серверной процедуры ЗагрузитьДанныеНаСервере(). Эта процедура будет отвечать за поиск номенклатуры в базе данных 1С и заполнение табличной части документа.
Добавьте следующую процедуру в модуль формы обработки с директивой &НаСервере:
&НаСервере
Процедура ЗагрузитьДанныеНаСервере(ДанныеДляЗагрузки, СсылкаНаДокумент)
// 1. Получаем объект документа по ссылке
ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// 2. Очищаем существующую табличную часть "Товары" перед заполнением
// Это позволит избежать дублирования строк при повторной загрузке.
// Если требуется дозаполнять, а не перезаписывать, этот шаг следует пропустить.
ДокументОбъект.Товары.Очистить();
// Создадим список для отслеживания номенклатуры, которая не была найдена
НеНайденныеНоменклатуры = Новый Массив;
// 3. Итерируемся по каждой строке из ТаблицыЗначений, полученной из Excel
Для Каждого СтрокаДанных Из ДанныеДляЗагрузки Цикл
// 4. Вызываем функцию поиска номенклатуры на сервере
Номенклатура = НайтиНоменклатуруПоНаименованию(СтрокаДанных.НоменклатураНаименование);
Если Номенклатура <> Неопределено Тогда
// 5. Если номенклатура найдена, добавляем новую строку в табличную часть документа
НоваяСтрокаТЧ = ДокументОбъект.Товары.Добавить();
НоваяСтрокаТЧ.Номенклатура = Номенклатура;
НоваяСтрокаТЧ.Количество = СтрокаДанных.Количество;
НоваяСтрокаТЧ.Цена = СтрокаДанных.Цена;
НоваяСтрокаТЧ.Сумма = СтрокаДанных.Сумма;
// Здесь можно добавить расчет НДС, если это необходимо
// НоваяСтрокаТЧ.СтавкаНДС = Номенклатура.СтавкаНДС;
// НоваяСтрокаТЧ.СуммаНДС = Окр(СтрокаДанных.Сумма / (1 + НоваяСтрокаТЧ.СтавкаНДС / 100) * (НоваяСтрокаТЧ.СтавкаНДС / 100), 2);
// НоваяСтрокаТЧ.Всего = СтрокаДанных.Сумма;
Иначе
// 6. Если номенклатура не найдена, фиксируем это
НеНайденныеНоменклатуры.Добавить(СтрокаДанных.НоменклатураНаименование);
КонецЕсли;
КонецЦикла;
// Выводим сообщение о не найденной номенклатуре после полного обхода всех строк
Если НеНайденныеНоменклатуры.Количество() > 0 Тогда
Сообщить("Внимание! Следующая номенклатура из файла Excel не была найдена в базе и пропущена: "
+ СтрСоединить(НеНайденныеНоменклатуры, ", "));
КонецЕсли;
Попытка
// 7. Записываем (и при необходимости проводим) документ
// Выбираем режим записи: РежимЗаписиДокумента.Запись для простого сохранения,
// РежимЗаписиДокумента.Проведение для сохранения и проведения.
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Данные успешно загружены и документ проведен.");
Исключение
Сообщить("Ошибка при записи документа: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В этой процедуре мы первым делом получаем объект документа по переданной ссылке, используя метод ПолучитьОбъект(). Это позволяет нам модифицировать данные документа. Далее мы очищаем табличную часть Товары, чтобы избежать дублирования строк при многократной загрузке из одного и того же документа. Мы обходим каждую строку полученной ТаблицаЗначений, для каждой строки пытаемся найти соответствующую номенклатуру в базе данных и, в случае успеха, добавляем новую строку в табличную часть документа, заполняя необходимые реквизиты. В конце мы осуществляем запись (и, при необходимости, проведение) документа с помощью ДокументОбъект.Записать().
Надежный поиск номенклатуры — критически важный элемент нашей обработки. От его корректной работы зависит точность загружаемых данных. Рассмотрим несколько стратегий поиска.
Этот метод подходит, если вы уверены, что наименования в Excel полностью совпадают с наименованиями в базе 1С. Однако, даже небольшие различия (пробелы, регистр, опечатки) могут привести к тому, что номенклатура не будет найдена.
&НаСервере
Функция НайтиНоменклатуруПоНаименованию(Наименование)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Наименование = &Наименование
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Наименование"; // Добавляем сортировку для детерминированного выбора, если вдруг есть полные дубликаты
Запрос.УстановитьПараметр("Наименование", Наименование);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Возврат Результат.Номенклатура;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Здесь мы используем объект Запрос для выборки данных из справочника Номенклатура. Параметр &Наименование позволяет безопасно передать значение для поиска. Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 и УПОРЯДОЧИТЬ ПО гарантирует, что мы получим только одну ссылку, даже если по наименованию будет найдено несколько совпадений (что нежелательно, но иногда встречается).
Если в Excel-файле присутствует артикул или код номенклатуры, и вы уверены в их уникальности, эти методы предпочтительнее. Они значительно снижают риск ошибок из-за расхождений в наименованиях.
&НаСервере
Функция НайтиНоменклатуруПоАртикулу(Артикул)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", Артикул);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Возврат Результат.Номенклатура;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
&НаСервере
Функция НайтиНоменклатуруПоКоду(Код)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Код = &Код";
Запрос.УстановитьПараметр("Код", Код);
Результат = Запрос.Выполнить().Выбрать();
Если Результат.Следующий() Тогда
Возврат Результат.Номенклатура;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Наиболее гибкий и надежный подход — это комбинированный поиск. Сначала пытаемся найти по самому надежному идентификатору (например, артикулу), а если не находим, переходим к менее надежному (например, наименованию).
&НаСервере
Функция НайтиНоменклатуруКомбинированно(Наименование, Артикул = "")
Если Не ПустаяСтрока(Артикул) Тогда
Номенклатура = НайтиНоменклатуруПоАртикулу(Артикул);
Если Номенклатура <> Неопределено Тогда
Возврат Номенклатура;
КонецЕсли;
КонецЕсли;
Если Не ПустаяСтрока(Наименование) Тогда
Номенклатура = НайтиНоменклатуруПоНаименованию(Наименование);
Возврат Номенклатура;
КонецЕсли;
Возврат Неопределено;
КонецФункции
В этом случае, если у вас в Excel есть колонка с артикулами, вы можете сначала искать по ней, а потом, как запасной вариант, по наименованию. Помните, что порядок поиска имеет значение.
Если после всех попыток номенклатура не найдена, у нас есть несколько вариантов:
ЗагрузитьДанныеНаСервере.Для того чтобы ваша обработка открывалась не через меню «Файл» -> «Открыть», а непосредственно из документа «ПоступлениеТоваровУслуг» (или любого другого), ее необходимо зарегистрировать как внешнюю обработку заполнения табличных частей. Для этого выполните следующие действия:
Создание команды в обработке: В конфигураторе откройте вашу внешнюю обработку. Перейдите на вкладку «Команды». Добавьте новую команду, например, «ЗагрузкаИзExcel». В ее свойствах обязательно установите параметр «Назначение» в значение «ЗаполнениеОбъекта».
Название этой команды будет отображаться в выпадающем списке кнопки «Заполнить» в форме документа.
Реализация процедуры ЗаполнитьОбъект в модуле объекта: В модуле объекта вашей внешней обработки необходимо реализовать экспортную процедуру с предопределенным именем ЗаполнитьОбъект.
Эта процедура будет вызываться системой 1С, когда пользователь выберет вашу обработку из списка заполнения. В качестве параметров она получит ссылку на документ, который нужно заполнить.
Процедура ЗаполнитьОбъект(ОбъектЗаполнения, ДанныеЗаполнения) Экспорт
// ОбъектЗаполнения - это объект документа, который мы должны заполнить.
// Передадим его в форму обработки для дальнейшей работы.
// Для этого мы можем сохранить его в реквизите формы, который мы создали ранее: ОбъектДокумента.
// Открываем нашу форму обработки и передаем ей контекст заполнения (ссылку на документ)
Форма = ПолучитьФорму("ВнешняяОбработка.ВашаОбработкаДляExcel.Форма.Форма",
Новый Структура("ОбъектЗаполнения", ОбъектЗаполнения));
Форма.ОткрытьМодально(); // Открываем форму модально, чтобы дождаться закрытия
КонецПроцедуры
Здесь "ВнешняяОбработка.ВашаОбработкаДляExcel.Форма.Форма" — это полное имя вашей формы обработки. Замените ВашаОбработкаДляExcel на имя вашей обработки. Мы используем структуру для передачи параметра ОбъектЗаполнения в форму.
Получение переданного объекта в форме: В модуле формы обработки, в процедуре ПриСозданииНаСервере, мы можем получить переданный объект документа из параметров формы.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, был ли передан параметр "ОбъектЗаполнения"
Если Параметры.Свойство("ОбъектЗаполнения") Тогда
// Присваиваем переданный объект нашему реквизиту формы
ОбъектДокумента = Параметры.ОбъектЗаполнения;
КонецЕсли;
КонецПроцедуры
Теперь реквизит ОбъектДокумента на форме будет содержать ссылку на документ, который мы хотим заполнить, и мы можем использовать его в процедуре ЗагрузитьДанныеНаСервере, как было показано ранее.
После всех этих настроек, в режиме 1С:Предприятие, при открытии документа «ПоступлениеТоваровУслуг», вы сможете перейти к табличной части «Товары», нажать кнопку «Заполнить» и в выпадающем списке увидите вашу обработку «ЗагрузкаИзExcel». При ее выборе откроется ваша форма, уже «привязанная» к текущему документу.
Хотя COM-объекты являются рабочим решением для Windows-клиентов, для более современных и кроссплатформенных разработок, а также для работы в среде Linux или веб-клиенте, рекомендуется использовать функционал Библиотеки Стандартных Подсистем (БСП), а именно подсистему «РаботаСФайлами».
БСП предоставляет готовые функции для работы с файлами различных форматов, включая Excel, без прямого использования COM-объектов. Это делает ваше решение более универсальным и менее зависимым от окружения клиента.
Для использования БСП необходимо:
ФайловыйОбменКлиент.НачатьВыборФайла() и РаботаСФайлами.ПрочитатьФайлExcel() (имена функций могут немного отличаться в разных версиях БСП).Примерная логика на клиенте с использованием БСП будет выглядеть так:
&НаКлиенте
Процедура ЗагрузитьИзExcelБСП(Команда)
Параметры = Новый Структура;
Параметры.Вставить("Заголовок", "Выберите файл Excel");
Параметры.Вставить("Расширения", "xls,xlsx"); // Фильтр файлов
// Вызываем стандартный диалог выбора файла БСП
ФайловыйОбменКлиент.НачатьВыборФайла(Параметры, "ЗавершениеВыбораФайла", Ложь);
КонецПроцедуры
&НаКлиенте
Процедура ЗавершениеВыбораФайла(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Если ВыбранныеФайлы <> Неопределено И ВыбранныеФайлы.Количество() > 0 Тогда
ИмяФайла = ВыбранныеФайлы[0].ПолноеИмя; // Получаем полный путь к выбранному файлу
// Далее нужно прочитать файл на сервере.
// Функции БСП для чтения Excel могут быть реализованы как на клиенте, так и на сервере.
// Если чтение на сервере, потребуется передать двоичные данные файла.
// Для простоты, предположим, что есть функция, которая может прочитать файл по имени
// (хотя в реальных сценариях файл нужно будет сначала загрузить на сервер через временное хранилище).
// Пример (псевдокод, точная реализация зависит от версии БСП и способа получения данных):
Попытка
// Чтение файла на сервере. Возможно, потребуется предварительная загрузка файла на сервер.
МассивСтрокИзExcel = ПрочитатьExcelНаСервере(ИмяФайла);
// Преобразование МассивСтрокИзExcel (массив массивов) в ТаблицуЗначений
ДанныеДляЗагрузки = Новый ТаблицаЗначений;
ДанныеДляЗагрузки.Колонки.Добавить("НоменклатураНаименование");
ДанныеДляЗагрузки.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
ДанныеДляЗагрузки.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число"));
ДанныеДляЗагрузки.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Для Каждого СтрокаМассива Из МассивСтрокИзExcel Цикл
Если СтрокаМассива.Количество() >= 4 Тогда // Проверяем, что есть достаточно колонок
НоваяСтрока = ДанныеДляЗагрузки.Добавить();
НоваяСтрока.НоменклатураНаименование = СтрокаМассива[0];
НоваяСтрока.Количество = СтрокаМассива[1];
НоваяСтрока.Цена = СтрокаМассива[2];
НоваяСтрока.Сумма = СтрокаМассива[3];
КонецЕсли;
КонецЦикла;
ЗагрузитьДанныеНаСервере(ДанныеДляЗагрузки, ОбъектДокумента);
Исключение
Сообщить("Ошибка при чтении файла Excel через БСП: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецПроцедуры
// Пример серверной функции для чтения файла через БСП (требует доработки в зависимости от БСП)
&НаСервере
Функция ПрочитатьExcelНаСервере(ИмяФайла)
// Реальная функция БСП может требовать ДвоичныеДанные файла
// Например:
// ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяФайла);
// Возврат РаботаСФайлами.ПрочитатьФайлExcel(ДвоичныеДанныеФайла, 0); // Читаем первый лист
// В данном упрощенном примере мы имитируем возврат данных
// Реальная функция БСП вернет массив массивов или ТаблицуЗначений
// в зависимости от версии и параметров.
Возврат Новый Массив; // Заглушка
КонецФункции
Использование БСП является более современным и рекомендуемым подходом, особенно для новых разработок. Оно абстрагирует разработчика от низкоуровневой работы с файлами и COM-объектами, предоставляя унифицированный API.
Мы подробно рассмотрели процесс создания внешней обработки для загрузки данных из Excel в табличную часть документа 1С на управляемых формах. Мы начали с базового сценария использования COM-объектов, рассмотрели ключевые аспекты клиент-серверного взаимодействия, изучили различные стратегии поиска номенклатуры и научились правильно интегрировать нашу обработку в интерфейс документа.
Теперь вы обладаете всеми необходимыми знаниями для реализации подобного функционала в своих решениях. Всегда помните о важности обработки ошибок и информирования пользователя о ходе выполнения и возможных проблемах, таких как ненайденная номенклатура. Выбирайте подход (COM или БСП) исходя из требований к кроссплатформенности и универсальности вашего решения.