КоллекцияДвижений.Записать

Методы · Write

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

Синтаксис

Записать()
Полное описание, параметры и ограничения

КоллекцияДвижений.Записать (RegisterRecordsCollection.Write)

КоллекцияДвижений (RegisterRecordsCollection)

Записать (Write)

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

Синтаксис:
Записать()
Описание:

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

Доступность:

Сервер, толстый клиент, внешнее соединение, мобильное приложение (сервер), мобильный автономный сервер.

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

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

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

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

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

Книга3×250 и ручка1×50: выбранная запись0→800, активность800→0→800, точечное исправление950 и отдельное удаление своей ручки900.

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

Серверные полные модули/подчиненный регистр/один собственный документ; клиентская форма выполняет только явные отдельные команды, фактических1С/БД-вызовов здесь не было.

Подготовка

  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. Новых резервных копий/архивов урок не создает. Итоговые учебные данные/история намеренно остаются в собственной отдельнойИБ; удаление самойИБ/документа/всей истории не является частью кода.

  21. Дополнение второго урока не требует новых объектов или второго пользователя. В уже описанном документеУчебнаяУстановкаЦен85 списокДвижения содержит только подчиненныйУчебныеСтрокиЦен85; независимыйУчебныеЦены85 существует в этой же собственнойCF, но вДвижения не включается. Первый урок и его модули остаются прежними.

  22. Источник новой коллекции строго серверный: после проверки единственного собственного сохраненного документаПрочитатьСвойДокумент85 возвращает настоящуюСсылка.ПолучитьОбъект(), иДвиженияОбъекта85 получает readonly-свойствоДокументОбъект.Движения. ОдноименноеДокумент.Движения вПроверитьСвоюСхему85 принадлежит объекту метаданных: егоКоличество/Содержит не являются примеромКоллекцияДвижений.

  23. НаборВнеДвижений85 создается только nativeРегистрыСведений.УчебныеЦены85.СоздатьНаборЗаписей() для проверкиИндекс=-1. Он не читается изБД, не заполняется, не записывается, не добавляется в коллекцию и не меняет данные/историю первого урока. Не заменяйте его сторонним объектом и не добавляйте для него команды.

