При работе с внешними обработками в 1С:Предприятии 8.3, особенно на управляемых формах, мы часто сталкиваемся с необходимостью сохранять данные между сеансами работы. Это особенно актуально для табличных частей (ТЧ), которые по умолчанию не имеют встроенного механизма автоматического сохранения настроек формы. Если на форме нет специальной галочки для сохранения реквизита ТЧ, нам приходится искать программные решения. В этой статье мы подробно рассмотрим, как эффективно и надежно сохранять значения табличных частей, учитывая особенности разработки внешних обработок на управляемых формах и избегая распространенных ошибок.
Самый правильный и поддерживаемый платформой способ сохранения данных формы, включая табличные части, — это использование стандартных обработчиков настроек формы: ПриСохраненииДанныхВНастройкахНаСервере и ПриЗагрузкеДанныхИзНастроекНаСервере. Эти обработчики предоставляют нам возможность гибко управлять тем, какие данные и как будут сохраняться в настройках пользователя. Давайте разберем этот механизм по шагам.
Чтобы платформа знала, что у нас есть данные для сохранения, которые были изменены, необходимо установить специальный флаг. Мы используем реквизит формы СохраняемыеВНастройкахДанныеМодифицированы и присваиваем ему значение Истина. Это нужно сделать при любом изменении данных, которые вы хотите сохранить, или перед закрытием формы. Типичное место для этого — обработчик события ПередЗакрытием формы на клиенте.
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
// Уведомляем платформу, что есть измененные данные для сохранения
СохраняемыеВНастройкахДанныеМодифицированы = Истина;
КонецПроцедуры
Этим действием мы сигнализируем системе, что нужно вызвать серверный обработчик сохранения настроек, который мы опишем далее.
Для сохранения данных табличной части воспользуемся серверным обработчиком ПриСохраненииДанныхВНастройкахНаСервере. В этот обработчик платформа передает параметр Настройки, который представляет собой коллекцию произвольного типа (по сути, это объект, похожий на структуру или соответствие, где мы можем хранить свои значения по ключам). Мы можем добавить в эту коллекцию нашу таблицу значений, предварительно выгрузив ее из табличной части объекта формы.
&НаСервере
Процедура ПриСохраненииДанныхВНастройкахНаСервере(Настройки)
// Выгружаем данные из табличной части в ТаблицуЗначений
Настройки["ТекстРасшифровки"] = Объект.ТекстРасшифровки.Выгрузить();
КонецПроцедуры
В данном примере Объект.ТекстРасшифровки — это ваша табличная часть, которая является реквизитом формы. Метод Выгрузить() преобразует ее в объект ТаблицаЗначений, который затем сохраняется в коллекции Настройки под произвольным, но уникальным для данной формы ключом, в нашем случае это "ТекстРасшифровки". Этот ключ будет использоваться при последующей загрузке.
Для восстановления данных при следующем открытии формы используется симметричный серверный обработчик ПриЗагрузкеДанныхИзНастроекНаСервере. Здесь мы извлекаем ранее сохраненную ТаблицуЗначений из параметра Настройки и загружаем ее обратно в табличную часть объекта формы.
&НаСервере
Процедура ПриЗагрузкеДанныхИзНастроекНаСервере(Настройки)
// Получаем сохраненную ТаблицуЗначений
ТЗ = Настройки["ТекстРасшифровки"];
Если ТипЗнч(ТЗ) = Тип("ТаблицаЗначений") Тогда
// Загружаем данные обратно в табличную часть
Объект.ТекстРасшифровки.Загрузить(ТЗ);
КонецЕсли;
КонецПроцедуры
Очень важно выполнить проверку типа: Если ТипЗнч(ТЗ) = Тип("ТаблицаЗначений") Тогда. Почему это так важно? При первом открытии обработки или в случае, если пользователь еще не сохранял никаких настроек для этой формы, ключ "ТекстРасшифровки" может отсутствовать в коллекции Настройки или содержать пустое значение. Без этой проверки попытка загрузить некорректное значение вызовет ошибку.
Этот подход является наиболее предпочтительным, поскольку он штатно поддерживается платформой, обеспечивает надежное сохранение и загрузку данных и не сопряжен с рисками, присущими низкоуровневым функциям. Он гарантирует, что ваши данные будут корректно сохраняться между сеансами работы пользователя с внешней обработкой. Для реализации сохранения настроек формы средствами БСП можно использовать обработку для сохранения настроек формы для всех пользователей.
Некоторые разработчики, сталкиваясь с отсутствием явной опции сохранения ТЧ, прибегают к использованию функций ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр(). Эти функции позволяют преобразовать любое значение 1С во внутреннее строковое представление и обратно. Этот метод часто называют "изощрением" или "извращением" из-за его особенностей и рисков, но он может быть рабочим в определенных случаях. Рассмотрим его принцип работы и выясним, почему он не является рекомендуемым и как возникают ошибки формата потока в 1С.
Идея заключается в том, чтобы при закрытии формы выгрузить табличную часть в ТаблицуЗначений, затем преобразовать эту таблицу в строку с помощью ЗначениеВСтрокуВнутр() и сохранить эту строку в реквизит формы (например, строковый реквизит неограниченной длины). При следующем открытии формы сохраненная строка из реквизита преобразуется обратно в ТаблицуЗначений с помощью ЗначениеИзСтрокиВнутр() и загружается в табличную часть. Давайте посмотрим на пример кода, который демонстрирует этот подход:
// Сохранение при закрытии формы
&НаСервере
Процедура СохранитьТЧНаСервере()
// Выгружаем табличную часть формы в ТаблицуЗначений
ТЗ = Объект.ТЧ.Выгрузить();
// Преобразуем ТаблицуЗначений во внутреннюю строку
// Объект.Таб - это реквизит формы, например, типа "Строка неограниченной длины"
Объект.Таб = ЗначениеВСтрокуВнутр(ТЗ);
КонецПроцедуры
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
// Вызываем серверную процедуру для сохранения данных
СохранитьТЧНаСервере();
КонецПроцедуры
// Восстановление при открытии формы
&НаСервере
Процедура ВосстановитьТЧНаСервере()
// Проверяем, есть ли сохраненные данные
Если ЗначениеЗаполнено(Объект.Таб) Тогда
// Преобразуем строку обратно в ТаблицуЗначений
ТЗ = ЗначениеИзСтрокиВнутр(Объект.Таб);
// Загружаем данные в табличную часть формы
Объект.ТЧ.Загрузить(ТЗ);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Вызываем серверную процедуру для восстановления данных
ВосстановитьТЧНаСервере();
КонецПроцедуры
В этом примере Объект.ТЧ — это ваша табличная часть, а Объект.Таб — это дополнительный реквизит формы типа "Строка неограниченной длины", предназначенный для хранения сериализованных данных ТЧ.
Несмотря на то, что этот подход может показаться рабочим (и действительно, в простых случаях он может выполнять свою функцию), платформа 1С явно не рекомендует его для общего использования. Разберем основные причины, почему мы должны избегать использования ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр(), и почему они часто приводят к проблемам, таким как "ошибка формата потока":
Согласно документации (например, в Справке по синтаксису платформы), "Строковое представление данных имеет специальный системный формат, использующий идентификацию данных внутри одной информационной базы. Используется для сохранения функциональной совместимости с 1С:Предприятием 7.7. Использовать для других целей не рекомендуется." Это критическое замечание. Оно означает, что формат строки, генерируемой ЗначениеВСтрокуВнутр(), не является стандартизированным или документированным для широкого применения. Он привязан к конкретной базе данных и может содержать ссылки на внутренние идентификаторы объектов (УИДы, ссылки на таблицы, внутренние служебные поля и т.д.), которые специфичны для данной ИБ.
Поскольку формат внутренний и предназначен в основном для обеспечения обратной совместимости с платформой 1С:Предприятие 7.7, 1С не гарантирует его стабильность и неизменность в будущих версиях платформы 8.х. То, что работает сегодня в одной версии платформы, может перестать работать после обновления платформы или даже конфигурации. Это создает значительные риски для долгосрочной поддержки и работоспособности вашего решения.
Ошибка "формата потока и преобразования", с которой столкнулся автор темы, является одним из наиболее частых симптомов некорректного использования или повреждения данных, сериализованных таким способом. Давайте проанализируем возможные причины возникновения этой ошибки:
ЗначениеИзСтрокиВнутр() не сможет ее корректно десериализовать, так как формат будет нарушен.ЗначениеВСтрокуВнутр()) и моментом их загрузки (вызовом ЗначениеИзСтрокиВнутр()), это также может вызвать ошибку. Внутренний формат может быть чувствителен к структуре данных на момент сериализации.Категорически не рекомендуется использовать эти функции для обмена данными между разными информационными базами. Поскольку внутренний формат содержит привязку к идентификаторам текущей ИБ, при переносе в другую базу эти идентификаторы не будут совпадать, что приведет к неработоспособности функции ЗначениеИзСтрокиВнутр() и, скорее всего, к ошибкам.
Мы должны учитывать, что при завершении работы всего приложения (не просто закрытии одной формы), серверные вызовы из клиентских обработчиков (например, из ПередЗакрытием) могут быть запрещены или работать некорректно из-за того, что платформа уже находится в процессе выключения. Это может привести к тому, что сохранение данных не произойдет, если пользователь закроет программу полностью, а не только текущую форму.
В целом, мы настоятельно рекомендуем избегать использования ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр() для сохранения настроек форм или других пользовательских данных, если есть более стандартные и надежные альтернативы. Этот метод должен рассматриваться только как крайняя мера в очень специфических сценариях и с полным пониманием всех вышеупомянутых рисков.
Помимо рассмотренных выше методов, платформа 1С предлагает и другие механизмы для сохранения различных данных. Их выбор зависит от конкретной ситуации, требований к объему данных, их доступности и сценария использования. Давайте рассмотрим некоторые из них.
Для сохранения настроек, специфичных для текущего пользователя, или общих настроек для всех пользователей, мы можем изучить работу с ХранилищемНастроек и ХранилищемОбщихНастроек. Эти объекты предоставляют удобные методы Сохранить() и Загрузить(), позволяющие работать с произвольными значениями. Мы можем сохранять целые ТаблицыЗначений в такие хранилища. Для просмотра, восстановления и очистки данных в таком хранилище можно использовать обработку для работы с хранилищем настроек пользователей.
// Переменная для уникального ключа настроек
// Ключ должен быть уникальным в рамках хранилища, чтобы не пересекаться с другими настройками
Перем КлючНастроек;
// Инициализация ключа при создании формы
&НаСервере
Процедура ПриСозданииНаСервере()
КлючНастроек = "МояВнешняяОбработка/ДанныеТабличнойЧасти"; // Произвольный, но уникальный ключ
КонецПроцедуры
// Пример сохранения ТабличнойЧасти в ХранилищеОбщихНастроек
&НаСервере
Процедура СохранитьТЧВХранилищеНаСервере()
// Выгружаем табличную часть в ТаблицуЗначений
ТаблицаДляСохранения = Объект.ТЧ.Выгрузить();
// Сохраняем ТаблицуЗначений в ХранилищеОбщихНастроек по заданному ключу
ХранилищеОбщихНастроек.Сохранить(ТаблицаДляСохранения, КлючНастроек);
// Для ХранилищаНастроек используем: ХранилищеНастроек.Сохранить(ТаблицаДляСохранения, КлючНастроек);
КонецПроцедуры
// Пример загрузки ТабличнойЧасти из ХранилищаОбщихНастроек
&НаСервере
Процедура ЗагрузитьТЧИзХранилищаНаСервере()
// Загружаем данные из ХранилищаОбщихНастроек
ТЗ = ХранилищеОбщихНастроек.Загрузить(КлючНастроек);
// Проверяем, что данные были найдены и имеют нужный тип
Если ТЗ <> Неопределено И ТипЗнч(ТЗ) = Тип("ТаблицаЗначений") Тогда
// Загружаем полученную ТаблицуЗначений обратно в табличную часть формы
Объект.ТЧ.Загрузить(ТЗ);
КонецЕсли;
КонецПроцедуры
// Вызовы из клиентских процедур формы
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// При открытии формы загружаем ранее сохраненные данные
ЗагрузитьТЧИзХранилищаНаСервере();
КонецПроцедуры
&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
// При закрытии формы сохраняем текущие данные
СохранитьТЧВХранилищеНаСервере();
КонецПроцедуры
При использовании ХранилищаНастроек или ХранилищаОбщихНастроек необходимо быть внимательными к контексту вызова. Если данные сохраняются при закрытии формы в рамках завершения работы всего приложения (не просто закрытие формы, а полное выключение 1С:Предприятия), серверные вызовы могут быть ограничены или запрещены на поздних этапах завершения работы. В таких случаях нужно убедиться, что код сохранения выполняется до того, как система начнет закрывать сессию, возможно, раньше, чем в стандартном обработчике ПередЗакрытием, или использовать специальные механизмы, позволяющие выполнить серверный код при завершении работы (если такие имеются в вашей версии платформы).
Для более универсального способа сохранения и передачи данных, особенно между различными информационными базами или для использования вне 1С, может быть применена сериализация и десериализация данных в 1С: XML, JSON и XDTO. Для этого применяются объекты ЗаписьXML и СериализаторXDTO.
// Функция для преобразования ТабличнойЧасти в XML-строку
&НаСервере
Функция ТЧВXMLСтроку(ТабличнаяЧасть)
Результат = "";
// Выгружаем табличную часть в ТаблицуЗначений для сериализации
ТаблицаЗначений = ТабличнаяЧасть.Выгрузить();
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку(); // Указываем, что запись будет производиться в строку
Сериализатор = Новый СериализаторXDTO;
Сериализатор.ЗаписатьXML(ЗаписьXML, ТаблицаЗначений); // Сериализуем ТаблицуЗначений в XML
Результат = ЗаписьXML.Закрыть(); // Получаем готовую XML-строку
Возврат Результат;
КонецФункции
// Функция для загрузки ТабличнойЧасти из XML-строки
&НаСервере
Функция ТЧИзXMLСтроки(XMLСтрока)
// Проверяем, что строка не пустая
Если ПустаяСтрока(XMLСтрока) Тогда
Возврат Неопределено;
КонецЕсли;
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLСтрока); // Устанавливаем XML-строку для чтения
Сериализатор = Новый СериализаторXDTO;
// Десериализуем XML обратно в ТаблицуЗначений
Возврат Сериализатор.ПрочитатьXML(ЧтениеXML);
КонецФункции
// Использование этих функций в обработке
&НаСервере
Процедура СохранитьТЧВРеквизитКакXML()
// Объект.XMLДанныеТЧ - это реквизит формы, например, типа "Строка неограниченной длины"
Объект.XMLДанныеТЧ = ТЧВXMLСтроку(Объект.ТЧ);
КонецПроцедуры
&НаСервере
Процедура ЗагрузитьТЧИзРеквизитаКакXML()
ТЗ = ТЧИзXMLСтроки(Объект.XMLДанныеТЧ);
Если ТЗ <> Неопределено И ТипЗнч(ТЗ) = Тип("ТаблицаЗначений") Тогда
Объект.ТЧ.Загрузить(ТЗ);
КонецЕсли;
КонецПроцедуры
Этот подход более надежен для обмена и сохранения в произвольных хранилищах, так как XML-формат является открытым и стандартизированным. Однако следует помнить, что если в таблице значений содержатся ссылки на объекты базы данных (например, на элементы справочников, документы), при переносе между разными базами эти ссылки могут стать "битыми", если соответствующие объекты не существуют в целевой базе или имеют другие уникальные идентификаторы (УИДы). В таком случае потребуется дополнительная логика сопоставления ссылок.
Механизм временного хранилища (функции ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища()) предназначен для безопасной и эффективной передачи значений мутабельного типа (таких как ТаблицаЗначений) между клиентом и сервером в рамках одного сеанса работы пользователя. Этот механизм не предназначен для постоянного хранения данных между сеансами (так как данные во временном хранилище очищаются после завершения сеанса), но он может быть очень полезен для оптимизации передачи больших объемов данных формы с клиента на сервер и обратно без лишней сериализации/десериализации на клиенте.
Если данные необходимо хранить вне базы данных 1С, например, для резервного копирования, обмена через файловую систему или ручного переноса, мы можем использовать методы ЗначениеВФайл() и ЗначениеИзФайла(). Эти функции работают аналогично ЗначениеВСтрокуВнутр(), но сохраняют/загружают данные напрямую из файла. Однако, как и в случае со ЗначениеВСтрокуВнутр(), этот метод также имеет свои ограничения, связанные с внутренним форматом, и не является рекомендованным для постоянного хранения настроек внутри обработки или для обмена между базами без дополнительных преобразований.
Мы выяснили, что наиболее надежным и рекомендуемым способом сохранения значений табличной части внешней обработки в 1С:Предприятии 8.3 является использование стандартных обработчиков формы ПриЗагрузкеДанныхИзНастроекНаСервере и ПриСохраненииДанныхВНастройкахНаСервере. Этот подход обеспечивает стабильность, поддерживается платформой и позволяет избежать многих проблем, связанных с низкоуровневой сериализацией.
Хотя функции ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр() могут быть использованы для аналогичных целей, мы настоятельно не рекомендуем их из-за значительных рисков, связанных с системным форматом, не гарантированной стабильностью и частыми ошибками формата потока. Эти ошибки могут быть вызваны повреждением кэша, изменениями в метаданных или проблемами с самими данными. В случае необходимости более сложного обмена или сохранения данных, мы можем рассмотреть варианты с использованием хранилищ настроек или сериализации в XML, которые предоставляют большую гибкость и надежность.
Всегда отдавайте предпочтение стандартным механизмам платформы, так как они разрабатываются с учетом будущих обновлений, обеспечивают наилучшую совместимость, производительность и минимизируют риски возникновения непредвиденных ошибок.