НастройкиИсторииДанных.По умолчанию

Конструкторы

Создает новый экземпляр объекта.

Синтаксис

Новый НастройкиИсторииДанных()
Полное описание, параметры и ограничения

НастройкиИсторииДанных.По умолчанию

НастройкиИсторииДанных (DataHistorySettings)

По умолчанию

Доступен, начиная с версии 8.3.11.

Синтаксис:
Новый НастройкиИсторииДанных()
Описание:

Создает новый экземпляр объекта.

Использование в версии:

Доступен, начиная с версии 8.3.11.

Методическая информация

Практические примеры

Создать две реальные собственные версии количества, сравнить, импортировать один снимок и удалить только его

Настройки применяются к единственному собственному учебному справочнику. Реальные записи1→3 дают реальные номера; импорт7 не записывает текущий объект, восстановленный черновик не сохраняется.

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

Полный клиентский общий модуль УчебнаяИстория85 в толстом обычном клиенте отдельной файловой ИБ УчебнаяИсторияВерсий85. Только будущий ручной учебный запуск; фактических запусков0.

Подготовка

  1. Только НОВАЯ отдельная файловая учебная ИБ8.5.1.1343, конфигурация УчебнаяИсторияВерсий85, совместимостьНеИспользовать, основной режим запуска ОбычноеПриложение. Запускайте толстый клиент обычного приложения, не серверную рабочую ИБ. Native8.5 ограничивает ИнформацияОЗаписиВерсииИсторииДанных и ее свойства толстым клиентом; обработчик на кластерном сервере этим уроком не обещается.

  2. Единственный справочник УчебныеТовары: неиерархический, без владельцев и табличных частей, НаименованиеСтрока50, КодСтрока9; реквизиты МаркерUUID(Строка36) и Количество(Число9,0,неотрицательное). Других справочников, документов, регистров, разделителей, планов обмена, подписок, фоновых заданий и расширений нет. Изначально0элементов.

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

  4. Заранее создайте своего настоящего учебного пользователя с административными правами для этого пустого стенда и именем/полным именем без секретов. Войдите им; импорт берет его фактический UUID и имена через ПользователиИнформационнойБазы, а не вымышляет автора.

  5. Создайте собственную обработку с обычной формой. Строковые реквизиты Операция и UUIDОпыта, булевый ПодтвержденСвойОпыт; кнопку Выполнить свяжите с обработчиком Выполнить(Кнопка). Вставьте полный дополнительный модуль формы. UUIDОпыта один раз получите выражением Строка(Новый УникальныйИдентификатор), сохраните в своем журнале опыта.

  6. В модуль менеджера справочника УчебныеТовары вставьте полный дополнительный модуль с обработчиком ОбработкаПослеЗаписиВерсийИсторииДанных и его собственными проверками. Он не обращается к клиентскому общему модулю. Не вызывайте событие вручную с самодельным объектом: настоящую коллекцию предоставляет платформа.

  7. Никакие изменения схемы/F7/объекты/настройки/версии/диалоги здесь не создавались и не запускались. Это полный авторский план самостоятельного будущего запуска только в своем стенде; runtimeVerifiedScenarios=0.

  8. Порядок отдельных операций: СоздатьДвеВерсии85, Прочитать85, Комментарий85. Для очереди по выбору СнятьПервуюИзОчереди85, затем ОбработатьОчередь85. Для миграционного снимка ИмпортироватьСнимок85, затем только явно УдалитьТолькоИмпорт85. Нельзя удалять все версии/метаданные или пропускать данные в API удаления.

  9. Только один оператор/сеанс изменяет опыт. После исключения нет автоматического повторения/отката/очистки: сначала прочитайте реальные оставшиеся записи. Повторное СоздатьДвеВерсии85 отклоняет даже частично созданный собственный объект.

