Изменить объект формы 1с

Обновлено: 07.07.2024

Одним из требований для прохождения аудита расширения в модели сервиса является программная модификация управляемых форм без заимствования самой формы в расширение.

Мы рассмотрим основные составляющие этой задачи: добавление реквизитов, добавление элементов формы и назначение обработчиков событий элементов формы.

Добавление реквизитов

Для добавления реквизитов используется метод объекта ФормаКлиентскогоПриложения

Реквизиты формы, добавленные через конфигуратор, удалить невозможно, поэтому второй аргумент процедуры можно не указывать.

Переменная ДобавляемыеРеквизиты является массивом объектов типа РеквизитФормы .

Например, если мы создаем таблицу значений из двух колонок, то программный код для их создания будет выглядеть следующим образом:

Процедуру ИзменитьРеквизиты логично вызывать из обработчика ПриСозданииНаСервере , но т.к. мы не заимствуем форму в расширение, то следует найти другую точку входа. Для конфигураций УТ 11, КА 2 и ERP 2 существует типовой механизм упрощенного изменения конфигураций. Нас интересует модуль МодификацияКонфигурацииПереопределяемый , в состав которого входит процедура

Данную процедуру можно заимствовать в расширение, добавить проверку имени формы и вставить код изменения реквизитов:

Для остальных конфигураций придется переопределять другие процедуры. Например

Использование той или иной процедуры следует проверить в модуле редактируемой формы.

Изменение элементов формы

Управление элементами формы производится через коллекцию Элементы объекта ФормаКлиентскогоПриложения , с помощью которой можно добавлять, удалять, изменять свойства элементов формы, а также перемещать элементы формы между родителями.

Для добавления нового элемента формы требуется передать в функцию Добавить данной коллекции имя элемента, его тип и родителя (при необходимости). Например:

По аналогии с добавлением реквизитов, данный код можно выполнять в функциях МодификацияКонфигурацииПереопределяемый.ПриСозданииНаСервере (УТ, КА, ERP) , ПодключаемыеКоманды.ПриСозданииНаСервере или ВерсионированиеОбъектов.ПриСозданииНаСервере .

При создании новых элементов формы старайтесь добавлять к их именам префикс своего расширения, чтобы избежать конфликтов с элементами формы типовой конфигурации. Этот же совет следует применять к именованию реквизитов формы, процедур, функций и глобальным переменным.

Обработка событий формы

Выполнить код по событию элемента формы можно двумя способами:

  • Создать команду, указать для этой команды имя обработчика события и назначить эту команду элементу формы
  • Выполнить метод УстановитьДействие элемента формы, чтобы указать имя обработчика события в модуле формы

Оба метода предполагают наличие в модуле формы процедуры с сигнатурой, соответствующей обработчику события. Для первого способа в модуле формы должна быть клиентская процедура, принимающая единственный аргумент - Команда. Для второго - всё зависит от события, для которого выполняется обработчик. Так, например, для события ПриИзменении элемента формы с типом ПолеВвода будет требоваться процедура, принимающая единственный аргумент - ЭлементФормы . А для события ПередНачаломДобавления таблицы формы - целых 6 аргументов ( ЭлементФормы , Отказ , Копирование , Родитель , ЭтоГруппа , Параметр ). Поэтому для некоторых событий попросту невозможно подобрать соответствующие клиентские методы в модуле формы и заимствования формы в расширение не избежать.

Для самых простых случаев (команда или событие без параметров) можно использовать следующие комбинации обработчиков и переопределяемых процедур:

  • обработчик Подключаемый_ВыполнитьПереопределяемуюКоманду с переопределением процедуры МодификацияКонфигурацииКлиентПереопределяемый.ВыполнитьПереопределяемуюКоманду для УТ, КА и ERP;
  • обработчик Подключаемый_ВыполнитьКоманду с переопределением процедуры ПодключаемыеКомандыКлиент.ВыполнитьКоманду

При этом в предопределенной процедуре обязательно проверять имя команды (или имя элемента формы) и имя самой формы.

Либо вариант с использованием команд:

Обратите внимание, что при использовании процедур, не предназначенных для программной обработки команд, следует возвращать поток выполнения методом глобального контекста ПродолжитьВыполнение , чтобы не нарушить работу типовых механизмов.

