Как правильно передать значение из одного реквизита в другой в 1С

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

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

Разбор типовой ситуации и анализ ошибок

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


Выборка = Справочники.Автоработы.ВыбратьИерархически();
Пока Выборка.Следующий() Цикл
    Если Выборка.ЭтоГруппа Тогда
        Продолжить;
    КонецЕсли;
    
    СпраОБ = Выборка.Ссылка.ПолучитьОбъект();
    
    Если Выборка.Артикул = "" Тогда
        // Ошибка логики: пустое условие или неверное использование Иначе
    Иначе
        Сообщить("До " + Выборка.Артикул);
        СпраОБ.Артикул = Выборка.НомерПозиции;
    КонецЕсли;
    
    СпраОБ.Записать();
КонецЦикла;

Проанализируем причины, по которым этот код может работать некорректно:

  1. Инверсия условия: В блоке Если...Иначе часто путают ветки выполнения. Если мы хотим обрабатывать пустые значения, код записи должен находиться сразу после Тогда, либо нужно использовать отрицание Если Не ЗначениеЗаполнено(...).
  2. Чтение из выборки вместо объекта: При выводе отладочных сообщений через Сообщить() разработчики часто обращаются к Выборка.Артикул. Однако Выборка хранит состояние данных на момент начала обхода. Если вы изменили СпраОБ.Артикул, в выборке значение останется старым до следующего запроса к базе.
  3. Доступность для записи: Попытка присвоить значение напрямую свойству выборки (Выборка.Артикул = ...) всегда приведет к ошибке "Поле объекта недоступно для записи". Записывать данные можно только в объект, полученный через ПолучитьОбъект().

Правильный алгоритм переноса данных

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


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

Оптимизация работы с большими объемами данных

Если в вашем справочнике, как упоминалось в исходной теме, около 700 000 записей, стандартный перебор через Выбрать() с последующим ПолучитьОбъект() и Записать() будет выполняться крайне медленно (может занять несколько часов или даже привести к зависанию системы). Рассмотрим шаги по оптимизации этого процесса.

1. Использование свойства ОбменДанными.Загрузка

При вызове метода Записать() 1С выполняет все проверки в модуле объекта и срабатывают все подписки на события. При техническом переносе данных это часто лишнее. Чтобы ускорить запись в разы, используйте следующий прием:


ОбъектСправочника.ОбменДанными.Загрузка = Истина;
ОбъектСправочника.Записать();

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

2. Использование Запроса вместо Выборки

Метод Выбрать() заставляет систему сканировать всю таблицу. Если нам нужны только элементы с пустым артикулом, гораздо эффективнее использовать Запрос. Это позволит отобрать только нужные 10-20% записей (если остальные уже заполнены), что существенно снизит нагрузку на сервер.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Автоработы.Ссылка КАК Ссылка,
    |   Автоработы.НомерПозиции КАК НомерПозиции
    |ИЗ
    |   Справочник.Автоработы КАК Автоработы
    |ГДЕ
    |   Автоработы.Артикул = """"
    |   И НЕ Автоработы.ЭтоГруппа";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Обработка только нужных элементов
КонецЦикла;

3. Группировка в транзакции

Каждый вызов метода Записать() вне явной транзакции инициирует отдельную транзакцию в СУБД, что очень накладно по времени. Проанализируем ситуацию: если записывать элементы порциями по 1000 штук в одной транзакции, скорость возрастет в десятки раз.


Счетчик = 0;
НачатьТранзакцию();
Пока Выборка.Следующий() Цикл
    // ... логика получения объекта и присвоения ...
    ОбъектСправочника.Записать();
    
    Счетчик = Счетчик + 1;
    Если Счетчик % 1000 = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
        Сообщить("Обработано " + Счетчик + " записей...");
    КонецЕсли;
КонецЦикла;
ЗафиксироватьТранзакцию();

Нюансы проверки на "пустоту"

Важно помнить, что сравнение Артикул = "" корректно только для строковых типов данных. Если Артикул имеет составной тип или тип ссылки, такая проверка может не сработать. Разберем наиболее универсальные способы:

Использование типовых инструментов

Прежде чем писать код вручную, проанализируем возможности вашей конфигурации. В большинстве современных решений (БП 3.0, УТ 11, ERP) встроена обработка "Групповое изменение реквизитов". Она позволяет:

  1. Отобрать элементы справочника по сложным фильтрам.
  2. Выбрать реквизит Артикул.
  3. В режиме разработчика (в дополнительных настройках) указать произвольный код или просто выбрать копирование значения из другого поля.

Этот инструмент уже оптимизирован для работы с большими данными и минимизирует риск совершения ошибок в коде.

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

← На главную