Как записать несколько значений из реквизита 'СписокЗначений' управляемой формы обработки в регистр сведений?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Понимание проблемы: запись одного или нескольких значений

Изначально, при попытке записи в регистр сведений, может возникнуть вопрос, как передать значение из реквизита формы Работа, который имеет тип СписокЗначений:


Регистр = РегистрыСведений.Работа2022.СоздатьМенеджерЗаписи();
    Регистр.Номенклатура = Номенклатура;
    Регистр.Ссылка = Документ.Ссылка;
    Регистр.Работа = ???????????????    
    Регистр.Записать();

В этой ситуации поле Регистр.Работа, скорее всего, имеет тип, соответствующий элементу справочника или другому примитивному типу, а не СписокЗначений. Соответственно, мы не можем просто присвоить весь список полю регистра. Нам нужно записать каждое отдельное значение из этого списка как новую запись в регистре сведений. Это означает, что если в нашем СпискеЗначений есть две работы, то в регистре сведений должно появиться две записи, каждая из которых будет содержать одну из этих работ.

Базовый подход: запись каждой записи через менеджер записи в цикле

Самый прямой способ решить эту задачу — это пройтись циклом по всем элементам нашего СпискаЗначений и для каждого элемента создать новую запись в регистре сведений с помощью МенеджераЗаписи. Рассмотрим этот подход подробнее.

Предположим, у нас есть реквизит формы с именем Работы (или Работа), который имеет тип СписокЗначений. Для того чтобы записать каждое значение из этого списка в регистр сведений Работа2022, мы выполним следующие шаги:

  1. Создадим цикл для перебора всех элементов списка.
  2. Внутри цикла для каждого элемента будем создавать новый объект МенеджерЗаписи для регистра сведений.
  3. Заполним измерения и ресурсы записи, включая текущее значение из списка.
  4. Вызовем метод Записать() для созданного менеджера записи.

Вот пример кода, реализующего этот подход:


// Предполагаем, что "Номенклатура" и "Документ" - это реквизиты обработки,
// а "Работы" - это реквизит типа СписокЗначений, содержащий элементы справочника "Работа"
// или аналогичные значения.

Для Каждого ЭлементСпискаРаботы Из Работы Цикл // ИЛИ "Работы", в зависимости от имени реквизита формы
    // Создаем менеджер записи для каждой отдельной работы
    Регистр = РегистрыСведений.Работа2022.СоздатьМенеджерЗаписи();
    
    // Заполняем измерения регистра
    Регистр.Номенклатура = Номенклатура; // Предполагаем, что это реквизит обработки
    Регистр.Ссылка = Документ.Ссылка;     // Предполагаем, что это реквизит обработки
    
    // Присваиваем значение работы из текущего элемента списка
    // Важно: если список значений хранит "ЭлементСпискаЗначений", то нужно обращаться к его свойству "Значение"
    Регистр.Работа = ЭлементСпискаРаботы.Значение; // Доступ к значению элемента списка

    // Записываем одну запись в регистр
    Регистр.Записать();
КонецЦикла;

Этот код успешно решит задачу записи множества работ в регистр сведений. Каждая итерация цикла создает и записывает одну строку в регистр, обеспечивая, что все работы из списка будут учтены.

Оптимизация производительности: использование НабораЗаписей для массовой записи

Хотя подход с МенеджеромЗаписи в цикле работает, он не является наиболее производительным решением, особенно при большом количестве записей. Каждое создание МенеджераЗаписи и вызов метода Записать() приводит к отдельному обращению к базе данных, что может быть медленно. Для повышения производительности при массовой записи данных в регистры сведений рекомендуется использовать объект НаборЗаписей.

Объект НаборЗаписей предназначен для работы с группой записей регистра как с единым целым. Мы можем добавить все необходимые записи в этот набор в памяти, а затем одним вызовом метода Записать() сохранить весь набор в базу данных. Это значительно сокращает количество обращений к СУБД и улучшает быстродействие.

Рассмотрим шаги по использованию НабораЗаписей:

  1. Создадим объект НаборЗаписей для нужного регистра сведений.
  2. Установим отбор для набора записей, если необходимо работать с конкретной группой существующих записей или замещать их (подробнее об этом ниже).
  3. В цикле по элементам СпискаЗначений будем добавлять новые записи в НаборЗаписей с помощью метода Добавить().
  4. Заполним измерения и ресурсы каждой новой записи в наборе.
  5. После того как все записи добавлены в набор, вызовем метод Записать() для всего набора.

Пример кода с использованием НабораЗаписей:


// Предполагаем, что "Номенклатура" и "Документ" - реквизиты обработки,
// а "Работы" - реквизит типа СписокЗначений.

// 1. Создаем объект НаборЗаписей для нужного регистра
НаборЗаписей = РегистрыСведений.Работа2022.СоздатьНаборЗаписей();

