Как заменить номенклатуру во всех регистрах сведений и накопления в 1С на обычных формах?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

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

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

Способ 1. Использование типовой обработки «Поиск и замена значений»

Самый простой и безопасный способ, не требующий написания программного кода, — использование стандартной обработки с диска ИТС «Поиск и замена значений». Для конфигураций на базе БСП (Библиотеки стандартных подсистем) аналогом выступает инструмент «Поиск и удаление дублей».

Давайте проанализируем, как работает этот инструмент изнутри:

  1. Обработка выполняет глобальный поиск по всей базе данных с помощью системной функции НайтиПоСсылкам, определяя все места, где используется заменяемая номенклатура (документы, справочники, регистры сведений, регистры накопления, регистры бухгалтерии).
  2. Для регистров сведений и накопления обработка программно открывает наборы записей и напрямую перезаписывает ссылки старой номенклатуры на новую.
  3. При записи наборов используется свойство ОбменДанными.Загрузка = Истина. Это критически важный режим, который отключает стандартные проверки бизнес-логики, контроль остатков и позволяет вносить изменения даже в закрытых периодах (если у пользователя достаточно прав для обхода даты запрета редактирования).

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

Способ 2. Программный метод замены в подчиненных регистрах (через документы)

Если вы решили написать собственную обработку или интегрировать замену в существующий код вашей конфигурации, необходимо четко разделять регистры по их структуре. Рассмотрим первую категорию — регистры, подчиненные регистратору (к ним относятся все регистры накопления и часть регистров сведений).

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

  1. Находим все документы-регистраторы, содержащие старую номенклатуру.
  2. Программно заменяем номенклатуру в табличных частях и реквизитах этих документов.
  3. Перезаписываем документы без проведения с флагом загрузки, после чего корректируем движения.

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


// Определяем заменяемые объекты
СтараяНоменклатура = Справочники.Номенклатура.НайтиПоКоду("00000001");
НоваяНоменклатура  = Справочники.Номенклатура.НайтиПоКоду("00000002");

// Ищем все ссылки на старую номенклатуру в базе данных
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(СтараяНоменклатура);
ТаблицаСсылок = НайтиПоСсылкам(МассивСсылок);

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

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

Способ 3. Программный метод для независимых регистров сведений

Вторая категория — это независимые регистры сведений (например, цены номенклатуры, штрихкоды, счета учета номенклатуры). У них нет документа-регистратора, записи хранятся самостоятельно.

Главная сложность здесь заключается в том, что номенклатура практически всегда является ключевым полем (измерением) регистра. Если мы попытаемся просто заменить значение измерения в существующей записи на новую номенклатуру, система может выдать ошибку уникальности («Запись с такими ключевыми полями существует»), если для новой номенклатуры на эту же дату (или в этом же разрезе) уже есть запись в регистре.

Давайте разберем по шагам правильный алгоритм работы с независимыми регистрами:

  1. Прочитать существующий набор записей по отбору со старой номенклатурой.
  2. Выгрузить его в таблицу значений для обработки.
  3. Удалить старые записи из регистра.
  4. Для каждой строки из таблицы значений изменить номенклатуру на новую.
  5. Проверить, нет ли в регистре записи с новыми ключевыми полями. Если записи нет — записать измененную строку. Если запись уже существует — принять решение (например, перезаписать более актуальными данными или пропустить).

Посмотрим на пример кода для работы с периодическим независимым регистром сведений ЦеныНоменклатуры:


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

ТаблицаДанных = НаборСтарый.Выгрузить();

// Очищаем старые записи в регистре по старой номенклатуре
НаборСтарый.Очистить();
НаборСтарый.Записать();

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

Критические риски прямой замены данных

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

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

← На главную