Как искать и изменять записи в регистре сведений по измерениям и ресурсам в 1С?

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

При работе с системой 1С:Предприятие 8.3 мы часто сталкиваемся с необходимостью манипулировать данными, хранящимися в регистрах сведений. Одним из таких кейсов является поиск и изменение записей в регистре сведений (удобно через универсальное редактирование записей регистров сведений), когда условие отбора включает не только измерения (ключевые поля), но и ресурсы (хранимые значения).

Рассмотрим типовую ситуацию: у нас есть регистр сведений, который хранит информацию о сотрудниках и их рабочих местах. Пусть структура регистра такова: измерение - Документ (ссылка на документ, который ввел запись), ресурсы - ФизЛицо (ссылка на элемент справочника Физические лица) и НомерРабочегоМеста (строка или число), а для исправления ошибок может понадобиться корректировка движений регистров (поможет массовое исправление данных в регистрах). Нам необходимо найти записи по определенному Документу и конкретному ФизЛицу, а затем обновить НомерРабочегоМеста. Стандартный механизм отбора через объект НаборЗаписей.Отбор позволяет устанавливать условия только по измерениям регистра. Это означает, что мы не можем напрямую указать отбор по ресурсу ФизЛицо.

В этой статье мы подробно разберем, как эффективно решить данную задачу, используя мощные средства языка 1С:Предприятие.

1. Использование языка запросов 1С для отбора по измерениям и ресурсам

Наиболее гибким и рекомендуемым способом для выборки записей из регистра сведений, когда условие отбора включает значения как измерений, так и ресурсов, является использование языка запросов 1С. Запросы позволяют нам формировать сложные условия выборки, объединяя поля из разных частей регистра.

Принцип работы:

Мы сформируем запрос, который выберет нужные записи из нашего регистра сведений. В условии ГДЕ (WHERE) мы укажем параметры отбора для Документа (измерение) и ФизЛица (ресурс). Это позволит нам получить точный список записей, которые соответствуют нашим критериям.

Давайте посмотрим на пример такого запроса:


// Создаем новый объект Запрос
Запрос = Новый Запрос;

// Определяем текст запроса
Запрос.Текст = 
    "ВЫБРАТЬ
    |    РегистрСведений.Документ КАК Документ,
    |    РегистрСведений.ФизЛицо КАК ФизЛицо,
    |    РегистрСведений.НомерРабочегоМеста КАК НомерРабочегоМеста
    |ИЗ
    |    РегистрСведений.ВашРегистрСведений КАК РегистрСведений
    |ГДЕ
    |    РегистрСведений.Документ = &ПараметрДокумент
    |    И РегистрСведений.ФизЛицо = &ПараметрФизЛицо";

// Устанавливаем значения параметров запроса
// Предполагается, что у нас есть ссылки на документ и физлицо
// Например: ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
//           ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
Запрос.УстановитьПараметр("ПараметрДокумент", ВашДокументСсылка);
Запрос.УстановитьПараметр("ПараметрФизЛицо", ВашеФизЛицоСсылка); // Предполагается, что ФизЛицо - это ссылка

// Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();

// Получаем выборку детальных записей, которую мы будем перебирать
ВыборкаДетальныхЗаписей = РезультатЗапроса.Выбрать();

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

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

2. Изменение записей после отбора запросом

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

2.1. Изменение через объект НаборЗаписей (для группового изменения по измерениям)

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

Порядок действий:

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

Давайте рассмотрим пример:


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

// Создаем набор записей для нашего регистра
НаборЗаписей = РегистрыСведений.ВашРегистрСведений.СоздатьНаборЗаписей();

// Устанавливаем отбор по измерению "Документ"
// Важно: Отбор НабораЗаписей работает только по измерениям!
НаборЗаписей.Отбор.Документ.Установить(ВашДокументСсылка); 

// Читаем все записи из регистра, соответствующие установленному отбору по документу
НаборЗаписей.Прочитать();

// Флаг, который поможет нам определить, была ли запись найдена и изменена
ЗаписьИзменена = Ложь;

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

// Проверяем, была ли запись найдена и изменена
Если ЗаписьИзменена Тогда
    // Если изменения были внесены, записываем измененный набор обратно в регистр
    // Это сохранит все изменения, сделанные в данном наборе
    НаборЗаписей.Записать();
    Сообщить("Номер рабочего места для " + ВашеФизЛицоСсылка + " в документе " + ВашДокументСсылка + " обновлен.");
