Свой последовательный журнал: настройки, реальные события и архивное сокращение

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

Две реальные команды новой одноразовой File базы: проверить уровни, политику и разделение с восстановлением; затем сохранить архив и удалить старые записи всего собственного журнала.

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

Неглобальный серверный модуль новой пустой File CF 8.5.1.1343 в отдельной VM; толстая управляемая форма. Будущие изменения реального учебного журнала, не только памяти.

Подготовка

  1. В отдельной НОВОЙ одноразовой Windows VM вручную создайте пустую файловую ИБ точно C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\IB. Не берите копию рабочей базы или чужую CF. UUID пути — учебный маркер, не защита от подмены базы, junction или злонамеренного кода.

  2. Конфигуратор → Конфигурация → свойства: Имя=СтендЖРПоследовательный85; Версия=OwnEventLogSequential4-e6bf76c4-72d0-490f-819b-d0bb63112546; ОсновнойРежимЗапуска=УправляемоеПриложение; РежимСовместимости=НеИспользовать; вариант встроенного языка Русский. Не добавляйте документы, справочники, регистры, регламентные задания и расширения.

  3. Общие → Роли → ЖР85Администратор: учебные права Администрирование, ЖурналРегистрации, АктивныеПользователи и доступ к своей форме. Администрирование → Пользователи: создайте только своего ЖР85Ученик с этой ролью. Код не создаёт пользователей и не меняет роли; он читает настоящую текущую идентичность и UUID единственного соединения.

  4. Администрирование → Настройка журнала регистрации: подтвердите поле Разделять хранение журнала по периодам, которое установленная справка описывает для последовательного формата. Если вместо этого доступен только SQLite-журнал — остановитесь. Не преобразовывайте чужой или уже существующий журнал ради урока. Getter периода не объявлен детектором формата.

  5. В этой будущей VM вручную создайте только новый пустой каталог C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\Archive. Все родительские каталоги должны существовать. Код не создаёт/удаляет каталоги, отказывается от любых существующих файлов и от добавления к старому архиву.

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

  7. Общие → Общие формы → УрокЖурнала85. Реквизиты: Остановка85 Булево исходно Ложь; Этап85 Число1.0 исходно0; ДатаСтарая85/Граница85 Дата с составом ДатаИВремя; МаскаУровней85 Число2.0; Период85 ПериодРазделенияХраненияДанныхЖурналаРегистрации; Использование85 Булево; Код85 Строка100; СтарыхПосле85/НовыхПосле85/СтарыхВАрхиве85/ВсегоВАрхиве85 Число15.0; Архив85/Протокол85 неограниченная Строка. Служебные состояние и исходные настройки скрыты, результат и многострочный протокол только для просмотра.

  8. В форме добавьте команды Подготовить85 и Архивировать85, их Действия одноимённые процедуры FORM; кнопки панели привяжите именно к этим командам. Вставьте весь FORM. Сначала только Подготовить. После протокола успешного восстановления подождите несколько реальных секунд, затем отдельно нажмите Архивировать. Не вызывайте callback команды вручную и не переоткрывайте форму для обхода остановки.

  9. Конфигурация → Открыть модуль управляемого приложения: вставьте APPLICATION. Настоящее ПриНачалеРаботыСистемы() открывает собственную форму. Обновите только эту новую пустую CF, закройте Конфигуратор и все другие соединения, запустите один толстый управляемый клиент 8.5.1.1343. Перечисление периода создаётся и обрабатывается сервером, его обмен с сервером документирован.

  10. Первый этап сохраняет реальные уровни, состояние своего Policy и период. Наборы уровней проверяются как множества настоящих enum, не порядок массива. Коды0/1/3/7/15 — только компактные суммы1/2/4/8 для передачи исходного набора; в setter передаётся настоящий Массив, не эти числа. При трёх уровнях Info записывается, Note3 не должен; при четырёх Note4 должен. Policy один и тот же для Disabled/Enabled, комментарии разные.

  11. Политика собственного события возвращается настоящим getter. ОписаниеИспользования фактически проверяется как Неопределено: это условие урока, не обещание для любого события. Отдельные новые descriptor для Ложь/Истина/исходного Bool не меняют сохранённый объект. Системные события Access, AccessDenied и транзакций не настраиваются.

  12. Разделение меняется на Час или День, затем исходное значение восстанавливается и перечитывается. Не обещается появление нового часового файла без смены часа. Все настройки первого этапа восстановлены до возврата; перед сокращением второго этапа временные уровни снова восстанавливаются и проверяются.

  13. Старые/новые даты берутся из настоящих строк журнала. ЗаписьЖурналаРегистрации не принимает дату события. Граница — реальная дата Keep, строго позже Info; все совпадения UUID/имени/комментария/уровня проверяются. Выгрузка идёт в серверную ТаблицаЗначений с пятью точными колонками, MaximumCount0 означает без лимита. Таблицы, массивы и descriptor через RPC не передаются.

  14. СократитьЖурналРегистрации удаляет ВСЕ записи всего своего учебного журнала старше даты Keep, включая платформенные системные записи, а не только отбор Info. .lgf — имя легенды; дополнительные файлы данных получают документированный префикс. Архив читается через явное своё имя. В текущем журнале проверяются все даты не раньше границы, в архиве все даты раньше границы. Автоматические системные события не позволяют обещать фиксированное общее число записей.

  15. Любая ошибка означает отказ и постоянный STOP этой формы, без повторения мутаций или восстановительных вызовов внутри catch. Если ошибка возникла до подтверждённого восстановления настроек, они могли остаться изменёнными. Не обещается автоматический rollback: сохраните исходную ошибку, остановите VM; отдельное ручное восстановление из зафиксированных исходных значений либо новый пустой стенд требует отдельного решения. Повторный запуск урока на использованном журнале запрещён.

  16. Архив — сохранённые удалённые записи, а не их автоматический импорт обратно. СкопироватьЖурналРегистрации и ОчиститьЖурналРегистрации здесь не вызываются и не получают покрытие: отдельный новый SQLite producer ещё не доказан. Этот код проверяется статически; создание VM/CF/ИБ, запись журнала, настройки и архивирование здесь не выполнялись.