Функция Выполнить85(Операция, UUIDОпыта, Подтверждение) Экспорт
    ПроверитьОпыт85(UUIDОпыта, Подтверждение);
    Если СтрРазделить("СоздатьДвеВерсии85,Прочитать85,Комментарий85,ИмпортироватьСнимок85,СнятьПервуюИзОчереди85,ОбработатьОчередь85,УдалитьТолькоИмпорт85", ",").Найти(Операция) = Неопределено Тогда
        ВызватьИсключение "Неизвестная отдельная учебная операция истории";
    КонецЕсли;
    Ссылка = НайтиСвойЕдинственныйОбъект85(UUIDОпыта, Истина);
    Если Операция = "СоздатьДвеВерсии85" Тогда
        Если Ссылка <> Неопределено Тогда
            ВызватьИсключение "Нельзя перезаписывать уже начатый собственный опыт";
        КонецЕсли;
        Настройки = Новый НастройкиИсторииДанных;
        Настройки.Использование = Истина;
        Поля = Новый Соответствие;
        Поля.Вставить("МаркерUUID", Истина);
        Поля.Вставить("Количество", Истина);
        Поля.Вставить("Наименование", Истина);
        Настройки.ИспользованиеПолей = Поля;
        ИсторияДанных.УстановитьНастройки(Метаданные.Справочники.УчебныеТовары, Настройки);
        ПрочитанныеНастройки = ИсторияДанных.ПолучитьНастройки(Метаданные.Справочники.УчебныеТовары);
        Если ПрочитанныеНастройки = Неопределено Или Не ПрочитанныеНастройки.Использование
            Или ПрочитанныеНастройки.ИспользованиеПолей.Получить("МаркерUUID") <> Истина
            Или ПрочитанныеНастройки.ИспользованиеПолей.Получить("Количество") <> Истина
            Или ПрочитанныеНастройки.ИспользованиеПолей.Получить("Наименование") <> Истина Тогда
            ВызватьИсключение "Применение own-профиля не подтверждено; остановитесь без записи данных";
        КонецЕсли;
        Свой = Справочники.УчебныеТовары.СоздатьЭлемент();
        Свой.МаркерUUID = UUIDОпыта;
        Свой.Наименование = "Уч85 " + UUIDОпыта;
        Свой.Количество = 1;
        Свой.ЗаписьИсторииДанных.КомментарийВерсии = "own-history85-first:" + UUIDОпыта;
        Свой.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Истина;
        Свой.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Ложь;
        Свой.Записать();
        Ссылка = Свой.Ссылка;
        Если ТранзакцияАктивна() Тогда
            ВызватьИсключение "Обновление истории не вызывается внутри транзакции";
        КонецЕсли;
        ИсторияДанных.ОбновитьИсторию(Ссылка, Ложь, Ложь);
        ПроверитьКоличествоВерсий85(ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта), 1);
        Свой = Ссылка.ПолучитьОбъект();
        Свой.Количество = 3;
        Свой.ЗаписьИсторииДанных.КомментарийВерсии = "own-history85-second:" + UUIDОпыта;
        Свой.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Истина;
        Свой.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Ложь;
        Свой.Записать();
        ИсторияДанных.ОбновитьИсторию(Ссылка, Ложь, Ложь);
        Версии = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
        ПроверитьКоличествоВерсий85(Версии, 2);
        Возврат СтрШаблон("Own-объектUUID=%1; фактические номера=%2,%3; количество1→3; очередь пока не обрабатывалась",
            Строка(Ссылка.УникальныйИдентификатор()), Версии[0].НомерВерсии, Версии[1].НомерВерсии);
    КонецЕсли;
    Если Ссылка = Неопределено Тогда
        ВызватьИсключение "Сначала создайте обе реальные own-версии";
    КонецЕсли;
    Версии = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
    Если Версии.Количество() < 2 Тогда
        ВызватьИсключение "Обе исходные собственные версии должны существовать";
    КонецЕсли;
    Первая = Версии[0].НомерВерсии;
    Вторая = Версии[1].НомерВерсии;
    Если Операция = "Прочитать85" Тогда
        Снимок1 = ИсторияДанных.ПолучитьДанныеВерсии(Ссылка, Первая);
        Снимок2 = ИсторияДанных.ПолучитьДанныеВерсии(Ссылка, Вторая);
        Если Снимок1.Количество <> 1 Или Снимок2.Количество <> 3 Тогда
            ВызватьИсключение "Исторический собственный переход1→3 не подтвержден";
        КонецЕсли;
        МетаданныеВерсии = ИсторияДанных.ПолучитьМетаданные(Ссылка, Вторая);
        Если ТипЗнч(МетаданныеВерсии) <> Тип("Структура") Или Не МетаданныеВерсии.Свойство("Поля")
            Или Не МетаданныеВерсии.Поля.Свойство("Количество") Тогда
            ВызватьИсключение "Фактические метаданные версии не содержат ожидаемое own-поле";
        КонецЕсли;
        Различия = ИсторияДанных.ПолучитьРазличияВерсий(Ссылка, Вторая, Первая);
        Если Различия = Неопределено Или Не Различия.Свойство("Количество") Тогда
            ВызватьИсключение "Own-различия количества не найдены";
        КонецЕсли;
        Изменение = Различия.Количество;
        Если Изменение.ЗначениеДоИзменения <> 1 Или Изменение.ЗначениеПослеИзменения <> 3 Тогда
            ВызватьИсключение "Native before/after перепутаны или история изменилась";
        КонецЕсли;
        Черновик = ИсторияДанных.СформироватьПоВерсии(Ссылка, Первая);
        Если Черновик.МаркерUUID <> UUIDОпыта Или Черновик.Количество <> 1
            Или Ссылка.ПолучитьОбъект().Количество <> 3 Тогда
            ВызватьИсключение "Не подтвержден отдельный незаписанный own-черновик первой версии";
        КонецЕсли;
        Возврат "Снимки1→3 и различия подтверждены. Черновик содержит1, текущий объект3: восстановление в базу не выполнялось";
    ИначеЕсли Операция = "Комментарий85" Тогда
        ИсторияДанных.ЗаписатьКомментарий(Ссылка, Вторая, "own-history85-checked:" + UUIDОпыта);
        После = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
        Если После[1].НомерВерсии <> Вторая Или После[1].Комментарий <> "own-history85-checked:" + UUIDОпыта Тогда
            ВызватьИсключение "Комментарий собственной фактической версии не подтвержден";
        КонецЕсли;
        Возврат "Изменен только комментарий своей второй версии; данные и номер не менялись";
    ИначеЕсли Операция = "ИмпортироватьСнимок85" Тогда
        ПроверитьКоличествоВерсий85(Версии, 2);
        Пользователь = ПользователиИнформационнойБазы.ТекущийПользователь();
        Если Пользователь = Неопределено Тогда
            ВызватьИсключение "Заранее войдите своим реальным учебным пользователем";
        КонецЕсли;
        ЧерновикИмпорта = Ссылка.ПолучитьОбъект();
        ЧерновикИмпорта.Количество = 7;
        ИсторияДанных.ЗаписатьВерсию(ЧерновикИмпорта, ТекущаяДата(), Пользователь.УникальныйИдентификатор,
            Пользователь.Имя, Пользователь.ПолноеИмя, ВидИзмененияДанных.Изменение,
            "own-history85-import:" + UUIDОпыта, "", Неопределено);
        После = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
        ПроверитьКоличествоВерсий85(После, 3);
        Если После[2].Комментарий <> "own-history85-import:" + UUIDОпыта
            Или ИсторияДанных.ПолучитьДанныеВерсии(Ссылка, После[2].НомерВерсии).Количество <> 7
            Или Ссылка.ПолучитьОбъект().Количество <> 3 Тогда
            ВызватьИсключение "Не подтвержден импорт только снимка, без записи текущего объекта";
        КонецЕсли;
        Возврат "Создан собственный исторический импорт количества7; текущий объект остался3. НомерИмпорта=" + Строка(После[2].НомерВерсии);
    ИначеЕсли Операция = "СнятьПервуюИзОчереди85" Тогда
        ИсторияДанных.УдалитьИзОбработкиПослеЗаписиВерсий(Ссылка, Первая);
        После = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
        Если После[0].НомерВерсии <> Первая Тогда
            ВызватьИсключение "Снятие из обработки не должно удалять собственную историческую версию";
        КонецЕсли;
        Возврат "Из обработки снята только первая own-версия; сама версия истории сохранена. Записей очереди API не перечисляет";
    ИначеЕсли Операция = "ОбработатьОчередь85" Тогда
        ИсторияДанных.ВыполнитьОбработкуПослеЗаписиВерсий(Ссылка);
        Возврат "Обработка запрошена только по своему объекту. Проверьте вывод собственного обработчика; отсутствие вывода не доказывает существование очереди";
    КонецЕсли;
    ПроверитьКоличествоВерсий85(Версии, 3);
    Импорт = Версии[2];
    Если Импорт.Комментарий <> "own-history85-import:" + UUIDОпыта
        Или ИсторияДанных.ПолучитьДанныеВерсии(Ссылка, Импорт.НомерВерсии).Количество <> 7 Тогда
        ВызватьИсключение "Удалять разрешено только свою подтвержденную импортную версию";
    КонецЕсли;
    ИсторияДанных.УдалитьВерсии(Ссылка, Импорт.НомерВерсии, Импорт.НомерВерсии);
    После = ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта);
    ПроверитьКоличествоВерсий85(После, 2);
    Если После[0].НомерВерсии <> Первая Или После[1].НомерВерсии <> Вторая
        Или Ссылка.ПолучитьОбъект().Количество <> 3 Тогда
        ВызватьИсключение "Удаление только собственного импортного снимка не подтверждено";
    КонецЕсли;
    Возврат "Удалена только одна own-импортная версия по точному замкнутому интервалу; обе исходные и текущий объект3 сохранены";
КонецФункции

Процедура ПроверитьОпыт85(UUIDОпыта, Подтверждение)
    ПроверитьСвойПустойСтенд();
    Если ТипЗнч(Подтверждение) <> Тип("Булево") Или Не Подтверждение Тогда
        ВызватьИсключение "Добровольно подтвердите только собственную новую файловую учебную ИБ";
    КонецЕсли;
    Если ТипЗнч(UUIDОпыта) <> Тип("Строка") Тогда
        ВызватьИсключение "UUIDОпыта должен быть строкой";
    КонецЕсли;
    UUID = Новый УникальныйИдентификатор(UUIDОпыта);
    Если UUIDОпыта <> Строка(UUID) Или UUIDОпыта = "00000000-0000-0000-0000-000000000000" Тогда
        ВызватьИсключение "Нужен канонический собственный ненулевой UUID опыта";
    КонецЕсли;
КонецПроцедуры