Иначе
    Сообщить("Запись для " + ВашеФизЛицоСсылка + " в документе " + ВашДокументСсылка + " не найдена для обновления.");
КонецЕсли;

Критически важно: При использовании метода НаборЗаписей.Записать() без предварительной установки отбора, весь регистр может быть очищен или перезаписан. Всегда убеждайтесь, что отбор установлен корректно, чтобы избежать потери данных и появления битых ссылок в регистрах — есть готовая диагностика и удаление поврежденных записей.

2.2. Изменение через объект МенеджерЗаписи (для точечного изменения)

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

Порядок действий:

  1. Создаем новый объект МенеджерЗаписи для нашего регистра.
  2. Устанавливаем значения всех измерений записи, которую мы хотим изменить. В вашем случае это Документ. Если ФизЛицо также является частью уникального ключа записи (например, если регистр непериодический и не имеет других измерений, а связка Документ + ФизЛицо должна быть уникальной), то его также нужно установить.
  3. Вызываем метод Прочитать() у МенеджерЗаписи. Этот метод попытается найти запись с заданными измерениями и загрузить ее в менеджер.
  4. Если запись найдена (метод Прочитать() вернул Истина), мы можем изменить значения ее ресурсов (например, НомерРабочегоМеста).
  5. Если запись не найдена, это может означать, что ее нужно создать. В этом случае мы просто устанавливаем все необходимые поля (измерения и ресурсы) и вызываем Записать().
  6. После внесения изменений, вызываем метод Записать(), чтобы сохранить запись в базе данных, используя редактор объектов информационной базы 8.3.

Рассмотрим пример использования МенеджерЗаписи:


// ... Предположим, у нас есть данные для уникальной идентификации записи
// ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
// НовыйНомерРабочегоМеста = "К205";

// Создаем менеджер записи для нашего регистра
МенеджерЗаписи = РегистрыСведений.ВашРегистрСведений.СоздатьМенеджерЗаписи();

// Устанавливаем значения измерений, которые уникально идентифицируют запись
// В нашем случае это Документ. Если ФизЛицо также участвует в ключе уникальности
// (например, если нет других измерений и для каждого Документа ФизЛицо уникально),
// то его также следует установить для идентификации.
// В данной структуре регистра (Измерение: Документ, Ресурсы: ФизЛицо, НомерРабочегоМеста),
// ФизЛицо по умолчанию является ресурсом, но фактически оно может выступать как часть
// логического ключа, по которому мы ищем.
// Для Прочитать() МенеджераЗаписи нужны только измерения.
// Если регистр имеет составной ключ (например, Документ + ФизЛицо - это измерения),
// то оба будут нужны для Прочитать().
// Но в вашем описании: Измерение - Документ, Ресурс - ФизЛицо.
// Значит, для Прочитать() достаточно только Документа, но это может быть не уникально.
// Чтобы найти конкретную запись, когда ФизЛицо - ресурс, мы должны либо получить
// все записи по документу и потом фильтровать, либо использовать запрос.
// Поэтому для точечного изменения по Документу И ФизЛицу, сначала нужно найти её запросом,
// а потом уже использовать МенеджерЗаписи, если регистр НЕ имеет ФизЛицо как измерение.

// Переопределим логику для МенеджераЗаписи, исходя из того, что ФизЛицо - РЕСУРС.
// Чтобы изменить конкретную запись, имея только Документ (измерение) и ФизЛицо (ресурс),
// мы должны сначала получить точные данные записи, включая все ее измерения и
// текущие значения ресурсов, чтобы потом создать МенеджерЗаписи с правильными ключами
// или, что проще, найти существующую запись по ее измерениям и потом программно
// проверить ресурс.

// Вернемся к изначальной идее: для МенеджераЗаписи нужны измерения для Прочитать()
// Если ваш регистр непериодический и имеет уникальность по Документу + ФизЛицо (что ФизЛицо тогда должно быть измерением),
// то код ниже будет работать.
// Если ФизЛицо - это ресурс, и Документ - единственное измерение, то запись по Документу
// может быть не уникальной. В этом случае, МенеджерЗаписи не сможет однозначно
// найти запись по одному только Документу, если для одного Документа есть несколько ФизЛиц.
// Тогда нам нужен запрос для уникальной выборки, как в начале.

