Построить свое хранилище пресетов: три обычные формы и сохранение 3×250=750

Учебные задачи

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

Где и как запускать

Полный непривилегированный общий модуль, полный менеджер своего metadata хранилища,3ordinary forms иordinary launcher; отдельная собственная файловаяИБ8.5.1.1343, авторство/statictests безruntime.

Подготовка

  1. Только новая отдельная файловая ИБ 8.5.1.1343 с пустой конфигурацией УчебныеСвоиПресеты85. Режим совместимости НеИспользовать; основной режим запуска ОбычноеПриложение. Вход только толстым клиентом в обычном приложении, не управляемом. Рабочие КА2/УНФ/УТ/БП и стандартные хранилища здесь не используются.

  2. Создайте собственного единственного пользователя guide-own-presets с новой учебной ролью и полными правами только этого стенда: свой регистр, хранилище, его формы/общий модуль/стартовая форма. Не добавляйте роль/пользователя в рабочую базу. Физическую изоляцию, одного пользователя/сеанса обеспечивает человек, код не доказывает отсутствие конкуренции.

  3. Нет каталогов/документов/планов/бизнес-процессов, разделителей/общих реквизитов, расширений базы/сеанса, подписок, регламентных заданий, внешних сервисов. История данных и обмен не используются.

  4. Регистры сведений → УчебныеДанныеПресетов85: Независимый, Непериодический, история НеИспользовать. Измерения КлючОбъекта85: Строка 36, КлючНастроек85: Строка 32, Пользователь85: Строка 80, индексировать. Ресурсы Данные85: только ХранилищеЗначения; Подпись85: Строка 128. Реквизитов нет, модуль набора записей пустой.

  5. Общие → Хранилища настроек → УчебныеПресеты85. В его модуль менеджера вставьте целиком вспомогательный блок HANDLERS. Предопределенные процедуры совпадают с именами четырех native событий; самостоятельно вызывать обработчики нельзя. Это собственная реализация хранения, не копия BSP и не доступ к стандартному хранилищу.

  6. Общие модули → УчебныеПресетыМаршрут85: Сервер=Да, Клиент(обычное приложение)=Да; Клиент(управляемое приложение), Глобальный, Внешнее соединение, Вызов сервера и Привилегированный=Нет; повторное использование возвращаемых значений НеИспользовать. Вставьте целиком основной код. Три метода получения обычных форм внутри условия ТолстыйКлиентОбычноеПриложение и не компилируются как серверные вызовы.

  7. В УчебныеПресеты85 → Формы создайте ровно три ОБЫЧНЫЕ формы: Пульт, Сохранение, Загрузка. ОсновнаяФормаСохранения=Сохранение, ОсновнаяФормаЗагрузки=Загрузка. Пульт получается по явному имени через ПолучитьФорму; для него не выдумывайте отсутствующее свойство ОсновнаяФорма у metadata хранилища.

  8. Во всех трех формах создайте реквизиты UUID85: Строка36, Пользователь85: Строка80, Результат85: Строка2048. UUID/пользователь только для чтения. В Пульт добавьте Ошибка85: Булево(Ложь), ПодписьИзменена85: Булево(Ложь), Подтверждение85: Строка100; в Сохранение добавьте Подтверждение85: Строка100; в Загрузка добавьте ОжидаемаяПодпись85: Строка128 только для чтения.

  9. В модуль формы Пульт вставьте целиком PANEL; добавьте обычные кнопки ОткрытьСохранение85, ОткрытьЗагрузку85, СменитьПодпись85 и назначьте одноименные действия с одним аргументомКнопка. Выведите подтверждение/результат. События открытия/закрытия пустые, автоматического вызова Сохранить нет.

  10. В модуль Сохранение вставьте SAVE_FORM, кнопки Сохранить85 и Отмена85 с одноименными действиями. В модуль Загрузка вставьте LOAD_FORM, кнопки Прочитать85 и Отмена85. Не назначайте никакие обработчики ПриОткрытии/ПриЗакрытии, стандартные действия кнопкам не задавайте. Закрытие крестиком/Отмена не вызывает сохранение учебного пресета; возможное сохранение платформой положения окон/интерфейса здесь не исследуется и не объявляется отсутствующим.

  11. Общие формы → УчебныйСтартПресета85: одна обычная общая форма с кнопкой ОткрытьПульт85; модуль полностью LAUNCHER. Откройте ее через ВсеФункции/ФункцииДляТехническогоСпециалиста и нажмите кнопку. Кнопка получает реальный глобальный менеджер, считает его собственные элементы, создает UUID, проверяет0строк и получает собственный Пульт.

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

  13. Проверьте отмену: Пульт → ОткрытьСохранение → Отмена. Запись еще не выполнялась; затем снова откройте Сохранение. Введите СОХРАНИТЬ:вашUUID в Подтверждение85 и нажмите Сохранить85 ровно один раз. Пакет в коде фиксирован: BOOK,3,250,750. Подтверждение очищается до вызова, fresh whitelist и существование ключа проверяются внутри собственного native обработчика.

  14. Закройте форму Сохранение. Пульт → ОткрытьЗагрузку → Прочитать85: увидите BOOK/3/250/750 и Книга3×250=750. Отмена/закрытие Загрузка тоже не сохраняют данные. Независимые Load/GetDescription/raw-readback уже проверяют один и тот же собственный ключ.

  15. Закройте Загрузка. В пульте введите ПОДПИСЬ:вашUUID, нажмите СменитьПодпись85 ровно один раз. Свежие исходные ключ/подпись/пакет должны совпасть. Меняется только Подпись85, данные ХранилищеЗначения не переназначаются. Снова откройте Загрузка и Прочитать85: подпись Проверено:750, пакет по-прежнему3×250=750.

  16. Load отсутствующего ключа возвращает Неопределено именно потому, что так написан наш ОбработкаЗагрузки. Не выдавайте этот отрицательный контракт за правило всех пользовательских хранилищ. GetDescription отсутствующего собственного ключа в нашей реализации вызывает исключение; УстановитьОписание не генерирует новый ключ, UUID/book-cart/currentuser всегда заданы явно.

  17. Предопределенные обработчики пользовательского хранилища исполняются с автоматическим отключением безопасного режима по native справке. Мы этот режим не переключаем; поэтому допустим только свой новый изолированный стенд, строго заданные ключи/данные и полный код четырех обработчиков. Не применяйте их к чужому хранилищу.

  18. Raw-данные и payload читаются из регистра заново. Общий модуль не использует повторно используемые значения; GetDescription/Load не объявляются универсальным обходом любого кэширования платформы. Снимки не являются атомарной защитой от второго сеанса, изоляция обязательна.

  19. Любое исключение ставит Ошибка85 в реальном пульте; следующие команды/дочерние формы отказываются работать. Сохранение/смена подписи могли завершиться частично. Нет автоматического retry/rollback/удаления: вручную исследуйте только этот UUID/текущего пользователя в одноразовойИБ. Не закрывайте пульт для слепого повтора маршрута.

  20. Пресет и новая подпись намеренно остаются в собственнойИБ для просмотра. Удаление всей базы/настроек/пользователей/истории и резервные копии/архивы не входят в этот урок. Здесь приведено авторство и статические проверки; реальная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();