// 2. Опционально: Установка отбора и чтение существующих записей.
// Это полезно, если вы хотите изменить уже существующие записи,
// относящиеся к данной Номенклатуре и Ссылке, или дополнить их.
// Если отбор не установлен, при Записать() со значением Замещать = Истина
// будут удалены все записи регистра, а затем записаны новые из набора.
НаборЗаписей.Отбор.Номенклатура.Установить(Номенклатура);
НаборЗаписей.Отбор.Ссылка.Установить(Документ.Ссылка);
// НаборЗаписей.Прочитать(); // Прочитает записи по установленному отбору в набор.
                           // Используйте, если вы хотите сначала прочитать существующие,
                           // модифицировать их или удалить выборочно перед записью.

// 3. Добавляем новые записи в набор из нашего СпискаЗначений
Для Каждого ЭлементСпискаРаботы Из Работы Цикл // ИЛИ "Работы"
    НоваяЗапись = НаборЗаписей.Добавить(); // Добавляем новую пустую запись в набор
    
    // Заполняем измерения и ресурсы новой записи
    НоваяЗапись.Номенклатура = Номенклатура;
    НоваяЗапись.Ссылка = Документ.Ссылка;
    НоваяЗапись.Работа = ЭлементСпискаРаботы.Значение; // Доступ к значению элемента списка
КонецЦикла;

// 4. Записываем весь набор записей в базу данных одной операцией
// По умолчанию, Замещать = Истина. Это означает, что все записи регистра,
// соответствующие установленному отбору, будут удалены, а затем записаны новые из набора.
НаборЗаписей.Записать(); 
// Если хотим только ДОБАВИТЬ новые записи без удаления старых (и готовы обрабатывать ошибки дублирования):
// НаборЗаписей.Записать(Ложь);

Режим записи НабораЗаписей: параметр «Замещать»

Метод Записать() объекта НаборЗаписей имеет важный необязательный параметр Замещать, который по умолчанию равен Истина. Этот параметр определяет поведение записи:

  1. Замещать = Истина (по умолчанию):
    • Если для НабораЗаписей был установлен отбор (как в примере выше, по Номенклатуре и Ссылке), то все существующие записи регистра, соответствующие этому отбору, будут удалены из базы данных, а затем вместо них будут записаны все записи, добавленные в наш НаборЗаписей. Это удобно, когда мы хотим полностью переопределить набор связанных записей.
    • Если отбор не был установлен, то все записи всего регистра сведений будут удалены, а затем будут записаны записи из нашего набора. Это крайне опасное поведение, которое может привести к потере данных, поэтому всегда будьте внимательны при использовании НабораЗаписей без отбора.
  2. Замещать = Ложь:
    • Записи из НабораЗаписей будут добавлены к существующим записям в регистре сведений. В этом режиме система контролирует уникальность записей по измерениям (и периоду для периодических регистров). Если мы попытаемся добавить запись, для которой уже существует запись с такими же значениями измерений, то будет сгенерирована ошибка, поскольку регистры сведений не допускают дубликатов по измерениям.

Выбор режима Замещать зависит от вашей бизнес-логики. Если вы всегда хотите, чтобы список работ для данной номенклатуры и ссылки был именно таким, какой вы передали в СпискеЗначений, и любые предыдущие записи должны быть удалены, используйте Замещать = Истина с обязательной установкой отбора. Если же вы хотите только добавить новые записи, избегая дубликатов, то используйте Замещать = Ложь, но будьте готовы обрабатывать возможные ошибки дублирования.

Контроль уникальности записей и обработка дубликатов

Критически важным аспектом при работе с регистрами сведений является контроль уникальности. В 1С не может существовать двух записей в одном регистре сведений, у которых полностью совпадают значения всех измерений (а также период для периодических регистров и регистратор для подчиненных регистров). Понимание этого механизма поможет избежать ошибок и обеспечить целостность данных.

Поведение при записи дубликатов:

  1. Через МенеджерЗаписи: Если при вызове Регистр.Записать() уже существует запись с такими же значениями измерений (и, возможно, периодом/регистратором), то существующая запись будет заменена новой. Старые значения ресурсов будут перезаписаны новыми значениями из менеджера записи.
  2. Через НаборЗаписей:
    • Если Замещать = Истина (с установленным отбором): все существующие записи по отбору удаляются, а затем записываются записи из набора. Это предотвращает дубликаты, так как старые записи уходят.
    • Если Замещать = Ложь: система попытается добавить записи. Если обнаружится, что запись с такими же измерениями уже существует, то возникнет ошибка "Запись с такими ключевыми полями уже существует!".