Функция НайтиСвойЕдинственныйОбъект85(UUIDОпыта, РазрешеноОтсутствие = Ложь)
    Выборка = Справочники.УчебныеТовары.Выбрать();
    Свой = Неопределено;
    Пока Выборка.Следующий() Цикл
        Если Свой <> Неопределено Или Выборка.МаркерUUID <> UUIDОпыта Или Выборка.ПометкаУдаления Тогда
            ВызватьИсключение "Разрешен только один собственный UUID-элемент, без чужих данных";
        КонецЕсли;
        Свой = Выборка.Ссылка;
    КонецЦикла;
    Если Свой = Неопределено И Не РазрешеноОтсутствие Тогда
        ВызватьИсключение "Сначала создайте реальный собственный элемент командой СоздатьДвеВерсии85";
    КонецЕсли;
    Возврат Свой;
КонецФункции

Функция ПолучитьСвоиВерсии85(Ссылка, UUIDОпыта)
    Если НайтиСвойЕдинственныйОбъект85(UUIDОпыта) <> Ссылка Тогда
        ВызватьИсключение "Подменена ссылка на собственный единственный элемент";
    КонецЕсли;
    Отбор = Новый Структура("Данные", Ссылка);
    Версии = ИсторияДанных.ВыбратьВерсии(Отбор,
        "Данные,НомерВерсии,Дата,Пользователь,ИмяПользователя,ПолноеИмяПользователя,ВидИзмененияДанных,Комментарий,Транзакция,Узел",
        "НомерВерсии Возр", 4);
    Если ТипЗнч(Версии) <> Тип("ТаблицаЗначений") Или Версии.Количество() > 3 Тогда
        ВызватьИсключение "Свой новый опыт допускает не более трех подтвержденных версий";
    КонецЕсли;
    Для Каждого Версия Из Версии Цикл
        Если Версия.Данные <> Ссылка Или Версия.НомерВерсии <= 0 Тогда
            ВызватьИсключение "Получена чужая ссылка или недопустимый номер версии";
        КонецЕсли;
        Снимок = ИсторияДанных.ПолучитьДанныеВерсии(Ссылка, Версия.НомерВерсии);
        Если ТипЗнч(Снимок) <> Тип("Структура") Или Не Снимок.Свойство("МаркерUUID")
            Или Снимок.МаркерUUID <> UUIDОпыта Тогда
            ВызватьИсключение "Исторические данные не принадлежат нашему реальному UUID-объекту";
        КонецЕсли;
    КонецЦикла;
    Возврат Версии;
КонецФункции

Процедура ПроверитьКоличествоВерсий85(Версии, Ожидание)
    Если Версии.Количество() <> Ожидание Тогда
        ВызватьИсключение "Фактическое число own-версий отличается; повторять запись автоматически нельзя";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 1 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.Справочники[0].Имя <> "УчебныеТовары"
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Количество() <> 2
        Или Метаданные.Справочники.УчебныеТовары.ТабличныеЧасти.Количество() <> 0
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Найти("МаркерUUID") = Неопределено
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Найти("Количество") = Неопределено Тогда
        ВызватьИсключение "Нужен только свой единственный учебный справочник с двумя точными реквизитами";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
Модуль собственной обычной формы обработки

Обычная форма толстого клиента, три реквизита и кнопка из подготовки.

Обычная форма толстого клиента, три реквизита и кнопка из подготовки.

Процедура Выполнить(Кнопка)
    Попытка
        Ответ = УчебнаяИстория85.Выполнить85(Операция, UUIDОпыта, ПодтвержденСвойОпыт);
    Исключение
        Сообщить("Own-операция не подтверждена. Не повторяйте запись автоматически; сначала прочитайте фактическое состояние собственного опыта");
        Возврат;
    КонецПопытки;
    Сообщить(Ответ);
КонецПроцедуры
Полный модуль менеджера собственного справочника УчебныеТовары

Полный реальный обработчик платформенного события со всеми собственными проверками, файловая ИБ/толстый клиент; на сервере кластера не проверялся.

Полный реальный обработчик платформенного события со всеми собственными проверками, файловая ИБ/толстый клиент; на сервере кластера не проверялся.

Процедура ОбработкаПослеЗаписиВерсийИсторииДанных(ИнформацияОЗаписиВерсий)
    Попытка
        Количество = ИнформацияОЗаписиВерсий.Количество();
        Если Количество = 0 Тогда
            Сообщить("Own-пакет истории пуст; ничего не записывается");
            Возврат;
        КонецЕсли;
        Для Индекс = 0 По Количество - 1 Цикл
            Информация = ИнформацияОЗаписиВерсий.Получить(Индекс);
            Если ИнформацияОЗаписиВерсий.Индекс(Информация) <> Индекс Тогда
                Сообщить("Own-индекс пакета не подтвержден");
                Возврат;
            КонецЕсли;
            Если ТипЗнч(Информация.Данные) <> Тип("СправочникСсылка.УчебныеТовары") Тогда
                Продолжить;
            КонецЕсли;
            Объект = Информация.Данные.ПолучитьОбъект();
            UUIDОпыта = Объект.МаркерUUID;
            ПроверитьСвойПакет85(UUIDОпыта, Информация.Данные);
            ДанныеВерсии = Информация.ДанныеВерсии;
            Различия = Информация.РазличияВерсий;
            Если ДанныеВерсии = Неопределено Тогда
                Сообщить("Own-пакет содержит удаление: данные версии отсутствуют; поле количества не читается");
                Продолжить;
            КонецЕсли;
            Если Не ДанныеВерсии.Свойство("МаркерUUID") Или ДанныеВерсии.МаркерUUID <> UUIDОпыта Тогда
                Сообщить("Чужой исторический маркер: содержимое не выводится");
                Продолжить;
            КонецЕсли;
            Сообщить(СтрШаблон("Own-пакет: индекс=%1/%2; версия=%3; дата=%4; вид=%5; количество=%6",
                Индекс, Количество, Информация.НомерВерсии, Информация.Дата, Информация.ВидИзмененияДанных, ДанныеВерсии.Количество));
            Сообщить(СтрШаблон("Автор: UUID=%1; имя=%2; полное=%3; комментарий=%4; транзакция задана=%5; узел задан=%6; различия заданы=%7",
                Информация.Пользователь, Информация.ИмяПользователя, Информация.ПолноеИмяПользователя, Информация.Комментарий,
                Не ПустаяСтрока(Информация.Транзакция), ЗначениеЗаполнено(Информация.Узел), Различия <> Неопределено));
        КонецЦикла;
    Исключение
        Сообщить("Собственный пакет не подтвержден; исключение перехвачено, запись объекта или повторная постановка не выполнялись");
    КонецПопытки;
КонецПроцедуры