Полезные советы

Работа с динамическими списками

Довольно частой задачей является модификация в расширении текста запроса динамического списка. Многие разработчики просто копируют текст запроса из конфигуратора, редактируют его и заменяют стандартный в свойстве ТекстЗапроса объекта Динамический список .

Для небольших изменений гораздо лучше воспользоваться объектом СхемаЗапроса , появившемся в версии платформы 8.3.5. Он позволит всегда иметь актуальный текст запроса, если он поменяется при обновлении конфигурации.

Переопределение открываемой формы

В некоторых случаях, когда требуется кардинальное изменение формы, следует не забывать о возможности переопределения открываемой формы в модуле менеджера объекта метаданных.

Для этого следует реализовать новую форму (не заимствовать её, а написать с нуля) и в модуле менеджера переопределить процедуру ОбработкаПолученияФормы .

Данный метод следует использовать с осторожностью или ограничивать его использование ключами открытия формы, т.к. в этом случае мы делаем недоступной типовую форму.

Под формой объекта данных в статье подразумевается форма ссылочного объекта (элемента справочника, документа и т.д.) или форма записи независимого регистра сведений.

Пусть в объекте есть строковый реквизит Наименование, длину значения которого мы хотим отображать на форме этого объекта. Добавляем в форму реквизит ДлинаСтроки типа Число и отображаем его в одноименное нередактируемое поле.

Также пусть в объекте есть реквизит типа ХранилищеЗначения, тип которого мы также хотим отображать в форме. Добавляем в форму реквизит ТипЗначенияВХранилище типа Строка и отображаем его в одноименное нередактируемое поле.



В каких же ситуациях нам потребуется обновлять эти косвенно связанные с объектом реквизиты?

Разумеется это нужно вызывать в событии ПриИзменении поля Наименование. А вот дальше многие ограничиваются только вызовом в ПриСозданииНаСервере[упр] и ПриОткрытии[обыч]. Тем самым они не учитывют

  1. Возможность выполнения пользователем команды "Перечитать" формы.

    В управляемой форме она всегда вызывает событие ПриЧтенииНаСервере.
    В обычной форме она вызывает событие ПриИзмененииДанных только в модифицированном состоянии формы. В немодифицированном состоянии команда делает ничего.
  2. Возможность изменения объекта в событии ПередЗаписью объекта.
    Форма отправляет на запись одно состояние объекта, а после выполнения записи получает другое.

Примеры использования команды "Перечитать"

Случай 1

Объект данных, отображаемый в форме, мог быть изменен в БД с момента его загрузки в форму. Типичные способы таких изменений

  1. в другой форме в этом же клиентском приложении
  2. этим же пользователем в другом клиентском приложении в этой же базе
  3. другим пользователем
  4. фоновым процессом

Тогда при попытке начать изменение любого поля формы, напрямую связанного с данными (флаг "Изменяет данные"), пользователь увидит предупреждение "Операция не может быть выполнена из-за несоответствия версии или отсутствия записи базы данных (возможно, запись была изменена или удалена)!"


Это результат срабатывания так называемой оптимистической объектной блокировки. Она гарантирует, что если пользователь изменяет объект, то его изменения не «затрут» изменения, сделанные другими сеансами или другими программными объектами этого же сеанса. И тогда, чтобы получить возможность редактировать объект, нужно будет его перечитать либо переоткрыть форму.

Случай 2

Пользователь изменил данные в форме и решил отменить сделанные изменения, но продолжить редактировать объект от его текущего состояния в БД.

В каких событиях обновлять форму?

Чтобы учесть все эти тонкости, нужно всю логику обновления косвенно связанных с объектом элементов формы поместить в процедуру ПриСозданииПриЧтенииНаСервере и вызывать ее из следующих обработчиков событий формы:

Управляемая форма
  1. ПриСозданииНаСервере - вызываем в конце тела обработчика, но имеем доступ только к объекту в реквизите формы
  2. ПриЧтенииНаСервере - имеем доступ ко всем данным объекта
  3. ПослеЗаписиНаСервере - имеем доступ ко всем данным объекта и вызываем потому, что объект мог измениться в событии ПередЗаписью объекта