КонецПроцедуры

Разбор по шагам

  1. Глобальное ХранилищаНастроек дает только пользовательские хранилища конфигурации. Его реальная коллекция/тип и ровно один собственный менеджер проверяются; стандартное хранилище не подменяется.

  2. Пульт, Сохранение и Загрузка — реально подготовленные обычные формы толстого клиента. Их получают соответствующими ПолучитьФорму/ПолучитьФормуСохранения/ПолучитьФормуЗагрузки с точными именами, владельцем и своим ключом; Неопределено обрабатывается до доступа к реквизитам.

  3. Перед началом полный регистр пуст. Новый UUID и настоящий текущий пользователь становятся тремя ключевыми полями UUID/book-cart/currentuser; посторонние записи вызывают отказ, а не исключаются фильтром.

  4. Отмена/крестик не сохраняют собственный учебный пресет; возможное сохранение платформой положения окна здесь не исследуется. Только явная кнопка с СОХРАНИТЬ:UUID вызывает native Сохранить с пятью настоящими аргументами и свой ОбработкаСохранения; это не прямой вызов события.

  5. Свой обработчик проверяет пакет и0строк, читает новый менеджер записи и сохраняет ХранилищеЗначения/подпись. Fresh Load возвращает пакет через ОбработкаЗагрузки, а GetDescription — настоящее описание через свой одноименный обработчик.

  6. ОбработкаЗагрузки знает отрицательный контракт именно этого хранилища: нет записи — возвращается Неопределено. Положительный Load и raw-чтение независимо подтверждают3/250/750, подпись и ключ.

  7. Отдельная команда ПОДПИСЬ:UUID сначала читает начальную подпись и пакет. УстановитьОписание вызывает собственный четвертый обработчик, который меняет только существующую Подпись85; данные пакета не переназначаются.

  8. Повторный Load/GetDescription/raw-readback дают Проверено:750 и прежнийBOOK/3/250/750. Ошибка блокирует реального владельца-пульта; автоматических откатов, удалений и повторов нет.

Как проверить результат

  1. Перед явным сохранением0строк; закрытие/Отмена Сохранение оставляют0.

  2. После сохранения ровно одна собственная строка(UUID,book-cart,guide-own-presets), подпись Книга3×250=750 и проверенный пакет750.

  3. В форме Загрузка: BOOK,3,250,750; GetDescription/переданное Load описание/raw-подпись совпадают.

  4. После смены подписи ровно та же одна строка, Проверено:750, данные3×250 неизменны.

  5. Чужой ключ/пользователь, другой пакет, непустой начальный регистр, неизвестная подпись или повтор операции приводят к отказу.

Ожидаемый результат

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

На что обратить внимание

  1. Контекст только thick ordinary, не управляемые формы. Все триGet* имеют3параметра и native результатФорма/Неопределено; наличие имени в metadata само по себе не доказывает runtime-открытие.

  2. Сохранение/Load-семантика задаются модулем менеджера. Стандартное хранилище/BSP и их параметры нельзя переносить без проверки.

  3. У обработчиков отключается безопасный режим платформой; безопасность урока основана на отдельной собственнойИБ, полном коде и строгих guards, не на обещании sandbox.

  4. Конкурентные изменения/частичный результат не устраняются моментальным readback. На ошибке остановитесь и исследуйте свой ключ, не повторяйте запись вслепую.

  5. ПолучитьМакет с противоречивой0-параметрной сигнатурой не используется и не покрывается; хранение не создает резервные копии.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.