Процедура ПроверитьСвойПакет85(UUIDОпыта, Ссылка)
    ПроверитьСвойПустойСтенд();
    Если ТипЗнч(UUIDОпыта) <> Тип("Строка") Тогда
        ВызватьИсключение "Маркер собственного объекта должен быть строкой";
    КонецЕсли;
    UUID = Новый УникальныйИдентификатор(UUIDОпыта);
    Если UUIDОпыта <> Строка(UUID) Или UUIDОпыта = "00000000-0000-0000-0000-000000000000" Тогда
        ВызватьИсключение "В собственном пакете нужен канонический ненулевой UUID";
    КонецЕсли;
    Выборка = Справочники.УчебныеТовары.Выбрать();
    Найден = Ложь;
    Пока Выборка.Следующий() Цикл
        Если Найден Или Выборка.Ссылка <> Ссылка Или Выборка.МаркерUUID <> UUIDОпыта Или Выборка.ПометкаУдаления Тогда
            ВызватьИсключение "Пакет не относится к собственному единственному элементу";
        КонецЕсли;
        Найден = Истина;
    КонецЦикла;
    Если Не Найден Тогда
        ВызватьИсключение "Собственный исторический объект отсутствует";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 1 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.Справочники[0].Имя <> "УчебныеТовары"
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Количество() <> 2
        Или Метаданные.Справочники.УчебныеТовары.ТабличныеЧасти.Количество() <> 0
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Найти("МаркерUUID") = Неопределено
        Или Метаданные.Справочники.УчебныеТовары.Реквизиты.Найти("Количество") = Неопределено Тогда
        ВызватьИсключение "Нужен только свой единственный учебный справочник с двумя точными реквизитами";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

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

  1. UUIDОпыта — признак принадлежности, а фактический UUID ссылки — другой идентификатор. Новый объект создается фабрикой только при0элементов; дальнейшие выборки допускают только этот один точныйUUID-элемент.

  2. УстановитьНастройки применяет явный профиль к своим метаданным. ПолучитьНастройки вызывается впервые после применения; в native указано кэширование результата. Если реальные поля не подтвердились, запись объекта запрещена.

  3. Первая запись имеетКоличество1, вторая3. ЗаписьИсторииДанных задает собственные комментарии, очередьПослеЗаписи=Истина и немедленное фоновое обновление=Ложь. ОбновитьИсторию(свояСсылка,Ложь,Ложь) вызывается после записи, вне транзакции и без автоматической обработки/20-дневной очистки.

  4. ВыбратьВерсии отбирает обязательноДанные=свояСсылка; сортировкаНомерВерсииВозр, лимит4 позволяет обнаружить лишнюю четвертую версию. Номера берутся из настоящей таблицы, не считаются равными1/2по умолчанию.

  5. ПолучитьДанныеВерсии даетСтруктура. ПолучитьМетаданные дает реальные поля версии. ПолучитьРазличияВерсий принимает после,затемдо; проверяются nativeЗначениеДоИзменения1 иЗначениеПослеИзменения3.

  6. СформироватьПоВерсии возвращает отдельный объект сКоличество1. МетодЗаписать у него не вызывается: текущие данные в ИБ остаются3. Создание черновика не равно восстановлению данных базы.

  7. ЗаписатьКомментарий меняет комментарий только своей второй фактической версии. Повторный отбор подтверждает номер/новыйкомментарий.

  8. ЗаписатьВерсию предназначен для переноса истории. В собственном незаписанном черновике установленоКоличество7, автор/date получены реально. Транзакция="", узелНеопределено: никакой случайный идентификатор транзакции/чужой узел не подставляется.

  9. УдалитьТолькоИмпорт85 требует ровно3версии, собственный импортный комментарий и количество7. УдалитьВерсии получает свою ссылку и одинаковый точный номер начала/конца. Повторно подтверждены обе исходные и текущие данные3.

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

  1. После СоздатьДвеВерсии85 ровно2фактические версии одного объекта; обе содержат точныйМаркерUUID иКоличество1/3.

  2. Чтение подтверждает diff1→3 и отдельный черновик1 при текущем объекте3; ни одна команда чтения не записывает черновик.

  3. Импорт дает третью собственную версию7, но текущий объект остается3; после отдельного удаления остаются ровно исходные2с теми же номерами.

  4. ДругойUUID, дополнительныйэлемент, дополнительнаяверсия, активная транзакция или чужие метаданные вызывают отказ до соответствующего изменения.

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

Полный собственный жизненный цикл истории с реальными объектами/автором/номерами, самостоятельным сравнением и адресным удалением только собственного импортного снимка.

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

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

  2. Настройки могут кэшироваться; повторное чтение не следует выдавать за независимую runtime-гарантию применения в другом сеансе. Проверки здесь статические.

  3. ПолучитьДанныеВерсии не содержит независимые разделители; в собственной схеме разделителей нет. Нельзя переносить структуру в рабочую ИБ с разделением без отдельного проектирования.

  4. Импорт истории и запись текущего объекта различаются. ЗаписатьВерсию не вызывает запись текущих данных; вымышленный UUID автора или произвольная строка транзакции недопустимы.

  5. После частичной ошибки нельзя повторять создание/импорт автоматически; номер версии не является глобальным идентификатором между объектами.

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

Загрузить свою цену, проверить две реальные версии и удалить только собственную запись

Полный процесс250→300→отсутствие: собственный UUID, реально вызванный обработчик заполнения, раздельная проверка подготовленного комментария и сохраненной истории.

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

Два полных собственных серверных модуля и реальный модуль набора записей в новой отдельной файловойИБ8.5.1.1343; runtime не выполнялся.