Обычная форма
  1. ПриИзмененииДанных - событие вызывается и для нового и для существующего объекта
  2. ПослеЗаписи - вызываем потому, что объект мог измениться в событии ПередЗаписью объекта

Управляемая форма

В управляемой форме при открытии существующего объекта выполняются два обработчика: сначала ПриЧтенииНаСервере и затем ПриСозданииНаСервере. Поэтому процедура ПриСозданииПриЧтенииНаСервере будет вызываться 2 раза подряд и нужно не задублировать выполнение логики обновления формы. Элементы формы могут зависеть как от данных объекта, доступных через реквизит формы, так и от недоступных через него (реквизиты типа ХранилищеЗначений), которые доступны только одном из них (ПриЧтенииНаСервере). Поэтому в процедуре ПриСозданииПриЧтенииНаСервере надо предусмотреть оба вызова таким образом, чтобы при создании формы существующего объекта при ее вызове из ПриЧтенииНаСервере выполнилась только логика работы с хранилищами значений, а при последующем вызове из ПриСозданииНаСервере выполнилась только логика, зависящая от остальных данных объекта. При этом ее вызов из уже открытой формы (из ПриЧтенииНаСервере и ПослеЗаписиНаСервере) должен выполнять все действия. Для этого создадим в общем модуле функцию ЭтоВызовПослеОткрытияФормы, где будем проверять наличие параметра ТолькоПросмотр у формы. Тогда процедура ПриСозданииПриЧтенииНаСервере будет выглядеть так

Из процедуры ПриСозданииПриЧтенииНаСервере можно выделить всю легкую клиентскую логику в процедуру НастроитьЭлементыФормы с директивой НаКлиентеНаСервереБезКонтекста. Такую легкую и доступную во всех контекстах формы процедуру можно звать при большинстве изменений реквизитов, которые должны менять косвенно связанные элементы формы.

Пример модуля управляемой формы

Обычная форма

В обычной форме благодаря наличию события ПриИзмененииДанных логику обновления формы можно помещать прямо в его обработчик. Здесь также стоит вынести всю легкую логику в процедуру НастроитьЭлементыФормы по аналогии с управляемой формой.

Пример модуля обычной формы

Применяю данную методику уже много лет.

Прикладываю выгрузку демонстрационной базы, где на примере справочника и регистра сведений показаны все описанные в статье проблемы и сама методика.

Часто требуется внести изменения в типовые формы объектов (документов, справочников, журналов, обработок и т.д.)
Есть несколько методов решения этих задач:

- Снимать с поддержки форму, вносить в нее изменения. Все просто, но проблемы возникнут при обновлении релиза конфигурации.

- Создавать элементы формы программно в общем модуле. Проблем с обновлением на новый релиз не будет. Рассмотрим данный метод.

В типовых конфигурациях в формах объектов в процедуре ПриСозданииНаСервере есть обращение к общему модулю СобытияФорм.ПриСозданииНаСервере



Далее идет обращение к общему модулю МодификацияКонфигурацииПереопределяемый.ПриСозданииНаСервере


В этом модуле процедура ПриСозданииНаСервере пустая. Тут и добавляем ссылку уже на наш модуль (не типовой модуль).

Доработка_ФормаПриСозданииНаСервере.ИзменитьФорму


В этой процедуре разбиваем формы по объектам и названиям форм.

Для удобства можно добавлять отельные общие модули для каждого объекта.


Тут редактируем запрос в динамическом списке. Методом СтрЗаменить. Добавляем левое соединение, добавляем новое поле в выборку запроса.

Программно добавляем колонку. ПутьКДанным указываем наша новое поле, которое мы добавили в запрос "Список.Д_ЕстьФайлы".

В результате появляется заполненная колонка.


Еще для примера приведу добавление "полей ввода" на форму.

Проверял на конфигурации 1С:Комплексная автоматизация 2

Платформа 1С:Предприятие 8.3 (8.3.13.1644)

Специальные предложения

Electronic Software Distribution

Интеграция 1С с системой Меркурий

Алкогольная декларация

Готовые переносы данных

54-ФЗ

