Installs into .claude/skills of the current project.
Are you the author of Form Edit?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ingvarconsulting-form-edit)
---
name: form-edit
description: Добавление и удаление элементов, реквизитов и команд в существующей управляемой форме 1С. Используй когда нужно точечно модифицировать готовую форму
allowed-tools:
- Bash
- Read
- Write
- Glob
---
# /form-edit — Редактирование формы
## MCP routing
- Preferred path: use MCP `unica` tool `unica.apply` с операциями формы; адрес
цели — логический, файлового селектора у поверхности нет.
- Не зови внутренние адаптеры напрямую: они спрятаны за MCP `unica`.
- Сначала вызови `unica.apply` с `at` и `ops`: это план без записи.
Когда пользователь поручил внести эту правку, вызови `unica.apply` только
с `executionToken` из `data.executionToken` успешного плана.
- Словарь операций узла даёт `unica.view {at}` в секции `can`: что не названо
там, того поверхность не пишет.
- Поддержку объекта проверяет сама операция; заблокированный поставщиком
объект правится через расширение, а не правкой метаданных поддержки.
Добавляет и удаляет элементы, реквизиты и команды существующей управляемой
формы. Идентификаторы, companion-элементы (`ContextMenu`, `ExtendedTooltip` и
прочие) и привязки событий собирает сама операция.
## Адрес и операции
Цель — узел формы: `<набор>:<Вид>.<Имя>.Form.<Форма>`. Имя набора даёт
`unica.view {}`, адрес по имени объекта — `unica.search {corpus: "names"}`.
| Операция | Что делает |
|---|---|
| `element.add` | добавляет элементы; `items[]` несёт то же описание, что раздел «JSON формат» ниже |
| `element.remove` | удаляет элемент, названный адресом (`…Form.Ф.Item.X`) или списком `items` |
| `formAttribute.add` | добавляет реквизит формы |
| `formCommand.add` | добавляет команду формы |
| `event.bind` | привязывает обработчик к событию формы или элемента |
| `form.add`, `form.set`, `form.remove` | состав форм объекта и их свойства |
## MCP вызов
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "unica.apply",
"arguments": {
"at": "main:Catalog.Номенклатура.Form.ФормаЭлемента",
"ops": [
{
"op": "element.add",
"args": {
"items": [
{"name": "Артикул", "type": "InputField", "path": "Объект.Артикул", "into": "ГруппаШапка"}
]
}
}
]
}
}
}
```
Применение — вызов только с `executionToken` из `data.executionToken` успешного плана.
## JSON формат
```json
{
"into": "ГруппаШапка",
"after": "Контрагент",
"elements": [
{ "input": "Склад", "path": "Объект.Склад", "on": ["OnChange"] }
],
"attributes": [
{ "name": "СуммаИтого", "type": "decimal(15,2)" }
],
"commands": [
{ "name": "Рассчитать", "action": "РассчитатьОбработка" }
]
}
```
### Расширения (extension-формы)
Для заимствованных форм (с `<BaseForm>`) автоматически активируется extension-режим: ID начинаются с 1000000+. Доступны дополнительные секции:
```json
{
"formEvents": [
{ "name": "OnCreateAtServer", "handler": "Расш1_ПриСозданииПосле", "callType": "After" },
{ "name": "OnOpen", "handler": "Расш1_ПриОткрытии", "callType": "Before" }
],
"elementEvents": [
{ "element": "Банк", "name": "OnChange", "handler": "Расш1_БанкПриИзменении", "callType": "Before" }
],
"commands": [
{ "name": "Подбор", "action": "Расш1_ПодборПосле", "callType": "After" },
{ "name": "Запрос", "actions": [
{ "callType": "Before", "handler": "Расш1_ЗапросПеред" },
{ "callType": "After", "handler": "Расш1_ЗапросПосле" }
]}
],
"elements": [
{ "input": "Поле", "path": "Объект.Поле", "on": [{ "event": "OnChange", "callType": "After" }] }
]
}
```
### Позиционирование элементов
| Ключ | По умолчанию | Описание |
|------|-------------|----------|
| `into` | корневой ChildItems | Имя группы/таблицы/страницы, куда вставлять |
| `after` | в конец | Имя элемента, после которого вставлять |
### Удаление элементов
`element.remove` называет цель адресом — `…Form.<Форма>.Item.<Имя>` — либо
списком имён в `items`:
```json
{
"op": "element.remove",
"args": {"items": [{"name": "Товары"}]}
}
```
Ключ `removeElements` — внутренняя форма описания, в которую операция
разворачивает эти имена; аргументом `element.remove` он не является и в вызове
отклоняется как неизвестное поле. Гарантии ниже описаны для обеих форм.
- Элемент сопоставляется с атрибутом XML `name` точно, с учётом регистра и пробелов. Поиск по префиксу и нормализация имени не выполняются.
- В записи разрешено только строковое непустое поле `name`. Параметров `includeCompanions` и `ifMissing` нет: они отклоняются как неизвестные поля.
- Удаляется всё структурное XML-поддерево элемента. Вложенные элементы и contained companions (`ContextMenu`, `ExtendedTooltip`, `AutoCommandBar` и другие узлы внутри поддерева) удаляются вместе с владельцем и перечисляются в результате с `reason: "contained"`.
- По умолчанию отсутствующая цель завершает весь вызов ошибкой `FORM_ELEMENT_NOT_FOUND`; повторное удаление отсутствующего элемента не является idempotent no-op.
- Удалять можно только публичный элемент рабочего дерева формы, непосредственно принадлежащий контейнеру `ChildItems`. Корневой `AutoCommandBar`, отдельный companion внутри владельца, baseline внутри `BaseForm` и другие именованные узлы вне рабочего дерева защищены; неоднозначные и перекрывающиеся цели также отклоняются.
- До публикации проверяются конфликты с тем же `definition` (`elements`, вложенные `children`/`columns`, `into`, `after`, `elementEvents`) и поддерживаемые ссылки в остающемся рабочем XML: binding paths вида `Items.<name>.CurrentData...` (включая имена с точками), `Form.Item.<name>.StandardCommand.*` и `AdditionSource/Item`.
- Если сохраняемый элемент ссылается на удаляемый contained companion, весь вызов завершается атомарной ошибкой `FORM_EDIT_REMOVE_SURVIVING_REFERENCE`: companion не отделяется от владельца и не удаляется частично. Ссылки из baseline `BaseForm` не блокируют изменение рабочего дерева, а сам baseline не редактируется.
- Проверка ссылок намеренно не анализирует BSL и не переписывает обращения к элементу в `Module.bsl`. Такие ссылки нужно найти и изменить отдельно до удаления.
- Весь batch атомарен: планирование, проверки ссылок и полная валидация спроецированного XML завершаются до фиксации транзакции. Apply дополнительно повторяет проверку формы (`unica.check` на узле формы) после записи внутри транзакции; при любой ошибке Form.xml не меняется.
Preview и apply возвращают одинаковую типизированную форму `data`:
```json
{
"changed": true,
"removed": [
{ "name": "Товары", "kind": "Table", "reason": "requested" },
{ "name": "ТоварыКонтекстноеМеню", "kind": "ContextMenu", "reason": "contained" }
],
"validation": "passed"
}
```
При планировании (`at` и `ops`) файл и cache events не меняются. Preview, apply и no-op проходят одну полную валидацию спроецированного XML до возврата `validation: "passed"`. Apply (`executionToken`) публикует только успешно проверенный результат и инвалидирует кэш событием `FormChanged`, только если `changed: true`. Валидный idempotent no-op без удаления сохраняет исходные байты, возвращает `changed: false`, пустой `removed` и не создаёт cache event; невалидный исходный XML завершается ошибкой.
### Типы элементов
В `element.add` каждый `items[]` задаёт `name` и `type` (платформенный XML-тег
из таблицы). Короткие ключи ниже относятся к внутреннему описанию элементов.
Словарь создаёт одиннадцать видов:
| Ключ | XML тег | Companions |
|------|---------|------------|
| `input` | InputField | ContextMenu, ExtendedTooltip |
| `html` | HTMLDocumentField | ContextMenu, ExtendedTooltip |
| `check` | CheckBoxField | ContextMenu, ExtendedTooltip |
| `label` | LabelDecoration | ContextMenu, ExtendedTooltip |
| `labelField` | LabelField | ContextMenu, ExtendedTooltip |
| `group` | UsualGroup | ExtendedTooltip |
| `table` | Table | ContextMenu, AutoCommandBar, Search*, ViewStatus* |
| `pages` | Pages | ExtendedTooltip |
| `page` | Page | ExtendedTooltip |
| `button` | Button | ExtendedTooltip |
| `commandBar` | CommandBar | — |
Группы и таблицы поддерживают `children`/`columns` для вложенных элементов.
### Поле HTML-документа
HTML-поле привязывается через `path` к строковому реквизиту формы.
Публичный `items[]` задаёт `name` и `type: "HTMLDocumentField"`;
внутренний ключ `html` здесь отклоняется, в том числе вместе с `type`.
Сначала создайте его через `formAttribute.add`, если реквизита ещё нет.
Затем запросите план добавления поля и примените его отдельным вызовом
`unica.apply` только с `executionToken` из `data.executionToken` ответа:
```json
{
"at": "main:Catalog.ТехническиеПроекты.Form.ФормаЭлемента",
"ops": [{"op": "element.add", "args": {"items": [{
"name": "ПолеОписания", "type": "HTMLDocumentField",
"path": "СтраницаРедактораОписания", "titleLocation": "none",
"width": 1, "height": 1, "autoMaxWidth": false, "skipOnInput": true,
"on": ["OnClick", "DocumentComplete"]
}]}}]
}
```
Поддержаны `path`, `title`, `tooltip`, `titleLocation`, `width`/`height`
(целые `0..4294967295`), `autoMaxWidth`/`autoMaxHeight`, `skipOnInput`,
`visible`, `userVisible`, `enabled`, `readOnly` и синонимы `hidden`/`disabled`.
Булевы параметры принимают JSON boolean. События — только `OnClick` и
`DocumentComplete`; имена обработчиков можно задать в `handlers` вместе с `on`.
Для существующего поля используйте `event.bind` по адресу `…Form.ФормаЭлемента.Item.ПолеОписания`.
DOM-свойство `Document` относится к выполнению 1С и не записывается в XML.
Для помещения HTML-поля в группу задайте `into` в отдельном `items[]`;
`after` задаёт соседний элемент. Вложенный typed-объект с
`type: "HTMLDocumentField"` в `children`/`columns` отклоняется с подсказкой
использовать отдельный item. Полученную форму прочитайте через `unica.view`
и проверьте через `unica.check`.
### Чего словарь не пишет
Остальные виды платформенных полей и декораций **поверхность не создаёт**:
поля картинки, текстового, табличного и форматированного документа,
диаграммы и сводной диаграммы, диаграммы Ганта, календаря, периода,
индикатора, ползунка, географической схемы, дендрограммы, планировщика,
радиокнопки, а также декоративную картинку и поле поиска.
Существующий элемент такого вида **читается** `unica.view` на узле формы и
переживает правку соседей; создать или переписать его этой операцией нельзя.
Если задача требует именно такого элемента — сообщи об этом как о пробеле
контракта Unica MCP и не подменяй его другим видом.
### Кнопки: command и stdCommand
- `"command": "ИмяКоманды"` → `Form.Command.ИмяКоманды`
- `"stdCommand": "Close"` → `Form.StandardCommand.Close`
- `"stdCommand": "Товары.Add"` → `Form.Item.Товары.StandardCommand.Add` (стандартная команда элемента)
### Доступность команд формы
Если пользователь просит заблокировать, отключить или сделать недоступной
команду формы, управляй UI-элементами, связанными с командой, а не объектом
команды. Не генерируй и не советуй `Команды[ИмяКоманды].Доступность`: у команды
формы не используется свойство `Доступность` для управления кнопками и пунктами
меню.
Перед написанием BSL-кода прочитай узел формы через `unica.view` по
квалифицированному адресу `at` (CTR.SOURCE.LOGICAL-NODE-VIEW-SHAPE). Например,
аргументы вызовов чтения формы и её коллекции элементов:
```json
{"name": "unica.view", "arguments": {"at": "main:Catalog.Номенклатура.Form.ФормаЭлемента"}}
```
```json
{"name": "unica.view", "arguments": {"at": "main:Catalog.Номенклатура.Form.ФормаЭлемента.Item"}}
```
Ответ узла содержит `props` и `branches`, страница коллекции — `items` с
адресами `at`. Обойди все вложенные ветви `Item` по адресам из `branches` и
все страницы по возвращённому `cursor`, сохраняя адрес и параметры чтения.
У элемента нынешний view публикует в `props` только `tag`, `title`, `visible`,
`enabled`, `readOnly` (`title` равен `null`, если отдельный заголовок не задан).
Для установления связи требуется `CommandName`, например
`Form.Command.<ИмяКоманды>`, либо соответствующая ссылка стандартной команды.
Однако нынешний view не публикует ни `CommandName`, ни `binding`: по одному
дереву элементов нельзя доказать связь с командой и полноту связанных элементов.
Не угадывай связь по имени или заголовку. Для недостающих данных используй
аварийный мост к исходникам, разрешённый для чтения файла вне Unica:
```json
{"name": "unica.resolve", "arguments": {"at": "main:Catalog.Номенклатура.Form.ФормаЭлемента"}}
```
Возвращённый `path` относится к корню выбранного набора исходников, а не
обязательно к корню рабочего пространства. Сверь корень и формат набора
с конфигурацией проекта. В выгрузке Designer это может быть дескриптор
`Catalogs/Номенклатура/Forms/ФормаЭлемента.xml`: дерево элементов находится
рядом, в `ФормаЭлемента/Ext/Form.xml`. Проверь существование файла и его
корень `Form` в пространстве имён `http://v8.1c.ru/8.3/xcf/logform`;
дескриптор `MetaDataObject` не содержит полного дерева формы. Не переноси
эту раскладку на EDT и другие форматы без подтверждения.
Прочитай содержимое формы через `Read` и найди все элементы с точным
`CommandName` нужной команды, учитывая пространство имён XML. Обойди весь XML, включая `AutoCommandBar`,
`ContextMenu`, командные панели таблиц и вложенные группы, а не только
`ChildItems` корня: проекция `Item` не показывает все эти контейнеры.
Зафиксируй имена элементов и их привязки. Это только чтение; изменения
исходников по-прежнему выполняются через `unica.apply` с preview и `executionToken`.
Проверь также автозаполнение командных панелей и создание элементов в модуле
формы: статический XML не доказывает состав динамического интерфейса.
Если файл недоступен, формат не подтверждён или полнота связей остаётся
неизвестной, назови конкретное ограничение и найденных кандидатов; не генерируй
BSL с неподтверждёнными именами. Пробел чтения MCP обозначай как
**Unica MCP contract gap**, а динамический состав формы — как требующий
проверки кода или открытой формы. Отсутствие `binding` в `view` само по себе
не завершает исследование.
Когда связи подтверждены, меняй все связанные элементы через
`Элементы[ИмяЭлемента].Доступность`: кнопку основной командной панели, кнопку
командной панели таблицы, пункт контекстного меню, кнопку группы или подменю.
Одна найденная кнопка не доказывает, что других элементов этой команды нет.
```bsl
Элементы[ИмяЭлемента].Доступность = Ложь;
```
Например, если в проверенной форме кнопки `ЗаполнитьВПанели` и
`ЗаполнитьВМеню` обе связаны с `Form.Command.Заполнить`, отключи обе:
```bsl
Для Каждого ИмяЭлемента Из СтрРазделить("ЗаполнитьВПанели,ЗаполнитьВМеню", ",") Цикл
Элементы[ИмяЭлемента].Доступность = Ложь;
КонецЦикла;
```
Если связанный элемент нельзя определить однозначно, сначала проанализируй форму
и назови найденные кандидаты. Для стандартных команд табличной части отдельно
проверь свойство таблицы `ТолькоПросмотр` (`readOnly` в `props` узла таблицы):
когда таблица уже только для просмотра, платформа обычно сама блокирует стандартные команды добавления,
копирования, удаления и перемещения строк. Ручная блокировка нужна прежде всего
для пользовательских элементов, запускающих изменение табличной части: подбор,
заполнение, загрузку, пересчёт, удаление строк, сортировку, изменение цен,
скидок, НДС и аналогичные действия.
### Допустимые события (`on`)
Editor до записи проверяет событие по единой платформенной матрице. Недопустимое сочетание возвращает `ok=false` и код `FORM_EVENT_*`, не меняя файл. Основные сочетания:
- **input**: `OnChange`, `StartChoice`, `ChoiceProcessing`, `Clearing`, `AutoComplete`, `TextEditEnd`, `Opening`, `Creating`, `EditTextChange`
- **check**: `OnChange`
- **table**: `OnStartEdit`, `OnEditEnd`, `OnChange`, `Selection`, `BeforeAddRow`, `BeforeDeleteRow`, `OnActivateRow`
- **label**: `Click`, `URLProcessing`
- **picture**: `Click`, `Drag`, `DragCheck`
- **pages**: `OnCurrentPageChange`
- **page/button/group/command bar**: события не поддерживаются
События `table` требуют непустой привязки: `path` для нового элемента или прямого `DataPath` у существующего `Table`. Платформа удаляет обработчики событий у несвязанной таблицы при загрузке/выгрузке конфигурации.
`OnReadAtServer`, `BeforeWrite`, `BeforeWriteAtServer`, `OnWriteAtServer`, `AfterWriteAtServer` и `AfterWrite` разрешены только при подтверждённом persistent object/record типе главного реквизита. Для `DataProcessorObject`, `ReportObject`, `DynamicList` и неизвестного контекста они отклоняются. `NewWriteProcessing` и `FillCheckProcessingAtServer` являются общими событиями формы и этим ограничением не связаны.
### Система типов (для attributes)
`string`, `string(100)`, `decimal(15,2)`, `boolean`, `date`, `dateTime`, `CatalogRef.XXX`, `DocumentObject.XXX`, `ValueTable`, `DynamicList`, `Type1 | Type2` (составной).
### Секции расширений
| Секция | Назначение |
|--------|-----------|
| `formEvents` | События уровня формы; `callType` только для расширения |
| `elementEvents` | События существующих элементов; `callType` только для расширения |
| `callType` на `commands` | callType на Action команды |
| `callType` на `on` | callType на событиях новых элементов (объектный формат) |
Все extension-секции опциональны — без них навык работает как с обычными формами.
Повтор идентичного binding является явным idempotent no-op. Конфликт обработчика/`callType`, duplicate, отсутствующий элемент и любой недопустимый event отклоняют весь batch до мутации; планирование использует тот же planner.
## Workflow
1. `unica.view` на узле формы — текущая структура и секция `can`
2. Собрать `ops` по описанию ниже
3. `unica.apply` с `at` и `ops` — план и `data.executionToken`
4. `unica.apply` только с `executionToken` — применение
5. `unica.check` на узле формы — проверка, затем `unica.view` — убедиться, что структура изменилась правильно