Подготовка

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

  2. Создайте единственного своего пользователя guide-record-cycle. В новой собственной конфигурации создайте учебную роль с полными правами только этого одноразового стенда и назначьте ее только этому пользователю. Нужны Администрирование, чтение/изменение собственных регистров, добавление/чтение собственного документа и все права истории данных, включая точно названное в native право ЧтениеИсторииОтсутствующихДанных. Не включайте RLS. В эту ИБ не должны входить другие пользователи/сеансы.

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

  4. Регистры сведений → УчебныеЦены85: режим записи Независимый, периодичность Непериодический, история данных Использовать. Только измерение UUIDОпыта85: Строка, длина 36, индексировать, проверка заполнения ПоказыватьОшибку. Только ресурс Цена: Число, 15 цифр, 2 дробных, неотрицательное. Реквизитов нет. Для UUID/цены включите участие в истории данных.

  5. Документы → УчебнаяУстановкаЦен85: номер Строка, длина 40, автонумерация выключена, проведение Разрешить, запись движений при проведении ЗаписыватьВыбранные. Только реквизит UUIDОпыта85: УникальныйИдентификатор; табличных частей нет. История документа не используется. Модуль документа оставьте пустым: проведения/перепроведения здесь не вызываются.

  6. Регистры сведений → УчебныеСтрокиЦен85: ПодчинениеРегистратору, периодичность ВПределахСекунды, единственный регистратор УчебнаяУстановкаЦен85. История НеИспользовать. Измерения UUIDОпыта85: Строка, длина 36, и КодТовара: Строка, длина 10; ресурсы Количество: Число, 10 цифр, 0 дробных, неотрицательное; Цена: Число, 15 цифр, 2 дробных, неотрицательное; других реквизитов нет. В документе в Движения добавьте только этот регистр.

  7. В уроке цены в модуле набора записейУчебныеЦены85 вставьте полный вспомогательный блокОбработкаЗаполнения для цены; второй модуль пока пустой. В уроке строк заполните только модуль набора записейУчебныеСтрокиЦен85 его полным вспомогательным блоком. Оба обработчика имеют два настоящих аргумента и реально вызываются методомЗаполнить; вручную вызывать события не нужно. Остальные события этих модулей оставьте пустыми.

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

  9. Общие формы → УчебныйЦикл85: управляемая форма. Реквизиты UUIDОпыта85: Строка 36, UUIDДокумента85: Строка 36, Подтверждение85: Строка 100, Результат85: Строка 2048, ОшибкаЭтапа85: Булево (Ложь). Выведите UUID/подтверждение/результат полями, UUIDДокумента показывайте только для чтения. Открывайте форму через ВсеФункции/ФункцииДляТехническогоСпециалиста в своем толстом управляемом клиенте.

  10. Для первого урока модуль общей формы полностью PRICE_FORM. Создайте командыНовыйUUID85, СоздатьПервую85, ЗаписатьВторую85, ПрочитатьДве85, УдалитьСвою85, назначьте одноименные действия и выведите кнопки. UUID создается только клиентской командой, не подставляется из чужой базы.

  11. Между отдельными изменяющими командами вручную вводите точноеПодтверждение85: имя этапа без85, двоеточие и UUID. НапримерСоздатьПервую:вашUUID. Код очищает подтверждение до серверного вызова. ПрочитатьДве/ПрочитатьИтог не изменяют данные и подтверждения не требуют.

  12. Первый урок: НовыйUUID85 → СоздатьПервую → ЗаписатьВторую → ПрочитатьДве → УдалитьСвою. Не меняйте UUID между этапами. Начальные регистры/документы должны быть пусты; код полного регистра/истории отклоняет неизвестные строки/версии, а не фильтрует их из вида.

  13. НастройкиИсторииДанных включаются только для собственного нового регистра с 0 строк/0 версий; профиль UUID/Цена остается включенным после урока и намеренно не восстанавливается. ОбновитьИсторию вызывается только вне транзакции с конкретным своим КлючомЗаписи, Ложь/Ложь: нет общей обработки очереди, 20-дневной очистки или глобального обновления.

  14. По native справке ПолучитьНастройки кэширует результат первого обращения до изменения конфигурации/версии. В коде первый getter следует после установки своего начального профиля. Повторный getter не объявляется свежей проверкой чужого изменения настроек: изоляция стенда обязательна. Наличие нужных сохраненных версий, UUID, цен и комментариев доказывается отдельно настоящей выборкой истории и снимками, а не этим кэшем.

  15. Не запускайте фоновые обновления истории. Во всех собственных объектахОбновлятьИсториюСразуПослеЗаписи=Ложь, ВыполнятьОбработкуПослеЗаписиВерсии=Ложь. После каждой записи/удаления синхронный конкретныйОбновитьИсторию и независимая выборка подтверждают реальные версии. Номера не обещаются1/2/3, а берутся из фактических данных.

  16. Для второго урока после завершения первого замените только модуль общей формы на полный ROWS_FORM и команды наНовыйUUID85, СоздатьДокумент85, НеВыбирать85, ВыбратьДве85, Отключить85, Включить85, ИсправитьКнигу85, УдалитьРучку85, ПрочитатьИтог85. Закройте старую форму, обновите конфигурацию и откройте новую с пустыми реквизитами; создайте новыйUUID. Независимый регистр к этому моменту пуст, его история остается.

  17. Второй урок выполняйте вручную именноСоздатьДокумент → НеВыбирать → ВыбратьДве → Отключить → Включить → ИсправитьКнигу → УдалитьРучку → ПрочитатьИтог. UUIDДокумента появляется только после настоящегоСохранить/Записать документа и не выдумывается. Не меняйте возвращенный UUID, не подставляйте чужую ссылку.

  18. Все запросы контроля данных читают целиком только три фиксированных собственных источника. Один собственный документ, собственныйUUID/период/два кода/реальные номера строк должны совпадать. Эти проверки не заменяют изоляцию или блокировки конкурентной системы.

  19. После ошибки форма запрещает последующие/повторные команды. Запись/настройка могла уже завершиться; ничего автоматически не откатывается и не очищается. Вручную проверьте только этот учебныйUUID/ключ/документ и фактические версии в одноразовойИБ; не снимайте блокировку формы и не повторяйте изменения вслепую.

  20. Новых резервных копий/архивов урок не создает. Итоговые учебные данные/история намеренно остаются в собственной отдельнойИБ; удаление самойИБ/документа/всей истории не является частью кода.

Функция ВыполнитьЦену85(Этап, UUID, Подтверждение) Экспорт
    ПроверитьСвоюСхему85(UUID);
    Если СтрРазделить("СоздатьПервую,ЗаписатьВторую,ПрочитатьДве,УдалитьСвою", ",").Найти(Этап) = Неопределено Тогда
        ВызватьИсключение "Неизвестный ручной этап цены";
    КонецЕсли;
    Если Этап <> "ПрочитатьДве" И Подтверждение <> Этап + ":" + UUID Тогда
        ВызватьИсключение "Введите точное отдельное подтверждение Этап:UUID";
    КонецЕсли;
    Ключ = РегистрыСведений.УчебныеЦены85.СоздатьКлючЗаписи(Новый Структура("UUIDОпыта85", UUID));
    Если Этап = "СоздатьПервую" Тогда
        ПроверитьТолькоСвоюЦену85(UUID, 0, 0);
        Если ПолучитьСвоиДанные85("РегистрСведений.УчебныеСтрокиЦен85").Количество() <> 0
            Или ПолучитьСвоиДанные85("Документ.УчебнаяУстановкаЦен85").Количество() <> 0 Тогда
            ВызватьИсключение "Первая цена только в новом стенде без документов/подчиненных строк";
        КонецЕсли;
        ПроверитьСвоюИсторию85(Ключ, UUID, 0);
        Настройки = Новый НастройкиИсторииДанных;
        Настройки.Использование = Истина;
        Поля = Новый Соответствие;
        Поля.Вставить("UUIDОпыта85", Истина);
        Поля.Вставить("Цена", Истина);
        Настройки.ИспользованиеПолей = Поля;
        ИсторияДанных.УстановитьНастройки(Метаданные.РегистрыСведений.УчебныеЦены85, Настройки);
        ПроверитьСвойПрофильИстории85();
        Запись = РегистрыСведений.УчебныеЦены85.СоздатьМенеджерЗаписи();
        Запись.Заполнить(Новый Структура("UUIDОпыта85,Цена", UUID, 250));
        Если Запись.UUIDОпыта85 <> UUID Или Запись.Цена <> 250 Тогда
            ВызватьИсключение "Собственный обработчик заполнения менеджера не подтвердил UUID/цену250";
        КонецЕсли;
        Запись.УстановитьКомментарийВерсииИсторииДанных("price85-first:" + UUID);
        КомментарийДоЗаписи = Запись.ПолучитьКомментарийВерсииИсторииДанных();
        Если ТипЗнч(КомментарийДоЗаписи) <> Тип("Строка")
            Или КомментарийДоЗаписи <> "price85-first:" + UUID
            Или Запись.ЗаписьИсторииДанных.КомментарийВерсии <> КомментарийДоЗаписи Тогда
            ВызватьИсключение "Проверка подготовленного комментария совместимости не прошла; это еще не история базы";
        КонецЕсли;
        Запись.ЗаписьИсторииДанных.Отказ = Ложь;
        Запись.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Ложь;
        Запись.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Ложь;
        Запись.Записать(Ложь);
        ПроверитьТолькоСвоюЦену85(UUID, 1, 250);
        ИсторияДанных.ОбновитьИсторию(Ключ, Ложь, Ложь);
        Версии = ПроверитьСвоюИсторию85(Ключ, UUID, 1);
        Возврат "Первая цена250 сохранена. Фактический номер версии=" + Строка(Версии[0].НомерВерсии) + "; комментарий проверен по материализованной истории.";
    КонецЕсли;
    ПроверитьСвойПрофильИстории85();
    Если Этап = "ЗаписатьВторую" Тогда
        ПроверитьТолькоСвоюЦену85(UUID, 1, 250);
        ПроверитьСвоюИсторию85(Ключ, UUID, 1);
        Набор = РегистрыСведений.УчебныеЦены85.СоздатьНаборЗаписей();
        Набор.Отбор.UUIDОпыта85.Установить(UUID);
        Набор.Добавить();
        Набор.Заполнить(Новый Структура("UUIDОпыта85,Цена", UUID, 300));
        Если Набор.Количество() <> 1 Или Набор.Получить(0).UUIDОпыта85 <> UUID Или Набор.Получить(0).Цена <> 300
            Или Набор.ОбменДанными.Загрузка Тогда
            ВызватьИсключение "Нужна ровно своя цена300 без режима загрузки обмена";
        КонецЕсли;
        Набор.УстановитьКомментарийВерсииИсторииДанных("price85-second:" + UUID);
        Если Набор.ЗаписьИсторииДанных.КомментарийВерсии <> "price85-second:" + UUID Тогда
            ВызватьИсключение "Deprecated setter не подтвердил подготовленный комментарий набора";
        КонецЕсли;
        Набор.ЗаписьИсторииДанных.Отказ = Ложь;
        Набор.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Ложь;
        Набор.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Ложь;
        Набор.Записать(РежимЗамещения.Обновление);
        ПроверитьТолькоСвоюЦену85(UUID, 1, 300);
        ИсторияДанных.ОбновитьИсторию(Ключ, Ложь, Ложь);
        Версии = ПроверитьСвоюИсторию85(Ключ, UUID, 2);
        Возврат СтрШаблон("Цена250→300; фактические номера=%1,%2, оба сохраненных комментария проверены.", Версии[0].НомерВерсии, Версии[1].НомерВерсии);
    КонецЕсли;
    ПроверитьТолькоСвоюЦену85(UUID, 1, 300);
    Версии = ПроверитьСвоюИсторию85(Ключ, UUID, 2);
    Если Этап = "ПрочитатьДве" Тогда
        Возврат СтрШаблон("Историческая цена250 и300; текущая300. Номера=%1,%2 получены из настоящего отбора, а не придуманы.", Версии[0].НомерВерсии, Версии[1].НомерВерсии);
    КонецЕсли;
    Запись = РегистрыСведений.УчебныеЦены85.СоздатьМенеджерЗаписи();
    Запись.UUIDОпыта85 = UUID;
    Запись.Прочитать();
    Если Не Запись.Выбран() Или Запись.Цена <> 300 Тогда
        ВызватьИсключение "Новый менеджер должен прочитать нашу существующую цену300 перед точным удалением";
    КонецЕсли;
    Запись.ЗаписьИсторииДанных.КомментарийВерсии = "price85-delete:" + UUID;
    Запись.ЗаписьИсторииДанных.Отказ = Ложь;
    Запись.ЗаписьИсторииДанных.ОбновлятьИсториюСразуПослеЗаписи = Ложь;
    Запись.ЗаписьИсторииДанных.ВыполнятьОбработкуПослеЗаписиВерсии = Ложь;
    Запись.Удалить();
    ПроверитьТолькоСвоюЦену85(UUID, 0, 0);
    После = РегистрыСведений.УчебныеЦены85.СоздатьМенеджерЗаписи();
    После.UUIDОпыта85 = UUID;
    После.Прочитать();
    Если После.Выбран() Тогда
        ВызватьИсключение "Удаление своей цены не подтверждено свежим менеджером";
    КонецЕсли;
    ИсторияДанных.ОбновитьИсторию(Ключ, Ложь, Ложь);
    Версии = ПроверитьСвоюИсторию85(Ключ, UUID, 3);
    Возврат "Своей текущей цены больше нет. Две версии250/300 и фактическая версия удаления сохранены; история не удалялась.";
