| name | form-patterns |
| description | For managed form module code and server calls |
| alwaysApply | false |
Form Module Patterns (client-server interaction)
Key principle: Minimize the number and size of server calls. Every &НаСервере call is a network round-trip plus serialization of the entire form context.
Rule 1: Directive hierarchy — prefer context-free calls
Preference order (from best to worst)
1. &НаКлиентеНаСервереБезКонтекста — чистые вычисления, обе среды
2. &НаСервереБезКонтекста — обращение к БД, без контекста формы
3. &НаСервере — нужен доступ к реквизитам формы
4. &НаКлиенте — интерактивная логика (диалоги, навигация)
Decision rule
Do you need access to the database?
├── No → &НаКлиенте or &НаКлиентеНаСервереБезКонтекста
└── Yes →
Do you need access to form attributes?
├── No → &НаСервереБезКонтекста (only parameters are passed)
└── Yes → &НаСервере (the entire context is passed)
// Чистое вычисление - обе среды
&НаКлиентеНаСервереБезКонтекста
Функция РассчитатьСумму(Количество, Цена, СтавкаНДС)
СуммаБезНДС = Количество * Цена;
СуммаНДС = СуммаБезНДС * СтавкаНДС / 100;
Возврат СуммаБезНДС + СуммаНДС;
КонецФункции
// Нужны данные из базы, но реквизиты формы НЕ нужны
&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатуры(НоменклатураСсылка)
Возврат ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
НоменклатураСсылка,
"Наименование, ЕдиницаИзмерения, СтавкаНДС, Цена");
КонецФункции
// Нужен доступ к реквизитам формы
&НаСервере
Процедура ПересчитатьИтогиНаСервере()
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
Объект.СуммаНДС = Объект.Товары.Итог("СуммаНДС");
КонецПроцедуры
Rule 2: Minimize server calls - group data
Each server call is about 100 ms (serialization + round-trip + deserialization). Three calls = 300 ms of latency. One call that returns all data = 100 ms.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ДанныеЗаполнения = ПолучитьДанныеЗаполненияПоКонтрагенту(Объект.Контрагент, Объект.Дата);
Объект.Договор = ДанныеЗаполнения.Договор;
Объект.Организация = ДанныеЗаполнения.Организация;
Объект.Валюта = ДанныеЗаполнения.Валюта;
КурсВалюты = ДанныеЗаполнения.Курс;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеЗаполненияПоКонтрагенту(КонтрагентСсылка, ДатаДокумента)
Результат = Новый Структура;
РеквизитыКонтрагента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
КонтрагентСсылка, "ДоговорПоУмолчанию, ОсновнаяОрганизация");
Результат.Вставить("Договор", РеквизитыКонтрагента.ДоговорПоУмолчанию);
Результат.Вставить("Организация", РеквизитыКонтрагента.ОсновнаяОрганизация);
Если ЗначениеЗаполнено(РеквизитыКонтрагента.ДоговорПоУмолчанию) Тогда
ВалютаДоговора = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
РеквизитыКонтрагента.ДоговорПоУмолчанию, "ВалютаВзаиморасчетов");
Результат.Вставить("Валюта", ВалютаДоговора);
Результат.Вставить("Курс", РаботаСКурсамиВалют.ПолучитьКурсВалюты(ВалютаДоговора, ДатаДокумента));
Иначе
Результат.Вставить("Валюта", Неопределено);
Результат.Вставить("Курс", 1);
КонецЕсли;
Возврат Результат;
КонецФункции
Rule 3: ДанныеФормыВЗначение / ЗначениеВДанныеФормы
On the server side of a form, object data is not the real object, but ДанныеФормы*. To call object module methods, conversion is required.
| Scenario | Is conversion needed? |
|---|
| Reading form attributes | No - Объект.Реквизит works directly |
| Calling object module methods | Yes |
| Passing the object to a common module | Yes - common modules work with real objects |
&НаСервере
Процедура ЗаполнитьПоУмолчаниюНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТоварыПоУмолчанию();
// ОБЯЗАТЕЛЬНО: преобразовать обратно, иначе изменения не отразятся на форме!
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Typical mistake - forgot ЗначениеВРеквизитФормы
// ПЛОХО: изменения теряются!
&НаСервере
Процедура ЗаполнитьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТаблицу();
// ЗАБЫЛИ: ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Rule 4: Form event handlers - order and purpose
Event order when opening a form
1. ПриСозданииНаСервере - form is created on the server, data is loaded
2. ПриОткрытии - form is displayed on the client
Event order when writing
1. ПередЗаписью - client: can be canceled (Отказ = Истина)
2. ПередЗаписьюНаСервере - server: final check
3. ПриЗаписиНаСервере - server: in the same transaction
4. ПослеЗаписиНаСервере - server: form update
5. ПослеЗаписи - client: notification
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ЗначенияЗаполнения") Тогда
ЗаполнитьЗначенияСвойств(Объект, Параметры.ЗначенияЗаполнения);
КонецЕсли;
Элементы.ГруппаСклад.Видимость = (Объект.ВидОперации <>
Перечисления.ВидыОпераций.Услуга);
ОбновитьСписокВыбораДоговоров();
КонецПроцедуры
// ПередЗаписью (клиент) - проверки, требующие подтверждения пользователя
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если Объект.СуммаДокумента > 1000000 Тогда
Если Не ПараметрыЗаписи.Свойство("ПодтверждениеСуммы") Тогда
Отказ = Истина;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияСуммы", ЭтотОбъект, ПараметрыЗаписи),
НСтр("ru = 'Сумма документа превышает 1 000 000. Продолжить?'"),
РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// ПослеЗаписи - оповещения на клиенте
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка", Объект.Ссылка), ЭтотОбъект);
КонецПроцедуры
Rule 5: Dynamic lists - configuration and optimization
A dynamic list automatically implements pagination, search, and sorting.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Отборы через КомпоновкаДанных (пользователь может их изменить)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
СписокДокументов,
"Организация",
Организация,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(Организация));
КонецПроцедуры
Custom query for a dynamic list
СписокДокументов.ПроизвольныйЗапрос = Истина;
СписокДокументов.ТекстЗапроса =
"ВЫБРАТЬ
| Реализация.Ссылка,
| Реализация.Дата,
| Реализация.Номер,
| Реализация.Контрагент,
| Реализация.СуммаДокумента,
| ЕСТЬNULL(Задолженность.СуммаОстаток, 0) КАК ОстатокЗадолженности
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки КАК Задолженность
| ПО Реализация.Контрагент = Задолженность.Контрагент
| И Реализация.Договор = Задолженность.Договор
|{ГДЕ
| Реализация.Дата >= &ДатаНачала}";
Rules: do not load all data; use КомпоновкаДанных for filters, not WHERE; do not use ORDER BY in a custom query.
Rule 6: Conditional formatting - programmatic configuration
&НаСервере
Процедура УстановитьУсловноеОформление()
УсловноеОформление.Элементы.Очистить();
ЭлементУО = УсловноеОформление.Элементы.Добавить();
ЭлементОтбора = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДатаОплаты");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
ЭлементОтбора.ПравоеЗначение = ТекущаяДатаСеанса();
ЭлементОтбора.Использование = Истина;
ЭлементОтбора2 = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Оплачен");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора2.ПравоеЗначение = Ложь;
ЭлементОтбора2.Использование = Истина;
ЭлементУО.Оформление.УстановитьЗначениеПараметра("ЦветФона", WebЦвета.МисттиРоуз);
ПолеОформления = ЭлементУО.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("СписокДокументов");
КонецПроцедуры
Rule 7: Asynchronous dialogs instead of modal ones
Modal calls (Предупреждение(), Вопрос()) are prohibited in the web client. Use ОписаниеОповещения.
ITS standard: "Restrictions on the use of modal methods".
&НаКлиенте
Процедура УдалитьСтрокуТоваров(Команда)
Если Элементы.Товары.ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияУдаления", ЭтотОбъект),
НСтр("ru = 'Удалить выбранную строку?'"),
РежимДиалогаВопрос.ДаНет,
,
КодВозвратаДиалога.Нет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодтвержденияУдаления(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.Товары.Удалить(Элементы.Товары.ТекущаяСтрока);
КонецЕсли;
КонецПроцедуры
Rule 8: Opening forms and handling the result
&НаКлиенте
Процедура ОткрытьФормуПодбораТоваров(Команда)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Организация", Объект.Организация);
ПараметрыФормы.Вставить("Склад", Объект.Склад);
ПараметрыФормы.Вставить("Дата", Объект.Дата);
ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
ОткрытьФорму("Обработка.ПодборТоваров.Форма",
ПараметрыФормы,
ЭтотОбъект,
,,,
Новый ОписаниеОповещения("ПослеПодбораТоваров", ЭтотОбъект));
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодбораТоваров(РезультатПодбора, ДополнительныеПараметры) Экспорт
Если РезультатПодбора = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ДанныеТовара Из РезультатПодбора Цикл
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеТовара);
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
Rule 9: Visibility management - batch changes
Collect all visibility/availability changes into a single server call to avoid UI "flicker".
&НаСервере
Процедура УправлениеВидимостью()
ЭтоУслуга = Объект.ВидОперации = Перечисления.ВидыОпераций.Услуга;
Элементы.Склад.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыКоличество.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыЕдиницаИзмерения.Видимость = НЕ ЭтоУслуга;
Элементы.ГруппаДоставка.Видимость = НЕ ЭтоУслуга;
Элементы.Контрагент.ТолькоПросмотр = Объект.Проведен;
КонецПроцедуры
&НаКлиенте
Процедура ВидОперацииПриИзменении(Элемент)
УправлениеВидимостью(); // Один серверный вызов на все изменения
КонецПроцедуры
Rule 10: Working with tabular sections - recalculate on the client where possible
// Изменение количества - пересчёт суммы на клиенте
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
// Изменение номенклатуры - нужен сервер, чтобы получить данные
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ДанныеНоменклатуры = ПолучитьДанныеНоменклатуры(ТекущаяСтрока.Номенклатура);
ТекущаяСтрока.ЕдиницаИзмерения = ДанныеНоменклатуры.ЕдиницаИзмерения;
ТекущаяСтрока.Цена = ДанныеНоменклатуры.Цена;
ТекущаяСтрока.СтавкаНДС = ДанныеНоменклатуры.СтавкаНДС;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
Rule 11: Notifications between forms
// Форма документа - после записи
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка, Проведен", Объект.Ссылка, Объект.Проведен),
ЭтотОбъект);
КонецПроцедуры
// Форма списка - обработка оповещения
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "Запись_РеализацияТоваровУслуг" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Rule 12: Data types passed between client and server
Freely passed types
Primitives (String, Number, Date, Boolean), References, Enumerations, Structure, Map, Array, FixedCollections, ValueStorage.
Types that are NOT passed (server only)
| Type | Alternative |
|---|
| ValueTable | FormDataCollection (via form attributes) |
| ValueTree | FormDataTree (via form attributes) |
| MetadataObject | Pass MetadataName (a string) |
| Query, QueryResult | Pass the result (structure/array) |
Rule 13: Properties of platform form elements are part of behavior
Some aspects of form correctness are determined not by module code, but by properties of the form and its elements. When generating XML, reviewing the form, or modifying a standard form, check these invariants.
| Area | Check |
|---|
| Commands | A command that changes persisted data must have ИзменяетСохраняемыеДанные. Otherwise, the form may not become modified, and with ТолькоПросмотр the command will remain active. |
| Tabular sections | On the tabular section page, the data path for the header must be filled in, for example Объект.Товары.КоличествоСтрок, if the header should display the number of rows. |
| Required fields | Use the ПроверкаЗаполнения property where requiredness is a property of the attribute. Manual validation in code is acceptable for conditional business rules, but should not replace platform validation without reason. |
| List form | Add ГруппаПользовательскихНастроек if the list form should show user filters. |
| Standard forms | Implement standard form customizations programmatically. If tabs are present, place new attributes on a separate service tab; if there are no tabs, place them in a service group without a header or highlighting, unless the specification indicates an exact location. |
For validation in a form handler, use the standard call:
Если Не ПроверитьЗаполнение() Тогда
Возврат;
КонецЕсли;
Rule 14: Visibility and availability are updated centrally
Do not scatter assignments of Видимость, Доступность, and ТолькоПросмотр across different form handlers. Collect them in a single procedure and call it as the final step after initialization and data filling.
&НаКлиенте
Процедура ОбновитьВидимостьДоступностьЭлементовФормы()
ЭтоУслуга = Объект.ВидОперации = Перечисления.ВидыОпераций.Услуга;
Элементы.Склад.Видимость = Не ЭтоУслуга;
Элементы.Комментарий.ТолькоПросмотр = Объект.Проведен;
КонецПроцедуры
Prefer boolean algebra over Если ... Тогда ... Иначе ... branching when the property value is expressed directly by a condition.
depends_on: []