Функция ПроверитьСтенд85()
    Если Метаданные.РежимСовместимости <> Метаданные.СвойстваОбъектов.РежимСовместимости.НеИспользовать Или СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\IB"";" Тогда
        ВызватьИсключение "Разрешена только своя новая пустая File база в отдельной учебной VM";
    КонецЕсли;
    Если Метаданные.Документы.Количество() <> 0 Или Метаданные.Справочники.Количество() <> 0
        Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0
        Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0
        Или Метаданные.РегламентныеЗадания.Количество() <> 0
        Или Метаданные.ОбщиеМодули.Количество() <> 1 Или Метаданные.ОбщиеМодули.Найти("ЖРПоследовательный85") = Неопределено
        Или Метаданные.ОбщиеФормы.Количество() <> 1 Или Метаданные.ОбщиеФормы.Найти("УрокЖурнала85") = Неопределено Тогда
        ВызватьИсключение "Нужен пустой стенд с одним своим модулем и формой, без данных и заданий";
    КонецЕсли;
    МодульМД = Метаданные.ОбщиеМодули.Найти("ЖРПоследовательный85");
    Если Не МодульМД.Сервер Или МодульМД.Глобальный Или МодульМД.ВызовСервера Или МодульМД.Привилегированный
        Или МодульМД.КлиентУправляемоеПриложение Или МодульМД.КлиентОбычноеПриложение Или МодульМД.ВнешнееСоединение
        Или МодульМД.ПовторноеИспользованиеВозвращаемыхЗначений <> Метаданные.СвойстваОбъектов.ПовторноеИспользованиеВозвращаемыхЗначений.НеИспользовать Тогда
        ВызватьИсключение "Нужен неглобальный серверный модуль без прямого RPC, привилегий и кеширования";
    КонецЕсли;
    Если РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.БазаДанных).Количество() <> 0
        Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансАктивные).Количество() <> 0
        Или РасширенияКонфигурации.Получить(, ИсточникРасширенийКонфигурации.СеансОтключенные).Количество() <> 0 Тогда
        ВызватьИсключение "Расширения в этой собственной лаборатории запрещены";
    КонецЕсли;
    Текущий = ПользователиИнформационнойБазы.ТекущийПользователь();
    Пользователи = ПользователиИнформационнойБазы.ПолучитьПользователей();
    Если ТипЗнч(Текущий) <> Тип("ПользовательИнформационнойБазы") Или Пользователи.Количество() <> 1 Тогда
        ВызватьИсключение "Нужен один настоящий учебный пользователь";
    КонецЕсли;
    Если Текущий.Имя <> "ЖР85Ученик" Или Пользователи[0].УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
        ВызватьИсключение "Не совпала фактическая идентичность собственного пользователя";
    КонецЕсли;
    Соединения = ПолучитьСоединенияИнформационнойБазы();
    Если Соединения.Количество() <> 1 Тогда
        ВызватьИсключение "Закройте Конфигуратор и все остальные соединения своего стенда";
    КонецЕсли;
    Соединение = Соединения[0];
    Если Соединение.НомерСоединения <> НомерСоединенияИнформационнойБазы()
        Или Соединение.Пользователь.УникальныйИдентификатор <> Текущий.УникальныйИдентификатор Тогда
        ВызватьИсключение "Единственное соединение должно принадлежать своему текущему пользователю";
    КонецЕсли;
    Возврат Текущий;