КонецФункции

Процедура ПроверитьСвойПрофильИстории85()
    Настройки = ИсторияДанных.ПолучитьНастройки(Метаданные.РегистрыСведений.УчебныеЦены85);
    Если Настройки = Неопределено Или Настройки.Использование <> Истина
        Или Настройки.ИспользованиеПолей.Получить("UUIDОпыта85") <> Истина
        Или Настройки.ИспользованиеПолей.Получить("Цена") <> Истина Тогда
        ВызватьИсключение "Полученные настройки не подтверждают историю своей цены/UUID; чужой профиль автоматически не включаем";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьТолькоСвоюЦену85(UUID, ОжидаемыеСтроки, ОжидаемаяЦена)
    Данные = ПолучитьСвоиДанные85("РегистрСведений.УчебныеЦены85");
    Если Данные.Количество() <> ОжидаемыеСтроки Тогда
        ВызватьИсключение "Во всем своем регистре другое число строк; неизвестные данные не изменяем";
    КонецЕсли;
    Для Каждого СтрокаЦены Из Данные Цикл
        Если СтрокаЦены.UUIDОпыта85 <> UUID Или СтрокаЦены.Цена <> ОжидаемаяЦена Тогда
            ВызватьИсключение "В регистре чужой UUID или цена не равна последнему подтвержденному этапу";
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Функция ПроверитьСвоюИсторию85(Ключ, UUID, ОжидаемыеВерсии)
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Обновление/проверка истории только вне чужой или своей активной транзакции";
    КонецЕсли;
    Отбор = Новый Структура("Метаданные", Метаданные.РегистрыСведений.УчебныеЦены85);
    Версии = ИсторияДанных.ВыбратьВерсии(Отбор, "Данные,НомерВерсии,Комментарий", "НомерВерсии Возр", 4);
    Если ТипЗнч(Версии) <> Тип("ТаблицаЗначений") Или Версии.Количество() <> ОжидаемыеВерсии Тогда
        ВызватьИсключение "Во всей истории своего нового регистра другое число версий; автоматически не повторять запись";
    КонецЕсли;
    ПредыдущийНомер = 0;
    Для Индекс = 0 По Версии.Количество() - 1 Цикл
        Версия = Версии.Получить(Индекс);
        Если ТипЗнч(Версия.Данные) <> Тип("РегистрСведенийКлючЗаписи.УчебныеЦены85")
            Или Версия.Данные.UUIDОпыта85 <> UUID Или Версия.НомерВерсии <= ПредыдущийНомер Тогда
            ВызватьИсключение "Чужой ключ или нарушенный порядок фактических номеров истории";
        КонецЕсли;
        ПредыдущийНомер = Версия.НомерВерсии;
        ДанныеВерсии = ИсторияДанных.ПолучитьДанныеВерсии(Ключ, Версия.НомерВерсии);
        Если Индекс = 2 Тогда
            Если ДанныеВерсии <> Неопределено Или Версия.Комментарий <> "price85-delete:" + UUID Тогда
                ВызватьИсключение "Удаление должно иметь свою фактическую версию без снимка данных";
            КонецЕсли;
        Иначе
            ОжидаемаяЦена = ?(Индекс = 0, 250, 300);
            ОжидаемыйКомментарий = ?(Индекс = 0, "price85-first:", "price85-second:") + UUID;
            Если ТипЗнч(ДанныеВерсии) <> Тип("Структура") Или Не ДанныеВерсии.Свойство("UUIDОпыта85")
                Или Не ДанныеВерсии.Свойство("Цена") Или ДанныеВерсии.UUIDОпыта85 <> UUID
                Или ДанныеВерсии.Цена <> ОжидаемаяЦена Или Версия.Комментарий <> ОжидаемыйКомментарий Тогда
                ВызватьИсключение "Материализованный исторический снимок/комментарий не равен своему ожидаемому250/300";
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Возврат Версии;
КонецФункции

