diff --git a/.claude/skills/skd-info/SKILL.md b/.claude/skills/skd-info/SKILL.md index 7756f0d9..4acc6019 100644 --- a/.claude/skills/skd-info/SKILL.md +++ b/.claude/skills/skd-info/SKILL.md @@ -1,7 +1,7 @@ --- name: skd-info description: Анализ структуры схемы компоновки данных 1С (СКД) — наборы, поля, параметры, варианты -argument-hint: [-Mode overview|query|fields|links|calculated|resources|params|variant|trace] [-Name ] +argument-hint: [-Mode overview|query|fields|links|calculated|resources|params|variant|templates|trace] [-Name ] 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` — деталь конкретного элемента. diff --git a/.claude/skills/skd-info/modes-reference.md b/.claude/skills/skd-info/modes-reference.md index 8780cf10..eb6bd983 100644 --- a/.claude/skills/skd-info/modes-reference.md +++ b/.claude/skills/skd-info/modes-reference.md @@ -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 ИЛИ заголовку (включая подстроку) и показывает полную цепочку происхождения за один вызов: diff --git a/.claude/skills/skd-info/scripts/skd-info.ps1 b/.claude/skills/skd-info/scripts/skd-info.ps1 index 70541a19..636903b8 100644 --- a/.claude/skills/skd-info/scripts/skd-info.ps1 +++ b/.claude/skills/skd-info/scripts/skd-info.ps1 @@ -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 variant structure (1..$($variants.Count))" } + if ($tplDefs.Count -gt 0) { + $hints += "-Mode templates template bindings and expressions" + } $hints += "-Mode trace -Name 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 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()