Как исправить ошибку "Записи регистра сведений стали неуникальными: РасчетПереоценкиВалютныхСредств" при обновлении 1С:Бухгалтерия?

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

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

Причины возникновения проблемы

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

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

  2. Изменение периодичности регистра: Как мы видим из обсуждаемой ситуации, одной из частых причин является изменение периодичности регистра. Если регистр был непериодическим, а стал периодическим (например, с периодичностью "День"), или периодичность изменилась с более грубой на более детальную (например, с "Месяц" на "День"), то записи, которые ранее были уникальными, могут стать дублями в рамках новой периодичности или ее отсутствия. В нашем случае, отчет о сравнении конфигураций четко указал на различие в периодичности: "Периодичность - Различаются значения". Это означает, что старые записи, возможно, не имевшие периода или имевшие период с меньшей детализацией, при переходе к новой структуре с более точным периодом или его появлением, образовали неуникальные комбинации измерений и периода.

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

Понимание ключа уникальности регистра сведений

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

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

Шаг 1: Выявляем дублирующиеся записи с помощью запроса

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

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

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


ВЫБРАТЬ
    РасчетПереоценкиВалютныхСредств.Период КАК ПериодРасчета,
    РасчетПереоценкиВалютныхСредств.Регистратор КАК Регистратор,
    РасчетПереоценкиВалютныхСредств.Валюта КАК Валюта,
    РасчетПереоценкиВалютныхСредств.Организация КАК Организация,
    РасчетПереоценкиВалютныхСредств.СчетУчета КАК СчетУчета,
    КОЛИЧЕСТВО(*) КАК КоличествоДублей
ИЗ
    РегистрСведений.РасчетПереоценкиВалютныхСредств КАК РасчетПереоценкиВалютныхСредств
СГРУППИРОВАТЬ ПО
    РасчетПереоценкиВалютныхСредств.Период,
    РасчетПереоценкиВалютныхСредств.Регистратор,
    РасчетПереоценкиВалютныхСредств.Валюта,
    РасчетПереоценкиВалютныхСредств.Организация,
    РасчетПереоценкиВалютныхСредств.СчетУчета
ИМЕЮЩИЕ
    КОЛИЧЕСТВО(*) > 1

Давайте разберем этот запрос:

  1. ВЫБРАТЬ ...: Мы выбираем все измерения регистра (Валюта, Организация, СчетУчета), поле Период и поле Регистратор, так как они формируют ключ уникальности. Дополнительно мы выбираем агрегатную функцию КОЛИЧЕСТВО(*), чтобы подсчитать, сколько записей попадает в каждую группу.

  2. ИЗ РегистрСведений.РасчетПереоценкиВалютныхСредств КАК РасчетПереоценкиВалютныхСредств: Указываем источник данных — наш регистр сведений.

  3. СГРУППИРОВАТЬ ПО ...: Это ключевая часть. Мы группируем записи по всем полям, которые определяют уникальность записи: Период, Регистратор, Валюта, Организация, СчетУчета. Каждая уникальная комбинация этих полей будет образовывать одну группу.

  4. ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1: Это условие применяется к результатам группировки. Оно отбирает только те группы, в которых количество записей больше единицы. Именно эти группы и содержат наши дубли.

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

Шаг 2: Программное удаление дублей для подчиненных регистраторов

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


НаборЗаписей = РегистрыСведений.РасчетПереоценкиВалютныхСредств.СоздатьНаборЗаписей;
НаборЗаписей.Отбор.ПериодРасчета.Установить(Выборка.ПериодРасчета); // Здесь будет ошибка

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

Для программного удаления дублей в подчиненном регистре сведений нам потребуется следующая последовательность действий:

  1. Получение дублирующихся записей вместе с регистраторами

    Мы уже сформировали запрос в Шаге 1, который выдает нам все дублирующие комбинации измерений, периода и регистратора. Выполним этот запрос и выгрузим его результат, например, в таблицу значений.

    
    Запрос = Новый Запрос;
    Запрос.Текст =
    "ВЫБРАТЬ
    |    РасчетПереоценкиВалютныхСредств.Период КАК ПериодРасчета,
    |    РасчетПереоценкиВалютныхСредств.Регистратор КАК Регистратор,
    |    РасчетПереоценкиВалютныхСредств.Валюта КАК Валюта,
    |    РасчетПереоценкиВалютныхСредств.Организация КАК Организация,
    |    РасчетПереоценкиВалютныхСредств.СчетУчета КАК СчетУчета,
    |    КОЛИЧЕСТВО(*) КАК Количество
    |ИЗ
    |    РегистрСведений.РасчетПереоценкиВалютныхСредств КАК РасчетПереоценкиВалютныхСредств
    |
    |СГРУППИРОВАТЬ ПО
    |    РасчетПереоценкиВалютныхСредств.Период,
    |    РасчетПереоценкиВалютныхСредств.Регистратор,
    |    РасчетПереоценкиВалютныхСредств.Валюта,
    |    РасчетПереоценкиВалютныхСредств.Организация,
    |    РасчетПереоценкиВалютныхСредств.СчетУчета
    |
    |ИМЕЮЩИЕ
    |    КОЛИЧЕСТВО(*) > 1";
    
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    

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

  2. Итерация по дублям и корректировка наборов записей

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

    1. Создадим набор записей для соответствующего регистратора: Мы должны получить набор записей для того документа, которому принадлежат дублирующие записи.

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

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

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

    3. Запишем измененный набор записей: После того как мы удалили все "лишние" дубли из объекта НаборЗаписей, нам необходимо записать эти изменения в базу данных.

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

Таким образом, мы итерируем по каждой группе дублей, получаем набор записей документа-регистратора, корректируем его, удаляя лишние записи, и сохраняем изменения.

Дополнительные рекомендации и важные моменты

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

  2. Перепроведение документов: После ручной или программной корректировки записей подчиненных регистров сведений, может потребоваться перепроведение документов-регистраторов. Это особенно актуально, если логика проведения этих документов зависит от записей в данном регистре. Если документы не будут перепроведены, их движения могут оказаться некорректными, что приведет к расхождениям в учете.

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

  4. Использование стандартных обработок: В некоторых случаях (например, для справочников или не подчиненных регистров) можно использовать стандартную обработку "Поиск и удаление дублей", которая находится в разделе "Администрирование" -> "Обслуживание" -> "Корректировка данных". Однако для подчиненных регистров сведений она обычно менее применима или требует глубокой настройки.

  5. Тестирование: Всегда тестируйте предложенное решение на копии рабочей базы перед применением на продуктивной системе. Это поможет убедиться в корректности кода и избежать новых ошибок.

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

← На главную