mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
Add templates mode for DCS template binding analysis
- Map (without -Name): field bindings with non-trivial expression detection, group bindings organized by group name (groupTemplate, groupHeaderTemplate, groupFooterTemplate, fieldTemplate) - Detail (-Name <group|field>): template content with rows, cells (static text and parameters), non-trivial expressions only - Trivial filter: Field=Field and Field=Представление(Field) hidden - Updated overview: shows binding type counts, templates hint in Next Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: skd-info
|
||||
description: Анализ структуры схемы компоновки данных 1С (СКД) — наборы, поля, параметры, варианты
|
||||
argument-hint: <TemplatePath> [-Mode overview|query|fields|links|calculated|resources|params|variant|trace] [-Name <dataset|variant|field>]
|
||||
argument-hint: <TemplatePath> [-Mode overview|query|fields|links|calculated|resources|params|variant|templates|trace] [-Name <dataset|variant|field|group>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -18,7 +18,7 @@ allowed-tools:
|
||||
|----------|----------|
|
||||
| `TemplatePath` | Путь к Template.xml или каталогу макета (авто-резолв в `Ext/Template.xml`) |
|
||||
| `Mode` | Режим анализа (по умолчанию `overview`) |
|
||||
| `Name` | Имя набора (query), поля (fields/calculated/resources/trace) или варианта (variant) |
|
||||
| `Name` | Имя набора (query), поля (fields/calculated/resources/trace), варианта (variant) или группировки/поля (templates) |
|
||||
| `Batch` | Номер пакета запроса, 0 = все (только query) |
|
||||
| `Limit` / `Offset` | Пагинация (по умолчанию 150 строк) |
|
||||
| `OutFile` | Записать результат в файл (UTF-8 BOM) |
|
||||
@@ -36,6 +36,8 @@ powershell.exe -NoProfile -File .claude\skills\skd-info\scripts\skd-info.ps1 -Te
|
||||
... -Mode resources -Name СуммаНалога
|
||||
... -Mode trace -Name "Коэффициент Ки"
|
||||
... -Mode variant -Name 1
|
||||
... -Mode templates
|
||||
... -Mode templates -Name ВидНалоговойБазы
|
||||
```
|
||||
|
||||
## Режимы
|
||||
@@ -50,6 +52,7 @@ powershell.exe -NoProfile -File .claude\skills\skd-info\scripts\skd-info.ps1 -Te
|
||||
| `resources` | Карта: имена ресурсов (`*` = групповые формулы) | Формулы агрегации по группировкам |
|
||||
| `params` | Таблица параметров: тип, значение, видимость | — |
|
||||
| `variant` | Список вариантов | Структура группировок + фильтры + вывод |
|
||||
| `templates` | Карта привязок шаблонов (field/group) | Содержимое шаблона: строки, ячейки, выражения |
|
||||
| `trace` | — | Полная цепочка: набор → вычисление → ресурс |
|
||||
|
||||
Паттерн: без `-Name` — карта/индекс, с `-Name` — деталь конкретного элемента.
|
||||
|
||||
@@ -174,6 +174,48 @@ DataParams: КлючВарианта="НоменклатураИЦены"
|
||||
Output: style=ЧерноБелый groups=Separately totalsH=None totalsV=None
|
||||
```
|
||||
|
||||
## templates — привязки шаблонов вывода
|
||||
|
||||
Три типа привязок: `fieldTemplate` (к полю), `groupTemplate` (к группировке, Header/Footer), `groupHeaderTemplate` (заголовок группы).
|
||||
|
||||
Без `-Name` — карта привязок:
|
||||
```
|
||||
=== Templates (70 defined: 49 field, 37 group) ===
|
||||
|
||||
Field bindings (49): (all trivial)
|
||||
ОстаточнаяСтоимостьНа0101, ОстаточнаяСтоимостьНа0102, ...
|
||||
|
||||
Group bindings (37):
|
||||
ВидНалоговойБазы
|
||||
Header -> Макет3 (1 rows, 1 params)
|
||||
СреднегодоваяСтоимость2019
|
||||
Footer -> Макет50 (1 rows) spacer
|
||||
GroupHeader -> Макет40 (3 rows)
|
||||
```
|
||||
|
||||
С `-Name <группировка|поле>` — содержимое шаблонов:
|
||||
```
|
||||
=== Templates: СреднегодоваяСтоимость2019 ===
|
||||
|
||||
Footer -> Макет50 [1 rows, 1 cells]:
|
||||
Row 1: (empty)
|
||||
|
||||
GroupHeader -> Макет40 [3 rows, 78 cells]:
|
||||
Row 1: "№ п/п" | "###Группировки1###" | "Инв. номер" | ...
|
||||
Row 2: "01.01" | "01.02" | ... | "31.12"
|
||||
Row 3: "1" | "2" | ... | "26"
|
||||
```
|
||||
|
||||
Для field-привязок:
|
||||
```
|
||||
=== Field template: ОстаточнаяСтоимостьНа0101 -> Макет4 ===
|
||||
[1 rows, 1 cells]
|
||||
Row 1: {ОстаточнаяСтоимостьНа0101}
|
||||
(all params trivial)
|
||||
```
|
||||
|
||||
**Тривиальность выражений**: `Поле = Поле` и `Поле = Представление(Поле)` считаются тривиальными и НЕ выводятся. Показываются только нетривиальные — когда выражение содержит другое поле, вызов метода, пустую строку и т.д.
|
||||
|
||||
## trace — трассировка поля от заголовка до запроса
|
||||
|
||||
Ищет поле по dataPath ИЛИ заголовку (включая подстроку) и показывает полную цепочку происхождения за один вызов:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$TemplatePath,
|
||||
[ValidateSet("overview", "query", "fields", "links", "calculated", "resources", "params", "variant", "trace")]
|
||||
[ValidateSet("overview", "query", "fields", "links", "calculated", "resources", "params", "variant", "trace", "templates")]
|
||||
[string]$Mode = "overview",
|
||||
[string]$Name,
|
||||
[int]$Batch = 0,
|
||||
@@ -44,6 +44,7 @@ $ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
|
||||
$ns.AddNamespace("v8ui", "http://v8.1c.ru/8.1/data/ui")
|
||||
$ns.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema")
|
||||
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
$ns.AddNamespace("dcsat", "http://v8.1c.ru/8.1/data-composition-system/area-template")
|
||||
|
||||
$root = $xmlDoc.DocumentElement
|
||||
|
||||
@@ -391,14 +392,19 @@ if ($Mode -eq "overview") {
|
||||
}
|
||||
}
|
||||
|
||||
# Templates — count only
|
||||
$templates = $root.SelectNodes("s:template", $ns)
|
||||
$groupTemplates = $root.SelectNodes("s:groupTemplate", $ns)
|
||||
if ($templates.Count -gt 0 -or $groupTemplates.Count -gt 0) {
|
||||
# Templates — count with binding types
|
||||
$tplDefs = $root.SelectNodes("s:template", $ns)
|
||||
$fieldTpls = $root.SelectNodes("s:fieldTemplate", $ns)
|
||||
$groupTpls = $root.SelectNodes("s:groupTemplate", $ns)
|
||||
$groupHeaderTpls = $root.SelectNodes("s:groupHeaderTemplate", $ns)
|
||||
$groupFooterTpls = $root.SelectNodes("s:groupFooterTemplate", $ns)
|
||||
$totalBindings = $fieldTpls.Count + $groupTpls.Count + $groupHeaderTpls.Count + $groupFooterTpls.Count
|
||||
if ($tplDefs.Count -gt 0) {
|
||||
$parts = @()
|
||||
if ($templates.Count -gt 0) { $parts += "$($templates.Count) templates" }
|
||||
if ($groupTemplates.Count -gt 0) { $parts += "$($groupTemplates.Count) group bindings" }
|
||||
$lines.Add("Templates: " + ($parts -join ", "))
|
||||
if ($fieldTpls.Count -gt 0) { $parts += "$($fieldTpls.Count) field" }
|
||||
$grpCount = $groupTpls.Count + $groupHeaderTpls.Count + $groupFooterTpls.Count
|
||||
if ($grpCount -gt 0) { $parts += "$grpCount group" }
|
||||
$lines.Add("Templates: $($tplDefs.Count) defined ($($parts -join ', ') bindings)")
|
||||
}
|
||||
|
||||
# Parameters — split visible/hidden
|
||||
@@ -519,6 +525,9 @@ if ($Mode -eq "overview") {
|
||||
} elseif ($variants.Count -gt 1) {
|
||||
$hints += "-Mode variant -Name <N> variant structure (1..$($variants.Count))"
|
||||
}
|
||||
if ($tplDefs.Count -gt 0) {
|
||||
$hints += "-Mode templates template bindings and expressions"
|
||||
}
|
||||
$hints += "-Mode trace -Name <f> trace field origin (by name or title)"
|
||||
$lines.Add("Next:")
|
||||
foreach ($h in $hints) { $lines.Add(" $h") }
|
||||
@@ -1456,6 +1465,299 @@ elseif ($Mode -eq "trace") {
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE: templates
|
||||
# ============================================================
|
||||
elseif ($Mode -eq "templates") {
|
||||
|
||||
# --- Helper: check if expression is trivial ---
|
||||
function Is-TrivialExpr([string]$paramName, [string]$expr) {
|
||||
$e = $expr.Trim()
|
||||
$n = $paramName.Trim()
|
||||
if ($e -eq $n) { return $true }
|
||||
if ($e -eq "Представление($n)") { return $true }
|
||||
return $false
|
||||
}
|
||||
|
||||
# --- Helper: parse template content (rows/cells) ---
|
||||
function Get-TemplateContent([System.Xml.XmlNode]$tplNode) {
|
||||
$innerT = $tplNode.SelectSingleNode("s:template", $ns)
|
||||
if (-not $innerT) { return @{ rows = 0; cells = @(); params = @(); nonTrivial = @() } }
|
||||
|
||||
$rows = $innerT.SelectNodes("dcsat:item", $ns)
|
||||
$rowCount = $rows.Count
|
||||
$cellData = [System.Collections.ArrayList]::new()
|
||||
$rowIdx = 0
|
||||
foreach ($row in $rows) {
|
||||
$rowIdx++
|
||||
$rowCells = [System.Collections.ArrayList]::new()
|
||||
foreach ($cell in $row.SelectNodes("dcsat:tableCell", $ns)) {
|
||||
$field = $cell.SelectSingleNode("dcsat:item", $ns)
|
||||
if (-not $field) {
|
||||
[void]$rowCells.Add("(empty)")
|
||||
continue
|
||||
}
|
||||
$val = $field.SelectSingleNode("dcsat:value", $ns)
|
||||
if (-not $val) {
|
||||
[void]$rowCells.Add("(empty)")
|
||||
continue
|
||||
}
|
||||
$xsiType = $val.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($xsiType -like "*LocalStringType*") {
|
||||
$text = Get-MLText $val
|
||||
if ($text) { [void]$rowCells.Add("`"$text`"") }
|
||||
else { [void]$rowCells.Add("(empty)") }
|
||||
} elseif ($xsiType -like "*Parameter*") {
|
||||
[void]$rowCells.Add("{$($val.InnerText)}")
|
||||
} else {
|
||||
[void]$rowCells.Add("(?)")
|
||||
}
|
||||
}
|
||||
[void]$cellData.Add(@{ row = $rowIdx; cells = $rowCells.ToArray() })
|
||||
}
|
||||
|
||||
# Parameters
|
||||
$paramNodes = $tplNode.SelectNodes("s:parameter", $ns)
|
||||
$paramList = [System.Collections.ArrayList]::new()
|
||||
$nonTrivialList = [System.Collections.ArrayList]::new()
|
||||
foreach ($p in $paramNodes) {
|
||||
$pn = $p.SelectSingleNode("dcsat:name", $ns)
|
||||
$pe = $p.SelectSingleNode("dcsat:expression", $ns)
|
||||
if ($pn -and $pe) {
|
||||
$pName = $pn.InnerText
|
||||
$pExpr = $pe.InnerText
|
||||
[void]$paramList.Add(@{ name = $pName; expression = $pExpr })
|
||||
if (-not (Is-TrivialExpr $pName $pExpr)) {
|
||||
[void]$nonTrivialList.Add(@{ name = $pName; expression = $pExpr })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @{
|
||||
rows = $rowCount
|
||||
cells = $cellData.ToArray()
|
||||
params = $paramList.ToArray()
|
||||
nonTrivial = $nonTrivialList.ToArray()
|
||||
}
|
||||
}
|
||||
|
||||
# --- Build template name -> node index ---
|
||||
$tplIndex = @{}
|
||||
foreach ($t in $root.SelectNodes("s:template", $ns)) {
|
||||
$tn = $t.SelectSingleNode("s:name", $ns)
|
||||
if ($tn) { $tplIndex[$tn.InnerText] = $t }
|
||||
}
|
||||
|
||||
# --- Parse bindings ---
|
||||
# Group bindings: groupTemplate + groupHeaderTemplate
|
||||
$groupBindings = [ordered]@{} # groupName -> @{ bindings = @(@{type; tplName; tplNode}) }
|
||||
|
||||
foreach ($gt in $root.SelectNodes("s:groupTemplate", $ns)) {
|
||||
$gn = $gt.SelectSingleNode("s:groupName", $ns)
|
||||
$gf = $gt.SelectSingleNode("s:groupField", $ns)
|
||||
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
|
||||
$tt = $gt.SelectSingleNode("s:templateType", $ns)
|
||||
$tn = $gt.SelectSingleNode("s:template", $ns)
|
||||
$ttStr = if ($tt) { $tt.InnerText } else { "-" }
|
||||
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
|
||||
|
||||
if (-not $groupBindings.Contains($gnStr)) {
|
||||
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
|
||||
}
|
||||
[void]$groupBindings[$gnStr].Add(@{ type = $ttStr; tplName = $tnStr })
|
||||
}
|
||||
|
||||
foreach ($ght in $root.SelectNodes("s:groupHeaderTemplate", $ns)) {
|
||||
$gn = $ght.SelectSingleNode("s:groupName", $ns)
|
||||
$gf = $ght.SelectSingleNode("s:groupField", $ns)
|
||||
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
|
||||
$tt = $ght.SelectSingleNode("s:templateType", $ns)
|
||||
$tn = $ght.SelectSingleNode("s:template", $ns)
|
||||
$ttStr = if ($tt) { "GroupHeader" } else { "GroupHeader" }
|
||||
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
|
||||
|
||||
if (-not $groupBindings.Contains($gnStr)) {
|
||||
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
|
||||
}
|
||||
[void]$groupBindings[$gnStr].Add(@{ type = $ttStr; tplName = $tnStr })
|
||||
}
|
||||
|
||||
foreach ($gft in $root.SelectNodes("s:groupFooterTemplate", $ns)) {
|
||||
$gn = $gft.SelectSingleNode("s:groupName", $ns)
|
||||
$gf = $gft.SelectSingleNode("s:groupField", $ns)
|
||||
$gnStr = if ($gn) { $gn.InnerText } elseif ($gf) { $gf.InnerText } else { "(default)" }
|
||||
$tn = $gft.SelectSingleNode("s:template", $ns)
|
||||
$tnStr = if ($tn) { $tn.InnerText } else { "-" }
|
||||
|
||||
if (-not $groupBindings.Contains($gnStr)) {
|
||||
$groupBindings[$gnStr] = [System.Collections.ArrayList]::new()
|
||||
}
|
||||
[void]$groupBindings[$gnStr].Add(@{ type = "GroupFooter"; tplName = $tnStr })
|
||||
}
|
||||
|
||||
# Field bindings: fieldTemplate
|
||||
$fieldBindings = [ordered]@{} # fieldName -> tplName
|
||||
$fieldNonTrivial = [System.Collections.ArrayList]::new()
|
||||
|
||||
foreach ($ft in $root.SelectNodes("s:fieldTemplate", $ns)) {
|
||||
$fn = $ft.SelectSingleNode("s:field", $ns)
|
||||
$tn = $ft.SelectSingleNode("s:template", $ns)
|
||||
if ($fn -and $tn) {
|
||||
$fName = $fn.InnerText
|
||||
$tName = $tn.InnerText
|
||||
$fieldBindings[$fName] = $tName
|
||||
# Check params for non-trivial expressions
|
||||
if ($tplIndex.ContainsKey($tName)) {
|
||||
$content = Get-TemplateContent $tplIndex[$tName]
|
||||
foreach ($nt in $content.nonTrivial) {
|
||||
[void]$fieldNonTrivial.Add(@{ field = $fName; template = $tName; name = $nt.name; expression = $nt.expression })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$totalTpl = $tplIndex.Count
|
||||
$fieldCount = $fieldBindings.Count
|
||||
$groupBindCount = 0
|
||||
foreach ($k in $groupBindings.Keys) { $groupBindCount += $groupBindings[$k].Count }
|
||||
|
||||
if (-not $Name) {
|
||||
# --- MAP mode ---
|
||||
$lines.Add("=== Templates ($totalTpl defined: $fieldCount field, $groupBindCount group) ===")
|
||||
|
||||
# Field bindings
|
||||
if ($fieldBindings.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
if ($fieldNonTrivial.Count -eq 0) {
|
||||
$fieldNames = @($fieldBindings.Keys)
|
||||
if ($fieldNames.Count -le 8) {
|
||||
$lines.Add("Field bindings ($fieldCount): $($fieldNames -join ', ') (all trivial)")
|
||||
} else {
|
||||
$lines.Add("Field bindings ($fieldCount): (all trivial)")
|
||||
$lines.Add(" $($fieldNames[0..7] -join ', '), ...")
|
||||
}
|
||||
} else {
|
||||
$trivialCount = $fieldBindings.Count - ($fieldNonTrivial | Select-Object -ExpandProperty field -Unique).Count
|
||||
$lines.Add("Field bindings ($fieldCount, $trivialCount trivial):")
|
||||
foreach ($nt in $fieldNonTrivial) {
|
||||
$lines.Add(" $($nt.field): $($nt.name) = $($nt.expression)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Group bindings
|
||||
if ($groupBindings.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Group bindings ($groupBindCount):")
|
||||
foreach ($gName in $groupBindings.Keys) {
|
||||
$bindings = $groupBindings[$gName]
|
||||
$parts = @()
|
||||
foreach ($b in $bindings) {
|
||||
$info = "$($b.type) -> $($b.tplName)"
|
||||
if ($tplIndex.ContainsKey($b.tplName)) {
|
||||
$content = Get-TemplateContent $tplIndex[$b.tplName]
|
||||
# Check if any cell has content
|
||||
$hasContent = $false
|
||||
foreach ($r in $content.cells) {
|
||||
foreach ($c in $r.cells) {
|
||||
if ($c -ne "(empty)") { $hasContent = $true; break }
|
||||
}
|
||||
if ($hasContent) { break }
|
||||
}
|
||||
$info += " ($($content.rows) rows"
|
||||
if ($content.params.Count -gt 0) { $info += ", $($content.params.Count) params" }
|
||||
$info += ")"
|
||||
if (-not $hasContent -and $content.params.Count -eq 0) {
|
||||
$info += " spacer"
|
||||
}
|
||||
if ($content.nonTrivial.Count -gt 0) {
|
||||
$ntNames = ($content.nonTrivial | ForEach-Object { $_.name }) -join ', '
|
||||
$info += " *$ntNames"
|
||||
}
|
||||
}
|
||||
$parts += $info
|
||||
}
|
||||
$lines.Add(" $gName")
|
||||
foreach ($p in $parts) {
|
||||
$lines.Add(" $p")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($fieldBindings.Count -gt 0 -or $groupBindings.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Use -Name <group|field> for template details.")
|
||||
}
|
||||
} else {
|
||||
# --- DETAIL mode ---
|
||||
$found = $false
|
||||
|
||||
# Check group bindings first
|
||||
if ($groupBindings.Contains($Name)) {
|
||||
$found = $true
|
||||
$bindings = $groupBindings[$Name]
|
||||
$lines.Add("=== Templates: $Name ===")
|
||||
foreach ($b in $bindings) {
|
||||
$lines.Add("")
|
||||
$tName = $b.tplName
|
||||
if (-not $tplIndex.ContainsKey($tName)) {
|
||||
$lines.Add("$($b.type) -> $tName (template not found)")
|
||||
continue
|
||||
}
|
||||
$content = Get-TemplateContent $tplIndex[$tName]
|
||||
$cellCount = 0
|
||||
foreach ($r in $content.cells) { $cellCount += $r.cells.Count }
|
||||
$lines.Add("$($b.type) -> $tName [$($content.rows) rows, $cellCount cells]:")
|
||||
|
||||
foreach ($r in $content.cells) {
|
||||
$cellStr = $r.cells -join " | "
|
||||
$lines.Add(" Row $($r.row): $cellStr")
|
||||
}
|
||||
|
||||
if ($content.nonTrivial.Count -gt 0) {
|
||||
$lines.Add(" Params:")
|
||||
foreach ($nt in $content.nonTrivial) {
|
||||
$lines.Add(" $($nt.name) = $($nt.expression)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Check field bindings
|
||||
if ($fieldBindings.Contains($Name)) {
|
||||
if ($found) { $lines.Add("") }
|
||||
$found = $true
|
||||
$tName = $fieldBindings[$Name]
|
||||
$lines.Add("=== Field template: $Name -> $tName ===")
|
||||
if ($tplIndex.ContainsKey($tName)) {
|
||||
$content = Get-TemplateContent $tplIndex[$tName]
|
||||
$cellCount = 0
|
||||
foreach ($r in $content.cells) { $cellCount += $r.cells.Count }
|
||||
$lines.Add("[$($content.rows) rows, $cellCount cells]")
|
||||
|
||||
foreach ($r in $content.cells) {
|
||||
$cellStr = $r.cells -join " | "
|
||||
$lines.Add(" Row $($r.row): $cellStr")
|
||||
}
|
||||
|
||||
if ($content.nonTrivial.Count -gt 0) {
|
||||
$lines.Add(" Non-trivial params:")
|
||||
foreach ($nt in $content.nonTrivial) {
|
||||
$lines.Add(" $($nt.name) = $($nt.expression)")
|
||||
}
|
||||
} else {
|
||||
$lines.Add(" (all params trivial)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $found) {
|
||||
Write-Error "Group or field '$Name' not found in template bindings"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Output ---
|
||||
|
||||
$result = $lines.ToArray()
|
||||
|
||||
Reference in New Issue
Block a user