Как правильно организовать выбор файла, клиент-серверное взаимодействие и запись объектов в управляемых формах 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Перевод функционала со стандартных обычных форм на управляемые формы платформы 1С:Предприятие 8 часто вызывает затруднения у начинающих разработчиков. Управляемый интерфейс строится на принципиально иной архитектуре: четком разделении контекста выполнения на клиентский и серверный, использовании командного интерфейса и строгих правилах работы с типами данных и файловой системой. Разберем по шагам, как решить базовые задачи при создании внешней обработки на управляемых формах.

1. Добавление кнопок и реквизитов на управляемую форму

В обычных формах элементы управления можно было напрямую привязать к переменным модуля или создать кнопку без привязки к отдельной сущности. В управляемых формах логика изменилась:

  1. Реквизиты формы: Чтобы отобразить на форме поле (например, путь к файлу), необходимо во вкладке «Реквизиты» редактора формы создать реквизит формы (например, ПутьКФайлу с типом Строка) и перетащить его в левое дерево элементов формы.
  2. Команды формы: Кнопки на форме создаются только на основании команд. Для добавления действия перейдем на вкладку «Команды», создадим команду (например, ЗагрузитьДанные), назначим ей обработчик действия на клиенте (&НаКлиенте) и перетащим созданную команду в дерево элементов или на командную панель формы.
  3. Кнопка выбора у поля ввода: Если требуется организовать выбор файла прямо по нажатию кнопки в поле ввода, у соответствующего элемента формы установим свойство КнопкаВыбора = Истина и создадим обработчик события НачалоВыбора.

2. Выбор файла на клиенте и его чтение на сервере

Рассмотрим типовую проблему: интерактивный диалог выбора файла выполняется на стороне клиента, однако чтение тяжелых табличных данных и запись в информационную базу должны производиться на сервере. Для этого файл помещается во временное хранилище на клиенте и извлекается на сервере.

Посмотрим на пример организации выбора файла и передачи его на сервер:


&НаКлиенте
Процедура ПутьКФайлуНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    Диалог.Заголовок = "Выберите файл для загрузки";
    Диалог.Фильтр = "Файл Excel (*.xls;*.xlsx)|*.xls;*.xlsx|Табличный документ (*.mxl)|*.mxl";
    Диалог.ПолноеИмяФайла = Объект.ПутьКФайлу;
    
    Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
    Диалог.Показать(Оповещение);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораФайла(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
    Если ВыбранныеФайлы = Неопределено Или ВыбранныеФайлы.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Записываем путь в реквизит формы для отображения в поле
    Объект.ПутьКФайлу = ВыбранныеФайлы[0];
    
    // Помещаем файл во временное хранилище для передачи на сервер
    ОповещениеПомещения = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
    НачатьПомещениеФайлаНаСервер(ОповещениеПомещения, , , , Объект.ПутьКФайлу, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
    Если ОписаниеПомещенногоФайла = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную процедуру обработки файла
    ОбработатьФайлНаСервере(ОписаниеПомещенногоФайла.Адрес);
    ПоказатьОповещениеПользователя("Загрузка завершена", , "Данные успешно обработаны");
КонецПроцедуры

На стороне сервера двоичные данные извлекаются из хранилища и загружаются в ТабличныйДокумент без необходимости использования нестабильных COM-объектов:


&НаСервере
Процедура ОбработатьФайлНаСервере(АдресВХранилище)
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    ТабДок = Новый ТабличныйДокумент;
    Попытка
        ТабДок.Прочитать(ИмяВременногоФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
    Исключение
        УдалитьФайлы(ИмяВременногоФайла);
        ВызватьИсключение "Ошибка чтения файла: " + ОписаниеОшибки();
    КонецПопытки;
    
    УдалитьФайлы(ИмяВременногоФайла);
    
    // Обработка строк таблицы
    КоличествоСтрок = ТабДок.ВысотаТаблицы;
    Для НомерСтроки = 2 По КоличествоСтрок Цикл
        НомерЛС = СокрЛП(ТабДок.ПолучитьОбласть("R" + НомерСтроки + "C1").ТекущаяОбласть.Текст);
        НомерЕЛС = СокрЛП(ТабДок.ПолучитьОбласть("R" + НомерСтроки + "C2").ТекущаяОбласть.Текст);
        
        Если ЗначениеЗаполнено(НомерЛС) Тогда
            ОбновитьДанныеЛицевогоСчета(НомерЛС, НомерЕЛС);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

3. Поиск и модификация объектов базы данных

Частая ошибка при переходе с обычных форм — попытка вызвать менеджер справочника (Справочники.КУ_ЛицевыеСчета) в процедуре с директивой &НаКлиенте. На клиенте глобальная коллекция Справочники недоступна. Все операции чтения и записи объектов должны выполняться исключительно в контексте &НаСервере или &НаСервереБезКонтекста.

Кроме того, полный перебор справочника через Справочники.КУ_ЛицевыеСчета.Выбрать() является крайне неоптимальным. Для поиска конкретной записи следует использовать объект Запрос или метод НайтиПоРеквизиту.

Рассмотрим оптимальный вариант обновления данных с контролем транзакций:


&НаСервере
Процедура ОбновитьДанныеЛицевогоСчета(НомерЛС, НомерЕЛС)
    // Оптимальный поиск ссылки через запрос
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   КУ_ЛицевыеСчета.Ссылка КАК Ссылка,
        |   КУ_ЛицевыеСчета.Помещение КАК Помещение
        |ИЗ
        |   Справочник.КУ_ЛицевыеСчета КАК КУ_ЛицевыеСчета
        |ГДЕ
        |   КУ_ЛицевыеСчета.НомерЛицевогоСчета = &НомерЛицевогоСчета";
    Запрос.УстановитьПараметр("НомерЛицевогоСчета", НомерЛС);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        НачатьТранзакцию();
        Попытка
            ОбъектЛС = Выборка.Ссылка.ПолучитьОбъект();
            ОбъектЛС.ГИСЖКХЕдиныйЛицевойСчет = НомерЕЛС;
            ОбъектЛС.Записать();
            
            Если ЗначениеЗаполнено(Выборка.Помещение) Тогда
                ОбъектПомещение = Выборка.Помещение.ПолучитьОбъект();
                ОбъектПомещение.ИдентКодВ_ГИС_ЖКХ = НомерЕЛС;
                ОбъектПомещение.Записать();
            КонецЕсли;
            
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
            ЗаписьЖурналаРегистрации("ЗагрузкаЕЛС", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
КонецПроцедуры

4. Причины ошибок при вызове метода Записать() и использование ОбменДанными.Загрузка

Если при вызове метода Записать() возникает ошибка контекста выполнения, проанализируем основные причины:

  1. Отказ в модуле объекта: В процедурах ПередЗаписью или ПриЗаписи срабатывает встроенная бизнес-логика конфигурации (например, проверка обязательного заполнения полей, проверка уникальности номера или формата данных), выставляющая флаг Отказ = Истина.
  2. Отсутствие серверной отладки: Если отладчик не заходит в модуль объекта, необходимо проверить параметры запуска службы сервера 1С:Предприятие (ключ -debug) и в конфигураторе включить режим «Остановка по ошибке» в меню «Отладка».

В качестве временной меры при загрузке нормативно-справочной информации используют установку свойства ОбменДанными.Загрузка = Истина:


ОбъектЛС = Выборка.Ссылка.ПолучитьОбъект();
ОбъектЛС.ОбменДанными.Загрузка = Истина;
ОбъектЛС.ГИСЖКХЕдиныйЛицевойСчет = НомерЕЛС;
ОбъектЛС.Записать();

Важно понимать последствия: включение режима загрузки отключает выполнение обработчиков событий модуля объекта, игнорирует системные проверки заполнения реквизитов и подавляет авторегистрацию изменений в планах обмена. Применять данный режим следует только тогда, когда точно известна причина блокировки записи и гарантируется целостность записываемых данных.

← На главную