Переупорядочить и удалить свои черновые записи без удаления из базы

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

Сдвигаем собственную B к концу и началу набора, затем удаляем A по объекту, B по текущему индексу и очищаем оставшуюся C. После каждого действия проверяются все три поля всех оставшихся записей.

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

Полный серверный общий модуль УчебноеПеремещениеПерерасчета85 собственной новой конфигурации УчебныйПерерасчет85. Все помощники предоставлены; отдельная полная форма запуска дана в подготовке/supportingModules. Запуск в 1С здесь не проверялся.

Подготовка

  1. Создайте НОВУЮ отдельную пустую учебную информационную базу на точной 8.5.1.1343 и конфигурацию УчебныйПерерасчет85. Не используйте рабочую базу или расширение готовой конфигурации. Никаких подписок, общих реквизитов, иных регистров, справочников или документов.

  2. Конфигуратор → Планы видов расчета → новый УчебныеВидыПерерасчета85. Использование периода действия=Нет; Зависимость от видов расчета=Не использовать; список базовых планов пуст. Без собственных реквизитов/табличных частей, предопределённых видов и кода модулей.

  3. Конфигуратор → Регистры расчета → новый УчебныйРегистрПерерасчета85. План видов расчета=УчебныеВидыПерерасчета85, периодичность=Месяц, Период действия=Нет, Базовый период=Нет. График не назначайте. Модули менеджера и набора записей пустые.

  4. У своего регистра единственное измерение Ключ85: единственный тип Строка, длина 20, допустимая длина Переменная. Единственный ресурс Результат85: единственный тип Число, длина 12, точность 2, допустимый знак Любой. Собственных реквизитов нет.

  5. У регистра → Перерасчеты → новый Очередь85 → Измерения → единственный Ключ85. В свойстве Измерение регистра выберите именно измерение Ключ85 родительского УчебныйРегистрПерерасчета85. Данные ведущих регистров пусты. Модуль набора записей перерасчета пуст.

  6. Конфигуратор → Документы → новый УчебныйРегистраторПерерасчета85. Проведение=Разрешить; в Движения включите только УчебныйРегистрПерерасчета85. Собственных реквизитов и табличных частей нет. Все объектные/менеджерские модули пустые; не создавайте документ и не проводите его.

  7. Остальные объектные модули, модули приложения и сеанса оставьте пустыми; не подключайте расширений, внешних обработчиков или подписок. Предопределённых элементов плана не создавайте. После ручной подготовки выполните проверку конфигурации и обновление учебной конфигурации базы. Здесь такая конфигурация и 1С не запускались.

  8. Все три поля каждой своей строки: ОбъектПерерасчета — пустая ссылка именно своего документа; ВидРасчета — пустая ссылка именно своего плана; Ключ85 — собственная строка 1..20. Пустые ссылки означают незаполненные учебные ключи, не существующие документы/виды и не готовые к записи данные.

  9. Конфигуратор → Общие → Общие модули → УчебноеПеремещениеПерерасчета85. Создайте отдельный неглобальный серверный общий модуль: Сервер=Да, Вызов сервера=Нет, Привилегированный=Нет, оба клиентских флага и внешнее соединение=Нет, повторное использование возвращаемых значений=Не использовать. Весь основной код вставьте на уровне этого модуля, не внутрь иной процедуры.

  10. Для ЭТОГО урока создайте собственную новую внешнюю обработку УчебныйПускПерерасчета85 с управляемой формой Форма, командой Выполнить и её кнопкой. Полный отдельный модуль формы ниже; не смешивайте его директивы с серверным общим модулем. Для другого урока используйте его собственный модуль формы и общее имя из соответствующей подготовки:

    &НаКлиенте
    Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
    Сообщить("Учебный опыт завершен без записи набора в базу");
    КонецПроцедуры

    &НаСервереБезКонтекста
    Процедура ВыполнитьНаСервере()
    УчебноеПеремещениеПерерасчета85.ВыполнитьПример();
    КонецПроцедуры

Процедура ВыполнитьПример() Экспорт
    ПроверитьСвойСтендПерерасчета();
    ОбъектПерерасчета = Документы.УчебныйРегистраторПерерасчета85.ПустаяСсылка();
    ВидРасчета = ПланыВидовРасчета.УчебныеВидыПерерасчета85.ПустаяСсылка();
    ПроверитьСвоиСсылки(ОбъектПерерасчета, ВидРасчета);
    Набор = СоздатьСвойПустойНабор();
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи(), ОбъектПерерасчета, ВидРасчета);
    ЗаписьA = ДобавитьСвоюЗапись(Набор, "A", ОбъектПерерасчета, ВидРасчета);
    ЗаписьB = ДобавитьСвоюЗапись(Набор, "B", ОбъектПерерасчета, ВидРасчета);
    ДобавитьСвоюЗапись(Набор, "C", ОбъектПерерасчета, ВидРасчета);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("A", "B", "C"), ОбъектПерерасчета, ВидРасчета);
    ПроверитьНеверноеСвоёСмещение(1, Неопределено, 3);
    ПроверитьНеверноеСвоёСмещение(1, "1", 3);
    ПроверитьНеверноеСвоёСмещение(1, 1.5, 3);
    ПроверитьНеверноеСвоёСмещение(1, -2, 3);
    ПроверитьНеверноеСвоёСмещение(1, 2, 3);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("A", "B", "C"), ОбъектПерерасчета, ВидРасчета);
    ПроверитьСвоёСмещение(Набор.Индекс(ЗаписьB), 1, Набор.Количество());
    Набор.Сдвинуть(ЗаписьB, 1);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("A", "C", "B"), ОбъектПерерасчета, ВидРасчета);
    ПроверитьСвоёСмещение(Набор.Индекс(ЗаписьB), -2, Набор.Количество());
    Набор.Сдвинуть(ЗаписьB, -2);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("B", "A", "C"), ОбъектПерерасчета, ВидРасчета);
    Набор.Удалить(ЗаписьA);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("B", "C"), ОбъектПерерасчета, ВидРасчета);
    ПроверитьСвойИндекс(0, Набор.Количество());
    Набор.Удалить(0);
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи("C"), ОбъектПерерасчета, ВидРасчета);
    ПроверитьНеверныйСвойИндекс(1, Набор.Количество());
    Набор.Очистить();
    ПроверитьСвойПолныйПорядок(Набор, СвоиКлючи(), ОбъектПерерасчета, ВидРасчета);
    ПроверитьНеверныйСвойИндекс(0, Набор.Количество());
КонецПроцедуры

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

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

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

Процедура ПроверитьСвойКлюч(Ключ)
    Если ТипЗнч(Ключ) <> Тип("Строка") Тогда
        ВызватьИсключение "Собственный ключ должен иметь тип Строка";
    КонецЕсли;
    Если СтрДлина(Ключ) < 1 Или СтрДлина(Ключ) > 20 Тогда
        ВызватьИсключение "Собственный ключ должен иметь длину от 1 до 20";
    КонецЕсли;
КонецПроцедуры

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

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

Процедура ПроверитьСвоюЗапись(Запись, Ключ, ОбъектПерерасчета, ВидРасчета)
    ПроверитьСвоиСсылки(Запись.ОбъектПерерасчета, Запись.ВидРасчета);
    ПроверитьСвойКлюч(Запись.Ключ85);
    Если Запись.ОбъектПерерасчета <> ОбъектПерерасчета Или Запись.ВидРасчета <> ВидРасчета Или Запись.Ключ85 <> Ключ Тогда
        ВызватьИсключение "Полный состав трех полей собственной строки не совпал";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойПолныйПорядок(Набор, Ключи, ОбъектПерерасчета, ВидРасчета)
    Если Набор.Количество() <> Ключи.Количество() Тогда
        ВызватьИсключение "Не совпало количество строк своего чернового набора";
    КонецЕсли;
    Если Ключи.Количество() > 0 Тогда
        Для Индекс = 0 По Ключи.Количество() - 1 Цикл
            Запись = Набор.Получить(Индекс);
            ПроверитьСвоюЗапись(Запись, Ключи[Индекс], ОбъектПерерасчета, ВидРасчета);
            ПроверитьСвоюЗапись(Набор[Индекс], Ключи[Индекс], ОбъектПерерасчета, ВидРасчета);
            Если Набор.Индекс(Запись) <> Индекс Тогда
                ВызватьИсключение "Индекс собственной полученной записи не совпал";
            КонецЕсли;
        КонецЦикла;
    КонецЕсли;
    Сообщить("Проверены все три поля, их типы и порядок: строк=" + Строка(Набор.Количество()));
КонецПроцедуры

Процедура ПроверитьСвойИндекс(Индекс, Количество)
    Если ТипЗнч(Индекс) <> Тип("Число") Тогда
        ВызватьИсключение "Собственный индекс должен иметь тип Число";
    КонецЕсли;
    Если Индекс <> Цел(Индекс) Или Индекс < 0 Или Индекс >= Количество Тогда
        ВызватьИсключение "Собственный индекс должен быть целым от 0 до количества минус 1";
    КонецЕсли;
КонецПроцедуры

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

Процедура ПроверитьСвоёСмещение(Индекс, Смещение, Количество)
    ПроверитьСвойИндекс(Индекс, Количество);
    Если ТипЗнч(Смещение) <> Тип("Число") Тогда
        ВызватьИсключение "Собственное смещение должно иметь тип Число";
    КонецЕсли;
    Если Смещение <> Цел(Смещение) Тогда
        ВызватьИсключение "Собственное смещение должно быть целым";
    КонецЕсли;
    ПроверитьСвойИндекс(Индекс + Смещение, Количество);
КонецПроцедуры

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

Собственная новая внешняя обработка УчебныйПускПерерасчета85 → Управляемая форма Форма → Модуль

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

&НаКлиенте
Процедура Выполнить(Команда)
    ВыполнитьНаСервере();
    Сообщить("Учебный опыт завершен без записи набора в базу");
КонецПроцедуры

&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере()
    УчебноеПеремещениеПерерасчета85.ВыполнитьПример();
КонецПроцедуры

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

  1. Сначала подготовить и проверить тот же новый собственный стенд. Набор создаётся пустым; три строки A,B,C принадлежат этому текущему набору, их ссылки намеренно пусты.

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

  3. Пять плохих смещений от индекса 1 — Неопределено,"1",1.5,-2,+2 — отклоняет только собственный валидатор, без вызова Сдвинуть. Полный A,B,C после них должен сохраниться.

  4. Native положительное смещение увеличивает индекс. Сдвинуть(ЗаписьB,1) перемещает B с 1 на 2: полный новый порядок A,C,B.

  5. Native отрицательное смещение направлено к началу. Теперь B имеет индекс 2; Сдвинуть(B,-2) даёт полный порядок B,A,C.

  6. Удалить принимает объект записи либо числовой индекс. Удалить(ЗаписьA) удаляет именно свою A, независимо от её перемещения на индекс 1. Полный остаток B,C.

  7. Перед удалением по индексу проверить текущие границы. Удалить(0) удаляет B уже из B,C, остаток C, а не исходную A.

  8. При одной строке собственное правило отклоняет индекс 1. Очистить удаляет все записи только из набора в памяти, полный остаток пуст.

  9. Индекс 0 после очистки снова недопустим по нашему правилу. Удалённые объекты строк не читаются, их потенциальная инвалидированность не угадывается.

  10. Все промежуточные проверки читают Получить и [] и сравнивают три поля, типы и Индекс. Набор ни разу не считан и не записан.

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

  1. Полная последовательность составов: A,B,C → A,C,B → B,A,C → B,C → C → пусто.

  2. В каждом непустом составе ОбъектПерерасчета и ВидРасчета — свои конкретные пустые ссылки, Ключ85 — ожидаемая строка; индекс каждой записи совпадает.

  3. Пять неверных смещений, индекс 1 при одной строке и индекс 0 после очистки отклоняются собственной проверкой до операции.

  4. Ни один catch не охватывает Сдвинуть, Удалить или Очистить. Неожиданная ошибка реальной операции прерывает урок.

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

Полный самостоятельный модуль выполняет конкретное редактирование только собственного чернового набора в памяти и проверяет все поля результата. Это не перерасчёт записанных движений и не выполнение нами внутри 1С.

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

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

  2. Смещение — количество позиций, не абсолютный целевой индекс. Перед вторым смещением B уже находится на 2, поэтому используется -2.

  3. После удаления индексы оставшихся записей изменяются. Проверяется текущий состав, а не сохранённый прежний индекс.

  4. Поведение native при дробном смещении, чужой или удалённой записи и выходе за пределы здесь не обещается: такие вызовы не выполняются.

  5. Ни Прочитать, ни Записать, ни восстановление записанного набора не используются. СоздатьНаборЗаписей даёт новый пустой набор; все строки добавлены или загружены нашим кодом. Удаление и очистка затрагивают только этот набор в памяти.

  6. ЭтотОбъект не используется: основной код находится в серверном общем модуле, а модуль набора перерасчета пуст. Свойство отсутствующего модульного контекста не подменяется параметром.

  7. Read-only guard сверяет доступную собственную схему перед созданием набора. Пустые остальные модули, отсутствие расширений и предопределённых элементов заданы точной подготовкой; guard не обещает анализа текстов сторонних модулей или проверки произвольной установленной конфигурации.

  8. В native ИзмерениеРегистра текст описывает измерение, но строка Тип называет РегистрРасчета. Код не угадывает ТипЗнч этого метаобъекта: сравнивает его с настоящим подготовленным родительским измерением. Тип строкового поля проверяется у родителя.

  9. Python-проверки — модель полностью заданных собственных значений и статическая сверка API установленной 8.5.1.1343. Они не выполняют BSL, не создают конфигурацию и не доказывают успешный опыт в 1С.

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

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

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

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

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