При переходе с обычных форм (платформа 1С 8.1) на управляемые формы (платформы 1С 8.2 и 8.3) многие разработчики сталкиваются с неожиданным поведением системы при попытке определить, является ли открываемый или записываемый объект новым. Привычные конструкции, работавшие годами, внезапно начинают вызывать ошибки компиляции или выполнения.
В этой статье мы подробно разберем, почему стандартные подходы не работают в контексте модуля управляемой формы, проанализируем внутренние механизмы платформы «1С:Предприятие» и рассмотрим все возможные способы решения этой задачи, включая наиболее оптимальные с точки зрения производительности и стандартов разработки фирмы «1С».
Давайте проанализируем классическую ситуацию. Разработчик пытается выполнить проверку в обработчике события ПриСозданииНаСервере модуля формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Объект.ЭтоНовый() Тогда // Ошибка! Метод не обнаружен
// Логика инициализации
КонецЕсли;
КонецПроцедуры
Система выдает ошибку, сообщающую, что метод ЭтоНовый() не определен. Почему это происходит? Разберемся в различиях архитектуры обычного и управляемого приложения.
В обычных формах основным реквизитом формы выступал сам прикладной объект базы данных (например, ДокументОбъект.РеализацияТоваровУслуг). Форма работала с ним напрямую, и разработчику были доступны все методы этого объекта, включая ЭтоНовый().
В управляемом приложении архитектура взаимодействия клиента и сервера кардинально изменилась. Форма больше не держит в памяти «тяжелый» объект базы данных. Вместо него на форму передается его облегченная проекция — специальный тип данных ДанныеФормыСтруктура. Реквизит формы Объект имеет именно этот тип данных.
У типа данных ДанныеФормыСтруктура нет и не может быть метода ЭтоНовый(), так как этот тип предназначен исключительно для визуализации и редактирования данных на клиенте и сервере, но сам по себе он не является бизнес-объектом базы данных. Именно поэтому вызов Объект.ЭтоНовый() или ЭтотОбъект.ЭтоНовый() приводит к ошибке.
Теперь рассмотрим рабочие и правильные альтернативы для решения нашей задачи.
Рассмотрим первый и самый легковесный способ проверки в обработчике ПриСозданииНаСервере. Когда платформа открывает форму существующего документа, она передает в параметры формы ключ этой записи (ссылку на элемент). Если открывается форма для создания нового документа, этот параметр будет пустым.
Разберем по шагам, как реализовать этот метод в коде:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем параметр "Ключ" на заполненность
Если Параметры.Ключ.Пустая() Тогда
// Сюда мы попадем, только если создается абсолютно новый документ
Объект.ПланДатаНачала = НачалоДня(ТекущаяДата());
Объект.ПланДатаОкончания = КонецДня(ТекущаяДата());
КонецЕсли;
КонецПроцедуры
Преимущества данного подхода:
Параметры.Ключ.Пустая() выполняется мгновенно, так как не требует обращения к базе данных или преобразования типов данных.Важный нюанс: Этот способ идеально работает именно в событии ПриСозданииНаСервере при интерактивном открытии формы. В других обработчиках событий (например, перед записью или при нажатии на произвольные кнопки формы) обращаться к параметрам напрямую уже не имеет смысла, так как контекст инициализации формы пройден.
Если вам необходимо проверить, является ли объект новым, не только при создании формы, но и в других процедурах модуля формы на сервере (или даже на клиенте), можно использовать проверку заполненности уникального идентификатора (ссылки) самого объекта.
Посмотрим на пример реализации:
&НаСервере
Процедура ПроверитьСтатусДокумента()
// Если ссылка пустая - документ еще ни разу не записывался в базу данных
Если Не ЗначениеЗаполнено(Объект.Ссылка) Тогда
// Наша логика для нового объекта
Иначе
// Логика для уже существующего в базе объекта
КонецЕсли;
КонецПроцедуры
Этот способ универсален, так как свойство Объект.Ссылка доступно в любой момент времени жизни формы.
Внимание, подводный камень! Существует редкая, но важная ситуация, когда этот способ может вернуть неверный результат. Если в коде конфигурации до момента открытия или записи формы для нового документа был программно вызван метод УстановитьСсылкуНового(), то свойство Объект.Ссылка уже будет содержать подготовленную ссылку (уникальный идентификатор), несмотря на то, что физически документа в базе данных еще нет. В этом специфическом случае проверка Объект.Ссылка.Пустая() вернет ложь.
Если вам принципиально необходимо использовать оригинальный метод объекта ЭтоНовый(), вы можете на сервере временно преобразовать облегченные данные формы в полноценный прикладной объект базы данных с помощью встроенной функции платформы РеквизитФормыВЗначение.
Проанализируем пример кода:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Преобразуем ДанныеФормыСтруктура в реальный ДокументОбъект
ДокОбъект = РеквизитФормыВЗначение("Объект");
// Теперь нам доступен стандартный метод ЭтоНовый()
Если ДокОбъект.ЭтоНовый() Тогда
// Выполняем действия для нового документа
КонецЕсли;
КонецПроцедуры
Критически важный момент! Использовать этот метод исключительно ради проверки на новизну категорически не рекомендуется стандартами разработки фирмы «1С». Операция РеквизитФормыВЗначение является крайне ресурсоемкой, так как заставляет платформу выполнять полную сериализацию структуры данных формы в объект, тратя процессорное время и оперативную память сервера. Применяйте этот способ только в том случае, если далее по коду вам в любом случае необходимо вызывать экспортные методы модуля объекта или выполнять сложные транзакционные действия с самим объектом.
Давайте обратим внимание на сообщение 6 из исходного обсуждения. Автор совершенно справедливо указывает, что начальное заполнение реквизитов нового документа (таких как плановые даты, склады, организации по умолчанию) правильнее делать не в форме, а в модуле самого объекта.
По правилам хорошего тона и архитектурным стандартам «1С», визуальная форма должна отвечать исключительно за отображение данных и интерфейс (доступность кнопок, видимость полей). Сами же бизнес-данные должны инициализироваться на уровне бизнес-логики — в модуле объекта.
Для этих целей в модуле любого документа или справочника существует предопределенный обработчик события ОбработкаЗаполнения. Платформа гарантированно вызывает его в тот момент, когда создается новый объект (будь то интерактивное создание пользователем, копирование существующего или ввод на основании).
Давайте посмотрим, как правильно перенести код инициализации дат из нашего первого примера в модуль объекта:
Шаг 1. Код в модуле документа (не в форме!):
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
// Данный обработчик вызывается только для НОВЫХ объектов.
// Проверка ЭтоНовый() здесь не требуется!
ПланДатаНачала = НачалоДня(ТекущаяДата());
ПланДатаОкончания = КонецДня(ТекущаяДата());
КонецПроцедуры
Шаг 2. Код в модуле формы документа:
В самой форме мы теперь занимаемся только интерфейсными задачами, используя самый легкий метод проверки параметров:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Ключ.Пустая() Тогда
// Настраиваем форму для НОВОГО документа
Элементы.ГруппаПлановыеДаты.Доступность = Истина;
Автозаголовок = Ложь;
Заголовок = "Создание нового документа планирования";
Иначе
// Настраиваем форму для СУЩЕСТВУЮЩЕГО документа
Элементы.ГруппаПлановыеДаты.Доступность = Ложь; // Запрещаем менять плановые даты
КонецЕсли;
КонецПроцедуры
Такой подход обеспечивает идеальное разделение ответственности между формой и объектом, гарантирует высокую скорость работы программы и предотвращает дублирование кода при программном создании документов из других мест конфигурации.