Как изменить значение поля-измерения в регистре сведений 1С:Предприятия?

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

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

Причина невозможности прямого изменения измерения

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

Представьте, что у вас есть запись с ключом (Измерение1=А, Измерение2=Б). Если вы попытаетесь изменить Измерение1 на В, система будет рассматривать это как попытку создать новую запись с ключом (Измерение1=В, Измерение2=Б) вместо существующей. Поскольку регистр сведений по своей природе не позволяет напрямую "переименовывать" ключи, платформа выдаст ошибку, например, "Запись не верна! Значение поля не соответствует установленному отбору" или "Попытка изменения поля, входящего в ключ записи, через отбор".

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

Решение: Удаление старых записей и создание новых с измененным значением измерения

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

Шаг 1: Выборка записей для изменения

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


Набор = РегистрыСведений.РеестрРамБалок.СоздатьНаборЗаписей();
Набор.Отбор.КВыгрузке.Установить(Истина);
Набор.Прочитать();

Здесь мы создаем НаборЗаписей для регистра РеестрРамБалок. Затем устанавливаем отбор по измерению КВыгрузке, указывая, что нас интересуют только те записи, где это измерение равно Истина. Метод Прочитать() загружает все соответствующие записи в наш набор.

Шаг 2: Изменение значений и создание новых записей

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


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

В этом цикле для каждой записи Стр из нашего набора мы выполняем следующие действия:

  1. Создаем менеджер записи: МЗ = РегистрыСведений.РеестрРамБалок.СоздатьМенеджерЗаписей();. МенеджерЗаписей – это объект, который позволяет нам работать с одной конкретной записью регистра сведений.
  2. Копируем свойства: ЗаполнитьЗначенияСвойств(МЗ, Стр);. Эта удобная функция позволяет скопировать все значения свойств (измерений, ресурсов, реквизитов) из одной структуры (в данном случае из записи Стр) в другую (в наш МенеджерЗаписей МЗ). Это гарантирует, что все остальные измерения, а также ресурсы и реквизиты, будут скопированы без изменений.
  3. Изменяем измерение: МЗ.КВыгрузке = Ложь;. Здесь мы устанавливаем новое значение для нашего измерения КВыгрузке. Важно понимать, что в этот момент в МЗ формируется новый уникальный ключ записи.
  4. Записываем новую запись: МЗ.Записать();. Этот метод попытается найти запись с ключом, соответствующим значениям измерений в МЗ. Если такой записи нет, будет создана новая запись. Если запись с таким же набором измерений (т.е. с новым ключом, где КВыгрузке = Ложь, а все остальные измерения совпадают с исходными) уже существует, она будет перезаписана. В нашем случае, поскольку мы меняем булево измерение с Истина на Ложь, скорее всего будет создана новая уникальная запись.

Шаг 3: Удаление старых записей

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


Набор.Очистить();
Набор.Записать();

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

Таким образом, полный код решения выглядит следующим образом:


Набор = РегистрыСведений.РеестрРамБалок.СоздатьНаборЗаписей();
Набор.Отбор.КВыгрузке.Установить(Истина);
Набор.Прочитать();

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

Набор.Очистить(); // Удаляем записи из набора в памяти
Набор.Записать(); // Удаляем записи из базы данных, которые были прочитаны в набор

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

Альтернативный подход с использованием запроса и Менеджера записей

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

Шаг 1: Выборка данных с помощью запроса

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Рег.*
    |ИЗ
    |    РегистрСведений.РеестрРамБалок КАК Рег
    |ГДЕ
    |    Рег.КВыгрузке = ИСТИНА";
Выборка = Запрос.Выполнить().Выбрать();

В этом запросе мы выбираем все поля (`Рег.*`) из нашего регистра РеестрРамБалок, где измерение КВыгрузке равно ИСТИНА. Результатом выполнения запроса будет объект Выборка, по которому мы можем итерироваться.

Шаг 2: Создание новых записей

