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