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