Процедура ПроверитьСвоюСхему85(UUID)
    ПроверитьСвойПустойСтенд();
    Если ИмяПользователя() <> "guide-record-cycle" Тогда
        ВызватьИсключение "Нужен единственный собственный учебный пользователь guide-record-cycle";
    КонецЕсли;
    Если ТипЗнч(UUID) <> Тип("Строка") Или СтрДлина(UUID) <> 36 Тогда
        ВызватьИсключение "UUID опыта должен быть настоящей строкой из36символов";
    КонецЕсли;
    ПроверкаUUID = Новый УникальныйИдентификатор(UUID);
    Если НРег(Строка(ПроверкаUUID)) <> НРег(UUID) Тогда
        ВызватьИсключение "Недопустимый UUID опыта";
    КонецЕсли;
    Если Метаданные.ОбщиеМодули.Количество() <> 2 Или Метаданные.ОбщиеФормы.Количество() <> 1 Тогда
        ВызватьИсключение "Нужны только два собственных серверных модуля и одна учебная общая форма";
    КонецЕсли;
    ИменаМодулей = СтрРазделить("УчебныеЦеныЦикл85,УчебныеСтрокиЦикл85", ",");
    Для Каждого ИмяМодуля Из ИменаМодулей Цикл
        Модуль = Метаданные.ОбщиеМодули.Найти(ИмяМодуля);
        Если Модуль = Неопределено Или Не Модуль.Сервер Или Не Модуль.ВызовСервера
            Или Модуль.Привилегированный
            Или Модуль.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
            ВызватьИсключение "Только свои непривилегированные серверные модули без кеширования";
        КонецЕсли;
    КонецЦикла;
    Если Метаданные.ОбщиеФормы.Найти("УчебныйЦикл85") = Неопределено Тогда
        ВызватьИсключение "Не подготовлена собственная общая форма";
    КонецЕсли;
    Цены = Метаданные.РегистрыСведений.Найти("УчебныеЦены85");
    Строки = Метаданные.РегистрыСведений.Найти("УчебныеСтрокиЦен85");
    Документ = Метаданные.Документы.Найти("УчебнаяУстановкаЦен85");
    Если Цены = Неопределено Или Строки = Неопределено Или Документ = Неопределено Тогда
        ВызватьИсключение "Нужны только два именованных собственных регистра и собственный документ";
    КонецЕсли;
    Если Цены.РежимЗаписи <> Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.Независимый
        Или Цены.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический
        Или Цены.Измерения.Количество() <> 1 Или Цены.Ресурсы.Количество() <> 1 Или Цены.Реквизиты.Количество() <> 0
        Или Цены.ИсторияДанных <> Метаданные.СвойстваОбъектов.ИспользованиеИсторииДанных.Использовать Тогда
        ВызватьИсключение "Независимый непериодический регистр цены должен иметь историю и только UUID/цену";
    КонецЕсли;
    Если Строки.РежимЗаписи <> Метаданные.СвойстваОбъектов.РежимЗаписиРегистра.ПодчинениеРегистратору
        Или Строки.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Секунда
        Или Строки.Измерения.Количество() <> 2 Или Строки.Ресурсы.Количество() <> 2 Или Строки.Реквизиты.Количество() <> 0
        Или Строки.ИсторияДанных <> Метаданные.СвойстваОбъектов.ИспользованиеИсторииДанных.НеИспользовать Тогда
        ВызватьИсключение "Подчиненные строки должны иметь секундный период, UUID/код, количество/цену и не использовать историю";
    КонецЕсли;
    Если Документ.Реквизиты.Количество() <> 1 Или Документ.Реквизиты.Найти("UUIDОпыта85") = Неопределено
        Или Документ.ТабличныеЧасти.Количество() <> 0 Или Документ.Движения.Количество() <> 1
        Или Не Документ.Движения.Содержит(Строки)
        Или Документ.ЗаписьДвиженийПриПроведении <> Метаданные.СвойстваОбъектов.ЗаписьДвиженийПриПроведении.ЗаписыватьВыбранные Тогда
        ВызватьИсключение "Документ должен использовать только свой регистр и выбранные движения";
    КонецЕсли;
    Для Каждого Регистр Из Метаданные.РегистрыСведений Цикл
        Метка = Регистр.Измерения.Найти("UUIDОпыта85");
        Цена = Регистр.Ресурсы.Найти("Цена");
        Если Метка = Неопределено Или Цена = Неопределено Тогда
            ВызватьИсключение "Отсутствуют обязательные именованные поля";
        КонецЕсли;
        Если Метка.Тип.Типы().Количество() <> 1 Или Не Метка.Тип.СодержитТип(Тип("Строка"))
            Или Метка.Тип.КвалификаторыСтроки.Длина <> 36
            Или Цена.Тип.Типы().Количество() <> 1 Или Не Цена.Тип.СодержитТип(Тип("Число"))
            Или Цена.Тип.КвалификаторыЧисла.Разрядность <> 15 Или Цена.Тип.КвалификаторыЧисла.РазрядностьДробнойЧасти <> 2 Тогда
            ВызватьИсключение "UUID толькоСтрока36, цена толькоЧисло15,2";
        КонецЕсли;
    КонецЦикла;
    Код = Строки.Измерения.Найти("КодТовара");
    Количество = Строки.Ресурсы.Найти("Количество");
    Если Код = Неопределено Или Количество = Неопределено Тогда
        ВызватьИсключение "Не подготовлены код/количество своих строк";
    КонецЕсли;
    Если Код.Тип.Типы().Количество() <> 1 Или Не Код.Тип.СодержитТип(Тип("Строка"))
        Или Код.Тип.КвалификаторыСтроки.Длина <> 10
        Или Количество.Тип.Типы().Количество() <> 1 Или Не Количество.Тип.СодержитТип(Тип("Число"))
        Или Количество.Тип.КвалификаторыЧисла.Разрядность <> 10
        Или Количество.Тип.КвалификаторыЧисла.РазрядностьДробнойЧасти <> 0
        Или Документ.Реквизиты.UUIDОпыта85.Тип.Типы().Количество() <> 1
        Или Не Документ.Реквизиты.UUIDОпыта85.Тип.СодержитТип(Тип("УникальныйИдентификатор")) Тогда
        ВызватьИсключение "Проверьте точные типы кода/количества/UUID документа";
    КонецЕсли;
КонецПроцедуры

Функция ПолучитьСвоиДанные85(Источник)
    Если СтрРазделить("РегистрСведений.УчебныеЦены85,РегистрСведений.УчебныеСтрокиЦен85,Документ.УчебнаяУстановкаЦен85", ",").Найти(Источник) = Неопределено Тогда
        ВызватьИсключение "Запрос допускает только три собственных фиксированных источника";
    КонецЕсли;
    Запрос = Новый Запрос("ВЫБРАТЬ Д.* ИЗ " + Источник + " КАК Д");
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 1 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 2 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры
Модуль набора записейУчебныеЦены85

Собственная CF → Регистры сведений → УчебныеЦены85 → Модуль набора записей, целиком.

Реальный серверный модуль набора записей собственного регистра; ОбработкаЗаполнения вызывается платформой, не вручную.

