mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-11 16:34:57 +03:00
Add /skd-info skill for DCS (Data Composition Schema) analysis
Implements 5 modes: overview (compact TOC), query (raw SQL with batch splitting), fields (field table with roles/restrictions), params (parameter table with types/defaults), variant (structure tree with filters and output settings). Update DCS spec with totalField group info. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
---
|
||||
name: skd-info
|
||||
description: Анализ структуры схемы компоновки данных 1С (СКД) — наборы, поля, параметры, варианты
|
||||
argument-hint: <TemplatePath> [-Mode overview|query|fields|params|variant] [-Name <dataset|variant>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /skd-info — Анализ схемы компоновки данных
|
||||
|
||||
Читает Template.xml схемы компоновки данных (СКД) и выводит компактную сводку. Заменяет необходимость читать тысячи строк XML.
|
||||
|
||||
## Использование
|
||||
|
||||
```
|
||||
/skd-info <TemplatePath>
|
||||
/skd-info <TemplatePath> -Mode query -Name НаборДанных1
|
||||
```
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|--------------|:------------:|--------------|---------------------------------------------------|
|
||||
| TemplatePath | да | — | Путь к Template.xml или каталогу макета |
|
||||
| Mode | нет | `overview` | Режим: `overview`, `query`, `fields`, `params`, `variant` |
|
||||
| Name | нет | — | Имя набора (query/fields) или варианта (variant) |
|
||||
| Batch | нет | `0` | Номер пакета запроса (0 = все). Только для query |
|
||||
| Limit | нет | `150` | Макс. строк вывода (защита от переполнения) |
|
||||
| Offset | нет | `0` | Пропустить N строк (для пагинации) |
|
||||
| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\skd-info\scripts\skd-info.ps1 -TemplatePath "<путь>"
|
||||
```
|
||||
|
||||
С указанием режима:
|
||||
```powershell
|
||||
... -Mode query -Name НоменклатураСЦенами
|
||||
... -Mode query -Name ДанныеТ13 -Batch 3
|
||||
... -Mode fields
|
||||
... -Mode fields -Name НаборДанных1
|
||||
... -Mode params
|
||||
... -Mode variant -Name Основной
|
||||
... -Mode variant -Name 1
|
||||
```
|
||||
|
||||
Для большого вывода используй `-OutFile`:
|
||||
```powershell
|
||||
... -Mode query -Name ДанныеТ13 -OutFile test-tmp\query.txt
|
||||
```
|
||||
Затем прочитай результат через Read tool.
|
||||
|
||||
## Режимы
|
||||
|
||||
### overview (по умолчанию) — оглавление
|
||||
|
||||
Всегда 20-50 строк. Показывает "карту" схемы для выбора дальнейших действий:
|
||||
|
||||
```
|
||||
=== DCS: Template.xml (361 lines) ===
|
||||
|
||||
Sources: ИсточникДанных1 (Local)
|
||||
|
||||
Datasets:
|
||||
[Query] НоменклатураСЦенами 7 fields, query 40 lines
|
||||
Links: (none)
|
||||
Calculated: УИД
|
||||
Totals: Цена=Максимум(Цена)
|
||||
Templates: Макет1(ТипЦен/Header)
|
||||
Params: (none)
|
||||
|
||||
Variants:
|
||||
[1] НоменклатураИЦены "Номенклатура и цены" Table 3 filters
|
||||
[2] НоменклатураБезЦен "Номенклатура без цен" Group(detail) 2 filters
|
||||
```
|
||||
|
||||
Для DataSetUnion — дерево:
|
||||
```
|
||||
Datasets:
|
||||
[Union] РасчетНалога 35 fields
|
||||
├─ [Query] ДанныеПоСреднегодовой query 120 lines
|
||||
└─ [Query] ДанныеПоКадастровой query 85 lines
|
||||
[Object] ДопДанные objectName=Таблица 4 fields
|
||||
```
|
||||
|
||||
### query — текст запроса
|
||||
|
||||
Извлекает raw-текст запроса с деэкранированием XML (`&`→`&`, `>`→`>`). Для пакетных запросов — оглавление батчей:
|
||||
|
||||
```
|
||||
=== Query: ДанныеТ13 (334 lines, 10 batches) ===
|
||||
Batch 1: lines 1-8 → ПОМЕСТИТЬ Представления_Периоды
|
||||
Batch 2: lines 9-26 → ПОМЕСТИТЬ Представления_СотрудникиОрганизации
|
||||
...
|
||||
--- Batch 1 ---
|
||||
ВЫБРАТЬ
|
||||
ДАТАВРЕМЯ(1, 1, 1) КАК Период
|
||||
ПОМЕСТИТЬ Представления_Периоды
|
||||
...
|
||||
```
|
||||
|
||||
Фильтр по номеру батча: `-Batch 3` покажет только 3-й пакет.
|
||||
|
||||
### fields — таблица полей
|
||||
|
||||
```
|
||||
=== Fields: НоменклатураСЦенами (7) ===
|
||||
dataPath title role restrict format
|
||||
Номенклатура - - - -
|
||||
Номенклатура.Артикул "Артикул" - - -
|
||||
Цена - - - ЧГ=0
|
||||
--- calculated ---
|
||||
УИД = БухгалтерскиеОтчеты.ПолучитьУИДСсылкиСтрокой(Номенклатура) restrict:cond,grp,ord
|
||||
--- totals ---
|
||||
Цена = Максимум(Цена)
|
||||
ПравоИнтерактивное = Максимум(ПравоИнтерактивное) [group:ОбъектМетаданных]
|
||||
```
|
||||
|
||||
### params — параметры схемы
|
||||
|
||||
```
|
||||
=== Parameters (16) ===
|
||||
Name Type Default Visible Expression
|
||||
Период StandardPeriod LastMonth yes -
|
||||
НачалоПериода DateTime - hidden &Период.ДатаНачала
|
||||
Организация CatalogRef.Организации null yes -
|
||||
```
|
||||
|
||||
### variant — структура варианта
|
||||
|
||||
```
|
||||
=== Variant [1]: НоменклатураИЦены "Номенклатура и цены" ===
|
||||
|
||||
Structure:
|
||||
Table "Таблица"
|
||||
├── Columns: [ТипЦен Items]
|
||||
│ Selection: Auto, Цена
|
||||
└── Rows: [Номенклатура Items]
|
||||
Selection: Номенклатура, УИД, Auto
|
||||
|
||||
Filter:
|
||||
[ ] Номенклатура InHierarchy [user]
|
||||
[ ] ТипЦен Equal
|
||||
[x] ВАрхиве = false "Исключая скрытые товары"
|
||||
|
||||
DataParams: КлючВарианта="НоменклатураИЦены"
|
||||
Output: style=ЧерноБелый groups=Separately totalsH=None totalsV=None
|
||||
```
|
||||
|
||||
## Разрешение пути
|
||||
|
||||
- Прямой путь: `path/to/Template.xml`
|
||||
- Каталог макета: `path/to/ИмяМакета/` → авто-резолв в `Ext/Template.xml`
|
||||
|
||||
## Что не выводится
|
||||
|
||||
- XML namespace-декларации
|
||||
- Обёртки v8:item/v8:lang/v8:content (извлекаем чистый текст)
|
||||
- userSettingID (GUID-ы пользовательских настроек)
|
||||
- Дефолтные periodAdditionBegin/End = 0001-01-01
|
||||
- viewMode
|
||||
|
||||
## Когда использовать
|
||||
|
||||
- **Перед анализом отчёта**: overview для понимания структуры
|
||||
- **Отладка данных**: query для просмотра текста запроса
|
||||
- **Модификация полей**: fields для полного списка с ролями
|
||||
- **Программный вызов**: params для списка параметров
|
||||
- **Изменение вывода**: variant для структуры группировок и фильтров
|
||||
|
||||
## Защита от переполнения
|
||||
|
||||
Вывод ограничен 150 строками по умолчанию. При превышении:
|
||||
```
|
||||
[TRUNCATED] Shown 150 of 400 lines. Use -Offset 150 to continue.
|
||||
```
|
||||
|
||||
Используйте `-Offset N` и `-Limit N` для постраничного просмотра.
|
||||
@@ -0,0 +1,961 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$TemplatePath,
|
||||
[ValidateSet("overview", "query", "fields", "params", "variant")]
|
||||
[string]$Mode = "overview",
|
||||
[string]$Name,
|
||||
[int]$Batch = 0,
|
||||
[int]$Limit = 150,
|
||||
[int]$Offset = 0,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve path ---
|
||||
|
||||
if (-not $TemplatePath.EndsWith(".xml")) {
|
||||
$candidate = Join-Path (Join-Path $TemplatePath "Ext") "Template.xml"
|
||||
if (Test-Path $candidate) {
|
||||
$TemplatePath = $candidate
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $TemplatePath)) {
|
||||
Write-Error "File not found: $TemplatePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$resolvedPath = (Resolve-Path $TemplatePath).Path
|
||||
|
||||
# --- Load XML ---
|
||||
|
||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$xmlDoc.PreserveWhitespace = $false
|
||||
$xmlDoc.Load($resolvedPath)
|
||||
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$ns.AddNamespace("s", "http://v8.1c.ru/8.1/data-composition-system/schema")
|
||||
$ns.AddNamespace("dcscom", "http://v8.1c.ru/8.1/data-composition-system/common")
|
||||
$ns.AddNamespace("dcscor", "http://v8.1c.ru/8.1/data-composition-system/core")
|
||||
$ns.AddNamespace("dcsset", "http://v8.1c.ru/8.1/data-composition-system/settings")
|
||||
$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")
|
||||
|
||||
$root = $xmlDoc.DocumentElement
|
||||
|
||||
# --- Helpers ---
|
||||
|
||||
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 ""
|
||||
}
|
||||
|
||||
function Unescape-Xml([string]$text) {
|
||||
if (-not $text) { return $text }
|
||||
$text = $text.Replace("&", "&")
|
||||
$text = $text.Replace(">", ">")
|
||||
$text = $text.Replace("<", "<")
|
||||
$text = $text.Replace(""", '"')
|
||||
$text = $text.Replace("'", "'")
|
||||
return $text
|
||||
}
|
||||
|
||||
function Get-CompactType($valueTypeNode) {
|
||||
if (-not $valueTypeNode) { return "" }
|
||||
$types = @()
|
||||
foreach ($t in $valueTypeNode.SelectNodes("v8:Type", $ns)) {
|
||||
$raw = $t.InnerText
|
||||
switch -Wildcard ($raw) {
|
||||
"xs:string" { $types += "String" }
|
||||
"xs:decimal" { $types += "Number" }
|
||||
"xs:boolean" { $types += "Boolean" }
|
||||
"xs:dateTime" { $types += "DateTime" }
|
||||
"v8:StandardPeriod" { $types += "StandardPeriod" }
|
||||
"v8:StandardBeginningDate" { $types += "StandardBeginningDate" }
|
||||
"v8:AccountType" { $types += "AccountType" }
|
||||
"v8:Null" { $types += "Null" }
|
||||
default {
|
||||
# Strip namespace prefixes like d4p1: cfg:
|
||||
$clean = $raw -replace '^[a-zA-Z0-9]+:', ''
|
||||
$types += $clean
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($types.Count -eq 0) { return "" }
|
||||
return ($types -join " | ")
|
||||
}
|
||||
|
||||
function Get-DataSetType($dsNode) {
|
||||
$xsiType = $dsNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($xsiType -like "*DataSetQuery*") { return "Query" }
|
||||
if ($xsiType -like "*DataSetObject*") { return "Object" }
|
||||
if ($xsiType -like "*DataSetUnion*") { return "Union" }
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
function Get-FieldCount($dsNode) {
|
||||
return $dsNode.SelectNodes("s:field", $ns).Count
|
||||
}
|
||||
|
||||
function Get-QueryLineCount($dsNode) {
|
||||
$queryNode = $dsNode.SelectSingleNode("s:query", $ns)
|
||||
if (-not $queryNode) { return 0 }
|
||||
$text = $queryNode.InnerText
|
||||
return ($text -split "`n").Count
|
||||
}
|
||||
|
||||
function Get-StructureItemType($itemNode) {
|
||||
$xsiType = $itemNode.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($xsiType -like "*StructureItemGroup*") { return "Group" }
|
||||
if ($xsiType -like "*StructureItemTable*") { return "Table" }
|
||||
if ($xsiType -like "*StructureItemChart*") { return "Chart" }
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
function Get-GroupFields($itemNode) {
|
||||
$fields = @()
|
||||
foreach ($gi in $itemNode.SelectNodes("dcsset:groupItems/dcsset:item", $ns)) {
|
||||
$fieldNode = $gi.SelectSingleNode("dcsset:field", $ns)
|
||||
$groupType = $gi.SelectSingleNode("dcsset:groupType", $ns)
|
||||
if ($fieldNode) {
|
||||
$f = $fieldNode.InnerText
|
||||
$gt = if ($groupType) { $groupType.InnerText } else { "" }
|
||||
if ($gt -and $gt -ne "Items") { $f += "($gt)" }
|
||||
$fields += $f
|
||||
}
|
||||
}
|
||||
return $fields
|
||||
}
|
||||
|
||||
function Get-SelectionFields($itemNode) {
|
||||
$fields = @()
|
||||
foreach ($si in $itemNode.SelectNodes("dcsset:selection/dcsset:item", $ns)) {
|
||||
$xsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($xsiType -like "*SelectedItemAuto*") {
|
||||
$fields += "Auto"
|
||||
} elseif ($xsiType -like "*SelectedItemField*") {
|
||||
$f = $si.SelectSingleNode("dcsset:field", $ns)
|
||||
if ($f) { $fields += $f.InnerText }
|
||||
} elseif ($xsiType -like "*SelectedItemFolder*") {
|
||||
$fields += "Folder"
|
||||
}
|
||||
}
|
||||
return $fields
|
||||
}
|
||||
|
||||
function Get-FilterSummary($settingsNode) {
|
||||
$filters = @()
|
||||
foreach ($fi in $settingsNode.SelectNodes("dcsset:filter/dcsset:item", $ns)) {
|
||||
$xsiType = $fi.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
|
||||
if ($xsiType -like "*FilterItemGroup*") {
|
||||
$groupType = $fi.SelectSingleNode("dcsset:groupType", $ns)
|
||||
$gt = if ($groupType) { $groupType.InnerText } else { "And" }
|
||||
$subCount = $fi.SelectNodes("dcsset:item", $ns).Count
|
||||
$filters += "[Group:$gt $subCount items]"
|
||||
continue
|
||||
}
|
||||
|
||||
$use = $fi.SelectSingleNode("dcsset:use", $ns)
|
||||
$isActive = if ($use -and $use.InnerText -eq "false") { "[ ]" } else { "[x]" }
|
||||
|
||||
$left = $fi.SelectSingleNode("dcsset:left", $ns)
|
||||
$comp = $fi.SelectSingleNode("dcsset:comparisonType", $ns)
|
||||
$right = $fi.SelectSingleNode("dcsset:right", $ns)
|
||||
$pres = $fi.SelectSingleNode("dcsset:presentation", $ns)
|
||||
$userSetting = $fi.SelectSingleNode("dcsset:userSettingID", $ns)
|
||||
|
||||
$leftStr = if ($left) { $left.InnerText } else { "?" }
|
||||
$compStr = if ($comp) { $comp.InnerText } else { "?" }
|
||||
$rightStr = ""
|
||||
if ($right) {
|
||||
$rightStr = " $($right.InnerText)"
|
||||
}
|
||||
|
||||
$presStr = ""
|
||||
if ($pres) {
|
||||
$pt = Get-MLText $pres
|
||||
if ($pt) { $presStr = " `"$pt`"" }
|
||||
}
|
||||
|
||||
$userStr = ""
|
||||
if ($userSetting) { $userStr = " [user]" }
|
||||
|
||||
$filters += "$isActive $leftStr $compStr$rightStr$presStr$userStr"
|
||||
}
|
||||
return $filters
|
||||
}
|
||||
|
||||
function Build-StructureTree {
|
||||
param($itemNode, [string]$prefix, [bool]$isLast, [System.Collections.Generic.List[string]]$outLines)
|
||||
|
||||
$itemType = Get-StructureItemType $itemNode
|
||||
$nameNode = $itemNode.SelectSingleNode("dcsset:name", $ns)
|
||||
$itemName = if ($nameNode) { $nameNode.InnerText } else { "" }
|
||||
|
||||
$groupFields = Get-GroupFields $itemNode
|
||||
$groupStr = if ($groupFields.Count -gt 0) { "[" + ($groupFields -join ", ") + "]" } else { "(detail)" }
|
||||
|
||||
$selFields = Get-SelectionFields $itemNode
|
||||
$selStr = if ($selFields.Count -gt 0) { "Selection: " + ($selFields -join ", ") } else { "" }
|
||||
|
||||
$line = ""
|
||||
switch ($itemType) {
|
||||
"Group" {
|
||||
$line = "$itemType $groupStr"
|
||||
if ($itemName) { $line = "$itemType `"$itemName`" $groupStr" }
|
||||
}
|
||||
"Table" {
|
||||
$line = "Table"
|
||||
if ($itemName) { $line = "Table `"$itemName`"" }
|
||||
}
|
||||
"Chart" {
|
||||
$line = "Chart"
|
||||
if ($itemName) { $line = "Chart `"$itemName`"" }
|
||||
}
|
||||
}
|
||||
|
||||
$outLines.Add("$prefix$line")
|
||||
if ($selStr -and $itemType -eq "Group") {
|
||||
$outLines.Add("$prefix $selStr")
|
||||
}
|
||||
|
||||
# For Table, show columns and rows
|
||||
if ($itemType -eq "Table") {
|
||||
$columns = $itemNode.SelectNodes("dcsset:column", $ns)
|
||||
$rows = $itemNode.SelectNodes("dcsset:row", $ns)
|
||||
|
||||
foreach ($col in $columns) {
|
||||
$colGroup = Get-GroupFields $col
|
||||
$colGroupStr = if ($colGroup.Count -gt 0) { "[" + ($colGroup -join ", ") + "]" } else { "(detail)" }
|
||||
$colSel = Get-SelectionFields $col
|
||||
$colSelStr = if ($colSel.Count -gt 0) { "Selection: " + ($colSel -join ", ") } else { "" }
|
||||
$connC = if ($rows.Count -gt 0) { [string][char]0x251C + [string][char]0x2500 + [string][char]0x2500 } else { [string][char]0x2514 + [string][char]0x2500 + [string][char]0x2500 }
|
||||
$contC = if ($rows.Count -gt 0) { [string][char]0x2502 + " " } else { " " }
|
||||
$outLines.Add("$prefix$connC Columns: $colGroupStr")
|
||||
if ($colSelStr) { $outLines.Add("$prefix$contC $colSelStr") }
|
||||
}
|
||||
|
||||
foreach ($row in $rows) {
|
||||
$rowGroup = Get-GroupFields $row
|
||||
$rowGroupStr = if ($rowGroup.Count -gt 0) { "[" + ($rowGroup -join ", ") + "]" } else { "(detail)" }
|
||||
$rowSel = Get-SelectionFields $row
|
||||
$rowSelStr = if ($rowSel.Count -gt 0) { "Selection: " + ($rowSel -join ", ") } else { "" }
|
||||
$outLines.Add("$prefix" + [string][char]0x2514 + [string][char]0x2500 + [string][char]0x2500 + " Rows: $rowGroupStr")
|
||||
if ($rowSelStr) { $outLines.Add("$prefix $rowSelStr") }
|
||||
}
|
||||
}
|
||||
|
||||
# Recurse into nested structure items (for Group)
|
||||
if ($itemType -eq "Group") {
|
||||
$children = $itemNode.SelectNodes("dcsset:item", $ns)
|
||||
for ($i = 0; $i -lt $children.Count; $i++) {
|
||||
$child = $children[$i]
|
||||
$childType = $child.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($childType -like "*StructureItem*") {
|
||||
$last = ($i -eq $children.Count - 1)
|
||||
$connector = if ($last) { [string][char]0x2514 + [string][char]0x2500 + " " } else { [string][char]0x251C + [string][char]0x2500 + " " }
|
||||
$continuation = if ($last) { " " } else { [string][char]0x2502 + " " }
|
||||
Build-StructureTree -itemNode $child -prefix "$prefix$continuation" -isLast $last -outLines $outLines
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Output collector ---
|
||||
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
# Determine template name from path
|
||||
$pathParts = $resolvedPath -split '[/\\]'
|
||||
$templateName = $resolvedPath
|
||||
for ($i = $pathParts.Count - 1; $i -ge 0; $i--) {
|
||||
if ($pathParts[$i] -eq "Ext" -and $i -ge 1) {
|
||||
$templateName = $pathParts[$i - 1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
$totalXmlLines = (Get-Content $resolvedPath).Count
|
||||
|
||||
# ============================================================
|
||||
# MODE: overview
|
||||
# ============================================================
|
||||
if ($Mode -eq "overview") {
|
||||
|
||||
$lines.Add("=== DCS: $templateName ($totalXmlLines lines) ===")
|
||||
$lines.Add("")
|
||||
|
||||
# Sources
|
||||
$sources = @()
|
||||
foreach ($ds in $root.SelectNodes("s:dataSource", $ns)) {
|
||||
$dsName = $ds.SelectSingleNode("s:name", $ns).InnerText
|
||||
$dsType = $ds.SelectSingleNode("s:dataSourceType", $ns).InnerText
|
||||
$sources += "$dsName ($dsType)"
|
||||
}
|
||||
$lines.Add("Sources: " + ($sources -join ", "))
|
||||
$lines.Add("")
|
||||
|
||||
# Datasets (recursive for Union)
|
||||
$lines.Add("Datasets:")
|
||||
foreach ($ds in $root.SelectNodes("s:dataSet", $ns)) {
|
||||
$dsType = Get-DataSetType $ds
|
||||
$dsName = $ds.SelectSingleNode("s:name", $ns).InnerText
|
||||
$fieldCount = Get-FieldCount $ds
|
||||
|
||||
switch ($dsType) {
|
||||
"Query" {
|
||||
$queryLines = Get-QueryLineCount $ds
|
||||
$lines.Add(" [Query] $dsName $fieldCount fields, query $queryLines lines")
|
||||
}
|
||||
"Object" {
|
||||
$objName = $ds.SelectSingleNode("s:objectName", $ns)
|
||||
$objStr = if ($objName) { " objectName=$($objName.InnerText)" } else { "" }
|
||||
$lines.Add(" [Object] $dsName$objStr $fieldCount fields")
|
||||
}
|
||||
"Union" {
|
||||
$lines.Add(" [Union] $dsName $fieldCount fields")
|
||||
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
|
||||
$subType = Get-DataSetType $subDs
|
||||
$subName = $subDs.SelectSingleNode("s:name", $ns)
|
||||
$subNameStr = if ($subName) { $subName.InnerText } else { "?" }
|
||||
$subFields = Get-FieldCount $subDs
|
||||
switch ($subType) {
|
||||
"Query" {
|
||||
$subQueryLines = Get-QueryLineCount $subDs
|
||||
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [Query] $subNameStr $subFields fields, query $subQueryLines lines")
|
||||
}
|
||||
"Object" {
|
||||
$subObjName = $subDs.SelectSingleNode("s:objectName", $ns)
|
||||
$subObjStr = if ($subObjName) { " objectName=$($subObjName.InnerText)" } else { "" }
|
||||
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [Object] $subNameStr$subObjStr $subFields fields")
|
||||
}
|
||||
default {
|
||||
$lines.Add(" " + [string][char]0x251C + [string][char]0x2500 + " [$subType] $subNameStr $subFields fields")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Links
|
||||
$links = $root.SelectNodes("s:dataSetLink", $ns)
|
||||
if ($links.Count -gt 0) {
|
||||
$linkStrs = @()
|
||||
foreach ($lnk in $links) {
|
||||
$srcDs = $lnk.SelectSingleNode("s:sourceDataSet", $ns).InnerText
|
||||
$dstDs = $lnk.SelectSingleNode("s:destinationDataSet", $ns).InnerText
|
||||
$srcExpr = $lnk.SelectSingleNode("s:sourceExpression", $ns).InnerText
|
||||
$dstExpr = $lnk.SelectSingleNode("s:destinationExpression", $ns).InnerText
|
||||
$paramNode = $lnk.SelectSingleNode("s:parameter", $ns)
|
||||
$paramStr = if ($paramNode) { " param=$($paramNode.InnerText)" } else { "" }
|
||||
$linkStrs += "$srcDs.$srcExpr -> $dstDs.$dstExpr$paramStr"
|
||||
}
|
||||
$lines.Add("Links: " + ($linkStrs -join "; "))
|
||||
} else {
|
||||
$lines.Add("Links: (none)")
|
||||
}
|
||||
|
||||
# Calculated fields
|
||||
$calcFields = $root.SelectNodes("s:calculatedField", $ns)
|
||||
if ($calcFields.Count -gt 0) {
|
||||
$calcNames = @()
|
||||
foreach ($cf in $calcFields) {
|
||||
$calcNames += $cf.SelectSingleNode("s:dataPath", $ns).InnerText
|
||||
}
|
||||
$lines.Add("Calculated: " + ($calcNames -join ", "))
|
||||
}
|
||||
|
||||
# Totals
|
||||
$totalFields = $root.SelectNodes("s:totalField", $ns)
|
||||
if ($totalFields.Count -gt 0) {
|
||||
$totalStrs = @()
|
||||
foreach ($tf in $totalFields) {
|
||||
$tfPath = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
|
||||
$tfExpr = $tf.SelectSingleNode("s:expression", $ns).InnerText
|
||||
$tfGroup = $tf.SelectSingleNode("s:group", $ns)
|
||||
$groupStr = ""
|
||||
if ($tfGroup) { $groupStr = " [group:$($tfGroup.InnerText)]" }
|
||||
$totalStrs += "$tfPath=$tfExpr$groupStr"
|
||||
}
|
||||
$lines.Add("Totals: " + ($totalStrs -join ", "))
|
||||
}
|
||||
|
||||
# Templates
|
||||
$templates = $root.SelectNodes("s:template", $ns)
|
||||
$groupTemplates = $root.SelectNodes("s:groupTemplate", $ns)
|
||||
if ($templates.Count -gt 0 -or $groupTemplates.Count -gt 0) {
|
||||
$tplStrs = @()
|
||||
foreach ($tpl in $templates) {
|
||||
$tplName = $tpl.SelectSingleNode("s:name", $ns).InnerText
|
||||
$tplStrs += $tplName
|
||||
}
|
||||
foreach ($gt in $groupTemplates) {
|
||||
$gtField = $gt.SelectSingleNode("s:groupField", $ns).InnerText
|
||||
$gtType = $gt.SelectSingleNode("s:templateType", $ns).InnerText
|
||||
$gtTpl = $gt.SelectSingleNode("s:template", $ns).InnerText
|
||||
$tplStrs += "$gtTpl($gtField/$gtType)"
|
||||
}
|
||||
$lines.Add("Templates: " + ($tplStrs -join ", "))
|
||||
}
|
||||
|
||||
# Parameters
|
||||
$params = $root.SelectNodes("s:parameter", $ns)
|
||||
if ($params.Count -gt 0) {
|
||||
$paramStrs = @()
|
||||
foreach ($p in $params) {
|
||||
$pName = $p.SelectSingleNode("s:name", $ns).InnerText
|
||||
$paramStrs += $pName
|
||||
}
|
||||
$lines.Add("Params ($($params.Count)): " + ($paramStrs -join ", "))
|
||||
} else {
|
||||
$lines.Add("Params: (none)")
|
||||
}
|
||||
|
||||
$lines.Add("")
|
||||
|
||||
# Variants
|
||||
$variants = $root.SelectNodes("s:settingsVariant", $ns)
|
||||
if ($variants.Count -gt 0) {
|
||||
$lines.Add("Variants:")
|
||||
$varIdx = 0
|
||||
foreach ($v in $variants) {
|
||||
$varIdx++
|
||||
$vName = $v.SelectSingleNode("dcsset:name", $ns).InnerText
|
||||
$vPres = $v.SelectSingleNode("dcsset:presentation", $ns)
|
||||
$vPresStr = ""
|
||||
if ($vPres) {
|
||||
$pt = Get-MLText $vPres
|
||||
if ($pt) { $vPresStr = " `"$pt`"" }
|
||||
}
|
||||
|
||||
$settings = $v.SelectSingleNode("dcsset:settings", $ns)
|
||||
$structItems = @()
|
||||
if ($settings) {
|
||||
foreach ($si in $settings.SelectNodes("dcsset:item", $ns)) {
|
||||
$siType = Get-StructureItemType $si
|
||||
$groupFields = Get-GroupFields $si
|
||||
$groupStr = if ($groupFields.Count -gt 0) { "(" + ($groupFields -join ",") + ")" } else { "(detail)" }
|
||||
$structItems += "$siType$groupStr"
|
||||
}
|
||||
}
|
||||
$structStr = if ($structItems.Count -gt 0) { " " + ($structItems -join ", ") } else { "" }
|
||||
|
||||
$filterCount = 0
|
||||
if ($settings) {
|
||||
$filterCount = $settings.SelectNodes("dcsset:filter/dcsset:item", $ns).Count
|
||||
}
|
||||
$filterStr = if ($filterCount -gt 0) { " $filterCount filters" } else { "" }
|
||||
|
||||
$lines.Add(" [$varIdx] $vName$vPresStr$structStr$filterStr")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE: query
|
||||
# ============================================================
|
||||
elseif ($Mode -eq "query") {
|
||||
|
||||
# Find dataset
|
||||
$dataSets = $root.SelectNodes("s:dataSet", $ns)
|
||||
$targetDs = $null
|
||||
|
||||
if ($Name) {
|
||||
# Search by name in top-level and nested
|
||||
foreach ($ds in $dataSets) {
|
||||
$dsNameNode = $ds.SelectSingleNode("s:name", $ns)
|
||||
if ($dsNameNode -and $dsNameNode.InnerText -eq $Name) { $targetDs = $ds; break }
|
||||
# Search in Union items
|
||||
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
|
||||
$subNameNode = $subDs.SelectSingleNode("s:name", $ns)
|
||||
if ($subNameNode -and $subNameNode.InnerText -eq $Name) { $targetDs = $subDs; break }
|
||||
}
|
||||
if ($targetDs) { break }
|
||||
}
|
||||
if (-not $targetDs) {
|
||||
Write-Error "Dataset '$Name' not found"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
# Take first Query dataset
|
||||
foreach ($ds in $dataSets) {
|
||||
$dsType = Get-DataSetType $ds
|
||||
if ($dsType -eq "Query") { $targetDs = $ds; break }
|
||||
if ($dsType -eq "Union") {
|
||||
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
|
||||
if ((Get-DataSetType $subDs) -eq "Query") { $targetDs = $subDs; break }
|
||||
}
|
||||
if ($targetDs) { break }
|
||||
}
|
||||
}
|
||||
if (-not $targetDs) {
|
||||
Write-Error "No Query dataset found"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$queryNode = $targetDs.SelectSingleNode("s:query", $ns)
|
||||
if (-not $queryNode) {
|
||||
Write-Error "Dataset has no query element"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$rawQuery = Unescape-Xml $queryNode.InnerText
|
||||
$dsNameStr = $targetDs.SelectSingleNode("s:name", $ns).InnerText
|
||||
|
||||
# Split into batches
|
||||
$batches = @()
|
||||
$batchTexts = $rawQuery -split ';\s*\r?\n\s*/{16,}\s*\r?\n'
|
||||
foreach ($bt in $batchTexts) {
|
||||
$trimmed = $bt.Trim()
|
||||
if ($trimmed) { $batches += $trimmed }
|
||||
}
|
||||
|
||||
$totalQueryLines = ($rawQuery -split "`n").Count
|
||||
|
||||
if ($batches.Count -le 1) {
|
||||
# Single query
|
||||
$lines.Add("=== Query: $dsNameStr ($totalQueryLines lines) ===")
|
||||
$lines.Add("")
|
||||
foreach ($ql in ($rawQuery.Trim() -split "`n")) {
|
||||
$lines.Add($ql.TrimEnd())
|
||||
}
|
||||
} else {
|
||||
$lines.Add("=== Query: $dsNameStr ($totalQueryLines lines, $($batches.Count) batches) ===")
|
||||
|
||||
if ($Batch -eq 0) {
|
||||
# Show TOC
|
||||
$lineNum = 1
|
||||
for ($bi = 0; $bi -lt $batches.Count; $bi++) {
|
||||
$batchLines = ($batches[$bi] -split "`n")
|
||||
$endLine = $lineNum + $batchLines.Count - 1
|
||||
# Detect ПОМЕСТИТЬ target
|
||||
$target = ""
|
||||
foreach ($bl in $batchLines) {
|
||||
if ($bl -match '^\s*(?:ПОМЕСТИТЬ|INTO)\s+(\S+)') {
|
||||
$target = [char]0x2192 + " " + $Matches[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
$lines.Add(" Batch $($bi + 1): lines $lineNum-$endLine $target")
|
||||
$lineNum = $endLine + 3 # +separator
|
||||
}
|
||||
$lines.Add("")
|
||||
|
||||
# Show all batches
|
||||
for ($bi = 0; $bi -lt $batches.Count; $bi++) {
|
||||
$lines.Add("--- Batch $($bi + 1) ---")
|
||||
foreach ($ql in ($batches[$bi] -split "`n")) {
|
||||
$lines.Add($ql.TrimEnd())
|
||||
}
|
||||
$lines.Add("")
|
||||
}
|
||||
} else {
|
||||
# Show specific batch
|
||||
if ($Batch -gt $batches.Count) {
|
||||
Write-Error "Batch $Batch not found (total: $($batches.Count))"
|
||||
exit 1
|
||||
}
|
||||
$lines.Add("")
|
||||
$lines.Add("--- Batch $Batch ---")
|
||||
foreach ($ql in ($batches[$Batch - 1] -split "`n")) {
|
||||
$lines.Add($ql.TrimEnd())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE: fields
|
||||
# ============================================================
|
||||
elseif ($Mode -eq "fields") {
|
||||
|
||||
$dataSets = $root.SelectNodes("s:dataSet", $ns)
|
||||
|
||||
function Show-DataSetFields($dsNode) {
|
||||
$dsType = Get-DataSetType $dsNode
|
||||
$dsNameStr = $dsNode.SelectSingleNode("s:name", $ns).InnerText
|
||||
$fields = $dsNode.SelectNodes("s:field", $ns)
|
||||
|
||||
$lines.Add("=== Fields: $dsNameStr [$dsType] ($($fields.Count)) ===")
|
||||
$lines.Add(" dataPath title role restrict format")
|
||||
|
||||
foreach ($f in $fields) {
|
||||
$dp = $f.SelectSingleNode("s:dataPath", $ns)
|
||||
$dpStr = if ($dp) { $dp.InnerText } else { "-" }
|
||||
|
||||
$titleNode = $f.SelectSingleNode("s:title", $ns)
|
||||
$titleStr = if ($titleNode) { Get-MLText $titleNode } else { "" }
|
||||
if (-not $titleStr) { $titleStr = "-" }
|
||||
|
||||
# Role
|
||||
$role = $f.SelectSingleNode("s:role", $ns)
|
||||
$roleStr = "-"
|
||||
if ($role) {
|
||||
$roleParts = @()
|
||||
foreach ($child in $role.ChildNodes) {
|
||||
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
|
||||
$roleParts += $child.LocalName
|
||||
}
|
||||
}
|
||||
if ($roleParts.Count -gt 0) { $roleStr = $roleParts -join "," }
|
||||
}
|
||||
|
||||
# UseRestriction
|
||||
$restrict = $f.SelectSingleNode("s:useRestriction", $ns)
|
||||
$restrictStr = "-"
|
||||
if ($restrict) {
|
||||
$restrictParts = @()
|
||||
foreach ($child in $restrict.ChildNodes) {
|
||||
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
|
||||
$restrictParts += $child.LocalName.Substring(0, [Math]::Min(4, $child.LocalName.Length))
|
||||
}
|
||||
}
|
||||
if ($restrictParts.Count -gt 0) { $restrictStr = $restrictParts -join "," }
|
||||
}
|
||||
|
||||
# Appearance format
|
||||
$formatStr = "-"
|
||||
$appearance = $f.SelectSingleNode("s:appearance", $ns)
|
||||
if ($appearance) {
|
||||
foreach ($appItem in $appearance.SelectNodes("dcscor:item", $ns)) {
|
||||
$paramNode = $appItem.SelectSingleNode("dcscor:parameter", $ns)
|
||||
$valNode = $appItem.SelectSingleNode("dcscor:value", $ns)
|
||||
if ($paramNode -and ($paramNode.InnerText -eq "Формат" -or $paramNode.InnerText -eq "Format") -and $valNode) {
|
||||
$formatStr = $valNode.InnerText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# presentationExpression
|
||||
$presExpr = $f.SelectSingleNode("s:presentationExpression", $ns)
|
||||
$presStr = ""
|
||||
if ($presExpr) { $presStr = " presExpr" }
|
||||
|
||||
$dpPad = $dpStr.PadRight(35)
|
||||
$titlePad = $titleStr.PadRight(22)
|
||||
$rolePad = $roleStr.PadRight(10)
|
||||
$restrictPad = $restrictStr.PadRight(12)
|
||||
|
||||
$lines.Add(" $dpPad $titlePad $rolePad $restrictPad $formatStr$presStr")
|
||||
}
|
||||
}
|
||||
|
||||
if ($Name) {
|
||||
$found = $false
|
||||
foreach ($ds in $dataSets) {
|
||||
$dsNameNode = $ds.SelectSingleNode("s:name", $ns)
|
||||
if ($dsNameNode -and $dsNameNode.InnerText -eq $Name) {
|
||||
Show-DataSetFields $ds
|
||||
$found = $true
|
||||
break
|
||||
}
|
||||
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
|
||||
$subNameNode = $subDs.SelectSingleNode("s:name", $ns)
|
||||
if ($subNameNode -and $subNameNode.InnerText -eq $Name) {
|
||||
Show-DataSetFields $subDs
|
||||
$found = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($found) { break }
|
||||
}
|
||||
if (-not $found) {
|
||||
Write-Error "Dataset '$Name' not found"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
# Show all datasets
|
||||
$first = $true
|
||||
foreach ($ds in $dataSets) {
|
||||
if (-not $first) { $lines.Add("") }
|
||||
$first = $false
|
||||
Show-DataSetFields $ds
|
||||
|
||||
$dsType = Get-DataSetType $ds
|
||||
if ($dsType -eq "Union") {
|
||||
foreach ($subDs in $ds.SelectNodes("s:item", $ns)) {
|
||||
$lines.Add("")
|
||||
Show-DataSetFields $subDs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Calculated fields
|
||||
$calcFields = $root.SelectNodes("s:calculatedField", $ns)
|
||||
if ($calcFields.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("--- calculated ---")
|
||||
foreach ($cf in $calcFields) {
|
||||
$cfPath = $cf.SelectSingleNode("s:dataPath", $ns).InnerText
|
||||
$cfExpr = $cf.SelectSingleNode("s:expression", $ns).InnerText
|
||||
$cfRestrict = $cf.SelectSingleNode("s:useRestriction", $ns)
|
||||
$restrictStr = ""
|
||||
if ($cfRestrict) {
|
||||
$parts = @()
|
||||
foreach ($child in $cfRestrict.ChildNodes) {
|
||||
if ($child.NodeType -eq "Element" -and $child.InnerText -eq "true") {
|
||||
$parts += $child.LocalName.Substring(0, [Math]::Min(4, $child.LocalName.Length))
|
||||
}
|
||||
}
|
||||
if ($parts.Count -gt 0) { $restrictStr = " restrict:" + ($parts -join ",") }
|
||||
}
|
||||
$lines.Add(" $cfPath = $cfExpr$restrictStr")
|
||||
}
|
||||
}
|
||||
|
||||
# Total fields
|
||||
$totalFields = $root.SelectNodes("s:totalField", $ns)
|
||||
if ($totalFields.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("--- totals ---")
|
||||
foreach ($tf in $totalFields) {
|
||||
$tfPath = $tf.SelectSingleNode("s:dataPath", $ns).InnerText
|
||||
$tfExpr = $tf.SelectSingleNode("s:expression", $ns).InnerText
|
||||
$tfGroup = $tf.SelectSingleNode("s:group", $ns)
|
||||
$groupStr = ""
|
||||
if ($tfGroup) { $groupStr = " [group:$($tfGroup.InnerText)]" }
|
||||
$lines.Add(" $tfPath = $tfExpr$groupStr")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE: params
|
||||
# ============================================================
|
||||
elseif ($Mode -eq "params") {
|
||||
|
||||
$params = $root.SelectNodes("s:parameter", $ns)
|
||||
$lines.Add("=== Parameters ($($params.Count)) ===")
|
||||
$lines.Add(" Name Type Default Visible Expression")
|
||||
|
||||
foreach ($p in $params) {
|
||||
$pName = $p.SelectSingleNode("s:name", $ns).InnerText
|
||||
$pType = Get-CompactType $p.SelectSingleNode("s:valueType", $ns)
|
||||
if (-not $pType) { $pType = "-" }
|
||||
|
||||
# Default value
|
||||
$valNode = $p.SelectSingleNode("s:value", $ns)
|
||||
$valStr = "-"
|
||||
if ($valNode) {
|
||||
$nilAttr = $valNode.GetAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($nilAttr -eq "true") {
|
||||
$valStr = "null"
|
||||
} else {
|
||||
$raw = $valNode.InnerText.Trim()
|
||||
if ($raw -eq "0001-01-01T00:00:00") {
|
||||
$valStr = "-"
|
||||
} elseif ($raw) {
|
||||
# Check for StandardPeriod variant
|
||||
$variant = $valNode.SelectSingleNode("v8:variant", $ns)
|
||||
if ($variant) {
|
||||
$valStr = $variant.InnerText
|
||||
} else {
|
||||
$valStr = $raw
|
||||
if ($valStr.Length -gt 15) { $valStr = $valStr.Substring(0, 12) + "..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Visibility
|
||||
$useRestrict = $p.SelectSingleNode("s:useRestriction", $ns)
|
||||
$visStr = "yes"
|
||||
if ($useRestrict -and $useRestrict.InnerText -eq "true") { $visStr = "hidden" }
|
||||
if ($useRestrict -and $useRestrict.InnerText -eq "false") { $visStr = "yes" }
|
||||
|
||||
# Expression
|
||||
$exprNode = $p.SelectSingleNode("s:expression", $ns)
|
||||
$exprStr = "-"
|
||||
if ($exprNode -and $exprNode.InnerText.Trim()) {
|
||||
$exprStr = Unescape-Xml $exprNode.InnerText.Trim()
|
||||
}
|
||||
|
||||
# availableAsField
|
||||
$availField = $p.SelectSingleNode("s:availableAsField", $ns)
|
||||
$availStr = ""
|
||||
if ($availField -and $availField.InnerText -eq "false") { $availStr = " [noField]" }
|
||||
|
||||
$namePad = $pName.PadRight(33)
|
||||
$typePad = $pType.PadRight(22)
|
||||
$valPad = $valStr.PadRight(16)
|
||||
$visPad = $visStr.PadRight(8)
|
||||
|
||||
$lines.Add(" $namePad $typePad $valPad $visPad $exprStr$availStr")
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE: variant
|
||||
# ============================================================
|
||||
elseif ($Mode -eq "variant") {
|
||||
|
||||
$variants = $root.SelectNodes("s:settingsVariant", $ns)
|
||||
|
||||
$targetVariant = $null
|
||||
if ($Name) {
|
||||
# Try by name
|
||||
$varIdx = 0
|
||||
foreach ($v in $variants) {
|
||||
$varIdx++
|
||||
$vName = $v.SelectSingleNode("dcsset:name", $ns).InnerText
|
||||
if ($vName -eq $Name -or "$varIdx" -eq $Name) {
|
||||
$targetVariant = $v
|
||||
$matchIdx = $varIdx
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $targetVariant) {
|
||||
Write-Error "Variant '$Name' not found"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
# Take first
|
||||
$targetVariant = $variants[0]
|
||||
$matchIdx = 1
|
||||
if (-not $targetVariant) {
|
||||
Write-Error "No variants found"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$vName = $targetVariant.SelectSingleNode("dcsset:name", $ns).InnerText
|
||||
$vPres = $targetVariant.SelectSingleNode("dcsset:presentation", $ns)
|
||||
$vPresStr = ""
|
||||
if ($vPres) {
|
||||
$pt = Get-MLText $vPres
|
||||
if ($pt) { $vPresStr = " `"$pt`"" }
|
||||
}
|
||||
|
||||
$lines.Add("=== Variant [$matchIdx]: $vName$vPresStr ===")
|
||||
|
||||
$settings = $targetVariant.SelectSingleNode("dcsset:settings", $ns)
|
||||
if (-not $settings) {
|
||||
$lines.Add(" (empty settings)")
|
||||
} else {
|
||||
# Selection at settings level
|
||||
$topSel = Get-SelectionFields $settings
|
||||
if ($topSel.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Selection: " + ($topSel -join ", "))
|
||||
}
|
||||
|
||||
# Structure
|
||||
$structItems = $settings.SelectNodes("dcsset:item", $ns)
|
||||
$hasStruct = $false
|
||||
foreach ($si in $structItems) {
|
||||
$siXsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($siXsiType -like "*StructureItem*") { $hasStruct = $true; break }
|
||||
}
|
||||
|
||||
if ($hasStruct) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Structure:")
|
||||
foreach ($si in $structItems) {
|
||||
$siXsiType = $si.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
if ($siXsiType -like "*StructureItem*") {
|
||||
Build-StructureTree -itemNode $si -prefix " " -isLast $false -outLines $lines
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Filter
|
||||
$filters = Get-FilterSummary $settings
|
||||
if ($filters.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Filter:")
|
||||
foreach ($f in $filters) {
|
||||
$lines.Add(" $f")
|
||||
}
|
||||
}
|
||||
|
||||
# Data parameters
|
||||
$dataParams = $settings.SelectNodes("dcsset:dataParameters/dcsset:item", $ns)
|
||||
if ($dataParams.Count -gt 0) {
|
||||
$dpStrs = @()
|
||||
foreach ($dp in $dataParams) {
|
||||
$dpParam = $dp.SelectSingleNode("dcscor:parameter", $ns)
|
||||
$dpVal = $dp.SelectSingleNode("dcscor:value", $ns)
|
||||
if ($dpParam -and $dpVal) {
|
||||
$dpStrs += "$($dpParam.InnerText)=`"$($dpVal.InnerText)`""
|
||||
}
|
||||
}
|
||||
if ($dpStrs.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("DataParams: " + ($dpStrs -join ", "))
|
||||
}
|
||||
}
|
||||
|
||||
# Output parameters
|
||||
$outParams = $settings.SelectNodes("dcsset:outputParameters/dcscor:item", $ns)
|
||||
if ($outParams.Count -gt 0) {
|
||||
$opStrs = @()
|
||||
foreach ($op in $outParams) {
|
||||
$opParam = $op.SelectSingleNode("dcscor:parameter", $ns)
|
||||
$opVal = $op.SelectSingleNode("dcscor:value", $ns)
|
||||
if ($opParam -and $opVal) {
|
||||
$paramName = $opParam.InnerText
|
||||
$paramVal = $opVal.InnerText
|
||||
# Shorten known long names
|
||||
switch ($paramName) {
|
||||
"МакетОформления" { $opStrs += "style=$paramVal" }
|
||||
"РасположениеПолейГруппировки" { $opStrs += "groups=$paramVal" }
|
||||
"ГоризонтальноеРасположениеОбщихИтогов" { $opStrs += "totalsH=$paramVal" }
|
||||
"ВертикальноеРасположениеОбщихИтогов" { $opStrs += "totalsV=$paramVal" }
|
||||
"ВыводитьЗаголовок" { $opStrs += "header=$paramVal" }
|
||||
"ВыводитьОтбор" { $opStrs += "filter=$paramVal" }
|
||||
"ВыводитьПараметрыДанных" { $opStrs += "dataParams=$paramVal" }
|
||||
"РасположениеРеквизитов" { $opStrs += "attrs=$paramVal" }
|
||||
default { $opStrs += "$paramName=$paramVal" }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($opStrs.Count -gt 0) {
|
||||
$lines.Add("")
|
||||
$lines.Add("Output: " + ($opStrs -join " "))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Output ---
|
||||
|
||||
$result = $lines.ToArray()
|
||||
$totalLines = $result.Count
|
||||
|
||||
# OutFile
|
||||
if ($OutFile) {
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllLines((Join-Path (Get-Location) $OutFile), $result, $utf8Bom)
|
||||
Write-Host "Written $totalLines lines to $OutFile"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Pagination
|
||||
if ($Offset -gt 0) {
|
||||
if ($Offset -ge $totalLines) {
|
||||
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
|
||||
exit 0
|
||||
}
|
||||
$result = $result[$Offset..($totalLines - 1)]
|
||||
}
|
||||
|
||||
if ($result.Count -gt $Limit) {
|
||||
$shown = $result[0..($Limit - 1)]
|
||||
foreach ($l in $shown) { Write-Host $l }
|
||||
Write-Host ""
|
||||
Write-Host "[TRUNCATED] Shown $Limit of $totalLines lines. Use -Offset $($Offset + $Limit) to continue."
|
||||
} else {
|
||||
foreach ($l in $result) { Write-Host $l }
|
||||
}
|
||||
+22
-1
@@ -446,7 +446,28 @@ DataCompositionSchema
|
||||
|---|---|---|
|
||||
| `dataPath` | да | Путь к полю |
|
||||
| `expression` | да | Агрегатная функция: `Сумма(...)`, `Количество(...)`, `Максимум(...)`, `Минимум(...)`, `Среднее(...)` |
|
||||
| `group` | нет | Для какой группировки считать итоги |
|
||||
| `group` | нет | Имя группировки, для которой считать итоги. Без `group` — для всех группировок |
|
||||
|
||||
### Разные формулы для разных группировок
|
||||
|
||||
Одно поле может иметь несколько записей `totalField` с разными формулами для разных группировок:
|
||||
|
||||
```xml
|
||||
<!-- Для группировки "ОбъектМетаданных" — агрегация самого поля -->
|
||||
<totalField>
|
||||
<dataPath>ПравоИнтерактивное</dataPath>
|
||||
<expression>Максимум(ПравоИнтерактивное)</expression>
|
||||
<group>ОбъектМетаданных</group>
|
||||
</totalField>
|
||||
<!-- Для группировки "Отчет" — агрегация другого поля -->
|
||||
<totalField>
|
||||
<dataPath>ПравоИнтерактивное</dataPath>
|
||||
<expression>Максимум(ПравоОтчета)</expression>
|
||||
<group>Отчет</group>
|
||||
</totalField>
|
||||
```
|
||||
|
||||
Это позволяет вычислять ресурс по-разному в зависимости от контекста группировки.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user