Настоящие native descriptor и режим события используются нашим отдельным потребителем: сумма счета 1050, выключенный план 0, альтернативные цены 400. Реальное журналирование и права доступа не меняются.
Где и как запускать
Серверный общий модуль собственной новой File CF 8.5.1.1343; толстая управляемая форма получает только проверенные скаляры. Runtime не выполнялся.
Подготовка
Что изучаем. Сначала создаём в памяти два настоящих описания отказа в доступе и объект использования события. Затем наш отдельный предпросмотр меняет режим и выбранное описание: 1050 → 0 → 1050 → 400 → 1050. Так видны роль Булево, содержимое свойства ОписаниеИспользования и порядок альтернатив. Это подготовка собственного плана, а не включение журналирования и не проверка реального отказа в доступе.
Создайте вручную НОВУЮ пустую файловую учебную базу только C:\1c-labs\OwnAccessDeniedPolicy85_c5771276-93dc-42d9-8f52-23ba39a74485\IB. Имя конфигурации УчебныйПланОтказа85; ОсновнойРежимЗапуска = УправляемоеПриложение; РежимСовместимости = НеИспользовать. Платформа Windows 8.5.1.1343, толстый управляемый клиент. Регистрируйте именно File="этот полный путь";: другой каталог, суффикс или формат строки отвергается. UUID в имени папки — только маркер лаборатории, не UUID базы.
Документы → Добавить → УчебнаяНакладная85. Создайте ровно три реквизита: Количество85, Цена85, Сумма85; каждый Число, длина 15, точность 2, не составной тип. Не добавляйте табличные части, формы документа, движения, подписки и модуль документа. Не создавайте и не записывайте ни одного документа: нам нужны только реальные метаданные, их полное имя и описание числовых типов.
Другие документы, справочники, регистры и расширения не добавляйте. В своей новой CF создайте собственную роль ПланОтказа85Администратор с полными учебными правами, включая Администрирование для ПолучитьПользователей и АктивныеПользователи для ПолучитьСоединенияИнформационнойБазы. Создайте единственного реального пользователя ПланОтказа85Ученик и назначьте роль. Код не создаёт пользователей и не меняет права. Проверяются настоящий UUID пользователя и текущее единственное соединение; Конфигуратор перед ручным тестом закройте.
Общие → Общие модули → ПланОтказаПамять85. Сервер = Да; Глобальный, ВызовСервера, Привилегированный, КлиентУправляемоеПриложение, КлиентОбычноеПриложение, ВнешнееСоединение = Нет. ПовторноеИспользованиеВозвращаемыхЗначений = НеИспользовать. Вставьте весь основной модуль CODE. Он проверяет эти настройки и не кеширует повторные проверки. Только ВыполнитьПредпросмотр85 экспортируется, без аргументов; все остальные функции работают с объектами, созданными здесь.
Общие → Общие формы → ПредпросмотрОтказа85, управляемая форма. Добавьте восемь реквизитов Число 15.2: Количество85, Сумма85, СуммаЦен85, Включен85, Отключен85, СноваВключен85, ДругойСостав85, Восстановлен85. Добавьте Протокол85 — Строка неограниченной длины, Остановка85 и Завершено85 — Булево с начальным значением Ложь. Покажите восемь чисел и многострочный протокол только для просмотра; служебные Булево пользователю не выводите.
Создайте собственную команду формы Предпросмотр85. В свойстве Действие назначьте обработчик Предпросмотр85(Команда); кнопку на командной панели привяжите к этой команде. Вставьте полный поддерживающий модуль формы. Его &НаСервере процедура вызывает серверный общий модуль, а клиент получает только восемь скаляров. Массивы, ТаблицаЗначений и native descriptor через RPC не передаются.
Конфигурация → Открыть модуль управляемого приложения: вставьте полный поддерживающий модуль УправляемоеПриложение. Штатное ПриНачалеРаботыСистемы() без аргументов откроет свою общую форму только в толстом управляемом клиенте. Сами обработчики событий не вызывайте. Константы перечислимых свойств метаданных в коде квалифицированы через Метаданные.СвойстваОбъектов.
ПоляРегистрации у описания отказа — readonly свойство. Поэтому ему ничего не присваиваем. У нового descriptor читаем настоящий Массив, требуем пустое состояние, вызываем документированный Массив.Добавить на нём и затем заново читаем свойство через полный ПроверитьОписание85. Если изменения не видны, тип неожиданен или начальный массив непустой, урок останавливается. Справка не превращается в обещание удержания изменений; это проверяет будущий ручной протокол.
A получает вложенный массив [Сумма85, Цена85] и отдельное Количество85; B — [Цена85, Сумма85] и Количество85. Это два собственных native описания одного настоящего документа. В обеих проекциях доступны обе альтернативы: наш потребитель выбирает первую имеющуюся и вычисляет реальные native Итог по собственной копии. Поэтому A даёт сумму счёта 1050, B — сумму цен 400. Отдельный режим Использование = Ложь означает нулевой предпросмотр по явно заданному правилу именно нашего потребителя.
Обновите вручную только эту новую учебную CF и запустите единственного своего пользователя в толстом управляемом клиенте. Нажмите Предпросмотр85 один раз. После успеха и после ошибки повтор запрещён. Для нового ручного протокола закройте клиент и начните новый собственный сеанс; не сбрасывайте служебные флаги в отладчике. В случае ошибки изучите первый отказ: нет автоматического повторения, catch-восстановления или продолжения после частичного memory изменения.
Здесь написаны и статически проверяются только source и тесты. CF, ИБ, роль, пользователь, 1С и GUI не создавались и не запускались. Нет запросов, записи прикладных данных, смены использования событий, записи журнала, сети, печати и файлового сохранения результата. Все числа далее — ожидаемый будущий ручной протокол, а не выполненный runtime.
Функция ПроверитьСтенд85()
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnAccessDeniedPolicy85_c5771276-93dc-42d9-8f52-23ba39a74485\IB"";" Тогда
ВызватьИсключение "Нужен свой точно подготовленный файловый стенд 8.5.1.1343";
КонецЕсли;
Если Метаданные.Документы.Количество() <> 1 Или Метаданные.Документы.Найти("УчебнаяНакладная85") = Неопределено
Или Метаданные.ОбщиеМодули.Количество() <> 1 Или Метаданные.ОбщиеМодули.Найти("ПланОтказаПамять85") = Неопределено
Или Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("ПредпросмотрОтказа85") = Неопределено
Или Метаданные.Справочники.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0
Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0
Или Метаданные.РегистрыРасчета.Количество() <> 0 Тогда
ВызватьИсключение "Допускаются только свои описание документа, общий модуль и общая форма";
КонецЕсли;
МодульМД = Метаданные.ОбщиеМодули.Найти("ПланОтказаПамять85");
Если Не МодульМД.Сервер Или МодульМД.Глобальный Или МодульМД.ВызовСервера Или МодульМД.Привилегированный
Или МодульМД.КлиентУправляемоеПриложение Или МодульМД.КлиентОбычноеПриложение Или МодульМД.ВнешнееСоединение Тогда
ВызватьИсключение "Общий модуль должен быть только серверным, неглобальным и без кеширования результатов";
КонецЕсли;
Если МодульМД.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
ВызватьИсключение "Повторные чтения не должны возвращать кешированный результат";
КонецЕсли;
Если РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.БазаДанных).Количество() <> 0
Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансАктивные).Количество() <> 0
Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансОтключенные).Количество() <> 0 Тогда
ВызватьИсключение "В собственном стенде не должно быть расширений";
КонецЕсли;
Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Если Текущий = Неопределено Или Пользователи.Количество() <> 1 Тогда
ВызватьИсключение "Нужен единственный реальный собственный пользователь";
КонецЕсли;
Если Текущий.Имя <> "ПланОтказа85Ученик" Или Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Не совпала настоящая идентичность своего пользователя ИБ";
КонецЕсли;
Соединения = ПолучитьСоединенияИнформационнойБазы();
Если Соединения.Количество() <> 1 Тогда
ВызватьИсключение "Закройте Конфигуратор и другие сеансы своей лаборатории";
КонецЕсли;
Соединение = Соединения[0];
Если Соединение.НомерСоединения <> НомерСоединенияИнформационнойБазы()
Или Соединение.Пользователь.УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
ВызватьИсключение "Единственное соединение должно принадлежать текущему учебному пользователю";
КонецЕсли;
МетаДокумент = Метаданные.Документы.Найти("УчебнаяНакладная85");
Если МетаДокумент.ПолноеИмя() <> "Документ.УчебнаяНакладная85" Или МетаДокумент.Реквизиты.Количество() <> 3 Тогда
ВызватьИсключение "Проверьте точное имя и три реквизита собственного документа";
КонецЕсли;
Для Каждого ИмяПоля Из ИменаПолей85() Цикл
ПолеМД = МетаДокумент.Реквизиты.Найти(ИмяПоля);
Если ПолеМД = Неопределено Тогда
ВызватьИсключение "Отсутствует собственный числовой реквизит";
КонецЕсли;
Если ПолеМД.Тип.Типы().Количество() <> 1 Или Не ПолеМД.Тип.СодержитТип(Тип("Число")) Тогда
ВызватьИсключение "Собственный реквизит должен иметь единственный тип Число";
КонецЕсли;
КонецЦикла;
Возврат МетаДокумент;
КонецФункции
Функция ИменаПолей85()
Имена = Новый Массив;
Имена.Добавить("Количество85");
Имена.Добавить("Цена85");
Имена.Добавить("Сумма85");
Возврат Имена;
КонецФункции
Функция СоздатьДанные85(МетаДокумент)
Данные = Новый ТаблицаЗначений;
Для Каждого ИмяПоля Из ИменаПолей85() Цикл
ПолеМД = МетаДокумент.Реквизиты.Найти(ИмяПоля);
Данные.Колонки.Добавить(ИмяПоля, ПолеМД.Тип);
КонецЦикла;
Первая = Данные.Добавить();
Первая.Количество85 = 3;
Первая.Цена85 = 250;
Первая.Сумма85 = Первая.Количество85 * Первая.Цена85;
Вторая = Данные.Добавить();
Вторая.Количество85 = 2;
Вторая.Цена85 = 150;
Вторая.Сумма85 = Вторая.Количество85 * Вторая.Цена85;
Возврат Данные;
КонецФункции
Функция ПрочитатьДанные85(Данные)
Если ТипЗнч(Данные) <> Тип("ТаблицаЗначений") Или Данные.Количество() <> 2 Или Данные.Колонки.Количество() <> 3 Тогда
ВызватьИсключение "Исходник должен содержать две собственные строки и три колонки";
КонецЕсли;
Для Каждого ИмяПоля Из ИменаПолей85() Цикл
Колонка = Данные.Колонки.Найти(ИмяПоля);
Если Колонка = Неопределено Тогда
ВызватьИсключение "Пропала колонка собственной таблицы";
КонецЕсли;
Если Колонка.ТипЗначения.Типы().Количество() <> 1 Или Не Колонка.ТипЗначения.СодержитТип(Тип("Число")) Тогда
ВызватьИсключение "Тип каждой колонки должен оставаться единственным Число";
КонецЕсли;
КонецЦикла;
Первая = Данные.Получить(0);
Вторая = Данные.Получить(1);
Если ТипЗнч(Первая.Количество85) <> Тип("Число") Или ТипЗнч(Первая.Цена85) <> Тип("Число") Или ТипЗнч(Первая.Сумма85) <> Тип("Число")
Или ТипЗнч(Вторая.Количество85) <> Тип("Число") Или ТипЗнч(Вторая.Цена85) <> Тип("Число") Или ТипЗнч(Вторая.Сумма85) <> Тип("Число") Тогда
ВызватьИсключение "Все шесть ячеек должны содержать числа";
КонецЕсли;
Если Первая.Количество85 <> 3 Или Первая.Цена85 <> 250 Или Первая.Сумма85 <> 750
Или Вторая.Количество85 <> 2 Или Вторая.Цена85 <> 150 Или Вторая.Сумма85 <> 300
Или Первая.Сумма85 <> Первая.Количество85 * Первая.Цена85 Или Вторая.Сумма85 <> Вторая.Количество85 * Вторая.Цена85 Тогда
ВызватьИсключение "Изменилось хотя бы одно значение собственного исходного счета";
КонецЕсли;
Количество = Данные.Итог("Количество85");
Сумма = Данные.Итог("Сумма85");
СуммаЦен = Данные.Итог("Цена85");
Если Количество <> 5 Или Сумма <> 1050 Или СуммаЦен <> 400 Тогда
ВызватьИсключение "Native итоги исходника должны быть 5, 1050 и 400";
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура("Количество,Сумма,СуммаЦен", Количество, Сумма, СуммаЦен);
КонецФункции
Процедура ПроверитьОписание85(Описание, МетаДокумент, ПервоеПоле, Пустое)
Если ТипЗнч(Описание) <> Тип("ОписаниеИспользованияСобытияОтказВДоступеЖурналаРегистрации") Тогда
ВызватьИсключение "Нужен реальный собственный native descriptor отказа в доступе";
КонецЕсли;
Если ТипЗнч(Описание.Объект) <> Тип("Строка") Или Описание.Объект <> МетаДокумент.ПолноеИмя()
Или Метаданные.НайтиПоПолномуИмени(Описание.Объект) <> МетаДокумент Тогда
ВызватьИсключение "Объект должен быть полным именем своих настоящих метаданных";
КонецЕсли;
Регистрация = Описание.ПоляРегистрации;
Если ТипЗнч(Регистрация) <> Тип("Массив") Тогда
ВызватьИсключение "Readonly свойство должно вернуть настоящий Массив";
КонецЕсли;
Если Пустое Тогда
Если Регистрация.Количество() <> 0 Тогда
ВызватьИсключение "Новый массив должен быть пустым; чужое или неожиданное состояние не изменяем";
КонецЕсли;
Возврат;
КонецЕсли;
Если ПервоеПоле <> "Сумма85" И ПервоеПоле <> "Цена85" Тогда
ВызватьИсключение "Допускаются только две собственные альтернативы";
КонецЕсли;
ВтороеПоле = ?(ПервоеПоле = "Сумма85", "Цена85", "Сумма85");
Если Регистрация.Количество() <> 2 Тогда
ВызватьИсключение "Нужны ровно две группы регистрации своего плана";
КонецЕсли;
Если ТипЗнч(Регистрация[0]) <> Тип("Массив") Или ТипЗнч(Регистрация[1]) <> Тип("Строка")
Или Регистрация[1] <> "Количество85" Тогда
ВызватьИсключение "Проверьте вложенные альтернативы и отдельное поле количества";
КонецЕсли;
Альтернативы = Регистрация[0];
Если Альтернативы.Количество() <> 2 Тогда
ВызватьИсключение "Вложенный массив должен содержать ровно две альтернативы";
КонецЕсли;
Если ТипЗнч(Альтернативы[0]) <> Тип("Строка") Или ТипЗнч(Альтернативы[1]) <> Тип("Строка")
Или Альтернативы[0] <> ПервоеПоле Или Альтернативы[1] <> ВтороеПоле Тогда
ВызватьИсключение "Нужно полностью подтвердить типы и порядок обеих альтернатив";
КонецЕсли;
КонецПроцедуры
Функция СоздатьОписание85(МетаДокумент, ПервоеПоле)
Если ПервоеПоле <> "Сумма85" И ПервоеПоле <> "Цена85" Тогда
ВызватьИсключение "Неизвестное поле нового собственного плана";
КонецЕсли;
ИмяОбъекта = МетаДокумент.ПолноеИмя();
Описание = Новый ОписаниеИспользованияСобытияОтказВДоступеЖурналаРегистрации(ИмяОбъекта);
ПроверитьОписание85(Описание, МетаДокумент, ПервоеПоле, Истина);
Регистрация = Описание.ПоляРегистрации;
Альтернативы = Новый Массив;
Альтернативы.Добавить(ПервоеПоле);
Альтернативы.Добавить(?(ПервоеПоле = "Сумма85", "Цена85", "Сумма85"));
Регистрация.Добавить(Альтернативы);
Регистрация.Добавить("Количество85");
ПроверитьОписание85(Описание, МетаДокумент, ПервоеПоле, Ложь);
Возврат Описание;
КонецФункции
Процедура ПроверитьСостав85(Состав, МетаДокумент, ПервоеПоле)
Если ТипЗнч(Состав) <> Тип("Массив") Или Состав.Количество() <> 1 Тогда
ВызватьИсключение "Состав должен быть своим массивом с одним описанием отказа";
КонецЕсли;
Описание = Состав[0];
ПроверитьОписание85(Описание, МетаДокумент, ПервоеПоле, Ложь);
КонецПроцедуры
Процедура ПроверитьПлан85(План, МетаДокумент, Использовать, ПервоеПоле)
Если ТипЗнч(План) <> Тип("ИспользованиеСобытияЖурналаРегистрации") Тогда
ВызватьИсключение "Нужен настоящий native объект использования события";
КонецЕсли;
Если ТипЗнч(План.Использование) <> Тип("Булево") Или План.Использование <> Использовать Тогда
ВызватьИсключение "Режим должен точно совпадать с ожидаемым собственным этапом";
КонецЕсли;
Состав = План.ОписаниеИспользования;
ПроверитьСостав85(Состав, МетаДокумент, ПервоеПоле);
КонецПроцедуры
Функция Предпросмотр85(План, МетаДокумент, Данные, Использовать, ПервоеПоле)
ПроверитьПлан85(План, МетаДокумент, Использовать, ПервоеПоле);
ПрочитатьДанные85(Данные);
Если Не План.Использование Тогда
ПроверитьПлан85(План, МетаДокумент, Ложь, ПервоеПоле);
Возврат Новый ФиксированнаяСтруктура("Количество,Итог,Поле", 0, 0, "");
КонецЕсли;
Состав = План.ОписаниеИспользования;
Описание = Состав[0];
Регистрация = Описание.ПоляРегистрации;
Альтернативы = Регистрация[0];
ВыбранноеПоле = "";
Для Индекс = 0 По Альтернативы.Количество() - 1 Цикл
ИмяПоля = Альтернативы[Индекс];
Если Данные.Колонки.Найти(ИмяПоля) <> Неопределено Тогда
ВыбранноеПоле = ИмяПоля;
Прервать;
КонецЕсли;
КонецЦикла;
ПолеКоличества = Регистрация[1];
Если ВыбранноеПоле = "" Или Данные.Колонки.Найти(ПолеКоличества) = Неопределено Тогда
ВызватьИсключение "Собственная проекция не обеспечивает обе группы вывода";
КонецЕсли;
Проекция = Данные.Скопировать(, ПолеКоличества + "," + ВыбранноеПоле);
Если Проекция.Количество() <> 2 Или Проекция.Колонки.Количество() <> 2
Или Проекция.Колонки.Найти(ПолеКоличества) = Неопределено Или Проекция.Колонки.Найти(ВыбранноеПоле) = Неопределено Тогда
ВызватьИсключение "Native копия должна содержать ровно две свои строки и две выбранные колонки";
КонецЕсли;
Количество = Проекция.Итог(ПолеКоличества);
Итог = Проекция.Итог(ВыбранноеПоле);
Если ТипЗнч(Количество) <> Тип("Число") Или ТипЗнч(Итог) <> Тип("Число") Тогда
ВызватьИсключение "Предпросмотр должен вычислить реальные числовые native итоги";
КонецЕсли;
ПрочитатьДанные85(Данные);
ПроверитьПлан85(План, МетаДокумент, Истина, ПервоеПоле);
Возврат Новый ФиксированнаяСтруктура("Количество,Итог,Поле", Количество, Итог, ВыбранноеПоле);
КонецФункции
Процедура ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Использовать, ПервоеПоле)
ПроверитьСтенд85();
ПрочитатьДанные85(Данные);
ПроверитьСостав85(СоставA, МетаДокумент, "Сумма85");
ПроверитьСостав85(СоставB, МетаДокумент, "Цена85");
ПроверитьПлан85(План, МетаДокумент, Использовать, ПервоеПоле);
КонецПроцедуры
Функция ВыполнитьПредпросмотр85() Экспорт
МетаДокумент = ПроверитьСтенд85();
ОписаниеA = СоздатьОписание85(МетаДокумент, "Сумма85");
ОписаниеB = СоздатьОписание85(МетаДокумент, "Цена85");
СоставA = Новый Массив;
СоставA.Добавить(ОписаниеA);
СоставB = Новый Массив;
СоставB.Добавить(ОписаниеB);
План = Новый ИспользованиеСобытияЖурналаРегистрации(Истина, СоставA);
Данные = СоздатьДанные85(МетаДокумент);
До = ПрочитатьДанные85(Данные);
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
Включен = Предпросмотр85(План, МетаДокумент, Данные, Истина, "Сумма85");
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
План.Использование = Ложь;
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Ложь, "Сумма85");
Отключен = Предпросмотр85(План, МетаДокумент, Данные, Ложь, "Сумма85");
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Ложь, "Сумма85");
План.Использование = Истина;
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
СноваВключен = Предпросмотр85(План, МетаДокумент, Данные, Истина, "Сумма85");
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
План.ОписаниеИспользования = СоставB;
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Цена85");
ДругойСостав = Предпросмотр85(План, МетаДокумент, Данные, Истина, "Цена85");
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Цена85");
План.ОписаниеИспользования = СоставA;
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
Восстановлен = Предпросмотр85(План, МетаДокумент, Данные, Истина, "Сумма85");
ПроверитьЭтап85(План, МетаДокумент, Данные, СоставA, СоставB, Истина, "Сумма85");
После = ПрочитатьДанные85(Данные);
Если Включен.Количество <> 5 Или Включен.Итог <> 1050 Или Включен.Поле <> "Сумма85"
Или Отключен.Количество <> 0 Или Отключен.Итог <> 0 Или Отключен.Поле <> ""
Или СноваВключен.Количество <> 5 Или СноваВключен.Итог <> 1050 Или СноваВключен.Поле <> "Сумма85"
Или ДругойСостав.Количество <> 5 Или ДругойСостав.Итог <> 400 Или ДругойСостав.Поле <> "Цена85"
Или Восстановлен.Количество <> 5 Или Восстановлен.Итог <> 1050 Или Восстановлен.Поле <> "Сумма85"
Или После.Количество <> До.Количество Или После.Сумма <> До.Сумма Или После.СуммаЦен <> До.СуммаЦен Тогда
ВызватьИсключение "Не подтвердился полный результат пяти этапов собственного предпросмотра";
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура("Количество,Сумма,СуммаЦен,Включен,Отключен,СноваВключен,ДругойСостав,Восстановлен",
После.Количество, После.Сумма, После.СуммаЦен, Включен.Итог, Отключен.Итог, СноваВключен.Итог, ДругойСостав.Итог, Восстановлен.Итог);
КонецФункцииСвоя управляемая форма: восемь скаляров, реальные команды и две границы остановки
Общие → Общие формы → ПредпросмотрОтказа85 → Модуль
Толстый управляемый клиент и &НаСервере процедуры собственной формы
&НаКлиенте
Процедура Предпросмотр85(Команда)
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Остановка85 Или Завершено85 Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Предпросмотр85 Тогда
Остановка85 = Истина;
ВызватьИсключение "Только своя команда и один запуск; после ошибки или успеха повтор запрещен";
КонецЕсли;
Попытка
ПредпросмотрНаСервере85();
Если Не Завершено85 Или Количество85 <> 5 Или Сумма85 <> 1050 Или СуммаЦен85 <> 400
Или Включен85 <> 1050 Или Отключен85 <> 0 Или СноваВключен85 <> 1050
Или ДругойСостав85 <> 400 Или Восстановлен85 <> 1050 Тогда
ВызватьИсключение "Не совпал весь скалярный пакет своего предпросмотра";
КонецЕсли;
Исключение
Остановка85 = Истина;
Протокол85 = "Отказ. Настройки журнала не менялись. Возможно частичное состояние только собственного memory плана; повтор и слепое восстановление запрещены.";
ВызватьИсключение;
КонецПопытки;
#Иначе
Остановка85 = Истина;
ВызватьИсключение "Подготовьте именно свой толстый управляемый клиент";
#КонецЕсли
КонецПроцедуры
&НаСервере
Процедура ПредпросмотрНаСервере85()
Если Остановка85 Или Завершено85 Тогда
Остановка85 = Истина;
ВызватьИсключение "Серверная граница также запрещает повтор";
КонецЕсли;
Попытка
Пакет = ПланОтказаПамять85.ВыполнитьПредпросмотр85();
Если Пакет.Количество <> 5 Или Пакет.Сумма <> 1050 Или Пакет.СуммаЦен <> 400
Или Пакет.Включен <> 1050 Или Пакет.Отключен <> 0 Или Пакет.СноваВключен <> 1050
Или Пакет.ДругойСостав <> 400 Или Пакет.Восстановлен <> 1050 Тогда
ВызватьИсключение "Сервер не принимает неполный или неподтвержденный результат";
КонецЕсли;
Количество85 = Пакет.Количество;
Сумма85 = Пакет.Сумма;
СуммаЦен85 = Пакет.СуммаЦен;
Включен85 = Пакет.Включен;
Отключен85 = Пакет.Отключен;
СноваВключен85 = Пакет.СноваВключен;
ДругойСостав85 = Пакет.ДругойСостав;
Восстановлен85 = Пакет.Восстановлен;
Протокол85 = "Свой счет: количество 5, сумма 1050, сумма цен 400. НАШ предпросмотр: 1050 → 0 → 1050 → 400 → 1050. Реальное журналирование, RLS и запросы не проверялись и не изменялись.";
Завершено85 = Истина;
Исключение
Остановка85 = Истина;
ВызватьИсключение;
КонецПопытки;
КонецПроцедурыНастоящее startup событие открывает только собственную форму
Конфигурация → Открыть модуль управляемого приложения
Толстый клиент, управляемое приложение
Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентУправляемоеПриложение Тогда
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnAccessDeniedPolicy85_c5771276-93dc-42d9-8f52-23ba39a74485\IB"";" Тогда
ВызватьИсключение "Открывается только собственная новая файловая лаборатория этого урока";
КонецЕсли;
ОткрытьФорму("ОбщаяФорма.ПредпросмотрОтказа85", , , Истина);
#Иначе
ВызватьИсключение "Нужен толстый управляемый клиент 8.5.1.1343";
#КонецЕсли
КонецПроцедурыРазбор по шагам
Проверяем точный File/version/NoUse, серверные настройки своего модуля, отсутствие расширений, настоящего единственного пользователя и соединение. Из реальных метаданных единственного документа получаем ПолноеИмя и три числовых типа. Документ не создаётся и не читается из базы.
Передаём ровно один настоящий String в ctor150. Проверяем native класс, actual ObjectString и metadata lookup, затем пустой массив из readonly свойства. Создаём два разных собственных порядка альтернатив A/B через Array.Add и свежий полный getter readback. Если удержание содержимого не подтверждено, немедленно отказываем; setter readonly свойства отсутствует.
Создаём две реальные строки ТаблицыЗначений: 3×250=750 и 2×150=300. Полностью перечитываем все шесть типизированных ячеек и вычисляем количество 5, сумму 1050, сумму цен 400. Native ctor151 получает явно Истина и новый массив с одним своим descriptor A; его фактическое содержимое заново проверяется.
Наш отдельный consumer читает actual режим и вложенные имена полей из native плана. Для включённого A он создаёт настоящую проекцию через Скопировать с пустым первым слотом и явными колонками, затем вычисляет Итог 1050/5. Не подменяем actual результат готовыми числами и не называем проекцию данными реального журнала.
Перед каждым из четырёх RW назначений выполняется полный свежий ПроверитьЭтап85: свой стенд, весь исходник, оба массива описаний и ожидаемый текущий native план. Использование=False даёт собственный пустой предпросмотр 0/0; True возвращает 1050/5; смена ОписаниеИспользования на свой B даёт 400/5; возврат своего A разрешён только после подтверждённого состояния B и возвращает 1050/5. Ни одно восстановление не выполняется из catch.
В конце проверяем поля, количество и итог каждого из пяти результатов, полностью неизменные входные ячейки и оба native описания. Сервер формы принимает восемь скаляров только целиком; клиент отдельно подтверждает их. Ошибки на любой границе устанавливают Остановка85, успех — Завершено85; ни один флаг не сбрасывается.
Как проверить результат
Ожидаемый будущий протокол: исходные 5/1050/400; пять собственных предпросмотров 1050 → 0 → 1050 → 400 → 1050. У включённых этапов количество 5, у отключённого 0; выбор A=Сумма85, B=Цена85; отключённый выбор пустой. Все шесть исходных ячеек и оба порядка вложенных альтернатив полностью проверяются, не только суммы или Count.
Все четыре setter имеют fresh whole-state guard непосредственно перед изменением и после него. Неизвестное состояние, неверная CF/пользователь/другое соединение, непустой новый массив, wrong type, потерянный порядок или частичный scalar пакет приводят к отказу; повтор, blind rollback и catch-успех запрещены.
MAIN реально использует все восемь PRIMARY. ПоляРегистрации не присваивается; generic ОписаниеИспользования допускается только после проверки фактического Array[ownDeniedDescriptor]. Нет нулевого ctor193/194, native event installation, запроса, RLS-теста или записи журнала.
Ожидаемый результат
Ожидаемый будущий ручной результат формы: 5, 1050, 400, 1050, 0, 1050, 400, 1050. Это реальный расчёт нашего memory consumer при указанной подготовке, не выполненный здесь запуск 1С и не доказательство регистрации отказов в журнале.
На что обратить внимание
У ctor150 String необязателен, но мы явно передаём один собственный FullName. Нулевой ctor150 пересекается с ctor193; аналогичную неоднозначность имеет access ctor149/194. Нулевые перегрузки не выбираются и не кредитуются.
Object8836 объявлен String. Не копируйте противоречивую фразу ctor150 про пустые массивы в Object: код требует реальный String своего документа и останавливается при другом результате. ПоляРегистрации8837 readonly; native setter-пример в большой странице SetEvent4041 не отменяет этот контракт.
Readonly свойство и тип Массив не являются гарантией, что изменения getter-результата удержатся в descriptor. Поэтому только собственный новый пустой массив, нативный Add и свежий полный readback; если подготовленная платформа не удержит изменения, будущий ручной протокол честно остановится, а не будет считаться успешным.
UseDescription имеет тип Произвольный. Для события _$Access$_.AccessDenied методическая область SetEvent4041 описывает Array[DeniedDescriptor]; здесь мы сами строим только такой массив и проверяем весь граф. Большая страница читалась только в относящемся к этому событию фрагменте, не целиком.
План никуда не устанавливается. Наше правило нулевого предпросмотра и суммы проекций не доказывают live RLS, query trigger, native Data.Data или порядок приоритетов реального регистратора. Не переносите код в рабочую базу: все входные данные и метаданные должны быть только своими.
Назначения и изменение массивов не атомарны. Возврат состава A допустим только после успешного этапа B и его полного fresh snapshot; при исключении не восстанавливаем вслепую. Sticky флаги защищают наш протокол формы, но не являются универсальным механизмом безопасности 1С.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.