Files
cc-1c-skills/.github/skills/meta-info/scripts/meta-info.ps1
T
2026-05-17 11:22:33 +00:00

1127 lines
43 KiB
PowerShell

# meta-info v1.1 — Compact summary of 1C metadata object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true)][Alias('Path')][string]$ObjectPath,
[ValidateSet("overview","brief","full")]
[string]$Mode = "overview",
[string]$Name,
[int]$Limit = 150,
[int]$Offset = 0,
[string]$OutFile
)
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# --- Output helper (always collect, paginate at the end) ---
$script:lines = @()
function Out([string]$text) { $script:lines += $text }
# --- Resolve path ---
if (-not [System.IO.Path]::IsPathRooted($ObjectPath)) {
$ObjectPath = Join-Path (Get-Location).Path $ObjectPath
}
# Directory -> find XML inside or as sibling
if (Test-Path $ObjectPath -PathType Container) {
$dirName = Split-Path $ObjectPath -Leaf
$candidate = Join-Path $ObjectPath "$dirName.xml"
$sibling = Join-Path (Split-Path $ObjectPath) "$dirName.xml"
if (Test-Path $candidate) {
$ObjectPath = $candidate
} elseif (Test-Path $sibling) {
$ObjectPath = $sibling
} else {
$xmlFiles = @(Get-ChildItem $ObjectPath -Filter "*.xml" -File | Select-Object -First 1)
if ($xmlFiles.Count -gt 0) {
$ObjectPath = $xmlFiles[0].FullName
} else {
Write-Host "[ERROR] No XML file found in directory: $ObjectPath"
exit 1
}
}
}
# File not found — check Dir/Name/Name.xml → Dir/Name.xml (common LLM mistake)
if (-not (Test-Path $ObjectPath)) {
$fileName = [System.IO.Path]::GetFileNameWithoutExtension($ObjectPath)
$parentDir = Split-Path $ObjectPath
$parentDirName = Split-Path $parentDir -Leaf
if ($fileName -eq $parentDirName) {
$candidate = Join-Path (Split-Path $parentDir) "$fileName.xml"
if (Test-Path $candidate) { $ObjectPath = $candidate }
}
}
if (-not (Test-Path $ObjectPath)) {
Write-Host "[ERROR] File not found: $ObjectPath"
exit 1
}
# --- Load XML ---
[xml]$xmlDoc = Get-Content -Path $ObjectPath -Encoding UTF8
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
$ns.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core")
$ns.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
$ns.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
$ns.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema")
$ns.AddNamespace("cfg", "http://v8.1c.ru/8.1/data/enterprise/current-config")
$ns.AddNamespace("app", "http://v8.1c.ru/8.2/managed-application/core")
$mdRoot = $xmlDoc.SelectSingleNode("/md:MetaDataObject", $ns)
if (-not $mdRoot) {
Write-Host "[ERROR] Not a valid 1C metadata XML file"
exit 1
}
# --- Detect object type ---
$typeNode = $null
$mdType = ""
foreach ($child in $mdRoot.ChildNodes) {
if ($child.NodeType -eq 'Element' -and $child.NamespaceURI -eq "http://v8.1c.ru/8.3/MDClasses") {
$typeNode = $child
$mdType = $child.LocalName
break
}
}
if (-not $typeNode) {
Write-Host "[ERROR] Cannot detect metadata type"
exit 1
}
# --- Type name maps ---
$typeNameMap = @{
"Catalog"="Справочник"; "Document"="Документ"; "Enum"="Перечисление"
"Constant"="Константа"; "InformationRegister"="Регистр сведений"
"AccumulationRegister"="Регистр накопления"; "AccountingRegister"="Регистр бухгалтерии"
"CalculationRegister"="Регистр расчёта"; "ChartOfAccounts"="План счетов"
"ChartOfCharacteristicTypes"="План видов характеристик"
"ChartOfCalculationTypes"="План видов расчёта"; "BusinessProcess"="Бизнес-процесс"
"Task"="Задача"; "ExchangePlan"="План обмена"; "DocumentJournal"="Журнал документов"
"Report"="Отчёт"; "DataProcessor"="Обработка"
"DefinedType"="Определяемый тип"; "CommonModule"="Общий модуль"
"ScheduledJob"="Регламентное задание"; "EventSubscription"="Подписка на событие"
"HTTPService"="HTTP-сервис"; "WebService"="Веб-сервис"
}
$refTypeMap = @{
"CatalogRef"="СправочникСсылка"; "DocumentRef"="ДокументСсылка"
"EnumRef"="ПеречислениеСсылка"; "ChartOfAccountsRef"="ПланСчетовСсылка"
"ChartOfCharacteristicTypesRef"="ПВХСсылка"; "ChartOfCalculationTypesRef"="ПВРСсылка"
"ExchangePlanRef"="ПланОбменаСсылка"; "BusinessProcessRef"="БизнесПроцессСсылка"
"TaskRef"="ЗадачаСсылка"
}
$regTypeMap = @{
"AccumulationRegister"="РН"; "AccountingRegister"="РБ"; "CalculationRegister"="РР"
"InformationRegister"="РС"
}
$periodMap = @{
"Nonperiodical"="Непериодический"; "Day"="День"; "Month"="Месяц"
"Quarter"="Квартал"; "Year"="Год"; "Second"="Секунда"
}
$writeModeMap = @{
"Independent"="независимая"; "RecorderSubordinate"="подчинение регистратору"
}
$reuseMap = @{
"DontUse"="нет"; "DuringRequest"="на время вызова"; "DuringSession"="на время сеанса"
}
$eventMap = @{
"BeforeWrite"="ПередЗаписью"; "OnWrite"="ПриЗаписи"; "AfterWrite"="ПослеЗаписи"
"BeforeDelete"="ПередУдалением"; "Posting"="ОбработкаПроведения"
"UndoPosting"="ОбработкаУдаленияПроведения"
"OnReadAtServer"="ПриЧтенииНаСервере"
"FillCheckProcessing"="ОбработкаПроверкиЗаполнения"
}
$objectTypeMap = @{
"CatalogObject"="СправочникОбъект"; "DocumentObject"="ДокументОбъект"
"ChartOfAccountsObject"="ПланСчетовОбъект"
"ChartOfCharacteristicTypesObject"="ПВХОбъект"
"BusinessProcessObject"="БизнесПроцессОбъект"; "TaskObject"="ЗадачаОбъект"
"ExchangePlanObject"="ПланОбменаОбъект"
"InformationRegisterRecordSet"="НаборЗаписейРС"
"AccumulationRegisterRecordSet"="НаборЗаписейРН"
"AccountingRegisterRecordSet"="НаборЗаписейРБ"
}
$numberPeriodMap = @{
"Year"="по году"; "Quarter"="по кварталу"; "Month"="по месяцу"; "Day"="по дню"
"WholeCatalog"="сквозная"
}
$ruTypeName = if ($typeNameMap.ContainsKey($mdType)) { $typeNameMap[$mdType] } else { $mdType }
# --- Helpers ---
function Get-MLText($node) {
if (-not $node) { return "" }
$c = $node.SelectSingleNode("v8:item[v8:lang='ru']/v8:content", $ns)
if ($c) { return $c.InnerText }
$c = $node.SelectSingleNode("v8:item/v8:content", $ns)
if ($c) { return $c.InnerText }
$text = $node.InnerText.Trim()
if ($text) { return $text }
return ""
}
function Format-Type($typeNode) {
if (-not $typeNode) { return "" }
$types = @()
foreach ($t in $typeNode.SelectNodes("v8:Type", $ns)) {
$raw = $t.InnerText
$types += Format-SingleType $raw $typeNode
}
foreach ($t in $typeNode.SelectNodes("v8:TypeSet", $ns)) {
$raw = $t.InnerText
if ($raw -match '^cfg:DefinedType\.(.+)$') {
$types += "ОпределяемыйТип.$($Matches[1])"
} elseif ($raw -match '^cfg:Characteristic\.(.+)$') {
$types += "Характеристика.$($Matches[1])"
} else {
$types += $raw
}
}
if ($types.Count -eq 0) { return "" }
if ($types.Count -eq 1) { return $types[0] }
return ($types -join " | ")
}
function Format-SingleType([string]$raw, $parentNode) {
switch -Wildcard ($raw) {
"xs:string" {
$sq = $parentNode.SelectSingleNode("v8:StringQualifiers/v8:Length", $ns)
$len = if ($sq) { $sq.InnerText } else { "" }
if ($len) { return "Строка($len)" } else { return "Строка" }
}
"xs:decimal" {
$dg = $parentNode.SelectSingleNode("v8:NumberQualifiers/v8:Digits", $ns)
$fr = $parentNode.SelectSingleNode("v8:NumberQualifiers/v8:FractionDigits", $ns)
$d = if ($dg) { $dg.InnerText } else { "" }
$f = if ($fr) { $fr.InnerText } else { "0" }
if ($d) { return "Число($d,$f)" } else { return "Число" }
}
"xs:boolean" { return "Булево" }
"xs:dateTime" {
$dq = $parentNode.SelectSingleNode("v8:DateQualifiers/v8:DateFractions", $ns)
if ($dq) {
switch ($dq.InnerText) {
"Date" { return "Дата" }
"Time" { return "Время" }
"DateTime" { return "ДатаВремя" }
default { return "Дата" }
}
}
return "ДатаВремя"
}
"v8:ValueStorage" { return "ХранилищеЗначения" }
"v8:UUID" { return "УникальныйИдентификатор" }
"v8:Null" { return "Null" }
default {
# Normalize d5p1:/dNpN: -> cfg:
$raw = $raw -replace '^d\d+p\d+:', 'cfg:'
# cfg:CatalogRef.Xxx -> СправочникСсылка.Xxx
if ($raw -match '^cfg:(\w+)Ref\.(.+)$') {
$prefix = "$($Matches[1])Ref"
$objn = $Matches[2]
if ($refTypeMap.ContainsKey($prefix)) {
return "$($refTypeMap[$prefix]).$objn"
}
}
# cfg:EnumRef.Xxx
if ($raw -match '^cfg:EnumRef\.(.+)$') {
return "ПеречислениеСсылка.$($Matches[1])"
}
# cfg:Characteristic.Xxx
if ($raw -match '^cfg:Characteristic\.(.+)$') {
return "Характеристика.$($Matches[1])"
}
# cfg:DefinedType.Xxx
if ($raw -match '^cfg:DefinedType\.(.+)$') {
return "ОпределяемыйТип.$($Matches[1])"
}
# Strip cfg: prefix for unknown
if ($raw -match '^cfg:(.+)$') {
return $Matches[1]
}
return $raw
}
}
}
function Format-Flags($propsNode, [bool]$isDimension = $false) {
$flags = @()
$fc = $propsNode.SelectSingleNode("md:FillChecking", $ns)
if ($fc -and $fc.InnerText -eq "ShowError") { $flags += "обязательный" }
$idx = $propsNode.SelectSingleNode("md:Indexing", $ns)
if ($idx) {
switch ($idx.InnerText) {
"Index" { $flags += "индекс" }
"IndexWithAdditionalOrder" { $flags += "индекс+доп" }
}
}
if ($isDimension) {
$master = $propsNode.SelectSingleNode("md:Master", $ns)
if ($master -and $master.InnerText -eq "true") { $flags += "ведущее" }
}
$ml = $propsNode.SelectSingleNode("md:MultiLine", $ns)
if ($ml -and $ml.InnerText -eq "true") { $flags += "многострочный" }
$use = $propsNode.SelectSingleNode("md:Use", $ns)
if ($use) {
switch ($use.InnerText) {
"ForFolder" { $flags += "для папок" }
"ForFolderAndItem" { $flags += "для папок и элементов" }
}
}
if ($flags.Count -eq 0) { return "" }
return " [$($flags -join ', ')]"
}
function Get-Attributes($parentNode, [string]$childTag = "Attribute", [bool]$isDimension = $false) {
$result = @()
foreach ($attr in $parentNode.SelectNodes("md:$childTag", $ns)) {
$aprops = $attr.SelectSingleNode("md:Properties", $ns)
if (-not $aprops) { continue }
$attrName = $aprops.SelectSingleNode("md:Name", $ns).InnerText
$typeStr = Format-Type $aprops.SelectSingleNode("md:Type", $ns)
$aflags = Format-Flags $aprops $isDimension
$result += @{ Name=$attrName; Type=$typeStr; Flags=$aflags; Props=$aprops }
}
return $result
}
function Get-TabularSections($parentNode) {
$result = @()
foreach ($ts in $parentNode.SelectNodes("md:TabularSection", $ns)) {
$tprops = $ts.SelectSingleNode("md:Properties", $ns)
$tsName = $tprops.SelectSingleNode("md:Name", $ns).InnerText
$tchildObjs = $ts.SelectSingleNode("md:ChildObjects", $ns)
$cols = @()
if ($tchildObjs) { $cols = @(Get-Attributes $tchildObjs) }
$result += @{ Name=$tsName; Columns=$cols; ColCount=$cols.Count }
}
return $result
}
function Format-AttrLine([hashtable]$attr, [int]$maxNameLen = 30) {
$padded = $attr.Name.PadRight($maxNameLen)
return " $padded $($attr.Type)$($attr.Flags)"
}
function Get-MaxNameLen($attrs) {
$max = 10
foreach ($a in $attrs) {
if ($a.Name.Length -gt $max) { $max = $a.Name.Length }
}
return [Math]::Min($max + 2, 40)
}
function Get-SimpleChildren($parentNode, [string]$tag) {
$result = @()
foreach ($child in $parentNode.SelectNodes("md:$tag", $ns)) {
$result += $child.InnerText
}
return $result
}
function Sort-AttrsRefFirst($attrs) {
$refs = @()
$prims = @()
foreach ($a in $attrs) {
$t = $a.Type
if ($t -match 'Ссылка\.' -or $t -match 'Характеристика\.' -or $t -match 'ОпределяемыйТип\.' -or $t -match 'ПланСчетовСсылка' -or $t -match 'ПВХСсылка' -or $t -match 'ПВРСсылка') {
$refs += $a
} else {
$prims += $a
}
}
return @($refs) + @($prims)
}
function Decline-Cols([int]$n) {
$m = $n % 10
$h = $n % 100
if ($h -ge 11 -and $h -le 19) { return "колонок" }
if ($m -eq 1) { return "колонка" }
if ($m -ge 2 -and $m -le 4) { return "колонки" }
return "колонок"
}
function Format-SourceType([string]$raw) {
$raw = $raw -replace '^d\d+p\d+:', 'cfg:'
if ($raw -match '^cfg:(\w+)\.(.+)$') {
$prefix = $Matches[1]; $name = $Matches[2]
if ($objectTypeMap.ContainsKey($prefix)) { return "$($objectTypeMap[$prefix]).$name" }
}
if ($raw -match '^cfg:(.+)$') { return $Matches[1] }
return $raw
}
function Get-HTTPEndpoints($childObjs) {
$result = @()
foreach ($tpl in $childObjs.SelectNodes("md:URLTemplate", $ns)) {
$tp = $tpl.SelectSingleNode("md:Properties", $ns)
$tplName = $tp.SelectSingleNode("md:Name", $ns).InnerText
$template = $tp.SelectSingleNode("md:Template", $ns).InnerText
$methods = @()
$tplCO = $tpl.SelectSingleNode("md:ChildObjects", $ns)
if ($tplCO) {
foreach ($m in $tplCO.SelectNodes("md:Method", $ns)) {
$mp = $m.SelectSingleNode("md:Properties", $ns)
$httpMethod = $mp.SelectSingleNode("md:HTTPMethod", $ns).InnerText
$handler = $mp.SelectSingleNode("md:Handler", $ns).InnerText
$methods += @{ HTTPMethod=$httpMethod; Handler=$handler; Name=$mp.SelectSingleNode("md:Name",$ns).InnerText }
}
}
$result += @{ Name=$tplName; Template=$template; Methods=$methods }
}
return $result
}
function Get-WSOperations($childObjs) {
$result = @()
foreach ($op in $childObjs.SelectNodes("md:Operation", $ns)) {
$oprops = $op.SelectSingleNode("md:Properties", $ns)
$opName = $oprops.SelectSingleNode("md:Name", $ns).InnerText
$retType = $oprops.SelectSingleNode("md:XDTOReturningValueType", $ns)
$retStr = if ($retType -and $retType.InnerText) { $retType.InnerText } else { "void" }
$procName = $oprops.SelectSingleNode("md:ProcedureName", $ns)
$params = @()
$opCO = $op.SelectSingleNode("md:ChildObjects", $ns)
if ($opCO) {
foreach ($p in $opCO.SelectNodes("md:Parameter", $ns)) {
$pp = $p.SelectSingleNode("md:Properties", $ns)
$pName = $pp.SelectSingleNode("md:Name", $ns).InnerText
$pType = $pp.SelectSingleNode("md:XDTOValueType", $ns)
$pTypeStr = if ($pType) { $pType.InnerText } else { "?" }
$dir = $pp.SelectSingleNode("md:TransferDirection", $ns)
$dirStr = if ($dir -and $dir.InnerText -ne "In") { " [$($dir.InnerText.ToLower())]" } else { "" }
$params += "${pName}: ${pTypeStr}${dirStr}"
}
}
$paramStr = $params -join ", "
$result += @{ Name=$opName; Params=$paramStr; ReturnType=$retStr; ProcName=$(if ($procName) { $procName.InnerText } else { "" }) }
}
return $result
}
# --- Extract metadata ---
$props = $typeNode.SelectSingleNode("md:Properties", $ns)
$childObjs = $typeNode.SelectSingleNode("md:ChildObjects", $ns)
$objName = $props.SelectSingleNode("md:Name", $ns).InnerText
$synNode = $props.SelectSingleNode("md:Synonym", $ns)
$synonym = Get-MLText $synNode
# --- Handle -Name drill-down ---
$drillDone = $false
if ($Name -and $childObjs) {
# Search in attributes/dimensions/resources
$attrTags = @("Attribute","Dimension","Resource")
foreach ($tag in $attrTags) {
if ($drillDone) { break }
foreach ($attr in $childObjs.SelectNodes("md:$tag", $ns)) {
$ap = $attr.SelectSingleNode("md:Properties", $ns)
if (-not $ap) { continue }
$an = $ap.SelectSingleNode("md:Name", $ns).InnerText
if ($an -eq $Name) {
$tagRu = switch ($tag) {
"Attribute" { "Реквизит" }
"Dimension" { "Измерение" }
"Resource" { "Ресурс" }
}
Out "$($tagRu): $an"
$typeStr = Format-Type $ap.SelectSingleNode("md:Type", $ns)
Out " Тип: $typeStr"
$fc = $ap.SelectSingleNode("md:FillChecking", $ns)
Out " Обязательный: $(if ($fc -and $fc.InnerText -eq 'ShowError') { 'да' } else { 'нет' })"
$idx = $ap.SelectSingleNode("md:Indexing", $ns)
$idxRu = if (-not $idx -or $idx.InnerText -eq 'DontIndex') { 'нет' }
elseif ($idx.InnerText -eq 'Index') { 'Индекс' }
elseif ($idx.InnerText -eq 'IndexWithAdditionalOrder') { 'Индекс с доп. упорядочиванием' }
else { $idx.InnerText }
Out " Индексирование: $idxRu"
$ml = $ap.SelectSingleNode("md:MultiLine", $ns)
if ($ml -and $ml.InnerText -eq "true") { Out " Многострочный: да" }
$use = $ap.SelectSingleNode("md:Use", $ns)
if ($use -and $use.InnerText -ne "ForItem") {
$useRu = switch ($use.InnerText) {
"ForFolder" { "для папок" }
"ForFolderAndItem" { "для папок и элементов" }
default { $use.InnerText }
}
Out " Использование: $useRu"
}
$fv = $ap.SelectSingleNode("md:FillValue", $ns)
if ($fv -and -not ($fv.GetAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance") -eq "true") -and $fv.InnerText) {
$fvText = $fv.InnerText
if ($fvText -match '\.EmptyRef$') { $fvText = "Пустая ссылка" }
elseif ($fvText -eq "false") { $fvText = "Ложь" }
elseif ($fvText -eq "true") { $fvText = "Истина" }
Out " Значение заполнения: $fvText"
} else {
Out " Значение заполнения: —"
}
if ($tag -eq "Dimension") {
$master = $ap.SelectSingleNode("md:Master", $ns)
Out " Ведущее: $(if ($master -and $master.InnerText -eq 'true') { 'да' } else { 'нет' })"
$mf = $ap.SelectSingleNode("md:MainFilter", $ns)
Out " Основной отбор: $(if ($mf -and $mf.InnerText -eq 'true') { 'да' } else { 'нет' })"
}
$synA = $ap.SelectSingleNode("md:Synonym", $ns)
$synText = Get-MLText $synA
if ($synText -and $synText -ne $an) { Out " Синоним: $synText" }
$drillDone = $true
break
}
}
}
# Search in tabular sections
if (-not $drillDone) {
foreach ($ts in $childObjs.SelectNodes("md:TabularSection", $ns)) {
$tp = $ts.SelectSingleNode("md:Properties", $ns)
$tn = $tp.SelectSingleNode("md:Name", $ns).InnerText
if ($tn -eq $Name) {
$tsCO = $ts.SelectSingleNode("md:ChildObjects", $ns)
$cols = @()
if ($tsCO) { $cols = @(Get-Attributes $tsCO) }
Out "ТЧ: $tn ($($cols.Count) $(Decline-Cols $cols.Count)):"
if ($cols.Count -gt 0) {
$ml = Get-MaxNameLen $cols
foreach ($c in $cols) { Out (Format-AttrLine $c $ml) }
}
$drillDone = $true
break
}
}
}
# Search in enum values
if (-not $drillDone) {
foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
$ep = $ev.SelectSingleNode("md:Properties", $ns)
$en = $ep.SelectSingleNode("md:Name", $ns).InnerText
if ($en -eq $Name) {
$synE = $ep.SelectSingleNode("md:Synonym", $ns)
$synText = Get-MLText $synE
Out "Значение перечисления: $en"
if ($synText) { Out " Синоним: `"$synText`"" }
$cm = $ep.SelectSingleNode("md:Comment", $ns)
if ($cm -and $cm.InnerText) { Out " Комментарий: $($cm.InnerText)" }
$drillDone = $true
break
}
}
}
# Search in HTTPService URLTemplates
if (-not $drillDone -and $mdType -eq "HTTPService" -and $childObjs) {
foreach ($tpl in $childObjs.SelectNodes("md:URLTemplate", $ns)) {
$tp = $tpl.SelectSingleNode("md:Properties", $ns)
if ($tp.SelectSingleNode("md:Name", $ns).InnerText -eq $Name) {
$template = $tp.SelectSingleNode("md:Template", $ns).InnerText
Out "Шаблон URL: $Name"
Out " Путь: $template"
$tplCO = $tpl.SelectSingleNode("md:ChildObjects", $ns)
if ($tplCO) {
foreach ($m in $tplCO.SelectNodes("md:Method", $ns)) {
$mp = $m.SelectSingleNode("md:Properties", $ns)
$httpMethod = $mp.SelectSingleNode("md:HTTPMethod", $ns).InnerText
$handler = $mp.SelectSingleNode("md:Handler", $ns).InnerText
Out " $httpMethod$handler"
}
}
$drillDone = $true; break
}
}
}
# Search in WebService Operations
if (-not $drillDone -and $mdType -eq "WebService" -and $childObjs) {
foreach ($op in $childObjs.SelectNodes("md:Operation", $ns)) {
$oprops = $op.SelectSingleNode("md:Properties", $ns)
if ($oprops.SelectSingleNode("md:Name", $ns).InnerText -eq $Name) {
Out "Операция: $Name"
$retType = $oprops.SelectSingleNode("md:XDTOReturningValueType", $ns)
Out " Возвращает: $(if ($retType -and $retType.InnerText) { $retType.InnerText } else { 'void' })"
$procName = $oprops.SelectSingleNode("md:ProcedureName", $ns)
if ($procName -and $procName.InnerText) { Out " Процедура: $($procName.InnerText)" }
$comment = $oprops.SelectSingleNode("md:Comment", $ns)
if ($comment -and $comment.InnerText) { Out " Комментарий: $($comment.InnerText)" }
$opCO = $op.SelectSingleNode("md:ChildObjects", $ns)
if ($opCO) {
$params = $opCO.SelectNodes("md:Parameter", $ns)
if ($params.Count -gt 0) {
Out " Параметры:"
foreach ($p in $params) {
$pp = $p.SelectSingleNode("md:Properties", $ns)
$pName = $pp.SelectSingleNode("md:Name", $ns).InnerText
$pType = $pp.SelectSingleNode("md:XDTOValueType", $ns)
$dir = $pp.SelectSingleNode("md:TransferDirection", $ns)
$dirStr = if ($dir -and $dir.InnerText -ne "In") { " [$($dir.InnerText.ToLower())]" } else { "" }
Out " ${pName}: $(if ($pType) { $pType.InnerText } else { '?' })${dirStr}"
}
}
}
$drillDone = $true; break
}
}
}
if (-not $drillDone) {
Write-Host "[ERROR] '$Name' not found in $objName"
exit 1
}
}
# --- Main output (not drill-down) ---
if (-not $drillDone) {
# --- Build header ---
$header = "=== $ruTypeName`: $objName"
if ($synonym -and $synonym -ne $objName) { $header += "`"$synonym`"" }
$header += " ==="
Out $header
# --- Mode: brief ---
if ($Mode -eq "brief") {
# Attributes
$attrs = @()
if ($childObjs) { $attrs = @(Get-Attributes $childObjs) }
if ($attrs.Count -gt 0) {
$names = ($attrs | ForEach-Object { $_.Name }) -join ", "
Out "Реквизиты ($($attrs.Count)): $names"
}
# Dimensions/Resources for registers
if ($mdType -match "Register$") {
$dims = @()
if ($childObjs) { $dims = @(Get-Attributes $childObjs "Dimension" $true) }
if ($dims.Count -gt 0) {
$names = ($dims | ForEach-Object { $_.Name }) -join ", "
Out "Измерения ($($dims.Count)): $names"
}
$res = @()
if ($childObjs) { $res = @(Get-Attributes $childObjs "Resource") }
if ($res.Count -gt 0) {
$names = ($res | ForEach-Object { $_.Name }) -join ", "
Out "Ресурсы ($($res.Count)): $names"
}
}
# Tabular sections
$tss = @()
if ($childObjs) { $tss = @(Get-TabularSections $childObjs) }
if ($tss.Count -gt 0) {
$tsParts = $tss | ForEach-Object { "$($_.Name)($($_.ColCount))" }
Out "ТЧ ($($tss.Count)): $($tsParts -join ', ')"
}
# Enum values
if ($mdType -eq "Enum") {
$vals = @()
if ($childObjs) {
foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
$ep = $ev.SelectSingleNode("md:Properties", $ns)
$vals += $ep.SelectSingleNode("md:Name", $ns).InnerText
}
}
if ($vals.Count -gt 0) {
Out "Значения ($($vals.Count)): $($vals -join ', ')"
}
}
# DefinedType brief
if ($mdType -eq "DefinedType") {
$typeNode2 = $props.SelectSingleNode("md:Type", $ns)
if ($typeNode2) {
$types = @()
foreach ($t in $typeNode2.SelectNodes("v8:Type", $ns)) {
$types += Format-SingleType $t.InnerText $typeNode2
}
if ($types.Count -gt 0) {
Out "Типы ($($types.Count)): $($types -join ', ')"
}
}
}
# CommonModule brief (same as overview — already compact)
if ($mdType -eq "CommonModule") {
$flags = @()
if ($props.SelectSingleNode("md:Global", $ns).InnerText -eq "true") { $flags += "Глобальный" }
if ($props.SelectSingleNode("md:Server", $ns).InnerText -eq "true") { $flags += "Сервер" }
if ($props.SelectSingleNode("md:ServerCall", $ns).InnerText -eq "true") { $flags += "Вызов сервера" }
if ($props.SelectSingleNode("md:ClientManagedApplication", $ns).InnerText -eq "true") { $flags += "Клиент управляемое" }
if ($props.SelectSingleNode("md:ClientOrdinaryApplication", $ns).InnerText -eq "true") { $flags += "Обычный клиент" }
if ($props.SelectSingleNode("md:ExternalConnection", $ns).InnerText -eq "true") { $flags += "Внешнее соединение" }
if ($props.SelectSingleNode("md:Privileged", $ns).InnerText -eq "true") { $flags += "Привилегированный" }
$reuse = $props.SelectSingleNode("md:ReturnValuesReuse", $ns)
if ($reuse -and $reuse.InnerText -ne "DontUse") {
$reuseRu = if ($reuseMap.ContainsKey($reuse.InnerText)) { $reuseMap[$reuse.InnerText] } else { $reuse.InnerText }
$flags += "Повторное использование: $reuseRu"
}
if ($flags.Count -gt 0) { Out ($flags -join " | ") }
}
# ScheduledJob brief (same as overview — already compact)
if ($mdType -eq "ScheduledJob") {
$method = $props.SelectSingleNode("md:MethodName", $ns)
if ($method -and $method.InnerText) {
$mName = $method.InnerText
if ($mName -match '^CommonModule\.(.+)$') { $mName = $Matches[1] }
Out "Метод: $mName"
}
$sjParts = @()
$use = $props.SelectSingleNode("md:Use", $ns)
$sjParts += "Использование: $(if ($use -and $use.InnerText -eq 'true') { 'да' } else { 'нет' })"
$predef = $props.SelectSingleNode("md:Predefined", $ns)
$sjParts += "Предопределённое: $(if ($predef -and $predef.InnerText -eq 'true') { 'да' } else { 'нет' })"
$restartCnt = $props.SelectSingleNode("md:RestartCountOnFailure", $ns)
$restartInt = $props.SelectSingleNode("md:RestartIntervalOnFailure", $ns)
if ($restartCnt -and [int]$restartCnt.InnerText -gt 0) {
$sjParts += "Перезапуск: $($restartCnt.InnerText) (через $($restartInt.InnerText) сек)"
}
Out ($sjParts -join " | ")
}
# EventSubscription brief
if ($mdType -eq "EventSubscription") {
$esParts = @()
$event = $props.SelectSingleNode("md:Event", $ns)
if ($event -and $event.InnerText) {
$evRu = if ($eventMap.ContainsKey($event.InnerText)) { $eventMap[$event.InnerText] } else { $event.InnerText }
$esParts += "Событие: $evRu"
}
$handler = $props.SelectSingleNode("md:Handler", $ns)
if ($handler -and $handler.InnerText) {
$hName = $handler.InnerText
if ($hName -match '^CommonModule\.(.+)$') { $hName = $Matches[1] }
$esParts += "Обработчик: $hName"
}
$source = $props.SelectSingleNode("md:Source", $ns)
if ($source) {
$srcCount = $source.SelectNodes("v8:Type", $ns).Count
if ($srcCount -gt 0) { $esParts += "Источники: $srcCount" }
}
if ($esParts.Count -gt 0) { Out ($esParts -join " | ") }
}
# HTTPService brief
if ($mdType -eq "HTTPService") {
$rootURL = $props.SelectSingleNode("md:RootURL", $ns)
if ($rootURL -and $rootURL.InnerText) { Out "Корневой URL: /$($rootURL.InnerText)" }
if ($childObjs) {
$endpoints = @(Get-HTTPEndpoints $childObjs)
if ($endpoints.Count -gt 0) {
$totalMethods = ($endpoints | ForEach-Object { $_.Methods.Count } | Measure-Object -Sum).Sum
Out "Шаблоны: $($endpoints.Count) | Методы: $totalMethods"
}
}
}
# WebService brief
if ($mdType -eq "WebService") {
$nsUrl = $props.SelectSingleNode("md:Namespace", $ns)
if ($nsUrl -and $nsUrl.InnerText) { Out "Пространство имён: $($nsUrl.InnerText)" }
if ($childObjs) {
$ops = @(Get-WSOperations $childObjs)
if ($ops.Count -gt 0) { Out "Операции: $($ops.Count)" }
}
}
} else {
# --- Mode: overview / full ---
# Document-specific header properties
if ($mdType -eq "Document") {
$numType = $props.SelectSingleNode("md:NumberType", $ns)
$numLen = $props.SelectSingleNode("md:NumberLength", $ns)
$numPer = $props.SelectSingleNode("md:NumberPeriodicity", $ns)
$autoNum = $props.SelectSingleNode("md:Autonumbering", $ns)
$posting = $props.SelectSingleNode("md:Posting", $ns)
$parts = @()
if ($numType -and $numLen) {
$nt = if ($numType.InnerText -eq "String") { "Строка" } else { "Число" }
$piece = "Номер: $nt($($numLen.InnerText))"
if ($numPer) {
$perRu = if ($numberPeriodMap.ContainsKey($numPer.InnerText)) { $numberPeriodMap[$numPer.InnerText] } else { $numPer.InnerText }
$piece += ", $perRu"
}
if ($autoNum -and $autoNum.InnerText -eq "true") { $piece += ", авто" }
$parts += $piece
}
if ($posting) {
$parts += "Проведение: $(if ($posting.InnerText -eq 'Allow') { 'да' } else { 'нет' })"
}
if ($parts.Count -gt 0) { Out ($parts -join " | ") }
}
# Catalog-specific header properties
if ($mdType -eq "Catalog") {
$parts = @()
$hier = $props.SelectSingleNode("md:Hierarchical", $ns)
if ($hier -and $hier.InnerText -eq "true") {
$ht = $props.SelectSingleNode("md:HierarchyType", $ns)
$htText = if ($ht -and $ht.InnerText -eq "HierarchyFoldersAndItems") { "группы и элементы" } else { "элементы" }
$limitNode = $props.SelectSingleNode("md:LimitLevelCount", $ns)
$levelNode = $props.SelectSingleNode("md:LevelCount", $ns)
if ($limitNode -and $limitNode.InnerText -eq "true" -and $levelNode) {
$htText += ", уровней: $($levelNode.InnerText)"
} else {
$htText += ", без ограничения уровней"
}
$parts += "Иерархический: $htText"
}
$codeLen = $props.SelectSingleNode("md:CodeLength", $ns)
$descLen = $props.SelectSingleNode("md:DescriptionLength", $ns)
if ($codeLen -and [int]$codeLen.InnerText -gt 0) { $parts += "Код($($codeLen.InnerText))" }
if ($descLen -and [int]$descLen.InnerText -gt 0) { $parts += "Наименование($($descLen.InnerText))" }
if ($parts.Count -gt 0) { Out ($parts -join " | ") }
}
# Register-specific header properties
if ($mdType -match "Register$") {
$parts = @()
if ($mdType -eq "InformationRegister") {
$per = $props.SelectSingleNode("md:InformationRegisterPeriodicity", $ns)
if ($per) {
$perRu = if ($periodMap.ContainsKey($per.InnerText)) { $periodMap[$per.InnerText] } else { $per.InnerText }
$parts += "Периодичность: $perRu"
}
$wm = $props.SelectSingleNode("md:WriteMode", $ns)
if ($wm) {
$wmRu = if ($writeModeMap.ContainsKey($wm.InnerText)) { $writeModeMap[$wm.InnerText] } else { $wm.InnerText }
$parts += "Запись: $wmRu"
}
}
if ($mdType -eq "AccumulationRegister") {
$regKind = $props.SelectSingleNode("md:RegisterType", $ns)
if ($regKind) {
$rkRu = switch ($regKind.InnerText) {
"Balances" { "остатки" }
"Turnovers" { "обороты" }
default { $regKind.InnerText }
}
$parts += "Вид: $rkRu"
}
}
if ($parts.Count -gt 0) { Out ($parts -join " | ") }
}
# Constant-specific: show type
if ($mdType -eq "Constant") {
$typeStr = Format-Type $props.SelectSingleNode("md:Type", $ns)
if ($typeStr) { Out "Тип: $typeStr" }
}
# Report-specific: MainDataCompositionSchema
if ($mdType -eq "Report") {
$mainDCS = $props.SelectSingleNode("md:MainDataCompositionSchema", $ns)
if ($mainDCS -and $mainDCS.InnerText) {
$dcsName = $mainDCS.InnerText
if ($dcsName -match '\.Template\.(.+)$') { $dcsName = $Matches[1] }
Out "Основная СКД: $dcsName"
}
}
# DefinedType: show types
if ($mdType -eq "DefinedType") {
$typeNode2 = $props.SelectSingleNode("md:Type", $ns)
if ($typeNode2) {
$types = @()
foreach ($t in $typeNode2.SelectNodes("v8:Type", $ns)) {
$types += Format-SingleType $t.InnerText $typeNode2
}
if ($types.Count -gt 0) {
Out "Типы ($($types.Count)):"
foreach ($t in $types) { Out " $t" }
}
}
}
# CommonModule: show flags
if ($mdType -eq "CommonModule") {
$flags = @()
if ($props.SelectSingleNode("md:Global", $ns).InnerText -eq "true") { $flags += "Глобальный" }
if ($props.SelectSingleNode("md:Server", $ns).InnerText -eq "true") { $flags += "Сервер" }
if ($props.SelectSingleNode("md:ServerCall", $ns).InnerText -eq "true") { $flags += "Вызов сервера" }
if ($props.SelectSingleNode("md:ClientManagedApplication", $ns).InnerText -eq "true") { $flags += "Клиент управляемое" }
if ($props.SelectSingleNode("md:ClientOrdinaryApplication", $ns).InnerText -eq "true") { $flags += "Обычный клиент" }
if ($props.SelectSingleNode("md:ExternalConnection", $ns).InnerText -eq "true") { $flags += "Внешнее соединение" }
if ($props.SelectSingleNode("md:Privileged", $ns).InnerText -eq "true") { $flags += "Привилегированный" }
$reuse = $props.SelectSingleNode("md:ReturnValuesReuse", $ns)
if ($reuse -and $reuse.InnerText -ne "DontUse") {
$reuseRu = if ($reuseMap.ContainsKey($reuse.InnerText)) { $reuseMap[$reuse.InnerText] } else { $reuse.InnerText }
$flags += "Повторное использование: $reuseRu"
}
if ($flags.Count -gt 0) { Out ($flags -join " | ") }
}
# ScheduledJob: show method and flags
if ($mdType -eq "ScheduledJob") {
$method = $props.SelectSingleNode("md:MethodName", $ns)
if ($method -and $method.InnerText) {
$mName = $method.InnerText
if ($mName -match '^CommonModule\.(.+)$') { $mName = $Matches[1] }
Out "Метод: $mName"
}
$sjParts = @()
$use = $props.SelectSingleNode("md:Use", $ns)
$sjParts += "Использование: $(if ($use -and $use.InnerText -eq 'true') { 'да' } else { 'нет' })"
$predef = $props.SelectSingleNode("md:Predefined", $ns)
$sjParts += "Предопределённое: $(if ($predef -and $predef.InnerText -eq 'true') { 'да' } else { 'нет' })"
$restartCnt = $props.SelectSingleNode("md:RestartCountOnFailure", $ns)
$restartInt = $props.SelectSingleNode("md:RestartIntervalOnFailure", $ns)
if ($restartCnt -and [int]$restartCnt.InnerText -gt 0) {
$sjParts += "Перезапуск: $($restartCnt.InnerText) (через $($restartInt.InnerText) сек)"
}
Out ($sjParts -join " | ")
}
# EventSubscription: show event, handler, sources
if ($mdType -eq "EventSubscription") {
$event = $props.SelectSingleNode("md:Event", $ns)
if ($event -and $event.InnerText) {
$evRu = if ($eventMap.ContainsKey($event.InnerText)) { $eventMap[$event.InnerText] } else { $event.InnerText }
Out "Событие: $evRu"
}
$handler = $props.SelectSingleNode("md:Handler", $ns)
if ($handler -and $handler.InnerText) {
$hName = $handler.InnerText
if ($hName -match '^CommonModule\.(.+)$') { $hName = $Matches[1] }
Out "Обработчик: $hName"
}
$source = $props.SelectSingleNode("md:Source", $ns)
if ($source) {
$srcTypes = @()
foreach ($t in $source.SelectNodes("v8:Type", $ns)) {
$srcTypes += Format-SourceType $t.InnerText
}
if ($srcTypes.Count -gt 0) {
if ($Mode -eq "full") {
Out "Источники ($($srcTypes.Count)):"
foreach ($s in $srcTypes) { Out " $s" }
} else {
Out "Источники ($($srcTypes.Count))"
}
}
}
}
# HTTPService: show root URL and endpoints
if ($mdType -eq "HTTPService") {
$rootURL = $props.SelectSingleNode("md:RootURL", $ns)
if ($rootURL -and $rootURL.InnerText) { Out "Корневой URL: /$($rootURL.InnerText)" }
if ($childObjs) {
$endpoints = @(Get-HTTPEndpoints $childObjs)
if ($endpoints.Count -gt 0) {
Out ""
Out "Шаблоны URL ($($endpoints.Count)):"
foreach ($ep in $endpoints) {
Out " $($ep.Template)"
foreach ($m in $ep.Methods) {
Out " $($m.HTTPMethod.PadRight(6))$($m.Handler)"
}
}
}
}
}
# WebService: show namespace and operations
if ($mdType -eq "WebService") {
$nsUrl = $props.SelectSingleNode("md:Namespace", $ns)
if ($nsUrl -and $nsUrl.InnerText) { Out "Пространство имён: $($nsUrl.InnerText)" }
if ($childObjs) {
$ops = @(Get-WSOperations $childObjs)
if ($ops.Count -gt 0) {
Out ""
Out "Операции ($($ops.Count)):"
foreach ($op in $ops) {
Out " $($op.Name)($($op.Params)) → $($op.ReturnType)"
}
}
}
}
# --- Enum values ---
if ($mdType -eq "Enum" -and $childObjs) {
$vals = @()
foreach ($ev in $childObjs.SelectNodes("md:EnumValue", $ns)) {
$ep = $ev.SelectSingleNode("md:Properties", $ns)
$vName = $ep.SelectSingleNode("md:Name", $ns).InnerText
$vSyn = Get-MLText $ep.SelectSingleNode("md:Synonym", $ns)
$vals += @{ Name=$vName; Synonym=$vSyn }
}
if ($vals.Count -gt 0) {
Out ""
Out "Значения ($($vals.Count)):"
$ml = Get-MaxNameLen $vals
foreach ($v in $vals) {
$padded = $v.Name.PadRight($ml)
$synText = if ($v.Synonym -and $v.Synonym -ne $v.Name) { "`"$($v.Synonym)`"" } else { "" }
Out " $padded $synText"
}
}
}
# --- Dimensions (registers) ---
if ($mdType -match "Register$" -and $childObjs) {
$dims = @(Get-Attributes $childObjs "Dimension" $true)
if ($dims.Count -gt 0) {
Out ""
Out "Измерения ($($dims.Count)):"
$ml = Get-MaxNameLen $dims
foreach ($d in $dims) { Out (Format-AttrLine $d $ml) }
}
}
# --- Resources (registers) ---
if ($mdType -match "Register$" -and $childObjs) {
$res = @(Get-Attributes $childObjs "Resource")
if ($res.Count -gt 0) {
Out ""
Out "Ресурсы ($($res.Count)):"
$ml = Get-MaxNameLen $res
foreach ($r in $res) { Out (Format-AttrLine $r $ml) }
}
}
# --- Attributes ---
if ($childObjs -and $mdType -ne "Enum") {
$attrs = @(Get-Attributes $childObjs)
if ($attrs.Count -gt 0) {
Out ""
Out "Реквизиты ($($attrs.Count)):"
$sorted = Sort-AttrsRefFirst $attrs
$ml = Get-MaxNameLen $sorted
foreach ($a in $sorted) { Out (Format-AttrLine $a $ml) }
}
}
# --- Tabular sections ---
if ($childObjs -and $mdType -ne "Enum") {
$tss = @(Get-TabularSections $childObjs)
if ($tss.Count -gt 0) {
if ($Mode -eq "full") {
foreach ($ts in $tss) {
Out ""
Out "ТЧ $($ts.Name) ($($ts.ColCount) $(Decline-Cols $ts.ColCount)):"
if ($ts.ColCount -gt 0) {
$sortedCols = Sort-AttrsRefFirst $ts.Columns
$ml = Get-MaxNameLen $sortedCols
foreach ($c in $sortedCols) { Out (Format-AttrLine $c $ml) }
}
}
} else {
# overview — just names with column counts
Out ""
$tsParts = $tss | ForEach-Object { "$($_.Name)($($_.ColCount))" }
Out "ТЧ ($($tss.Count)): $($tsParts -join ', ')"
}
}
}
# Forms/Templates/Commands in overview for Reports & DataProcessors
if ($Mode -eq "overview" -and $childObjs -and ($mdType -eq "Report" -or $mdType -eq "DataProcessor")) {
$forms = @(Get-SimpleChildren $childObjs "Form")
if ($forms.Count -gt 0) { Out "Формы: $($forms -join ', ')" }
$templates = @(Get-SimpleChildren $childObjs "Template")
if ($templates.Count -gt 0) { Out "Макеты: $($templates -join ', ')" }
$commands = @(Get-SimpleChildren $childObjs "Command")
if ($commands.Count -gt 0) { Out "Команды: $($commands -join ', ')" }
}
# --- Full mode: additional sections ---
if ($Mode -eq "full" -and $childObjs) {
# Register records (documents)
if ($mdType -eq "Document") {
$regRecs = @()
foreach ($item in $props.SelectNodes("md:RegisterRecords/xr:Item", $ns)) {
$raw = $item.InnerText
if ($raw -match '^(\w+)\.(.+)$') {
$prefix = $Matches[1]
$rname = $Matches[2]
$short = if ($regTypeMap.ContainsKey($prefix)) { $regTypeMap[$prefix] } else { $prefix }
$regRecs += "$short.$rname"
} else {
$regRecs += $raw
}
}
if ($regRecs.Count -gt 0) {
Out ""
Out "Движения ($($regRecs.Count)): $($regRecs -join ', ')"
}
# BasedOn
$basedOn = @()
foreach ($item in $props.SelectNodes("md:BasedOn/xr:Item", $ns)) {
$raw = $item.InnerText
if ($raw -match '^\w+\.(.+)$') { $basedOn += $Matches[1] } else { $basedOn += $raw }
}
if ($basedOn.Count -gt 0) {
Out "Ввод на основании: $($basedOn -join ', ')"
}
}
# Forms
$forms = @(Get-SimpleChildren $childObjs "Form")
if ($forms.Count -gt 0) {
Out "Формы: $($forms -join ', ')"
}
# Templates
$templates = @(Get-SimpleChildren $childObjs "Template")
if ($templates.Count -gt 0) {
Out "Макеты: $($templates -join ', ')"
}
# Commands
$commands = @(Get-SimpleChildren $childObjs "Command")
if ($commands.Count -gt 0) {
Out "Команды: $($commands -join ', ')"
}
}
}
}
# --- Pagination and output ---
$totalLines = $script:lines.Count
$outLines = $script:lines
if ($Offset -gt 0) {
if ($Offset -ge $totalLines) {
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
exit 0
}
$outLines = $outLines[$Offset..($totalLines - 1)]
}
if ($Limit -gt 0 -and $outLines.Count -gt $Limit) {
$shown = $outLines[0..($Limit - 1)]
$remaining = $totalLines - $Offset - $Limit
$shown += ""
$shown += "[ОБРЕЗАНО] Показано $Limit из $totalLines строк. Используйте -Offset $($Offset + $Limit) для продолжения."
$outLines = $shown
}
if ($OutFile) {
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
$OutFile = Join-Path (Get-Location).Path $OutFile
}
$utf8 = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllLines($OutFile, $outLines, $utf8)
Write-Host "Output written to $OutFile"
} else {
foreach ($l in $outLines) { Write-Host $l }
}