КонецФункции

Функция ИмяСобытия85(Метка)
    Метки = СтрРазделить("Info,Note3,Note4,Disabled,Enabled,Keep", ",");
    Если Метки.Найти(Метка) = Неопределено Тогда
        ВызватьИсключение "Неизвестная метка собственного учебного события";
    КонецЕсли;
    Возврат "Учебный.ЖР85.e6bf76c4." + ?(Метка = "Disabled" Или Метка = "Enabled", "Policy", Метка);
КонецФункции

Функция ИмяАрхива85()
    Возврат "C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\Archive\own-journal-before-cutoff.lgf";
КонецФункции

Процедура ПроверитьПустойАрхив85()
    Каталог = Новый Файл("C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\Archive");
    Выход = Новый Файл(ИмяАрхива85());
    Если Не Каталог.Существует() Или Каталог.ЭтоФайл() Или Выход.Существует()
        Или НайтиФайлы(Каталог.ПолноеИмя, "*", Ложь).Количество() <> 0 Тогда
        ВызватьИсключение "В своей VM вручную подготовьте только этот новый пустой каталог; существующий архив запрещён";
    КонецЕсли;
КонецПроцедуры

Функция ПрочитатьЖурнал85(Метка, ВходнойФайл)
    Текущий = ПроверитьСтенд85();
    Если ТипЗнч(Метка) <> Тип("Строка") Или ТипЗнч(ВходнойФайл) <> Тип("Строка") Тогда
        ВызватьИсключение "Нужны точные строковые метка и источник";
    КонецЕсли;
    Если ВходнойФайл <> "" И ВходнойФайл <> ИмяАрхива85() Тогда
        ВызватьИсключение "Чужие файлы журнала читать запрещено";
    КонецЕсли;
    Отбор = Новый Структура;
    Если Метка <> "" Тогда
        Отбор.Вставить("Событие", ИмяСобытия85(Метка));
        Отбор.Вставить("Комментарий", "Учебный маркер " + Метка);
        Отбор.Вставить("Пользователь", Текущий);
    КонецЕсли;
    Таблица = Новый ТаблицаЗначений;
    Если ВходнойФайл = "" Тогда
        ВыгрузитьЖурналРегистрации(Таблица, Отбор, "Дата,Пользователь,Событие,Комментарий,Уровень", , 0);
    Иначе
        ВыгрузитьЖурналРегистрации(Таблица, Отбор, "Дата,Пользователь,Событие,Комментарий,Уровень", ВходнойФайл, 0);
    КонецЕсли;
    Если ТипЗнч(Таблица) <> Тип("ТаблицаЗначений") Или Таблица.Колонки.Количество() <> 5 Тогда
        ВызватьИсключение "Нужна фактическая таблица ровно с пятью колонками";
    КонецЕсли;
    Для Каждого ИмяКолонки Из СтрРазделить("Дата,Пользователь,Событие,Комментарий,Уровень", ",") Цикл
        Если Таблица.Колонки.Найти(ИмяКолонки) = Неопределено Тогда
            ВызватьИсключение "Не найдена обязательная колонка журнала";
        КонецЕсли;
    КонецЦикла;
    Для Каждого СтрокаЖР Из Таблица Цикл
        Если ТипЗнч(СтрокаЖР) <> Тип("СтрокаТаблицыЗначений") Или ТипЗнч(СтрокаЖР.Дата) <> Тип("Дата")
            Или ТипЗнч(СтрокаЖР.Пользователь) <> Тип("УникальныйИдентификатор")
            Или ТипЗнч(СтрокаЖР.Событие) <> Тип("Строка") Или ТипЗнч(СтрокаЖР.Комментарий) <> Тип("Строка")
            Или ТипЗнч(СтрокаЖР.Уровень) <> Тип("УровеньЖурналаРегистрации") Тогда
            ВызватьИсключение "Каждая строка должна иметь все настоящие документированные типы";
        КонецЕсли;
        Если Метка <> "" Тогда
            Уровень = ?(Метка = "Note3" Или Метка = "Note4", УровеньЖурналаРегистрации.Примечание, УровеньЖурналаРегистрации.Информация);
            Если СтрокаЖР.Событие <> ИмяСобытия85(Метка) Или СтрокаЖР.Комментарий <> "Учебный маркер " + Метка
                Или СтрокаЖР.Пользователь <> Текущий.УникальныйИдентификатор Или СтрокаЖР.Уровень <> Уровень Тогда
                ВызватьИсключение "Не подтверждены имя, комментарий, UUID и уровень собственного события";
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    Возврат Таблица;
КонецФункции

