mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-01 17:27:45 +03:00
Merge dev: form skills (info, compile, validate, add, patterns)
Complete managed form pipeline: - /form-info: compact XML analysis - /form-compile: JSON DSL to Form.xml generation - /form-validate: structural validation - /form-add: add elements/attributes/commands to existing forms - /form-patterns: layout guide with archetypes and conventions Event validation, element-level stdCommand, ERP patterns. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,127 @@
|
|||||||
|
---
|
||||||
|
name: form-add
|
||||||
|
description: Добавление элементов, реквизитов и команд в существующую управляемую форму 1С (Form.xml)
|
||||||
|
argument-hint: <FormPath> <JsonPath>
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Write
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /form-add — Добавление в форму
|
||||||
|
|
||||||
|
Добавляет элементы, реквизиты и/или команды в существующий Form.xml. Автоматически выделяет ID из правильного пула, генерирует companion-элементы (ContextMenu, ExtendedTooltip, и др.) и обработчики событий.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
```
|
||||||
|
/form-add <FormPath> <JsonPath>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Параметры
|
||||||
|
|
||||||
|
| Параметр | Обязательный | Описание |
|
||||||
|
|-----------|:------------:|----------------------------------|
|
||||||
|
| FormPath | да | Путь к существующему Form.xml |
|
||||||
|
| JsonPath | да | Путь к JSON с описанием добавлений |
|
||||||
|
|
||||||
|
## Команда
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\form-add\scripts\form-add.ps1 -FormPath "<путь>" -JsonPath "<путь>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON формат
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"into": "ГруппаШапка",
|
||||||
|
"after": "Контрагент",
|
||||||
|
"elements": [
|
||||||
|
{ "input": "Склад", "path": "Объект.Склад", "on": ["OnChange"] }
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "СуммаИтого", "type": "decimal(15,2)" }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Рассчитать", "action": "РассчитатьОбработка" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Позиционирование элементов
|
||||||
|
|
||||||
|
| Ключ | По умолчанию | Описание |
|
||||||
|
|------|-------------|----------|
|
||||||
|
| `into` | корневой ChildItems | Имя группы/таблицы/страницы, куда вставлять |
|
||||||
|
| `after` | в конец | Имя элемента, после которого вставлять |
|
||||||
|
|
||||||
|
### Типы элементов
|
||||||
|
|
||||||
|
Те же DSL-ключи, что в `/form-compile`:
|
||||||
|
|
||||||
|
| Ключ | XML тег | Companions |
|
||||||
|
|------|---------|------------|
|
||||||
|
| `input` | InputField | 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 |
|
||||||
|
|
||||||
|
Группы и таблицы поддерживают `children`/`columns` для вложенных элементов.
|
||||||
|
|
||||||
|
### Кнопки: command и stdCommand
|
||||||
|
|
||||||
|
- `"command": "ИмяКоманды"` → `Form.Command.ИмяКоманды`
|
||||||
|
- `"stdCommand": "Close"` → `Form.StandardCommand.Close`
|
||||||
|
- `"stdCommand": "Товары.Add"` → `Form.Item.Товары.StandardCommand.Add` (стандартная команда элемента)
|
||||||
|
|
||||||
|
### Допустимые события (`on`)
|
||||||
|
|
||||||
|
Компилятор предупреждает об ошибках в именах событий. Основные:
|
||||||
|
|
||||||
|
- **input**: `OnChange`, `StartChoice`, `ChoiceProcessing`, `Clearing`, `AutoComplete`, `TextEditEnd`
|
||||||
|
- **check**: `OnChange`
|
||||||
|
- **table**: `OnStartEdit`, `OnEditEnd`, `OnChange`, `Selection`, `BeforeAddRow`, `BeforeDeleteRow`, `OnActivateRow`
|
||||||
|
- **label/picture**: `Click`, `URLProcessing`
|
||||||
|
- **pages**: `OnCurrentPageChange`
|
||||||
|
- **button**: `Click`
|
||||||
|
|
||||||
|
### Система типов (для attributes)
|
||||||
|
|
||||||
|
`string`, `string(100)`, `decimal(15,2)`, `boolean`, `date`, `dateTime`, `CatalogRef.XXX`, `DocumentObject.XXX`, `ValueTable`, `DynamicList`, `Type1 | Type2` (составной).
|
||||||
|
|
||||||
|
## Вывод
|
||||||
|
|
||||||
|
```
|
||||||
|
=== form-add: Форма ===
|
||||||
|
|
||||||
|
Added elements (into ГруппаШапка, after Контрагент):
|
||||||
|
+ [Input] Склад -> Объект.Склад {OnChange}
|
||||||
|
|
||||||
|
Added attributes:
|
||||||
|
+ СуммаИтого: decimal(15,2) (id=12)
|
||||||
|
|
||||||
|
---
|
||||||
|
Total: 1 element(s) (+2 companions), 1 attribute(s)
|
||||||
|
Run /form-validate to verify.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Когда использовать
|
||||||
|
|
||||||
|
- **После `/form-compile`**: добавить элементы, которые не были в исходном JSON
|
||||||
|
- **Модификация существующих форм**: добавить поле, реквизит или команду в форму из конфигурации
|
||||||
|
- **Пакетное добавление**: один JSON может содержать элементы + реквизиты + команды
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. `/form-info` — посмотреть текущую структуру формы
|
||||||
|
2. Создать JSON с описанием добавлений
|
||||||
|
3. `/form-add` — добавить в форму
|
||||||
|
4. `/form-validate` — проверить корректность
|
||||||
|
5. `/form-info` — убедиться что добавилось правильно
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,409 @@
|
|||||||
|
---
|
||||||
|
name: form-compile
|
||||||
|
description: Компиляция управляемой формы 1С (Form.xml) из компактного JSON-определения
|
||||||
|
argument-hint: <JsonPath> <OutputPath>
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Write
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /form-compile — Генерация Form.xml из JSON DSL
|
||||||
|
|
||||||
|
Принимает компактное JSON-определение формы (20–50 строк) и генерирует полный корректный Form.xml (100–500+ строк) с namespace-декларациями, автогенерированными companion-элементами, последовательными ID.
|
||||||
|
|
||||||
|
> **При проектировании формы с нуля (5+ элементов или нечёткие требования)** — вызовите `/form-patterns` для загрузки справочника: архетипы, конвенции именования, продвинутые паттерны. Для простых форм (1–3 поля, пользователь описал что нужно) — не нужно.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
```
|
||||||
|
/form-compile <JsonPath> <OutputPath>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Параметры
|
||||||
|
|
||||||
|
| Параметр | Обязательный | Описание |
|
||||||
|
|------------|:------------:|-----------------------------------|
|
||||||
|
| JsonPath | да | Путь к JSON-определению формы |
|
||||||
|
| OutputPath | да | Путь к выходному файлу Form.xml |
|
||||||
|
|
||||||
|
## Команда
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\form-compile\scripts\form-compile.ps1 -JsonPath "<json>" -OutputPath "<xml>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON DSL — справка
|
||||||
|
|
||||||
|
### Структура верхнего уровня
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Заголовок формы",
|
||||||
|
"properties": { "autoTitle": false, ... },
|
||||||
|
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
|
||||||
|
"excludedCommands": ["Reread"],
|
||||||
|
"elements": [ ... ],
|
||||||
|
"attributes": [ ... ],
|
||||||
|
"commands": [ ... ],
|
||||||
|
"parameters": [ ... ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `title` — заголовок формы (multilingual). Можно указать и в `properties`, но лучше на верхнем уровне
|
||||||
|
- `properties` — свойства формы: `autoTitle`, `windowOpeningMode`, `commandBarLocation`, `saveDataInSettings`, `width`, `height` и др.
|
||||||
|
- `events` — обработчики событий формы (ключ: имя события 1С, значение: имя процедуры)
|
||||||
|
- `excludedCommands` — исключённые стандартные команды
|
||||||
|
|
||||||
|
### Элементы (ключ определяет тип)
|
||||||
|
|
||||||
|
| DSL ключ | XML элемент | Значение ключа |
|
||||||
|
|--------------|-------------------|---------------------------------------------------|
|
||||||
|
| `"group"` | UsualGroup | `"horizontal"` / `"vertical"` / `"alwaysHorizontal"` / `"alwaysVertical"` / `"collapsible"` |
|
||||||
|
| `"input"` | InputField | имя элемента |
|
||||||
|
| `"check"` | CheckBoxField | имя |
|
||||||
|
| `"label"` | LabelDecoration | имя (текст задаётся через `title`) |
|
||||||
|
| `"labelField"` | LabelField | имя |
|
||||||
|
| `"table"` | Table | имя |
|
||||||
|
| `"pages"` | Pages | имя |
|
||||||
|
| `"page"` | Page | имя |
|
||||||
|
| `"button"` | Button | имя |
|
||||||
|
| `"picture"` | PictureDecoration | имя |
|
||||||
|
| `"picField"` | PictureField | имя |
|
||||||
|
| `"calendar"` | CalendarField | имя |
|
||||||
|
| `"cmdBar"` | CommandBar | имя |
|
||||||
|
| `"popup"` | Popup | имя |
|
||||||
|
|
||||||
|
### Общие свойства (все типы элементов)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `name` | Переопределить имя (по умолчанию = значение ключа типа) |
|
||||||
|
| `title` | Заголовок элемента |
|
||||||
|
| `visible: false` | Скрыть (синоним: `hidden: true`) |
|
||||||
|
| `enabled: false` | Сделать недоступным (синоним: `disabled: true`) |
|
||||||
|
| `readOnly: true` | Только чтение |
|
||||||
|
| `on: [...]` | События с автоименованием обработчиков |
|
||||||
|
| `handlers: {...}` | Явное задание имён обработчиков: `{"OnChange": "МоёИмя"}` |
|
||||||
|
|
||||||
|
### Допустимые имена событий (`on`)
|
||||||
|
|
||||||
|
Компилятор предупреждает о неизвестных событиях. Имена регистрозависимы — используйте точно как указано.
|
||||||
|
|
||||||
|
**Форма** (`events`): `OnCreateAtServer`, `OnOpen`, `BeforeClose`, `OnClose`, `NotificationProcessing`, `ChoiceProcessing`, `OnReadAtServer`, `BeforeWriteAtServer`, `OnWriteAtServer`, `AfterWriteAtServer`, `BeforeWrite`, `AfterWrite`, `FillCheckProcessingAtServer`, `BeforeLoadDataFromSettingsAtServer`, `OnLoadDataFromSettingsAtServer`, `ExternalEvent`, `Opening`
|
||||||
|
|
||||||
|
**input / picField**: `OnChange`, `StartChoice`, `ChoiceProcessing`, `AutoComplete`, `TextEditEnd`, `Clearing`, `Creating`, `EditTextChange`
|
||||||
|
|
||||||
|
**check**: `OnChange`
|
||||||
|
|
||||||
|
**table**: `OnStartEdit`, `OnEditEnd`, `OnChange`, `Selection`, `ValueChoice`, `BeforeAddRow`, `BeforeDeleteRow`, `AfterDeleteRow`, `BeforeRowChange`, `BeforeEditEnd`, `OnActivateRow`, `OnActivateCell`, `Drag`, `DragStart`, `DragCheck`, `DragEnd`
|
||||||
|
|
||||||
|
**label / picture**: `Click`, `URLProcessing`
|
||||||
|
|
||||||
|
**labelField**: `OnChange`, `StartChoice`, `ChoiceProcessing`, `Click`, `URLProcessing`, `Clearing`
|
||||||
|
|
||||||
|
**button**: `Click`
|
||||||
|
|
||||||
|
**pages**: `OnCurrentPageChange`
|
||||||
|
|
||||||
|
### Поле ввода (input)
|
||||||
|
|
||||||
|
| Ключ | Описание | Пример |
|
||||||
|
|------|----------|--------|
|
||||||
|
| `path` | DataPath — привязка к данным | `"Объект.Организация"` |
|
||||||
|
| `titleLocation` | Размещение заголовка | `"none"`, `"left"`, `"top"` |
|
||||||
|
| `multiLine: true` | Многострочное поле | текстовое поле, комментарий |
|
||||||
|
| `passwordMode: true` | Режим пароля (звёздочки) | поле ввода пароля |
|
||||||
|
| `choiceButton: true` | Кнопка выбора ("...") | ссылочное поле |
|
||||||
|
| `clearButton: true` | Кнопка очистки ("X") | |
|
||||||
|
| `spinButton: true` | Кнопка прокрутки | числовые поля |
|
||||||
|
| `dropListButton: true` | Кнопка выпадающего списка | |
|
||||||
|
| `markIncomplete: true` | Пометка незаполненного | обязательные поля |
|
||||||
|
| `skipOnInput: true` | Пропускать при обходе Tab | |
|
||||||
|
| `inputHint` | Подсказка в пустом поле | `"Введите наименование..."` |
|
||||||
|
| `width` / `height` | Размер | числа |
|
||||||
|
| `autoMaxWidth: false` | Отключить авто-ширину | для фиксированных полей |
|
||||||
|
| `horizontalStretch: true` | Растягивать по ширине | |
|
||||||
|
|
||||||
|
### Чекбокс (check)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `path` | DataPath |
|
||||||
|
| `titleLocation` | Размещение заголовка |
|
||||||
|
|
||||||
|
### Надпись-декорация (label)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `title` | Текст надписи (обязательно) |
|
||||||
|
| `hyperlink: true` | Сделать ссылкой |
|
||||||
|
| `width` / `height` | Размер |
|
||||||
|
|
||||||
|
### Группа (group)
|
||||||
|
|
||||||
|
Значение ключа задаёт ориентацию: `"horizontal"`, `"vertical"`, `"alwaysHorizontal"`, `"alwaysVertical"`, `"collapsible"`.
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `showTitle: true` | Показывать заголовок группы |
|
||||||
|
| `united: false` | Не объединять рамку |
|
||||||
|
| `representation` | `"none"`, `"normal"`, `"weak"`, `"strong"` |
|
||||||
|
| `children: [...]` | Вложенные элементы |
|
||||||
|
|
||||||
|
### Таблица (table)
|
||||||
|
|
||||||
|
**Важно**: таблица требует связанный реквизит формы типа `ValueTable` с колонками (см. раздел "Связки").
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `path` | DataPath (привязка к реквизиту-таблице) |
|
||||||
|
| `columns: [...]` | Колонки — массив элементов (обычно `input`) |
|
||||||
|
| `changeRowSet: true` | Разрешить добавление/удаление строк |
|
||||||
|
| `changeRowOrder: true` | Разрешить перемещение строк |
|
||||||
|
| `height` | Высота в строках таблицы |
|
||||||
|
| `header: false` | Скрыть шапку |
|
||||||
|
| `footer: true` | Показать подвал |
|
||||||
|
| `commandBarLocation` | `"None"`, `"Top"`, `"Auto"` |
|
||||||
|
| `searchStringLocation` | `"None"`, `"Top"`, `"Auto"` |
|
||||||
|
|
||||||
|
### Страницы (pages + page)
|
||||||
|
|
||||||
|
| Ключ (pages) | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `pagesRepresentation` | `"None"`, `"TabsOnTop"`, `"TabsOnBottom"` и др. |
|
||||||
|
| `children: [...]` | Массив `page` |
|
||||||
|
|
||||||
|
| Ключ (page) | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `title` | Заголовок вкладки |
|
||||||
|
| `group` | Ориентация внутри страницы |
|
||||||
|
| `children: [...]` | Содержимое страницы |
|
||||||
|
|
||||||
|
### Кнопка (button)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `command` | Имя команды формы → `Form.Command.Имя` |
|
||||||
|
| `stdCommand` | Стандартная команда: `"Close"` → `Form.StandardCommand.Close`; с точкой: `"Товары.Add"` → `Form.Item.Товары.StandardCommand.Add` |
|
||||||
|
| `defaultButton: true` | Кнопка по умолчанию |
|
||||||
|
| `type` | `"usual"`, `"hyperlink"`, `"commandBar"` |
|
||||||
|
| `picture` | Картинка кнопки |
|
||||||
|
| `representation` | `"Auto"`, `"Text"`, `"Picture"`, `"PictureAndText"` |
|
||||||
|
| `locationInCommandBar` | `"Auto"`, `"InCommandBar"`, `"InAdditionalSubmenu"` |
|
||||||
|
|
||||||
|
### Командная панель (cmdBar)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `autofill: true` | Автозаполнение стандартными командами |
|
||||||
|
| `children: [...]` | Кнопки панели |
|
||||||
|
|
||||||
|
### Выпадающее меню (popup)
|
||||||
|
|
||||||
|
| Ключ | Описание |
|
||||||
|
|------|----------|
|
||||||
|
| `title` | Заголовок подменю |
|
||||||
|
| `children: [...]` | Кнопки подменю |
|
||||||
|
|
||||||
|
Используется внутри `cmdBar` для группировки кнопок в подменю:
|
||||||
|
```json
|
||||||
|
{ "cmdBar": "Панель", "children": [
|
||||||
|
{ "popup": "Добавить", "title": "Добавить", "children": [
|
||||||
|
{ "button": "ДобавитьСтроку", "stdCommand": "Товары.Add" },
|
||||||
|
{ "button": "ДобавитьИзДокумента", "command": "ДобавитьИзДокумента", "title": "Из документа" }
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Реквизиты (attributes)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "name": "Объект", "type": "DataProcessorObject.Загрузка", "main": true }
|
||||||
|
{ "name": "Итого", "type": "decimal(15,2)" }
|
||||||
|
{ "name": "Таблица", "type": "ValueTable", "columns": [
|
||||||
|
{ "name": "Номенклатура", "type": "CatalogRef.Номенклатура" },
|
||||||
|
{ "name": "Количество", "type": "decimal(10,3)" }
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `savedData: true` — сохраняемые данные
|
||||||
|
|
||||||
|
### Команды (commands)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "name": "Загрузить", "action": "ЗагрузитьОбработка", "shortcut": "Ctrl+Enter" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- `title` — заголовок (если отличается от name)
|
||||||
|
- `picture` — картинка команды
|
||||||
|
|
||||||
|
### Система типов
|
||||||
|
|
||||||
|
| DSL | XML |
|
||||||
|
|------------------------|----------------------------------------|
|
||||||
|
| `"string"` / `"string(100)"` | `xs:string` + StringQualifiers |
|
||||||
|
| `"decimal(15,2)"` | `xs:decimal` + NumberQualifiers |
|
||||||
|
| `"decimal(10,0,nonneg)"` | с AllowedSign=Nonnegative |
|
||||||
|
| `"boolean"` | `xs:boolean` |
|
||||||
|
| `"date"` / `"dateTime"` / `"time"` | `xs:dateTime` + DateFractions |
|
||||||
|
| `"CatalogRef.XXX"` | `cfg:CatalogRef.XXX` |
|
||||||
|
| `"DocumentRef.XXX"` | `cfg:DocumentRef.XXX` |
|
||||||
|
| `"ValueTable"` | `v8:ValueTable` |
|
||||||
|
| `"ValueList"` | `v8:ValueListType` |
|
||||||
|
| `"Type1 \| Type2"` | составной тип |
|
||||||
|
|
||||||
|
## Связки: элемент + реквизит
|
||||||
|
|
||||||
|
Таблица и некоторые поля требуют связанный реквизит. Элемент ссылается на реквизит через `path`.
|
||||||
|
|
||||||
|
**Таблица** — элемент `table` + реквизит `ValueTable`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"elements": [
|
||||||
|
{ "table": "Товары", "path": "Объект.Товары", "columns": [
|
||||||
|
{ "input": "Номенклатура", "path": "Объект.Товары.Номенклатура" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "DataProcessorObject.Загрузка", "main": true,
|
||||||
|
"columns": [
|
||||||
|
{ "name": "Товары", "type": "ValueTable", "columns": [
|
||||||
|
{ "name": "Номенклатура", "type": "CatalogRef.Номенклатура" }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Или, если таблица привязана к реквизиту формы (не к Объект):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"elements": [
|
||||||
|
{ "table": "ТаблицаДанных", "path": "ТаблицаДанных", "columns": [
|
||||||
|
{ "input": "Наименование", "path": "ТаблицаДанных.Наименование" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "ТаблицаДанных", "type": "ValueTable", "columns": [
|
||||||
|
{ "name": "Наименование", "type": "string(150)" }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Паттерны
|
||||||
|
|
||||||
|
### Диалог загрузки файла
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Загрузка из файла",
|
||||||
|
"properties": { "autoTitle": false },
|
||||||
|
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
|
||||||
|
"elements": [
|
||||||
|
{ "group": "horizontal", "name": "ГруппаФайл", "children": [
|
||||||
|
{ "input": "ИмяФайла", "path": "ИмяФайла", "title": "Файл", "inputHint": "Выберите файл...", "choiceButton": true, "on": ["StartChoice"] },
|
||||||
|
{ "check": "ПерваяСтрокаЗаголовок", "path": "ПерваяСтрокаЗаголовок" }
|
||||||
|
]},
|
||||||
|
{ "input": "Результат", "path": "Результат", "multiLine": true, "height": 8, "readOnly": true, "title": "Лог" },
|
||||||
|
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
|
||||||
|
{ "button": "Загрузить", "command": "Загрузить", "defaultButton": true },
|
||||||
|
{ "button": "Закрыть", "stdCommand": "Close" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "ExternalDataProcessorObject.ЗагрузкаИзФайла", "main": true },
|
||||||
|
{ "name": "ИмяФайла", "type": "string" },
|
||||||
|
{ "name": "ПерваяСтрокаЗаголовок", "type": "boolean" },
|
||||||
|
{ "name": "Результат", "type": "string" }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Загрузить", "action": "ЗагрузитьОбработка", "shortcut": "Ctrl+Enter" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Мастер (wizard) с шагами
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Мастер настройки",
|
||||||
|
"properties": { "autoTitle": false },
|
||||||
|
"elements": [
|
||||||
|
{ "pages": "СтраницыМастера", "pagesRepresentation": "None", "children": [
|
||||||
|
{ "page": "Шаг1", "title": "Параметры", "children": [
|
||||||
|
{ "input": "Параметр1", "path": "Параметр1" }
|
||||||
|
]},
|
||||||
|
{ "page": "Шаг2", "title": "Результат", "children": [
|
||||||
|
{ "input": "Итог", "path": "Итог", "readOnly": true }
|
||||||
|
]}
|
||||||
|
]},
|
||||||
|
{ "group": "horizontal", "name": "Навигация", "children": [
|
||||||
|
{ "button": "Назад", "command": "Назад", "title": "< Назад" },
|
||||||
|
{ "button": "Далее", "command": "Далее", "title": "Далее >" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "ExternalDataProcessorObject.Мастер", "main": true },
|
||||||
|
{ "name": "Параметр1", "type": "string" },
|
||||||
|
{ "name": "Итог", "type": "string" }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Назад", "action": "НазадОбработка" },
|
||||||
|
{ "name": "Далее", "action": "ДалееОбработка" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Список с фильтром и таблицей
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Просмотр данных",
|
||||||
|
"elements": [
|
||||||
|
{ "group": "horizontal", "name": "Фильтр", "children": [
|
||||||
|
{ "input": "Период", "path": "Период", "on": ["OnChange"] },
|
||||||
|
{ "input": "Организация", "path": "Организация", "on": ["OnChange"] }
|
||||||
|
]},
|
||||||
|
{ "table": "Данные", "path": "Данные", "changeRowSet": true, "columns": [
|
||||||
|
{ "input": "Дата", "path": "Данные.Дата" },
|
||||||
|
{ "input": "Сумма", "path": "Данные.Сумма" },
|
||||||
|
{ "input": "Комментарий", "path": "Данные.Комментарий" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "ExternalDataProcessorObject.Просмотр", "main": true },
|
||||||
|
{ "name": "Период", "type": "date" },
|
||||||
|
{ "name": "Организация", "type": "string" },
|
||||||
|
{ "name": "Данные", "type": "ValueTable", "columns": [
|
||||||
|
{ "name": "Дата", "type": "date" },
|
||||||
|
{ "name": "Сумма", "type": "decimal(15,2)" },
|
||||||
|
{ "name": "Комментарий", "type": "string(200)" }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Автогенерация
|
||||||
|
|
||||||
|
- **Companion-элементы**: ContextMenu, ExtendedTooltip и др. создаются автоматически
|
||||||
|
- **Обработчики событий**: `"on": ["OnChange"]` → `ОрганизацияПриИзменении`
|
||||||
|
- **Namespace**: все 17 namespace-деклараций
|
||||||
|
- **ID**: последовательная нумерация, AutoCommandBar = id="-1"
|
||||||
|
- **Unknown keys**: выводится предупреждение о нераспознанных ключах
|
||||||
|
|
||||||
|
## Верификация
|
||||||
|
|
||||||
|
```
|
||||||
|
/form-validate <OutputPath> — проверка корректности XML
|
||||||
|
/form-info <OutputPath> — визуальная сводка структуры
|
||||||
|
```
|
||||||
|
|
||||||
|
## Особенности для внешних обработок (EPF)
|
||||||
|
|
||||||
|
- **Тип главного реквизита**: `ExternalDataProcessorObject.ИмяОбработки` (не `DataProcessorObject`)
|
||||||
|
- **DataPath**: используйте реквизиты формы (`ИмяРеквизита`), а не `Объект.ИмяРеквизита` — у внешних обработок нет реквизитов объекта в метаданных
|
||||||
|
- **Ссылочные типы**: `CatalogRef.XXX`, `DocumentRef.XXX` и т.д. могут не собраться в пустой базе — используйте `string` или базовые типы для автономной сборки
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
|||||||
|
---
|
||||||
|
name: form-info
|
||||||
|
description: Анализ структуры управляемой формы 1С (Form.xml) — элементы, реквизиты, команды, события
|
||||||
|
argument-hint: <FormPath>
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /form-info — Компактная сводка формы
|
||||||
|
|
||||||
|
Читает Form.xml управляемой формы и выводит компактную сводку: дерево элементов, реквизиты с типами, команды, события. Заменяет необходимость читать тысячи строк XML.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
```
|
||||||
|
/form-info <FormPath>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Параметры
|
||||||
|
|
||||||
|
| Параметр | Обязательный | По умолчанию | Описание |
|
||||||
|
|-----------|:------------:|--------------|---------------------------------------------|
|
||||||
|
| FormPath | да | — | Путь к файлу Form.xml |
|
||||||
|
| Limit | нет | `150` | Макс. строк вывода (защита от переполнения) |
|
||||||
|
| Offset | нет | `0` | Пропустить N строк (для пагинации) |
|
||||||
|
|
||||||
|
## Команда
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\form-info\scripts\form-info.ps1 -FormPath "<путь к Form.xml>"
|
||||||
|
```
|
||||||
|
|
||||||
|
С пагинацией:
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\form-info\scripts\form-info.ps1 -FormPath "<путь>" -Offset 150
|
||||||
|
```
|
||||||
|
|
||||||
|
## Чтение вывода
|
||||||
|
|
||||||
|
### Заголовок
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Form: ФормаДокумента — "Реализация товаров и услуг" (Documents.РеализацияТоваровУслуг) ===
|
||||||
|
```
|
||||||
|
|
||||||
|
Имя формы, заголовок (Title) и контекст объекта определяются из пути к файлу и XML.
|
||||||
|
|
||||||
|
### Properties — свойства формы
|
||||||
|
|
||||||
|
Только нестандартные свойства (отличающиеся от умолчания). Title показывается в заголовке, не здесь:
|
||||||
|
|
||||||
|
```
|
||||||
|
Properties: AutoTitle=false, WindowOpeningMode=LockOwnerWindow, CommandBarLocation=Bottom
|
||||||
|
```
|
||||||
|
|
||||||
|
### Events — обработчики событий формы
|
||||||
|
|
||||||
|
```
|
||||||
|
Events:
|
||||||
|
OnCreateAtServer -> ПриСозданииНаСервере
|
||||||
|
OnOpen -> ПриОткрытии
|
||||||
|
```
|
||||||
|
|
||||||
|
### Elements — дерево UI-элементов
|
||||||
|
|
||||||
|
Компактное дерево с типами, привязками к данным, флагами и событиями:
|
||||||
|
|
||||||
|
```
|
||||||
|
Elements:
|
||||||
|
├─ [Group:AH] ГруппаШапка
|
||||||
|
│ ├─ [Input] Организация -> Объект.Организация {OnChange}
|
||||||
|
│ └─ [Input] Договор -> Объект.Договор [visible:false] {StartChoice}
|
||||||
|
├─ [Table] Товары -> Объект.Товары
|
||||||
|
│ ├─ [Input] Номенклатура -> Объект.Товары.Номенклатура {OnChange}
|
||||||
|
│ └─ [Input] Сумма -> Объект.Товары.Сумма [ro]
|
||||||
|
└─ [Pages] Страницы
|
||||||
|
├─ [Page] Основное (5 items)
|
||||||
|
└─ [Page] Печать (2 items)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Сокращения типов элементов:**
|
||||||
|
|
||||||
|
| Сокращение | Элемент |
|
||||||
|
|---|---|
|
||||||
|
| `[Group:V]` | UsualGroup Vertical |
|
||||||
|
| `[Group:H]` | UsualGroup Horizontal |
|
||||||
|
| `[Group:AH]` | UsualGroup AlwaysHorizontal |
|
||||||
|
| `[Group:AV]` | UsualGroup AlwaysVertical |
|
||||||
|
| `[Group]` | UsualGroup (ориентация по умолчанию) |
|
||||||
|
| `[Input]` | InputField |
|
||||||
|
| `[Check]` | CheckBoxField |
|
||||||
|
| `[Label]` | LabelDecoration |
|
||||||
|
| `[LabelField]` | LabelField |
|
||||||
|
| `[Picture]` | PictureDecoration |
|
||||||
|
| `[PicField]` | PictureField |
|
||||||
|
| `[Calendar]` | CalendarField |
|
||||||
|
| `[Table]` | Table |
|
||||||
|
| `[Button]` | Button |
|
||||||
|
| `[CmdBar]` | CommandBar |
|
||||||
|
| `[Pages]` | Pages |
|
||||||
|
| `[Page]` | Page (показывает кол-во элементов вместо раскрытия) |
|
||||||
|
| `[Popup]` | Popup |
|
||||||
|
| `[BtnGroup]` | ButtonGroup |
|
||||||
|
|
||||||
|
**Флаги** (только при отклонении от умолчания):
|
||||||
|
- `[visible:false]` — элемент скрыт (Visible=false)
|
||||||
|
- `[enabled:false]` — элемент недоступен (Enabled=false)
|
||||||
|
- `[ro]` — ReadOnly=true
|
||||||
|
- `,collapse` — Behavior=Collapsible (для групп)
|
||||||
|
|
||||||
|
**Привязка к данным**: `-> Объект.Поле` — DataPath
|
||||||
|
|
||||||
|
**Привязка к команде**: `-> ИмяКоманды [cmd]` — команда формы, `-> Close [std]` — стандартная команда
|
||||||
|
|
||||||
|
**События**: `{OnChange, StartChoice}` — имена обработчиков
|
||||||
|
|
||||||
|
**Заголовок**: `[title:Текст]` — только если отличается от имени элемента
|
||||||
|
|
||||||
|
### Attributes — реквизиты формы
|
||||||
|
|
||||||
|
```
|
||||||
|
Attributes:
|
||||||
|
*Объект: DocumentObject.РеализацияТоваров (main)
|
||||||
|
Валюта: CatalogRef.Валюты
|
||||||
|
Итого: decimal(15,2)
|
||||||
|
Таблица: ValueTable [Номенклатура: CatalogRef.Номенклатура, Кол: decimal(10,3)]
|
||||||
|
Список: DynamicList -> Catalog.Пользователи
|
||||||
|
```
|
||||||
|
|
||||||
|
- `*` и `(main)` — основной реквизит формы (MainAttribute)
|
||||||
|
- Типы ValueTable/ValueTree раскрывают колонки в `[...]`
|
||||||
|
- DynamicList показывает MainTable через `->`
|
||||||
|
|
||||||
|
### Parameters — параметры формы
|
||||||
|
|
||||||
|
```
|
||||||
|
Parameters:
|
||||||
|
Ключ: DocumentRef.ЗакупкаТоваров (key)
|
||||||
|
Основание: DocumentRef.*
|
||||||
|
```
|
||||||
|
|
||||||
|
- `(key)` — ключевой параметр (KeyParameter)
|
||||||
|
|
||||||
|
### Commands — команды формы
|
||||||
|
|
||||||
|
```
|
||||||
|
Commands:
|
||||||
|
Печать -> ПечатьДокумента [Ctrl+P]
|
||||||
|
Заполнить -> ЗаполнитьОбработка
|
||||||
|
```
|
||||||
|
|
||||||
|
Формат: `Имя -> Обработчик [Сочетание]`
|
||||||
|
|
||||||
|
## Что пропускается
|
||||||
|
|
||||||
|
Скрипт убирает 80%+ XML-объёма:
|
||||||
|
- Визуальные свойства (Width, Height, Color, Font, Border, Align, Stretch)
|
||||||
|
- Автогенерированные ExtendedTooltip и ContextMenu
|
||||||
|
- Мультиязычные обёртки (v8:item/v8:lang/v8:content)
|
||||||
|
- Namespace-декларации
|
||||||
|
- Атрибуты id
|
||||||
|
|
||||||
|
Для точечного изучения деталей — используйте grep по имени элемента из сводки.
|
||||||
|
|
||||||
|
## Когда использовать
|
||||||
|
|
||||||
|
- **Перед модификацией формы**: понять структуру, найти нужную группу для вставки элемента
|
||||||
|
- **Анализ формы**: какие реквизиты, команды, обработчики задействованы
|
||||||
|
- **Навигация по большим формам**: 28K строк XML → 50-100 строк контекста
|
||||||
|
|
||||||
|
## Защита от переполнения
|
||||||
|
|
||||||
|
Вывод ограничен 150 строками по умолчанию. При превышении:
|
||||||
|
```
|
||||||
|
[TRUNCATED] Shown 150 of 220 lines. Use -Offset 150 to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
Используйте `-Offset N` и `-Limit N` для постраничного просмотра.
|
||||||
@@ -0,0 +1,527 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$FormPath,
|
||||||
|
[int]$Limit = 150,
|
||||||
|
[int]$Offset = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
# --- Validate path ---
|
||||||
|
|
||||||
|
if (-not (Test-Path $FormPath)) {
|
||||||
|
Write-Error "File not found: $FormPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Load XML ---
|
||||||
|
|
||||||
|
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||||
|
$xmlDoc.PreserveWhitespace = $false
|
||||||
|
$xmlDoc.Load((Resolve-Path $FormPath).Path)
|
||||||
|
|
||||||
|
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||||
|
$ns.AddNamespace("d", "http://v8.1c.ru/8.3/xcf/logform")
|
||||||
|
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||||
|
$ns.AddNamespace("v8ui", "http://v8.1c.ru/8.1/data/ui")
|
||||||
|
$ns.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
|
||||||
|
$ns.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema")
|
||||||
|
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
$ns.AddNamespace("cfg", "http://v8.1c.ru/8.1/data/enterprise/current-config")
|
||||||
|
$ns.AddNamespace("dcsset", "http://v8.1c.ru/8.1/data-composition-system/settings")
|
||||||
|
|
||||||
|
$root = $xmlDoc.DocumentElement
|
||||||
|
|
||||||
|
# --- Helper: extract multilang text ---
|
||||||
|
|
||||||
|
function Get-MLText($node) {
|
||||||
|
if (-not $node) { return "" }
|
||||||
|
$content = $node.SelectSingleNode("v8:item/v8:content", $ns)
|
||||||
|
if ($content) { return $content.InnerText }
|
||||||
|
$text = $node.InnerText.Trim()
|
||||||
|
if ($text) { return $text }
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper: format type compactly ---
|
||||||
|
|
||||||
|
function Format-Type($typeNode) {
|
||||||
|
if (-not $typeNode -or -not $typeNode.HasChildNodes) { return "" }
|
||||||
|
|
||||||
|
$typeSet = $typeNode.SelectSingleNode("v8:TypeSet", $ns)
|
||||||
|
if ($typeSet) {
|
||||||
|
$val = $typeSet.InnerText
|
||||||
|
# Strip cfg: prefix for DefinedType, keep as-is
|
||||||
|
if ($val -like "cfg:*") { $val = $val.Substring(4) }
|
||||||
|
return $val
|
||||||
|
}
|
||||||
|
|
||||||
|
$types = $typeNode.SelectNodes("v8:Type", $ns)
|
||||||
|
if ($types.Count -eq 0) { return "" }
|
||||||
|
|
||||||
|
$parts = @()
|
||||||
|
foreach ($t in $types) {
|
||||||
|
$raw = $t.InnerText
|
||||||
|
switch -Wildcard ($raw) {
|
||||||
|
"xs:string" {
|
||||||
|
$sq = $typeNode.SelectSingleNode("v8:StringQualifiers/v8:Length", $ns)
|
||||||
|
$len = if ($sq) { [int]$sq.InnerText } else { 0 }
|
||||||
|
if ($len -gt 0) { $parts += "string($len)" } else { $parts += "string" }
|
||||||
|
}
|
||||||
|
"xs:decimal" {
|
||||||
|
$nq = $typeNode.SelectSingleNode("v8:NumberQualifiers", $ns)
|
||||||
|
if ($nq) {
|
||||||
|
$d = $nq.SelectSingleNode("v8:Digits", $ns)
|
||||||
|
$f = $nq.SelectSingleNode("v8:FractionDigits", $ns)
|
||||||
|
$digits = if ($d) { $d.InnerText } else { "0" }
|
||||||
|
$frac = if ($f) { $f.InnerText } else { "0" }
|
||||||
|
$parts += "decimal($digits,$frac)"
|
||||||
|
} else {
|
||||||
|
$parts += "decimal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"xs:boolean" { $parts += "boolean" }
|
||||||
|
"xs:dateTime" {
|
||||||
|
$dq = $typeNode.SelectSingleNode("v8:DateQualifiers/v8:DateFractions", $ns)
|
||||||
|
if ($dq) {
|
||||||
|
switch ($dq.InnerText) {
|
||||||
|
"Date" { $parts += "date" }
|
||||||
|
"Time" { $parts += "time" }
|
||||||
|
default { $parts += "dateTime" }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$parts += "dateTime"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"xs:binary" { $parts += "binary" }
|
||||||
|
"cfg:*" { $parts += $raw.Substring(4) }
|
||||||
|
"v8:ValueTable" { $parts += "ValueTable" }
|
||||||
|
"v8:ValueTree" { $parts += "ValueTree" }
|
||||||
|
"v8:ValueListType" { $parts += "ValueList" }
|
||||||
|
"v8:TypeDescription" { $parts += "TypeDescription" }
|
||||||
|
"v8:Universal" { $parts += "Universal" }
|
||||||
|
"v8:FixedArray" { $parts += "FixedArray" }
|
||||||
|
"v8:FixedStructure" { $parts += "FixedStructure" }
|
||||||
|
"v8ui:FormattedString" { $parts += "FormattedString" }
|
||||||
|
"v8ui:Picture" { $parts += "Picture" }
|
||||||
|
"v8ui:Color" { $parts += "Color" }
|
||||||
|
"v8ui:Font" { $parts += "Font" }
|
||||||
|
"dcsset:*" { $parts += $raw.Replace("dcsset:", "DCS.") }
|
||||||
|
"dcssch:*" { $parts += $raw.Replace("dcssch:", "DCS.") }
|
||||||
|
"dcscor:*" { $parts += $raw.Replace("dcscor:", "DCS.") }
|
||||||
|
default { $parts += $raw }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($parts -join " | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper: check if title differs from name ---
|
||||||
|
|
||||||
|
function Test-TitleDiffers($node, [string]$name) {
|
||||||
|
$titleNode = $node.SelectSingleNode("d:Title", $ns)
|
||||||
|
if (-not $titleNode) { return $null }
|
||||||
|
$titleText = Get-MLText $titleNode
|
||||||
|
if (-not $titleText) { return $null }
|
||||||
|
# Normalize: remove spaces, lowercase
|
||||||
|
$normTitle = ($titleText -replace '\s', '').ToLower()
|
||||||
|
$normName = $name.ToLower()
|
||||||
|
if ($normTitle -eq $normName) { return $null }
|
||||||
|
return $titleText
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper: get events as compact string ---
|
||||||
|
|
||||||
|
function Get-EventsStr($node) {
|
||||||
|
$eventsNode = $node.SelectSingleNode("d:Events", $ns)
|
||||||
|
if (-not $eventsNode) { return "" }
|
||||||
|
$evts = @()
|
||||||
|
foreach ($e in $eventsNode.SelectNodes("d:Event", $ns)) {
|
||||||
|
$evts += $e.GetAttribute("name")
|
||||||
|
}
|
||||||
|
if ($evts.Count -eq 0) { return "" }
|
||||||
|
return " {$($evts -join ', ')}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper: get flags ---
|
||||||
|
|
||||||
|
function Get-Flags($node) {
|
||||||
|
$flags = @()
|
||||||
|
$vis = $node.SelectSingleNode("d:Visible", $ns)
|
||||||
|
if ($vis -and $vis.InnerText -eq "false") { $flags += "visible:false" }
|
||||||
|
$en = $node.SelectSingleNode("d:Enabled", $ns)
|
||||||
|
if ($en -and $en.InnerText -eq "false") { $flags += "enabled:false" }
|
||||||
|
$ro = $node.SelectSingleNode("d:ReadOnly", $ns)
|
||||||
|
if ($ro -and $ro.InnerText -eq "true") { $flags += "ro" }
|
||||||
|
if ($flags.Count -eq 0) { return "" }
|
||||||
|
return " [$($flags -join ',')]"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Element type abbreviations ---
|
||||||
|
|
||||||
|
$skipElements = @{
|
||||||
|
"ExtendedTooltip" = $true
|
||||||
|
"ContextMenu" = $true
|
||||||
|
"AutoCommandBar" = $true
|
||||||
|
"SearchStringAddition" = $true
|
||||||
|
"ViewStatusAddition" = $true
|
||||||
|
"SearchControlAddition" = $true
|
||||||
|
"ColumnGroup" = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-ElementTag($node) {
|
||||||
|
$localName = $node.LocalName
|
||||||
|
switch ($localName) {
|
||||||
|
"UsualGroup" {
|
||||||
|
$groupNode = $node.SelectSingleNode("d:Group", $ns)
|
||||||
|
$orient = ""
|
||||||
|
if ($groupNode) {
|
||||||
|
switch ($groupNode.InnerText) {
|
||||||
|
"Vertical" { $orient = ":V" }
|
||||||
|
"Horizontal" { $orient = ":H" }
|
||||||
|
"AlwaysHorizontal" { $orient = ":AH" }
|
||||||
|
"AlwaysVertical" { $orient = ":AV" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$beh = $node.SelectSingleNode("d:Behavior", $ns)
|
||||||
|
$collapse = ""
|
||||||
|
if ($beh -and $beh.InnerText -eq "Collapsible") { $collapse = ",collapse" }
|
||||||
|
return "[Group$orient$collapse]"
|
||||||
|
}
|
||||||
|
"InputField" { return "[Input]" }
|
||||||
|
"CheckBoxField" { return "[Check]" }
|
||||||
|
"LabelDecoration" { return "[Label]" }
|
||||||
|
"LabelField" { return "[LabelField]" }
|
||||||
|
"PictureDecoration" { return "[Picture]" }
|
||||||
|
"PictureField" { return "[PicField]" }
|
||||||
|
"CalendarField" { return "[Calendar]" }
|
||||||
|
"Table" { return "[Table]" }
|
||||||
|
"Button" { return "[Button]" }
|
||||||
|
"CommandBar" { return "[CmdBar]" }
|
||||||
|
"Pages" { return "[Pages]" }
|
||||||
|
"Page" { return "[Page]" }
|
||||||
|
"Popup" { return "[Popup]" }
|
||||||
|
"ButtonGroup" { return "[BtnGroup]" }
|
||||||
|
default { return "[$localName]" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Count significant children (for Page summary) ---
|
||||||
|
|
||||||
|
function Count-SignificantChildren($childItemsNode) {
|
||||||
|
if (-not $childItemsNode) { return 0 }
|
||||||
|
$count = 0
|
||||||
|
foreach ($child in $childItemsNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne "Element") { continue }
|
||||||
|
if ($skipElements.ContainsKey($child.LocalName)) { continue }
|
||||||
|
$count++
|
||||||
|
}
|
||||||
|
return $count
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Build element tree recursively ---
|
||||||
|
|
||||||
|
$treeLines = [System.Collections.Generic.List[string]]::new()
|
||||||
|
|
||||||
|
function Build-Tree($childItemsNode, [string]$prefix, [bool]$isLast) {
|
||||||
|
if (-not $childItemsNode) { return }
|
||||||
|
|
||||||
|
# Collect significant children
|
||||||
|
$children = @()
|
||||||
|
foreach ($child in $childItemsNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne "Element") { continue }
|
||||||
|
if ($skipElements.ContainsKey($child.LocalName)) { continue }
|
||||||
|
$children += $child
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0; $i -lt $children.Count; $i++) {
|
||||||
|
$child = $children[$i]
|
||||||
|
$last = ($i -eq $children.Count - 1)
|
||||||
|
$connector = if ($last) { [char]0x2514 + [string][char]0x2500 } else { [char]0x251C + [string][char]0x2500 }
|
||||||
|
$continuation = if ($last) { " " } else { [string][char]0x2502 + " " }
|
||||||
|
|
||||||
|
$tag = Get-ElementTag $child
|
||||||
|
$name = $child.GetAttribute("name")
|
||||||
|
$flags = Get-Flags $child
|
||||||
|
$events = Get-EventsStr $child
|
||||||
|
|
||||||
|
# DataPath or CommandName
|
||||||
|
$binding = ""
|
||||||
|
$dp = $child.SelectSingleNode("d:DataPath", $ns)
|
||||||
|
if ($dp) {
|
||||||
|
$binding = " -> $($dp.InnerText)"
|
||||||
|
} else {
|
||||||
|
$cn = $child.SelectSingleNode("d:CommandName", $ns)
|
||||||
|
if ($cn) {
|
||||||
|
$cnVal = $cn.InnerText
|
||||||
|
if ($cnVal -match '^Form\.StandardCommand\.(.+)$') {
|
||||||
|
$binding = " -> $($Matches[1]) [std]"
|
||||||
|
} elseif ($cnVal -match '^Form\.Command\.(.+)$') {
|
||||||
|
$binding = " -> $($Matches[1]) [cmd]"
|
||||||
|
} else {
|
||||||
|
$binding = " -> $cnVal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Title differs?
|
||||||
|
$titleStr = ""
|
||||||
|
$diffTitle = Test-TitleDiffers $child $name
|
||||||
|
if ($diffTitle) { $titleStr = " [title:$diffTitle]" }
|
||||||
|
|
||||||
|
$line = "$prefix$connector $tag $name$binding$flags$titleStr$events"
|
||||||
|
$treeLines.Add($line)
|
||||||
|
|
||||||
|
# Recurse into containers (but not Page — show summary)
|
||||||
|
$localName = $child.LocalName
|
||||||
|
if ($localName -eq "Page") {
|
||||||
|
$ci = $child.SelectSingleNode("d:ChildItems", $ns)
|
||||||
|
$cnt = Count-SignificantChildren $ci
|
||||||
|
# Append count to last line
|
||||||
|
$idx = $treeLines.Count - 1
|
||||||
|
$treeLines[$idx] = $treeLines[$idx] + " ($cnt items)"
|
||||||
|
} elseif ($localName -in @("UsualGroup", "Pages", "Table", "CommandBar", "ButtonGroup", "Popup")) {
|
||||||
|
$ci = $child.SelectSingleNode("d:ChildItems", $ns)
|
||||||
|
if ($ci) {
|
||||||
|
Build-Tree $ci "$prefix$continuation" $last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Determine form name and object from path ---
|
||||||
|
|
||||||
|
$resolvedPath = (Resolve-Path $FormPath).Path
|
||||||
|
$parts = $resolvedPath -split '[/\\]'
|
||||||
|
|
||||||
|
$formName = ""
|
||||||
|
$objectContext = ""
|
||||||
|
|
||||||
|
# Look for /Forms/<FormName>/Ext/Form.xml pattern
|
||||||
|
$formsIdx = -1
|
||||||
|
for ($i = $parts.Count - 1; $i -ge 0; $i--) {
|
||||||
|
if ($parts[$i] -eq "Forms") { $formsIdx = $i; break }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($formsIdx -ge 0 -and ($formsIdx + 1) -lt $parts.Count) {
|
||||||
|
$formName = $parts[$formsIdx + 1]
|
||||||
|
# Object is 2 levels up: .../<ObjectType>/<ObjectName>/Forms/...
|
||||||
|
if ($formsIdx -ge 2) {
|
||||||
|
$objType = $parts[$formsIdx - 2]
|
||||||
|
$objName = $parts[$formsIdx - 1]
|
||||||
|
$objectContext = "$objType.$objName"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# CommonForms pattern: .../<ObjectType>/<FormName>/Ext/Form.xml
|
||||||
|
$extIdx = -1
|
||||||
|
for ($i = $parts.Count - 1; $i -ge 0; $i--) {
|
||||||
|
if ($parts[$i] -eq "Ext") { $extIdx = $i; break }
|
||||||
|
}
|
||||||
|
if ($extIdx -ge 2) {
|
||||||
|
$formName = $parts[$extIdx - 1]
|
||||||
|
$objType = $parts[$extIdx - 2]
|
||||||
|
$objectContext = $objType
|
||||||
|
} else {
|
||||||
|
$formName = [System.IO.Path]::GetFileNameWithoutExtension($FormPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Collect output ---
|
||||||
|
|
||||||
|
$lines = @()
|
||||||
|
|
||||||
|
# Header — include Title if present
|
||||||
|
$titleNode = $root.SelectSingleNode("d:Title", $ns)
|
||||||
|
$formTitle = $null
|
||||||
|
if ($titleNode) {
|
||||||
|
$formTitle = Get-MLText $titleNode
|
||||||
|
if (-not $formTitle) { $formTitle = $titleNode.InnerText }
|
||||||
|
}
|
||||||
|
$header = "=== Form: $formName"
|
||||||
|
if ($formTitle) { $header += " — `"$formTitle`"" }
|
||||||
|
if ($objectContext) { $header += " ($objectContext)" }
|
||||||
|
$header += " ==="
|
||||||
|
$lines += $header
|
||||||
|
|
||||||
|
# --- Form properties (Title excluded — shown in header) ---
|
||||||
|
|
||||||
|
$propNames = @(
|
||||||
|
"Width", "Height", "Group",
|
||||||
|
"WindowOpeningMode", "EnterKeyBehavior", "AutoTitle", "AutoURL",
|
||||||
|
"AutoFillCheck", "Customizable", "CommandBarLocation",
|
||||||
|
"SaveDataInSettings", "AutoSaveDataInSettings",
|
||||||
|
"AutoTime", "UsePostingMode", "RepostOnWrite",
|
||||||
|
"UseForFoldersAndItems",
|
||||||
|
"ReportResult", "DetailsData", "ReportFormType",
|
||||||
|
"VerticalScroll", "ScalingMode"
|
||||||
|
)
|
||||||
|
|
||||||
|
$props = @()
|
||||||
|
foreach ($pn in $propNames) {
|
||||||
|
$pNode = $root.SelectSingleNode("d:$pn", $ns)
|
||||||
|
if ($pNode) {
|
||||||
|
$val = Get-MLText $pNode
|
||||||
|
if (-not $val) { $val = $pNode.InnerText }
|
||||||
|
$props += "$pn=$val"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($props.Count -gt 0) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Properties: $($props -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Excluded commands ---
|
||||||
|
|
||||||
|
$excludedCmds = @()
|
||||||
|
foreach ($ec in $root.SelectNodes("d:CommandSet/d:ExcludedCommand", $ns)) {
|
||||||
|
$excludedCmds += $ec.InnerText
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Form events ---
|
||||||
|
|
||||||
|
$formEvents = $root.SelectSingleNode("d:Events", $ns)
|
||||||
|
if ($formEvents -and $formEvents.HasChildNodes) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Events:"
|
||||||
|
foreach ($e in $formEvents.SelectNodes("d:Event", $ns)) {
|
||||||
|
$eName = $e.GetAttribute("name")
|
||||||
|
$eHandler = $e.InnerText
|
||||||
|
$lines += " $eName -> $eHandler"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Element tree ---
|
||||||
|
|
||||||
|
$childItems = $root.SelectSingleNode("d:ChildItems", $ns)
|
||||||
|
if ($childItems) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Elements:"
|
||||||
|
Build-Tree $childItems " " $false
|
||||||
|
$lines += $treeLines.ToArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Attributes ---
|
||||||
|
|
||||||
|
$attrsNode = $root.SelectSingleNode("d:Attributes", $ns)
|
||||||
|
if ($attrsNode) {
|
||||||
|
$attrLines = @()
|
||||||
|
foreach ($attr in $attrsNode.SelectNodes("d:Attribute", $ns)) {
|
||||||
|
$aName = $attr.GetAttribute("name")
|
||||||
|
$typeNode = $attr.SelectSingleNode("d:Type", $ns)
|
||||||
|
$typeStr = Format-Type $typeNode
|
||||||
|
|
||||||
|
$mainAttr = $attr.SelectSingleNode("d:MainAttribute", $ns)
|
||||||
|
$isMain = ($mainAttr -and $mainAttr.InnerText -eq "true")
|
||||||
|
|
||||||
|
$prefix = if ($isMain) { "*" } else { " " }
|
||||||
|
$mainSuffix = if ($isMain) { " (main)" } else { "" }
|
||||||
|
|
||||||
|
# DynamicList: show MainTable
|
||||||
|
$settings = $attr.SelectSingleNode("d:Settings", $ns)
|
||||||
|
$dynTable = ""
|
||||||
|
if ($settings -and $typeStr -eq "DynamicList") {
|
||||||
|
$mt = $settings.SelectSingleNode("d:MainTable", $ns)
|
||||||
|
if ($mt) { $dynTable = " -> $($mt.InnerText)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ValueTable/ValueTree columns
|
||||||
|
$colStr = ""
|
||||||
|
$columns = $attr.SelectSingleNode("d:Columns", $ns)
|
||||||
|
if ($columns -and ($typeStr -eq "ValueTable" -or $typeStr -eq "ValueTree")) {
|
||||||
|
$cols = @()
|
||||||
|
foreach ($col in $columns.SelectNodes("d:Column", $ns)) {
|
||||||
|
$cName = $col.GetAttribute("name")
|
||||||
|
$cTypeNode = $col.SelectSingleNode("d:Type", $ns)
|
||||||
|
$cType = Format-Type $cTypeNode
|
||||||
|
if ($cType) { $cols += "$cName`: $cType" } else { $cols += $cName }
|
||||||
|
}
|
||||||
|
if ($cols.Count -gt 0) {
|
||||||
|
$colStr = " [$($cols -join ', ')]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$line = " $prefix$aName`: $typeStr$colStr$dynTable$mainSuffix"
|
||||||
|
if (-not $typeStr -and -not $colStr -and -not $dynTable) {
|
||||||
|
$line = " $prefix$aName$mainSuffix"
|
||||||
|
}
|
||||||
|
$attrLines += $line
|
||||||
|
}
|
||||||
|
if ($attrLines.Count -gt 0) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Attributes:"
|
||||||
|
$lines += $attrLines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Parameters ---
|
||||||
|
|
||||||
|
$paramsNode = $root.SelectSingleNode("d:Parameters", $ns)
|
||||||
|
if ($paramsNode) {
|
||||||
|
$paramLines = @()
|
||||||
|
foreach ($param in $paramsNode.SelectNodes("d:Parameter", $ns)) {
|
||||||
|
$pName = $param.GetAttribute("name")
|
||||||
|
$typeNode = $param.SelectSingleNode("d:Type", $ns)
|
||||||
|
$typeStr = Format-Type $typeNode
|
||||||
|
|
||||||
|
$keyParam = $param.SelectSingleNode("d:KeyParameter", $ns)
|
||||||
|
$isKey = ($keyParam -and $keyParam.InnerText -eq "true")
|
||||||
|
$keySuffix = if ($isKey) { " (key)" } else { "" }
|
||||||
|
|
||||||
|
if ($typeStr) {
|
||||||
|
$paramLines += " $pName`: $typeStr$keySuffix"
|
||||||
|
} else {
|
||||||
|
$paramLines += " $pName$keySuffix"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($paramLines.Count -gt 0) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Parameters:"
|
||||||
|
$lines += $paramLines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Commands ---
|
||||||
|
|
||||||
|
$cmdsNode = $root.SelectSingleNode("d:Commands", $ns)
|
||||||
|
if ($cmdsNode) {
|
||||||
|
$cmdLines = @()
|
||||||
|
foreach ($cmd in $cmdsNode.SelectNodes("d:Command", $ns)) {
|
||||||
|
$cName = $cmd.GetAttribute("name")
|
||||||
|
$action = $cmd.SelectSingleNode("d:Action", $ns)
|
||||||
|
$shortcut = $cmd.SelectSingleNode("d:Shortcut", $ns)
|
||||||
|
|
||||||
|
$actionStr = if ($action) { " -> $($action.InnerText)" } else { "" }
|
||||||
|
$scStr = if ($shortcut) { " [$($shortcut.InnerText)]" } else { "" }
|
||||||
|
|
||||||
|
$cmdLines += " $cName$actionStr$scStr"
|
||||||
|
}
|
||||||
|
if ($cmdLines.Count -gt 0) {
|
||||||
|
$lines += ""
|
||||||
|
$lines += "Commands:"
|
||||||
|
$lines += $cmdLines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Truncation protection ---
|
||||||
|
|
||||||
|
$totalLines = $lines.Count
|
||||||
|
|
||||||
|
if ($Offset -gt 0) {
|
||||||
|
if ($Offset -ge $totalLines) {
|
||||||
|
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
$lines = $lines[$Offset..($totalLines - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lines.Count -gt $Limit) {
|
||||||
|
$shown = $lines[0..($Limit - 1)]
|
||||||
|
foreach ($l in $shown) { Write-Host $l }
|
||||||
|
$remaining = $totalLines - $Offset - $Limit
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[TRUNCATED] Shown $Limit of $totalLines lines. Use -Offset $($Offset + $Limit) to continue."
|
||||||
|
} else {
|
||||||
|
foreach ($l in $lines) { Write-Host $l }
|
||||||
|
}
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
---
|
||||||
|
name: form-patterns
|
||||||
|
description: Справочник паттернов компоновки управляемых форм 1С — архетипы, конвенции именования, продвинутые приёмы
|
||||||
|
argument-hint: (no arguments)
|
||||||
|
allowed-tools: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# /form-patterns — паттерны компоновки форм
|
||||||
|
|
||||||
|
Справочник типовых паттернов дизайна управляемых форм 1С. Вызывай **перед** проектированием формы через `/form-compile`, когда требования пользователя не детализируют расположение элементов.
|
||||||
|
|
||||||
|
**Как использовать:** выбери подходящий архетип, применяй конвенции именования, при необходимости используй продвинутые паттерны.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Архетипы форм
|
||||||
|
|
||||||
|
### Форма документа
|
||||||
|
|
||||||
|
```
|
||||||
|
Шапка (horizontal, 2 колонки)
|
||||||
|
├─ Левая (vertical): НомерДата (H: Номер + Дата "от"), Контрагент, Договор
|
||||||
|
├─ Правая (vertical): Организация, Подразделение, ЦеныИВалюта (надпись-ссылка)
|
||||||
|
Страницы (pages)
|
||||||
|
├─ Товары: таблица Объект.Товары
|
||||||
|
├─ Услуги: таблица Объект.Услуги (опционально)
|
||||||
|
└─ Дополнительно: прочие реквизиты
|
||||||
|
Подвал (vertical)
|
||||||
|
├─ Итоги (horizontal): Всего, НДС, Скидка
|
||||||
|
└─ КомментарийОтветственный (horizontal): Комментарий + Ответственный
|
||||||
|
```
|
||||||
|
|
||||||
|
**События:** OnCreateAtServer, OnReadAtServer, OnOpen, BeforeWriteAtServer, AfterWriteAtServer, AfterWrite, NotificationProcessing
|
||||||
|
**Свойства:** autoTitle=false
|
||||||
|
|
||||||
|
### Форма обработки (DataProcessor)
|
||||||
|
|
||||||
|
```
|
||||||
|
Параметры (vertical)
|
||||||
|
├─ Группа полей ввода (Организация, Период, режимы работы)
|
||||||
|
├─ Информационные надписи (label, hyperlink)
|
||||||
|
Рабочая область
|
||||||
|
├─ Таблица данных или Pages с вкладками
|
||||||
|
Кнопки действий
|
||||||
|
├─ Выполнить / Применить (defaultButton)
|
||||||
|
├─ Закрыть (stdCommand: Close)
|
||||||
|
```
|
||||||
|
|
||||||
|
**События:** OnCreateAtServer, OnOpen, NotificationProcessing
|
||||||
|
**Свойства:** windowOpeningMode=LockOwnerWindow (если диалог), autoTitle=false
|
||||||
|
|
||||||
|
### Форма списка
|
||||||
|
|
||||||
|
```
|
||||||
|
Отборы (group: alwaysHorizontal)
|
||||||
|
├─ ГруппаОтбор[Поле] (H): Флажок + Поле ввода (для каждого фильтра)
|
||||||
|
Список (table, DynamicList)
|
||||||
|
├─ Колонки: labelField (не input — данные только для чтения)
|
||||||
|
```
|
||||||
|
|
||||||
|
**События:** OnCreateAtServer, OnOpen, NotificationProcessing, OnLoadDataFromSettingsAtServer
|
||||||
|
**Свойства:** autoSaveDataInSettings=Use
|
||||||
|
**Фильтры:** пара реквизитов на каждый — `Отбор[Поле]` (значение) + `Отбор[Поле]Использование` (boolean)
|
||||||
|
|
||||||
|
### Форма элемента справочника
|
||||||
|
|
||||||
|
**Простая:**
|
||||||
|
```
|
||||||
|
ГруппаРеквизитов (horizontal)
|
||||||
|
├─ Наименование -> Объект.Description
|
||||||
|
└─ Код -> Объект.Code (если нужен)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Сложная:**
|
||||||
|
```
|
||||||
|
Главное (vertical)
|
||||||
|
├─ Наименование -> Объект.Description
|
||||||
|
├─ Параметры (horizontal, 2 колонки)
|
||||||
|
│ ├─ Левая: основные реквизиты
|
||||||
|
│ └─ Правая: дополнительные реквизиты
|
||||||
|
└─ КонтактныеДанные / Дополнительно (vertical)
|
||||||
|
```
|
||||||
|
|
||||||
|
**События:** OnCreateAtServer, OnReadAtServer, BeforeWriteAtServer, NotificationProcessing
|
||||||
|
|
||||||
|
### Мастер (Wizard)
|
||||||
|
|
||||||
|
```
|
||||||
|
Страницы (pages, OnCurrentPageChange)
|
||||||
|
├─ Шаг1: описание + параметры
|
||||||
|
├─ Шаг2: основная работа
|
||||||
|
└─ Шаг3: результат
|
||||||
|
Кнопки (horizontal)
|
||||||
|
├─ Назад (command), Далее (command, defaultButton), Выполнить (command)
|
||||||
|
└─ Закрыть (stdCommand: Close)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Свойства:** windowOpeningMode=LockOwnerWindow, commandBarLocation=None
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Конвенции именования
|
||||||
|
|
||||||
|
### Группы
|
||||||
|
|
||||||
|
| Назначение | Имя | Тип |
|
||||||
|
|-----------|-----|-----|
|
||||||
|
| Шапка | `ГруппаШапка` | horizontal |
|
||||||
|
| Левая колонка | `ГруппаШапкаЛевая` | vertical |
|
||||||
|
| Правая колонка | `ГруппаШапкаПравая` | vertical |
|
||||||
|
| Номер+Дата | `ГруппаНомерДата` | horizontal |
|
||||||
|
| Подвал | `ГруппаПодвал` | vertical |
|
||||||
|
| Итоги | `ГруппаИтоги` | horizontal |
|
||||||
|
| Кнопки | `ГруппаКнопок` | horizontal |
|
||||||
|
| Страницы | `ГруппаСтраницы` / `Страницы` | pages |
|
||||||
|
| Предупреждение | `ГруппаПредупреждение` | horizontal, visible:false |
|
||||||
|
| Доп. секция | `ГруппаДополнительно` / `ГруппаПрочее` | vertical, collapse |
|
||||||
|
|
||||||
|
### Элементы
|
||||||
|
|
||||||
|
| Назначение | Имя |
|
||||||
|
|-----------|-----|
|
||||||
|
| Поле в таблице | `[Таблица][Поле]` |
|
||||||
|
| Итог | `Итоги[Поле]` |
|
||||||
|
| Надпись-ссылка | `[Поле]Надпись` |
|
||||||
|
| Фильтр | `Отбор[Поле]` |
|
||||||
|
| Флажок фильтра | `Отбор[Поле]Использование` |
|
||||||
|
| Кнопка команды | `[Команда]Кнопка` |
|
||||||
|
| Баннер-картинка | `[Баннер]Картинка` |
|
||||||
|
| Баннер-надпись | `[Баннер]Надпись` |
|
||||||
|
| Подменю | `Подменю[Действие]` |
|
||||||
|
|
||||||
|
### Обработчики событий
|
||||||
|
|
||||||
|
Имя = имя элемента + суффикс на русском:
|
||||||
|
|
||||||
|
| Событие | Суффикс | Пример |
|
||||||
|
|---------|---------|--------|
|
||||||
|
| OnChange | ПриИзменении | `ОрганизацияПриИзменении` |
|
||||||
|
| StartChoice | НачалоВыбора | `КонтрагентНачалоВыбора` |
|
||||||
|
| Click | Нажатие | `ЦеныИВалютаНажатие` |
|
||||||
|
| OnEditEnd | ПриОкончанииРедактирования | `ТоварыПриОкончанииРедактирования` |
|
||||||
|
| OnStartEdit | ПриНачалеРедактирования | `ТоварыПриНачалеРедактирования` |
|
||||||
|
|
||||||
|
Обработчики формы: `ПриСозданииНаСервере`, `ПриОткрытии`, `ПередЗакрытием`, `ОбработкаОповещения`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Принципы компоновки
|
||||||
|
|
||||||
|
1. **Порядок чтения.** Сверху вниз, слева направо. Самое важное — вверху.
|
||||||
|
2. **Двухколоночная шапка.** Основные реквизиты слева (контрагент, склад), организационные справа (организация, подразделение).
|
||||||
|
3. **Кнопки действий внизу.** Главная кнопка — `defaultButton: true`. Закрыть — всегда последняя.
|
||||||
|
4. **Таблицы — основная область.** Табличные части занимают большую часть формы, обычно на Pages.
|
||||||
|
5. **Итоги рядом с таблицей.** В подвале, горизонтальная группа, все поля readOnly.
|
||||||
|
6. **Фильтры — отдельная зона.** Над списком, alwaysHorizontal, пара «флажок + поле» на каждый фильтр.
|
||||||
|
7. **Скрытые элементы для состояний.** Баннеры, предупреждения — `visible: false`, показываются программно.
|
||||||
|
8. **Надписи-ссылки для диалогов.** `labelField` с `hyperlink: true` и событием Click.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Продвинутые паттерны (ERP)
|
||||||
|
|
||||||
|
### Сворачиваемые группы
|
||||||
|
|
||||||
|
Для необязательных секций (подписи, дополнительно, прочее):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "group": "vertical", "name": "ГруппаПодписи", "title": "Подписи",
|
||||||
|
"behavior": "Collapsible", "collapsed": true, "children": [...] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Баннер-предупреждение
|
||||||
|
|
||||||
|
Группа «картинка + надпись», скрыта по умолчанию, показывается программно:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "group": "horizontal", "name": "ГруппаПредупреждение", "showTitle": false,
|
||||||
|
"visible": false, "children": [
|
||||||
|
{ "picture": "ПредупреждениеКартинка" },
|
||||||
|
{ "label": "ПредупреждениеНадпись", "title": "Текст", "maxWidth": 76, "autoMaxWidth": false }
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Popup-меню в командной панели
|
||||||
|
|
||||||
|
Группировка связанных команд (печать, отправка) в одну кнопку с иконкой:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "cmdBar": "КоманднаяПанель", "children": [
|
||||||
|
{ "popup": "ПодменюПечать", "title": "Печать",
|
||||||
|
"picture": "StdPicture.Print", "representation": "Picture", "children": [
|
||||||
|
{ "button": "ПечатьНакладная", "command": "Печать" },
|
||||||
|
{ "button": "ПечатьСчёт", "command": "ПечатьСчёт" }
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Форма без стандартной командной панели
|
||||||
|
|
||||||
|
Для модальных диалогов и мастеров:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "properties": { "commandBarLocation": "None", "windowOpeningMode": "LockWholeInterface" } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Надпись-гиперссылка
|
||||||
|
|
||||||
|
Вместо кнопки для открытия подформ (ЦеныИВалюта, УчётнаяПолитика):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "labelField": "ЦеныИВалютаНадпись", "path": "ЦеныИВалюта", "hyperlink": true, "on": ["Click"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример: форма обработки (полный DSL)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Загрузка данных из CSV",
|
||||||
|
"properties": { "autoTitle": false, "windowOpeningMode": "LockOwnerWindow" },
|
||||||
|
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
|
||||||
|
"elements": [
|
||||||
|
{ "group": "vertical", "name": "ГруппаПараметры", "children": [
|
||||||
|
{ "input": "ФайлЗагрузки", "path": "ФайлЗагрузки", "title": "Файл", "clearButton": true, "horizontalStretch": true, "on": ["StartChoice"] },
|
||||||
|
{ "input": "Кодировка", "path": "Кодировка" },
|
||||||
|
{ "input": "Разделитель", "path": "Разделитель", "title": "Разделитель колонок" }
|
||||||
|
]},
|
||||||
|
{ "table": "Данные", "path": "Объект.Данные", "on": ["OnStartEdit"], "columns": [
|
||||||
|
{ "input": "ДанныеНомерСтроки", "path": "Объект.Данные.LineNumber", "readOnly": true, "title": "№" },
|
||||||
|
{ "input": "ДанныеНаименование", "path": "Объект.Данные.Наименование" },
|
||||||
|
{ "input": "ДанныеКоличество", "path": "Объект.Данные.Количество", "on": ["OnChange"] },
|
||||||
|
{ "input": "ДанныеСумма", "path": "Объект.Данные.Сумма", "readOnly": true }
|
||||||
|
]},
|
||||||
|
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
|
||||||
|
{ "button": "Загрузить", "command": "Загрузить", "title": "Загрузить из файла", "defaultButton": true },
|
||||||
|
{ "button": "Очистить", "command": "Очистить", "title": "Очистить таблицу" },
|
||||||
|
{ "button": "Закрыть", "stdCommand": "Close" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "ExternalDataProcessorObject.ЗагрузкаИзCSV", "main": true },
|
||||||
|
{ "name": "ФайлЗагрузки", "type": "string" },
|
||||||
|
{ "name": "Кодировка", "type": "string(20)" },
|
||||||
|
{ "name": "Разделитель", "type": "string(5)" }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Загрузить", "action": "ЗагрузитьОбработка" },
|
||||||
|
{ "name": "Очистить", "action": "ОчиститьОбработка" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: form-validate
|
||||||
|
description: Валидация структурной корректности управляемой формы 1С (Form.xml)
|
||||||
|
argument-hint: <FormPath>
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /form-validate — Валидатор формы
|
||||||
|
|
||||||
|
Проверяет Form.xml управляемой формы на структурные ошибки: уникальность ID, наличие companion-элементов, корректность ссылок DataPath и команд.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
```
|
||||||
|
/form-validate <FormPath>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Параметры
|
||||||
|
|
||||||
|
| Параметр | Обязательный | По умолчанию | Описание |
|
||||||
|
|-----------|:------------:|--------------|-----------------------------|
|
||||||
|
| FormPath | да | — | Путь к файлу Form.xml |
|
||||||
|
| MaxErrors | нет | 30 | Остановиться после N ошибок |
|
||||||
|
|
||||||
|
## Команда
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\form-validate\scripts\form-validate.ps1 -FormPath "<путь>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Выполняемые проверки
|
||||||
|
|
||||||
|
| # | Проверка | Серьёзность |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Корневой элемент `<Form>`, version="2.17" | ERROR / WARN |
|
||||||
|
| 2 | `<AutoCommandBar>` присутствует, id="-1" | ERROR |
|
||||||
|
| 3 | Уникальность ID элементов (отдельный пул) | ERROR |
|
||||||
|
| 4 | Уникальность ID реквизитов (отдельный пул) | ERROR |
|
||||||
|
| 5 | Уникальность ID команд (отдельный пул) | ERROR |
|
||||||
|
| 6 | Companion-элементы (ContextMenu, ExtendedTooltip, и др.) | ERROR |
|
||||||
|
| 7 | DataPath → ссылается на существующий реквизит | ERROR |
|
||||||
|
| 8 | CommandName кнопок → ссылается на существующую команду | ERROR |
|
||||||
|
| 9 | События имеют непустые имена обработчиков | ERROR |
|
||||||
|
| 10 | Команды имеют Action (обработчик) | ERROR |
|
||||||
|
| 11 | Не более одного MainAttribute | ERROR |
|
||||||
|
|
||||||
|
## Вывод
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Validation: ФормаДокумента ===
|
||||||
|
|
||||||
|
[OK] Root element: Form version=2.17
|
||||||
|
[OK] AutoCommandBar: name='ФормаКоманднаяПанель', id=-1
|
||||||
|
[OK] Unique element IDs: 96 elements
|
||||||
|
[OK] Unique attribute IDs: 38 entries
|
||||||
|
[OK] Unique command IDs: 5 entries
|
||||||
|
[OK] Companion elements: 86 elements checked
|
||||||
|
[OK] DataPath references: 53 paths checked
|
||||||
|
[OK] Command references: 2 buttons checked
|
||||||
|
[OK] Event handlers: 41 events checked
|
||||||
|
[OK] Command actions: 5 commands checked
|
||||||
|
[OK] MainAttribute: 1 main attribute
|
||||||
|
|
||||||
|
---
|
||||||
|
Total: 96 elements, 38 attributes, 5 commands
|
||||||
|
All checks passed.
|
||||||
|
```
|
||||||
|
|
||||||
|
Код возврата: 0 = все проверки пройдены, 1 = есть ошибки.
|
||||||
|
|
||||||
|
## Когда использовать
|
||||||
|
|
||||||
|
- **После `/form-compile`**: проверить корректность сгенерированной формы
|
||||||
|
- **После ручного редактирования Form.xml**: убедиться что ID уникальны, companions на месте, ссылки валидны
|
||||||
|
- **При отладке**: выявить ошибки в структуре формы до сборки EPF
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$FormPath,
|
||||||
|
|
||||||
|
[int]$MaxErrors = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
# --- Load XML ---
|
||||||
|
|
||||||
|
if (-not (Test-Path $FormPath)) {
|
||||||
|
Write-Error "File not found: $FormPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||||
|
$xmlDoc.PreserveWhitespace = $false
|
||||||
|
try {
|
||||||
|
$xmlDoc.Load((Resolve-Path $FormPath).Path)
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] XML parse error: $($_.Exception.Message)"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "---"
|
||||||
|
Write-Host "Errors: 1, Warnings: 0"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||||
|
$nsMgr.AddNamespace("f", "http://v8.1c.ru/8.3/xcf/logform")
|
||||||
|
$nsMgr.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||||
|
|
||||||
|
$root = $xmlDoc.DocumentElement
|
||||||
|
|
||||||
|
# --- Counters ---
|
||||||
|
|
||||||
|
$errors = 0
|
||||||
|
$warnings = 0
|
||||||
|
$stopped = $false
|
||||||
|
|
||||||
|
function Report-OK {
|
||||||
|
param([string]$msg)
|
||||||
|
Write-Host "[OK] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Report-Error {
|
||||||
|
param([string]$msg)
|
||||||
|
$script:errors++
|
||||||
|
Write-Host "[ERROR] $msg"
|
||||||
|
if ($script:errors -ge $MaxErrors) {
|
||||||
|
$script:stopped = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Report-Warn {
|
||||||
|
param([string]$msg)
|
||||||
|
$script:warnings++
|
||||||
|
Write-Host "[WARN] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Form name from path ---
|
||||||
|
|
||||||
|
$formName = [System.IO.Path]::GetFileNameWithoutExtension($FormPath)
|
||||||
|
$parentDir = [System.IO.Path]::GetDirectoryName($FormPath)
|
||||||
|
if ($parentDir) {
|
||||||
|
$extDir = [System.IO.Path]::GetFileName($parentDir)
|
||||||
|
if ($extDir -eq "Ext") {
|
||||||
|
$formDir = [System.IO.Path]::GetDirectoryName($parentDir)
|
||||||
|
if ($formDir) { $formName = [System.IO.Path]::GetFileName($formDir) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "=== Validation: $formName ==="
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# --- Check 1: Root element and version ---
|
||||||
|
|
||||||
|
if ($root.LocalName -ne "Form") {
|
||||||
|
Report-Error "Root element is '$($root.LocalName)', expected 'Form'"
|
||||||
|
} else {
|
||||||
|
$version = $root.GetAttribute("version")
|
||||||
|
if ($version -eq "2.17") {
|
||||||
|
Report-OK "Root element: Form version=$version"
|
||||||
|
} elseif ($version) {
|
||||||
|
Report-Warn "Form version='$version' (expected 2.17)"
|
||||||
|
} else {
|
||||||
|
Report-Warn "Form version attribute missing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 2: AutoCommandBar ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$acb = $root.SelectSingleNode("f:AutoCommandBar", $nsMgr)
|
||||||
|
if ($acb) {
|
||||||
|
$acbName = $acb.GetAttribute("name")
|
||||||
|
$acbId = $acb.GetAttribute("id")
|
||||||
|
if ($acbId -eq "-1") {
|
||||||
|
Report-OK "AutoCommandBar: name='$acbName', id=$acbId"
|
||||||
|
} else {
|
||||||
|
Report-Error "AutoCommandBar id='$acbId', expected '-1'"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Report-Error "AutoCommandBar element missing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Collect all elements with IDs ---
|
||||||
|
|
||||||
|
$elementIds = @{} # id -> name (element ID pool)
|
||||||
|
$allElements = @() # @{Name; Tag; Id; ParentName; Node}
|
||||||
|
|
||||||
|
function Collect-Elements {
|
||||||
|
param($node, [string]$parentName)
|
||||||
|
|
||||||
|
foreach ($child in $node.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
|
||||||
|
$name = $child.GetAttribute("name")
|
||||||
|
$id = $child.GetAttribute("id")
|
||||||
|
|
||||||
|
if ($name -and $id) {
|
||||||
|
$tag = $child.LocalName
|
||||||
|
|
||||||
|
$script:allElements += @{
|
||||||
|
Name = $name
|
||||||
|
Tag = $tag
|
||||||
|
Id = $id
|
||||||
|
ParentName = $parentName
|
||||||
|
Node = $child
|
||||||
|
}
|
||||||
|
|
||||||
|
# Track element IDs (skip AutoCommandBar which has -1)
|
||||||
|
if ($id -ne "-1") {
|
||||||
|
if ($elementIds.ContainsKey($id)) {
|
||||||
|
Report-Error "Duplicate element id=${id}: '$name' and '$($elementIds[$id])'"
|
||||||
|
} else {
|
||||||
|
$elementIds[$id] = $name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Recurse into ChildItems
|
||||||
|
$childItems = $child.SelectSingleNode("f:ChildItems", $nsMgr)
|
||||||
|
if ($childItems) {
|
||||||
|
Collect-Elements -node $childItems -parentName $name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect from ChildItems
|
||||||
|
$childItemsRoot = $root.SelectSingleNode("f:ChildItems", $nsMgr)
|
||||||
|
if ($childItemsRoot) {
|
||||||
|
Collect-Elements -node $childItemsRoot -parentName "(root)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Also collect from AutoCommandBar's ChildItems
|
||||||
|
$acb = $root.SelectSingleNode("f:AutoCommandBar", $nsMgr)
|
||||||
|
if ($acb) {
|
||||||
|
$acbChildren = $acb.SelectSingleNode("f:ChildItems", $nsMgr)
|
||||||
|
if ($acbChildren) {
|
||||||
|
Collect-Elements -node $acbChildren -parentName "ФормаКоманднаяПанель"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 3: Unique element IDs ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$dupCount = ($allElements | Group-Object { $_.Id } | Where-Object { $_.Count -gt 1 -and $_.Name -ne "-1" }).Count
|
||||||
|
if ($dupCount -eq 0) {
|
||||||
|
Report-OK "Unique element IDs: $($elementIds.Count) elements"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Collect attributes (separate ID pool) ---
|
||||||
|
|
||||||
|
$attrMap = @{} # name -> node
|
||||||
|
$attrIds = @{} # id -> name
|
||||||
|
$attrNodes = $root.SelectNodes("f:Attributes/f:Attribute", $nsMgr)
|
||||||
|
foreach ($attr in $attrNodes) {
|
||||||
|
$attrName = $attr.GetAttribute("name")
|
||||||
|
$attrId = $attr.GetAttribute("id")
|
||||||
|
if ($attrName) {
|
||||||
|
$attrMap[$attrName] = $attr
|
||||||
|
}
|
||||||
|
if ($attrId -and $attrId -ne "") {
|
||||||
|
if ($attrIds.ContainsKey($attrId)) {
|
||||||
|
Report-Error "Duplicate attribute id=${attrId}: '$attrName' and '$($attrIds[$attrId])'"
|
||||||
|
} else {
|
||||||
|
$attrIds[$attrId] = $attrName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Column IDs are a separate sub-pool per attribute — check uniqueness within parent
|
||||||
|
$colIds = @{}
|
||||||
|
foreach ($col in $attr.SelectNodes("f:Columns/f:Column", $nsMgr)) {
|
||||||
|
$colId = $col.GetAttribute("id")
|
||||||
|
$colName = $col.GetAttribute("name")
|
||||||
|
if ($colId -and $colId -ne "") {
|
||||||
|
if ($colIds.ContainsKey($colId)) {
|
||||||
|
Report-Error "Duplicate column id=${colId} in '$attrName': '$colName' and '$($colIds[$colId])'"
|
||||||
|
} else {
|
||||||
|
$colIds[$colId] = $colName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$attrDupCount = ($attrIds.GetEnumerator() | Group-Object Value | Where-Object { $_.Count -gt 1 }).Count
|
||||||
|
if ($attrDupCount -eq 0 -and $attrIds.Count -gt 0) {
|
||||||
|
Report-OK "Unique attribute IDs: $($attrIds.Count) entries"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Collect commands (separate ID pool) ---
|
||||||
|
|
||||||
|
$cmdMap = @{} # name -> node
|
||||||
|
$cmdIds = @{} # id -> name
|
||||||
|
$cmdNodes = $root.SelectNodes("f:Commands/f:Command", $nsMgr)
|
||||||
|
foreach ($cmd in $cmdNodes) {
|
||||||
|
$cmdName = $cmd.GetAttribute("name")
|
||||||
|
$cmdId = $cmd.GetAttribute("id")
|
||||||
|
if ($cmdName) {
|
||||||
|
$cmdMap[$cmdName] = $cmd
|
||||||
|
}
|
||||||
|
if ($cmdId -and $cmdId -ne "") {
|
||||||
|
if ($cmdIds.ContainsKey($cmdId)) {
|
||||||
|
Report-Error "Duplicate command id=${cmdId}: '$cmdName' and '$($cmdIds[$cmdId])'"
|
||||||
|
} else {
|
||||||
|
$cmdIds[$cmdId] = $cmdName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
if ($cmdIds.Count -gt 0) {
|
||||||
|
$cmdDupCount = ($cmdIds.GetEnumerator() | Group-Object Value | Where-Object { $_.Count -gt 1 }).Count
|
||||||
|
if ($cmdDupCount -eq 0) {
|
||||||
|
Report-OK "Unique command IDs: $($cmdIds.Count) entries"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 4: Companion elements ---
|
||||||
|
|
||||||
|
# Define required companions per element type
|
||||||
|
$companionRules = @{
|
||||||
|
"InputField" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"CheckBoxField" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"LabelDecoration" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"LabelField" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"PictureDecoration" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"PictureField" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"CalendarField" = @("ContextMenu", "ExtendedTooltip")
|
||||||
|
"UsualGroup" = @("ExtendedTooltip")
|
||||||
|
"Pages" = @("ExtendedTooltip")
|
||||||
|
"Page" = @("ExtendedTooltip")
|
||||||
|
"Button" = @("ExtendedTooltip")
|
||||||
|
"Table" = @("ContextMenu", "AutoCommandBar", "SearchStringAddition", "ViewStatusAddition", "SearchControlAddition")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$companionErrors = 0
|
||||||
|
$companionChecked = 0
|
||||||
|
|
||||||
|
foreach ($el in $allElements) {
|
||||||
|
if ($stopped) { break }
|
||||||
|
$tag = $el.Tag
|
||||||
|
$elName = $el.Name
|
||||||
|
$node = $el.Node
|
||||||
|
|
||||||
|
if (-not $companionRules.ContainsKey($tag)) { continue }
|
||||||
|
|
||||||
|
$required = $companionRules[$tag]
|
||||||
|
$companionChecked++
|
||||||
|
|
||||||
|
foreach ($compTag in $required) {
|
||||||
|
$compNode = $node.SelectSingleNode("f:$compTag", $nsMgr)
|
||||||
|
if (-not $compNode) {
|
||||||
|
Report-Error "[$tag] '$elName': missing companion <$compTag>"
|
||||||
|
$companionErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($companionErrors -eq 0 -and $companionChecked -gt 0) {
|
||||||
|
Report-OK "Companion elements: $companionChecked elements checked"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 5: DataPath -> Attribute references ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$pathErrors = 0
|
||||||
|
$pathChecked = 0
|
||||||
|
|
||||||
|
foreach ($el in $allElements) {
|
||||||
|
if ($stopped) { break }
|
||||||
|
$tag = $el.Tag
|
||||||
|
$elName = $el.Name
|
||||||
|
$node = $el.Node
|
||||||
|
|
||||||
|
# Skip companion elements
|
||||||
|
if ($tag -in @("ContextMenu", "ExtendedTooltip", "AutoCommandBar", "SearchStringAddition", "ViewStatusAddition", "SearchControlAddition")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$dpNode = $node.SelectSingleNode("f:DataPath", $nsMgr)
|
||||||
|
if (-not $dpNode) { continue }
|
||||||
|
|
||||||
|
$dataPath = $dpNode.InnerText.Trim()
|
||||||
|
if (-not $dataPath) { continue }
|
||||||
|
|
||||||
|
$pathChecked++
|
||||||
|
|
||||||
|
# Extract root segment of path, strip array indices like [0]
|
||||||
|
$cleanPath = $dataPath -replace '\[\d+\]', ''
|
||||||
|
$segments = $cleanPath -split '\.'
|
||||||
|
$rootAttr = $segments[0]
|
||||||
|
|
||||||
|
if (-not $attrMap.ContainsKey($rootAttr)) {
|
||||||
|
Report-Error "[$tag] '$elName': DataPath='$dataPath' — attribute '$rootAttr' not found"
|
||||||
|
$pathErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pathErrors -eq 0 -and $pathChecked -gt 0) {
|
||||||
|
Report-OK "DataPath references: $pathChecked paths checked"
|
||||||
|
} elseif ($pathChecked -eq 0) {
|
||||||
|
Report-OK "DataPath references: none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 6: Button command references ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$cmdErrors = 0
|
||||||
|
$cmdChecked = 0
|
||||||
|
|
||||||
|
foreach ($el in $allElements) {
|
||||||
|
if ($stopped) { break }
|
||||||
|
$tag = $el.Tag
|
||||||
|
$elName = $el.Name
|
||||||
|
$node = $el.Node
|
||||||
|
|
||||||
|
if ($tag -ne "Button") { continue }
|
||||||
|
|
||||||
|
$cmdNode = $node.SelectSingleNode("f:CommandName", $nsMgr)
|
||||||
|
if (-not $cmdNode) { continue }
|
||||||
|
|
||||||
|
$cmdRef = $cmdNode.InnerText.Trim()
|
||||||
|
if (-not $cmdRef) { continue }
|
||||||
|
|
||||||
|
# Form.Command.XXX -> check command XXX exists
|
||||||
|
if ($cmdRef -match '^Form\.Command\.(.+)$') {
|
||||||
|
$cmdName = $Matches[1]
|
||||||
|
$cmdChecked++
|
||||||
|
if (-not $cmdMap.ContainsKey($cmdName)) {
|
||||||
|
Report-Error "[Button] '$elName': CommandName='$cmdRef' — command '$cmdName' not found in Commands"
|
||||||
|
$cmdErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Form.StandardCommand.XXX — skip, standard commands always exist
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cmdErrors -eq 0 -and $cmdChecked -gt 0) {
|
||||||
|
Report-OK "Command references: $cmdChecked buttons checked"
|
||||||
|
} elseif ($cmdChecked -eq 0) {
|
||||||
|
Report-OK "Command references: none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 7: Events have handler names ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$eventErrors = 0
|
||||||
|
$eventChecked = 0
|
||||||
|
|
||||||
|
# Form-level events
|
||||||
|
$formEvents = $root.SelectSingleNode("f:Events", $nsMgr)
|
||||||
|
if ($formEvents) {
|
||||||
|
foreach ($evt in $formEvents.SelectNodes("f:Event", $nsMgr)) {
|
||||||
|
$evtName = $evt.GetAttribute("name")
|
||||||
|
$handler = $evt.InnerText.Trim()
|
||||||
|
$eventChecked++
|
||||||
|
if (-not $handler) {
|
||||||
|
Report-Error "Form event '$evtName': empty handler name"
|
||||||
|
$eventErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Element-level events
|
||||||
|
foreach ($el in $allElements) {
|
||||||
|
if ($stopped) { break }
|
||||||
|
$tag = $el.Tag
|
||||||
|
$elName = $el.Name
|
||||||
|
$node = $el.Node
|
||||||
|
|
||||||
|
$eventsNode = $node.SelectSingleNode("f:Events", $nsMgr)
|
||||||
|
if (-not $eventsNode) { continue }
|
||||||
|
|
||||||
|
foreach ($evt in $eventsNode.SelectNodes("f:Event", $nsMgr)) {
|
||||||
|
$evtName = $evt.GetAttribute("name")
|
||||||
|
$handler = $evt.InnerText.Trim()
|
||||||
|
$eventChecked++
|
||||||
|
if (-not $handler) {
|
||||||
|
Report-Error "[$tag] '$elName' event '$evtName': empty handler name"
|
||||||
|
$eventErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($eventErrors -eq 0 -and $eventChecked -gt 0) {
|
||||||
|
Report-OK "Event handlers: $eventChecked events checked"
|
||||||
|
} elseif ($eventChecked -eq 0) {
|
||||||
|
Report-OK "Event handlers: none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 8: Command actions ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$actionErrors = 0
|
||||||
|
$actionChecked = 0
|
||||||
|
|
||||||
|
foreach ($cmd in $cmdNodes) {
|
||||||
|
if ($stopped) { break }
|
||||||
|
$cmdName = $cmd.GetAttribute("name")
|
||||||
|
$actionNode = $cmd.SelectSingleNode("f:Action", $nsMgr)
|
||||||
|
$actionChecked++
|
||||||
|
if (-not $actionNode -or -not $actionNode.InnerText.Trim()) {
|
||||||
|
Report-Error "Command '$cmdName': missing or empty Action"
|
||||||
|
$actionErrors++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($actionErrors -eq 0 -and $actionChecked -gt 0) {
|
||||||
|
Report-OK "Command actions: $actionChecked commands checked"
|
||||||
|
} elseif ($actionChecked -eq 0) {
|
||||||
|
Report-OK "Command actions: none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 9: MainAttribute count ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$mainCount = 0
|
||||||
|
foreach ($attr in $attrNodes) {
|
||||||
|
$mainNode = $attr.SelectSingleNode("f:MainAttribute", $nsMgr)
|
||||||
|
if ($mainNode -and $mainNode.InnerText -eq "true") {
|
||||||
|
$mainCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($mainCount -le 1) {
|
||||||
|
$mainInfo = if ($mainCount -eq 1) { "1 main attribute" } else { "no main attribute" }
|
||||||
|
Report-OK "MainAttribute: $mainInfo"
|
||||||
|
} else {
|
||||||
|
Report-Error "Multiple MainAttribute=true ($mainCount found, expected 0 or 1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check 10: Title must be multilingual XML (not plain text) ---
|
||||||
|
|
||||||
|
if (-not $stopped) {
|
||||||
|
$titleNode = $root.SelectSingleNode("f:Title", $nsMgr)
|
||||||
|
if ($titleNode) {
|
||||||
|
$v8items = $titleNode.SelectNodes("v8:item", $nsMgr)
|
||||||
|
if ($v8items.Count -eq 0 -and $titleNode.InnerText.Trim() -ne "") {
|
||||||
|
Report-Error "Form Title is plain text ('$($titleNode.InnerText.Trim())') — must be multilingual XML (<v8:item>). Use top-level 'title' key in form-compile DSL."
|
||||||
|
} else {
|
||||||
|
Report-OK "Title: multilingual XML"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "---"
|
||||||
|
Write-Host "Total: $($allElements.Count) elements, $($attrNodes.Count) attributes, $($cmdNodes.Count) commands"
|
||||||
|
|
||||||
|
if ($stopped) {
|
||||||
|
Write-Host "Stopped after $MaxErrors errors. Fix and re-run."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors -eq 0 -and $warnings -eq 0) {
|
||||||
|
Write-Host "All checks passed."
|
||||||
|
} else {
|
||||||
|
Write-Host "Errors: $errors, Warnings: $warnings"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errors -gt 0) {
|
||||||
|
exit 1
|
||||||
|
} else {
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
|--------|--------|----------|------|
|
|--------|--------|----------|------|
|
||||||
| Внешние обработки (EPF) | 10 навыков `/epf-*` | Создание, модификация, сборка обработок из XML-исходников | [Подробнее](docs/epf-guide.md) |
|
| Внешние обработки (EPF) | 10 навыков `/epf-*` | Создание, модификация, сборка обработок из XML-исходников | [Подробнее](docs/epf-guide.md) |
|
||||||
| Табличный документ (MXL) | 4 навыка `/mxl-*` | Анализ, создание, компиляция макетов печатных форм | [Подробнее](docs/mxl-guide.md) |
|
| Табличный документ (MXL) | 4 навыка `/mxl-*` | Анализ, создание, компиляция макетов печатных форм | [Подробнее](docs/mxl-guide.md) |
|
||||||
|
| Управляемые формы (Form) | 4 навыка `/form-*` | Анализ, генерация, модификация, валидация управляемых форм | [Подробнее](docs/form-guide.md) |
|
||||||
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
- [Сборка и разборка EPF](docs/build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
- [Сборка и разборка EPF](docs/build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
||||||
- [Табличный документ (MXL)](docs/1c-spreadsheet-spec.md) — XML-формат SpreadsheetDocument, совместимость версий
|
- [Табличный документ (MXL)](docs/1c-spreadsheet-spec.md) — XML-формат SpreadsheetDocument, совместимость версий
|
||||||
- [MXL DSL](docs/mxl-dsl-spec.md) — JSON-формат описания макета для `/mxl-compile` и `/mxl-decompile`
|
- [MXL DSL](docs/mxl-dsl-spec.md) — JSON-формат описания макета для `/mxl-compile` и `/mxl-decompile`
|
||||||
|
- [Form DSL](docs/form-dsl-spec.md) — JSON-формат описания формы для `/form-compile`
|
||||||
|
|
||||||
## Структура репозитория
|
## Структура репозитория
|
||||||
|
|
||||||
@@ -55,14 +57,20 @@
|
|||||||
├── mxl-validate/ # Валидация макета
|
├── mxl-validate/ # Валидация макета
|
||||||
├── mxl-compile/ # Компиляция макета из JSON
|
├── mxl-compile/ # Компиляция макета из JSON
|
||||||
├── mxl-decompile/ # Декомпиляция макета в JSON
|
├── mxl-decompile/ # Декомпиляция макета в JSON
|
||||||
|
├── form-info/ # Анализ структуры управляемой формы
|
||||||
|
├── form-compile/ # Компиляция формы из JSON
|
||||||
|
├── form-validate/ # Валидация формы
|
||||||
|
├── form-add/ # Добавление элементов в форму
|
||||||
└── img-grid/ # Сетка для анализа изображений
|
└── img-grid/ # Сетка для анализа изображений
|
||||||
docs/
|
docs/
|
||||||
├── epf-guide.md # Гайд: внешние обработки
|
├── epf-guide.md # Гайд: внешние обработки
|
||||||
├── mxl-guide.md # Гайд: табличный документ
|
├── mxl-guide.md # Гайд: табличный документ
|
||||||
|
├── form-guide.md # Гайд: управляемые формы
|
||||||
├── 1c-xml-format-spec.md # Спецификация XML-формата
|
├── 1c-xml-format-spec.md # Спецификация XML-формата
|
||||||
├── 1c-form-spec.md # Спецификация управляемых форм
|
├── 1c-form-spec.md # Спецификация управляемых форм
|
||||||
├── 1c-help-spec.md # Спецификация встроенной справки
|
├── 1c-help-spec.md # Спецификация встроенной справки
|
||||||
├── build-spec.md # Спецификация сборки/разборки
|
├── build-spec.md # Спецификация сборки/разборки
|
||||||
├── 1c-spreadsheet-spec.md # Спецификация табличного документа
|
├── 1c-spreadsheet-spec.md # Спецификация табличного документа
|
||||||
└── mxl-dsl-spec.md # Спецификация MXL DSL
|
├── mxl-dsl-spec.md # Спецификация MXL DSL
|
||||||
|
└── form-dsl-spec.md # Спецификация Form DSL
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,462 @@
|
|||||||
|
# Form DSL Specification
|
||||||
|
|
||||||
|
Спецификация JSON-формата для `/form-compile` — компактного описания управляемых форм 1С:Предприятия 8.3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Корневой объект
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Заголовок формы",
|
||||||
|
"properties": { ... },
|
||||||
|
"excludedCommands": [ ... ],
|
||||||
|
"events": { ... },
|
||||||
|
"elements": [ ... ],
|
||||||
|
"attributes": [ ... ],
|
||||||
|
"parameters": [ ... ],
|
||||||
|
"commands": [ ... ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `title` | string | Заголовок формы (необязательный) |
|
||||||
|
| `properties` | object | Свойства формы (необязательный) |
|
||||||
|
| `excludedCommands` | string[] | Исключённые стандартные команды (необязательный) |
|
||||||
|
| `events` | object | Обработчики событий формы (необязательный) |
|
||||||
|
| `elements` | array | Дерево UI-элементов (необязательный) |
|
||||||
|
| `attributes` | array | Реквизиты формы (необязательный) |
|
||||||
|
| `parameters` | array | Параметры формы (необязательный) |
|
||||||
|
| `commands` | array | Команды формы (необязательный) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Properties — свойства формы
|
||||||
|
|
||||||
|
Объект со свойствами в camelCase. Компилятор преобразует в PascalCase для XML.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"properties": {
|
||||||
|
"autoTitle": false,
|
||||||
|
"windowOpeningMode": "LockOwnerWindow",
|
||||||
|
"commandBarLocation": "Bottom"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Поддерживаемые свойства
|
||||||
|
|
||||||
|
| DSL ключ | XML элемент | Значения |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `autoTitle` | `<AutoTitle>` | `true` / `false` |
|
||||||
|
| `windowOpeningMode` | `<WindowOpeningMode>` | `LockOwnerWindow`, `Modeless` |
|
||||||
|
| `commandBarLocation` | `<CommandBarLocation>` | `Top`, `Bottom`, `None` |
|
||||||
|
| `saveDataInSettings` | `<SaveDataInSettings>` | `UseList`, `Use`, `DontUse` |
|
||||||
|
| `autoSaveDataInSettings` | `<AutoSaveDataInSettings>` | `Use`, `DontUse` |
|
||||||
|
| `autoTime` | `<AutoTime>` | `CurrentOrLast`, `Current`, `Last` |
|
||||||
|
| `usePostingMode` | `<UsePostingMode>` | `Auto`, `Postings`, `Movements` |
|
||||||
|
| `repostOnWrite` | `<RepostOnWrite>` | `true` / `false` |
|
||||||
|
| `autoURL` | `<AutoURL>` | `true` / `false` |
|
||||||
|
| `autoFillCheck` | `<AutoFillCheck>` | `true` / `false` |
|
||||||
|
| `customizable` | `<Customizable>` | `true` / `false` |
|
||||||
|
| `enterKeyBehavior` | `<EnterKeyBehavior>` | `DefaultButton`, `NewLine` |
|
||||||
|
| `verticalScroll` | `<VerticalScroll>` | `useIfNecessary`, `Auto`, `AlwaysShow`, `Never` |
|
||||||
|
| `width` | `<Width>` | число |
|
||||||
|
| `height` | `<Height>` | число |
|
||||||
|
| `group` | `<Group>` | `Vertical`, `Horizontal`, `AlwaysHorizontal`, `AlwaysVertical` |
|
||||||
|
| `useForFoldersAndItems` | `<UseForFoldersAndItems>` | `Folders`, `Items`, `FoldersAndItems` |
|
||||||
|
|
||||||
|
Нераспознанные ключи преобразуются с автоматическим PascalCase (первая буква в верхний регистр).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Events — обработчики событий формы
|
||||||
|
|
||||||
|
```json
|
||||||
|
"events": {
|
||||||
|
"OnCreateAtServer": "ПриСозданииНаСервере",
|
||||||
|
"OnOpen": "ПриОткрытии"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ключ — имя события, значение — имя процедуры-обработчика.
|
||||||
|
|
||||||
|
### Доступные события
|
||||||
|
|
||||||
|
| Событие | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `OnCreateAtServer` | Создание формы на сервере |
|
||||||
|
| `OnOpen` | Открытие формы |
|
||||||
|
| `BeforeClose` | Перед закрытием |
|
||||||
|
| `OnClose` | При закрытии |
|
||||||
|
| `BeforeWrite` | Перед записью |
|
||||||
|
| `BeforeWriteAtServer` | Перед записью на сервере |
|
||||||
|
| `OnWriteAtServer` | При записи на сервере |
|
||||||
|
| `AfterWriteAtServer` | После записи на сервере |
|
||||||
|
| `AfterWrite` | После записи |
|
||||||
|
| `OnReadAtServer` | При чтении объекта |
|
||||||
|
| `NotificationProcessing` | Обработка оповещений |
|
||||||
|
| `ChoiceProcessing` | Обработка выбора |
|
||||||
|
| `FillCheckProcessingAtServer` | Проверка заполнения |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Elements — дерево UI-элементов
|
||||||
|
|
||||||
|
Массив объектов. Тип элемента определяется ключом-идентификатором.
|
||||||
|
|
||||||
|
### 4.1. Общие свойства всех элементов
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `name` | string | Имя элемента (по умолчанию — из значения ключа типа) |
|
||||||
|
| `title` | string | Заголовок |
|
||||||
|
| `hidden` | bool | `true` → `<Visible>false</Visible>` |
|
||||||
|
| `disabled` | bool | `true` → `<Enabled>false</Enabled>` |
|
||||||
|
| `readOnly` | bool | `true` → `<ReadOnly>true</ReadOnly>` |
|
||||||
|
| `on` | string[] | Массив имён событий |
|
||||||
|
| `handlers` | object | Явные имена обработчиков: `{"OnChange": "МойОбработчик"}` |
|
||||||
|
|
||||||
|
### 4.2. Автоименование обработчиков
|
||||||
|
|
||||||
|
При указании `"on"` без `"handlers"` имя обработчика генерируется автоматически:
|
||||||
|
|
||||||
|
```
|
||||||
|
<ИмяЭлемента><РусскийСуффикс>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Событие | Суффикс |
|
||||||
|
|---------|---------|
|
||||||
|
| `OnChange` | `ПриИзменении` |
|
||||||
|
| `StartChoice` | `НачалоВыбора` |
|
||||||
|
| `ChoiceProcessing` | `ОбработкаВыбора` |
|
||||||
|
| `AutoComplete` | `АвтоПодбор` |
|
||||||
|
| `Clearing` | `Очистка` |
|
||||||
|
| `Opening` | `Открытие` |
|
||||||
|
| `Click` | `Нажатие` |
|
||||||
|
| `OnActivateRow` | `ПриАктивизацииСтроки` |
|
||||||
|
| `BeforeAddRow` | `ПередНачаломДобавления` |
|
||||||
|
| `BeforeDeleteRow` | `ПередУдалением` |
|
||||||
|
| `BeforeRowChange` | `ПередНачаломИзменения` |
|
||||||
|
| `OnStartEdit` | `ПриНачалеРедактирования` |
|
||||||
|
| `OnEndEdit` | `ПриОкончанииРедактирования` |
|
||||||
|
| `Selection` | `ВыборСтроки` |
|
||||||
|
| `OnCurrentPageChange` | `ПриСменеСтраницы` |
|
||||||
|
| `TextEditEnd` | `ОкончаниеВводаТекста` |
|
||||||
|
|
||||||
|
Пример: элемент `Контрагент` + событие `OnChange` → обработчик `КонтрагентПриИзменении`.
|
||||||
|
|
||||||
|
### 4.3. Типы элементов
|
||||||
|
|
||||||
|
#### group — UsualGroup
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "group": "horizontal", "name": "ГруппаШапка", "children": [ ... ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `group` | string | Ориентация: `horizontal`, `vertical`, `alwaysHorizontal`, `alwaysVertical`, `collapsible` |
|
||||||
|
| `children` | array | Вложенные элементы |
|
||||||
|
| `showTitle` | bool | Показывать заголовок группы |
|
||||||
|
| `representation` | string | `none`, `normal`, `weak`, `strong` |
|
||||||
|
| `united` | bool | Объединение |
|
||||||
|
|
||||||
|
#### input — InputField
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "input": "Организация", "path": "Объект.Организация", "on": ["OnChange"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `path` | string | DataPath |
|
||||||
|
| `multiLine` | bool | Многострочный режим |
|
||||||
|
| `passwordMode` | bool | Режим пароля |
|
||||||
|
| `titleLocation` | string | `none`, `left`, `right`, `top`, `bottom` |
|
||||||
|
| `choiceButton` | bool | Показывать кнопку выбора |
|
||||||
|
| `clearButton` | bool | Показывать кнопку очистки |
|
||||||
|
| `spinButton` | bool | Показывать кнопку прокрутки |
|
||||||
|
| `dropListButton` | bool | Показывать кнопку раскрытия |
|
||||||
|
| `markIncomplete` | bool | Автопометка незаполненных |
|
||||||
|
| `skipOnInput` | bool | Пропускать при вводе |
|
||||||
|
| `inputHint` | string | Подсказка ввода (placeholder) |
|
||||||
|
| `width` | int | Ширина |
|
||||||
|
| `height` | int | Высота |
|
||||||
|
| `horizontalStretch` | bool | Растягивание по горизонтали |
|
||||||
|
| `verticalStretch` | bool | Растягивание по вертикали |
|
||||||
|
| `autoMaxWidth` | bool | Автомаксимальная ширина |
|
||||||
|
| `autoMaxHeight` | bool | Автомаксимальная высота |
|
||||||
|
|
||||||
|
#### check — CheckBoxField
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "check": "ФлагАктивности", "path": "Активен", "on": ["OnChange"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `path` | string | DataPath |
|
||||||
|
| `titleLocation` | string | Расположение заголовка |
|
||||||
|
|
||||||
|
#### label — LabelDecoration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "label": "Подсказка", "title": "Выберите параметры", "hyperlink": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `title` | string | Текст надписи |
|
||||||
|
| `hyperlink` | bool | Режим гиперссылки |
|
||||||
|
| `width` | int | Ширина |
|
||||||
|
| `height` | int | Высота |
|
||||||
|
| `autoMaxWidth` | bool | Автомаксимальная ширина |
|
||||||
|
| `autoMaxHeight` | bool | Автомаксимальная высота |
|
||||||
|
|
||||||
|
#### labelField — LabelField
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "labelField": "СтатусОбработки", "path": "Статус" }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `path` | string | DataPath |
|
||||||
|
| `hyperlink` | bool | Режим гиперссылки |
|
||||||
|
|
||||||
|
#### table — Table
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"table": "Товары", "path": "Объект.Товары",
|
||||||
|
"columns": [
|
||||||
|
{ "input": "Номенклатура", "path": "Объект.Товары.Номенклатура" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `path` | string | DataPath |
|
||||||
|
| `columns` | array | Колонки (элементы input/check/labelField/picField) |
|
||||||
|
| `representation` | string | `List`, `Tree`, `HierarchicalList` |
|
||||||
|
| `changeRowSet` | bool | Разрешить добавление/удаление строк |
|
||||||
|
| `changeRowOrder` | bool | Разрешить перемещение строк |
|
||||||
|
| `height` | int | Высота в строках таблицы |
|
||||||
|
| `header` | bool | Показывать шапку |
|
||||||
|
| `footer` | bool | Показывать подвал |
|
||||||
|
| `commandBarLocation` | string | `None`, `Top`, `Bottom`, `Auto` |
|
||||||
|
| `searchStringLocation` | string | `None`, `Top`, `Bottom`, `CommandBar`, `Auto` |
|
||||||
|
|
||||||
|
#### pages / page — Pages / Page
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pages": "Страницы", "children": [
|
||||||
|
{ "page": "Основное", "children": [ ... ] },
|
||||||
|
{ "page": "Дополнительно", "children": [ ... ] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Page поддерживает `group` для задания ориентации содержимого и `children` для вложенных элементов.
|
||||||
|
|
||||||
|
Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `TabsOnBottom`, `TabsOnLeft`, `TabsOnRight`.
|
||||||
|
|
||||||
|
#### button — Button
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "button": "Загрузить", "command": "Загрузить", "defaultButton": true }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `command` | string | Имя команды формы (→ `Form.Command.<name>`) |
|
||||||
|
| `stdCommand` | string | Стандартная команда (→ `Form.StandardCommand.<name>`) |
|
||||||
|
| `type` | string | `usual`, `hyperlink`, `commandBar` |
|
||||||
|
| `defaultButton` | bool | Кнопка по умолчанию |
|
||||||
|
| `picture` | string | Ссылка на картинку (`StdPicture.Name`) |
|
||||||
|
| `representation` | string | `Auto`, `Picture`, `Text`, `PictureAndText` |
|
||||||
|
| `locationInCommandBar` | string | `InCommandBar`, `InAdditionalSubmenu` |
|
||||||
|
|
||||||
|
#### picture — PictureDecoration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "picture": "Логотип", "src": "CommonPicture.Логотип" }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `src` или `picture` (как свойство) | string | Ссылка на картинку |
|
||||||
|
| `hyperlink` | bool | Режим гиперссылки |
|
||||||
|
| `width` | int | Ширина |
|
||||||
|
| `height` | int | Высота |
|
||||||
|
|
||||||
|
#### picField — PictureField
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "picField": "Фото", "path": "Фотография" }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### calendar — CalendarField
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "calendar": "Дата", "path": "ДатаОтчета" }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### cmdBar — CommandBar
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "cmdBar": "КоманднаяПанель", "children": [ ... ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### popup — Popup
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "popup": "Печать", "picture": "StdPicture.Print", "children": [ ... ] }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Attributes — реквизиты формы
|
||||||
|
|
||||||
|
```json
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "DocumentObject.Реализация", "main": true },
|
||||||
|
{ "name": "Итого", "type": "decimal(15,2)" },
|
||||||
|
{ "name": "Таблица", "type": "ValueTable", "columns": [
|
||||||
|
{ "name": "Номенклатура", "type": "CatalogRef.Номенклатура" },
|
||||||
|
{ "name": "Количество", "type": "decimal(10,3)" }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `name` | string | Имя реквизита (обязательно) |
|
||||||
|
| `type` | string | Тип (shorthand) |
|
||||||
|
| `main` | bool | Основной реквизит формы |
|
||||||
|
| `title` | string | Заголовок |
|
||||||
|
| `savedData` | bool | Сохраняемые данные |
|
||||||
|
| `fillChecking` | string | `Show`, `DontShow` |
|
||||||
|
| `columns` | array | Колонки для ValueTable/ValueTree |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Parameters — параметры формы
|
||||||
|
|
||||||
|
```json
|
||||||
|
"parameters": [
|
||||||
|
{ "name": "Ключ", "type": "DocumentRef.Реализация", "key": true },
|
||||||
|
{ "name": "Основание", "type": "DocumentRef.Реализация" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `name` | string | Имя параметра (обязательно) |
|
||||||
|
| `type` | string | Тип (shorthand) |
|
||||||
|
| `key` | bool | Ключевой параметр |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Commands — команды формы
|
||||||
|
|
||||||
|
```json
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Печать", "action": "ПечатьОбработка", "shortcut": "Ctrl+P" },
|
||||||
|
{ "name": "Обновить", "action": "ОбновитьОбработка", "picture": "StdPicture.Refresh" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------|-----|----------|
|
||||||
|
| `name` | string | Имя команды (обязательно) |
|
||||||
|
| `action` | string | Имя процедуры-обработчика |
|
||||||
|
| `title` | string | Заголовок |
|
||||||
|
| `shortcut` | string | Клавиатурное сочетание |
|
||||||
|
| `picture` | string | Ссылка на картинку |
|
||||||
|
| `representation` | string | `Auto`, `Picture`, `Text`, `PictureAndText` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Система типов (shorthand)
|
||||||
|
|
||||||
|
### Примитивные типы
|
||||||
|
|
||||||
|
| DSL | XML |
|
||||||
|
|-----|-----|
|
||||||
|
| `"string"` | `xs:string` (неограниченная) |
|
||||||
|
| `"string(100)"` | `xs:string` + Length=100 |
|
||||||
|
| `"decimal(15,2)"` | `xs:decimal` + Digits=15, FractionDigits=2, AllowedSign=Any |
|
||||||
|
| `"decimal(10,0,nonneg)"` | `xs:decimal` + AllowedSign=Nonnegative |
|
||||||
|
| `"boolean"` | `xs:boolean` |
|
||||||
|
| `"date"` | `xs:dateTime` + DateFractions=Date |
|
||||||
|
| `"dateTime"` | `xs:dateTime` + DateFractions=DateTime |
|
||||||
|
| `"time"` | `xs:dateTime` + DateFractions=Time |
|
||||||
|
|
||||||
|
### Ссылочные типы
|
||||||
|
|
||||||
|
| DSL | XML |
|
||||||
|
|-----|-----|
|
||||||
|
| `"CatalogRef.Организации"` | `cfg:CatalogRef.Организации` |
|
||||||
|
| `"DocumentObject.Реализация"` | `cfg:DocumentObject.Реализация` |
|
||||||
|
| `"EnumRef.СтавкиНДС"` | `cfg:EnumRef.СтавкиНДС` |
|
||||||
|
| `"DataProcessorObject.ЗагрузкаДанных"` | `cfg:DataProcessorObject.ЗагрузкаДанных` |
|
||||||
|
|
||||||
|
### Платформенные типы
|
||||||
|
|
||||||
|
| DSL | XML |
|
||||||
|
|-----|-----|
|
||||||
|
| `"ValueTable"` | `v8:ValueTable` |
|
||||||
|
| `"ValueTree"` | `v8:ValueTree` |
|
||||||
|
| `"ValueList"` | `v8:ValueListType` |
|
||||||
|
| `"FormattedString"` | `v8ui:FormattedString` |
|
||||||
|
| `"Picture"` | `v8ui:Picture` |
|
||||||
|
| `"DynamicList"` | `cfg:DynamicList` |
|
||||||
|
|
||||||
|
### Составные типы
|
||||||
|
|
||||||
|
Разделитель `" | "`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"type": "CatalogRef.Организации | CatalogRef.ИндивидуальныеПредприниматели"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Автогенерация
|
||||||
|
|
||||||
|
### Companion-элементы
|
||||||
|
|
||||||
|
Для каждого элемента автоматически создаются служебные вложенные элементы:
|
||||||
|
|
||||||
|
| Тип элемента | Companions |
|
||||||
|
|---|---|
|
||||||
|
| UsualGroup | ExtendedTooltip |
|
||||||
|
| InputField | ContextMenu, ExtendedTooltip |
|
||||||
|
| CheckBoxField | ContextMenu, ExtendedTooltip |
|
||||||
|
| LabelDecoration | ContextMenu, ExtendedTooltip |
|
||||||
|
| LabelField | ContextMenu, ExtendedTooltip |
|
||||||
|
| PictureDecoration | ContextMenu, ExtendedTooltip |
|
||||||
|
| PictureField | ContextMenu, ExtendedTooltip |
|
||||||
|
| CalendarField | ContextMenu, ExtendedTooltip |
|
||||||
|
| Table | ContextMenu, AutoCommandBar, SearchStringAddition, ViewStatusAddition, SearchControlAddition |
|
||||||
|
| Pages | ExtendedTooltip |
|
||||||
|
| Page | ExtendedTooltip |
|
||||||
|
| Button | ExtendedTooltip |
|
||||||
|
|
||||||
|
Именование: `<name>КонтекстноеМеню`, `<name>РасширеннаяПодсказка`, `<name>КоманднаяПанель`, `<name>СтрокаПоиска`, `<name>СостояниеПросмотра`, `<name>УправлениеПоиском`.
|
||||||
|
|
||||||
|
### ID
|
||||||
|
|
||||||
|
Последовательная нумерация начиная с 1. `AutoCommandBar` формы всегда имеет `id="-1"`.
|
||||||
|
|
||||||
|
### Namespace
|
||||||
|
|
||||||
|
Все 17 namespace-деклараций добавляются автоматически (version="2.17").
|
||||||
|
|
||||||
|
### Кодировка
|
||||||
|
|
||||||
|
UTF-8 с BOM (как в файлах конфигурации 1С).
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
# Управляемые формы (Form)
|
||||||
|
|
||||||
|
Навыки группы `/form-*` позволяют анализировать, генерировать и валидировать управляемые формы 1С:Предприятия 8.3 из XML-исходников, не читая тысячи строк XML.
|
||||||
|
|
||||||
|
## Навыки
|
||||||
|
|
||||||
|
| Навык | Параметры | Описание |
|
||||||
|
|-------|-----------|----------|
|
||||||
|
| `/form-info` | `<FormPath>` | Компактная сводка: дерево элементов, реквизиты, команды, события |
|
||||||
|
| `/form-compile` | `<JsonPath> <OutputPath>` | Генерация Form.xml из компактного JSON-определения |
|
||||||
|
| `/form-validate` | `<FormPath>` | Валидация: уникальность ID, companions, DataPath, команды |
|
||||||
|
| `/form-add` | `<FormPath> <JsonPath>` | Добавление элементов, реквизитов, команд в существующую форму |
|
||||||
|
| `/form-patterns` | (без параметров) | Справочник паттернов: архетипы, конвенции именования, продвинутые приёмы |
|
||||||
|
|
||||||
|
## Сценарии использования
|
||||||
|
|
||||||
|
### Анализ формы перед модификацией
|
||||||
|
|
||||||
|
Файлы Form.xml содержат от сотен до десятков тысяч строк XML. 80% объёма — визуальный шум (цвета, шрифты, размеры, автогенерированные подсказки). `/form-info` извлекает суть.
|
||||||
|
|
||||||
|
```
|
||||||
|
> Покажи структуру формы документа РеализацияТоваровУслуг
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude найдёт Form.xml и вызовет `/form-info`. Результат — компактная сводка (40–100 строк вместо тысяч):
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Form: ФормаДокумента — "Реализация товаров и услуг" (Documents.РеализацияТоваровУслуг) ===
|
||||||
|
|
||||||
|
Properties: AutoTitle=false, CommandBarLocation=None
|
||||||
|
|
||||||
|
Events:
|
||||||
|
OnCreateAtServer -> ПриСозданииНаСервере
|
||||||
|
OnOpen -> ПриОткрытии
|
||||||
|
|
||||||
|
Elements:
|
||||||
|
├─ [Group:AH] ГруппаШапка
|
||||||
|
│ ├─ [Input] Организация -> Объект.Организация {OnChange}
|
||||||
|
│ └─ [Input] Контрагент -> Объект.Контрагент {OnChange, StartChoice}
|
||||||
|
├─ [Table] Товары -> Объект.Товары
|
||||||
|
│ ├─ [Input] Номенклатура -> Объект.Товары.Номенклатура {OnChange}
|
||||||
|
│ └─ [Input] Сумма -> Объект.Товары.Сумма [ro]
|
||||||
|
└─ [Pages] Страницы
|
||||||
|
├─ [Page] Основное (5 items)
|
||||||
|
└─ [Page] Печать (2 items)
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
*Объект: DocumentObject.РеализацияТоваровУслуг (main)
|
||||||
|
Валюта: CatalogRef.Валюты
|
||||||
|
...
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
Печать -> ПечатьДокумента [Ctrl+P]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Добавление элементов в существующую форму
|
||||||
|
|
||||||
|
`/form-add` добавляет элементы, реквизиты и команды в существующий Form.xml. Автоматически назначает ID, генерирует companion-элементы и обработчики событий.
|
||||||
|
|
||||||
|
```
|
||||||
|
> Добавь поле "Склад" в шапку формы после "Контрагент"
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/form-info` для анализа структуры, создаст JSON и вызовет `/form-add`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"into": "ГруппаШапка",
|
||||||
|
"after": "Контрагент",
|
||||||
|
"elements": [
|
||||||
|
{ "input": "Склад", "path": "Объект.Склад", "on": ["OnChange"] }
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Склад", "type": "CatalogRef.Склады" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Позиционирование: `into` указывает группу-контейнер, `after` — элемент, после которого вставлять. Оба опциональны (по умолчанию — в конец корневого ChildItems).
|
||||||
|
|
||||||
|
Один вызов может содержать элементы, реквизиты и команды одновременно. Группы поддерживают `children` для вложенных элементов.
|
||||||
|
|
||||||
|
### Поиск обработчиков и привязок
|
||||||
|
|
||||||
|
Из сводки видно, какие события подключены к каким элементам:
|
||||||
|
|
||||||
|
```
|
||||||
|
> Какие обработчики срабатывают при изменении контрагента?
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/form-info` и найдёт: `[Input] Контрагент -> Объект.Контрагент {OnChange, StartChoice}` — далее откроет модуль формы и найдёт процедуры по именам событий.
|
||||||
|
|
||||||
|
### Точечное погружение в детали
|
||||||
|
|
||||||
|
Сводка — это карта. Для деталей по конкретному элементу достаточно grep по имени из сводки:
|
||||||
|
|
||||||
|
```
|
||||||
|
> Покажи все свойства элемента ДоговорКонтрагента
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude сделает grep по Form.xml и найдёт полный XML-блок с ChoiceParameters, ChoiceFoldersAndItems, стилями и прочими свойствами.
|
||||||
|
|
||||||
|
## Чтение вывода
|
||||||
|
|
||||||
|
### Дерево элементов
|
||||||
|
|
||||||
|
| Обозначение | Элемент |
|
||||||
|
|---|---|
|
||||||
|
| `[Group:V]` `[Group:H]` `[Group:AH]` `[Group:AV]` | Группа (Vertical / Horizontal / AlwaysHorizontal / AlwaysVertical) |
|
||||||
|
| `[Input]` | Поле ввода |
|
||||||
|
| `[Check]` | Флажок |
|
||||||
|
| `[Label]` | Декоративная надпись |
|
||||||
|
| `[LabelField]` | Поле надписи (привязанное к данным) |
|
||||||
|
| `[Table]` | Таблица |
|
||||||
|
| `[Pages]` / `[Page]` | Вкладки (страницы показывают количество элементов) |
|
||||||
|
| `[Button]` | Кнопка |
|
||||||
|
| `[CmdBar]` | Командная панель |
|
||||||
|
| `[Popup]` | Выпадающее меню |
|
||||||
|
| `[Picture]` | Декоративная картинка |
|
||||||
|
| `[PicField]` | Поле картинки |
|
||||||
|
| `[Calendar]` | Календарь |
|
||||||
|
|
||||||
|
### Флаги
|
||||||
|
|
||||||
|
Показываются только при отклонении от умолчания:
|
||||||
|
- `[visible:false]` — элемент скрыт (Visible=false)
|
||||||
|
- `[enabled:false]` — элемент недоступен (Enabled=false)
|
||||||
|
- `[ro]` — только чтение (ReadOnly=true)
|
||||||
|
- `,collapse` — сворачиваемая группа (Behavior=Collapsible)
|
||||||
|
|
||||||
|
### Привязки и события
|
||||||
|
|
||||||
|
- `-> Объект.Поле` — DataPath (привязка к данным)
|
||||||
|
- `-> Имя [cmd]` — привязка к команде формы
|
||||||
|
- `-> Close [std]` — привязка к стандартной команде
|
||||||
|
- `{OnChange, StartChoice}` — имена подключённых событий
|
||||||
|
- `[title:Текст]` — заголовок, если отличается от имени элемента
|
||||||
|
|
||||||
|
### Реквизиты
|
||||||
|
|
||||||
|
- `*Объект: DocumentObject.Реализация (main)` — основной реквизит формы
|
||||||
|
- `Таблица: ValueTable [Кол1: тип, Кол2: тип]` — таблица значений с колонками
|
||||||
|
- `Список: DynamicList -> Catalog.Пользователи` — динамический список с основной таблицей
|
||||||
|
|
||||||
|
## Генерация формы с нуля
|
||||||
|
|
||||||
|
`/form-compile` принимает компактное JSON-определение (20–50 строк) и генерирует полный Form.xml (100–500+ строк) с namespace-декларациями, companion-элементами и последовательными ID.
|
||||||
|
|
||||||
|
```
|
||||||
|
> Создай форму загрузки данных с полями Организация, Контрагент, таблицей Товары и кнопкой Загрузить
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude создаст JSON-определение и вызовет `/form-compile`. Пример JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"properties": { "autoTitle": false },
|
||||||
|
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
|
||||||
|
"elements": [
|
||||||
|
{ "group": "horizontal", "name": "Шапка", "children": [
|
||||||
|
{ "input": "Организация", "path": "Объект.Организация", "on": ["OnChange"] },
|
||||||
|
{ "input": "Контрагент", "path": "Объект.Контрагент" }
|
||||||
|
]},
|
||||||
|
{ "table": "Товары", "path": "Объект.Товары", "columns": [
|
||||||
|
{ "input": "Номенклатура", "path": "Объект.Товары.Номенклатура" },
|
||||||
|
{ "input": "Сумма", "path": "Объект.Товары.Сумма", "readOnly": true }
|
||||||
|
]},
|
||||||
|
{ "button": "Загрузить", "command": "Загрузить" }
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "DataProcessorObject.ЗагрузкаДанных", "main": true }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Загрузить", "action": "ЗагрузитьОбработка" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Верификация результата
|
||||||
|
|
||||||
|
После компиляции используйте `/form-validate` и `/form-info` для проверки:
|
||||||
|
|
||||||
|
```
|
||||||
|
> /form-validate src/МояОбработка/Forms/Форма/Ext/Form.xml
|
||||||
|
> /form-info src/МояОбработка/Forms/Форма/Ext/Form.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
`/form-validate` проверит структурную корректность (ID, companions, ссылки), `/form-info` покажет результат в виде компактной сводки.
|
||||||
|
|
||||||
|
## Примеры слеш-команд
|
||||||
|
|
||||||
|
```
|
||||||
|
> /form-info upload/acc_8.3.24/Documents/РеализацияТоваровУслуг/Forms/ФормаДокумента/Ext/Form.xml
|
||||||
|
> /form-info src/МояОбработка/Forms/Форма/Ext/Form.xml
|
||||||
|
> /form-compile src/form.json src/МояОбработка/Forms/Форма/Ext/Form.xml
|
||||||
|
> /form-add src/МояОбработка/Forms/Форма/Ext/Form.xml src/additions.json
|
||||||
|
> /form-validate src/МояОбработка/Forms/Форма/Ext/Form.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Связь с EPF-навыками
|
||||||
|
|
||||||
|
Навыки `/form-*` работают с формами из любых источников — конфигурации и внешних обработок. При работе с обработками:
|
||||||
|
|
||||||
|
1. `/epf-add-form` — создать форму (каркас)
|
||||||
|
2. `/form-compile` — сгенерировать Form.xml из JSON-определения
|
||||||
|
3. `/form-add` — добавить элементы/реквизиты/команды в существующую форму
|
||||||
|
4. `/form-validate` — проверить корректность
|
||||||
|
5. `/form-info` — проанализировать результат
|
||||||
|
6. `/epf-build` — собрать EPF
|
||||||
|
|
||||||
|
## Спецификации
|
||||||
|
|
||||||
|
- [Управляемая форма](1c-form-spec.md) — Form.xml, элементы, команды, реквизиты, система типов
|
||||||
|
- [Form DSL](form-dsl-spec.md) — JSON-формат описания формы для `/form-compile` и `/form-add`
|
||||||
|
- [Паттерны компоновки](form-patterns.md) — типовые архетипы форм, конвенции именования, примеры DSL
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
# Паттерны компоновки управляемых форм
|
||||||
|
|
||||||
|
Рекомендации по дизайну форм, извлечённые из типовых конфигураций 1С. Используйте при создании форм через `/form-compile`, когда требования пользователя не детализируют расположение элементов.
|
||||||
|
|
||||||
|
## Архетипы форм
|
||||||
|
|
||||||
|
### Форма документа
|
||||||
|
|
||||||
|
```
|
||||||
|
Шапка (horizontal, 2 колонки)
|
||||||
|
├─ Левая (vertical): НомерДата (H: Номер + Дата "от"), Контрагент, Договор
|
||||||
|
├─ Правая (vertical): Организация, Подразделение, ЦеныИВалюта (надпись-ссылка)
|
||||||
|
Страницы (pages)
|
||||||
|
├─ Товары: таблица Объект.Товары
|
||||||
|
├─ Услуги: таблица Объект.Услуги (опционально)
|
||||||
|
└─ Дополнительно: прочие реквизиты
|
||||||
|
Подвал (vertical)
|
||||||
|
├─ Итоги (horizontal): Всего, НДС, Скидка
|
||||||
|
└─ КомментарийОтветственный (horizontal): Комментарий + Ответственный
|
||||||
|
```
|
||||||
|
|
||||||
|
**Типичные события:** OnCreateAtServer, OnReadAtServer, OnOpen, BeforeWriteAtServer, AfterWriteAtServer, AfterWrite, NotificationProcessing
|
||||||
|
|
||||||
|
**Свойства:** autoTitle=false, командная панель со стандартными + глобальными командами
|
||||||
|
|
||||||
|
### Форма обработки (DataProcessor)
|
||||||
|
|
||||||
|
```
|
||||||
|
Параметры (vertical)
|
||||||
|
├─ Группа полей ввода (Организация, Период, режимы работы)
|
||||||
|
├─ Информационные надписи (label, hyperlink)
|
||||||
|
Рабочая область
|
||||||
|
├─ Таблица данных или Pages с вкладками
|
||||||
|
Кнопки действий
|
||||||
|
├─ Выполнить / Применить (defaultButton)
|
||||||
|
├─ Закрыть (stdCommand: Close)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Типичные события:** OnCreateAtServer, OnOpen, NotificationProcessing
|
||||||
|
|
||||||
|
**Свойства:** windowOpeningMode=LockOwnerWindow (если диалог), autoTitle=false
|
||||||
|
|
||||||
|
### Форма списка
|
||||||
|
|
||||||
|
```
|
||||||
|
Отборы (group: alwaysHorizontal)
|
||||||
|
├─ ГруппаОтбор[Поле] (H): Флажок + Поле ввода (для каждого фильтра)
|
||||||
|
Список (table, DynamicList)
|
||||||
|
├─ Колонки: labelField (не input — данные только для чтения)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Типичные события:** OnCreateAtServer, OnOpen, NotificationProcessing, OnLoadDataFromSettingsAtServer
|
||||||
|
|
||||||
|
**Свойства:** autoSaveDataInSettings=Use (запомнить отборы)
|
||||||
|
|
||||||
|
**Фильтры:** пара реквизитов на каждый фильтр — `Отбор[Поле]` (значение) + `Отбор[Поле]Использование` (boolean, флажок вкл/выкл)
|
||||||
|
|
||||||
|
### Форма элемента справочника
|
||||||
|
|
||||||
|
**Простая:**
|
||||||
|
```
|
||||||
|
ГруппаРеквизитов (horizontal)
|
||||||
|
├─ Наименование -> Объект.Description
|
||||||
|
└─ Код -> Объект.Code (если нужен)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Сложная:**
|
||||||
|
```
|
||||||
|
Главное (vertical)
|
||||||
|
├─ Наименование -> Объект.Description
|
||||||
|
├─ Параметры (horizontal, 2 колонки)
|
||||||
|
│ ├─ Левая: основные реквизиты
|
||||||
|
│ └─ Правая: дополнительные реквизиты
|
||||||
|
└─ КонтактныеДанные / Дополнительно (vertical)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Типичные события:** OnCreateAtServer, OnReadAtServer, BeforeWriteAtServer, NotificationProcessing
|
||||||
|
|
||||||
|
### Мастер (Wizard)
|
||||||
|
|
||||||
|
```
|
||||||
|
Страницы (pages, OnCurrentPageChange)
|
||||||
|
├─ Шаг1: описание + параметры
|
||||||
|
├─ Шаг2: основная работа
|
||||||
|
└─ Шаг3: результат
|
||||||
|
Кнопки (horizontal)
|
||||||
|
├─ Назад (command), Далее (command, defaultButton), Выполнить (command)
|
||||||
|
└─ Закрыть (stdCommand: Close)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Свойства:** windowOpeningMode=LockOwnerWindow
|
||||||
|
|
||||||
|
## Конвенции именования
|
||||||
|
|
||||||
|
### Группы
|
||||||
|
|
||||||
|
| Назначение | Имя | Тип |
|
||||||
|
|-----------|-----|-----|
|
||||||
|
| Шапка | `ГруппаШапка` | horizontal |
|
||||||
|
| Левая колонка | `ГруппаШапкаЛевая` | vertical |
|
||||||
|
| Правая колонка | `ГруппаШапкаПравая` | vertical |
|
||||||
|
| Номер+Дата | `ГруппаНомерДата` | horizontal |
|
||||||
|
| Подвал | `ГруппаПодвал` | vertical |
|
||||||
|
| Итоги | `ГруппаИтоги` | horizontal |
|
||||||
|
| Кнопки | `ГруппаКнопок` | horizontal |
|
||||||
|
| Страницы | `ГруппаСтраницы` / `Страницы` | pages |
|
||||||
|
| Предупреждение | `ГруппаПредупреждение` | horizontal, visible:false |
|
||||||
|
| Доп. секция | `ГруппаДополнительно` / `ГруппаПрочее` | vertical, collapse |
|
||||||
|
|
||||||
|
### Элементы
|
||||||
|
|
||||||
|
| Назначение | Имя | Суффикс |
|
||||||
|
|-----------|-----|---------|
|
||||||
|
| Поле в таблице | `[Таблица][Поле]` | — |
|
||||||
|
| Итог | `Итоги[Поле]` | — |
|
||||||
|
| Надпись-ссылка | `[Поле]Надпись` | — |
|
||||||
|
| Фильтр | `Отбор[Поле]` | — |
|
||||||
|
| Флажок фильтра | `Отбор[Поле]Использование` | — |
|
||||||
|
| Кнопка команды | `[Команда]Кнопка` | — |
|
||||||
|
| Баннер-картинка | `[Баннер]Картинка` | — |
|
||||||
|
| Баннер-надпись | `[Баннер]Надпись` | — |
|
||||||
|
| Подменю | `Подменю[Действие]` | — |
|
||||||
|
|
||||||
|
### Обработчики событий
|
||||||
|
|
||||||
|
Имя обработчика = имя элемента + суффикс события на русском:
|
||||||
|
|
||||||
|
| Событие | Суффикс | Пример |
|
||||||
|
|---------|---------|--------|
|
||||||
|
| OnChange | ПриИзменении | `ОрганизацияПриИзменении` |
|
||||||
|
| StartChoice | НачалоВыбора | `КонтрагентНачалоВыбора` |
|
||||||
|
| Click | Нажатие | `ЦеныИВалютаНажатие` |
|
||||||
|
| OnEditEnd | ПриОкончанииРедактирования | `ТоварыПриОкончанииРедактирования` |
|
||||||
|
| OnStartEdit | ПриНачалеРедактирования | `ТоварыПриНачалеРедактирования` |
|
||||||
|
|
||||||
|
Обработчики формы — стандартные имена: `ПриСозданииНаСервере`, `ПриОткрытии`, `ПередЗакрытием`, `ОбработкаОповещения`.
|
||||||
|
|
||||||
|
## Принципы компоновки
|
||||||
|
|
||||||
|
1. **Порядок чтения.** Сверху вниз, слева направо. Самое важное — вверху.
|
||||||
|
2. **Двухколоночная шапка.** Основные реквизиты слева (контрагент, склад), организационные справа (организация, подразделение).
|
||||||
|
3. **Кнопки действий внизу.** Главная кнопка — `defaultButton: true`. Закрыть — всегда последняя.
|
||||||
|
4. **Таблицы — основная область.** Табличные части занимают большую часть формы, обычно на Pages.
|
||||||
|
5. **Итоги рядом с таблицей.** В подвале, горизонтальная группа, все поля readOnly.
|
||||||
|
6. **Фильтры — отдельная зона.** Над списком, горизонтальная группа (alwaysHorizontal), пара "флажок + поле" на каждый фильтр.
|
||||||
|
7. **Скрытые элементы для состояний.** Баннеры, предупреждения — `visible: false` по умолчанию, показываются программно.
|
||||||
|
8. **Надписи-ссылки для диалогов.** `labelField` с `hyperlink: true` и событием Click — для открытия подформ (ЦеныИВалюта, УчётнаяПолитика).
|
||||||
|
|
||||||
|
## Продвинутые паттерны (ERP)
|
||||||
|
|
||||||
|
Извлечены из конфигурации «Управление предприятием» (ERP 8.3.24). Применяйте в сложных формах.
|
||||||
|
|
||||||
|
### Сворачиваемые группы (Collapsible)
|
||||||
|
|
||||||
|
Для необязательных секций — «Подписи», «Дополнительно», «Прочее». Сворачиваются по умолчанию, экономят место.
|
||||||
|
|
||||||
|
```
|
||||||
|
ГруппаПодписи (vertical, collapse, collapsed)
|
||||||
|
├─ Руководитель -> Объект.Руководитель
|
||||||
|
└─ ГлавныйБухгалтер -> Объект.ГлавныйБухгалтер
|
||||||
|
```
|
||||||
|
|
||||||
|
DSL:
|
||||||
|
```json
|
||||||
|
{ "group": "vertical", "name": "ГруппаПодписи", "title": "Подписи",
|
||||||
|
"behavior": "Collapsible", "collapsed": true, "children": [
|
||||||
|
{ "input": "Руководитель", "path": "Объект.Руководитель" },
|
||||||
|
{ "input": "ГлавныйБухгалтер", "path": "Объект.ГлавныйБухгалтер" }
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Баннер-предупреждение (Status Banner)
|
||||||
|
|
||||||
|
Группа «картинка + надпись» без заголовка, скрыта по умолчанию. Показывается программно при определённых условиях (просрочка, блокировка, информация).
|
||||||
|
|
||||||
|
```
|
||||||
|
ГруппаПредупреждение (horizontal, showTitle:false, visible:false)
|
||||||
|
├─ [Picture] ПредупреждениеКартинка -> StdPicture.Information
|
||||||
|
└─ [Label] ПредупреждениеНадпись (maxWidth:76, textColor:style:ПоясняющийТекст)
|
||||||
|
```
|
||||||
|
|
||||||
|
DSL:
|
||||||
|
```json
|
||||||
|
{ "group": "horizontal", "name": "ГруппаПредупреждение", "showTitle": false,
|
||||||
|
"visible": false, "children": [
|
||||||
|
{ "picture": "ПредупреждениеКартинка" },
|
||||||
|
{ "label": "ПредупреждениеНадпись", "title": "Текст предупреждения",
|
||||||
|
"maxWidth": 76, "autoMaxWidth": false }
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Выпадающее меню в командной панели (Popup)
|
||||||
|
|
||||||
|
Группировка связанных команд (печать, отправка, выгрузка) в одну кнопку-меню с иконкой.
|
||||||
|
|
||||||
|
```
|
||||||
|
[CmdBar] КоманднаяПанель
|
||||||
|
├─ [Popup] ПодменюПечать (picture: StdPicture.Print, representation: Picture)
|
||||||
|
│ ├─ [Button] ПечатьНакладная -> Печать [cmd]
|
||||||
|
│ └─ [Button] ПечатьСчёт -> ПечатьСчёт [cmd]
|
||||||
|
└─ [Popup] ПодменюОтправить (picture: StdPicture.SendByEmail)
|
||||||
|
└─ [Button] ОтправитьПоПочте -> Отправить [cmd]
|
||||||
|
```
|
||||||
|
|
||||||
|
DSL:
|
||||||
|
```json
|
||||||
|
{ "cmdBar": "КоманднаяПанель", "children": [
|
||||||
|
{ "popup": "ПодменюПечать", "title": "Печать",
|
||||||
|
"picture": "StdPicture.Print", "representation": "Picture", "children": [
|
||||||
|
{ "button": "ПечатьНакладная", "command": "Печать" },
|
||||||
|
{ "button": "ПечатьСчёт", "command": "ПечатьСчёт" }
|
||||||
|
]},
|
||||||
|
{ "popup": "ПодменюОтправить", "title": "Отправить",
|
||||||
|
"picture": "StdPicture.SendByEmail", "representation": "Picture", "children": [
|
||||||
|
{ "button": "ОтправитьПоПочте", "command": "Отправить" }
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Форма без стандартной командной панели
|
||||||
|
|
||||||
|
Для модальных диалогов и мастеров — отключение стандартной командной панели, полностью ручное управление кнопками.
|
||||||
|
|
||||||
|
```
|
||||||
|
properties: commandBarLocation=None, windowOpeningMode=LockWholeInterface
|
||||||
|
Содержимое (vertical)
|
||||||
|
├─ ... рабочая область ...
|
||||||
|
ГруппаКнопок (horizontal)
|
||||||
|
├─ Назад (command), Далее (command, defaultButton)
|
||||||
|
└─ Закрыть (stdCommand: Close)
|
||||||
|
```
|
||||||
|
|
||||||
|
DSL:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"properties": { "commandBarLocation": "None", "windowOpeningMode": "LockWholeInterface" },
|
||||||
|
"elements": [
|
||||||
|
{ "group": "vertical", "name": "Содержимое", "children": [ "..." ] },
|
||||||
|
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
|
||||||
|
{ "button": "Назад", "command": "Назад" },
|
||||||
|
{ "button": "Далее", "command": "Далее", "defaultButton": true },
|
||||||
|
{ "button": "Закрыть", "stdCommand": "Close" }
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Надпись-гиперссылка для открытия подформ
|
||||||
|
|
||||||
|
`labelField` с `hyperlink: true` и событием Click — вместо кнопки. Типичный приём для «ЦеныИВалюта», «УчётнаяПолитика» и подобных.
|
||||||
|
|
||||||
|
```
|
||||||
|
[LabelField] ЦеныИВалютаНадпись -> ЦеныИВалюта (hyperlink) {Click}
|
||||||
|
```
|
||||||
|
|
||||||
|
DSL:
|
||||||
|
```json
|
||||||
|
{ "labelField": "ЦеныИВалютаНадпись", "path": "ЦеныИВалюта",
|
||||||
|
"hyperlink": true, "on": ["Click"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Примеры DSL
|
||||||
|
|
||||||
|
### Типичная форма обработки
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "Загрузка данных из CSV",
|
||||||
|
"properties": { "autoTitle": false, "windowOpeningMode": "LockOwnerWindow" },
|
||||||
|
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
|
||||||
|
"elements": [
|
||||||
|
{ "group": "vertical", "name": "ГруппаПараметры", "children": [
|
||||||
|
{ "input": "ФайлЗагрузки", "path": "ФайлЗагрузки", "title": "Файл", "clearButton": true, "horizontalStretch": true, "on": ["StartChoice"] },
|
||||||
|
{ "input": "Кодировка", "path": "Кодировка", "title": "Кодировка" },
|
||||||
|
{ "input": "Разделитель", "path": "Разделитель", "title": "Разделитель колонок" }
|
||||||
|
]},
|
||||||
|
{ "table": "Данные", "path": "Объект.Данные", "on": ["OnStartEdit"], "columns": [
|
||||||
|
{ "input": "ДанныеНомерСтроки", "path": "Объект.Данные.LineNumber", "readOnly": true, "title": "№" },
|
||||||
|
{ "input": "ДанныеНаименование", "path": "Объект.Данные.Наименование" },
|
||||||
|
{ "input": "ДанныеКоличество", "path": "Объект.Данные.Количество", "on": ["OnChange"] },
|
||||||
|
{ "input": "ДанныеСумма", "path": "Объект.Данные.Сумма", "readOnly": true }
|
||||||
|
]},
|
||||||
|
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
|
||||||
|
{ "button": "Загрузить", "command": "Загрузить", "title": "Загрузить из файла", "defaultButton": true },
|
||||||
|
{ "button": "Очистить", "command": "Очистить", "title": "Очистить таблицу" },
|
||||||
|
{ "button": "Закрыть", "stdCommand": "Close" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Объект", "type": "ExternalDataProcessorObject.ЗагрузкаИзCSV", "main": true },
|
||||||
|
{ "name": "ФайлЗагрузки", "type": "string" },
|
||||||
|
{ "name": "Кодировка", "type": "string(20)" },
|
||||||
|
{ "name": "Разделитель", "type": "string(5)" }
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
{ "name": "Загрузить", "action": "ЗагрузитьОбработка" },
|
||||||
|
{ "name": "Очистить", "action": "ОчиститьОбработка" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Типичная форма со списком и фильтрами
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"properties": { "autoTitle": false, "autoSaveDataInSettings": "Use" },
|
||||||
|
"events": {
|
||||||
|
"OnCreateAtServer": "ПриСозданииНаСервере",
|
||||||
|
"NotificationProcessing": "ОбработкаОповещения"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{ "group": "alwaysHorizontal", "name": "ГруппаОтборы", "children": [
|
||||||
|
{ "group": "horizontal", "name": "ГруппаОтборОрганизация", "children": [
|
||||||
|
{ "check": "ОтборОрганизацияИспользование", "path": "ОтборОрганизацияИспользование", "titleLocation": "none", "on": ["OnChange"] },
|
||||||
|
{ "input": "ОтборОрганизация", "path": "ОтборОрганизация", "title": "Организация", "on": ["OnChange"] }
|
||||||
|
]},
|
||||||
|
{ "group": "horizontal", "name": "ГруппаОтборКонтрагент", "children": [
|
||||||
|
{ "check": "ОтборКонтрагентИспользование", "path": "ОтборКонтрагентИспользование", "titleLocation": "none", "on": ["OnChange"] },
|
||||||
|
{ "input": "ОтборКонтрагент", "path": "ОтборКонтрагент", "title": "Контрагент", "on": ["OnChange"] }
|
||||||
|
]}
|
||||||
|
]},
|
||||||
|
{ "table": "Список", "path": "Список", "on": ["Selection", "OnActivateRow"], "columns": [
|
||||||
|
{ "labelField": "СписокДата", "path": "Список.Дата", "title": "Дата" },
|
||||||
|
{ "labelField": "СписокНомер", "path": "Список.Номер", "title": "Номер" },
|
||||||
|
{ "labelField": "СписокКонтрагент", "path": "Список.Контрагент" },
|
||||||
|
{ "labelField": "СписокСумма", "path": "Список.Сумма" }
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{ "name": "Список", "type": "DynamicList", "mainTable": "Document.РеализацияТоваров" },
|
||||||
|
{ "name": "ОтборОрганизация", "type": "CatalogRef.Организации" },
|
||||||
|
{ "name": "ОтборОрганизацияИспользование", "type": "boolean" },
|
||||||
|
{ "name": "ОтборКонтрагент", "type": "CatalogRef.Контрагенты" },
|
||||||
|
{ "name": "ОтборКонтрагентИспользование", "type": "boolean" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user