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