Процедура ЗаписатьМаркер85(Метка, Уровень)
    ЗаписьЖурналаРегистрации(ИмяСобытия85(Метка), Уровень, , , "Учебный маркер " + Метка);
КонецПроцедуры

Функция МаскаУровней85()
    Уровни = ПолучитьИспользованиеЖурналаРегистрации();
    Если ТипЗнч(Уровни) <> Тип("Массив") Или Уровни.Количество() > 4 Тогда
        ВызватьИсключение "Получатель должен вернуть настоящий массив не более четырёх уровней";
    КонецЕсли;
    Просмотренные = Новый Массив;
    Маска = 0;
    Для Каждого Уровень Из Уровни Цикл
        Если ТипЗнч(Уровень) <> Тип("УровеньЖурналаРегистрации") Или Просмотренные.Найти(Уровень) <> Неопределено Тогда
            ВызватьИсключение "Произвольные значения и повторные уровни запрещены";
        КонецЕсли;
        Просмотренные.Добавить(Уровень);
        Если Уровень = УровеньЖурналаРегистрации.Ошибка Тогда
            Маска = Маска + 1;
        ИначеЕсли Уровень = УровеньЖурналаРегистрации.Предупреждение Тогда
            Маска = Маска + 2;
        ИначеЕсли Уровень = УровеньЖурналаРегистрации.Информация Тогда
            Маска = Маска + 4;
        ИначеЕсли Уровень = УровеньЖурналаРегистрации.Примечание Тогда
            Маска = Маска + 8;
        Иначе
            ВызватьИсключение "Неизвестный уровень журнала";
        КонецЕсли;
    КонецЦикла;
    Если Маска <> 0 И Маска <> 1 И Маска <> 3 И Маска <> 7 И Маска <> 15 Тогда
        ВызватьИсключение "Набор уровней не разрешён установленной платформой";
    КонецЕсли;
    Возврат Маска;
КонецФункции

Процедура УстановитьУровни85(Маска)
    Если ТипЗнч(Маска) <> Тип("Число") Тогда
        ВызватьИсключение "Код набора уровней должен быть числом";
    КонецЕсли;
    Если Маска <> 0 И Маска <> 1 И Маска <> 3 И Маска <> 7 И Маска <> 15 Тогда
        ВызватьИсключение "Допускаются только пять документированных наборов";
    КонецЕсли;
    Уровни = Новый Массив;
    Если Маска >= 1 Тогда Уровни.Добавить(УровеньЖурналаРегистрации.Ошибка); КонецЕсли;
    Если Маска >= 3 Тогда Уровни.Добавить(УровеньЖурналаРегистрации.Предупреждение); КонецЕсли;
    Если Маска >= 7 Тогда Уровни.Добавить(УровеньЖурналаРегистрации.Информация); КонецЕсли;
    Если Маска = 15 Тогда Уровни.Добавить(УровеньЖурналаРегистрации.Примечание); КонецЕсли;
    УстановитьИспользованиеЖурналаРегистрации(Уровни);
    Если МаскаУровней85() <> Маска Тогда
        ВызватьИсключение "Настройка уровней фактически не совпала с ожидаемой";
    КонецЕсли;
КонецПроцедуры

Функция ПрочитатьПолитику85()
    Политика = ПолучитьИспользованиеСобытияЖурналаРегистрации(ИмяСобытия85("Enabled"));
    Если ТипЗнч(Политика) <> Тип("ИспользованиеСобытияЖурналаРегистрации") Тогда
        ВызватьИсключение "Нужен настоящий объект использования собственного события";
    КонецЕсли;
    Если ТипЗнч(Политика.Использование) <> Тип("Булево") Или Политика.ОписаниеИспользования <> Неопределено Тогда
        ВызватьИсключение "Неожиданная политика: произвольное описание не изменяем";
    КонецЕсли;
    Возврат Политика.Использование;
КонецФункции

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

Функция ПрочитатьПериод85()
    Период = ПолучитьПериодРазделенияХраненияДанныхЖурналаРегистрации();
    Если ТипЗнч(Период) <> Тип("ПериодРазделенияХраненияДанныхЖурналаРегистрации") Тогда
        ВызватьИсключение "Период должен быть настоящим значением перечисления";
    КонецЕсли;
    Возврат Период;
КонецФункции

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

