В процессе разработки обработок и отчетов в платформе «1С:Предприятие» разработчики нередко сталкиваются с ошибкой «Поле объекта недоступно для записи (Ссылка)». Анализ этой проблемы показывает, что она возникает при попытке переприсвоить значение переменной, которая была передана в функцию по ссылке и является реквизитом объекта или формы, доступным только для чтения.
Рассмотри детально причину возникновения данной ошибки, проанализируем фрагмент кода и разберем наиболее эффективные технические и методологические способы ее устранения.
Проанализируем ситуацию на примере вызова функции, в которую передается параметр:
Функция АрендаАТ(Сотрудник1)
...
Если ЕстьПодмена = 1 Тогда
Сотрудник1 = СотрудникПодмена; // Здесь возникает ошибка
КонецЕсли;
КонецФункции
Если при вызове функции АрендаАТ в качестве аргумента передается значение, доступное только для чтения (например, Ссылка.Сотрудник1 или реквизит текущей строки таблицы, полученный через ссылку), платформа «1С:Предприятие» передает этот параметр по ссылке (по умолчанию, если не указано ключевое слово Знач).
При попытке изменить значение внутри функции (выполнить присвоение Сотрудник1 = СотрудникПодмена;) система пытается записать новое значение непосредственно в исходное поле объекта, из которого оно было прочитано. Так как реквизит объекта по ссылке недоступен для записи, платформа генерирует исключение «Поле объекта недоступно для записи (Ссылка)».
Первый и наиболее простой технический способ решения проблемы — передавать параметр в функцию по значению, а не по ссылке. Для этого перед именем параметра в объявлении функции необходимо добавить ключевое слово Знач.
В этом случае внутри функции создается локальная копия переменной. Все модификации параметра будут происходить с локальной копией и не затронут исходный источник данных.
Посмотрим на пример правильного объявления функции:
Функция АрендаАТ(Знач Сотрудник1)
// Код поиска подмены
...
Если ЕстьПодмена = 1 Тогда
Сотрудник1 = СотрудникПодмена;
КонецЕсли;
Возврат Сотрудник1;
КонецФункции
Использование функции для прямого изменения входных параметров считается плохой практикой программирования. Согласно стандартам разработки «1С», функция должна вычислять и возвращать новое значение через оператор Возврат, не изменяя переданные параметры.
Перепишем алгоритм по правилам платформы:
Функция ПолучитьПодменяющегоСотрудника(Знач ИсходныйСотрудник)
РезультатСотрудник = ИсходныйСотрудник;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| СоответствиеСотрудников.СотрудникПодмена КАК СотрудникПодмена
|ИЗ
| РегистрСведений.СоответствиеСотрудников КАК СоответствиеСотрудников
|ГДЕ
| СоответствиеСотрудников.Сотрудник = &Сотрудник";
Запрос.УстановитьПараметр("Сотрудник", ИсходныйСотрудник);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
РезультатСотрудник = Выборка.СотрудникПодмена;
КонецЕсли;
Возврат РезультатСотрудник;
КонецФункции
При вызове такой функции полученный результат присваивается локальной переменной в вызывающем коде:
ТекущийСотрудник = ПолучитьПодменяющегоСотрудника(Ссылка.Сотрудник1);
Посмотрим на работу с регистром сведений. Если регистр сведений является непериодическим, создание и выполнение объекта Новый Запрос при каждом вызове функции существенно снижает производительность системы (особенно если обработка выполняется в цикле по списку сотрудников).
Для оптимизации вызова вместо запроса выгодно использовать встроенный метод менеджера регистра Получить():
Функция ПолучитьПодменяющегоСотрудникаБыстро(Знач ИсходныйСотрудник)
Отбор = Новый Структура("Сотрудник", ИсходныйСотрудник);
ЗначенияРегистра = РегистрыСведений.СоответствиеСотрудников.Получить(Отбор);
Если ЗначенияРегистра.Свойство("СотрудникПодмена") И ЗначениеЗаполнено(ЗначенияРегистра.СотрудникПодмена) Тогда
Возврат ЗначенияРегистра.СотрудникПодмена;
Иначе
Возврат ИсходныйСотрудник;
КонецЕсли;
КонецФункции
Разберем методологическую сторону подмены сотрудников. В конфигурации «Зарплата и Управление Персоналом» прямая подмена сотрудников в печатных формах и отчетах может привести к нарушениям регламентированного учета.