Настоящий пользовательский менеджер конфигурации вызывает четыре собственных обработчика. Пакет реально сохраняется в своем регистре, загружается и остается прежним после смены подписи.
Где и как запускать
Полный непривилегированный общий модуль, полный менеджер своего metadata хранилища,3ordinary forms иordinary launcher; отдельная собственная файловаяИБ8.5.1.1343, авторство/statictests безruntime.
Подготовка
Только новая отдельная файловая ИБ 8.5.1.1343 с пустой конфигурацией УчебныеСвоиПресеты85. Режим совместимости НеИспользовать; основной режим запуска ОбычноеПриложение. Вход только толстым клиентом в обычном приложении, не управляемом. Рабочие КА2/УНФ/УТ/БП и стандартные хранилища здесь не используются.
Создайте собственного единственного пользователя guide-own-presets с новой учебной ролью и полными правами только этого стенда: свой регистр, хранилище, его формы/общий модуль/стартовая форма. Не добавляйте роль/пользователя в рабочую базу. Физическую изоляцию, одного пользователя/сеанса обеспечивает человек, код не доказывает отсутствие конкуренции.
Нет каталогов/документов/планов/бизнес-процессов, разделителей/общих реквизитов, расширений базы/сеанса, подписок, регламентных заданий, внешних сервисов. История данных и обмен не используются.
Регистры сведений → УчебныеДанныеПресетов85: Независимый, Непериодический, история НеИспользовать. Измерения КлючОбъекта85: Строка 36, КлючНастроек85: Строка 32, Пользователь85: Строка 80, индексировать. Ресурсы Данные85: только ХранилищеЗначения; Подпись85: Строка 128. Реквизитов нет, модуль набора записей пустой.
Общие → Хранилища настроек → УчебныеПресеты85. В его модуль менеджера вставьте целиком вспомогательный блок HANDLERS. Предопределенные процедуры совпадают с именами четырех native событий; самостоятельно вызывать обработчики нельзя. Это собственная реализация хранения, не копия BSP и не доступ к стандартному хранилищу.
Общие модули → УчебныеПресетыМаршрут85: Сервер=Да, Клиент(обычное приложение)=Да; Клиент(управляемое приложение), Глобальный, Внешнее соединение, Вызов сервера и Привилегированный=Нет; повторное использование возвращаемых значений НеИспользовать. Вставьте целиком основной код. Три метода получения обычных форм внутри условия ТолстыйКлиентОбычноеПриложение и не компилируются как серверные вызовы.
В УчебныеПресеты85 → Формы создайте ровно три ОБЫЧНЫЕ формы: Пульт, Сохранение, Загрузка. ОсновнаяФормаСохранения=Сохранение, ОсновнаяФормаЗагрузки=Загрузка. Пульт получается по явному имени через ПолучитьФорму; для него не выдумывайте отсутствующее свойство ОсновнаяФорма у metadata хранилища.
Во всех трех формах создайте реквизиты UUID85: Строка36, Пользователь85: Строка80, Результат85: Строка2048. UUID/пользователь только для чтения. В Пульт добавьте Ошибка85: Булево(Ложь), ПодписьИзменена85: Булево(Ложь), Подтверждение85: Строка100; в Сохранение добавьте Подтверждение85: Строка100; в Загрузка добавьте ОжидаемаяПодпись85: Строка128 только для чтения.
В модуль формы Пульт вставьте целиком PANEL; добавьте обычные кнопки ОткрытьСохранение85, ОткрытьЗагрузку85, СменитьПодпись85 и назначьте одноименные действия с одним аргументомКнопка. Выведите подтверждение/результат. События открытия/закрытия пустые, автоматического вызова Сохранить нет.
В модуль Сохранение вставьте SAVE_FORM, кнопки Сохранить85 и Отмена85 с одноименными действиями. В модуль Загрузка вставьте LOAD_FORM, кнопки Прочитать85 и Отмена85. Не назначайте никакие обработчики ПриОткрытии/ПриЗакрытии, стандартные действия кнопкам не задавайте. Закрытие крестиком/Отмена не вызывает сохранение учебного пресета; возможное сохранение платформой положения окон/интерфейса здесь не исследуется и не объявляется отсутствующим.
Общие формы → УчебныйСтартПресета85: одна обычная общая форма с кнопкой ОткрытьПульт85; модуль полностью LAUNCHER. Откройте ее через ВсеФункции/ФункцииДляТехническогоСпециалиста и нажмите кнопку. Кнопка получает реальный глобальный менеджер, считает его собственные элементы, создает UUID, проверяет0строк и получает собственный Пульт.
Обновите конфигурацию базы данных. Изначально собственный регистр пуст. Не вводите UUID самостоятельно и не меняйте его между этапами: все три формы получают один настоящий UUID пульта и текущего пользователя, дочерние формы имеют реального пульта-владельца.
Проверьте отмену: Пульт → ОткрытьСохранение → Отмена. Запись еще не выполнялась; затем снова откройте Сохранение. Введите СОХРАНИТЬ:вашUUID в Подтверждение85 и нажмите Сохранить85 ровно один раз. Пакет в коде фиксирован: BOOK,3,250,750. Подтверждение очищается до вызова, fresh whitelist и существование ключа проверяются внутри собственного native обработчика.
Закройте форму Сохранение. Пульт → ОткрытьЗагрузку → Прочитать85: увидите BOOK/3/250/750 и Книга3×250=750. Отмена/закрытие Загрузка тоже не сохраняют данные. Независимые Load/GetDescription/raw-readback уже проверяют один и тот же собственный ключ.
Закройте Загрузка. В пульте введите ПОДПИСЬ:вашUUID, нажмите СменитьПодпись85 ровно один раз. Свежие исходные ключ/подпись/пакет должны совпасть. Меняется только Подпись85, данные ХранилищеЗначения не переназначаются. Снова откройте Загрузка и Прочитать85: подпись Проверено:750, пакет по-прежнему3×250=750.
Load отсутствующего ключа возвращает Неопределено именно потому, что так написан наш ОбработкаЗагрузки. Не выдавайте этот отрицательный контракт за правило всех пользовательских хранилищ. GetDescription отсутствующего собственного ключа в нашей реализации вызывает исключение; УстановитьОписание не генерирует новый ключ, UUID/book-cart/currentuser всегда заданы явно.
Предопределенные обработчики пользовательского хранилища исполняются с автоматическим отключением безопасного режима по native справке. Мы этот режим не переключаем; поэтому допустим только свой новый изолированный стенд, строго заданные ключи/данные и полный код четырех обработчиков. Не применяйте их к чужому хранилищу.
Raw-данные и payload читаются из регистра заново. Общий модуль не использует повторно используемые значения; GetDescription/Load не объявляются универсальным обходом любого кэширования платформы. Снимки не являются атомарной защитой от второго сеанса, изоляция обязательна.
Любое исключение ставит Ошибка85 в реальном пульте; следующие команды/дочерние формы отказываются работать. Сохранение/смена подписи могли завершиться частично. Нет автоматического retry/rollback/удаления: вручную исследуйте только этот UUID/текущего пользователя в одноразовойИБ. Не закрывайте пульт для слепого повтора маршрута.
Пресет и новая подпись намеренно остаются в собственнойИБ для просмотра. Удаление всей базы/настроек/пользователей/истории и резервные копии/архивы не входят в этот урок. Здесь приведено авторство и статические проверки; реальная1С/БД/формы не запускались.
Процедура ПроверитьСвойПресет85(UUID, Пользователь) Экспорт
ПроверитьСвойПустойСтенд();
Если ИмяПользователя() <> "guide-own-presets" Или Пользователь <> ИмяПользователя() Тогда
ВызватьИсключение "Только текущий собственный пользователь guide-own-presets";
КонецЕсли;
Если ТипЗнч(UUID) <> Тип("Строка") Или СтрДлина(UUID) <> 36
Или НРег(Строка(Новый УникальныйИдентификатор(UUID))) <> НРег(UUID) Тогда
ВызватьИсключение "Нужен действительный собственный UUID";
КонецЕсли;
Если Метаданные.ОсновнойРежимЗапуска <> РежимЗапускаКлиентскогоПриложения.ОбычноеПриложение
Или Метаданные.ХранилищаНастроек.Количество() <> 1
Или Метаданные.ХранилищаНастроек.Найти("УчебныеПресеты85") = Неопределено
Или Метаданные.ОбщиеМодули.Количество() <> 1 Или Метаданные.ОбщиеМодули.Найти("УчебныеПресетыМаршрут85") = Неопределено
Или Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("УчебныйСтартПресета85") = Неопределено Тогда
ВызватьИсключение "Только своя новая конфигурация обычного приложения с одним хранилищем/модулем/стартовой формой";
КонецЕсли;
Модуль = Метаданные.ОбщиеМодули.УчебныеПресетыМаршрут85;
Если Не Модуль.Сервер Или Модуль.Привилегированный
Или Модуль.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
ВызватьИсключение "Свой модуль не должен быть привилегированным или кэшировать данные";
КонецЕсли;
Регистр = Метаданные.РегистрыСведений.Найти("УчебныеДанныеПресетов85");
Если Регистр = Неопределено Или Регистр.РежимЗаписи <> Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый
Или Регистр.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический
Или Регистр.ИсторияДанных <> Метаданные.СвойстваОбъектов.ИспользованиеИсторииДанных.НеИспользовать
Или Регистр.Измерения.Количество() <> 3 Или Регистр.Ресурсы.Количество() <> 2 Или Регистр.Реквизиты.Количество() <> 0 Тогда
ВызватьИсключение "Нужен точный собственный независимый регистр без истории";
КонецЕсли;
Имена = СтрРазделить("КлючОбъекта85,КлючНастроек85,Пользователь85", ",");
Длины = Новый Массив;
Длины.Добавить(36);
Длины.Добавить(32);
Длины.Добавить(80);
Для Индекс = 0 По 2 Цикл
Измерение = Регистр.Измерения.Найти(Имена[Индекс]);
Если Измерение = Неопределено Или Измерение.Тип.Типы().Количество() <> 1
Или Не Измерение.Тип.СодержитТип(Тип("Строка")) Или Измерение.Тип.КвалификаторыСтроки.Длина <> Длины[Индекс] Тогда
ВызватьИсключение "Ключи только именованные строки36/32/80";
КонецЕсли;
КонецЦикла;
Данные = Регистр.Ресурсы.Найти("Данные85");
Подпись = Регистр.Ресурсы.Найти("Подпись85");
Если Данные = Неопределено Или Подпись = Неопределено Или Данные.Тип.Типы().Количество() <> 1
Или Не Данные.Тип.СодержитТип(Тип("ХранилищеЗначения")) Или Подпись.Тип.Типы().Количество() <> 1
Или Не Подпись.Тип.СодержитТип(Тип("Строка")) Или Подпись.Тип.КвалификаторыСтроки.Длина <> 128 Тогда
ВызватьИсключение "Только ресурсХранилищеЗначения и строковая подпись128";
КонецЕсли;
Формы = Метаданные.ХранилищаНастроек.УчебныеПресеты85.Формы;
Если Формы.Количество() <> 3 Тогда
ВызватьИсключение "Подготовьте ровно три собственные обычные формы хранилища";
КонецЕсли;
Для Каждого Форма Из Формы Цикл
Если СтрРазделить("Пульт,Сохранение,Загрузка", ",").Найти(Форма.Имя) = Неопределено Тогда
ВызватьИсключение "Недопустимая форма в своем хранилище";
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция СвоеХранилище85() Экспорт
Хранилища = ХранилищаНастроек;
Если ТипЗнч(Хранилища) <> Тип("ХранилищаНастроекМенеджер") Тогда
ВызватьИсключение "Нужен native менеджер собственных хранилищ, не стандартное хранилище";
КонецЕсли;
Количество = 0;
Для Каждого Хранилище Из Хранилища Цикл
Количество = Количество + 1;
Если ТипЗнч(Хранилище) <> Тип("ХранилищеНастроекМенеджер.УчебныеПресеты85") Тогда
ВызватьИсключение "В собственном пространстве обнаружено другое хранилище";
КонецЕсли;
КонецЦикла;
Если Количество <> 1 Тогда
ВызватьИсключение "Разрешено ровно одно собственное хранилище";
КонецЕсли;
Возврат Хранилища.УчебныеПресеты85;
КонецФункции
Функция СвойРегистр85(UUID, Пользователь, Количество) Экспорт
ПроверитьСвойПресет85(UUID, Пользователь);
Запрос = Новый Запрос("ВЫБРАТЬ Д.* ИЗ РегистрСведений.УчебныеДанныеПресетов85 КАК Д");
Данные = Запрос.Выполнить().Выгрузить();
Если Данные.Количество() <> Количество Тогда
ВызватьИсключение "Во всем собственном регистре другое число строк; неизвестные данные не изменять";
КонецЕсли;
Для Каждого СтрокаДанных Из Данные Цикл
Если СтрокаДанных.КлючОбъекта85 <> UUID Или СтрокаДанных.КлючНастроек85 <> "book-cart"
Или СтрокаДанных.Пользователь85 <> Пользователь Тогда
ВызватьИсключение "Строка не входит в точный whitelist своегоUUID/пресета/текущего пользователя";
КонецЕсли;
ПроверитьПакет85(СтрокаДанных.Данные85.Получить());
КонецЦикла;
Возврат Данные;
КонецФункции
Процедура ПроверитьПакет85(Пакет) Экспорт
Если ТипЗнч(Пакет) <> Тип("Структура") Или Пакет.Количество() <> 4
Или Не Пакет.Свойство("Код") Или Не Пакет.Свойство("Количество")
Или Не Пакет.Свойство("Цена") Или Не Пакет.Свойство("Сумма") Тогда
ВызватьИсключение "Нужен собственный пакетКод/Количество/Цена/Сумма";
КонецЕсли;
Если Пакет.Код <> "BOOK" Или ТипЗнч(Пакет.Количество) <> Тип("Число") Или Пакет.Количество <> 3
Или ТипЗнч(Пакет.Цена) <> Тип("Число") Или Пакет.Цена <> 250
Или ТипЗнч(Пакет.Сумма) <> Тип("Число") Или Пакет.Сумма <> 3 * 250 Тогда
ВызватьИсключение "Собственный пакет должен численно соответствоватьBOOK/3×250=750";
КонецЕсли;
КонецПроцедуры
Функция ОписаниеСвоегоПресета85(UUID, Пользователь, Представление) Экспорт
Описание = Новый ОписаниеНастроек;
Описание.КлючОбъекта = UUID;
Описание.КлючНастроек = "book-cart";
Описание.Пользователь = Пользователь;
Описание.Представление = Представление;
Возврат Описание;
КонецФункции
Процедура СохранитьСвойПресет85(UUID, Пользователь, Подтверждение) Экспорт
Если Подтверждение <> "СОХРАНИТЬ:" + UUID Тогда
ВызватьИсключение "Введите отдельное точное подтверждениеСОХРАНИТЬ:UUID";
КонецЕсли;
СвойРегистр85(UUID, Пользователь, 0);
Пакет = Новый Структура("Код,Количество,Цена,Сумма", "BOOK", 3, 250, 3 * 250);
Описание = ОписаниеСвоегоПресета85(UUID, Пользователь, "Книга3×250=750");
Хранилище = СвоеХранилище85();
Хранилище.Сохранить(UUID, "book-cart", Пакет, Описание, Пользователь);
Данные = СвойРегистр85(UUID, Пользователь, 1);
Если Данные.Получить(0).Подпись85 <> "Книга3×250=750" Тогда
ВызватьИсключение "Сохраненная подпись не подтверждена в реальном регистре";
КонецЕсли;
ПрочитатьСвойПресет85(UUID, Пользователь, "Книга3×250=750");
КонецПроцедуры
Функция ПрочитатьСвойПресет85(UUID, Пользователь, ОжидаемаяПодпись) Экспорт
Данные = СвойРегистр85(UUID, Пользователь, 1);
Хранилище = СвоеХранилище85();
ЗагруженноеОписание = Новый ОписаниеНастроек;
Пакет = Хранилище.Загрузить(UUID, "book-cart", ЗагруженноеОписание, Пользователь);
ПроверитьПакет85(Пакет);
Описание = Хранилище.ПолучитьОписание(UUID, "book-cart", Пользователь);
Если Данные.Получить(0).Подпись85 <> ОжидаемаяПодпись
Или ЗагруженноеОписание.Представление <> ОжидаемаяПодпись Или Описание.Представление <> ОжидаемаяПодпись
Или Описание.КлючОбъекта <> UUID Или Описание.КлючНастроек <> "book-cart" Или Описание.Пользователь <> Пользователь Тогда
ВызватьИсключение "Raw/Load/GetDescription должны подтверждать одну собственную подпись и точный ключ";
КонецЕсли;
Возврат Пакет;
КонецФункции
Процедура ИзменитьСвоюПодпись85(UUID, Пользователь, Подтверждение) Экспорт
Если Подтверждение <> "ПОДПИСЬ:" + UUID Тогда
ВызватьИсключение "Введите отдельное подтверждениеПОДПИСЬ:UUID";
КонецЕсли;
ПрочитатьСвойПресет85(UUID, Пользователь, "Книга3×250=750");
Хранилище = СвоеХранилище85();
Описание = ОписаниеСвоегоПресета85(UUID, Пользователь, "Проверено:750");
Хранилище.УстановитьОписание(UUID, "book-cart", Описание, Пользователь);
ПрочитатьСвойПресет85(UUID, Пользователь, "Проверено:750");
КонецПроцедуры
#Если ТолстыйКлиентОбычноеПриложение Тогда
Процедура ОткрытьСвойПульт85() Экспорт
UUID = Строка(Новый УникальныйИдентификатор);
Пользователь = ИмяПользователя();
СвойРегистр85(UUID, Пользователь, 0);
Хранилище = СвоеХранилище85();
Если Хранилище.Загрузить(UUID, "book-cart", , Пользователь) <> Неопределено Тогда
ВызватьИсключение "Собственная реализацияLoad должна подтвердить отсутствие нового ключа";
КонецЕсли;
Пульт = Хранилище.ПолучитьФорму("Пульт", Неопределено, UUID);
Если Пульт = Неопределено Или ТипЗнч(Пульт) <> Тип("Форма") Тогда
ВызватьИсключение "Нужна реально подготовленная обычная формаПульт";
КонецЕсли;
Пульт.UUID85 = UUID;
Пульт.Пользователь85 = Пользователь;
Пульт.Ошибка85 = Ложь;
Пульт.ПодписьИзменена85 = Ложь;
Пульт.Открыть();
КонецПроцедуры
Процедура ОткрытьСвоюЗапись85(Пульт) Экспорт
Если Пульт.Ошибка85 Тогда
ВызватьИсключение "Результат прошлого этапа неизвестен; не открывать новую попытку записи";
КонецЕсли;
СвойРегистр85(Пульт.UUID85, Пульт.Пользователь85, 0);
Хранилище = СвоеХранилище85();
Форма = Хранилище.ПолучитьФормуСохранения("Сохранение", Пульт, Пульт.UUID85 + ":save");
Если Форма = Неопределено Или ТипЗнч(Форма) <> Тип("Форма") Или Форма.ВладелецФормы <> Пульт Тогда
ВызватьИсключение "Нужна своя обычная формаСохранение с точным пультом-владельцем";
КонецЕсли;
Форма.UUID85 = Пульт.UUID85;
Форма.Пользователь85 = Пульт.Пользователь85;
Форма.Подтверждение85 = "";
Форма.Открыть();
КонецПроцедуры
Процедура ОткрытьСвоеЧтение85(Пульт) Экспорт
Если Пульт.Ошибка85 Тогда
ВызватьИсключение "Остановитесь после ошибки; скрытого повторного сохранения нет";
КонецЕсли;
Подпись = ?(Пульт.ПодписьИзменена85, "Проверено:750", "Книга3×250=750");
ПрочитатьСвойПресет85(Пульт.UUID85, Пульт.Пользователь85, Подпись);
Хранилище = СвоеХранилище85();
Форма = Хранилище.ПолучитьФормуЗагрузки("Загрузка", Пульт, Пульт.UUID85 + ":load");
Если Форма = Неопределено Или ТипЗнч(Форма) <> Тип("Форма") Или Форма.ВладелецФормы <> Пульт Тогда
ВызватьИсключение "Нужна своя обычная формаЗагрузка с точным владельцем";
КонецЕсли;
Форма.UUID85 = Пульт.UUID85;
Форма.Пользователь85 = Пульт.Пользователь85;
Форма.ОжидаемаяПодпись85 = Подпись;
Форма.Открыть();
КонецПроцедуры
#КонецЕсли
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 1 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедурыМодуль менеджера УчебныеПресеты85: четыре предопределенных события
Своя CF УчебныеСвоиПресеты85 → Общие → ХранилищаНастроек → УчебныеПресеты85 → модуль менеджера целиком.
Модуль менеджера собственного хранилища. Native четыре события вызываются платформой, не вручную. Сервер и толстый обычный клиент согласно контрактам событий; подготовка и весь маршрут только своей отдельной ИБ.
Процедура ОбработкаСохранения(КлючОбъекта, КлючНастроек, Настройки, ОписаниеНастроек, Пользователь)
ПроверитьСвойКлюч85(КлючОбъекта, КлючНастроек, Пользователь);
УчебныеПресетыМаршрут85.СвойРегистр85(КлючОбъекта, Пользователь, 0);
УчебныеПресетыМаршрут85.ПроверитьПакет85(Настройки);
Если ОписаниеНастроек = Неопределено Или ОписаниеНастроек.Представление <> "Книга3×250=750" Тогда
ВызватьИсключение "Сохранение только собственной начальной подписи и реального описания";
КонецЕсли;
Запись = РегистрыСведений.УчебныеДанныеПресетов85.СоздатьМенеджерЗаписи();
Запись.КлючОбъекта85 = КлючОбъекта;
Запись.КлючНастроек85 = КлючНастроек;
Запись.Пользователь85 = Пользователь;
Запись.Прочитать();
Если Запись.Выбран() Тогда
ВызватьИсключение "Существующее не заменяется";
КонецЕсли;
Запись.КлючОбъекта85 = КлючОбъекта;
Запись.КлючНастроек85 = КлючНастроек;
Запись.Пользователь85 = Пользователь;
Запись.Данные85 = Новый ХранилищеЗначения(Настройки);
Запись.Подпись85 = ОписаниеНастроек.Представление;
Запись.Записать(Ложь);
КонецПроцедуры
Процедура ОбработкаЗагрузки(КлючОбъекта, КлючНастроек, Настройки, ОписаниеНастроек, Пользователь)
ПроверитьСвойКлюч85(КлючОбъекта, КлючНастроек, Пользователь);
Запись = ПрочитатьСвоюЗапись85(КлючОбъекта, КлючНастроек, Пользователь);
Если Не Запись.Выбран() Тогда
УчебныеПресетыМаршрут85.СвойРегистр85(КлючОбъекта, Пользователь, 0);
Настройки = Неопределено;
Возврат;
КонецЕсли;
УчебныеПресетыМаршрут85.СвойРегистр85(КлючОбъекта, Пользователь, 1);
Настройки = Запись.Данные85.Получить();
УчебныеПресетыМаршрут85.ПроверитьПакет85(Настройки);
Если ОписаниеНастроек <> Неопределено Тогда
ОписаниеНастроек = УчебныеПресетыМаршрут85.ОписаниеСвоегоПресета85(КлючОбъекта, Пользователь, Запись.Подпись85);
КонецЕсли;
КонецПроцедуры
Процедура ОбработкаПолученияОписания(КлючОбъекта, КлючНастроек, ОписаниеНастроек, Пользователь)
ПроверитьСвойКлюч85(КлючОбъекта, КлючНастроек, Пользователь);
УчебныеПресетыМаршрут85.СвойРегистр85(КлючОбъекта, Пользователь, 1);
Запись = ПрочитатьСвоюЗапись85(КлючОбъекта, КлючНастроек, Пользователь);
Если Не Запись.Выбран() Тогда
ВызватьИсключение "Описания отсутствующего своего пресета нет";
КонецЕсли;
ОписаниеНастроек = УчебныеПресетыМаршрут85.ОписаниеСвоегоПресета85(КлючОбъекта, Пользователь, Запись.Подпись85);
КонецПроцедуры
Процедура ОбработкаУстановкиОписания(КлючОбъекта, КлючНастроек, ОписаниеНастроек, Пользователь)
ПроверитьСвойКлюч85(КлючОбъекта, КлючНастроек, Пользователь);
Данные = УчебныеПресетыМаршрут85.СвойРегистр85(КлючОбъекта, Пользователь, 1);
Если Данные.Получить(0).Подпись85 <> "Книга3×250=750" Или ОписаниеНастроек.Представление <> "Проверено:750" Тогда
ВызватьИсключение "Изменение только своей существующей начальной подписи наПроверено:750";
КонецЕсли;
Запись = ПрочитатьСвоюЗапись85(КлючОбъекта, КлючНастроек, Пользователь);
Если Не Запись.Выбран() Или Запись.Подпись85 <> "Книга3×250=750" Тогда
ВызватьИсключение "Fresh менеджер не подтвердил прежнюю свою подпись";
КонецЕсли;
УчебныеПресетыМаршрут85.ПроверитьПакет85(Запись.Данные85.Получить());
Запись.Подпись85 = ОписаниеНастроек.Представление;
Запись.Записать();
КонецПроцедуры
Процедура ПроверитьСвойКлюч85(КлючОбъекта, КлючНастроек, Пользователь)
УчебныеПресетыМаршрут85.ПроверитьСвойПресет85(КлючОбъекта, Пользователь);
Если ТипЗнч(КлючНастроек) <> Тип("Строка") Или КлючНастроек <> "book-cart" Тогда
ВызватьИсключение "Не генерировать неизвестные ключи; разрешен толькоbook-cart собственногоUUID";
КонецЕсли;
КонецПроцедуры
Функция ПрочитатьСвоюЗапись85(КлючОбъекта, КлючНастроек, Пользователь)
Запись = РегистрыСведений.УчебныеДанныеПресетов85.СоздатьМенеджерЗаписи();
Запись.КлючОбъекта85 = КлючОбъекта;
Запись.КлючНастроек85 = КлючНастроек;
Запись.Пользователь85 = Пользователь;
Запись.Прочитать();
Возврат Запись;
КонецФункцииМодуль обычной формы Пульт
Своя CF → Общие → ХранилищаНастроек → УчебныеПресеты85 → Формы → Пульт → модуль формы целиком.
Толстый клиент, обычное приложение. Три одноимённых действия кнопок с параметром Кнопка; реквизиты и guards своего пульта заданы в подготовке.
Процедура ОткрытьСохранение85(Кнопка)
Если Ошибка85 Тогда
ВызватьИсключение "Предыдущая ошибка требует ручной проверки; не повторять";
КонецЕсли;
Попытка
УчебныеПресетыМаршрут85.ОткрытьСвоюЗапись85(ЭтаФорма);
Исключение
Ошибка85 = Истина;
Результат85 = "СТОП: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
Процедура ОткрытьЗагрузку85(Кнопка)
Если Ошибка85 Тогда
ВызватьИсключение "После ошибки сначала вручную исследуйте собственный стенд";
КонецЕсли;
Попытка
УчебныеПресетыМаршрут85.ОткрытьСвоеЧтение85(ЭтаФорма);
Исключение
Ошибка85 = Истина;
Результат85 = "СТОП: " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедуры
Процедура СменитьПодпись85(Кнопка)
Если Ошибка85 Или ПодписьИзменена85 Тогда
ВызватьИсключение "Не повторять изменение или неопределенную операцию";
КонецЕсли;
Согласие = Подтверждение85;
Подтверждение85 = "";
Попытка
УчебныеПресетыМаршрут85.ИзменитьСвоюПодпись85(UUID85, Пользователь85, Согласие);
ПодписьИзменена85 = Истина;
Результат85 = "ПодписьПроверено:750, пакетBOOK/3×250=750 подтвержден отдельно";
Исключение
Ошибка85 = Истина;
Результат85 = "СТОП: результат изменения может быть частичным. " + ОписаниеОшибки();
КонецПопытки;
КонецПроцедурыМодуль обычной формы Сохранение
Своя CF → Общие → ХранилищаНастроек → УчебныеПресеты85 → Формы → Сохранение → модуль формы целиком.
Толстый клиент, обычное приложение. Native владелец — собственный пульт. Действия Сохранить85/Отмена85(Кнопка), никаких автоматических событий открытия/закрытия.
Процедура Сохранить85(Кнопка)
Если ВладелецФормы = Неопределено Или ТипЗнч(ВладелецФормы) <> Тип("Форма") Тогда
ВызватьИсключение "Нужен собственный обычный пульт-владелец";
КонецЕсли;
Если ВладелецФормы.Ошибка85 Или ВладелецФормы.UUID85 <> UUID85
Или ВладелецФормы.Пользователь85 <> Пользователь85 Тогда
ВызватьИсключение "Пульт остановлен или не соответствует нашемуUUID/пользователю";
КонецЕсли;
Согласие = Подтверждение85;
Подтверждение85 = "";
Попытка
УчебныеПресетыМаршрут85.СохранитьСвойПресет85(UUID85, Пользователь85, Согласие);
ВладелецФормы.Результат85 = "Пресет750 сохранен и независимо загружен. Закройте эту форму; затемЗагрузка.";
Результат85 = ВладелецФормы.Результат85;
Исключение
ВладелецФормы.Ошибка85 = Истина;
Результат85 = "СТОП: запись могла завершиться частично. " + ОписаниеОшибки();
ВладелецФормы.Результат85 = Результат85;
КонецПопытки;
КонецПроцедуры
Процедура Отмена85(Кнопка)
ЭтаФорма.Закрыть();
КонецПроцедурыМодуль обычной формы Загрузка
Своя CF → Общие → ХранилищаНастроек → УчебныеПресеты85 → Формы → Загрузка → модуль формы целиком.
Толстый клиент, обычное приложение. Native владелец — собственный пульт. Действия Прочитать85/Отмена85(Кнопка), сохранение этим модулем не вызывается.
Процедура Прочитать85(Кнопка)
Если ВладелецФормы = Неопределено Или ТипЗнч(ВладелецФормы) <> Тип("Форма") Тогда
ВызватьИсключение "Нужен реальный собственный обычный пульт";
КонецЕсли;
Если ВладелецФормы.Ошибка85 Или ВладелецФормы.UUID85 <> UUID85
Или ВладелецФормы.Пользователь85 <> Пользователь85 Тогда
ВызватьИсключение "Владелец не соответствует собственному опыту или остановлен";
КонецЕсли;
Попытка
Пакет = УчебныеПресетыМаршрут85.ПрочитатьСвойПресет85(UUID85, Пользователь85, ОжидаемаяПодпись85);
Результат85 = СтрШаблон("Код=%1, количество=%2, цена=%3, сумма=%4; подпись=%5", Пакет.Код, Пакет.Количество, Пакет.Цена, Пакет.Сумма, ОжидаемаяПодпись85);
Исключение
ВладелецФормы.Ошибка85 = Истина;
Результат85 = "СТОП: " + ОписаниеОшибки();
ВладелецФормы.Результат85 = Результат85;
КонецПопытки;
КонецПроцедуры
Процедура Отмена85(Кнопка)
ЭтаФорма.Закрыть();
КонецПроцедурыМодуль общей обычной стартовой формы
Своя CF → Общие → ОбщиеФормы → УчебныйСтартПресета85 → модуль обычной формы целиком.
Толстый клиент, обычное приложение. Реальное действие кнопки ОткрытьПульт85(Кнопка), вызывающее общий модуль собственного стенда.
Процедура ОткрытьПульт85(Кнопка)
УчебныеПресетыМаршрут85.ОткрытьСвойПульт85();
КонецПроцедурыРазбор по шагам
Глобальное ХранилищаНастроек дает только пользовательские хранилища конфигурации. Его реальная коллекция/тип и ровно один собственный менеджер проверяются; стандартное хранилище не подменяется.
Пульт, Сохранение и Загрузка — реально подготовленные обычные формы толстого клиента. Их получают соответствующими ПолучитьФорму/ПолучитьФормуСохранения/ПолучитьФормуЗагрузки с точными именами, владельцем и своим ключом; Неопределено обрабатывается до доступа к реквизитам.
Перед началом полный регистр пуст. Новый UUID и настоящий текущий пользователь становятся тремя ключевыми полями UUID/book-cart/currentuser; посторонние записи вызывают отказ, а не исключаются фильтром.
Отмена/крестик не сохраняют собственный учебный пресет; возможное сохранение платформой положения окна здесь не исследуется. Только явная кнопка с СОХРАНИТЬ:UUID вызывает native Сохранить с пятью настоящими аргументами и свой ОбработкаСохранения; это не прямой вызов события.
Свой обработчик проверяет пакет и0строк, читает новый менеджер записи и сохраняет ХранилищеЗначения/подпись. Fresh Load возвращает пакет через ОбработкаЗагрузки, а GetDescription — настоящее описание через свой одноименный обработчик.
ОбработкаЗагрузки знает отрицательный контракт именно этого хранилища: нет записи — возвращается Неопределено. Положительный Load и raw-чтение независимо подтверждают3/250/750, подпись и ключ.
Отдельная команда ПОДПИСЬ:UUID сначала читает начальную подпись и пакет. УстановитьОписание вызывает собственный четвертый обработчик, который меняет только существующую Подпись85; данные пакета не переназначаются.
Повторный Load/GetDescription/raw-readback дают Проверено:750 и прежнийBOOK/3/250/750. Ошибка блокирует реального владельца-пульта; автоматических откатов, удалений и повторов нет.
Как проверить результат
Перед явным сохранением0строк; закрытие/Отмена Сохранение оставляют0.
После сохранения ровно одна собственная строка(UUID,book-cart,guide-own-presets), подпись Книга3×250=750 и проверенный пакет750.
В форме Загрузка: BOOK,3,250,750; GetDescription/переданное Load описание/raw-подпись совпадают.
После смены подписи ровно та же одна строка, Проверено:750, данные3×250 неизменны.
Чужой ключ/пользователь, другой пакет, непустой начальный регистр, неизвестная подпись или повтор операции приводят к отказу.
Ожидаемый результат
Ожидаемый при ручном запуске результат: один свой сохраненный пресет750 с новой подписью, понятные формы сохранения/загрузки и работающие собственные native обработчики.
На что обратить внимание
Контекст только thick ordinary, не управляемые формы. Все триGet* имеют3параметра и native результатФорма/Неопределено; наличие имени в metadata само по себе не доказывает runtime-открытие.
Сохранение/Load-семантика задаются модулем менеджера. Стандартное хранилище/BSP и их параметры нельзя переносить без проверки.
У обработчиков отключается безопасный режим платформой; безопасность урока основана на отдельной собственнойИБ, полном коде и строгих guards, не на обещании sandbox.
Конкурентные изменения/частичный результат не устраняются моментальным readback. На ошибке остановитесь и исследуйте свой ключ, не повторяйте запись вслепую.
ПолучитьМакет с противоречивой0-параметрной сигнатурой не используется и не покрывается; хранение не создает резервные копии.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.