Управление проектом на Инфостарте

Траектория обучения 1С-разработчика

Не знал о такой лазейке. Впрочем точно тот же эффект можно получить при программном изменении формы в расширении. (1)Способ из особо хорош, когда нужно массово модифицирвать формы, не влезая в модуль каждой, даже в расширении (1) Конечно, добавить модуль "МодификацияКонфигурацииПереопределяемый" в расширении и писать в &После("ПриСозданииНаСервере") все что нужно. 1) Не нужно настраивать поддержку. 2) При обновлении больше времени на сравнение объединение будет уходить у поставленной на редактирование конфы. Создавать элементы формы программно в общем модуле. Проблем с обновлением на новый релиз не будет

очень спорная мысль. с чего вы решили, что не будет? в последних релизах ЗУП, УТ, ЕРП разрабы так лихо выпиливают таблицы в метаданных и добавляют новые, что упор на модификацию форм посредством кода скорее ущербен, чем удобен.

мне лично проще проверить форму в расширении и "доработать", чем копаться в тонне переопределяемого кода и держать весь этот хламо-код в голове.

дело вкуса и . лени

(2) Не забывайте только что расширения можно применять далеко не во всех конфигурациях - у многих компаний используются конфигурации с режимом совместимости со старыми версиями платформы, при которых использовать расширения либо вообще не получится, либо с рядом ограничений, поэтому такой метод будет еще долгое время востребован (3) тс привел пример пример своего ноу-хау с кодом из методов общих модулей в новых конфигурациях.
поэтому я смущен категоричным пиаром программного способа как "высшего" метода

к чему тс было балаболить о том, что весьма условно?
а так да, все относительно и я же написал