Далее мы обходим полученную выборку и для каждой строки создаем новую запись с измененным значением измерения КВыгрузке.


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

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

Шаг 3: Удаление старых записей

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


// Повторная выборка для удаления старых записей
ЗапросДляУдаления = Новый Запрос;
ЗапросДляУдаления.Текст = 
    "ВЫБРАТЬ
    |    Рег.*
    |ИЗ
    |    РегистрСведений.РеестрРамБалок КАК Рег
    |ГДЕ
    |    Рег.КВыгрузке = ИСТИНА";
ВыборкаДляУдаления = ЗапросДляУдаления.Выполнить().Выбрать();

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

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

Производительность и архитектурные соображения

Мы, как разработчики, всегда должны думать о производительности и правильности архитектуры. Давайте проанализируем эти аспекты.

Влияние на производительность

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

Архитектура регистра: boolean-измерение

В данном случае, измерение КВыгрузке имеет булевый тип. Давайте задумаемся, насколько целесообразно использовать булево поле в качестве измерения регистра сведений, особенно если оно фактически отражает статус записи (например, "к выгрузке" или "не к выгрузке").

  1. Проблемы изменения: Как мы убедились, изменение булева измерения вызывает сложности, требуя удаления и создания новых записей. Это усложняет код и снижает производительность.
  2. Потенциальные дубли: Если измерение КВыгрузке является единственным отличием между записями, то две записи могут иметь одинаковые значения по всем остальным измерениям, отличаясь только этим флагом. Например:
    • (ИзмерениеА=1, ИзмерениеБ=2, КВыгрузке=Истина, Ресурс=X)
    • (ИзмерениеА=1, ИзмерениеБ=2, КВыгрузке=Ложь, Ресурс=Y)
    Такая структура может быть осмысленной, если Ресурс (или другой реквизит) также должен отличаться в зависимости от состояния КВыгрузке. Однако, если разница только в булевом флаге, и значения остальных ресурсов/реквизитов должны быть одинаковыми, то это усложняет логику.
  3. Альтернативное решение: Ресурс или Реквизит: В большинстве случаев, если поле отражает статус записи и не является частью уникального ключа, по которому необходимо обязательно различать записи, его целесообразнее разместить в ресурсах или реквизитах регистра.
    • Ресурс: Если КВыгрузке является числовым, булевым или другим значением, которое "характеризует" набор измерений, то оно может быть ресурсом. Ресурсы - это данные, хранящиеся на пересечении измерений. Для периодических регистров ресурсов, они возвращаются методами СрезПервых() и СрезПоследних().
    • Реквизит: Если КВыгрузке является дополнительной, неключевой информацией о записи, то его можно сделать реквизитом. Реквизиты – это дополнительные атрибуты каждой записи. Изменение ресурса или реквизита не приводит к изменению ключа записи, и их можно модифицировать напрямую, без удаления и создания новых записей.
  4. Требования к измерениям: Регистр сведений должен содержать хотя бы одно измерение. Измерения автоматически включаются в основной отбор регистра и являются основой для построения индексов в базе данных.
  5. Порядок измерений: Порядок следования измерений также важен. Доступ к первым измерениям обычно осуществляется быстрее, чем к последним, и это влияет на эффективность использования индексов в базе данных.

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

Важные моменты и рекомендации

  1. Тестирование: Мы всегда должны тщательно тестировать такие операции на копии базы данных перед применением на рабочем контуре, особенно при массовом изменении данных.
  2. Транзакции: Убедитесь, что операция выполняется в рамках одной транзакции (если это не так по умолчанию), чтобы обеспечить атомарность (все или ничего) и предотвратить потерю данных в случае ошибки.
  3. Уникальность: При создании новых записей всегда помните о требовании уникальности ключа регистра. Если по какой-то причине уже существует запись с новым ключом (то есть, с КВыгрузке = Ложь и теми же остальными измерениями), то МЗ.Записать() перезапишет ее, а не создаст дубликат.

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

← На главную