// Давайте предположим, что регистр имеет измерения Документ и ФизЛицо
// (хотя в оригинале ФизЛицо названо ресурсом, часто на практике его делают измерением
// для таких целей). Если ФизЛицо - измерение, то:
// МенеджерЗаписи.Документ = ВыборкаДетальныхЗаписей.Документ; // Измерение
// МенеджерЗаписи.ФизЛицо = ВыборкаДетальныхЗаписей.ФизЛицо;   // Измерение

// Если же строго по условию, что ФизЛицо - РЕСУРС, то МенеджерЗаписи.Прочитать()
// не будет работать по нему. Тогда после выборки запросом, мы можем создать
// МенеджерЗаписи, установить его измерения, прочитать, а затем изменить ресурс,
// если найденная запись соответствует дополнительным условиям.

// Учитывая формулировку вопроса ("прописать эти НомераРабочиМест у каждого фИЗлИЦА"),
// наиболее вероятно, что для одного Документа может быть много ФизЛиц.
// И если ФизЛицо - ресурс, то запись в регистре идентифицируется ТОЛЬКО Документом,
// а ФизЛицо и НомерРабочегоМеста являются данными. Это означает, что для одного Документа
// может быть только ОДНА запись. Если же для одного Документа может быть несколько
// ФизЛиц, то ФизЛицо должно быть ИЗМЕРЕНИЕМ.
// Давайте исходить из того, что для уникальности записи в регистре, ФизЛицо все же
// является логической частью ключа, даже если оно формально объявлено ресурсом,
// или что мы ищем запись с определенным Документом и ФизЛицом, чтобы обновить её ресурс.

// Если для уникальной идентификации записи используются комбинации значений измерений,
// а в вашем случае ФизЛицо - это ресурс, то для МенеджераЗаписи мы должны использовать
// все измерения для метода Прочитать(). Если ФизЛицо, будучи ресурсом, является
// единственным критерием кроме Документа, тогда подход с МенеджеромЗаписи
// будет работать только если вы сначала нашли запись запросом и у вас есть все ее измерения.

// Давайте возьмем более общий и безопасный подход, когда мы уже знаем Документ и ФизЛицо
// (полученные, например, из выборки запроса).

// Для примера с МенеджеромЗаписи, допустим, мы хотим изменить запись
// с Документ = ВашДокументСсылка и ФизЛицо = ВашеФизЛицоСсылка.
// Если в регистре сведений уникальность записей определяется ТОЛЬКО значением измерения 'Документ'
// (т.е. для одного документа может быть только одна запись),
// и ФизЛицо является ресурсом, то МенеджерЗаписи будет выглядеть так:
МенеджерЗаписи = РегистрыСведений.ВашРегистрСведений.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Документ = ВашДокументСсылка; // Устанавливаем значение измерения

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

Важное уточнение: В данном примере с МенеджерЗаписи мы предполагаем, что комбинация Документ (измерение) и ФизЛицо (ресурс) является уникальной для вашего бизнес-процесса. Однако, если ФизЛицо является ресурсом, а не измерением, то по умолчанию регистр сведений обеспечивает уникальность только по значениям измерений. Если для одного Документа может быть несколько записей с разными ФизЛицами, то ФизЛицо должно быть оформлено как измерение регистра. Если же ФизЛицо остаётся ресурсом, то для точечного изменения единственной записи по Документу и ФизЛицу, вам потребуется: 1) найти эту запись запросом, как мы делали выше, 2) получить её уникальные измерения, 3) затем использовать МенеджерЗаписи с этими измерениями.

3. Итерация по выборке РегистрСведенийВыборка (менее эффективный способ)

Для обхода всех записей регистра сведений можно использовать объект РегистрСведенийВыборка. Этот подход позволяет перебирать записи одну за другой и программно проверять условия, в том числе по ресурсам. Однако, для больших объемов данных он может быть менее эффективным, чем прямой запрос с условием ГДЕ, поскольку сначала выбираются все записи, а затем фильтруются уже на стороне клиентского кода.

Когда это может быть полезно:

Этот подход может быть использован в случаях, когда требуется не просто отфильтровать, а выполнить более сложную логику проверки или агрегации данных, которую сложно выразить в рамках одного SQL-запроса, либо когда объем данных в регистре очень мал.


// ... Предположим, у нас есть ссылки для сравнения
// ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
// НовыйНомерРабочегоМеста = "К205";

// Создаем выборку для всего регистра
Выборка = РегистрыСведений.ВашРегистрСведений.Выбрать();

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

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

Заключение

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

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

← На главную