(3)Так там и БСП будет более ранних версий - где таких общих функций может и не быть вовсе, или их применение в формах тоже будет сведено к минимуму - так что большинство форм не будет их вызывать! (7) Такие вызовы общих модулей используются уже с БСП 2, конечно не в таком специальном виде как в публикации через общие модули " СобытияФорм ", но в подавляющем большинстве форм имеются вызовы общих модулей других подсистем (например, "версионирование данных", "доп. отчетов и обработок"), которые в принципе можно использовать по аналогии с описанным способом - в них также доступен контекст вызываемой формы (вроде что-то похожее использовалось даже в УТ 10).
В принципе все это решается еще проще - созданием подписки на событие получения формы менеджеров всех объектов, в которой можно переопределить поведение всех необходимых форм объектов. событие получения формы менеджеров всех объектов, в которой можно переопределить поведение всех необходимых форм объектов.
Вот тут подробнее. Сколько не смотрел я это событие - так ничего толкового из него не вынес - событие срабатывает в серверном контексте, причём контекст объекта формы не доступен - максимум что там можно - только подменить один путь к форме на другой (читай одно имя вызова формы, другим) - и всё :-( фигня фигней - конечно в ряде случаев может быть полезно - когда Вы делаете свою форму - то её можно насильно подсовывать вместо других - анализируя путь исходного вызова формы. Но менять именно саму форму в этом событии совсем нельзя! (11) Вы правы, поработать с формой через подписку не получится, погорячился, а жаль ;( (3) три года назад поднимали совместимость с 8.1 на 8.2. А затем я поднял последовательно до 8.3.5. Тогда только 8.3.8 еще была.
Пару дней работы и все переписано грубо говоря, так что долго это наверно пока УПП поддерживается, КА уже сдулось. Пару лет и никто про это не вспомнит! (15) Далеко не все хотят возиться с доработками (или же это нецелесообразно в краткосрочной перспективе, или же не хватает соответствующей компетенции), необходимыми для отключения в конфигурации режима совместимости со старыми версиями платформы, бывает это даже связано с тем, что у специалиста нет понимания и уверенности в том, что конфа будет работать также стабильно и без "глюков" как и до отключения совместимости, так что думаю в старых конфах этот метод просуществует еще долго. (16) а по поводу статьи. Давно была статья, как вести доработку в типовых. Просто наверно про нее уже забыли. А тут лишь кусочек, единственное чуть больше примеров как писать этот код. Но в книжке разработчика все написано и так.

Давно пользуюсь подобной лазейкой. Но, как универсальное решение данный способ, увы, не подходит.

Общий модуль "СобытияФорм" есть даже далеко не во всех типовых конфигурациях на УФ (он не входит в состав БСП), и даже там где есть -
не везде вызывается из форм. В итоге приходится вклинивать функцию редактирования формы сразу в несколько разных обработчиков (в основном из модулей БСП, но и в некоторые специфичные для конкретной конфигурации и раздела учета - например для бухгалтерского учета). И делать проверку - обработана форма уже или ещё нет - т.к. в некоторых формах разные обработчики могу вызывать повторно. Это всё я уже реализую в опубликованном тут моём решении .

Но, это всё более менее хорошо до тех пор, пока в формах такие обработчики вызываются - но есть формы - где они вовсе не вызываются. Да есть и просто автогенерируемые формы - там вообще ничего такого не вызывается и не будет вызываться (и расширения не помогут) пока такую форму не создашь физически в конфигураторе.

Ну и главное - такая модификация элементов форм работает ровно до момент, пока не нужно связывать с программно добавленными элементами обработчики событий - вот тут наступает полная трабла - т.к. обработчик события можно назначить только из модуля формы ("спасибо" ужасное фирме 1С за такое ограничение).

Правда при размещени кнопок, имеющих внешние обработчики ещё как-то можно бороться (как типвыми так и не типовыми способами - смотря что доступно в форме) - об этом у меня есть в планах желание написать статью, а публикация, фактически использующая данную технику - уже на подходе.

То, вот, с обработчиками таких событий как, например, "ПриИзменении" или "ОбработкаВыбора" - вот тут беда. без модификации формы или применения расширений - никак не обойтись!

Кручусь такое чувство вокруг ответа но не могу понять как у открытой формы поменять основной реквизит Объект на объект другого документа.
Если перефразировать на рабочем столе открыта форма документа, получаю другой объект форму которого нужно отразить, вопрос как?

(1) Форма документа на рабочем столе для заполнения, рядом список документов, необходимо при выборе из это го списка документов отображать его форму на рабочем столе

(4) Все же зачем, можно как и (2)предложил (конечно используя методы 8.2) но все же зачем?

Нарисуй форму общую и при выборе дока заполняй програмно реквизиты формы , а потом записывай их в документ.

(8) Уточню.
Есть рабочий стол с двумя формами одного документа. Форма документа и форма списка.
Пользователь не создает новый документ в списке а просто заполняет форму на рабочем столе, нажимает записать и закрыть и форма создается.
В какой то момент ему необходимо открыть ранее созданный документ, для того чтобы не открывать в отдельном окне хочу вывести это документ в привычном для пользователя месте, на рабочем столе.

может что то упускаю занимаюсь с 8.2 наскоками, времени нет ..

Рисуешь общую форму и используешь ее для отображения данных документа.

Я 8 хорошо понял, но нужно дополнительно писать открытие, закрытие передача данных, проведение изменение.
Хочется без "сандвича с сыром" - без промежуточного кода, разве нельзя как то подсунуть другой объект, в 8.2. формы вообще оттащили от объекта, буду копать

ЗначениеВРеквизитФормы (ValueToFormAttribute)
Синтаксис:
ЗначениеВРеквизитФормы(<Значение>, <ИмяРеквизита>)
Параметры:
<Значение> (обязательный)
Тип: Произвольный. Значение прикладного типа, которое необходимо преобразовать в реквизит формы и для которого определено преобразование в универсальный объект.
Тип: Строка. Имя реквизита формы, в который необходимо поместить преобразованное значение.
Описание:
Преобразует объект прикладного типа в реквизит управляемой формы (данные формы) с указанным именем.
Если реквизит является основным реквизитом формы объекта или записи регистра сведений, то осуществляется обновление параметра формы Ключ и автоматического заголовка формы. Если ключ старого объекта отличается от ключа нового и старый объект был заблокирован формой, то блокировка на старый объект освобождается. Обновление флага модифицированности при этом не производится.

Исторически сложилось так, что обработка изменения данных, связанных с реквизитами формы обрабатывалась в обработчиках событий этих реквизитов. Так было в 77, так до сих пор пишется код типовых конфигураций. Однако в 80 появился метод ПодключитьОбработчикИзмененияДанных, с помощью которого код изменений можно писать более прозрачно.

Рассмотрим простейший пример: пусть у документа есть реквизиты "Валюта","Курс","Кратность","Контрагент","Договор","Дата".

При изменении реквизита "Валюта" нужно установить курс и кратность валюты на дату "Дата".
При изменении реквизита "Контрагент" нужно установить в реквизит "Договор" основной договор контрагента.

Классическая схема написания обработчиков событий формы будет такая:

Однако у приведенной схемы есть недостаток.
Допустим, пользователь создал новый документ и в него подставился контрагент по умолчанию.
По идее мы должны смоделировать в этой ситуации интерактивный выбор контрагента, чтобы проставился договор или скопировать код процедуры Реквизит_Контрагент_ПриИзменении.
Это можно сделать так:
[1с]
Выполнить(""+ПолучитьДействие(ЭлементыФормы.Контрагент,"ПриИзменении")+"(ЭлементыФормы.Контрагент)");
[/1с]
Или так:
[1с]
Реквизит_Контрагент_ПриИзменении();
[/1с]

Но хочется большей универсальности чтобы не приходилось дублировать код.
Добиться этого можно другой схемой:

[1с]
Процедура ПриОткрытии()

//Назначаем всем данным формы обработчик ИзменениеДанных
Для Каждого Эл из ЭлементыФормы Цикл
Попытка
ПодключитьОбработчикИзмененияДанных(Эл.Данные, "ИзменениеДанных");
Исключение
КонецПопытки;
КонецЦикла;

Процедура ОтображениеДанных(Реквизит)
Если Реквизит="Договор" ИЛИ Реквизит="Контрагент" ИЛИ Реквизит="*" Тогда
ЭлементыФормы.кнВзаиморасчеты.Доступность=Пусто(Договор) Или Пусто(Контрагент);
КонецЕсли;
КонецПроцедуры

Процедура ИзменениеДанных(Реквизит)
Если Реквизит="Контрагент" Тогда
Договор=ПолучитьОсновнойДоговорПоКонтрагенту(Контрагент);
ИначеЕсли Реквизит="Валюта" Тогда
Курс=ПолучитьКурсПоВалюте(Валюта);
Кратность=ПолучитьКратностьПоВалюте(Валюта);
КонецЕсли;
ОтображениеДанных(Реквизит);
КонецПроцедуры
[/1с]

Конечно, было бы идеально, если подобные цепочки событий можно было описывать в модуле объекта, аналогично Delpy с их Property Get, Property Set (события по установке/чтению свойств объекта) но пока еще это не реализовано в 80.

А зачем нужен вызов с реквизитом "*"?
Этот вызов происходит при открытии формы и предназначен для правильного отображения статуса объектов.
Например, по задаче требуется, чтобы если был выбран договор и контрагент, то была доступна кнопка "кнВзаиморасчеты". Понятно, что контролировать доступность (отображение) этой кнопки нужно только при открытии формы и при изменении контрагента или договора. При открытии формы вызывается изменение данных всей формы (Реквизит "*"), в остальных случаях вызывается изменение каждого конкретного реквизита.

Кстати, заметим. В начале договор и контрагент пусты.
Пользователь вызывает договор. Пользователь вводит контрагента, вызывается событие ИзменениеДанных("Контрагент"), после чего проставляется договор и вызывается событие ИзменениеДанных("Договор"), после этого вызывается ОтображениеДанных("Договор"), которое делает кнопку "кнВзаиморасчеты" доступной и еще раз ОтображениеДанных("Контрагент"), которое оставляет кнопку доступной.

Как видно, при такой схеме обработки событий можно очень экономно писать код по обновлению отображения формы в зависимости от данных объекта, сравните с кодом, который вы обычно пишите в ОбновленииОтображения и почуствуйте разницу.

Однако в бочке меда есть ложка дегтя - все вышесказанное не работает для таблиц значения и табличных частей. Обработкик подключить можно, но он не вызывается при изменениях. Это очень печально и надеюсь, в будущих версиях платформы будет исправлено.

Читайте также: