Иногда требуется создать внешнюю обработку с большим количеством реквизитов (в том числе и с табличной частью), первое, что мы делаем для сохранения настроек в этом случае - устанавливаем свойство формы АвтоматическоеСохранениеДанныхВНастройках в Использовать. Затем ставим напротив всех нужных реквизитов галочки "Сохранение" и наслаждаемся результатом.
Однако, очень часто обработку нужно проверять на тестовой базе или другом сервере и т.п., поэтому требуется сохранить все значения реквизитов формы (в том числе табличной части) в файл, а потом загрузить из файла уже в другой конфигурации.
То, что это возможно легко понять по намёкам на форумах, однако ни одного законченного решения найти не удалось. Что ж, к этому нам не привыкать, начинаем придумывать велосипед.
Пробуем сначала сохранить значения всех реквизитов формы в файл. Поиски в интернете приводят нас к процедуре ЗначениеВФайл и функции ЗначениеВСтрокуВнутр, приём последней пользоваться как бы не рекомендуется, якобы она предназначена для обмена с 7.7. Обе конструкции языка доступны только на сервере и толстом клиенте, поэтому возникает вопрос, как бы обойти значения всех реквизитов формы &НаСервере.
Ведь цикл
Для Каждого Реквизит Из Объект Цикл
КонецЦикла;
выдаст вполне ожидаемую не для всех ошибку: Итератор для значения не определен.
Тоже самое ожидает нас и с переменными ЭтаФорма, ЭтотОбъект. На такую простую вещь можно потратить пару часов, однако отладчик и поисковики творят чудеса - получить значения всех реквизитов формы внешней обработки на сервере можно с помощью следующего кода:
Для Каждого Реквизит Из ЭтаФорма.ПолучитьРеквизиты("Объект") Цикл
ЗначениеРеквизита = Объект[Реквизит.Имя];
КонецЦикла;
Казалось бы, теперь всё просто, однако не совсем. Табличная часть, как и таблица значений имеют не совсем ожидаемый тип значений, их тип ДанныеФормыКоллекция. А ведь они должны быть обязательно выгружены для сохранения, иначе в файле получим только их уникальные индификаторы.
В результате этого знания получаем:
// Сохраняет все данные формы в файл ПолноеИмяФайлаНастроек
&НаСервере
Процедура ЭкспортНастроекВФайлНаСервере(ПолноеИмяФайлаНастроек)
Настройки = Новый Структура;
Для Каждого Реквизит Из ЭтаФорма.ПолучитьРеквизиты("Объект") Цикл
ЗначениеРеквизита = Объект[Реквизит.Имя];
Если ТипЗнч(ЗначениеРеквизита) = Тип("ДанныеФормыКоллекция") Тогда
Настройки.Вставить(Реквизит.Имя, ЗначениеРеквизита.Выгрузить());
Продолжить;
КонецЕсли;
Настройки.Вставить(Реквизит.Имя, ЗначениеРеквизита);
КонецЦикла;
Попытка
ЗначениеВФайл(ПолноеИмяФайлаНастроек, Настройки);
Исключение
Сообщить("Ошибка при сохранении файла настроек: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
// Выбор файла для сохранения настроек и вызов серверной процедуры сохранения файла
&НаКлиенте
Процедура ЭкспортНастроекВФайл(Команда)
ДиалогСохраненияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
Фильтр = НСтр("ru = 'Файлы TXT'") + "(*.txt)|*.txt|";
ДиалогСохраненияФайла.Фильтр = Фильтр;
ДиалогСохраненияФайла.МножественныйВыбор = Ложь;
Если ДиалогСохраненияФайла.Выбрать() Тогда
ЭкспортНастроекВФайлНаСервере(ДиалогСохраненияФайла.ПолноеИмяФайла);
КонецЕсли;
КонецПроцедуры
Это даже работает, но радоваться рано - этот код неправильный! Мы допустили одну из самых распространенных ошибок при написании кода на управляемых формах. Выбрав имя файла на клиенте, а потом передав его на сервер, мы забыли, что при клиент-серверном режиме работы код, выполняемый на стороне сервера, будет запущен под другим пользователем. И далеко не факт, что этот пользователь будет иметь доступ туда (например, локальный диск терминальной сессии по RDP), куда мы сохраняем файл.
Поэтому переписываем код, сохраняем файл теперь на клиенте, правда придётся воспользоваться не очень рекомендуемой функцией ЗначениеВСтрокуВнутр:
// Сохраняет все данные формы в строку и возвращает её
&НаСервере
Функция ЭкспортНастроекВСтрокуНаСервере()
Настройки = Новый Структура;
Для Каждого Реквизит Из ЭтаФорма.ПолучитьРеквизиты("Объект") Цикл
ЗначениеРеквизита = Объект[Реквизит.Имя];
Если ТипЗнч(ЗначениеРеквизита) = Тип("ДанныеФормыКоллекция") Тогда
Настройки.Вставить(Реквизит.Имя, ЗначениеРеквизита.Выгрузить());
Продолжить;
КонецЕсли;
Настройки.Вставить(Реквизит.Имя, ЗначениеРеквизита);
КонецЦикла;
Возврат ЗначениеВСтрокуВнутр(Настройки);
КонецФункции
// Выбор файла для сохранения настроек и сохранение файла используя данные полученные на сервере
&НаКлиенте
Процедура ЭкспортНастроекВФайл(Команда)
ДиалогСохраненияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
Фильтр = НСтр("ru = 'Файлы TXT'") + "(*.txt)|*.txt|";
ДиалогСохраненияФайла.Фильтр = Фильтр;
ДиалогСохраненияФайла.МножественныйВыбор = Ложь;
Если ДиалогСохраненияФайла.Выбрать() Тогда
СтрокаНастроек = ЭкспортНастроекВСтрокуНаСервере();
Если СтрокаНастроек <> "" Тогда
Попытка
ФайлНастроек = Новый ЗаписьТекста(ДиалогСохраненияФайла.ПолноеИмяФайла);
ФайлНастроек.Записать(СтрокаНастроек);
ФайлНастроек.Закрыть();
Исключение
Сообщить("Ошибка при сохранении настроек на клиенте: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Теперь перейдём к загрузке из файла. Мы не будем использовать, как мы уже выяснили, следующий неправильный код с передачей только имени файла на сервер:
// Заполняет значения всех реквизитов формы из файла ПолноеИмяФайлаНастроек
Процедура ИмпортНастроекИзФайлаНаСервере(ПолноеИмяФайлаНастроек)
Попытка
Настройки = ЗначениеИзФайла(ПолноеИмяФайлаНастроек);
Исключение
Сообщить("Ошибка при чтении файла настроек: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Для Каждого ЭлементНастройки Из Настройки Цикл
Попытка
Если ТипЗнч(Объект[ЭлементНастройки.Ключ]) = Тип("ДанныеФормыКоллекция") Тогда
Объект[ЭлементНастройки.Ключ].Загрузить(ЭлементНастройки.Значение);
Продолжить;
КонецЕсли;
Объект[ЭлементНастройки.Ключ] = ЭлементНастройки.Значение;
Исключение
Сообщить("Ошибка при импорте настроек: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
// Выбор файла для загрузки настроек и вызов серверной процедуры загрузки файла
&НаКлиенте
Процедура ИмпортНастроекИзФайла(Команда)
ДиалогСохраненияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Фильтр = НСтр("ru = 'Файлы TXT'") + "(*.txt)|*.txt|";
ДиалогСохраненияФайла.Фильтр = Фильтр;
ДиалогСохраненияФайла.МножественныйВыбор = Ложь;
Если ДиалогСохраненияФайла.Выбрать() Тогда
ИмпортНастроекИзФайлаНаСервере(ДиалогСохраненияФайла.ПолноеИмяФайла);
КонецЕсли;
КонецПроцедуры
А напишем другой вариант, например, используя временное хранилище:
// Заполняет значения всех реквизитов формы из временого хранилища, адрес которого указан в АдресВременногоХранилища
Процедура ИмпортНастроекИзВременногоХранилищаНаСервере(АдресВременногоХранилища)
Если НЕ ЭтоАдресВременногоХранилища(АдресВременногоХранилища) Тогда
Возврат;
КонецЕсли;
ИмяВременногоФайлаНаСервере = ПолучитьИмяВременногоФайла();
Попытка
ПолучитьИзВременногоХранилища(АдресВременногоХранилища).Записать(ИмяВременногоФайлаНаСервере);
Настройки = ЗначениеИзФайла(ИмяВременногоФайлаНаСервере);
УдалитьФайлы(ИмяВременногоФайлаНаСервере);
Исключение
Сообщить("Ошибка при загрузке настроек на сервере: " + ОписаниеОшибки());
УдалитьФайлы(ИмяВременногоФайлаНаСервере);
Возврат;
КонецПопытки;
Для Каждого ЭлементНастройки Из Настройки Цикл
Попытка
Если ТипЗнч(Объект[ЭлементНастройки.Ключ]) = Тип("ДанныеФормыКоллекция") Тогда
Объект[ЭлементНастройки.Ключ].Загрузить(ЭлементНастройки.Значение);
Продолжить;
КонецЕсли;
Объект[ЭлементНастройки.Ключ] = ЭлементНастройки.Значение;
Исключение
Сообщить("Ошибка при импорте настроек: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
// При выборе файла вызов серверной процедуры загрузки файла из временного хранилища
&НаКлиенте
Процедура ОбработатьВыборФайлаИмпорта(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если НЕ Результат Тогда
Возврат;
КонецЕсли;
ИмпортНастроекИзВременногоХранилищаНаСервере(Адрес);
КонецПроцедуры
// Выбор файла для загрузки настроек
&НаКлиенте
Процедура ИмпортНастроекИзФайла(Команда)
ДиалогСохраненияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Фильтр = НСтр("ru = 'Файлы TXT'") + "(*.txt)|*.txt|";
ДиалогСохраненияФайла.Фильтр = Фильтр;
ДиалогСохраненияФайла.МножественныйВыбор = Ложь;
Если ДиалогСохраненияФайла.Выбрать() Тогда
Оповещение = Новый ОписаниеОповещения("ОбработатьВыборФайлаИмпорта", ЭтаФорма);
НачатьПомещениеФайла(Оповещение, , ДиалогСохраненияФайла.ПолноеИмяФайла, Ложь, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
Если внимательно посмотреть код, то легко заметить, что после получения данных из временного хранилища (с типом ДвоичныеДанные) мы вынуждены записать их во временный файл. Другого нормального способа получить данные с типом Строка из временного хранилища попросту нет.
Чтобы не насиловать файловую систему временными файлами, давайте перепишем код, без использования временного хранилища, но с использованием не очень рекомендуемой функции ЗначениеИзСтрокиВнутр:
// Заполняет значения всех реквизитов формы из строки СтрокаНастроек
Процедура ИмпортНастроекИзСтрокиНаСервере(СтрокаНастроек)
Если НЕ ЗначениеЗаполнено(СтрокаНастроек) Тогда
Сообщить("Ошибка. Пустое значение в настройках, импорт не произведён.");
Возврат;
КонецЕсли;
Попытка
Настройки = ЗначениеИзСтрокиВнутр(СтрокаНастроек);
Исключение
Сообщить("Ошибка при загрузке настроек на сервере: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
Для Каждого ЭлементНастройки Из Настройки Цикл
Попытка
Если ТипЗнч(Объект[ЭлементНастройки.Ключ]) = Тип("ДанныеФормыКоллекция") Тогда
Объект[ЭлементНастройки.Ключ].Загрузить(ЭлементНастройки.Значение);
Продолжить;
КонецЕсли;
Объект[ЭлементНастройки.Ключ] = ЭлементНастройки.Значение;
Исключение
Сообщить("Ошибка при импорте настроек: " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
// Выбор файла для загрузки настроек и вызов серверной процедуры загрузки
&НаКлиенте
Процедура ИмпортНастроекИзФайла(Команда)
ДиалоЧтенияФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Фильтр = НСтр("ru = 'Файлы TXT'") + "(*.txt)|*.txt|";
ДиалоЧтенияФайла.Фильтр = Фильтр;
ДиалоЧтенияФайла.МножественныйВыбор = Ложь;
Если ДиалоЧтенияФайла.Выбрать() Тогда
Попытка
ФайлНастроек = Новый ЧтениеТекста(ДиалоЧтенияФайла.ПолноеИмяФайла);
СтрокаНастроек = ФайлНастроек.Прочитать();
ФайлНастроек.Закрыть();
Исключение
Сообщить("Ошибка при чтении настроек на клиенте: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
ИмпортНастроекИзСтрокиНаСервере(СтрокаНастроек);
КонецЕсли;
КонецПроцедуры
Как видно, вариантов сохранения настроек в файл и чтения настроек из файла очень много, даже в этой статье мы рассмотрели далеко не все, однако эти варианты рабочие и должны сэкономить вам время, когда у вас возникнет такая необходимость.
В прикрепленной ниже внешней обработке используется последний вариант сохранения и чтения настроек через файл из этой статьи, ну и, конечно, автосохранение штатными средствами в информационной базе.