Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда платформа выдает ошибку в строке кода, которая выглядит абсолютно корректной и безобидной. Одной из таких загадочных ошибок является «Поле объекта недоступно для записи», указывающая на переменную, которая даже не является реквизитом записываемого объекта, или на строку, где никакой записи вроде бы не происходит.
В этой статье мы подробно разберем реальный практический кейс с форума, проанализируем внутренние механизмы платформы 1С (передачу параметров по ссылке и по значению), а также выясним, почему исправление кода в конфигураторе часто не решает проблему без административных манипуляций с сервером приложений.
Рассмотрим ситуацию. Разработчик получает ошибку выполнения кода на сервере (или клиенте):
ФормаДокумента.Форма(848)}: Поле объекта недоступно для записи (Телефон)
зТелефон="7"+Прав(зТелефон,СтрДлина(зТелефон)-1);
При этом разработчик недоумевает: переменная зТелефон — это локальная переменная внутри функции, а ошибка указывает на реквизит Телефон, который в этой строке вообще не фигурирует. Более того, код функции форматирования номера выглядит следующим образом:
Функция ОбработатьНомерТелефона(Тел)
зТелефон=СтрЗаменить(Тел," ","");
зТелефон=СтрЗаменить(зТелефон,"-","");
зТелефон=СтрЗаменить(зТелефон,"(","");
зТелефон=СтрЗаменить(зТелефон,")","");
зТелефон=СтрЗаменить(зТелефон,"+","");
Если Лев(зТелефон,1) = "8" Тогда
зТелефон="7"+Прав(зТелефон,СтрДлина(зТелефон)-1);
КонецЕсли;
Возврат зТелефон;
КонецФункции
А вызывается эта функция в модуле формы так:
Если НЕ Контрагент.ЕстьЛичныйКабинет Тогда
ТелефонЛК = ОбработатьНомерТелефона(Менеджер.Телефон);
КонецЕсли;
СтруктураДанныхJSON.Вставить("phone", ТелефонЛК);
Давайте разберем по шагам, почему возникает эта ошибка и почему для её устранения потребовалось перезагружать сервер приложений 1С.
Для того чтобы понять природу ошибки, обратимся к правилам работы платформы 1С с параметрами процедур и функций. По умолчанию в языке программирования 1С все параметры передаются по ссылке (аналог ByRef в других языках), если явно не указано ключевое слово Знач (передача по значению).
Когда мы передаем в функцию переменную по ссылке, внутри функции создается не копия значения, а временный указатель на исходную ячейку памяти. Любые изменения этой переменной внутри функции мгновенно отражаются на исходном объекте в вызывающем коде.
Рассмотрим, что происходит при вызове:
ТелефонЛК = ОбработатьНомерТелефона(Менеджер.Телефон);
Менеджер представляет собой ссылку на справочник (тип СправочникСсылка.Пользователи или аналогичный).Менеджер.Телефон без получения объекта (через метод ПолучитьОбъект()) невозможно.Тел, и мы попытались написать Тел = "7" + ..., платформа попыталась бы изменить значение исходного реквизита Менеджер.Телефон. Так как он доступен только для чтения, система мгновенно выдала бы ошибку: «Поле объекта недоступно для записи (Телефон)». При этом платформа ругается именно на исходное имя реквизита (Телефон), а не на имя переменной внутри функции (Тел).Но внимательный читатель заметит: в коде функции параметр называется Тел, а изменения происходят с локальной переменной зТелефон! В теории, строка зТелефон = СтрЗаменить(Тел, " ", "") должна была просто скопировать строковое значение из переменной Тел в новую локальную переменную зТелефон, и дальнейшая работа велась бы уже с локальной строкой. Почему же ошибка все равно происходила?
Ответ кроется в истории изменения кода и механизме динамического обновления конфигурации (также известного среди разработчиков как «демократическое обновление»).
Скорее всего, изначально функция выглядела иначе. Параметр функции назывался именно зТелефон:
// Старая (проблемная) версия функции в кэше сервера:
Функция ОбработатьНомерТелефона(зТелефон)
зТелефон=СтрЗаменить(зТелефон," ","");
...
зТелефон="7"+Прав(зТелефон,СтрДлина(зТелефон)-1); // Здесь происходила попытка записи в ссылку!
Возврат зТелефон;
КонецФункции
Разработчик обнаружил ошибку, зашел в конфигуратор и исправил имя параметра на Тел, сделав зТелефон локальной переменной. После этого он применил динамическое обновление базы данных (без монопольного монополизирования и перезапуска рабочих процессов сервера).
При динамическом обновлении часто возникает рассинхронизация:
rphost) продолжают хранить в своей оперативной памяти (кэше модулей) скомпилированную старую версию кода.зТелефон и передается по ссылке), и транслирует ошибку на строку, которая в новой версии кода уже переписана.Именно поэтому очистка клиентского кэша не приносила результата (так как код выполнялся на сервере), а помогла только полная перезагрузка службы сервера 1С (или перезапуск рабочих процессов), которая принудительно очистила оперативную память и заставила сервер скомпилировать актуальный код из базы данных.
Чтобы избежать подобных ситуаций, необходимо придерживаться стандартов безопасной разработки и правильного администрирования систем на базе 1С:Предприятие.
Если ваша процедура или функция предназначена для обработки входящих данных (форматирование строк, вычисления, проверки) и не должна изменять саму исходную переменную в вызывающем коде, всегда объявляйте параметры со словом Знач. Это гарантирует передачу параметра по значению (копированием):
Функция ОбработатьНомерТелефона(Знач Тел)
// Теперь Тел — это гарантированная локальная копия.
// Даже если мы передали Менеджер.Телефон, мы можем безопасно изменять переменную Тел внутри функции.
Тел = СтрЗаменить(Тел, " ", "");
Тел = СтрЗаменить(Тел, "-", "");
Тел = СтрЗаменить(Тел, "(", "");
Тел = СтрЗаменить(Тел, ")", "");
Тел = СтрЗаменить(Тел, "+", "");
Если Лев(Тел, 1) = "8" Тогда
Тел = "7" + Прав(Тел, СтрДлина(Тел) - 1);
КонецЕсли;
Возврат Тел;
КонецФункции
Такой подход защитит ваш код от случайных попыток модификации объектов, закрытых для записи, и сделает код более понятным и надежным.
Динамическое обновление — удобный инструмент для быстрого исправления мелких ошибок, но на крупных базах с большим количеством активных пользователей он часто приводит к непредсказуемому поведению платформы («фантомным» ошибкам, зависанию сеансов, утечкам памяти).
Рекомендуется выполнять обновления конфигурации монопольно в техническое окно с последующим перезапуском службы сервера 1С или рабочих процессов.
Если на сервере 1С крутится множество рабочих баз данных, и перезагружать всю службу целиком нежелательно, можно очистить кэш конкретной базы или перезапустить отдельные рабочие процессы:
rphost (платформа автоматически поднимет новый чистый процесс).srvinfo\reg_1541\ (за исключением файлов конфигурации и логов транзакций), где хранятся скомпилированные сервером файлы модулей.Понимание принципов передачи параметров в 1С и механизмов работы серверного кэша убережет вас от траты времени на поиск несуществующих ошибок в коде и позволит быстро локализовать и устранять подобные сбои.