Процедура ПроверитьПодготовку85(Пакет) Экспорт
    Описание = Новый Структура("ДатаСтарая,МаскаУровней,Период,Использование,Код", Тип("Дата"), Тип("Число"), Тип("ПериодРазделенияХраненияДанныхЖурналаРегистрации"), Тип("Булево"), Тип("Строка"));
    ПроверитьПоля85(Пакет, Описание);
    Если Пакет.Код <> "OwnEventLogSequential4-e6bf76c4-72d0-490f-819b-d0bb63112546"
        Или (Пакет.МаскаУровней <> 0 И Пакет.МаскаУровней <> 1 И Пакет.МаскаУровней <> 3 И Пакет.МаскаУровней <> 7 И Пакет.МаскаУровней <> 15) Тогда
        ВызватьИсключение "Не совпали код стенда и разрешённый исходный набор уровней";
    КонецЕсли;
КонецПроцедуры

Функция Подготовить85() Экспорт
    ПроверитьСтенд85();
    ПроверитьПустойАрхив85();
    Для Каждого Метка Из СтрРазделить("Info,Note3,Note4,Disabled,Enabled,Keep", ",") Цикл
        Если ПрочитатьЖурнал85(Метка, "").Количество() <> 0 Тогда
            ВызватьИсключение "Этот стенд уже использовался; повторные изменения запрещены";
        КонецЕсли;
    КонецЦикла;
    СтараяМаска = МаскаУровней85();
    СтароеИспользование = ПрочитатьПолитику85();
    СтарыйПериод = ПрочитатьПериод85();
    УстановитьУровни85(7);
    ЗаписатьМаркер85("Info", УровеньЖурналаРегистрации.Информация);
    ЗаписатьМаркер85("Note3", УровеньЖурналаРегистрации.Примечание);
    Старые = ПрочитатьЖурнал85("Info", "");
    Если Старые.Количество() <> 1 Или ПрочитатьЖурнал85("Note3", "").Количество() <> 0 Тогда
        ВызватьИсключение "Информация должна записаться, а Примечание при трёх уровнях — нет";
    КонецЕсли;
    УстановитьУровни85(15);
    ЗаписатьМаркер85("Note4", УровеньЖурналаРегистрации.Примечание);
    УстановитьПолитику85(Ложь);
    ЗаписатьМаркер85("Disabled", УровеньЖурналаРегистрации.Информация);
    Если ПрочитатьЖурнал85("Note4", "").Количество() <> 1 Или ПрочитатьЖурнал85("Disabled", "").Количество() <> 0 Тогда
        ВызватьИсключение "Не подтверждены четвёртый уровень и отключение своего события";
    КонецЕсли;
    УстановитьПолитику85(Истина);
    ЗаписатьМаркер85("Enabled", УровеньЖурналаРегистрации.Информация);
    Если ПрочитатьЖурнал85("Enabled", "").Количество() <> 1 Тогда
        ВызватьИсключение "Включённое собственное событие должно появиться один раз";
    КонецЕсли;
    НовыйПериод = ?(СтарыйПериод = ПериодРазделенияХраненияДанныхЖурналаРегистрации.Час, ПериодРазделенияХраненияДанныхЖурналаРегистрации.День, ПериодРазделенияХраненияДанныхЖурналаРегистрации.Час);
    УстановитьПериодРазделенияХраненияДанныхЖурналаРегистрации(НовыйПериод);
    Если ПрочитатьПериод85() <> НовыйПериод Тогда
        ВызватьИсключение "Новый период разделения фактически не подтверждён";
    КонецЕсли;
    УстановитьПериодРазделенияХраненияДанныхЖурналаРегистрации(СтарыйПериод);
    Если ПрочитатьПериод85() <> СтарыйПериод Тогда
        ВызватьИсключение "Исходный период не восстановлен";
    КонецЕсли;
    УстановитьПолитику85(СтароеИспользование);
    УстановитьУровни85(СтараяМаска);
    Если ПрочитатьПолитику85() <> СтароеИспользование Или ПрочитатьПериод85() <> СтарыйПериод Тогда
        ВызватьИсключение "Вся исходная настройка должна быть подтверждена до второй команды";
    КонецЕсли;
    Пакет = Новый ФиксированнаяСтруктура(Новый Структура("ДатаСтарая,МаскаУровней,Период,Использование,Код", Старые.Получить(0).Дата, СтараяМаска, СтарыйПериод, СтароеИспользование, "OwnEventLogSequential4-e6bf76c4-72d0-490f-819b-d0bb63112546"));
    ПроверитьПодготовку85(Пакет);
    Возврат Пакет;
КонецФункции

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

