При написании обработок для загрузки данных из внешних файлов, таких как Excel, программисты 1С часто сталкиваются с необходимостью не только создавать новые элементы справочников, но и обновлять существующие. В процессе реализации обновления реквизитов найденного элемента (например, справочника «Номенклатура») может возникнуть системная ошибка: «поле объекта не доступно для записи». Давайте подробно разберем эту ситуацию, выясним причину возникновения ошибки и посмотрим, как правильно работать с объектами базы данных.
Рассмотрим типичный пример кода, который вызывает данную ошибку. В нашей ситуации разработчик проверяет, пустая ли ссылка на номенклатуру. Если пустая — создается новый элемент, и здесь всё работает корректно. Но если элемент найден, разработчик пытается дозаполнить его реквизиты (например, артикул и родителя).
Посмотрим на ошибочный фрагмент кода из ветки Иначе:
Иначе
мНоменклатура = мНоменклатура.Ссылка;
мНоменклатура.ПолучитьОбъект();
мНоменклатура.Артикул = Артикул;
мНоменклатура.Родитель = гНоменклатура.Ссылка;
мНоменклатура.Записать();
КонецЕсли;
Проанализируем, что здесь происходит не так. Ошибка кроется в самой концепции работы с данными в платформе 1С:Предприятие. Переменная мНоменклатура в данном случае содержит Ссылку. Ссылка — это закешированные данные, предназначенные исключительно для чтения. Платформа блокирует любую попытку присвоить новое значение реквизиту ссылки, выдавая сообщение, что поле объекта недоступно для записи.
Разработчик вызвал метод ПолучитьОбъект(), но не присвоил результат его выполнения переменной. Метод отработал, вернул объект базы данных в «никуда», а переменная мНоменклатура так и осталась ссылкой.
Чтобы изменить данные, необходимо получить полноценный «Объект» базы данных, сохранить его в переменную, изменить необходимые свойства и уже затем выполнить метод Записать(). Разберем по шагам, как это правильно реализовать.
Исправленный вариант кода будет выглядеть следующим образом:
Иначе
// Получаем объект и обязательно присваиваем его переменной
мНоменклатураОбъект = мНоменклатура.ПолучитьОбъект();
// Теперь изменяем реквизиты именно у объекта
мНоменклатураОбъект.Артикул = Артикул;
мНоменклатураОбъект.Родитель = гНоменклатура.Ссылка;
// Записываем изменения в базу данных
мНоменклатураОбъект.Записать();
КонецЕсли;
Мы рекомендуем всегда логически разделять переменные, содержащие ссылки (для поиска и чтения), и переменные, содержащие объекты (для модификации и записи). Использование суффикса ...Объект (как в мНоменклатураОбъект) делает код более читаемым и защищает от случайных ошибок.
После исправления кода можно столкнуться с другой особенностью: при наборе точки после переменной объекта (например, мНоменклатураОбъект.) конфигуратор не предлагает контекстную подсказку с реквизитами справочника. Разберем причину.
Конфигуратор 1С не показывает свойства, потому что тип переменной задан неявно. Платформа на этапе написания кода не знает, объект какого именно справочника вернул метод ПолучитьОбъект(). Чтобы включить автодополнение, воспользуемся специальными комментариями, которые подскажут конфигуратору тип переменной:
// ВСТАВИТЬ: СправочникОбъект.Номенклатура
мНоменклатураОбъект = мНоменклатура.ПолучитьОбъект();
мНоменклатураОбъект. // Теперь после точки появится список реквизитов!
Часто подобные обработки загрузки из Excel сначала пишутся и тестируются на файловой базе (локально). Но при переносе решения в клиент-серверную базу (на сервер 1С) возникают новые сложности. Проанализируем ключевые нюансы, которые необходимо учесть:
&НаСервере. На клиенте методы изменения базы данных недоступны.C:\Загрузки\). Файл нужно сначала передать с клиента на сервер. Для этого на клиенте файл помещается во временное хранилище с помощью функции ПоместитьВоВременноеХранилище, полученный адрес передается в серверную процедуру, где файл извлекается (ПолучитьИзВременногоХранилища) и сохраняется во временный каталог сервера (ПолучитьИмяВременногоФайла).COMОбъект("Excel.Application") часто приводит к ошибкам из-за отсутствия установленного MS Office или недостатка прав у службы сервера 1С. Гораздо надежнее использовать штатный объект 1С ТабличныйДокумент и его метод Прочитать(). Это работает быстро и не требует стороннего ПО.При загрузке тысяч строк из прайс-листа Excel код должен быть не только рабочим, но и отказоустойчивым. Рассмотрим несколько практических советов по улучшению качества вашей загрузки:
1. Обработка блокировок
В рабочей базе существующая номенклатура может быть открыта и заблокирована другим пользователем (например, менеджером). В этом случае метод ПолучитьОбъект() вызовет критическую ошибку, и вся загрузка остановится. Рекомендуем оборачивать получение объекта в конструкцию обработки исключений:
Попытка
мНоменклатураОбъект = мНоменклатура.ПолучитьОбъект();
мНоменклатураОбъект.Артикул = Артикул;
мНоменклатураОбъект.Записать();
Исключение
Сообщить("Ошибка блокировки для: " + мНоменклатура + ". Элемент пропущен.");
КонецПопытки;
2. Ускорение записи через ОбменДанными
Если конфигурация содержит множество тяжелых проверок или подписок на события при записи номенклатуры (которые не требуются при первоначальной загрузке каталога), их можно отключить. Установите свойство ОбменДанными.Загрузка = Истина перед вызовом метода записи:
мНоменклатураОбъект.ОбменДанными.Загрузка = Истина;
мНоменклатураОбъект.Записать();
3. Использование транзакций
Чтобы значительно ускорить массовую загрузку, группируйте операции записи в транзакции порциями, например, по 500 элементов, используя методы НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это существенно снизит нагрузку на СУБД и сократит время обработки файла.