framework_eng/skills/bsl-practices/form-patterns/SKILL.md
Form module patterns (client-server interaction). This skill teaches the agent to write 1C managed form modules correctly.
npx skillsauth add steelmorgan/1c-agent-based-dev-framework form-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Key principle: Minimize the number and size of server calls. Each &НаСервере call is a network round-trip plus serialization of the full form context.
1. &НаКлиентеНаСервереБезКонтекста — чистые вычисления, обе среды
2. &НаСервереБезКонтекста — обращение к БД, без контекста формы
3. &НаСервере — нужен доступ к реквизитам формы
4. &НаКлиенте — интерактивная логика (диалоги, навигация)
Нужно ли обращаться к базе данных?
├── Нет → &НаКлиенте или &НаКлиентеНаСервереБезКонтекста
└── Да →
Нужен ли доступ к реквизитам формы?
├── Нет → &НаСервереБезКонтекста (передаём только параметры)
└── Да → &НаСервере (передаётся весь контекст)
// Чистое вычисление — обе среды
&НаКлиентеНаСервереБезКонтекста
Функция РассчитатьСумму(Количество, Цена, СтавкаНДС)
СуммаБезНДС = Количество * Цена;
СуммаНДС = СуммаБезНДС * СтавкаНДС / 100;
Возврат СуммаБезНДС + СуммаНДС;
КонецФункции
// Нужны данные из БД, но НЕ нужны реквизиты формы
&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатуры(НоменклатураСсылка)
Возврат ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
НоменклатураСсылка,
"Наименование, ЕдиницаИзмерения, СтавкаНДС, Цена");
КонецФункции
// Нужен доступ к реквизитам формы
&НаСервере
Процедура ПересчитатьИтогиНаСервере()
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
Объект.СуммаНДС = Объект.Товары.Итог("СуммаНДС");
КонецПроцедуры
Each server call takes ~100 ms (serialization + round-trip + deserialization). Three calls equal 300 ms of delay. One call that returns all data equals 100 ms.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ДанныеЗаполнения = ПолучитьДанныеЗаполненияПоКонтрагенту(Объект.Контрагент, Объект.Дата);
Объект.Договор = ДанныеЗаполнения.Договор;
Объект.Организация = ДанныеЗаполнения.Организация;
Объект.Валюта = ДанныеЗаполнения.Валюта;
КурсВалюты = ДанныеЗаполнения.Курс;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеЗаполненияПоКонтрагенту(КонтрагентСсылка, ДатаДокумента)
Результат = Новый Структура;
РеквизитыКонтрагента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
КонтрагентСсылка, "ДоговорПоУмолчанию, ОсновнаяОрганизация");
Результат.Вставить("Договор", РеквизитыКонтрагента.ДоговорПоУмолчанию);
Результат.Вставить("Организация", РеквизитыКонтрагента.ОсновнаяОрганизация);
Если ЗначениеЗаполнено(РеквизитыКонтрагента.ДоговорПоУмолчанию) Тогда
ВалютаДоговора = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
РеквизитыКонтрагента.ДоговорПоУмолчанию, "ВалютаВзаиморасчетов");
Результат.Вставить("Валюта", ВалютаДоговора);
Результат.Вставить("Курс", РаботаСКурсамиВалют.ПолучитьКурсВалюты(ВалютаДоговора, ДатаДокумента));
Иначе
Результат.Вставить("Валюта", Неопределено);
Результат.Вставить("Курс", 1);
КонецЕсли;
Возврат Результат;
КонецФункции
On the server, form data is not a real object but ДанныеФормы*. You must convert it to call object module methods.
| Scenario | Does conversion need to happen? |
|----------|------------------------------|
| 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 |
&НаСервере
Процедура ЗаполнитьПоУмолчаниюНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТоварыПоУмолчанию();
// ОБЯЗАТЕЛЬНО: преобразовать обратно, иначе изменения не отразятся на форме!
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
// ПЛОХО: изменения потеряны!
&НаСервере
Процедура ЗаполнитьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТаблицу();
// ЗАБЫЛИ: ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
1. ПриСозданииНаСервере — форма создаётся на сервере, данные загружены
2. ПриОткрытии — форма отобразилась на клиенте
1. ПередЗаписьюНаКлиенте — клиент: можно отменить (Отказ = Истина)
2. ПередЗаписьюНаСервере — сервер: последняя проверка
3. ПриЗаписиНаСервере — сервер: в той же транзакции
4. ПослеЗаписиНаСервере — сервер: обновление формы
5. ПослеЗаписи — клиент: оповещение
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ЗначенияЗаполнения") Тогда
ЗаполнитьЗначенияСвойств(Объект, Параметры.ЗначенияЗаполнения);
КонецЕсли;
Элементы.ГруппаСклад.Видимость = (Объект.ВидОперации <>
Перечисления.ВидыОпераций.Услуга);
ОбновитьСписокВыбораДоговоров();
КонецПроцедуры
// ПередЗаписьюНаКлиенте — проверки, требующие подтверждения пользователя
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если Объект.СуммаДокумента > 1000000 Тогда
Если Не ПараметрыЗаписи.Свойство("ПодтверждениеСуммы") Тогда
Отказ = Истина;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияСуммы", ЭтотОбъект, ПараметрыЗаписи),
НСтр("ru = 'Сумма документа превышает 1 000 000. Продолжить?'"),
РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// ПослеЗаписи — оповещения на клиенте
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка", Объект.Ссылка), ЭтотОбъект);
КонецПроцедуры
A dynamic list automatically provides pagination, search, and sorting.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Отборы через КомпоновкаДанных (пользователь сможет изменять)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
СписокДокументов,
"Организация",
Организация,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(Организация));
КонецПроцедуры
СписокДокументов.ПроизвольныйЗапрос = Истина;
СписокДокументов.ТекстЗапроса =
"ВЫБРАТЬ
| Реализация.Ссылка,
| Реализация.Дата,
| Реализация.Номер,
| Реализация.Контрагент,
| Реализация.СуммаДокумента,
| ЕСТЬNULL(Задолженность.СуммаОстаток, 0) КАК ОстатокЗадолженности
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки КАК Задолженность
| ПО Реализация.Контрагент = Задолженность.Контрагент
| И Реализация.Договор = Задолженность.Договор
|{ГДЕ
| Реализация.Дата >= &ДатаНачала}";
Rules: do not load the entire dataset; apply filters via КомпоновкаДанных instead of WHERE; do not use УПОРЯДОЧИТЬ ПО in a custom query.
&НаСервере
Процедура УстановитьУсловноеОформление()
УсловноеОформление.Элементы.Очистить();
ЭлементУО = УсловноеОформление.Элементы.Добавить();
ЭлементОтбора = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДатаОплаты");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
ЭлементОтбора.ПравоеЗначение = ТекущаяДатаСеанса();
ЭлементОтбора.Использование = Истина;
ЭлементОтбора2 = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Оплачен");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора2.ПравоеЗначение = Ложь;
ЭлементОтбора2.Использование = Истина;
ЭлементУО.Оформление.УстановитьЗначениеПараметра("ЦветФона", WebЦвета.МисттиРоуз);
ПолеОформления = ЭлементУО.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("СписокДокументов");
КонецПроцедуры
Modal calls (Предупреждение(), Вопрос()) are forbidden in the web client. Use ОписаниеОповещения instead.
ITS standard: “Restrictions on the use of modal methods.”
&НаКлиенте
Процедура УдалитьСтрокуТоваров(Команда)
Если Элементы.Товары.ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияУдаления", ЭтотОбъект),
НСтр("ru = 'Удалить выбранную строку?'"),
РежимДиалогаВопрос.ДаНет,
,
КодВозвратаДиалога.Нет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодтвержденияУдаления(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.Товары.Удалить(Элементы.Товары.ТекущаяСтрока);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОткрытьФормуПодбораТоваров(Команда)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Организация", Объект.Организация);
ПараметрыФормы.Вставить("Склад", Объект.Склад);
ПараметрыФормы.Вставить("Дата", Объект.Дата);
ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
ОткрытьФорму("Обработка.ПодборТоваров.Форма",
ПараметрыФормы,
ЭтотОбъект,
,,,
Новый ОписаниеОповещения("ПослеПодбораТоваров", ЭтотОбъект));
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодбораТоваров(РезультатПодбора, ДополнительныеПараметры) Экспорт
Если РезультатПодбора = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ДанныеТовара Из РезультатПодбора Цикл
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеТовара);
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
All visibility/accessibility changes should be done in a single server call to avoid interface flickering.
&НаСервере
Процедура УправлениеВидимостью()
ЭтоУслуга = Объект.ВидОперации = Перечисления.ВидыОпераций.Услуга;
Элементы.Склад.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыКоличество.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыЕдиницаИзмерения.Видимость = НЕ ЭтоУслуга;
Элементы.ГруппаДоставка.Видимость = НЕ ЭтоУслуга;
Элементы.Контрагент.ТолькоПросмотр = Объект.Проведен;
КонецПроцедуры
&НаКлиенте
Процедура ВидОперацииПриИзменении(Элемент)
УправлениеВидимостью(); // Один серверный вызов для всех изменений
КонецПроцедуры
// Изменение количества — пересчёт суммы на клиенте
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
// Изменение номенклатуры — нужен сервер для получения данных
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ДанныеНоменклатуры = ПолучитьДанныеНоменклатуры(ТекущаяСтрока.Номенклатура);
ТекущаяСтрока.ЕдиницаИзмерения = ДанныеНоменклатуры.ЕдиницаИзмерения;
ТекущаяСтрока.Цена = ДанныеНоменклатуры.Цена;
ТекущаяСтрока.СтавкаНДС = ДанныеНоменклатуры.СтавкаНДС;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
// Форма документа — после записи
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка, Проведен", Объект.Ссылка, Объект.Проведен),
ЭтотОбъект);
КонецПроцедуры
// Форма списка — обработка оповещения
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "Запись_РеализацияТоваровУслуг" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Primitives (Строка, Число, Дата, Булево), References, Enumerations, Структура, Соответствие, Массив, ФиксированныеКоллекции, ХранилищеЗначения.
| Type | Alternative | |-----|-------------| | ТаблицаЗначений | ДанныеФормыКоллекция (через реквизиты формы) | | ДеревоЗначений | ДанныеФормыДерево (через реквизиты формы) | | ОбъектМетаданных | Передавать ИмяМетаданных (строку) | | Запрос, РезультатЗапроса | Передавать результат (структура/массив) |
tools
Diagnostics for Vanessa Automation runs. Use when a feature scenario failed, artifacts were not created, or you need to classify a failure after launch.
tools
Creating and refining Vanessa Automation feature scenarios based on real project requirements. Use when you need to write or update a scenario test, not just run it.
tools
--- name: v8-session-manager description: Use when working with the 1С session manager (v8-session-manager) - launch, configuration, connecting 1С clients, reading session_list, calling proxied MCP-tools from 1С extensions, diagnostics. Triggers: mention of `v8-session-manager`, `session_list`, 1С extension MCP showcase, error “no active sessions” / “session_id required”, connecting a client to the manager via `mcpMode=ws`. provides_capabilities: # Built-in manager tools — always available whi
tools
Use when Codex needs to manage v8-runner on local 1C projects through the CLI: configure v8project.yaml, initialize infobases or EDT workspaces, build sources from Designer or EDT, run syntax checks and tests, dump infobase changes, convert source formats, load or export artifacts, launch 1C clients, or choose safe 1C automation command sequences.