Функция ВыполнитьСтроки85(Этап, UUID, UUIDДокумента, Подтверждение) Экспорт
    ПроверитьСвоюСхему85(UUID);
    Если СтрРазделить("СоздатьДокумент,НеВыбирать,ВыбратьДве,Отключить,Включить,ИсправитьКнигу,УдалитьРучку,ПрочитатьИтог", ",").Найти(Этап) = Неопределено Тогда
        ВызватьИсключение "Неизвестный ручной этап строк";
    КонецЕсли;
    Если Этап <> "ПрочитатьИтог" И Подтверждение <> Этап + ":" + UUID Тогда
        ВызватьИсключение "Каждое изменение требует своего точного подтверждения Этап:UUID";
    КонецЕсли;
    Если ПолучитьСвоиДанные85("РегистрСведений.УчебныеЦены85").Количество() <> 0 Тогда
        ВызватьИсключение "Сначала завершите отдельный урок цены или оставьте независимый регистр пустым";
    КонецЕсли;
    Если Этап = "СоздатьДокумент" Тогда
        Если ПолучитьСвоиДанные85("Документ.УчебнаяУстановкаЦен85").Количество() <> 0
            Или ПолучитьСвоиДанные85("РегистрСведений.УчебныеСтрокиЦен85").Количество() <> 0 Тогда
            ВызватьИсключение "Новый документ только в своем новом пустом стенде; существующие не заменяем";
        КонецЕсли;
        Документ = Документы.УчебнаяУстановкаЦен85.СоздатьДокумент();
        Документ.Дата = Дата(2026, 10, 10, 12, 0, 0);
        Документ.Номер = UUID;
        Документ.UUIDОпыта85 = Новый УникальныйИдентификатор(UUID);
        Документ.Записать();
        UUIDДокумента = Строка(Документ.Ссылка.УникальныйИдентификатор());
        СвойДокумент = ПрочитатьСвойДокумент85(UUID, UUIDДокумента);
        ПроверитьВсеСвоиСтроки85(СвойДокумент.Ссылка, UUID, 0, 0, Истина);
        Возврат Новый Структура("UUIDДокумента,Результат", UUIDДокумента, "Сохранен только свой документ, строк0. Следующий отдельный шагНеВыбирать.");
    КонецЕсли;
    Документ = ПрочитатьСвойДокумент85(UUID, UUIDДокумента);
    Если Этап = "НеВыбирать" Или Этап = "ВыбратьДве" Тогда
        ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 0, 0, Истина);
        ДвиженияОбъекта85 = Документ.Движения;
        Если ТипЗнч(ДвиженияОбъекта85) <> Тип("КоллекцияДвижений") Или ДвиженияОбъекта85.Количество() <> 1 Тогда
            ВызватьИсключение "У своего объекта документа должна быть одна настоящая коллекция с одним набором движений";
        КонецЕсли;
        Набор = ДвиженияОбъекта85.Получить(0);
        Если ТипЗнч(Набор) <> Тип("РегистрСведенийНаборЗаписей.УчебныеСтрокиЦен85")
            Или ДвиженияОбъекта85.Найти("УчебныеСтрокиЦен85") <> Набор
            Или ДвиженияОбъекта85.УчебныеСтрокиЦен85 <> Набор
            Или ДвиженияОбъекта85.Индекс(Набор) <> 0
            Или ДвиженияОбъекта85.Найти("УчебныеЦены85") <> Неопределено Тогда
            ВызватьИсключение "Индекс0, имя и свойство должны дать один собственный подчиненный набор, а независимый регистр отсутствует в движениях";
        КонецЕсли;
        НаборВнеДвижений85 = РегистрыСведений.УчебныеЦены85.СоздатьНаборЗаписей();
        Если ДвиженияОбъекта85.Индекс(НаборВнеДвижений85) <> -1 Тогда
            ВызватьИсключение "Собственный свежий независимый набор не должен быть элементом движений этого документа";
        КонецЕсли;
        Набор.Отбор.Регистратор.Установить(Документ.Ссылка);
        Набор.Заполнить(Новый Структура("UUIDОпыта85,Регистратор", UUID, Документ.Ссылка));
        Если Набор.Количество() <> 2 Или Набор.ОбменДанными.Загрузка Тогда
            ВызватьИсключение "Свой обработчик должен дать две строки без загрузки обмена";
        КонецЕсли;
        Набор.Записывать = Этап = "ВыбратьДве";
        Документ.Движения.Записать();
        Если Набор.Записывать Тогда
            ВызватьИсключение "КоллекцияДвижений.Записать по native должна снять признакЗаписывать";
        КонецЕсли;
        Если Этап = "НеВыбирать" Тогда
            ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 0, 0, Истина);
            Текст = "Записывать=Ложь: коллекция не сохранила набор, строк0. Это не запрет прямогоНабор.Записать.";
        Иначе
            ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 2, 250, Истина);
            Текст = "Выбранный набор записан: Книга3×250, ручка1×50, сумма800; признакЗаписывать снят коллекцией.";
        КонецЕсли;
    ИначеЕсли Этап = "Отключить" Или Этап = "Включить" Тогда
        ПрежняяАктивность = Этап = "Отключить";
        ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 2, 250, ПрежняяАктивность);
        Набор = РегистрыСведений.УчебныеСтрокиЦен85.СоздатьНаборЗаписей();
        Набор.Отбор.Регистратор.Установить(Документ.Ссылка);
        Набор.Прочитать();
        Если Набор.Количество() <> 2 Или Набор.ОбменДанными.Загрузка Тогда
            ВызватьИсключение "Нужен свежий свой полный набор из двух строк без обхода проверок";
        КонецЕсли;
        Набор.УстановитьАктивность(Не ПрежняяАктивность);
        Набор.Записать(Истина);
        ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 2, 250, Не ПрежняяАктивность);
        Текст = ?(Этап = "Отключить", "Активная сумма0, rawсумма800: строки существуют, но не активны.", "Активная иrawсумма800 снова совпадают.");
    ИначеЕсли Этап = "ИсправитьКнигу" Или Этап = "УдалитьРучку" Тогда
        ПрежняяЦена = ?(Этап = "ИсправитьКнигу", 250, 300);
        Снимок = ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 2, ПрежняяЦена, Истина);
        Исходная = Снимок.Найти(?(Этап = "ИсправитьКнигу", "BOOK", "PEN"), "КодТовара");
        Если Исходная = Неопределено Тогда
            ВызватьИсключение "Нельзя работать с отсутствующей собственной строкой";
        КонецЕсли;
        Набор = РегистрыСведений.УчебныеСтрокиЦен85.СоздатьНаборЗаписей();
        Набор.Отбор.Регистратор.Установить(Документ.Ссылка);
        Если Набор.РасширенныеРежимыЗамещения Тогда
            ВызватьИсключение "Новый собственный набор должен иметь nativedefaultРасширенныеРежимыЗамещения=Ложь";
        КонецЕсли;
        Набор.РасширенныеРежимыЗамещения = Истина;
        СтрокаНабора = Набор.Добавить();
        ЗаполнитьЗначенияСвойств(СтрокаНабора, Исходная, "Период,Активность,Регистратор,UUIDОпыта85,КодТовара,Количество,Цена");
        СтрокаНабора.НомерСтроки = Исходная.НомерСтроки;
        Если Набор.Количество() <> 1 Или СтрокаНабора.Регистратор <> Документ.Ссылка
            Или СтрокаНабора.UUIDОпыта85 <> UUID Или СтрокаНабора.НомерСтроки <= 0
            Или Набор.ОбменДанными.Загрузка Тогда
            ВызватьИсключение "Расширенный режим только одной реально прочитанной собственной строки";
        КонецЕсли;
        Если Этап = "ИсправитьКнигу" Тогда
            СтрокаНабора.Цена = 300;
            Набор.Записать(РежимЗамещения.Обновление);
            После = ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 2, 300, Истина);
            Если После.Найти("BOOK", "КодТовара").НомерСтроки <> Исходная.НомерСтроки Тогда
                ВызватьИсключение "Обновление должно сохранить фактический ключ своей книги";
            КонецЕсли;
            Текст = "Обновлена только фактическая строкаBOOK: 3×300+1×50=950. Новая строка не добавлялась.";
        Иначе
            Набор.Записать(РежимЗамещения.Удаление);
            ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 1, 300, Истина);
            Текст = "Удалена только прочитанная строкаPEN собственного документа. Книга3×300=900 и документ сохранены.";
        КонецЕсли;
    Иначе
        ПроверитьВсеСвоиСтроки85(Документ.Ссылка, UUID, 1, 300, Истина);
        Текст = "Итог: одна своя активная книга3×300=900, один свой документ; никаких чужих строк и удаления документа.";
    КонецЕсли;
    ПрочитатьСвойДокумент85(UUID, UUIDДокумента);
    Возврат Новый Структура("UUIDДокумента,Результат", UUIDДокумента, Текст);
КонецФункции

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

Функция ПроверитьВсеСвоиСтроки85(Ссылка, UUID, ОжидаемоеКоличество, ЦенаКниги, Активность)
    Данные = ПолучитьСвоиДанные85("РегистрСведений.УчебныеСтрокиЦен85");
    Если Данные.Количество() <> ОжидаемоеКоличество Тогда
        ВызватьИсключение "Во всем своем подчиненном регистре другое число строк; неизвестные не изменяем";
    КонецЕсли;
    Коды = Новый Соответствие;
    Номера = Новый Соответствие;
    Сумма = 0;
    АктивнаяСумма = 0;
    Для Каждого СтрокаДанных Из Данные Цикл
        Если СтрокаДанных.Регистратор <> Ссылка Или СтрокаДанных.UUIDОпыта85 <> UUID
            Или СтрокаДанных.Период <> Дата(2026, 10, 10, 12, 0, 0)
            Или СтрокаДанных.Активность <> Активность Или СтрокаДанных.НомерСтроки <= 0
            Или Номера.Получить(СтрокаДанных.НомерСтроки) <> Неопределено
            Или Коды.Получить(СтрокаДанных.КодТовара) <> Неопределено Тогда
            ВызватьИсключение "Чужой/неожиданный регистратор, UUID, период, активность или повторный реальный ключ";
        КонецЕсли;
        Если СтрокаДанных.КодТовара = "BOOK" Тогда
            Если СтрокаДанных.Количество <> 3 Или СтрокаДанных.Цена <> ЦенаКниги Тогда
                ВызватьИсключение "Книга должна соответствовать независимому контрольному расчету3×цена";
            КонецЕсли;
        ИначеЕсли СтрокаДанных.КодТовара = "PEN" И ОжидаемоеКоличество = 2 Тогда
            Если СтрокаДанных.Количество <> 1 Или СтрокаДанных.Цена <> 50 Тогда
                ВызватьИсключение "Своя ручка должна оставаться1×50";
            КонецЕсли;
        Иначе
            ВызватьИсключение "Код не входит в точный whitelist своего этапаBOOK/PEN";
        КонецЕсли;
        Коды.Вставить(СтрокаДанных.КодТовара, Истина);
        Номера.Вставить(СтрокаДанных.НомерСтроки, Истина);
        Сумма = Сумма + СтрокаДанных.Количество * СтрокаДанных.Цена;
        Если СтрокаДанных.Активность Тогда
            АктивнаяСумма = АктивнаяСумма + СтрокаДанных.Количество * СтрокаДанных.Цена;
        КонецЕсли;
    КонецЦикла;
    ОжидаемаяСумма = ?(ОжидаемоеКоличество = 0, 0, 3 * ЦенаКниги + ?(ОжидаемоеКоличество = 2, 50, 0));
    Если Сумма <> ОжидаемаяСумма Или АктивнаяСумма <> ?(Активность, ОжидаемаяСумма, 0) Тогда
        ВызватьИсключение "Raw/активная сумма не равны независимому расчету800/0/950/900";
    КонецЕсли;
    Возврат Данные;
КонецФункции

Процедура ПроверитьСвоюСхему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
        Или ТипЗнч(ДанныеЗаполнения.Регистратор) <> Тип("ДокументСсылка.УчебнаяУстановкаЦен85")
        Или ДанныеЗаполнения.Регистратор.Пустая() Или ЭтотОбъект.Количество() <> 0 Или ЭтотОбъект.ОбменДанными.Загрузка Тогда
        ВызватьИсключение "Только новый пустой набор/реальная своя ссылка/UUID, не загрузка обмена";
    КонецЕсли;
    Документ = ДанныеЗаполнения.Регистратор.ПолучитьОбъект();
    Если Документ = Неопределено Или Документ.UUIDОпыта85 <> Новый УникальныйИдентификатор(ДанныеЗаполнения.UUIDОпыта85)
        Или Документ.Дата <> Дата(2026, 10, 10, 12, 0, 0) Тогда
        ВызватьИсключение "Регистратор не является реально сохраненным собственным документом";
    КонецЕсли;
    Для Индекс = 0 По 1 Цикл
        Запись = ЭтотОбъект.Добавить();
        Запись.Регистратор = Документ.Ссылка;
        Запись.UUIDОпыта85 = ДанныеЗаполнения.UUIDОпыта85;
        Запись.Период = Документ.Дата;
        Запись.Активность = Истина;
        Запись.КодТовара = ?(Индекс = 0, "BOOK", "PEN");
        Запись.Количество = ?(Индекс = 0, 3, 1);
        Запись.Цена = ?(Индекс = 0, 250, 50);
    КонецЦикла;
КонецПроцедуры
Модуль управляемой общей формы для строк

Собственная 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(Команда)
    ЗапуститьСтроки85("ПрочитатьИтог");