Функция Архивировать85(Пакет) Экспорт
    ПроверитьСтенд85();
    ПроверитьПодготовку85(Пакет);
    ПроверитьПустойАрхив85();
    Старые = ПрочитатьЖурнал85("Info", "");
    Если Старые.Количество() <> 1 Или ПрочитатьЖурнал85("Keep", "").Количество() <> 0
        Или ПрочитатьЖурнал85("Note4", "").Количество() <> 1 Или ПрочитатьЖурнал85("Enabled", "").Количество() <> 1
        Или ПрочитатьЖурнал85("Note3", "").Количество() <> 0 Или ПрочитатьЖурнал85("Disabled", "").Количество() <> 0 Тогда
        ВызватьИсключение "Нужен полностью подтверждённый первый этап без повторного Keep";
    КонецЕсли;
    Если Старые.Получить(0).Дата <> Пакет.ДатаСтарая Или ТекущаяДата() <= Пакет.ДатаСтарая
        Или МаскаУровней85() <> Пакет.МаскаУровней Или ПрочитатьПериод85() <> Пакет.Период
        Или ПрочитатьПолитику85() <> Пакет.Использование Тогда
        ВызватьИсключение "Нужны исходные настройки, фактическая старая дата и реальный разрыв времени";
    КонецЕсли;
    УстановитьУровни85(15);
    ЗаписатьМаркер85("Keep", УровеньЖурналаРегистрации.Информация);
    Новые = ПрочитатьЖурнал85("Keep", "");
    Если Новые.Количество() <> 1 Тогда
        ВызватьИсключение "Новый сохраняемый маркер должен появиться ровно один раз";
    КонецЕсли;
    Граница = Новые.Получить(0).Дата;
    Если Граница <= Пакет.ДатаСтарая Тогда
        ВызватьИсключение "Фактическая новая дата должна быть строго позже старой";
    КонецЕсли;
    УстановитьУровни85(Пакет.МаскаУровней);
    Если ПрочитатьПериод85() <> Пакет.Период Или ПрочитатьПолитику85() <> Пакет.Использование Тогда
        ВызватьИсключение "Все настройки должны быть восстановлены до необратимого сокращения";
    КонецЕсли;
    До = ПрочитатьЖурнал85("", "");
    ПериодЖР = ПолучитьПериодЖурналаРегистрации();
    Если ТипЗнч(ПериодЖР) <> Тип("Диапазон") Тогда
        ВызватьИсключение "Период журнала должен быть настоящим диапазоном";
    КонецЕсли;
    Если ТипЗнч(ПериодЖР.Начало) <> Тип("Дата") Или ТипЗнч(ПериодЖР.Конец) <> Тип("Дата")
        Или До.Количество() < 2 Или ПериодЖР.Начало > Пакет.ДатаСтарая Или ПериодЖР.Конец < Граница Тогда
        ВызватьИсключение "Весь собственный журнал должен содержать обе реальные даты";
    КонецЕсли;
    ПроверитьСтенд85();
    ПроверитьПустойАрхив85();
    СократитьЖурналРегистрации(Граница, ИмяАрхива85());
    Выход = Новый Файл(ИмяАрхива85());
    Если Не Выход.Существует() Или Не Выход.ЭтоФайл() Тогда
        ВызватьИсключение "Новый собственный архив не появился";
    КонецЕсли;
    СтарыеПосле = ПрочитатьЖурнал85("Info", "");
    НовыеПосле = ПрочитатьЖурнал85("Keep", "");
    АрхивСтарых = ПрочитатьЖурнал85("Info", ИмяАрхива85());
    Если СтарыеПосле.Количество() <> 0 Или НовыеПосле.Количество() <> 1 Или АрхивСтарых.Количество() <> 1
        Или ПрочитатьЖурнал85("Keep", ИмяАрхива85()).Количество() <> 0 Тогда
        ВызватьИсключение "Не подтверждено разделение собственных старого и сохраняемого событий";
    КонецЕсли;
    Если АрхивСтарых.Получить(0).Дата <> Пакет.ДатаСтарая Или НовыеПосле.Получить(0).Дата <> Граница Тогда
        ВызватьИсключение "Изменились реальные даты сохранённых событий";
    КонецЕсли;
    После = ПрочитатьЖурнал85("", "");
    Для Каждого СтрокаЖР Из После Цикл
        Если СтрокаЖР.Дата < Граница Тогда ВызватьИсключение "В текущем журнале осталась запись старше границы"; КонецЕсли;
    КонецЦикла;
    ВесьАрхив = ПрочитатьЖурнал85("", ИмяАрхива85());
    Для Каждого СтрокаЖР Из ВесьАрхив Цикл
        Если СтрокаЖР.Дата >= Граница Тогда ВызватьИсключение "В архив попала запись не старше границы"; КонецЕсли;
    КонецЦикла;
    ПериодЖР = ПолучитьПериодЖурналаРегистрации();
    Если ТипЗнч(ПериодЖР) <> Тип("Диапазон") Тогда ВызватьИсключение "Нужен фактический диапазон после сокращения"; КонецЕсли;
    Если ТипЗнч(ПериодЖР.Начало) <> Тип("Дата") Или ТипЗнч(ПериодЖР.Конец) <> Тип("Дата")
        Или ПериодЖР.Начало < Граница Или ПериодЖР.Конец < Граница Тогда
        ВызватьИсключение "Границы всего журнала не подтверждены";
    КонецЕсли;
    Итог = Новый ФиксированнаяСтруктура(Новый Структура("ДатаСтарая,Граница,СтарыхПосле,НовыхПосле,СтарыхВАрхиве,ВсегоВАрхиве,Архив", Пакет.ДатаСтарая, Граница, СтарыеПосле.Количество(), НовыеПосле.Количество(), АрхивСтарых.Количество(), ВесьАрхив.Количество(), ИмяАрхива85()));
    ПроверитьИтог85(Итог);
    Возврат Итог;
КонецФункции
Две собственные команды, проверенные пакеты и постоянная остановка

Общие → Общие формы → УрокЖурнала85 → Модуль

Толстый управляемый клиент / &НаСервере

&НаКлиенте
Процедура Подготовить85(Команда)
    Попытка
        Если Остановка85 Или Этап85 <> 0 Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Подготовить85 Тогда
            ВызватьИсключение "Команда разрешена только один раз на новом стенде";
        КонецЕсли;
        ПодготовитьНаСервере85();
        Если Остановка85 Или Этап85 <> 1 Или ТипЗнч(ДатаСтарая85) <> Тип("Дата")
            Или ТипЗнч(МаскаУровней85) <> Тип("Число") Или ТипЗнч(Период85) <> Тип("ПериодРазделенияХраненияДанныхЖурналаРегистрации")
            Или ТипЗнч(Использование85) <> Тип("Булево") Или Код85 <> "OwnEventLogSequential4-e6bf76c4-72d0-490f-819b-d0bb63112546" Тогда
            ВызватьИсключение "Первый этап не подтверждён на границе клиент/сервер";
        КонецЕсли;
    Исключение
        Остановка85 = Истина;
        Протокол85 = "ОТКАЗ, не успех. Не повторяйте команды и не переоткрывайте форму для обхода STOP. Настройки могли не восстановиться. Сохраните исходную ошибку и остановите учебную VM.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

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

&НаКлиенте
Процедура Архивировать85(Команда)
    Попытка
        Если Остановка85 Или Этап85 <> 1 Или ТипЗнч(Команда) <> Тип("КомандаФормы") Или Команда <> Команды.Архивировать85 Тогда
            ВызватьИсключение "Нужен завершённый первый этап и ровно одна своя команда архивации";
        КонецЕсли;
        АрхивироватьНаСервере85();
        Если Остановка85 Или Этап85 <> 2 Или ТипЗнч(Граница85) <> Тип("Дата") Или Граница85 <= ДатаСтарая85
            Или ТипЗнч(СтарыхПосле85) <> Тип("Число") Или СтарыхПосле85 <> 0
            Или ТипЗнч(НовыхПосле85) <> Тип("Число") Или НовыхПосле85 <> 1
            Или ТипЗнч(СтарыхВАрхиве85) <> Тип("Число") Или СтарыхВАрхиве85 <> 1
            Или ТипЗнч(ВсегоВАрхиве85) <> Тип("Число") Или ВсегоВАрхиве85 < 1
            Или Архив85 <> "C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\Archive\own-journal-before-cutoff.lgf" Тогда
            ВызватьИсключение "Не подтверждён весь фактический итог";
        КонецЕсли;
    Исключение
        Остановка85 = Истина;
        Протокол85 = "ОТКАЗ, не успех. После ошибки нет повторных мутаций или автоматического отката. Удалённые записи не восстановлены в текущий журнал; архив не является импортом. Сохраните ошибку и остановите учебную VM.";
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры

&НаСервере
Процедура АрхивироватьНаСервере85()
    Попытка
        Если Остановка85 Или Этап85 <> 1 Тогда ВызватьИсключение "Архивация на этом этапе запрещена"; КонецЕсли;
        Пакет = Новый ФиксированнаяСтруктура(Новый Структура("ДатаСтарая,МаскаУровней,Период,Использование,Код", ДатаСтарая85, МаскаУровней85, Период85, Использование85, Код85));
        ЖРПоследовательный85.ПроверитьПодготовку85(Пакет);
        Итог = ЖРПоследовательный85.Архивировать85(Пакет);
        ЖРПоследовательный85.ПроверитьИтог85(Итог);
        Если Итог.ДатаСтарая <> ДатаСтарая85 Тогда ВызватьИсключение "Подменена исходная дата"; КонецЕсли;
        Граница85 = Итог.Граница;
        СтарыхПосле85 = Итог.СтарыхПосле;
        НовыхПосле85 = Итог.НовыхПосле;
        СтарыхВАрхиве85 = Итог.СтарыхВАрхиве;
        ВсегоВАрхиве85 = Итог.ВсегоВАрхиве;
        Архив85 = Итог.Архив;
        Этап85 = 2;
        Протокол85 = "В текущем журнале старых своих Info:0, сохранённых Keep:1; в архиве старых Info:1. ВСЕ записи всего собственного журнала старше " + Строка(Граница85) + " удалены. В архиве всего " + Строка(ВсегоВАрхиве85) + " записей, включая возможные системные. Архив: " + Архив85 + ". Настройки восстановлены ДО сокращения. Импорт удалённых записей не выполнялся.";
    Исключение
        Остановка85 = Истина;
        ВызватьИсключение;
    КонецПопытки;
КонецПроцедуры
Штатное событие запуска открывает только собственную лабораторию

Конфигурация → Открыть модуль управляемого приложения

Толстый управляемый клиент

Процедура ПриНачалеРаботыСистемы()
#Если ТолстыйКлиентУправляемоеПриложение Тогда
    Если СтрокаСоединенияИнформационнойБазы() <> "File=""C:\1c-labs\OwnEventLogSequential4_9e2c4621-52cc-4d19-8c2d-f035ba650763\IB"";" Тогда
        ВызватьИсключение "Откройте только свою новую учебную File базу в отдельной VM";
    КонецЕсли;
    ОткрытьФорму("ОбщаяФорма.УрокЖурнала85", , , Истина);
#Иначе
    ВызватьИсключение "Нужен толстый управляемый клиент 8.5.1.1343";
#КонецЕсли
КонецПроцедуры

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

  1. Подготовить отдельную новую пустую VM/File CF, одного своего пользователя, последовательный журнал, точные форму/модуль и пустой каталог архива; проверить все маркеры и единственное соединение.

  2. Подготовить85: получить исходные настройки через native getters, наблюдать уровни7/15 и один свой Policy Ложь/Истина на реальных строках. Проверить период Час/День, восстановить и подтвердить все исходные настройки.

  3. Подождать реальный разрыв времени между двумя командами. Архивировать85 перечитывает старую строку и настройки, создаёт Keep и берёт его настоящую дату. Временные уровни восстанавливаются ДО сокращения.

  4. После полного readback и отказа от любого существующего архива один раз сократить весь собственный журнал до фактической даты Keep с новым .lgf архивом. Проверить Info0/Keep1 в текущем журнале и Info1/Keep0 в архиве, все даты обоих журналов и настоящий период текущего журнала.

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

  1. Будущий первый протокол: Info1, Note3=0, Note4=1, Disabled0, Enabled1; уровни, свой Policy и период восстановлены. Getter сам по себе не доказывает запись события: проверяются actual строки.

  2. Будущий итог: старая дата строго меньше границы, СтарыхПосле0, НовыхПосле1, СтарыхВАрхиве1, ВсегоВАрхиве≥1 и точный новый путь. Общее число может включать системные записи и берётся фактически.

  3. Пакеты имеют ровно5 и7 полей с проверкой всех имён/типов до чтения. Только4 primary. Ошибка/отказ не отмечается как полезный успешный пример; повторение и автоматический ремонт запрещены.

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

Ожидаемый будущий ручной результат: исходные настройки восстановлены до whole-own-lab сокращения; старый Info доступен в новом архиве, новый Keep остаётся в текущем журнале. Здесь 1С не запускалась.

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

  1. Никогда не запускайте в рабочей базе: дата сокращения касается всего журнала, без event-фильтра. Проверки учебных имён/UUID не являются защитой от намеренной подмены.

  2. Setter уровня допускает лишь0/1/3/7/15 наборы enum, требует администратора и отсутствие других соединений. Числа — только представление пакета, не аргумент native setter.

  3. Period setter доступен только последовательному журналу. Native enum периода имеет серверный контекст; не переносите код в чисто клиентский модуль.

  4. Native Range.Начало/Конец и ТаблицаЗначений.Колонки только читаются. UUID строки журнала сравнивается с фактическим пользовательским UUID, не с объектом пользователя.

  5. Truncate существующий архив пытается дополнить: здесь любой старый файл или содержимое каталога запрещены. Удаление не может быть отменено повторным чтением архива.

  6. STOP действует в текущей форме; не закрывайте/открывайте её заново ради повторного запуска. Ошибка до восстановления может оставить учебные настройки изменёнными; успешное восстановление не выдумывается.

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

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

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

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

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