Самостоятельный будущий опыт на своей новой VM, SQL базе, Windows readonly login, ODBC DSN и пустой файловой CF. Все фабрики настоящие; код только читает SQL, изменяя копию и черновик лишь в памяти.
Где и как запускать
Собственный Windows x64 толстый обычный клиент1С8.5.1.1343/новая файловая CF; native SQL вызовы здесь только authored, runtime0.
Подготовка
Все действия ниже — будущая ручная подготовка отдельного собственного стенда. Здесь только авторство BSL/SQL и статические проверки: VM, SQL Server, ODBC, CF, права, соединения и 1С не создавались и не запускались. Не используйте рабочую базу или подключение предприятия.
Создайте свою изолированную Windows VM GUIDE85SQLVM без рабочих баз и внешних маршрутов. SQL Server — новый default instance на localhost. Собственная база Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c и Windows login GUIDE85SQLVM\Guide85Read должны отсутствовать; существующие объекты никогда не заменяются.
Создайте собственный обычный Windows аккаунт Guide85Read, не локального/SQL администратора. Администратор подготовки — другой собственный аккаунт. В SSMS именно своего нового localhost instance будущий администратор вручную выполняет полный SQL-подготовительный блок ниже один раз: два CREATE TABLE, собственные NEWID, три строки каждой таблицы, login/user и readonly права. Частично выполненный блок не откатывается автоматически; после ошибки не запускайте его заново и не удаляйте ничего без отдельного решения.
Под Windows GUIDE85SQLVM\Guide85Read вручную выполните отдельный readonly SQL-блок. Нужны DB_NAME собственной базы, SYSTEM_USER своего login, SELECT=1 и INSERT/UPDATE/DELETE=0 у обеих таблиц, отсутствие sysadmin/db_owner/db_datawriter/db_ddladmin. Обе таблицы: COUNT3, SUM1500, A500/B750/C250. Сохраните действительный own_run_id из seed SELECT; не вводите UUID чужого источника. Здесь эти проверки не выполнялись.
Нужен совместимый 64-bit ODBC драйвер SQL Server для x64 процесса 1С. В 64-bit ODBC Data Sources создайте новый System DSN Guide85ReadLocal: localhost, default instance, default database Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c, Windows authentication. Не меняйте существующий DSN и не отключайте общую TLS проверку. Сертификат и настройки выбранного драйвера проверяет человек внутри своей VM; имя DSN не доказывает реальный адрес, базу или права.
Создайте новую пустую файловую базу строго в C:\1c-labs\OwnExternalReadonly15_2871b8c9-bf2c-44f7-92ff-6d32793e0e4c\IB, CF УчебноеЧтениеSQL85, встроенный язык Русский, РежимСовместимости=НеИспользовать, ОсновнойРежимЗапуска=ОбычноеПриложение. UUID каталога — метка стенда, не UUID информационной базы. Отдельно Сервис → Параметры → Общие → Редактирование конфигурации: Оба, чтобы видеть обычные клиентские флаги; это настройка редактора, не свойство SupportedRunModes.
Создайте единственную собственную роль и пользователя Уч85ЧтениеSQL с Администрированием и полными правами только этой пустой CF. Native GetUsers и изменение параметров сеанса требуют административных прав 1С. Это не дает Windows login прав записи SQL. Запускайте только x64 толстый обычный клиент8.5.1.1343 из Windows Guide85Read; серверной службы, другой сессии и клиент-серверной ИБ нет.
ВнешниеИсточникиДанных → УчебныйSQLЧтение85 → Данные → Таблицы → Добавить. В конструкторе выберите Вручную → Готово. У Лоты85: ВидТаблицы=Таблица, ИмяВИсточникеДанных=dbo.object_lots; Код=item_id Строка36, Метка=run_id Строка36, Название=title Строка40, Сумма=amount Число9.0. Все поля одиночного типа, конечная длина строк, Null запрещён, ТолькоЧтение=Нет. Родитель и версия не назначаются.
В редакторе Лоты85 → ПоляКлюча → кнопка выбора перенесите ровно Код вправо. Одно ключевое поле дает тип ОбъектныеДанные; ПолеПредставления=Название. У Строки85: имя dbo.row_lots, Метка=run_id Строка36, Номер=line_no Число9.0, Название=title Строка40, Сумма=amount Число9.0. ПоляКлюча: Метка, затем Номер — два поля дают НеобъектныеДанные. Тип таблицы и порядок PK должны точно совпасть с SQL.
У обеих таблиц ТолькоЧтение=Нет для допустимого memory-edit объекта, но внешняя запись запрещена собственному SQL login и отсутствует в BSL. Не путайте флаг CF с проверкой прав СУБД. Модули менеджера, объекта и набора оставьте пустыми, без подписок, обменов, функций, кубов, фоновых заданий и предопределенных изменений.
Создайте два общих модуля УчебныеSQLСсылки85 и УчебныеSQLКлючи85: Клиент (обычное приложение)=Да, Клиент (управляемое приложение)=Нет, Сервер=Нет, ВызовСервера=Нет, ВнешнееСоединение=Нет, Глобальный=Нет, Привилегированный=Нет, ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать. MAIN текущей карточки и supporting другой карточки — полные модули, вставляйте каждый один раз, не внутрь процедуры.
Создайте обычную общую форму ПроверкаSQL85. Реквизиты: МеткаСтенда85=Строка36, ПодготовкаПодтверждена85=Булево, ВнешнийИтог85/ПредварительныйИтог85/ВыбраннаяСумма85=Число9.0, Статус85=Строка неограниченной длины. ПолеВвода МеткаСтенда85 связывается с одноименным реквизитом. Флажок ПодготовкаПодтверждена85 — с одноименным Булево. Четыре поля результата связаны с одноименными реквизитами, ТолькоПросмотр=Да; Статус85 многострочный.
На своей форме добавьте Кнопка Ссылки85 и Кнопка Ключи85. Нажатие свяжите ровно с Ссылки85(Элемент) и Ключи85(Элемент); native Button.Click0 дополняется первым Элемент обычной формы по event104. ПриОткрытии — одноименная процедура без параметров. Вставьте полный supporting модуль формы и модуль обычного приложения; его ПриНачалеРаботыСистемы0 только открывает свою панель. Обработчики вручную не вызываются.
После ручной проверки Windows/SQL/DSN/CF обновите только свою базу и запустите обычный толстый клиент. Панель открывается без SQL-соединения. Введите свой run_id, подтвердите флажком исключительно собственный readonly стенд и нажмите Ссылки или Ключи. Только это действие запустит будущий native Connect. Код устанавливает только параметры собственного сеанса; пароль не читается, не хранится и не выводится.
Ссылки показывают внешний итог1500 и предварительный1600: копия собственного A меняется500→600 в памяти, B750/C250 не меняются. Отдельный новый черновикD200 не записан и не входит в прогноз1600. Ключи показывают исходный1500 и actualB750 по composite PK; D отсутствует и дает native пустой ключ без чтения несуществующей DB-строки.
После ошибки sticky STOP не сбрасывается повторной инициализацией или другой кнопкой. Старые числа не объявляются успешным новым результатом. Сохраняется первичная диагностика; закрытие предпринимается не более одного раза. Если эта попытка также бросила ошибку, обе диагностики явно сообщаются и состояние своего соединения считается неизвестным. Проверьте только свое соединение/настройки вручную, не повторяйте автоматически. В этом уроке нет записи/удаления, транзакционного отката, runtime-компиляции или проверки реальной доставки GUI.
Процедура ПроверитьСтенд85() Экспорт
#Если ТолстыйКлиентОбычноеПриложение Тогда
Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnExternalReadonly15_2871b8c9-bf2c-44f7-92ff-6d32793e0e4c\IB"";" Тогда
ВызватьИсключение "Нужна отдельная собственная файловая база учебного SQL-стенда";
КонецЕсли;
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 1 Или Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Роли.Количество() <> 1 Тогда
ВызватьИсключение "Подготовьте пустую собственную CF с единственным учебным источником";
КонецЕсли;
Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь();
Если Пользователь = Неопределено Тогда
ВызватьИсключение "Войдите собственным явно созданным пользователем Уч85ЧтениеSQL";
КонецЕсли;
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Если Пользователь.Имя <> "Уч85ЧтениеSQL" Или Пользователи.Количество() <> 1 Тогда
ВызватьИсключение "Разрешен только единственный собственный учебный пользователь";
КонецЕсли;
Для Каждого СвойПользователь Из Пользователи Цикл
Если СвойПользователь.УникальныйИдентификатор <> Пользователь.УникальныйИдентификатор Тогда
ВызватьИсключение "Не совпал собственный пользователь стенда";
КонецЕсли;
КонецЦикла;
МетаИсточник = Метаданные.ВнешниеИсточникиДанных.УчебныйSQLЧтение85;
Если МетаИсточник.Таблицы.Количество() <> 2 Или МетаИсточник.Кубы.Количество() <> 0
Или МетаИсточник.Функции.Количество() <> 0 Тогда
ВызватьИсключение "Нужны только две собственные таблицы без функций и кубов";
КонецЕсли;
ПроверитьМетаТаблицу85(МетаИсточник.Таблицы.Лоты85, Истина);
ПроверитьМетаТаблицу85(МетаИсточник.Таблицы.Строки85, Ложь);
#Иначе
ВызватьИсключение "Запустите собственный толстый обычный клиент, не сервер или тонкий клиент";
#КонецЕсли
КонецПроцедуры
Процедура ПроверитьUUID85(Значение)
Если ТипЗнч(Значение) <> Тип("Строка") Или СтрДлина(Значение) <> 36
Или Значение <> СокрЛП(Значение) Или Значение = "00000000-0000-0000-0000-000000000000" Тогда
ВызватьИсключение "Скопируйте действительный собственный UUID из полного seed SELECT";
КонецЕсли;
Идентификатор = Новый УникальныйИдентификатор(Значение);
Если НРег(Строка(Идентификатор)) <> НРег(Значение) Тогда
ВызватьИсключение "Нужна каноническая строка собственного UUID";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьМетаТаблицу85(МетаТаблица, Объектные)
Если Объектные Тогда
ИмяТаблицы = "dbo.object_lots";
ИмяКлюча = "Код";
ИмяКлючаSQL = "item_id";
ТипТаблицы = Метаданные.СвойстваОбъектов.ТипДанныхТаблицыВнешнегоИсточникаДанных.ОбъектныеДанные;
ЧислоКлючей = 1;
Иначе
ИмяТаблицы = "dbo.row_lots";
ИмяКлюча = "Номер";
ИмяКлючаSQL = "line_no";
ТипТаблицы = Метаданные.СвойстваОбъектов.ТипДанныхТаблицыВнешнегоИсточникаДанных.НеобъектныеДанные;
ЧислоКлючей = 2;
КонецЕсли;
Если МетаТаблица.ИмяВИсточникеДанных <> ИмяТаблицы Или МетаТаблица.ТипДанныхТаблицы <> ТипТаблицы
Или МетаТаблица.ТолькоЧтение Или МетаТаблица.Поля.Количество() <> 4
Или МетаТаблица.ПоляКлюча.Количество() <> ЧислоКлючей Тогда
ВызватьИсключение "Описание собственной таблицы не совпало с подготовленным SQL";
КонецЕсли;
Имена = Новый Массив;
Для Каждого Поле Из МетаТаблица.Поля Цикл
Если Поле.ТолькоЧтение Или Поле.РазрешитьNull Или Поле.Тип.Типы().Количество() <> 1 Тогда
ВызватьИсключение "Учебные поля имеют один явный тип, Null запрещен";
КонецЕсли;
Если Поле.Имя = ИмяКлюча Тогда
ИмяSQL = ИмяКлючаSQL;
Длина = 36;
Числовое = Не Объектные;
ИначеЕсли Поле.Имя = "Метка" Тогда
ИмяSQL = "run_id";
Длина = 36;
Числовое = Ложь;
ИначеЕсли Поле.Имя = "Название" Тогда
ИмяSQL = "title";
Длина = 40;
Числовое = Ложь;
ИначеЕсли Поле.Имя = "Сумма" Тогда
ИмяSQL = "amount";
Длина = 0;
Числовое = Истина;
Иначе
ВызватьИсключение "Обнаружено неизвестное поле учебной таблицы";
КонецЕсли;
Если Поле.ИмяВИсточникеДанных <> ИмяSQL Или Имена.Найти(Поле.Имя) <> Неопределено Тогда
ВызватьИсключение "Не совпало SQL имя или поле повторилось";
КонецЕсли;
Если Числовое Тогда
Если Не Поле.Тип.СодержитТип(Тип("Число")) Или Поле.Тип.КвалификаторыЧисла.Разрядность <> 9
Или Поле.Тип.КвалификаторыЧисла.РазрядностьДробнойЧасти <> 0 Тогда
ВызватьИсключение "Нужен одиночный числовой тип9.0";
КонецЕсли;
Иначе
Если Не Поле.Тип.СодержитТип(Тип("Строка")) Или Поле.Тип.КвалификаторыСтроки.Длина <> Длина Тогда
ВызватьИсключение "Не совпал одиночный строковый тип и его конечная длина";
КонецЕсли;
КонецЕсли;
Имена.Добавить(Поле.Имя);
КонецЦикла;
Позиция = 0;
Для Каждого ПолеКлюча Из МетаТаблица.ПоляКлюча Цикл
Если Объектные Тогда
ОжидаемоеИмя = "Код";
ИначеЕсли Позиция = 0 Тогда
ОжидаемоеИмя = "Метка";
Иначе
ОжидаемоеИмя = "Номер";
КонецЕсли;
Если ТипЗнч(ПолеКлюча) <> Тип("Поле") Или ПолеКлюча.Имя <> ОжидаемоеИмя Тогда
ВызватьИсключение "Не совпали действительные поля и порядок SQL PK";
КонецЕсли;
Позиция = Позиция + 1;
КонецЦикла;
КонецПроцедуры
Функция НачатьСвоёЧтение85(Метка)
ПроверитьСтенд85();
ПроверитьUUID85(Метка);
Источник = ВнешниеИсточникиДанных.УчебныйSQLЧтение85;
Если ТипЗнч(Источник) <> Тип("ВнешнийИсточникДанныхМенеджер.УчебныйSQLЧтение85")
Или Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Отключен Тогда
ВызватьИсключение "Нужен свой источник без уже существующего соединения";
КонецЕсли;
Параметры = Источник.ПолучитьПараметрыСоединенияСеанса();
Если Параметры.СтрокаСоединения <> Неопределено И Параметры.СтрокаСоединения <> ""
И Параметры.СтрокаСоединения <> "DSN=Guide85ReadLocal;" Тогда
ВызватьИсключение "Не разрешено заменять неизвестные параметры чужого DSN";
КонецЕсли;
Параметры.СУБД = "MSSQLServer";
Параметры.СтрокаСоединения = "DSN=Guide85ReadLocal;";
Параметры.АутентификацияОС = Истина;
Параметры.АутентификацияСтандартная = Ложь;
Источник.УстановитьПараметрыСоединенияСеанса(Параметры);
Фактические = Источник.ПолучитьПараметрыСоединенияСеанса();
Если Фактические.СУБД <> "MSSQLServer" Или Фактические.СтрокаСоединения <> "DSN=Guide85ReadLocal;"
Или Фактические.АутентификацияОС <> Истина Или Фактические.АутентификацияСтандартная <> Ложь Тогда
ВызватьИсключение "Не подтверждены собственные параметры Windows-аутентификации";
КонецЕсли;
Возврат Источник;
КонецФункции
Функция СнятьСнимок85(Данные, Метка, Объектные)
Если ТипЗнч(Данные) <> Тип("ТаблицаЗначений") Или Данные.Количество() <> 3 Или Данные.Колонки.Количество() <> 4 Тогда
ВызватьИсключение "Нужен полный результат из трёх строк и четырёх полей";
КонецЕсли;
Для Каждого ИмяКолонки Из СтрРазделить("Ключ,Метка,Название,Сумма", ",") Цикл
Если Данные.Колонки.Найти(ИмяКолонки) = Неопределено Тогда
ВызватьИсключение "Отсутствует поле полного результата";
КонецЕсли;
КонецЦикла;
Строки = Новый Массив;
Ключи = Новый Массив;
Названия = Новый Массив;
Итог = 0;
Для Каждого Лот Из Данные Цикл
Если ТипЗнч(Лот.Метка) <> Тип("Строка") Или Лот.Метка <> Метка
Или ТипЗнч(Лот.Название) <> Тип("Строка") Или ТипЗнч(Лот.Сумма) <> Тип("Число")
Или Ключи.Найти(Лот.Ключ) <> Неопределено Или Названия.Найти(Лот.Название) <> Неопределено Тогда
ВызватьИсключение "Нужны только три собственные разные подготовленные строки";
КонецЕсли;
Если Объектные Тогда
ПроверитьUUID85(Лот.Ключ);
Иначе
Если ТипЗнч(Лот.Ключ) <> Тип("Число") Тогда
ВызватьИсключение "Номер необъектной записи должен оставаться числом";
КонецЕсли;
КонецЕсли;
Если Лот.Название = "A" Тогда
ОжидаемаяСумма = 500;
ОжидаемыйНомер = 1;
ИначеЕсли Лот.Название = "B" Тогда
ОжидаемаяСумма = 750;
ОжидаемыйНомер = 2;
ИначеЕсли Лот.Название = "C" Тогда
ОжидаемаяСумма = 250;
ОжидаемыйНомер = 3;
Иначе
ВызватьИсключение "Обнаружена строка вне собственного seed A/B/C";
КонецЕсли;
Если Лот.Сумма <> ОжидаемаяСумма Или (Не Объектные И Лот.Ключ <> ОжидаемыйНомер) Тогда
ВызватьИсключение "Исходная строка не совпала с собственным fixture";
КонецЕсли;
Ключи.Добавить(Лот.Ключ);
Названия.Добавить(Лот.Название);
Итог = Итог + Лот.Сумма;
Строки.Добавить(Новый ФиксированнаяСтруктура("Ключ,Метка,Название,Сумма", Лот.Ключ, Лот.Метка, Лот.Название, Лот.Сумма));
КонецЦикла;
Если Итог <> 1500 Тогда
ВызватьИсключение "Независимый итог полного источника должен быть1500";
КонецЕсли;
Возврат Новый ФиксированныйМассив(Строки);
КонецФункции
Функция ПрочитатьЛоты85(Метка, Объектные)
ПроверитьСтенд85();
Если Объектные Тогда
ТекстЗапроса = "ВЫБРАТЬ Т.Код КАК Ключ, Т.Метка, Т.Название, Т.Сумма
|ИЗ ВнешнийИсточникДанных.УчебныйSQLЧтение85.Таблица.Лоты85 КАК Т";
Иначе
ТекстЗапроса = "ВЫБРАТЬ Т.Номер КАК Ключ, Т.Метка, Т.Название, Т.Сумма
|ИЗ ВнешнийИсточникДанных.УчебныйSQLЧтение85.Таблица.Строки85 КАК Т";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Данные = Запрос.Выполнить().Выгрузить();
Возврат СнятьСнимок85(Данные, Метка, Объектные);
КонецФункции
Функция НайтиПоНазванию85(Снимок, Название)
Найденная = Неопределено;
Для Каждого СтрокаЛота Из Снимок Цикл
Если СтрокаЛота.Название = Название Тогда
Если Найденная <> Неопределено Тогда
ВызватьИсключение "Название повторилось в собственном полном снимке";
КонецЕсли;
Найденная = СтрокаЛота;
КонецЕсли;
КонецЦикла;
Возврат Найденная;
КонецФункции
Процедура СравнитьСнимки85(До, После)
Если ТипЗнч(До) <> Тип("ФиксированныйМассив") Или ТипЗнч(После) <> Тип("ФиксированныйМассив")
Или До.Количество() <> 3 Или После.Количество() <> До.Количество() Тогда
ВызватьИсключение "Изменился тип или полный размер собственного источника";
КонецЕсли;
Для Каждого Исходная Из До Цикл
Текущая = НайтиПоНазванию85(После, Исходная.Название);
Если Текущая = Неопределено Тогда
ВызватьИсключение "Исчезла собственная исходная строка";
КонецЕсли;
Если ТипЗнч(Текущая.Ключ) <> ТипЗнч(Исходная.Ключ) Или Текущая.Ключ <> Исходная.Ключ
Или ТипЗнч(Текущая.Метка) <> ТипЗнч(Исходная.Метка) Или Текущая.Метка <> Исходная.Метка
Или ТипЗнч(Текущая.Название) <> ТипЗнч(Исходная.Название) Или Текущая.Название <> Исходная.Название
Или ТипЗнч(Текущая.Сумма) <> ТипЗнч(Исходная.Сумма) Или Текущая.Сумма <> Исходная.Сумма Тогда
ВызватьИсключение "Полные типизированные поля источника изменились";
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция НайтиКлюч85(Менеджер, Снимок, Название)
СтрокаЛота = НайтиПоНазванию85(Снимок, Название);
Если СтрокаЛота = Неопределено Тогда
Возврат Менеджер.ПустойКлюч();
КонецЕсли;
ПоляКлюча = Новый Структура("Метка,Номер", СтрокаЛота.Метка, СтрокаЛота.Ключ);
Возврат Менеджер.СоздатьКлючЗаписи(ПоляКлюча);
КонецФункции
Функция ВыполнитьКлючи85(Метка) Экспорт
Источник = НачатьСвоёЧтение85(Метка);
ЗакрытиеНачато = Ложь;
Попытка
Источник.УстановитьСоединение();
Если Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Подключен Тогда
ВызватьИсключение "Не установлено собственное readonly соединение";
КонецЕсли;
Менеджер = Источник.Таблицы.Строки85;
Если ТипЗнч(Менеджер) <> Тип("ВнешнийИсточникДанныхТаблицаМенеджер.УчебныйSQLЧтение85.Строки85") Тогда
ВызватьИсключение "Не получен менеджер своей необъектной таблицы";
КонецЕсли;
До = ПрочитатьЛоты85(Метка, Ложь);
Набор = Менеджер.СоздатьНаборЗаписей();
Если ТипЗнч(Набор) <> Тип("ВнешнийИсточникДанныхТаблицаНаборЗаписей.УчебныйSQLЧтение85.Строки85") Или Набор.Количество() <> 0 Тогда
ВызватьИсключение "Фабрика должна дать собственный пустой native набор";
КонецЕсли;
Набор.Прочитать();
Если Набор.Количество() <> 3 Тогда
ВызватьИсключение "Набор обязан прочитать все три собственные строки";
КонецЕсли;
СуммаНабора = 0;
ПрочитанныеНазвания = Новый Массив;
Для Каждого Запись Из Набор Цикл
Если ТипЗнч(Запись) <> Тип("ВнешнийИсточникДанныхТаблицаЗапись.УчебныйSQLЧтение85.Строки85") Тогда
ВызватьИсключение "Обход должен дать настоящую запись собственного набора";
КонецЕсли;
Исходная = НайтиПоНазванию85(До, Запись.Название);
Если Исходная = Неопределено Или ПрочитанныеНазвания.Найти(Запись.Название) <> Неопределено Тогда
ВызватьИсключение "Набор содержит неизвестную или повторенную строку";
КонецЕсли;
Если ТипЗнч(Запись.Номер) <> ТипЗнч(Исходная.Ключ) Или Запись.Номер <> Исходная.Ключ
Или ТипЗнч(Запись.Метка) <> ТипЗнч(Исходная.Метка) Или Запись.Метка <> Исходная.Метка
Или ТипЗнч(Запись.Название) <> ТипЗнч(Исходная.Название) Или Запись.Название <> Исходная.Название
Или ТипЗнч(Запись.Сумма) <> ТипЗнч(Исходная.Сумма) Или Запись.Сумма <> Исходная.Сумма Тогда
ВызватьИсключение "Каждое actual поле набора должно совпасть с независимым полным запросом";
КонецЕсли;
ПрочитанныеНазвания.Добавить(Запись.Название);
СуммаНабора = СуммаНабора + Запись.Сумма;
КонецЦикла;
Если СуммаНабора <> 1500 Тогда
ВызватьИсключение "Независимый итог прочитанного набора должен дать1500";
КонецЕсли;
КлючB = НайтиКлюч85(Менеджер, До, "B");
ИсходныйB = НайтиПоНазванию85(До, "B");
Если ТипЗнч(КлючB) <> Тип("ВнешнийИсточникДанныхТаблицаКлючЗаписи.УчебныйSQLЧтение85.Строки85") Или КлючB.Пустой()
Или ТипЗнч(КлючB.Метка) <> ТипЗнч(ИсходныйB.Метка) Или КлючB.Метка <> ИсходныйB.Метка
Или ТипЗнч(КлючB.Номер) <> ТипЗнч(ИсходныйB.Ключ) Или КлючB.Номер <> ИсходныйB.Ключ Тогда
ВызватьИсключение "Фактический composite PK не совпал с настоящим ключом собственного B";
КонецЕсли;
ВыбраннаяСумма = Неопределено;
Для Каждого Запись Из Набор Цикл
Если Запись.Метка = КлючB.Метка И Запись.Номер = КлючB.Номер Тогда
Если ВыбраннаяСумма <> Неопределено Тогда
ВызватьИсключение "Ключ matched более одной своей строки";
КонецЕсли;
ВыбраннаяСумма = Запись.Сумма;
КонецЕсли;
КонецЦикла;
Если ВыбраннаяСумма <> ИсходныйB.Сумма Или ВыбраннаяСумма <> 750 Тогда
ВызватьИсключение "Сумма действительной выбранной строки B должна дать750";
КонецЕсли;
ОтсутствующийКлюч = НайтиКлюч85(Менеджер, До, "D");
Если ТипЗнч(ОтсутствующийКлюч) <> ТипЗнч(КлючB) Или Не ОтсутствующийКлюч.Пустой() Тогда
ВызватьИсключение "Отсутствие D должно дать настоящий пустой ключ без чтения несуществующей записи";
КонецЕсли;
После = ПрочитатьЛоты85(Метка, Ложь);
СравнитьСнимки85(До, После);
ЗакрытиеНачато = Истина;
Источник.РазорватьСоединение();
Если Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Отключен Тогда
ВызватьИсключение "Проверьте закрытие только собственного учебного соединения";
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура("До,Прогноз,После,Выбранная", СуммаНабора, СуммаНабора, СуммаНабора, ВыбраннаяСумма);
Исключение
ПервичнаяОшибка = ОписаниеОшибки();
ОшибкаЗакрытия = "";
Если Не ЗакрытиеНачато Тогда
ЗакрытиеНачато = Истина;
Попытка
Источник.РазорватьСоединение();
Исключение
ОшибкаЗакрытия = ОписаниеОшибки();
КонецПопытки;
КонецЕсли;
Если ОшибкаЗакрытия <> "" Тогда
ВызватьИсключение "Первичная ошибка: " + ПервичнаяОшибка + Символы.ПС
+ "Отдельная ошибка закрытия: " + ОшибкаЗакрытия + Символы.ПС
+ "Состояние своего соединения неизвестно. STOP, без автоматического повтора.";
КонецЕсли;
ВызватьИсключение ПервичнаяОшибка;
КонецПопытки;
КонецФункцииВторой полный общий модуль этого же учебного стенда
Своя CF → Общие → ОбщиеМодули → УчебныеSQLСсылки85 → Модуль целиком
Клиент (обычное приложение)Да/СерверНет/ВызовСервераНет, та же собственная CF.
Процедура ПроверитьСтенд85() Экспорт
#Если ТолстыйКлиентОбычноеПриложение Тогда
Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnExternalReadonly15_2871b8c9-bf2c-44f7-92ff-6d32793e0e4c\IB"";" Тогда
ВызватьИсключение "Нужна отдельная собственная файловая база учебного SQL-стенда";
КонецЕсли;
Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 1 Или Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Роли.Количество() <> 1 Тогда
ВызватьИсключение "Подготовьте пустую собственную CF с единственным учебным источником";
КонецЕсли;
Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь();
Если Пользователь = Неопределено Тогда
ВызватьИсключение "Войдите собственным явно созданным пользователем Уч85ЧтениеSQL";
КонецЕсли;
Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
Если Пользователь.Имя <> "Уч85ЧтениеSQL" Или Пользователи.Количество() <> 1 Тогда
ВызватьИсключение "Разрешен только единственный собственный учебный пользователь";
КонецЕсли;
Для Каждого СвойПользователь Из Пользователи Цикл
Если СвойПользователь.УникальныйИдентификатор <> Пользователь.УникальныйИдентификатор Тогда
ВызватьИсключение "Не совпал собственный пользователь стенда";
КонецЕсли;
КонецЦикла;
МетаИсточник = Метаданные.ВнешниеИсточникиДанных.УчебныйSQLЧтение85;
Если МетаИсточник.Таблицы.Количество() <> 2 Или МетаИсточник.Кубы.Количество() <> 0
Или МетаИсточник.Функции.Количество() <> 0 Тогда
ВызватьИсключение "Нужны только две собственные таблицы без функций и кубов";
КонецЕсли;
ПроверитьМетаТаблицу85(МетаИсточник.Таблицы.Лоты85, Истина);
ПроверитьМетаТаблицу85(МетаИсточник.Таблицы.Строки85, Ложь);
#Иначе
ВызватьИсключение "Запустите собственный толстый обычный клиент, не сервер или тонкий клиент";
#КонецЕсли
КонецПроцедуры
Процедура ПроверитьUUID85(Значение)
Если ТипЗнч(Значение) <> Тип("Строка") Или СтрДлина(Значение) <> 36
Или Значение <> СокрЛП(Значение) Или Значение = "00000000-0000-0000-0000-000000000000" Тогда
ВызватьИсключение "Скопируйте действительный собственный UUID из полного seed SELECT";
КонецЕсли;
Идентификатор = Новый УникальныйИдентификатор(Значение);
Если НРег(Строка(Идентификатор)) <> НРег(Значение) Тогда
ВызватьИсключение "Нужна каноническая строка собственного UUID";
КонецЕсли;
КонецПроцедуры
Процедура ПроверитьМетаТаблицу85(МетаТаблица, Объектные)
Если Объектные Тогда
ИмяТаблицы = "dbo.object_lots";
ИмяКлюча = "Код";
ИмяКлючаSQL = "item_id";
ТипТаблицы = Метаданные.СвойстваОбъектов.ТипДанныхТаблицыВнешнегоИсточникаДанных.ОбъектныеДанные;
ЧислоКлючей = 1;
Иначе
ИмяТаблицы = "dbo.row_lots";
ИмяКлюча = "Номер";
ИмяКлючаSQL = "line_no";
ТипТаблицы = Метаданные.СвойстваОбъектов.ТипДанныхТаблицыВнешнегоИсточникаДанных.НеобъектныеДанные;
ЧислоКлючей = 2;
КонецЕсли;
Если МетаТаблица.ИмяВИсточникеДанных <> ИмяТаблицы Или МетаТаблица.ТипДанныхТаблицы <> ТипТаблицы
Или МетаТаблица.ТолькоЧтение Или МетаТаблица.Поля.Количество() <> 4
Или МетаТаблица.ПоляКлюча.Количество() <> ЧислоКлючей Тогда
ВызватьИсключение "Описание собственной таблицы не совпало с подготовленным SQL";
КонецЕсли;
Имена = Новый Массив;
Для Каждого Поле Из МетаТаблица.Поля Цикл
Если Поле.ТолькоЧтение Или Поле.РазрешитьNull Или Поле.Тип.Типы().Количество() <> 1 Тогда
ВызватьИсключение "Учебные поля имеют один явный тип, Null запрещен";
КонецЕсли;
Если Поле.Имя = ИмяКлюча Тогда
ИмяSQL = ИмяКлючаSQL;
Длина = 36;
Числовое = Не Объектные;
ИначеЕсли Поле.Имя = "Метка" Тогда
ИмяSQL = "run_id";
Длина = 36;
Числовое = Ложь;
ИначеЕсли Поле.Имя = "Название" Тогда
ИмяSQL = "title";
Длина = 40;
Числовое = Ложь;
ИначеЕсли Поле.Имя = "Сумма" Тогда
ИмяSQL = "amount";
Длина = 0;
Числовое = Истина;
Иначе
ВызватьИсключение "Обнаружено неизвестное поле учебной таблицы";
КонецЕсли;
Если Поле.ИмяВИсточникеДанных <> ИмяSQL Или Имена.Найти(Поле.Имя) <> Неопределено Тогда
ВызватьИсключение "Не совпало SQL имя или поле повторилось";
КонецЕсли;
Если Числовое Тогда
Если Не Поле.Тип.СодержитТип(Тип("Число")) Или Поле.Тип.КвалификаторыЧисла.Разрядность <> 9
Или Поле.Тип.КвалификаторыЧисла.РазрядностьДробнойЧасти <> 0 Тогда
ВызватьИсключение "Нужен одиночный числовой тип9.0";
КонецЕсли;
Иначе
Если Не Поле.Тип.СодержитТип(Тип("Строка")) Или Поле.Тип.КвалификаторыСтроки.Длина <> Длина Тогда
ВызватьИсключение "Не совпал одиночный строковый тип и его конечная длина";
КонецЕсли;
КонецЕсли;
Имена.Добавить(Поле.Имя);
КонецЦикла;
Позиция = 0;
Для Каждого ПолеКлюча Из МетаТаблица.ПоляКлюча Цикл
Если Объектные Тогда
ОжидаемоеИмя = "Код";
ИначеЕсли Позиция = 0 Тогда
ОжидаемоеИмя = "Метка";
Иначе
ОжидаемоеИмя = "Номер";
КонецЕсли;
Если ТипЗнч(ПолеКлюча) <> Тип("Поле") Или ПолеКлюча.Имя <> ОжидаемоеИмя Тогда
ВызватьИсключение "Не совпали действительные поля и порядок SQL PK";
КонецЕсли;
Позиция = Позиция + 1;
КонецЦикла;
КонецПроцедуры
Функция НачатьСвоёЧтение85(Метка)
ПроверитьСтенд85();
ПроверитьUUID85(Метка);
Источник = ВнешниеИсточникиДанных.УчебныйSQLЧтение85;
Если ТипЗнч(Источник) <> Тип("ВнешнийИсточникДанныхМенеджер.УчебныйSQLЧтение85")
Или Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Отключен Тогда
ВызватьИсключение "Нужен свой источник без уже существующего соединения";
КонецЕсли;
Параметры = Источник.ПолучитьПараметрыСоединенияСеанса();
Если Параметры.СтрокаСоединения <> Неопределено И Параметры.СтрокаСоединения <> ""
И Параметры.СтрокаСоединения <> "DSN=Guide85ReadLocal;" Тогда
ВызватьИсключение "Не разрешено заменять неизвестные параметры чужого DSN";
КонецЕсли;
Параметры.СУБД = "MSSQLServer";
Параметры.СтрокаСоединения = "DSN=Guide85ReadLocal;";
Параметры.АутентификацияОС = Истина;
Параметры.АутентификацияСтандартная = Ложь;
Источник.УстановитьПараметрыСоединенияСеанса(Параметры);
Фактические = Источник.ПолучитьПараметрыСоединенияСеанса();
Если Фактические.СУБД <> "MSSQLServer" Или Фактические.СтрокаСоединения <> "DSN=Guide85ReadLocal;"
Или Фактические.АутентификацияОС <> Истина Или Фактические.АутентификацияСтандартная <> Ложь Тогда
ВызватьИсключение "Не подтверждены собственные параметры Windows-аутентификации";
КонецЕсли;
Возврат Источник;
КонецФункции
Функция СнятьСнимок85(Данные, Метка, Объектные)
Если ТипЗнч(Данные) <> Тип("ТаблицаЗначений") Или Данные.Количество() <> 3 Или Данные.Колонки.Количество() <> 4 Тогда
ВызватьИсключение "Нужен полный результат из трёх строк и четырёх полей";
КонецЕсли;
Для Каждого ИмяКолонки Из СтрРазделить("Ключ,Метка,Название,Сумма", ",") Цикл
Если Данные.Колонки.Найти(ИмяКолонки) = Неопределено Тогда
ВызватьИсключение "Отсутствует поле полного результата";
КонецЕсли;
КонецЦикла;
Строки = Новый Массив;
Ключи = Новый Массив;
Названия = Новый Массив;
Итог = 0;
Для Каждого Лот Из Данные Цикл
Если ТипЗнч(Лот.Метка) <> Тип("Строка") Или Лот.Метка <> Метка
Или ТипЗнч(Лот.Название) <> Тип("Строка") Или ТипЗнч(Лот.Сумма) <> Тип("Число")
Или Ключи.Найти(Лот.Ключ) <> Неопределено Или Названия.Найти(Лот.Название) <> Неопределено Тогда
ВызватьИсключение "Нужны только три собственные разные подготовленные строки";
КонецЕсли;
Если Объектные Тогда
ПроверитьUUID85(Лот.Ключ);
Иначе
Если ТипЗнч(Лот.Ключ) <> Тип("Число") Тогда
ВызватьИсключение "Номер необъектной записи должен оставаться числом";
КонецЕсли;
КонецЕсли;
Если Лот.Название = "A" Тогда
ОжидаемаяСумма = 500;
ОжидаемыйНомер = 1;
ИначеЕсли Лот.Название = "B" Тогда
ОжидаемаяСумма = 750;
ОжидаемыйНомер = 2;
ИначеЕсли Лот.Название = "C" Тогда
ОжидаемаяСумма = 250;
ОжидаемыйНомер = 3;
Иначе
ВызватьИсключение "Обнаружена строка вне собственного seed A/B/C";
КонецЕсли;
Если Лот.Сумма <> ОжидаемаяСумма Или (Не Объектные И Лот.Ключ <> ОжидаемыйНомер) Тогда
ВызватьИсключение "Исходная строка не совпала с собственным fixture";
КонецЕсли;
Ключи.Добавить(Лот.Ключ);
Названия.Добавить(Лот.Название);
Итог = Итог + Лот.Сумма;
Строки.Добавить(Новый ФиксированнаяСтруктура("Ключ,Метка,Название,Сумма", Лот.Ключ, Лот.Метка, Лот.Название, Лот.Сумма));
КонецЦикла;
Если Итог <> 1500 Тогда
ВызватьИсключение "Независимый итог полного источника должен быть1500";
КонецЕсли;
Возврат Новый ФиксированныйМассив(Строки);
КонецФункции
Функция ПрочитатьЛоты85(Метка, Объектные)
ПроверитьСтенд85();
Если Объектные Тогда
ТекстЗапроса = "ВЫБРАТЬ Т.Код КАК Ключ, Т.Метка, Т.Название, Т.Сумма
|ИЗ ВнешнийИсточникДанных.УчебныйSQLЧтение85.Таблица.Лоты85 КАК Т";
Иначе
ТекстЗапроса = "ВЫБРАТЬ Т.Номер КАК Ключ, Т.Метка, Т.Название, Т.Сумма
|ИЗ ВнешнийИсточникДанных.УчебныйSQLЧтение85.Таблица.Строки85 КАК Т";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Данные = Запрос.Выполнить().Выгрузить();
Возврат СнятьСнимок85(Данные, Метка, Объектные);
КонецФункции
Функция НайтиПоНазванию85(Снимок, Название)
Найденная = Неопределено;
Для Каждого СтрокаЛота Из Снимок Цикл
Если СтрокаЛота.Название = Название Тогда
Если Найденная <> Неопределено Тогда
ВызватьИсключение "Название повторилось в собственном полном снимке";
КонецЕсли;
Найденная = СтрокаЛота;
КонецЕсли;
КонецЦикла;
Возврат Найденная;
КонецФункции
Процедура СравнитьСнимки85(До, После)
Если ТипЗнч(До) <> Тип("ФиксированныйМассив") Или ТипЗнч(После) <> Тип("ФиксированныйМассив")
Или До.Количество() <> 3 Или После.Количество() <> До.Количество() Тогда
ВызватьИсключение "Изменился тип или полный размер собственного источника";
КонецЕсли;
Для Каждого Исходная Из До Цикл
Текущая = НайтиПоНазванию85(После, Исходная.Название);
Если Текущая = Неопределено Тогда
ВызватьИсключение "Исчезла собственная исходная строка";
КонецЕсли;
Если ТипЗнч(Текущая.Ключ) <> ТипЗнч(Исходная.Ключ) Или Текущая.Ключ <> Исходная.Ключ
Или ТипЗнч(Текущая.Метка) <> ТипЗнч(Исходная.Метка) Или Текущая.Метка <> Исходная.Метка
Или ТипЗнч(Текущая.Название) <> ТипЗнч(Исходная.Название) Или Текущая.Название <> Исходная.Название
Или ТипЗнч(Текущая.Сумма) <> ТипЗнч(Исходная.Сумма) Или Текущая.Сумма <> Исходная.Сумма Тогда
ВызватьИсключение "Полные типизированные поля источника изменились";
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция ВыполнитьСсылки85(Метка) Экспорт
Источник = НачатьСвоёЧтение85(Метка);
ЗакрытиеНачато = Ложь;
Попытка
Источник.УстановитьСоединение();
Если Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Подключен Тогда
ВызватьИсключение "Не установлено собственное readonly соединение";
КонецЕсли;
Менеджер = Источник.Таблицы.Лоты85;
Если ТипЗнч(Менеджер) <> Тип("ВнешнийИсточникДанныхТаблицаМенеджер.УчебныйSQLЧтение85.Лоты85") Тогда
ВызватьИсключение "Не получен менеджер своей объектной таблицы";
КонецЕсли;
До = ПрочитатьЛоты85(Метка, Истина);
ЛотA = НайтиПоНазванию85(До, "A");
ЛотB = НайтиПоНазванию85(До, "B");
ЛотC = НайтиПоНазванию85(До, "C");
СсылкаЛота = Менеджер.НайтиПоПолю("Код", ЛотA.Ключ);
Если ТипЗнч(СсылкаЛота) <> Тип("ВнешнийИсточникДанныхТаблицаСсылка.УчебныйSQLЧтение85.Лоты85") Или СсылкаЛота.Пустая()
Или СсылкаЛота.Метаданные() <> Метаданные.ВнешниеИсточникиДанных.УчебныйSQLЧтение85.Таблицы.Лоты85 Тогда
ВызватьИсключение "Не найдена настоящая ссылка собственного A";
КонецЕсли;
Если ТипЗнч(СсылкаЛота.Код) <> ТипЗнч(ЛотA.Ключ) Или СсылкаЛота.Код <> ЛотA.Ключ
Или ТипЗнч(СсылкаЛота.Метка) <> ТипЗнч(ЛотA.Метка) Или СсылкаЛота.Метка <> ЛотA.Метка
Или ТипЗнч(СсылкаЛота.Название) <> ТипЗнч(ЛотA.Название) Или СсылкаЛота.Название <> ЛотA.Название
Или ТипЗнч(СсылкаЛота.Сумма) <> ТипЗнч(ЛотA.Сумма) Или СсылкаЛота.Сумма <> ЛотA.Сумма Тогда
ВызватьИсключение "Ссылка не соответствует всем фактическим полям исходного A";
КонецЕсли;
ЗначениеСсылки = СсылкаЛота.Значение();
Восстановленная = Менеджер.ПолучитьСсылку(ЗначениеСсылки);
Если ТипЗнч(Восстановленная) <> ТипЗнч(СсылкаЛота) Или Восстановленная <> СсылкаЛота Тогда
ВызватьИсключение "Не прошел roundtrip фактического непрозрачного значения ссылки";
КонецЕсли;
ПустаяСсылка = Менеджер.ПустаяСсылка();
Отсутствующая = Менеджер.НайтиПоПолю("Код", "00000000-0000-0000-0000-000000000000");
Если ТипЗнч(ПустаяСсылка) <> ТипЗнч(СсылкаЛота) Или Не ПустаяСсылка.Пустая()
Или ТипЗнч(Отсутствующая) <> ТипЗнч(СсылкаЛота) Или Не Отсутствующая.Пустая() Тогда
ВызватьИсключение "Ветка отсутствующего собственного элемента должна дать пустые ссылки";
КонецЕсли;
Копия = СсылкаЛота.Скопировать();
Если ТипЗнч(Копия) <> Тип("ВнешнийИсточникДанныхТаблицаОбъект.УчебныйSQLЧтение85.Лоты85") Или Не Копия.ЭтоНовый()
Или Копия.Метаданные() <> СсылкаЛота.Метаданные()
Или ТипЗнч(Копия.Метка) <> Тип("Строка") Или Копия.Метка <> ЛотA.Метка
Или ТипЗнч(Копия.Название) <> Тип("Строка") Или Копия.Название <> ЛотA.Название
Или ТипЗнч(Копия.Сумма) <> Тип("Число") Или Копия.Сумма <> ЛотA.Сумма Тогда
ВызватьИсключение "Проверьте фактическую несохраненную копию своего A";
КонецЕсли;
Копия.Сумма = 600;
Если Копия.Сумма <> 600 Или Не Копия.Модифицированность() Тогда
ВызватьИсключение "Не подтверждено собственное изменение только памяти копии";
КонецЕсли;
ПредварительныйИтог = Копия.Сумма + ЛотB.Сумма + ЛотC.Сумма;
Если ПредварительныйИтог <> 1600 Тогда
ВызватьИсключение "Прогноз копии600 и исходных750+250 должен дать1600";
КонецЕсли;
Черновик = Менеджер.СоздатьОбъект();
Если ТипЗнч(Черновик) <> ТипЗнч(Копия) Или Не Черновик.ЭтоНовый()
Или Черновик.Метаданные() <> Копия.Метаданные() Тогда
ВызватьИсключение "Не создан новый собственный объект таблицы только в памяти";
КонецЕсли;
Черновик.Метка = Метка;
Черновик.Название = "D";
Черновик.Сумма = 200;
Если Черновик.Метка <> Метка Или Черновик.Название <> "D" Или Черновик.Сумма <> 200 Тогда
ВызватьИсключение "Поля отдельного несохраненного черновика не совпали";
КонецЕсли;
После = ПрочитатьЛоты85(Метка, Истина);
СравнитьСнимки85(До, После);
ЗакрытиеНачато = Истина;
Источник.РазорватьСоединение();
Если Источник.ПолучитьСостояние() <> СостояниеВнешнегоИсточникаДанных.Отключен Тогда
ВызватьИсключение "Проверьте закрытие только собственного учебного соединения";
КонецЕсли;
Возврат Новый ФиксированнаяСтруктура("До,Прогноз,После,Выбранная", 1500, ПредварительныйИтог, 1500, Черновик.Сумма);
Исключение
ПервичнаяОшибка = ОписаниеОшибки();
ОшибкаЗакрытия = "";
Если Не ЗакрытиеНачато Тогда
ЗакрытиеНачато = Истина;
Попытка
Источник.РазорватьСоединение();
Исключение
ОшибкаЗакрытия = ОписаниеОшибки();
КонецПопытки;
КонецЕсли;
Если ОшибкаЗакрытия <> "" Тогда
ВызватьИсключение "Первичная ошибка: " + ПервичнаяОшибка + Символы.ПС
+ "Отдельная ошибка закрытия: " + ОшибкаЗакрытия + Символы.ПС
+ "Состояние своего соединения неизвестно. STOP, без автоматического повтора.";
КонецЕсли;
ВызватьИсключение ПервичнаяОшибка;
КонецПопытки;
КонецФункцииСвоя ordinary панель: UUID, явное подтверждение, две реальные кнопки
Своя CF → Общие → ОбщиеФормы → ПроверкаSQL85 → Модуль целиком
Только толстый обычный клиент; настоящий Button0+generic Element104.
Перем Остановка85;
Процедура ПриОткрытии()
Если Остановка85 <> Неопределено Тогда
ВызватьИсключение "Повторная инициализация не должна снимать прежний STOP";
КонецЕсли;
Остановка85 = Истина;
Попытка
УчебныеSQLСсылки85.ПроверитьСтенд85();
Если ЭтаФорма.ВладелецФормы <> Неопределено Тогда
ВызватьИсключение "Нужна самостоятельная учебная SQL-панель";
КонецЕсли;
Для Каждого ИмяПоля Из СтрРазделить("ВнешнийИтог85,ПредварительныйИтог85,ВыбраннаяСумма85,Статус85", ",") Цикл
Поле = ЭлементыФормы[ИмяПоля];
Если ТипЗнч(Поле) <> Тип("ПолеВвода") Или Поле.Данные <> ИмяПоля Тогда
ВызватьИсключение "Подготовьте все собственные связанные поля результата";
КонецЕсли;
Поле.ТолькоПросмотр = Истина;
Если Не Поле.ТолькоПросмотр Тогда
ВызватьИсключение "Результат должен оставаться только для просмотра";
КонецЕсли;
КонецЦикла;
Если ТипЗнч(ЭлементыФормы.МеткаСтенда85) <> Тип("ПолеВвода") Или ЭлементыФормы.МеткаСтенда85.Данные <> "МеткаСтенда85"
Или ТипЗнч(ЭлементыФормы.ПодготовкаПодтверждена85) <> Тип("Флажок") Или ЭлементыФормы.ПодготовкаПодтверждена85.Данные <> "ПодготовкаПодтверждена85" Тогда
ВызватьИсключение "Подготовьте собственное поле UUID и флажок подтверждения стенда";
КонецЕсли;
ПодготовкаПодтверждена85 = Ложь;
Статус85 = "Соединения нет. Введите seed run_id, подтвердите свой readonly стенд и нажмите одну кнопку.";
Остановка85 = Ложь;
Исключение
Остановка85 = Истина;
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Процедура ПринятьРезультат85(Кандидат)
Если ТипЗнч(Кандидат) <> Тип("ФиксированнаяСтруктура")
Или ТипЗнч(Кандидат.До) <> Тип("Число") Или Кандидат.До <> 1500
Или ТипЗнч(Кандидат.После) <> Тип("Число") Или Кандидат.После <> 1500
Или ТипЗнч(Кандидат.Прогноз) <> Тип("Число") Или ТипЗнч(Кандидат.Выбранная) <> Тип("Число")
Или Не ((Кандидат.Прогноз = 1600 И Кандидат.Выбранная = 200) Или (Кандидат.Прогноз = 1500 И Кандидат.Выбранная = 750)) Тогда
ВызватьИсключение "Не получен полный собственный проверенный результат";
КонецЕсли;
ВнешнийИтог85 = Кандидат.После;
ПредварительныйИтог85 = Кандидат.Прогноз;
ВыбраннаяСумма85 = Кандидат.Выбранная;
Статус85 = "Внешний источник1500 не изменился. Прогноз только в памяти: " + Строка(Кандидат.Прогноз);
Для Каждого ИмяПоля Из СтрРазделить("ВнешнийИтог85,ПредварительныйИтог85,ВыбраннаяСумма85,Статус85", ",") Цикл
Поле = ЭлементыФормы[ИмяПоля];
Если Не Поле.ТолькоПросмотр Тогда
ВызватьИсключение "Нарушен readonly режим собственного вывода";
КонецЕсли;
КонецЦикла;
Если ЭлементыФормы.ВнешнийИтог85.Значение <> ВнешнийИтог85
Или ЭлементыФормы.ПредварительныйИтог85.Значение <> ПредварительныйИтог85
Или ЭлементыФормы.ВыбраннаяСумма85.Значение <> ВыбраннаяСумма85 Или ЭлементыФормы.Статус85.Значение <> Статус85 Тогда
ВызватьИсключение "Фактические связанные поля формы не совпали с результатом";
КонецЕсли;
КонецПроцедуры
Процедура Ссылки85(Элемент)
Попытка
Если Остановка85 <> Ложь Или ТипЗнч(Элемент) <> Тип("Кнопка") Или Элемент <> ЭлементыФормы.Ссылки85
Или ПодготовкаПодтверждена85 <> Истина Или ЭлементыФормы.ПодготовкаПодтверждена85.Значение <> Истина Тогда
ВызватьИсключение "Нажмите свою кнопку только после ручного подтверждения собственного readonly стенда";
КонецЕсли;
Кандидат = УчебныеSQLСсылки85.ВыполнитьСсылки85(МеткаСтенда85);
ПринятьРезультат85(Кандидат);
Исключение
Остановка85 = Истина;
Статус85 = "СТОП. Проверка не завершена; старые числа не являются новым успешным результатом. Не повторяйте автоматически.";
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
Процедура Ключи85(Элемент)
Попытка
Если Остановка85 <> Ложь Или ТипЗнч(Элемент) <> Тип("Кнопка") Или Элемент <> ЭлементыФормы.Ключи85
Или ПодготовкаПодтверждена85 <> Истина Или ЭлементыФормы.ПодготовкаПодтверждена85.Значение <> Истина Тогда
ВызватьИсключение "Нажмите свою кнопку только после ручного подтверждения собственного readonly стенда";
КонецЕсли;
Кандидат = УчебныеSQLКлючи85.ВыполнитьКлючи85(МеткаСтенда85);
ПринятьРезультат85(Кандидат);
Исключение
Остановка85 = Истина;
Статус85 = "СТОП. Проверка не завершена; старые числа не являются новым успешным результатом. Не повторяйте автоматически.";
ВызватьИсключение;
КонецПопытки;
КонецПроцедурыМодуль обычного приложения: только открыть панель без SQL
Своя CF → Модуль обычного приложения целиком
Только толстый обычный клиент собственной файловой CF.
Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентОбычноеПриложение Тогда
УчебныеSQLСсылки85.ПроверитьСтенд85();
Форма = ПолучитьОбщуюФорму("ПроверкаSQL85");
Если Форма = Неопределено Или Форма.ВладелецФормы <> Неопределено Тогда
ВызватьИсключение "Не получена самостоятельная собственная SQL-панель";
КонецЕсли;
Форма.Открыть();
#Иначе
ВызватьИсключение "Нужен собственный толстый обычный клиент";
#КонецЕсли
КонецПроцедурыSQL только для ручной подготовки нового собственного стенда
Будущий собственный SSMS/admin, localhost GUIDE85SQLVM default instance, master→новая база
Не BSL; отдельный ручной setup, здесь не выполнялся.
USE [master];
IF DB_NAME() <> N'master' THROW 51000, N'Only master on the own new local instance', 1;
IF ISNULL(CONVERT(nvarchar(128), SERVERPROPERTY('MachineName')), N'') <> N'GUIDE85SQLVM'
OR SERVERPROPERTY('InstanceName') IS NOT NULL THROW 51001, N'Only the own VM default instance', 1;
IF DB_ID(N'Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c') IS NOT NULL THROW 51002, N'Existing database is not replaced', 1;
IF SUSER_ID(N'GUIDE85SQLVM\Guide85Read') IS NOT NULL THROW 51003, N'Existing login is not changed', 1;
CREATE DATABASE [Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c];
EXEC(N'
USE [Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c];
IF DB_NAME() <> N''Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c'' THROW 51004, N''Only the own new database'', 1;
CREATE TABLE dbo.object_lots (
item_id nvarchar(36) NOT NULL PRIMARY KEY,
run_id nvarchar(36) NOT NULL,
title nvarchar(40) NOT NULL,
amount numeric(9,0) NOT NULL CHECK (amount >= 0)
);
CREATE TABLE dbo.row_lots (
run_id nvarchar(36) NOT NULL,
line_no numeric(9,0) NOT NULL CHECK (line_no > 0),
title nvarchar(40) NOT NULL,
amount numeric(9,0) NOT NULL CHECK (amount >= 0),
PRIMARY KEY (run_id, line_no)
);
DECLARE @run nvarchar(36) = LOWER(CONVERT(nvarchar(36), NEWID()));
INSERT INTO dbo.object_lots (item_id, run_id, title, amount) VALUES
(LOWER(CONVERT(nvarchar(36), NEWID())), @run, N''A'', 500),
(LOWER(CONVERT(nvarchar(36), NEWID())), @run, N''B'', 750),
(LOWER(CONVERT(nvarchar(36), NEWID())), @run, N''C'', 250);
INSERT INTO dbo.row_lots (run_id, line_no, title, amount) VALUES
(@run, 1, N''A'', 500), (@run, 2, N''B'', 750), (@run, 3, N''C'', 250);
CREATE LOGIN [GUIDE85SQLVM\Guide85Read] FROM WINDOWS
WITH DEFAULT_DATABASE = [Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c];
CREATE USER [Guide85Read] FOR LOGIN [GUIDE85SQLVM\Guide85Read];
GRANT CONNECT TO [Guide85Read];
GRANT SELECT ON OBJECT::dbo.object_lots TO [Guide85Read];
GRANT SELECT ON OBJECT::dbo.row_lots TO [Guide85Read];
DENY INSERT, UPDATE, DELETE ON OBJECT::dbo.object_lots TO [Guide85Read];
DENY INSERT, UPDATE, DELETE ON OBJECT::dbo.row_lots TO [Guide85Read];
SELECT DB_NAME() AS own_database, @run AS own_run_id;
SELECT item_id, run_id, title, amount FROM dbo.object_lots;
SELECT run_id, line_no, title, amount FROM dbo.row_lots;
');SQL только чтение: вручную под Windows Guide85Read
Будущий собственный SSMS под GUIDE85SQLVM\Guide85Read → только учебная новая база
Не BSL; проверка настоящих прав и1500, здесь не выполнялась.
USE [Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c];
IF DB_NAME() <> N'Guide85Read_2871b8c9bf2c44f792ff6d32793e0e4c'
OR SYSTEM_USER <> N'GUIDE85SQLVM\Guide85Read' THROW 51100, N'Only the own readonly Windows login', 1;
IF ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 1) <> 0
OR ISNULL(IS_MEMBER(N'db_owner'), 1) <> 0
OR ISNULL(IS_MEMBER(N'db_datawriter'), 1) <> 0
OR ISNULL(IS_MEMBER(N'db_ddladmin'), 1) <> 0 THROW 51101, N'No admin or writer role for readonly login', 1;
IF ISNULL(HAS_PERMS_BY_NAME(N'dbo.object_lots', N'OBJECT', N'SELECT'), 0) <> 1
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.row_lots', N'OBJECT', N'SELECT'), 0) <> 1
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.object_lots', N'OBJECT', N'INSERT'), 1) <> 0
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.object_lots', N'OBJECT', N'UPDATE'), 1) <> 0
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.object_lots', N'OBJECT', N'DELETE'), 1) <> 0
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.row_lots', N'OBJECT', N'INSERT'), 1) <> 0
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.row_lots', N'OBJECT', N'UPDATE'), 1) <> 0
OR ISNULL(HAS_PERMS_BY_NAME(N'dbo.row_lots', N'OBJECT', N'DELETE'), 1) <> 0 THROW 51102, N'Own table permissions must be readonly', 1;
SELECT DB_NAME() AS own_database, SYSTEM_USER AS own_windows_login;
SELECT item_id, run_id, title, amount FROM dbo.object_lots;
SELECT run_id, line_no, title, amount FROM dbo.row_lots;
SELECT COUNT(*) AS own_count, SUM(amount) AS own_total FROM dbo.object_lots;
SELECT COUNT(*) AS own_count, SUM(amount) AS own_total FROM dbo.row_lots;Разбор по шагам
Native СоздатьНаборЗаписей возвращает настоящий пустой набор собственной необъектной таблицы. Прочитать получает все три строки; прямого конструктора/unknown source нет.
Для Каждого получает настоящую запись набора. Все четыре readonly-поля сравниваются с независимым полным запросом; итог отдельно суммируется1500.
Метка и Номер собственного B берутся из фактического источника. Структура содержит ровно эти имена и реальные типы; СоздатьКлючЗаписи формирует настоящий key собственной таблицы.
Пустой() отличает настоящий непустой ключ. Его actual поля сопоставляются с конкретной записью набора и дают выбранную сумму750, не просто равенство абстрактных ключей.
Поиск D возвращает native ПустойКлюч. Его Пустой()=Истина завершает ветвь; чтения несуществующей DB-строки нет.
После выбора и отрицательной ветви повторный полный запрос подтверждает каждое исходное поле и неизменный1500. Все записи набора только читаются.
Как проверить результат
На собственном будущем стенде: обе SQL таблицы имеют три строки и сумму1500 до и после, полные поля и PK совпадают; SSMS readonly-проверка прав выполняется отдельно.
Для Ссылки ожидается memory-прогноз1600 и отдельный draft200. Для Ключи actual выбранный B750, отсутствующий D не читается и не создается.
В отдельном собственном учебном варианте измените поле/число строк/DSN/права или снимите подтверждение: процедура должна отказать без Write/Delete. Не проверяйте отказ на рабочих данных.
Настоящая доставка кнопки, открытие формы, SQL драйвер и runtime здесь не проверялись; callbacks вручную не вызывайте.
Ожидаемый результат
Полный источник остается1500. Новичок различает объектную ссылку, составной ключ необъектной записи и несохраненный объект памяти.
На что обратить внимание
Только документация будущего самостоятельного запуска; реальные VM/SQL/ODBC/CF/1С/сеть/изменения прав здесь не выполнялись. Static native проверки не являются компиляцией или runtime.
Непустая ссылка не гарантирует существование произвольной строки. FindByField при дублях возвращает только один объект; уникальность собственного PK сначала проверяется полным источником.
Имя DSN, UUID каталога и run_id не доказывают адрес, авторизацию или владение чужой базой. Изоляцию обеспечивает новая собственная VM/SQL/CF, readonly login и ручная проверка настоящих параметров/прав.
Copy не записывает объект по native контракту. Значения key/Ссылка новой копии и draft намеренно не угадываются; SQL Write/Delete и RecordManager здесь отсутствуют.
МенеджерЗаписи исключен: его generic поля в installed prop9217 readonly, допустимый путь задания key для Read не подтвержден. GetObject4301 с неполным объявленным returnType не используется; это не утверждение runtime-дефекта.
Снимки не блокируют параллельные изменения и не доказывают атомарность. Только один собственный пользователь/сеанс, никаких чужих процессов и автоматических повторов после ошибки.
Последовательный подготовительный SQL script не обещает общего rollback CREATE DATABASE/login/seed. После частичного отказа не повторяйте и не удаляйте автоматически.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.