КонецПроцедуры

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

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

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

  2. Перед каждымНеВыбирать/ВыбратьДве свежий полный запрос подтверждает0строк своего подчиненного регистра. После получения собственного объекта читаетсяДвижения: проверяются реальный типКоллекцияДвижений иКоличество()=1. Это один набор записей в составе документа, а не одна запись регистра; только после этой проверки разрешенПолучить(0).

  3. Получить(0), Найти("УчебныеСтрокиЦен85") и именованное свойствоУчебныеСтрокиЦен85 должны вернуть один и тот же настоящий подчиненный набор; его тип иИндекс(Набор)=0 проверяются доЗаполнить/Записать. Неверное имя не подменяется fallback-объектом.

  4. Найти("УчебныеЦены85") даетНеопределено: реально существующий независимый регистр не включен в движения этого документа. Его отдельный собственный пустой набор даетИндекс=-1. Отсутствующее имя и объект вне коллекции — два разных отрицательных случая, не ошибка индекса0.

  5. МетодЗаполнить реально вызывает обработчик подчиненного набора: две собственные строкиBOOK/PEN, период10.10.2026 12:00, активностьИстина. Номера присваиваются платформой, заранее не выдумываются.

  6. НеВыбирать устанавливаетЗаписывать=Ложь и вызывает именноКоллекцияДвижений.Записать(): строк0. Это не запрет прямогоНабор.Записать и не проведение документа.

  7. ВыбратьДве заново строит набор, включаетЗаписывать и записывает коллекцией. Fresh raw-проверка дает две строки и800; nativeКоллекцияДвижений дополнительно сбрасываетЗаписывать, что проверяется отдельно.

  8. Отключить/Включить применяютУстановитьАктивность к своему свежепрочитанному полному набору. Raw-сумма остается800, производная активная сумма800→0→800. Это не остаток/оборот или изменение цены.

  9. ИсправитьКнигу берет фактическийНомерСтроки только из свежего полного whitelist-снимка. РасширенныеРежимыЗамещения нового набора первоначальноЛожь, явно включается; только после этого записывается реальныйНомерСтроки одной своейBOOK.

  10. РежимОбновление исправляет existingBOOK до300, без добавления. Сохраняется реальный номер; контроль3×300+1×50=950.

  11. УдалитьРучку отдельным подтверждением берет фактическуюPEN того же своего документа, строит ровно одну строку иРежимУдаление. Свежий контроль показывает толькоBOOK/900, документ остается.

  12. ПрочитатьИтог читает полный регистр/документ и проверяет1/900. Подчиненный регистр не использует историю; методы истории содержательно разобраны в отдельном независимом регистре.

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

  1. СоздатьДокумент:1свой непроведенный документ/0строк. НеВыбирать:0строк после коллекции,Записывать=Ложь.

  2. ВыбратьДве:2свои строкиBOOK/PEN, положительные уникальные реальные номера, raw/активная сумма800; flag снят коллекцией.

  3. Отключить:raw800/активная0; Включить:raw800/активная800, никаких новых/чужих строк.

  4. ИсправитьКнигу:2строки/950, прежний реальный номерBOOK; ручка1×50 не изменена.

  5. УдалитьРучку/ПрочитатьИтог:1BOOK/900,1свой исходный документ. ЧужойUUID/ссылка/код/неожиданный raw-state вызывают отказ.

  6. НеВыбирать:Количество коллекции=1; после nativeЗаполнить количество строк выбранного по индексу набора=2; после существующегоКоллекцияДвижений.Записать сЗаписывать=Ложь свежий запрос всей собственной таблицы даетБД=0. Поэтому1/2/0 не противоречат друг другу.

  7. ВыбратьДве:тот же проверенный состав коллекции=1, новый заполненный набор=2, послеЗаписывать=Истина и существующегоЗаписать свежий полный запрос даетБД=2 и3×250+1×50=800. СбросЗаписывать проверяется отдельно и не заменяет проверку данныхБД.

  8. Неверный состав/тип, Получить не того набора, несовпадение имени/свойства/индекса, неожиданно найденный независимый регистр илиИндекс вне коллекции не равный-1 останавливают ветку до заполнения/записи. После ошибки управляемая форма сохраняет прежний sticky-stop; автоматического повторения, очистки и удаления документа нет.

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

При успешном ручном выполнении на своём стенде ожидается: выборочная запись0→800, активная сумма800→0→800, точное обновление950 и удаление только своей ручки900. Здесь только исходники и статические проверки, не результат выполненной работы в1С.

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

  1. Native страницаSET.Записать ошибочно упоминаетДлинаНомераСтроки. СтраницаРасширенныеРежимыЗамещения14190 прямо разрешаетUpdate/Delete и записьНомерСтроки; используется именно реально существующее свойство, не выдуманное из опечатки.

  2. Расширенные режимы появились в8.3.26; нужен точный8.5.1.1343 без старого режима совместимости. В независимом регистре значениеExtendedReplaceMode не влияет на поведение.

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

  4. Ни raw-query, ни fresh readback не гарантируют атомарности при других сеансах. Изолированный собственный стенд обязателен, слепой rollback/повтор после ошибки запрещены.

  5. Количество/цена/активность читаются из реальных записей. Активная сумма — наш явный расчет в памяти, не обещанная виртуальная таблица или бухгалтерский остаток.

  6. КоллекцияДвижений не создается конструктором и readonly-свойствоДокумент.Движения не присваивается. Серверный код получает ее от собственного реального документа; клиентская форма не вызывает эти методы самостоятельно.

  7. Количество() коллекции считает включенные в документ наборы, аКоличество() набора — строки в его памяти. Ни одно из них не доказывает число сохраненных строк. Сохраненные0/2 доказывает отдельный свежий полный запрос, а получение объекта документа не объявляется автоматическим чтением всех его движений.

  8. Индекс=-1 относится только к созданному здесь отдельному независимому набору. Не вызывайтеПолучить(-1): отрицательное число — результат поиска отсутствующего элемента, а не допустимый индекс доступа. Все ожидаемые результаты требуют ручного выполнения в собственнойИБ; в этой работе реальных вызовов1С/БД не было.

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

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

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

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

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