Разработка обработок для импорта данных из внешних файлов (в частности, формата Excel) в документы 1С:Предприятия 8.3 — одна из самых частых задач программиста. При этом критически важно соблюдать базовые стандарты разработки и правильно распределять нагрузку между клиентской и серверной частями приложения. В этой статье мы подробно разберем, какие действия должны выполняться на клиенте, какие — на сервере, как передавать файлы без лишней нагрузки на сеть, а также рассмотрим особенности проверки реквизитов и выполнения операций в фоновом режиме.
Для начала сформулируем фундаментальное правило разделения контекстов в управляемом приложении 1С:
ТаблицаЗначений.ДокументОбъект.ОперацияБух), а также транзакционные операции.Рассмотрим детально каждый этап загрузки данных.
Недопустимо пытаться открыть файл напрямую на сервере по локальному пути (например, "C:\MyFolder\file.xlsx"), так как у сервера 1С нет прямого доступа к файловой системе компьютера пользователя. Правильный подход — прочитать файл на клиенте и передать его на сервер через Временное хранилище.
В современных управляемых формах (особенно для поддержки работы в веб-клиенте) синхронные методы работы с файлами заблокированы. Поэтому мы должны использовать асинхронные методы платформы. Разберем шаблон вызова загрузки файла на клиенте:
&НаКлиенте
Процедура ЗагрузитьДанные(Команда)
// Первичная проверка заполнения ключевых параметров на клиенте
Если НЕ ЗначениеЗаполнено(Объект.ИмяФайлаДанных) Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(НСтр("ru = 'Не указан файл данных для загрузки'"));
Возврат;
КонецЕсли;
// Описание оповещения о завершении выбора и загрузки файла
ОписаниеОповещения = Новый ОписаниеОповещения("ЗагрузитьДанныеЗавершение", ЭтотОбъект);
// Используем безопасный асинхронный механизм БСП и платформы
ПараметрыЗагрузки = Новый ПараметрыПомещенияФайла;
ПараметрыЗагрузки.Интерактивно = Ложь; // Файл уже выбран в реквизите
НачатьПомещениеФайлаНаСервер(ОписаниеОповещения, , , Объект.ИмяФайлаДанных, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ЗагрузитьДанныеЗавершение(РезультатЗагрузки, ДополнительныеПараметры) Экспорт
Если РезультатЗагрузки = Неопределено Или НЕ РезультатЗагрузки.ИмяФайлаПомещено Тогда
Возврат; // Пользователь отменил операцию или произошла ошибка
КонецЕсли;
// Передаем адрес временного хранилища на сервер для дальнейшей обработки
АдресХранилища = РезультатЗагрузки.Адрес;
ЗагрузитьДанныеНаСервере(АдресХранилища);
КонецПроцедуры
После того как файл в виде двоичных данных попал во временное хранилище, управление передается на сервер. Здесь у нас есть несколько вариантов чтения структуры Excel. Выясним преимущества и недостатки каждого из них:
Этот метод часто используется разработчиками благодаря быстродействию (особенно через ADO-соединение). Однако у него есть критический недостаток — полное отсутствие кроссплатформенности. Если сервер 1С работает под управлением ОС Linux (что сейчас является стандартом для многих крупных инсталляций), попытка создания COM-объекта вызовет ошибку. Также этот метод требует физической установки пакета Microsoft Office на сервере или клиенте.
Это полностью кроссплатформенный и безопасный вариант, который работает на любой операционной системе сервера без установки стороннего ПО. Платформа 1С умеет нативно читать файлы .xlsx и .xls.
Рассмотрим пример серверной процедуры, которая получает двоичные данные из хранилища, сохраняет их во временный файл на сервере и считывает с помощью ТабличногоДокумента:
&НаСервере
Процедура ЗагрузитьДанныеНаСервере(АдресХранилища)
// Получаем двоичные данные из хранилища
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
// Создаем уникальное имя временного файла на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ДвоичныеДанныеФайла.Записать(ИмяВременногоФайла);
// Читаем файл в Табличный документ
ТабДок = Новый ТабличныйДокумент;
Попытка
ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
// Обязательно удаляем временный файл при ошибке
УдалитьФайлы(ИмяВременногоФайла);
ВызватьИсключение НСтр("ru = 'Не удалось прочитать файл Excel. Возможно, файл поврежден или имеет неверный формат.'");
КонецПопытки;
// Удаляем временный файл с диска сервера сразу после чтения в оперативную память
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Логируем ошибку удаления, если файл заблокирован процессом
КонецПопытки;
// Парсим табличный документ и создаем документы базы данных
ОбработатьТабличныйДокументНаСервере(ТабДок);
КонецПроцедуры
После прочтения файла в объект ТабличныйДокумент мы можем обойти его строки на сервере, выполнить поиск элементов справочников по наименованию или артикулу и сформировать необходимые документы. Поскольку мы создаем документы типа ОперацияБух (которые являются чисто серверными объектами), вся эта логика изолирована на сервере.
&НаСервере
Процедура ОбработатьТабличныйДокументНаСервере(ТабДок)
КоличествоСтрок = ТабДок.ВысотаТаблицы;
// Предполагаем, что первая строка содержит заголовки колонок, читаем со 2-й строки
Для НомерСтроки = 2 По КоличествоСтрок Цикл
Артикул = СокрЛП(ТабДок.ПолучитьОбласть(НомерСтроки, 1).ТекущийОбласть.Текст);
Количество = Число(ТабДок.ПолучитьОбласть(НомерСтроки, 2).ТекущийОбласть.Текст);
Сумма = Число(ТабДок.ПолучитьОбласть(НомерСтроки, 3).ТекущийОбласть.Текст);
Если ПустаяСтрока(Артикул) Тогда
Продолжить;
КонецЕсли;
// Поиск номенклатуры в базе данных
СсылкаНоменклатура = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", Артикул);
Если СсылкаНоменклатура.Пустая() Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
СтроковыеФункцииКлиентСервер.ШаблонПрогресса(НСтр("ru = 'Строка %1: Номенклатура с артикулом %2 не найдена!'"), НомерСтроки, Артикул)
);
Продолжить;
КонецЕсли;
// Логика создания документа ОперацияБух (Бухгалтерская справка)
СоздатьОперациюБухгалтерскогоУчета(СсылкаНоменклатура, Количество, Сумма);
КонецЦикла;
КонецПроцедуры
Разберем подробнее, как правильно организовать проверку реквизитов формы (например, Организация, Подразделение, Период), которые должны быть заполнены перед началом чтения файла Excel.
Для интерактивных реквизитов формы, которые добавлены на форму нашей обработки, самым простым и правильным способом является использование свойства реквизита Проверка заполнения. Установите для нужных реквизитов значение Выдавать ошибку. В этом случае при нажатии на кнопку выполнения команды система автоматически (на уровне платформы) проверит заполненность полей и подсветит их пользователю красным цветом, не вызывая серверных процедур.
Если же логика проверки более сложная (например, зависит от комбинации значений), то в коде обработки на сервере следует использовать типовые механизмы проверки заполнения:
&НаСервере
Функция ПроверитьЗаполнениеРеквизитовНаСервере()
// Преобразуем реквизиты формы в реальный прикладной объект обработки
ОбъектОбработки = РеквизитФормыВЗначение("Объект");
// Вызываем стандартный метод проверки заполнения
// Он инициализирует событие ОбработкаПроверкиЗаполнения в модуле объекта обработки
Возврат ОбъектОбработки.ПроверитьЗаполнение();
КонецФункции
Метод ПроверитьЗаполнение() удобен тем, что он собирает все ошибки заполнения реквизитов в соответствии с правилами, описанными в модуле объекта обработки, и выводит их пользователю единым списком сообщений.
Часто такие загрузчики проектируются как внешние обработки (файлы .epf). Если вам необходимо запустить экспортную процедуру из модуля объекта вашей внешней обработки, находясь в контексте формы этой же обработки, используйте метод преобразования реквизита формы в объект:
&НаСервере
Процедура ВыполнитьМетодВнешнейОбработки()
// Создаем полноценный прикладной объект из данных формы
ОбъектЭкземпляр = РеквизитФормыВЗначение("Объект");
// Вызываем экспортную процедуру модуля объекта внешней обработки
ОбъектЭкземпляр.ВыполнитьЗагрузкуДанных();
// Если объект изменил свои внутренние реквизиты и их нужно вернуть на форму:
ЗначениеВРеквизитФормы(ОбъектЭкземпляр, "Объект");
КонецПроцедуры
Если же перед вами стоит задача запуска длительного процесса импорта в фоновом задании (чтобы сеанс пользователя не зависал на время чтения 100 000 строк из Excel), мы рекомендуем использовать стандартную библиотеку БСП. Механизм длительных операций позволяет запустить процедуру общего модуля или метода внешней обработки в фоне с помощью функции ДлительныеОперации.ЗапуститьВыполнениеВФоне(), передавая туда параметры и адрес временного хранилища с файлом данных. Это избавит вас от необходимости писать собственные сложные "велосипеды" по отслеживанию состояния потоков выполнения.