Стратегии предотвращения дубликатов или обновления существующих записей:

  1. Использование отбора в НабореЗаписей для замещения:

    Если ваша логика подразумевает, что для комбинации Номенклатура и Ссылка всегда должен быть только тот набор работ, который вы передаете сейчас, то оптимально использовать НаборЗаписей с отбором и Замещать = Истина. Это гарантирует, что старые работы для данного контекста будут удалены, а новые — записаны.

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

    Если вы хотите добавить новые работы, но при этом обновить ресурсы для уже существующих работ (если измерения совпадают), можно предварительно проверить наличие каждой записи с помощью запроса к регистру сведений. Этот подход более сложен, но дает больший контроль.

    
    // Пример для МенеджераЗаписи (если не используем НаборЗаписей с Замещать=Истина)
    Для Каждого ЭлементСпискаРаботы Из Работы Цикл // ИЛИ "Работы"
        // Определяем текущие измерения для поиска
        ТекущаяНоменклатура = Номенклатура;
        ТекущаяСсылка = Документ.Ссылка;
        ТекущаяРабота = ЭлементСпискаРаботы.Значение;
    
        // Создаем запрос для проверки наличия записи
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |    Работа2022.Номенклатура КАК Номенклатура,
            |    Работа2022.Ссылка КАК Ссылка,
            |    Работа2022.Работа КАК Работа
            |ИЗ
            |    РегистрСведений.Работа2022 КАК Работа2022
            |ГДЕ
            |    Работа2022.Номенклатура = &Номенклатура
            |    И Работа2022.Ссылка = &Ссылка
            |    И Работа2022.Работа = &Работа";
                
        Запрос.УстановитьПараметр("Номенклатура", ТекущаяНоменклатура);
        Запрос.УстановитьПараметр("Ссылка", ТекущаяСсылка);
        Запрос.УстановитьПараметр("Работа", ТекущаяРабота);
                
        РезультатЗапроса = Запрос.Выполнить().Выбрать();
                
        Если РезультатЗапроса.Следующий() Тогда
            // Запись с такими измерениями уже существует.
            // В данном случае, МенеджерЗаписи сам ее перезапишет,
            // поэтому дополнительный вызов Записать() не нужен, если ресурсов нет или они не меняются.
            // Если же есть ресурсы, которые нужно обновить:
            // Менеджер = РегистрыСведений.Работа2022.СоздатьМенеджерЗаписи();
            // Менеджер.Номенклатура = ТекущаяНоменклатура;
            // Менеджер.Ссылка = ТекущаяСсылка;
            // Менеджер.Работа = ТекущаяРабота;
            // Менеджер.<Ресурс1> = НовоеЗначениеРесурса1; // Обновляем ресурс
            // Менеджер.Записать();
            // Если цель - только добавить новые и пропустить существующие:
            Продолжить; 
        Иначе
            // Запись не существует, создаем новую
            Менеджер = РегистрыСведений.Работа2022.СоздатьМенеджерЗаписи();
            Менеджер.Номенклатура = ТекущаяНоменклатура;
            Менеджер.Ссылка = ТекущаяСсылка;
            Менеджер.Работа = ТекущаяРабота;
            // Заполняем ресурсы, если они есть
            Менеджер.Записать();
        КонецЕсли;
    КонецЦикла;
    

    Этот подход позволяет гибко управлять каждой записью, решая, обновить ли существующую, добавить новую или проигнорировать.

  3. Использование стандартных механизмов: В типовых конфигурациях 1С существуют обработки для поиска и удаления дублей, например, "Поиск и удаление дублей" в разделе "Администрирование – Обслуживание – Корректировка данных". Эти инструменты могут быть полезны для периодической чистки данных, но не для предотвращения дубликатов в процессе оперативной записи.

Особенности работы с управляемыми формами

Реквизит формы типа СписокЗначений, размещенный на управляемой форме, предоставляет удобный интерфейс для взаимодействия с коллекцией данных. Он может отображаться как таблица или как поле ввода, в зависимости от его свойств и способа использования. Для отображения данных регистра сведений на управляемой форме в виде таблицы, обычно используются реквизиты формы с типом ДинамическийСписок, привязанные к соответствующему регистру сведений. Это позволяет просматривать, фильтровать и сортировать данные регистра непосредственно на форме.

Заключение

Для эффективной и корректной записи множества значений из реквизита СписокЗначений управляемой формы обработки в регистр сведений, мы рекомендуем использовать объект НаборЗаписей. Этот подход обеспечивает лучшую производительность по сравнению с записью через МенеджерЗаписи в цикле. Важно тщательно продумать логику обработки дубликатов и режима замещения, чтобы обеспечить целостность и актуальность данных в вашем регистре сведений. Всегда устанавливайте отбор для НабораЗаписей при использовании параметра Замещать = Истина, чтобы избежать случайного удаления всех записей регистра.

← На главную