При работе с системой 1С:Предприятие 8.3 мы часто сталкиваемся с необходимостью манипулировать данными, хранящимися в регистрах сведений. Одним из таких кейсов является поиск и изменение записей в регистре сведений (удобно через универсальное редактирование записей регистров сведений), когда условие отбора включает не только измерения (ключевые поля), но и ресурсы (хранимые значения).
Рассмотрим типовую ситуацию: у нас есть регистр сведений, который хранит информацию о сотрудниках и их рабочих местах. Пусть структура регистра такова: измерение - Документ (ссылка на документ, который ввел запись), ресурсы - ФизЛицо (ссылка на элемент справочника Физические лица) и НомерРабочегоМеста (строка или число), а для исправления ошибок может понадобиться корректировка движений регистров (поможет массовое исправление данных в регистрах). Нам необходимо найти записи по определенному Документу и конкретному ФизЛицу, а затем обновить НомерРабочегоМеста. Стандартный механизм отбора через объект НаборЗаписей.Отбор позволяет устанавливать условия только по измерениям регистра. Это означает, что мы не можем напрямую указать отбор по ресурсу ФизЛицо.
В этой статье мы подробно разберем, как эффективно решить данную задачу, используя мощные средства языка 1С:Предприятие.
Наиболее гибким и рекомендуемым способом для выборки записей из регистра сведений, когда условие отбора включает значения как измерений, так и ресурсов, является использование языка запросов 1С. Запросы позволяют нам формировать сложные условия выборки, объединяя поля из разных частей регистра.
Принцип работы:
Мы сформируем запрос, который выберет нужные записи из нашего регистра сведений. В условии ГДЕ (WHERE) мы укажем параметры отбора для Документа (измерение) и ФизЛица (ресурс). Это позволит нам получить точный список записей, которые соответствуют нашим критериям.
Давайте посмотрим на пример такого запроса:
// Создаем новый объект Запрос
Запрос = Новый Запрос;
// Определяем текст запроса
Запрос.Текст =
"ВЫБРАТЬ
| РегистрСведений.Документ КАК Документ,
| РегистрСведений.ФизЛицо КАК ФизЛицо,
| РегистрСведений.НомерРабочегоМеста КАК НомерРабочегоМеста
|ИЗ
| РегистрСведений.ВашРегистрСведений КАК РегистрСведений
|ГДЕ
| РегистрСведений.Документ = &ПараметрДокумент
| И РегистрСведений.ФизЛицо = &ПараметрФизЛицо";
// Устанавливаем значения параметров запроса
// Предполагается, что у нас есть ссылки на документ и физлицо
// Например: ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
Запрос.УстановитьПараметр("ПараметрДокумент", ВашДокументСсылка);
Запрос.УстановитьПараметр("ПараметрФизЛицо", ВашеФизЛицоСсылка); // Предполагается, что ФизЛицо - это ссылка
// Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();
// Получаем выборку детальных записей, которую мы будем перебирать
ВыборкаДетальныхЗаписей = РезультатЗапроса.Выбрать();
// Перебираем каждую запись, найденную запросом
Пока ВыборкаДетальныхЗаписей.Следующий() Цикл
// Здесь мы можем получить доступ к данным каждой записи:
// Документ = ВыборкаДетальныхЗаписей.Документ;
// ФизЛицо = ВыборкаДетальныхЗаписей.ФизЛицо;
// НомерРабочегоМеста = ВыборкаДетальныхЗаписей.НомерРабочегоМеста;
// И далее выполнить необходимые действия, например, подготовиться к изменению
// ТекущийНомерРабочегоМеста = ВыборкаДетальныхЗаписей.НомерРабочегоМеста;
// Сообщить("Найдена запись: Документ=" + Документ + ", ФизЛицо=" + ФизЛицо + ", НомерРабочегоМеста=" + НомерРабочегоМеста);
КонецЦикла;
Как мы видим, запрос позволяет нам очень точно отобрать необходимые записи, даже если часть условий относится к ресурсам. После выполнения запроса мы получаем выборку, которую можем использовать для дальнейших операций, таких как изменение существующих записей или создание новых.
После того как мы получили нужные записи с помощью запроса, следующим шагом будет их изменение. В 1С есть два основных механизма для изменения записей регистра сведений: через объект НаборЗаписей для групповых операций и через объект МенеджерЗаписи для точечных изменений. Выбор метода зависит от того, сколько записей мы собираемся менять и насколько уникально каждая запись идентифицируется.
Если нам нужно изменить НомерРабочегоМеста для группы записей, которые мы отобрали запросом, при условии, что эти записи относятся к одному и тому же значению измерения Документ, мы можем использовать объект НаборЗаписей. Этот способ особенно удобен, когда нам нужно обновить несколько записей, относящихся к одному измерению, но отфильтрованных по ресурсам.
Порядок действий:
НаборЗаписей для нашего регистра.НаборЗаписей по измерениям регистра (в нашем случае это Документ). Это критически важный шаг, так как НаборЗаписей работает только с отбором по измерениям.Прочитать() для НаборЗаписей, чтобы загрузить в него все записи, соответствующие установленному отбору по Документу.ФизЛицо), которое мы изначально использовали в запросе.НомерРабочегоМеста.Записать() у НаборЗаписей, чтобы сохранить изменения в базе данных, либо используем универсальное редактирование движений документа с возможностью импорта / экспорта.Давайте рассмотрим пример:
// ... Предположим, мы уже выполнили запрос и получили ВыборкуДетальныхЗаписей
// и у нас есть ссылка на текущий документ и физлицо, для которого хотим обновить данные
// Например, из формы или из параметров
// ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
// НовыйНомерРабочегоМеста = "К205";
// Создаем набор записей для нашего регистра
НаборЗаписей = РегистрыСведений.ВашРегистрСведений.СоздатьНаборЗаписей();
// Устанавливаем отбор по измерению "Документ"
// Важно: Отбор НабораЗаписей работает только по измерениям!
НаборЗаписей.Отбор.Документ.Установить(ВашДокументСсылка);
// Читаем все записи из регистра, соответствующие установленному отбору по документу
НаборЗаписей.Прочитать();
// Флаг, который поможет нам определить, была ли запись найдена и изменена
ЗаписьИзменена = Ложь;
// Теперь нам нужно найти в этом наборе записей ту, которая соответствует нашему ФизЛицу (ресурсу)
// и изменить ее НомерРабочегоМеста
Для Каждого ЗаписьРегистра Из НаборЗаписей Цикл
Если ЗаписьРегистра.ФизЛицо = ВашеФизЛицоСсылка Тогда
// Мы нашли нужную запись!
ЗаписьРегистра.НомерРабочегоМеста = НовыйНомерРабочегоМеста; // Устанавливаем новое значение ресурса
ЗаписьИзменена = Истина;
Прервать; // Если мы ищем конкретную запись, то можем выйти после ее нахождения
КонецЕсли;
КонецЦикла;
// Проверяем, была ли запись найдена и изменена
Если ЗаписьИзменена Тогда
// Если изменения были внесены, записываем измененный набор обратно в регистр
// Это сохранит все изменения, сделанные в данном наборе
НаборЗаписей.Записать();
Сообщить("Номер рабочего места для " + ВашеФизЛицоСсылка + " в документе " + ВашДокументСсылка + " обновлен.");
Иначе
Сообщить("Запись для " + ВашеФизЛицоСсылка + " в документе " + ВашДокументСсылка + " не найдена для обновления.");
КонецЕсли;
Критически важно: При использовании метода НаборЗаписей.Записать() без предварительной установки отбора, весь регистр может быть очищен или перезаписан. Всегда убеждайтесь, что отбор установлен корректно, чтобы избежать потери данных и появления битых ссылок в регистрах — есть готовая диагностика и удаление поврежденных записей.
Если нам нужно изменить одну конкретную запись, и мы можем однозначно идентифицировать ее с помощью всех измерений, то для этой цели идеально подходит объект МенеджерЗаписи. МенеджерЗаписи позволяет работать с отдельной записью регистра, предоставляя удобные методы для ее чтения, изменения и записи.
Порядок действий:
МенеджерЗаписи для нашего регистра.Документ. Если ФизЛицо также является частью уникального ключа записи (например, если регистр непериодический и не имеет других измерений, а связка Документ + ФизЛицо должна быть уникальной), то его также нужно установить.Прочитать() у МенеджерЗаписи. Этот метод попытается найти запись с заданными измерениями и загрузить ее в менеджер.Прочитать() вернул Истина), мы можем изменить значения ее ресурсов (например, НомерРабочегоМеста).Записать().Записать(), чтобы сохранить запись в базе данных, используя редактор объектов информационной базы 8.3.Рассмотрим пример использования МенеджерЗаписи:
// ... Предположим, у нас есть данные для уникальной идентификации записи
// ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
// НовыйНомерРабочегоМеста = "К205";
// Создаем менеджер записи для нашего регистра
МенеджерЗаписи = РегистрыСведений.ВашРегистрСведений.СоздатьМенеджерЗаписи();
// Устанавливаем значения измерений, которые уникально идентифицируют запись
// В нашем случае это Документ. Если ФизЛицо также участвует в ключе уникальности
// (например, если нет других измерений и для каждого Документа ФизЛицо уникально),
// то его также следует установить для идентификации.
// В данной структуре регистра (Измерение: Документ, Ресурсы: ФизЛицо, НомерРабочегоМеста),
// ФизЛицо по умолчанию является ресурсом, но фактически оно может выступать как часть
// логического ключа, по которому мы ищем.
// Для Прочитать() МенеджераЗаписи нужны только измерения.
// Если регистр имеет составной ключ (например, Документ + ФизЛицо - это измерения),
// то оба будут нужны для Прочитать().
// Но в вашем описании: Измерение - Документ, Ресурс - ФизЛицо.
// Значит, для Прочитать() достаточно только Документа, но это может быть не уникально.
// Чтобы найти конкретную запись, когда ФизЛицо - ресурс, мы должны либо получить
// все записи по документу и потом фильтровать, либо использовать запрос.
// Поэтому для точечного изменения по Документу И ФизЛицу, сначала нужно найти её запросом,
// а потом уже использовать МенеджерЗаписи, если регистр НЕ имеет ФизЛицо как измерение.
// Переопределим логику для МенеджераЗаписи, исходя из того, что ФизЛицо - РЕСУРС.
// Чтобы изменить конкретную запись, имея только Документ (измерение) и ФизЛицо (ресурс),
// мы должны сначала получить точные данные записи, включая все ее измерения и
// текущие значения ресурсов, чтобы потом создать МенеджерЗаписи с правильными ключами
// или, что проще, найти существующую запись по ее измерениям и потом программно
// проверить ресурс.
// Вернемся к изначальной идее: для МенеджераЗаписи нужны измерения для Прочитать()
// Если ваш регистр непериодический и имеет уникальность по Документу + ФизЛицо (что ФизЛицо тогда должно быть измерением),
// то код ниже будет работать.
// Если ФизЛицо - это ресурс, и Документ - единственное измерение, то запись по Документу
// может быть не уникальной. В этом случае, МенеджерЗаписи не сможет однозначно
// найти запись по одному только Документу, если для одного Документа есть несколько ФизЛиц.
// Тогда нам нужен запрос для уникальной выборки, как в начале.
// Давайте предположим, что регистр имеет измерения Документ и ФизЛицо
// (хотя в оригинале ФизЛицо названо ресурсом, часто на практике его делают измерением
// для таких целей). Если ФизЛицо - измерение, то:
// МенеджерЗаписи.Документ = ВыборкаДетальныхЗаписей.Документ; // Измерение
// МенеджерЗаписи.ФизЛицо = ВыборкаДетальныхЗаписей.ФизЛицо; // Измерение
// Если же строго по условию, что ФизЛицо - РЕСУРС, то МенеджерЗаписи.Прочитать()
// не будет работать по нему. Тогда после выборки запросом, мы можем создать
// МенеджерЗаписи, установить его измерения, прочитать, а затем изменить ресурс,
// если найденная запись соответствует дополнительным условиям.
// Учитывая формулировку вопроса ("прописать эти НомераРабочиМест у каждого фИЗлИЦА"),
// наиболее вероятно, что для одного Документа может быть много ФизЛиц.
// И если ФизЛицо - ресурс, то запись в регистре идентифицируется ТОЛЬКО Документом,
// а ФизЛицо и НомерРабочегоМеста являются данными. Это означает, что для одного Документа
// может быть только ОДНА запись. Если же для одного Документа может быть несколько
// ФизЛиц, то ФизЛицо должно быть ИЗМЕРЕНИЕМ.
// Давайте исходить из того, что для уникальности записи в регистре, ФизЛицо все же
// является логической частью ключа, даже если оно формально объявлено ресурсом,
// или что мы ищем запись с определенным Документом и ФизЛицом, чтобы обновить её ресурс.
// Если для уникальной идентификации записи используются комбинации значений измерений,
// а в вашем случае ФизЛицо - это ресурс, то для МенеджераЗаписи мы должны использовать
// все измерения для метода Прочитать(). Если ФизЛицо, будучи ресурсом, является
// единственным критерием кроме Документа, тогда подход с МенеджеромЗаписи
// будет работать только если вы сначала нашли запись запросом и у вас есть все ее измерения.
// Давайте возьмем более общий и безопасный подход, когда мы уже знаем Документ и ФизЛицо
// (полученные, например, из выборки запроса).
// Для примера с МенеджеромЗаписи, допустим, мы хотим изменить запись
// с Документ = ВашДокументСсылка и ФизЛицо = ВашеФизЛицоСсылка.
// Если в регистре сведений уникальность записей определяется ТОЛЬКО значением измерения 'Документ'
// (т.е. для одного документа может быть только одна запись),
// и ФизЛицо является ресурсом, то МенеджерЗаписи будет выглядеть так:
МенеджерЗаписи = РегистрыСведений.ВашРегистрСведений.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Документ = ВашДокументСсылка; // Устанавливаем значение измерения
// Пытаемся прочитать запись.
Если МенеджерЗаписи.Прочитать() Тогда
// Запись найдена. Теперь можем проверить ресурс ФизЛицо, если для нас это важно,
// или сразу изменить НомерРабочегоМеста.
// Если ФизЛицо в регистре уже соответствует нашему ВашеФизЛицоСсылка, то меняем:
Если МенеджерЗаписи.ФизЛицо = ВашеФизЛицоСсылка Тогда
МенеджерЗаписи.НомерРабочегоМеста = НовыйНомерРабочегоМеста; // Устанавливаем новое значение
МенеджерЗаписи.Записать();
Сообщить("Запись успешно обновлена через МенеджерЗаписи.");
Иначе
// Если запись найдена по Документу, но ФизЛицо не совпадает,
// это может означать ошибку в логике или что для этого Документа
// уже есть запись с другим физлицом.
Сообщить("Найдена запись по документу, но физлицо не совпадает. Обновление невозможно.");
КонецЕсли;
Иначе
// Запись не найдена по Документу. Возможно, нужно создать новую.
// При создании новой записи, мы должны установить и измерения, и ресурсы.
МенеджерЗаписи.Документ = ВашДокументСсылка;
МенеджерЗаписи.ФизЛицо = ВашеФизЛицоСсылка;
МенеджерЗаписи.НомерРабочегоМеста = НовыйНомерРабочегоМеста;
МенеджерЗаписи.Записать();
Сообщить("Новая запись успешно создана через МенеджерЗаписи.");
КонецЕсли;
Важное уточнение: В данном примере с МенеджерЗаписи мы предполагаем, что комбинация Документ (измерение) и ФизЛицо (ресурс) является уникальной для вашего бизнес-процесса. Однако, если ФизЛицо является ресурсом, а не измерением, то по умолчанию регистр сведений обеспечивает уникальность только по значениям измерений. Если для одного Документа может быть несколько записей с разными ФизЛицами, то ФизЛицо должно быть оформлено как измерение регистра. Если же ФизЛицо остаётся ресурсом, то для точечного изменения единственной записи по Документу и ФизЛицу, вам потребуется: 1) найти эту запись запросом, как мы делали выше, 2) получить её уникальные измерения, 3) затем использовать МенеджерЗаписи с этими измерениями.
Для обхода всех записей регистра сведений можно использовать объект РегистрСведенийВыборка. Этот подход позволяет перебирать записи одну за другой и программно проверять условия, в том числе по ресурсам. Однако, для больших объемов данных он может быть менее эффективным, чем прямой запрос с условием ГДЕ, поскольку сначала выбираются все записи, а затем фильтруются уже на стороне клиентского кода.
Когда это может быть полезно:
Этот подход может быть использован в случаях, когда требуется не просто отфильтровать, а выполнить более сложную логику проверки или агрегации данных, которую сложно выразить в рамках одного SQL-запроса, либо когда объем данных в регистре очень мал.
// ... Предположим, у нас есть ссылки для сравнения
// ВашДокументСсылка = Документы.МойДокумент.НайтиПоКоду("000000001");
// ВашеФизЛицоСсылка = Справочники.ФизическиеЛица.НайтиПоНаименованию("Иванов И.И.");
// НовыйНомерРабочегоМеста = "К205";
// Создаем выборку для всего регистра
Выборка = РегистрыСведений.ВашРегистрСведений.Выбрать();
// Перебираем все записи в регистре
Пока Выборка.Следующий() Цикл
// Проверяем условия по измерениям и ресурсам программно
Если Выборка.Документ = ВашДокументСсылка И Выборка.ФизЛицо = ВашеФизЛицоСсылка Тогда
// Мы нашли нужную запись!
Сообщить("Найдена запись: Документ=" + Выборка.Документ + ", ФизЛицо=" + Выборка.ФизЛицо + ", НомерРабочегоМеста=" + Выборка.НомерРабочегоМеста);
// Для изменения этой записи нам потребуется либо МенеджерЗаписи,
// либо НаборЗаписей, как мы описывали выше.
// Например, используя МенеджерЗаписи:
МенеджерЗаписиДляИзменения = РегистрыСведений.ВашРегистрСведений.СоздатьМенеджерЗаписи();
МенеджерЗаписиДляИзменения.Документ = Выборка.Документ;
// Если ФизЛицо является измерением, то:
// МенеджерЗаписиДляИзменения.ФизЛицо = Выборка.ФизЛицо;
Если МенеджерЗаписиДляИзменения.Прочитать() Тогда
// Убеждаемся, что мы работаем с той же записью
Если МенеджерЗаписиДляИзменения.ФизЛицо = Выборка.ФизЛицо Тогда
МенеджерЗаписиДляИзменения.НомерРабочегоМеста = НовыйНомерРабочегоМеста;
МенеджерЗаписиДляИзменения.Записать();
Сообщить("Запись успешно обновлена во время итерации.");
КонецЕсли;
КонецЕсли;
// Если мы ищем одну конкретную запись, можно выйти после ее нахождения
// Прервать;
КонецЕсли;
КонецЦикла;
Важное замечание: При изменении записей в цикле Пока Выборка.Следующий(), необходимо быть очень осторожным. Если вы изменяете записи в том же регистре, из которого идет выборка, это может привести к непредсказуемым результатам или ошибкам блокировки. Лучше сначала собрать все необходимые изменения в отдельную структуру данных (например, таблица значений), а затем выполнить изменения в регистре после завершения выборки.
Мы видим, что для эффективного поиска и изменения записей в регистре сведений по комбинации измерений и ресурсов, наиболее подходящим и производительным методом является использование языка запросов 1С для выборки данных. Затем, в зависимости от объема и специфики изменений, мы можем использовать объекты НаборЗаписей для группового обновления или МенеджерЗаписи для точечного изменения конкретных записей.
Всегда помните о необходимости вызывать метод .Записать() у НаборЗаписей или МенеджерЗаписи после внесения любых изменений, чтобы сохранить их в базе данных. Правильное понимание и применение этих механизмов позволит нам создавать надежные и эффективные решения в 1С:Предприятие.