mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-08 04:30:19 +03:00
Improve form skills: validation, docs, round-trip consistency
form-compile: warn about unknown DSL keys in element definitions, document stdCommand/command button keys and EPF-specific notes. form-validate: check that form-level Title uses multilingual XML, not plain text (which causes XDTO errors at build time). form-add: warn about duplicate element names, clarify after-not-found message when using into+after together. form-info: show Title in header instead of Properties line, display commands as DSL-friendly format (-> Name [cmd], -> Close [std]) instead of raw Form.Command/Form.StandardCommand paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d05199d048
commit
c665800916
@@ -634,7 +634,7 @@ function Insert-IntoContainer($container, $newNode, $afterName, $childIndent) {
|
||||
if ($afterElem) {
|
||||
$refNode = $afterElem.NextSibling
|
||||
} else {
|
||||
Write-Host "[WARN] Element '$afterName' not found in target container, appending at end"
|
||||
Write-Host "[WARN] after='$afterName' not found in target container, appending at end"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,6 +737,21 @@ if ($def.elements -and $def.elements.Count -gt 0) {
|
||||
# Detect indent level
|
||||
$childIndent = Get-ChildIndent $targetCI
|
||||
|
||||
# Check for duplicate element names
|
||||
foreach ($el in $def.elements) {
|
||||
$typeKey = $null
|
||||
foreach ($key in @("group","input","check","label","labelField","table","pages","page","button","picture","picField","calendar","cmdBar","popup")) {
|
||||
if ($el.$key -ne $null) { $typeKey = $key; break }
|
||||
}
|
||||
if ($typeKey) {
|
||||
$elName = Get-ElementName -el $el -typeKey $typeKey
|
||||
$existing = Find-Element $rootCI $elName
|
||||
if ($existing) {
|
||||
Write-Host "[WARN] Element '$elName' already exists in form (id=$($existing.GetAttribute('id')))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Remember starting element ID for companion counting
|
||||
$startElemId = $script:nextElemId
|
||||
|
||||
|
||||
@@ -63,6 +63,13 @@ powershell.exe -NoProfile -File .claude\skills\form-compile\scripts\form-compile
|
||||
- `"readOnly": true` — ReadOnly=true
|
||||
- `"on": ["OnChange", "StartChoice"]` — события с автоименованием обработчиков
|
||||
|
||||
### Свойства кнопок
|
||||
|
||||
- `"command": "ИмяКоманды"` — привязка к команде формы → `Form.Command.ИмяКоманды`
|
||||
- `"stdCommand": "Close"` — привязка к стандартной команде → `Form.StandardCommand.Close`
|
||||
- `"defaultButton": true` — кнопка по умолчанию
|
||||
- `"type": "hyperlink"` — тип кнопки (`"usual"` / `"hyperlink"` / `"commandBar"`)
|
||||
|
||||
### Система типов (shorthand)
|
||||
|
||||
| DSL | XML |
|
||||
@@ -119,3 +126,9 @@ powershell.exe -NoProfile -File .claude\skills\form-compile\scripts\form-compile
|
||||
|
||||
Структура в сводке должна совпадать с определением в JSON.
|
||||
|
||||
## Особенности для внешних обработок (EPF)
|
||||
|
||||
- **Тип главного реквизита**: `ExternalDataProcessorObject.ИмяОбработки` (не `DataProcessorObject`)
|
||||
- **DataPath**: используйте реквизиты формы (`ИмяРеквизита`), а не `Объект.ИмяРеквизита` — у внешних обработок нет реквизитов объекта в метаданных
|
||||
- **Ссылочные типы**: `CatalogRef.XXX`, `DocumentRef.XXX` и т.д. могут не собраться в пустой базе — используйте `string` или базовые типы для автономной сборки
|
||||
|
||||
|
||||
@@ -272,6 +272,47 @@ function Emit-Element {
|
||||
return
|
||||
}
|
||||
|
||||
# Validate known keys — warn about typos and unknown properties
|
||||
$knownKeys = @{
|
||||
# type keys
|
||||
"group"=1;"input"=1;"check"=1;"label"=1;"labelField"=1;"table"=1;"pages"=1;"page"=1
|
||||
"button"=1;"picture"=1;"picField"=1;"calendar"=1;"cmdBar"=1;"popup"=1
|
||||
# naming & binding
|
||||
"name"=1;"path"=1;"title"=1
|
||||
# visibility & state
|
||||
"visible"=1;"hidden"=1;"enabled"=1;"disabled"=1;"readOnly"=1
|
||||
# events
|
||||
"on"=1;"handlers"=1
|
||||
# layout
|
||||
"titleLocation"=1;"representation"=1;"width"=1;"height"=1
|
||||
"horizontalStretch"=1;"verticalStretch"=1;"autoMaxWidth"=1;"autoMaxHeight"=1
|
||||
# input-specific
|
||||
"multiLine"=1;"passwordMode"=1;"choiceButton"=1;"clearButton"=1
|
||||
"spinButton"=1;"dropListButton"=1;"markIncomplete"=1;"skipOnInput"=1;"inputHint"=1
|
||||
# label/hyperlink
|
||||
"hyperlink"=1
|
||||
# group-specific
|
||||
"showTitle"=1;"united"=1
|
||||
# hierarchy
|
||||
"children"=1;"columns"=1
|
||||
# table-specific
|
||||
"changeRowSet"=1;"changeRowOrder"=1;"header"=1;"footer"=1
|
||||
"commandBarLocation"=1;"searchStringLocation"=1
|
||||
# pages-specific
|
||||
"pagesRepresentation"=1
|
||||
# button-specific
|
||||
"type"=1;"command"=1;"stdCommand"=1;"defaultButton"=1;"locationInCommandBar"=1
|
||||
# picture/decoration
|
||||
"src"=1
|
||||
# cmdBar-specific
|
||||
"autofill"=1
|
||||
}
|
||||
foreach ($p in $el.PSObject.Properties) {
|
||||
if (-not $knownKeys.ContainsKey($p.Name)) {
|
||||
Write-Warning "Element '$($el.$typeKey)': unknown key '$($p.Name)' — ignored. Check SKILL.md for valid keys."
|
||||
}
|
||||
}
|
||||
|
||||
$name = Get-ElementName -el $el -typeKey $typeKey
|
||||
$id = New-Id
|
||||
|
||||
|
||||
@@ -42,14 +42,14 @@ powershell.exe -NoProfile -File .claude\skills\form-info\scripts\form-info.ps1 -
|
||||
### Заголовок
|
||||
|
||||
```
|
||||
=== Form: ФормаДокумента (Documents.РеализацияТоваровУслуг) ===
|
||||
=== Form: ФормаДокумента — "Реализация товаров и услуг" (Documents.РеализацияТоваровУслуг) ===
|
||||
```
|
||||
|
||||
Имя формы и контекст объекта определяются из пути к файлу.
|
||||
Имя формы, заголовок (Title) и контекст объекта определяются из пути к файлу и XML.
|
||||
|
||||
### Properties — свойства формы
|
||||
|
||||
Только нестандартные свойства (отличающиеся от умолчания):
|
||||
Только нестандартные свойства (отличающиеся от умолчания). Title показывается в заголовке, не здесь:
|
||||
|
||||
```
|
||||
Properties: AutoTitle=false, WindowOpeningMode=LockOwnerWindow, CommandBarLocation=Bottom
|
||||
@@ -110,7 +110,9 @@ Elements:
|
||||
- `[ro]` — ReadOnly=true
|
||||
- `,collapse` — Behavior=Collapsible (для групп)
|
||||
|
||||
**Привязка**: `-> Объект.Поле` — DataPath или CommandName
|
||||
**Привязка к данным**: `-> Объект.Поле` — DataPath
|
||||
|
||||
**Привязка к команде**: `-> ИмяКоманды [cmd]` — команда формы, `-> Close [std]` — стандартная команда
|
||||
|
||||
**События**: `{OnChange, StartChoice}` — имена обработчиков
|
||||
|
||||
|
||||
@@ -253,7 +253,16 @@ function Build-Tree($childItemsNode, [string]$prefix, [bool]$isLast) {
|
||||
$binding = " -> $($dp.InnerText)"
|
||||
} else {
|
||||
$cn = $child.SelectSingleNode("d:CommandName", $ns)
|
||||
if ($cn) { $binding = " -> $($cn.InnerText)" }
|
||||
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?
|
||||
@@ -322,16 +331,23 @@ if ($formsIdx -ge 0 -and ($formsIdx + 1) -lt $parts.Count) {
|
||||
|
||||
$lines = @()
|
||||
|
||||
# Header
|
||||
# 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 ---
|
||||
# --- Form properties (Title excluded — shown in header) ---
|
||||
|
||||
$propNames = @(
|
||||
"Title", "Width", "Height", "Group",
|
||||
"Width", "Height", "Group",
|
||||
"WindowOpeningMode", "EnterKeyBehavior", "AutoTitle", "AutoURL",
|
||||
"AutoFillCheck", "Customizable", "CommandBarLocation",
|
||||
"SaveDataInSettings", "AutoSaveDataInSettings",
|
||||
|
||||
@@ -463,6 +463,20 @@ if (-not $stopped) {
|
||||
}
|
||||
}
|
||||
|
||||
# --- 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 ""
|
||||
|
||||
Reference in New Issue
Block a user