Процедура ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
    СтандартнаяОбработка = Ложь;
    Если ТипЗнч(ДанныеЗаполнения) <> Тип("Структура") Или ДанныеЗаполнения.Количество() <> 2
        Или Не ДанныеЗаполнения.Свойство("UUIDОпыта85") Или Не ДанныеЗаполнения.Свойство("Цена")
        Или ТипЗнч(ДанныеЗаполнения.UUIDОпыта85) <> Тип("Строка") Или СтрДлина(ДанныеЗаполнения.UUIDОпыта85) <> 36
        Или ТипЗнч(ДанныеЗаполнения.Цена) <> Тип("Число")
        Или (ДанныеЗаполнения.Цена <> 250 И ДанныеЗаполнения.Цена <> 300) Или ЭтотОбъект.Количество() <> 1 Тогда
        ВызватьИсключение "Заполнение только собственным точным форматомUUID/цена250или300 и единственной строкой";
    КонецЕсли;
    ПроверкаUUID = Новый УникальныйИдентификатор(ДанныеЗаполнения.UUIDОпыта85);
    Если НРег(Строка(ПроверкаUUID)) <> НРег(ДанныеЗаполнения.UUIDОпыта85) Тогда
        ВызватьИсключение "Недопустимый UUID заполнения";
    КонецЕсли;
    Если ЭтотОбъект.ОбменДанными.Загрузка Тогда
        ВызватьИсключение "Учебное заполнение не использует обход проверок черезЗагрузка";
    КонецЕсли;
    СтрокаЦены = ЭтотОбъект.Получить(0);
    СтрокаЦены.UUIDОпыта85 = ДанныеЗаполнения.UUIDОпыта85;
    СтрокаЦены.Цена = ДанныеЗаполнения.Цена;
КонецПроцедуры
Модуль управляемой общей формы для цены

Собственная CF → Общие формы → УчебныйЦикл85 → Модуль формы, целиком; текущий вариант для этого урока.

Модуль собственной управляемой общей формы УчебныйЦикл85, клиентские команды и реальные вызовы своих серверных общих модулей.

&НаКлиенте
Процедура НовыйUUID85(Команда)
    Если ЗначениеЗаполнено(UUIDОпыта85) Тогда
        ВызватьИсключение "Существующий UUID не заменяется. Новую попытку после ошибки автоматически не запускайте";
    КонецЕсли;
    UUIDОпыта85 = Строка(Новый УникальныйИдентификатор);
    Результат85 = "Скопируйте UUID и вводите отдельное подтверждение перед каждой изменяющей командой.";
КонецПроцедуры

&НаКлиенте
Процедура СоздатьПервую85(Команда)
    ЗапуститьЦену85("СоздатьПервую");
КонецПроцедуры

&НаКлиенте
Процедура ЗаписатьВторую85(Команда)
    ЗапуститьЦену85("ЗаписатьВторую");
КонецПроцедуры

&НаКлиенте
Процедура ПрочитатьДве85(Команда)
    ЗапуститьЦену85("ПрочитатьДве");
КонецПроцедуры

&НаКлиенте
Процедура УдалитьСвою85(Команда)
    ЗапуститьЦену85("УдалитьСвою");
КонецПроцедуры

&НаКлиенте
Процедура ЗапуститьЦену85(Этап)
    Если ОшибкаЭтапа85 Тогда
        ВызватьИсключение "Прошлый этап завершился ошибкой: результат может быть частичным; сначала вручную исследуйте собственный стенд";
    КонецЕсли;
    ТекущееПодтверждение = Подтверждение85;
    Подтверждение85 = "";
    Попытка
        Результат85 = УчебныеЦеныЦикл85.ВыполнитьЦену85(Этап, UUIDОпыта85, ТекущееПодтверждение);
    Исключение
        ОшибкаЭтапа85 = Истина;
        Результат85 = "СТОП: частичный результат возможен. " + ОписаниеОшибки();
    КонецПопытки;
КонецПроцедуры

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

  1. Менеджер записи применяется только независимому регистру. Заполнить передает нашу структуру в событиеОбработкаЗаполнения с одной записью; собственный обработчик устанавливаетUUID/цену, а не получает неизвестный готовый объект.

  2. СоздатьПервую требует0своих/чужих строк во всех источниках и0версий всего регистра. Включается только профиль своего нового регистра и проверяется отдельнымGetSettings; профиль намеренно остается.

  3. МетодыУстановитьКомментарийВерсииИсторииДанных у менеджера/набора не рекомендуются с8.3.15. Здесь это отдельная совместимость старого кода: сначала проверяется pending-комментарий. Для нового кода используйте readonly-контейнерЗаписьИсторииДанных и его RWКомментарийВерсии.

  4. После фактической записи250 конкретныйОбновитьИсторию материализует историю вне транзакции. ВыбратьВерсии/ПолучитьДанныеВерсии независимо проверяютUUID, цену и сохраненный комментарий; getter менеджера до записи не считается persisted-proof.

  5. ЗаписатьВторую строит собственный набор из единственной строки, задает точный отборUUID иЗаполнить с ценой300. РежимОбновление не добавляет новую строку при отсутствии соответствующего ключа. ЗагрузкаОбмена остаетсяЛожь.

  6. ПрочитатьДве выполняет только чтение: текущая300 и реальные исторические250/300. Номера строго возрастают, но не принимаются равными1/2.

  7. УдалитьСвою отдельная подтвержденная команда: fresh менеджер прочитал собственную300, затемDelete без аргументов удаляет именно этот ключ. Второй fresh менеджер и общий raw-запрос доказывают отсутствие.

  8. Фактическая версия удаления имеетНеопределено вместо снимка данных. Первые две версии и их комментарии сохранены; методов удаления истории нет. Ошибка останавливает цепочку без слепого rollback/retry.

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

  1. ПослеСоздатьПервую: весь независимый регистр содержит ровноUUID/250; одна фактическая версия сprice85-first:UUID и снимком250.

  2. ПослеЗаписатьВторую: весь регистрUUID/300; ровно две настоящие версии250/300, сохраненные комментарииprice85-first:UUID/price85-second:UUID.

  3. ПрочитатьДве возвращает реальные номера из таблицы версий и не записывает данные.

  4. ПослеУдалитьСвою: raw0, свежийВыбран=Ложь, история3 с прежними250/300 иНеопределено в снимке удаления; price85-delete:UUID.

  5. ДругойUUID/профиль/количество строк/чужая версия/неверное подтверждение/активная транзакция останавливают урок до соответствующего воздействия.

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

При успешном ручном выполнении на своём стенде ожидается: цена250→300→отсутствие текущей записи; реальные версии и комментарии истории сохранены. Здесь только подготовлен и статически проверен код, запуск1С не выполнялся.

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

  1. Полная роль/правоЧтениеИсторииОтсутствующихДанных обязательны для начальных0версий и чтения истории после удаления; без них возможны исключение или исключение отсутствующих данных из выборки.

  2. Нельзя переносить менеджер записи в подчиненный регистр: native описание разрешает его только независимому. Активность/НомерСтроки/Регистратор менеджера записи намеренно не используются.

  3. 0строк/0версий и UUID не являются атомарной блокировкой. УчебнаяИБ должна быть изолирована; при параллельном вмешательстве не продолжать.

  4. Успешный pending-getter не доказывает созданную историю. Синхронное обновление/полная выборка/реальные снимки — отдельные проверки.

  5. Начальные настройки своего регистра действительно изменяются и не возвращаются; никакой атомарности/очистки после неуспеха не обещается.

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

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

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

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

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