В практике разработки на платформе 1С:Предприятие часто возникает задача массового изменения данных в справочниках. Одной из самых распространенных ошибок начинающих программистов является попытка перезаписать данные существующего элемента через создание нового или прямое присвоение ссылки. В этой статье мы подробно разберем, почему старый подход не работает, и научимся правильно обновлять информацию о контрагентах, соблюдая стандарты разработки и оптимизации.
Проанализируем ситуацию, с которой столкнулся автор темы. Попытка использовать метод СоздатьЭлемент() для редактирования уже существующей записи в базе данных является в корне неверной. Рассмотрим, что происходит в коде:
// Ошибочный подход
ТелефонОбработки = Справочники.Контрагенты.СоздатьЭлемент();
ТелефонОбработки.Ссылка = Стр.Ссылка; // Ошибка: Свойство Ссылка доступно только для чтения
Метод СоздатьЭлемент() всегда генерирует в памяти абсолютно новый объект справочника с новым уникальным идентификатором (GUID). Попытка вручную присвоить ему Ссылка от уже существующего контрагента приведет к ошибке исполнения, так как это поле защищено от записи платформой. Ссылка — это уникальный «паспорт» записи, который выдается один раз при создании и не может быть изменен программно через присваивание.
Также использование метода Прочитать() после создания нового элемента не имеет смысла, так как объект еще не связан с базой данных по конкретному ключу. Наша задача — не создать новое, а «вызвать» из базы существующее для редактирования.
Для того чтобы изменить данные существующего контрагента, нам необходимо из Ссылки (которая является лишь указателем на запись в БД) получить Объект (который является набором данных в оперативной памяти, доступным для записи). Рассмотрим исправленный и лаконичный код, который решает задачу:
Для Каждого Стр Из СписокКонтрагентовДляОбработки Цикл
// Получаем объект из ссылки для возможности редактирования
ТекущийКонтрагент = Стр.Ссылка.ПолучитьОбъект();
// Проверяем, удалось ли получить объект (например, если он не удален)
Если ТекущийКонтрагент <> Неопределено Тогда
// Присваиваем новое значение реквизиту
ТекущийКонтрагент.ОсновнойТелефон = Стр.НовыйНомерТелефона;
// Записываем изменения в базу данных
ТекущийКонтрагент.Записать();
КонецЕсли;
КонецЦикла;
В этом примере мы используем метод ПолучитьОбъект(). Он считывает все данные конкретного контрагента из базы данных и позволяет нам менять любые его реквизиты. После вызова метода Записать() платформа обновляет существующую запись в таблице справочника.
Если мы планируем обрабатывать тысячи контрагентов, простая циклическая запись может существенно замедлить работу системы и вызвать блокировки. Для ускорения процесса массового обновления данных рекомендуется использовать свойство ОбменДанными.Загрузка. Проанализируем, как это работает:
ТекущийКонтрагент = Стр.Ссылка.ПолучитьОбъект();
Если ТекущийКонтрагент <> Неопределено Тогда
// Установка флага загрузки отключает лишние проверки в модуле объекта
ТекущийКонтрагент.ОбменДанными.Загрузка = Истина;
ТекущийКонтрагент.ОсновнойТелефон = Стр.НовыйНомерТелефона;
ТекущийКонтрагент.Записать();
КонецЕсли;
Важный момент: установка ОбменДанными.Загрузка = Истина подавляет выполнение стандартных проверок в процедурах ПередЗаписью и ПриЗаписи, которые могут быть прописаны в конфигурации (например, проверка корректности заполнения ИНН или проверка прав). Используйте этот метод осторожно, только если вы уверены в качестве записываемых данных.
Выясним причину, по которой прямое изменение реквизита ОсновнойТелефон может быть недостаточно. В современных конфигурациях, таких как 1С:Бухгалтерия 3.0, УТ 11 или ERP, телефоны хранятся в специальных табличных частях или регистрах сведений (механизм БСП — Библиотека Стандартных Подсистем).
Если вы просто запишете строку в реквизит справочника, она может не отобразиться в карточке контрагента или не попасть в отчеты. Разберем, как правильно записывать телефон через программный интерфейс БСП:
// Пример использования функций общего модуля УправлениеКонтактнойИнформацией
ВидИнформации = Справочники.ВидыКонтактнойИнформации.ТелефонКонтрагента;
УправлениеКонтактнойИнформацией.ЗаписатьКонтактнуюИнформацию(ТекущийКонтрагент.Ссылка, Стр.НовыйНомерТелефона, ВидИнформации, ТекущийОбъект);
Этот метод автоматически выполнит следующие действия:
КонтактнаяИнформация объекта.Чтобы избежать лишних обращений к базе данных и не раздувать журнал регистрации, перед записью всегда полезно проверить: а изменилось ли значение на самом деле? Посмотрим на пример реализации такой проверки:
Если ТекущийКонтрагент.ОсновнойТелефон <> Стр.НовыйНомерТелефона Тогда
ТекущийКонтрагент.ОсновнойТелефон = Стр.НовыйНомерТелефона;
ТекущийКонтрагент.Записать();
КонецЕсли;
Такой простой логический блок может сэкономить значительное время при обработке больших массивов данных, если часть номеров уже находится в актуальном состоянии.
При массовой обработке данных рекомендуется объединять записи в транзакции. Это гарантирует, что если в середине процесса произойдет сбой (например, выключится свет или пропадет связь с сервером), данные не останутся в «полуобновленном» состоянии. Проанализируем структуру кода с транзакцией:
НачатьТранзакцию();
Попытка
Для Каждого Стр Из СписокКонтрагентов Цикл
// ... Логика получения объекта и записи ...
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Сообщить("Произошла ошибка при записи данных: " + ОписаниеОшибки());
КонецПопытки;
Подведем итог: для успешного обновления данных контрагента необходимо всегда использовать метод ПолучитьОбъект() из ссылки, не забывать о проверке на равенство старого и нового значений, а в случае работы с современными конфигурациями — использовать встроенные механизмы управления контактной информацией.