mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 08:04:56 +03:00
Add configuration root skills (4 new cf-* skills) and guide
New skills for working with root-level 1C configuration files: - cf-info: analyze configuration structure (3 modes: brief/overview/full) - cf-init: scaffold empty configuration (Configuration.xml, ConfigDumpInfo.xml, Languages/) - cf-validate: validate structural correctness (8 checks) - cf-edit: edit properties, ChildObjects, default roles (6 operations) Also adds docs/cf-guide.md and updates README and specs index. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: cf-edit
|
||||||
|
description: Точечное редактирование конфигурации 1С (Configuration.xml) — свойства, состав ChildObjects, роли по умолчанию
|
||||||
|
argument-hint: -ConfigPath <path> -Operation <op> -Value <value>
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Write
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /cf-edit — редактирование конфигурации 1С
|
||||||
|
|
||||||
|
Точечное редактирование Configuration.xml: свойства, состав ChildObjects, роли по умолчанию.
|
||||||
|
|
||||||
|
## Параметры и команда
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `ConfigPath` | Путь к Configuration.xml или каталогу выгрузки |
|
||||||
|
| `Operation` | Операция (см. таблицу) |
|
||||||
|
| `Value` | Значение для операции (batch через `;;`) |
|
||||||
|
| `DefinitionFile` | JSON-файл с массивом операций |
|
||||||
|
| `NoValidate` | Пропустить авто-валидацию |
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\cf-edit\scripts\cf-edit.ps1 -ConfigPath '<path>' -Operation modify-property -Value 'Version=1.0.0.1'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Операции
|
||||||
|
|
||||||
|
| Операция | Формат Value | Описание |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| `modify-property` | `Ключ=Значение` (batch `;;`) | Изменить свойство |
|
||||||
|
| `add-childObject` | `Type.Name` (batch `;;`) | Добавить объект в ChildObjects |
|
||||||
|
| `remove-childObject` | `Type.Name` (batch `;;`) | Удалить объект из ChildObjects |
|
||||||
|
| `add-defaultRole` | `Role.Name` или `Name` | Добавить роль по умолчанию |
|
||||||
|
| `remove-defaultRole` | `Role.Name` или `Name` | Удалить роль по умолчанию |
|
||||||
|
| `set-defaultRoles` | Имена через `;;` | Заменить список ролей по умолчанию |
|
||||||
|
|
||||||
|
Подробнее: `reference.md` в каталоге навыка.
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Изменить версию и поставщика
|
||||||
|
... -ConfigPath test-tmp/cf -Operation modify-property -Value "Version=1.0.0.1 ;; Vendor=Фирма 1С"
|
||||||
|
|
||||||
|
# Добавить объекты
|
||||||
|
... -ConfigPath test-tmp/cf -Operation add-childObject -Value "Catalog.Товары ;; Document.Заказ"
|
||||||
|
|
||||||
|
# Удалить объект
|
||||||
|
... -ConfigPath test-tmp/cf -Operation remove-childObject -Value "Catalog.Устаревший"
|
||||||
|
|
||||||
|
# Роли по умолчанию
|
||||||
|
... -ConfigPath test-tmp/cf -Operation add-defaultRole -Value "ПолныеПрава"
|
||||||
|
... -ConfigPath test-tmp/cf -Operation set-defaultRoles -Value "ПолныеПрава ;; Администратор"
|
||||||
|
```
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# cf-edit — справочник операций
|
||||||
|
|
||||||
|
## modify-property
|
||||||
|
|
||||||
|
Свойства для редактирования:
|
||||||
|
|
||||||
|
### Скалярные
|
||||||
|
`Name`, `Version`, `Vendor`, `Comment`, `NamePrefix`, `UpdateCatalogAddress`
|
||||||
|
|
||||||
|
### LocalString (многоязычные)
|
||||||
|
`Synonym`, `BriefInformation`, `DetailedInformation`, `Copyright`, `VendorInformationAddress`, `ConfigurationInformationAddress`
|
||||||
|
|
||||||
|
### Enum
|
||||||
|
| Свойство | Допустимые значения |
|
||||||
|
|----------|---------------------|
|
||||||
|
| `CompatibilityMode` | `Version8_3_20` ... `Version8_3_27`, `DontUse` |
|
||||||
|
| `ConfigurationExtensionCompatibilityMode` | то же |
|
||||||
|
| `DefaultRunMode` | `ManagedApplication`, `OrdinaryApplication`, `Auto` |
|
||||||
|
| `ScriptVariant` | `Russian`, `English` |
|
||||||
|
| `DataLockControlMode` | `Managed`, `Automatic`, `AutomaticAndManaged` |
|
||||||
|
| `ObjectAutonumerationMode` | `NotAutoFree`, `AutoFree` |
|
||||||
|
| `ModalityUseMode` | `DontUse`, `Use`, `UseWithWarnings` |
|
||||||
|
| `SynchronousPlatformExtensionAndAddInCallUseMode` | `DontUse`, `Use`, `UseWithWarnings` |
|
||||||
|
| `InterfaceCompatibilityMode` | `Taxi`, `TaxiEnableVersion8_2`, `Version8_2` |
|
||||||
|
| `DatabaseTablespacesUseMode` | `DontUse`, `Use` |
|
||||||
|
| `MainClientApplicationWindowMode` | `Normal`, `Fullscreen`, `Kiosk` |
|
||||||
|
|
||||||
|
### Ref
|
||||||
|
`DefaultLanguage` — значение вида `Language.Русский`
|
||||||
|
|
||||||
|
### Формат batch
|
||||||
|
`"Version=1.0.0.1 ;; Vendor=Фирма 1С ;; Synonym=Тестовая конфигурация"`
|
||||||
|
|
||||||
|
## add-childObject / remove-childObject
|
||||||
|
|
||||||
|
Формат: `Type.Name` — XML-тип и имя объекта через точку.
|
||||||
|
|
||||||
|
При добавлении объект вставляется в каноническую позицию:
|
||||||
|
1. Находит последний элемент того же типа → вставляет после
|
||||||
|
2. Если тип отсутствует → находит последний элемент предшествующего типа → вставляет после
|
||||||
|
3. Внутри одного типа — алфавитный порядок
|
||||||
|
|
||||||
|
Batch: `"Catalog.Товары ;; Document.Заказ ;; Enum.ВидыОплат"`
|
||||||
|
|
||||||
|
## add-defaultRole / remove-defaultRole / set-defaultRoles
|
||||||
|
|
||||||
|
Имя роли: `ПолныеПрава` или `Role.ПолныеПрава` (префикс `Role.` добавляется автоматически).
|
||||||
|
|
||||||
|
`set-defaultRoles` полностью заменяет список ролей.
|
||||||
|
|
||||||
|
## DefinitionFile (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "operation": "modify-property", "value": "Version=2.0.0.1 ;; Vendor=Test" },
|
||||||
|
{ "operation": "add-childObject", "value": "Catalog.Товары ;; Document.Заказ" },
|
||||||
|
{ "operation": "add-defaultRole", "value": "ПолныеПрава" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Авто-валидация
|
||||||
|
|
||||||
|
После сохранения автоматически запускается `cf-validate` (если не указан `-NoValidate`).
|
||||||
@@ -0,0 +1,523 @@
|
|||||||
|
# cf-edit v1.0 — Edit 1C configuration root (Configuration.xml)
|
||||||
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)][string]$ConfigPath,
|
||||||
|
[string]$DefinitionFile,
|
||||||
|
[ValidateSet("modify-property","add-childObject","remove-childObject","add-defaultRole","remove-defaultRole","set-defaultRoles")]
|
||||||
|
[string]$Operation,
|
||||||
|
[string]$Value,
|
||||||
|
[switch]$NoValidate
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
# --- Mode validation ---
|
||||||
|
if ($DefinitionFile -and $Operation) { Write-Error "Cannot use both -DefinitionFile and -Operation"; exit 1 }
|
||||||
|
if (-not $DefinitionFile -and -not $Operation) { Write-Error "Either -DefinitionFile or -Operation is required"; exit 1 }
|
||||||
|
|
||||||
|
# --- Resolve path ---
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) {
|
||||||
|
$ConfigPath = Join-Path (Get-Location).Path $ConfigPath
|
||||||
|
}
|
||||||
|
if (Test-Path $ConfigPath -PathType Container) {
|
||||||
|
$candidate = Join-Path $ConfigPath "Configuration.xml"
|
||||||
|
if (Test-Path $candidate) { $ConfigPath = $candidate }
|
||||||
|
else { Write-Error "No Configuration.xml in directory"; exit 1 }
|
||||||
|
}
|
||||||
|
if (-not (Test-Path $ConfigPath)) { Write-Error "File not found: $ConfigPath"; exit 1 }
|
||||||
|
$resolvedPath = (Resolve-Path $ConfigPath).Path
|
||||||
|
|
||||||
|
# --- Load XML with PreserveWhitespace ---
|
||||||
|
$script:xmlDoc = New-Object System.Xml.XmlDocument
|
||||||
|
$script:xmlDoc.PreserveWhitespace = $true
|
||||||
|
$script:xmlDoc.Load($resolvedPath)
|
||||||
|
|
||||||
|
$script:addCount = 0
|
||||||
|
$script:removeCount = 0
|
||||||
|
$script:modifyCount = 0
|
||||||
|
|
||||||
|
function Info([string]$msg) { Write-Host "[INFO] $msg" }
|
||||||
|
function Warn([string]$msg) { Write-Host "[WARN] $msg" }
|
||||||
|
|
||||||
|
# --- Detect structure ---
|
||||||
|
$root = $script:xmlDoc.DocumentElement
|
||||||
|
$script:mdNs = "http://v8.1c.ru/8.3/MDClasses"
|
||||||
|
$script:xrNs = "http://v8.1c.ru/8.3/xcf/readable"
|
||||||
|
$script:xsiNs = "http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
$script:v8Ns = "http://v8.1c.ru/8.1/data/core"
|
||||||
|
|
||||||
|
$script:cfgEl = $null
|
||||||
|
foreach ($child in $root.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Configuration") {
|
||||||
|
$script:cfgEl = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $script:cfgEl) { Write-Error "No <Configuration> element found"; exit 1 }
|
||||||
|
|
||||||
|
$script:propsEl = $null
|
||||||
|
$script:childObjsEl = $null
|
||||||
|
foreach ($child in $script:cfgEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
if ($child.LocalName -eq "Properties") { $script:propsEl = $child }
|
||||||
|
if ($child.LocalName -eq "ChildObjects") { $script:childObjsEl = $child }
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:objName = ""
|
||||||
|
foreach ($child in $script:propsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Name") {
|
||||||
|
$script:objName = $child.InnerText.Trim(); break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Info "Configuration: $($script:objName)"
|
||||||
|
|
||||||
|
# --- Canonical type order for ChildObjects (44 types) ---
|
||||||
|
$script:typeOrder = @(
|
||||||
|
"Language","Subsystem","StyleItem","Style",
|
||||||
|
"CommonPicture","SessionParameter","Role","CommonTemplate",
|
||||||
|
"FilterCriterion","CommonModule","CommonAttribute","ExchangePlan",
|
||||||
|
"XDTOPackage","WebService","HTTPService","WSReference",
|
||||||
|
"EventSubscription","ScheduledJob","SettingsStorage","FunctionalOption",
|
||||||
|
"FunctionalOptionsParameter","DefinedType","CommonCommand","CommandGroup",
|
||||||
|
"Constant","CommonForm","Catalog","Document",
|
||||||
|
"DocumentNumerator","Sequence","DocumentJournal","Enum",
|
||||||
|
"Report","DataProcessor","InformationRegister","AccumulationRegister",
|
||||||
|
"ChartOfCharacteristicTypes","ChartOfAccounts","AccountingRegister",
|
||||||
|
"ChartOfCalculationTypes","CalculationRegister",
|
||||||
|
"BusinessProcess","Task","IntegrationService"
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- XML manipulation helpers (from subsystem-edit pattern) ---
|
||||||
|
function Get-ChildIndent($container) {
|
||||||
|
foreach ($child in $container.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Whitespace' -or $child.NodeType -eq 'SignificantWhitespace') {
|
||||||
|
if ($child.Value -match '^\r?\n(\t+)$') { return $Matches[1] }
|
||||||
|
if ($child.Value -match '^\r?\n(\t+)') { return $Matches[1] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$depth = 0; $current = $container
|
||||||
|
while ($current -and $current -ne $script:xmlDoc.DocumentElement) { $depth++; $current = $current.ParentNode }
|
||||||
|
return "`t" * ($depth + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Insert-BeforeElement($container, $newNode, $refNode, $childIndent) {
|
||||||
|
$ws = $script:xmlDoc.CreateWhitespace("`r`n$childIndent")
|
||||||
|
if ($refNode) {
|
||||||
|
$container.InsertBefore($ws, $refNode) | Out-Null
|
||||||
|
$container.InsertBefore($newNode, $ws) | Out-Null
|
||||||
|
} else {
|
||||||
|
$trailing = $container.LastChild
|
||||||
|
if ($trailing -and ($trailing.NodeType -eq 'Whitespace' -or $trailing.NodeType -eq 'SignificantWhitespace')) {
|
||||||
|
$container.InsertBefore($ws, $trailing) | Out-Null
|
||||||
|
$container.InsertBefore($newNode, $trailing) | Out-Null
|
||||||
|
} else {
|
||||||
|
$container.AppendChild($ws) | Out-Null
|
||||||
|
$container.AppendChild($newNode) | Out-Null
|
||||||
|
$parentIndent = if ($childIndent.Length -gt 1) { $childIndent.Substring(0, $childIndent.Length - 1) } else { "" }
|
||||||
|
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$parentIndent")
|
||||||
|
$container.AppendChild($closeWs) | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Remove-NodeWithWhitespace($node) {
|
||||||
|
$parent = $node.ParentNode
|
||||||
|
$prev = $node.PreviousSibling
|
||||||
|
$next = $node.NextSibling
|
||||||
|
if ($prev -and ($prev.NodeType -eq 'Whitespace' -or $prev.NodeType -eq 'SignificantWhitespace')) {
|
||||||
|
$parent.RemoveChild($prev) | Out-Null
|
||||||
|
} elseif ($next -and ($next.NodeType -eq 'Whitespace' -or $next.NodeType -eq 'SignificantWhitespace')) {
|
||||||
|
$parent.RemoveChild($next) | Out-Null
|
||||||
|
}
|
||||||
|
$parent.RemoveChild($node) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Expand-SelfClosingElement($container, $parentIndent) {
|
||||||
|
if (-not $container.HasChildNodes -or $container.IsEmpty) {
|
||||||
|
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$parentIndent")
|
||||||
|
$container.AppendChild($closeWs) | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Import-Fragment([string]$xmlString) {
|
||||||
|
$wrapper = "<_W xmlns=`"$($script:mdNs)`" xmlns:xsi=`"$($script:xsiNs)`" xmlns:v8=`"$($script:v8Ns)`" xmlns:xr=`"$($script:xrNs)`" xmlns:xs=`"http://www.w3.org/2001/XMLSchema`">$xmlString</_W>"
|
||||||
|
$frag = New-Object System.Xml.XmlDocument
|
||||||
|
$frag.PreserveWhitespace = $true
|
||||||
|
$frag.LoadXml($wrapper)
|
||||||
|
$nodes = @()
|
||||||
|
foreach ($child in $frag.DocumentElement.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element') {
|
||||||
|
$nodes += $script:xmlDoc.ImportNode($child, $true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ,$nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Parse batch value (split by ;;) ---
|
||||||
|
function Parse-BatchValue([string]$val) {
|
||||||
|
$items = @()
|
||||||
|
foreach ($part in $val.Split(";;")) {
|
||||||
|
$trimmed = $part.Trim()
|
||||||
|
if ($trimmed) { $items += $trimmed }
|
||||||
|
}
|
||||||
|
return ,$items
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- LocalString properties ---
|
||||||
|
$mlProps = @("Synonym","BriefInformation","DetailedInformation","Copyright","VendorInformationAddress","ConfigurationInformationAddress")
|
||||||
|
# Scalar properties
|
||||||
|
$scalarProps = @("Name","Version","Vendor","Comment","NamePrefix","UpdateCatalogAddress")
|
||||||
|
# Ref properties
|
||||||
|
$refProps = @("DefaultLanguage")
|
||||||
|
|
||||||
|
# --- Operation: modify-property ---
|
||||||
|
function Do-ModifyProperty([string]$batchVal) {
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$eqIdx = $item.IndexOf("=")
|
||||||
|
if ($eqIdx -lt 1) {
|
||||||
|
Write-Error "Invalid property format '$item', expected 'Key=Value'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$propName = $item.Substring(0, $eqIdx).Trim()
|
||||||
|
$propValue = $item.Substring($eqIdx + 1).Trim()
|
||||||
|
|
||||||
|
# Find property element
|
||||||
|
$propEl = $null
|
||||||
|
foreach ($child in $script:propsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $propName) {
|
||||||
|
$propEl = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $propEl) {
|
||||||
|
Write-Error "Property '$propName' not found in Properties"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($mlProps -contains $propName) {
|
||||||
|
# LocalString
|
||||||
|
if (-not $propValue) {
|
||||||
|
$propEl.InnerXml = ""
|
||||||
|
} else {
|
||||||
|
$indent = Get-ChildIndent $script:propsEl
|
||||||
|
$escaped = [System.Security.SecurityElement]::Escape($propValue)
|
||||||
|
$mlXml = "`r`n$indent`t<v8:item>`r`n$indent`t`t<v8:lang>ru</v8:lang>`r`n$indent`t`t<v8:content>$escaped</v8:content>`r`n$indent`t</v8:item>`r`n$indent"
|
||||||
|
$propEl.InnerXml = $mlXml
|
||||||
|
}
|
||||||
|
} elseif ($scalarProps -contains $propName -or $refProps -contains $propName) {
|
||||||
|
# Simple text
|
||||||
|
if (-not $propValue) { $propEl.InnerXml = "" }
|
||||||
|
else { $propEl.InnerText = $propValue }
|
||||||
|
} else {
|
||||||
|
# Enum or other — just set text
|
||||||
|
$propEl.InnerText = $propValue
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:modifyCount++
|
||||||
|
Info "Set $propName = `"$propValue`""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Operation: add-childObject ---
|
||||||
|
function Do-AddChildObject([string]$batchVal) {
|
||||||
|
if (-not $script:childObjsEl) { Write-Error "No <ChildObjects> element found"; exit 1 }
|
||||||
|
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
$cfgIndent = Get-ChildIndent $script:cfgEl
|
||||||
|
|
||||||
|
# Expand self-closing if needed
|
||||||
|
if (-not $script:childObjsEl.HasChildNodes -or $script:childObjsEl.IsEmpty) {
|
||||||
|
Expand-SelfClosingElement $script:childObjsEl $cfgIndent
|
||||||
|
}
|
||||||
|
$childIndent = Get-ChildIndent $script:childObjsEl
|
||||||
|
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$dotIdx = $item.IndexOf(".")
|
||||||
|
if ($dotIdx -lt 1) {
|
||||||
|
Write-Error "Invalid format '$item', expected 'Type.Name'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$typeName = $item.Substring(0, $dotIdx)
|
||||||
|
$objNameVal = $item.Substring($dotIdx + 1)
|
||||||
|
|
||||||
|
# Check type is valid
|
||||||
|
$typeIdx = $script:typeOrder.IndexOf($typeName)
|
||||||
|
if ($typeIdx -lt 0) {
|
||||||
|
Write-Error "Unknown type '$typeName'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dedup check
|
||||||
|
$existing = $false
|
||||||
|
foreach ($child in $script:childObjsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $typeName -and $child.InnerText -eq $objNameVal) {
|
||||||
|
$existing = $true; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($existing) {
|
||||||
|
Warn "Already exists: $typeName.$objNameVal"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find insertion point: after last element of same type, or after last element of preceding type
|
||||||
|
$insertBefore = $null
|
||||||
|
$lastSameType = $null
|
||||||
|
$lastPrecedingType = $null
|
||||||
|
$currentTypeIdx = -1
|
||||||
|
|
||||||
|
foreach ($child in $script:childObjsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
$childTypeIdx = $script:typeOrder.IndexOf($child.LocalName)
|
||||||
|
if ($childTypeIdx -lt 0) { continue }
|
||||||
|
|
||||||
|
if ($child.LocalName -eq $typeName) {
|
||||||
|
# Same type — check alphabetical order
|
||||||
|
if ($child.InnerText -gt $objNameVal -and -not $insertBefore) {
|
||||||
|
# Insert before this element (alphabetical)
|
||||||
|
$insertBefore = $child
|
||||||
|
}
|
||||||
|
$lastSameType = $child
|
||||||
|
} elseif ($childTypeIdx -lt $typeIdx) {
|
||||||
|
$lastPrecedingType = $child
|
||||||
|
} elseif ($childTypeIdx -gt $typeIdx -and -not $insertBefore) {
|
||||||
|
# First element of a later type — insert before it
|
||||||
|
$insertBefore = $child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create element
|
||||||
|
$newEl = $script:xmlDoc.CreateElement($typeName, $script:mdNs)
|
||||||
|
$newEl.InnerText = $objNameVal
|
||||||
|
|
||||||
|
if ($insertBefore) {
|
||||||
|
Insert-BeforeElement $script:childObjsEl $newEl $insertBefore $childIndent
|
||||||
|
} else {
|
||||||
|
# Append at end (or after last same/preceding type)
|
||||||
|
Insert-BeforeElement $script:childObjsEl $newEl $null $childIndent
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:addCount++
|
||||||
|
Info "Added: $typeName.$objNameVal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Operation: remove-childObject ---
|
||||||
|
function Do-RemoveChildObject([string]$batchVal) {
|
||||||
|
if (-not $script:childObjsEl) { Write-Error "No <ChildObjects> element found"; exit 1 }
|
||||||
|
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$dotIdx = $item.IndexOf(".")
|
||||||
|
if ($dotIdx -lt 1) {
|
||||||
|
Write-Error "Invalid format '$item', expected 'Type.Name'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$typeName = $item.Substring(0, $dotIdx)
|
||||||
|
$objNameVal = $item.Substring($dotIdx + 1)
|
||||||
|
|
||||||
|
$found = $false
|
||||||
|
foreach ($child in @($script:childObjsEl.ChildNodes)) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $typeName -and $child.InnerText -eq $objNameVal) {
|
||||||
|
Remove-NodeWithWhitespace $child
|
||||||
|
$script:removeCount++
|
||||||
|
Info "Removed: $typeName.$objNameVal"
|
||||||
|
$found = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $found) { Warn "Not found: $typeName.$objNameVal" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Operation: add-defaultRole ---
|
||||||
|
function Do-AddDefaultRole([string]$batchVal) {
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
|
||||||
|
# Find DefaultRoles element
|
||||||
|
$rolesEl = $null
|
||||||
|
foreach ($child in $script:propsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "DefaultRoles") {
|
||||||
|
$rolesEl = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $rolesEl) { Write-Error "No <DefaultRoles> element found in Properties"; exit 1 }
|
||||||
|
|
||||||
|
$propsIndent = Get-ChildIndent $script:propsEl
|
||||||
|
if (-not $rolesEl.HasChildNodes -or $rolesEl.IsEmpty) {
|
||||||
|
Expand-SelfClosingElement $rolesEl $propsIndent
|
||||||
|
}
|
||||||
|
$roleIndent = Get-ChildIndent $rolesEl
|
||||||
|
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$roleName = $item
|
||||||
|
if (-not $roleName.StartsWith("Role.")) { $roleName = "Role.$roleName" }
|
||||||
|
|
||||||
|
# Dedup
|
||||||
|
$existing = $false
|
||||||
|
foreach ($child in $rolesEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.InnerText.Trim() -eq $roleName) {
|
||||||
|
$existing = $true; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($existing) {
|
||||||
|
Warn "DefaultRole already exists: $roleName"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$fragXml = "<xr:Item xsi:type=`"xr:MDObjectRef`">$roleName</xr:Item>"
|
||||||
|
$nodes = Import-Fragment $fragXml
|
||||||
|
if ($nodes.Count -gt 0) {
|
||||||
|
Insert-BeforeElement $rolesEl $nodes[0] $null $roleIndent
|
||||||
|
$script:addCount++
|
||||||
|
Info "Added DefaultRole: $roleName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Operation: remove-defaultRole ---
|
||||||
|
function Do-RemoveDefaultRole([string]$batchVal) {
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
|
||||||
|
$rolesEl = $null
|
||||||
|
foreach ($child in $script:propsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "DefaultRoles") {
|
||||||
|
$rolesEl = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $rolesEl) { Write-Error "No <DefaultRoles> element found"; exit 1 }
|
||||||
|
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$roleName = $item
|
||||||
|
if (-not $roleName.StartsWith("Role.")) { $roleName = "Role.$roleName" }
|
||||||
|
|
||||||
|
$found = $false
|
||||||
|
foreach ($child in @($rolesEl.ChildNodes)) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.InnerText.Trim() -eq $roleName) {
|
||||||
|
Remove-NodeWithWhitespace $child
|
||||||
|
$script:removeCount++
|
||||||
|
Info "Removed DefaultRole: $roleName"
|
||||||
|
$found = $true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $found) { Warn "DefaultRole not found: $roleName" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Operation: set-defaultRoles ---
|
||||||
|
function Do-SetDefaultRoles([string]$batchVal) {
|
||||||
|
$items = Parse-BatchValue $batchVal
|
||||||
|
|
||||||
|
$rolesEl = $null
|
||||||
|
foreach ($child in $script:propsEl.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "DefaultRoles") {
|
||||||
|
$rolesEl = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $rolesEl) { Write-Error "No <DefaultRoles> element found"; exit 1 }
|
||||||
|
|
||||||
|
# Clear all existing children
|
||||||
|
while ($rolesEl.HasChildNodes) {
|
||||||
|
$rolesEl.RemoveChild($rolesEl.FirstChild) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($items.Count -eq 0) {
|
||||||
|
$script:modifyCount++
|
||||||
|
Info "Cleared DefaultRoles"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$propsIndent = Get-ChildIndent $script:propsEl
|
||||||
|
$roleIndent = "$propsIndent`t"
|
||||||
|
|
||||||
|
# Add closing whitespace
|
||||||
|
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$propsIndent")
|
||||||
|
$rolesEl.AppendChild($closeWs) | Out-Null
|
||||||
|
|
||||||
|
foreach ($item in $items) {
|
||||||
|
$roleName = $item
|
||||||
|
if (-not $roleName.StartsWith("Role.")) { $roleName = "Role.$roleName" }
|
||||||
|
|
||||||
|
$fragXml = "<xr:Item xsi:type=`"xr:MDObjectRef`">$roleName</xr:Item>"
|
||||||
|
$nodes = Import-Fragment $fragXml
|
||||||
|
if ($nodes.Count -gt 0) {
|
||||||
|
Insert-BeforeElement $rolesEl $nodes[0] $null $roleIndent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$script:modifyCount++
|
||||||
|
Info "Set DefaultRoles: $($items.Count) roles"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Execute operations ---
|
||||||
|
$operations = @()
|
||||||
|
if ($DefinitionFile) {
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($DefinitionFile)) {
|
||||||
|
$DefinitionFile = Join-Path (Get-Location).Path $DefinitionFile
|
||||||
|
}
|
||||||
|
$jsonText = Get-Content -Raw -Encoding UTF8 $DefinitionFile
|
||||||
|
$ops = $jsonText | ConvertFrom-Json
|
||||||
|
if ($ops -is [System.Array]) {
|
||||||
|
foreach ($op in $ops) { $operations += $op }
|
||||||
|
} else {
|
||||||
|
$operations += $ops
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$operations += @{ operation = $Operation; value = $Value }
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($op in $operations) {
|
||||||
|
$opName = if ($op.operation) { "$($op.operation)" } else { "$Operation" }
|
||||||
|
$opValue = if ($op.value) { "$($op.value)" } else { "$Value" }
|
||||||
|
|
||||||
|
switch ($opName) {
|
||||||
|
"modify-property" { Do-ModifyProperty $opValue }
|
||||||
|
"add-childObject" { Do-AddChildObject $opValue }
|
||||||
|
"remove-childObject" { Do-RemoveChildObject $opValue }
|
||||||
|
"add-defaultRole" { Do-AddDefaultRole $opValue }
|
||||||
|
"remove-defaultRole" { Do-RemoveDefaultRole $opValue }
|
||||||
|
"set-defaultRoles" { Do-SetDefaultRoles $opValue }
|
||||||
|
default { Write-Error "Unknown operation: $opName"; exit 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Save ---
|
||||||
|
$settings = New-Object System.Xml.XmlWriterSettings
|
||||||
|
$settings.Encoding = New-Object System.Text.UTF8Encoding($true)
|
||||||
|
$settings.Indent = $false
|
||||||
|
$settings.NewLineHandling = [System.Xml.NewLineHandling]::None
|
||||||
|
|
||||||
|
$memStream = New-Object System.IO.MemoryStream
|
||||||
|
$writer = [System.Xml.XmlWriter]::Create($memStream, $settings)
|
||||||
|
$script:xmlDoc.Save($writer)
|
||||||
|
$writer.Flush(); $writer.Close()
|
||||||
|
|
||||||
|
$bytes = $memStream.ToArray()
|
||||||
|
$memStream.Close()
|
||||||
|
$text = [System.Text.Encoding]::UTF8.GetString($bytes)
|
||||||
|
if ($text.Length -gt 0 -and $text[0] -eq [char]0xFEFF) { $text = $text.Substring(1) }
|
||||||
|
$text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
|
||||||
|
|
||||||
|
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||||
|
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
|
||||||
|
Info "Saved: $resolvedPath"
|
||||||
|
|
||||||
|
# --- Auto-validate ---
|
||||||
|
if (-not $NoValidate) {
|
||||||
|
$validateScript = Join-Path (Join-Path $PSScriptRoot "..\..\cf-validate") "scripts\cf-validate.ps1"
|
||||||
|
$validateScript = [System.IO.Path]::GetFullPath($validateScript)
|
||||||
|
if (Test-Path $validateScript) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "--- Running cf-validate ---"
|
||||||
|
& powershell.exe -NoProfile -File $validateScript -ConfigPath $resolvedPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== cf-edit summary ==="
|
||||||
|
Write-Host " Configuration: $($script:objName)"
|
||||||
|
Write-Host " Added: $($script:addCount)"
|
||||||
|
Write-Host " Removed: $($script:removeCount)"
|
||||||
|
Write-Host " Modified: $($script:modifyCount)"
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
name: cf-info
|
||||||
|
description: Анализ структуры конфигурации 1С — свойства, состав, счётчики объектов. Используй для обзора конфигурации — какие объекты есть, сколько их, какие настройки
|
||||||
|
argument-hint: <ConfigPath> [-Mode overview|brief|full]
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /cf-info — Структура конфигурации 1С
|
||||||
|
|
||||||
|
Читает Configuration.xml из выгрузки конфигурации и выводит компактное описание структуры.
|
||||||
|
|
||||||
|
## Параметры и команда
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `ConfigPath` | Путь к Configuration.xml или каталогу выгрузки |
|
||||||
|
| `Mode` | Режим: `overview` (default), `brief`, `full` |
|
||||||
|
| `Limit` / `Offset` | Пагинация (по умолчанию 150 строк) |
|
||||||
|
| `OutFile` | Записать результат в файл (UTF-8 BOM) |
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\cf-info\scripts\cf-info.ps1 -ConfigPath "<путь>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Три режима
|
||||||
|
|
||||||
|
| Режим | Что показывает |
|
||||||
|
|---|---|
|
||||||
|
| `overview` *(default)* | Заголовок + ключевые свойства + таблица счётчиков объектов по типам |
|
||||||
|
| `brief` | Одна строка: Имя — "Синоним" vВерсия \| N объектов \| совместимость |
|
||||||
|
| `full` | Все свойства по категориям + полный список ChildObjects + DefaultRoles + мобильные функциональности |
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Обзор пустой конфигурации
|
||||||
|
... -ConfigPath upload/cfempty
|
||||||
|
|
||||||
|
# Краткая сводка реальной конфигурации
|
||||||
|
... -ConfigPath upload/acc_8.3.24 -Mode brief
|
||||||
|
|
||||||
|
# Полная информация
|
||||||
|
... -ConfigPath upload/acc_8.3.24 -Mode full
|
||||||
|
|
||||||
|
# С пагинацией
|
||||||
|
... -ConfigPath upload/acc_8.3.24 -Mode full -Limit 50 -Offset 100
|
||||||
|
```
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
# cf-info v1.0 — Compact summary of 1C configuration root
|
||||||
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)][string]$ConfigPath,
|
||||||
|
[ValidateSet("overview","brief","full")]
|
||||||
|
[string]$Mode = "overview",
|
||||||
|
[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($ConfigPath)) {
|
||||||
|
$ConfigPath = Join-Path (Get-Location).Path $ConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
# Directory -> find Configuration.xml
|
||||||
|
if (Test-Path $ConfigPath -PathType Container) {
|
||||||
|
$candidate = Join-Path $ConfigPath "Configuration.xml"
|
||||||
|
if (Test-Path $candidate) {
|
||||||
|
$ConfigPath = $candidate
|
||||||
|
} else {
|
||||||
|
Write-Host "[ERROR] No Configuration.xml found in directory: $ConfigPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $ConfigPath)) {
|
||||||
|
Write-Host "[ERROR] File not found: $ConfigPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Load XML ---
|
||||||
|
[xml]$xmlDoc = Get-Content -Path $ConfigPath -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("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 (no MetaDataObject root)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$cfgNode = $mdRoot.SelectSingleNode("md:Configuration", $ns)
|
||||||
|
if (-not $cfgNode) {
|
||||||
|
Write-Host "[ERROR] No <Configuration> element found"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $mdRoot.GetAttribute("version")
|
||||||
|
$propsNode = $cfgNode.SelectSingleNode("md:Properties", $ns)
|
||||||
|
$childObjNode = $cfgNode.SelectSingleNode("md:ChildObjects", $ns)
|
||||||
|
|
||||||
|
# --- Helpers ---
|
||||||
|
function Get-MLText($node) {
|
||||||
|
if (-not $node) { return "" }
|
||||||
|
$item = $node.SelectSingleNode("v8:item/v8:content", $ns)
|
||||||
|
if ($item -and $item.InnerText) { return $item.InnerText }
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-PropText([string]$propName) {
|
||||||
|
$n = $propsNode.SelectSingleNode("md:$propName", $ns)
|
||||||
|
if ($n -and $n.InnerText) { return $n.InnerText }
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-PropML([string]$propName) {
|
||||||
|
$n = $propsNode.SelectSingleNode("md:$propName", $ns)
|
||||||
|
return (Get-MLText $n)
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Type name maps (canonical order, 44 types) ---
|
||||||
|
$typeOrder = @(
|
||||||
|
"Language","Subsystem","StyleItem","Style",
|
||||||
|
"CommonPicture","SessionParameter","Role","CommonTemplate",
|
||||||
|
"FilterCriterion","CommonModule","CommonAttribute","ExchangePlan",
|
||||||
|
"XDTOPackage","WebService","HTTPService","WSReference",
|
||||||
|
"EventSubscription","ScheduledJob","SettingsStorage","FunctionalOption",
|
||||||
|
"FunctionalOptionsParameter","DefinedType","CommonCommand","CommandGroup",
|
||||||
|
"Constant","CommonForm","Catalog","Document",
|
||||||
|
"DocumentNumerator","Sequence","DocumentJournal","Enum",
|
||||||
|
"Report","DataProcessor","InformationRegister","AccumulationRegister",
|
||||||
|
"ChartOfCharacteristicTypes","ChartOfAccounts","AccountingRegister",
|
||||||
|
"ChartOfCalculationTypes","CalculationRegister",
|
||||||
|
"BusinessProcess","Task","IntegrationService"
|
||||||
|
)
|
||||||
|
|
||||||
|
$typeRuNames = @{
|
||||||
|
"Language"="Языки"; "Subsystem"="Подсистемы"; "StyleItem"="Элементы стиля"; "Style"="Стили"
|
||||||
|
"CommonPicture"="Общие картинки"; "SessionParameter"="Параметры сеанса"; "Role"="Роли"
|
||||||
|
"CommonTemplate"="Общие макеты"; "FilterCriterion"="Критерии отбора"; "CommonModule"="Общие модули"
|
||||||
|
"CommonAttribute"="Общие реквизиты"; "ExchangePlan"="Планы обмена"; "XDTOPackage"="XDTO-пакеты"
|
||||||
|
"WebService"="Веб-сервисы"; "HTTPService"="HTTP-сервисы"; "WSReference"="WS-ссылки"
|
||||||
|
"EventSubscription"="Подписки на события"; "ScheduledJob"="Регламентные задания"
|
||||||
|
"SettingsStorage"="Хранилища настроек"; "FunctionalOption"="Функциональные опции"
|
||||||
|
"FunctionalOptionsParameter"="Параметры ФО"; "DefinedType"="Определяемые типы"
|
||||||
|
"CommonCommand"="Общие команды"; "CommandGroup"="Группы команд"; "Constant"="Константы"
|
||||||
|
"CommonForm"="Общие формы"; "Catalog"="Справочники"; "Document"="Документы"
|
||||||
|
"DocumentNumerator"="Нумераторы"; "Sequence"="Последовательности"; "DocumentJournal"="Журналы документов"
|
||||||
|
"Enum"="Перечисления"; "Report"="Отчёты"; "DataProcessor"="Обработки"
|
||||||
|
"InformationRegister"="Регистры сведений"; "AccumulationRegister"="Регистры накопления"
|
||||||
|
"ChartOfCharacteristicTypes"="ПВХ"; "ChartOfAccounts"="Планы счетов"
|
||||||
|
"AccountingRegister"="Регистры бухгалтерии"; "ChartOfCalculationTypes"="ПВР"
|
||||||
|
"CalculationRegister"="Регистры расчёта"; "BusinessProcess"="Бизнес-процессы"
|
||||||
|
"Task"="Задачи"; "IntegrationService"="Сервисы интеграции"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Count objects in ChildObjects ---
|
||||||
|
$objectCounts = [ordered]@{}
|
||||||
|
$totalObjects = 0
|
||||||
|
|
||||||
|
if ($childObjNode) {
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
$typeName = $child.LocalName
|
||||||
|
if (-not $objectCounts.Contains($typeName)) {
|
||||||
|
$objectCounts[$typeName] = 0
|
||||||
|
}
|
||||||
|
$objectCounts[$typeName] = $objectCounts[$typeName] + 1
|
||||||
|
$totalObjects++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Read key properties ---
|
||||||
|
$cfgName = Get-PropText "Name"
|
||||||
|
$cfgSynonym = Get-PropML "Synonym"
|
||||||
|
$cfgVersion = Get-PropText "Version"
|
||||||
|
$cfgVendor = Get-PropText "Vendor"
|
||||||
|
$cfgCompat = Get-PropText "CompatibilityMode"
|
||||||
|
$cfgExtCompat = Get-PropText "ConfigurationExtensionCompatibilityMode"
|
||||||
|
$cfgDefaultRun = Get-PropText "DefaultRunMode"
|
||||||
|
$cfgScript = Get-PropText "ScriptVariant"
|
||||||
|
$cfgDefaultLang = Get-PropText "DefaultLanguage"
|
||||||
|
$cfgDataLock = Get-PropText "DataLockControlMode"
|
||||||
|
$dash = [char]0x2014
|
||||||
|
$cfgModality = Get-PropText "ModalityUseMode"
|
||||||
|
$cfgIntfCompat = Get-PropText "InterfaceCompatibilityMode"
|
||||||
|
$cfgAutoNum = Get-PropText "ObjectAutonumerationMode"
|
||||||
|
$cfgSyncCalls = Get-PropText "SynchronousPlatformExtensionAndAddInCallUseMode"
|
||||||
|
$cfgDbSpaces = Get-PropText "DatabaseTablespacesUseMode"
|
||||||
|
$cfgWindowMode = Get-PropText "MainClientApplicationWindowMode"
|
||||||
|
|
||||||
|
# --- BRIEF mode ---
|
||||||
|
if ($Mode -eq "brief") {
|
||||||
|
$synPart = if ($cfgSynonym) { " $dash `"$cfgSynonym`"" } else { "" }
|
||||||
|
$verPart = if ($cfgVersion) { " v$cfgVersion" } else { "" }
|
||||||
|
$compatPart = if ($cfgCompat) { " | $cfgCompat" } else { "" }
|
||||||
|
Out "Конфигурация: ${cfgName}${synPart}${verPart} | $totalObjects объектов${compatPart}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- OVERVIEW mode ---
|
||||||
|
if ($Mode -eq "overview") {
|
||||||
|
$synPart = if ($cfgSynonym) { " $dash `"$cfgSynonym`"" } else { "" }
|
||||||
|
$verPart = if ($cfgVersion) { " v$cfgVersion" } else { "" }
|
||||||
|
Out "=== Конфигурация: ${cfgName}${synPart}${verPart} ==="
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# Key properties
|
||||||
|
Out "Формат: $version"
|
||||||
|
if ($cfgVendor) { Out "Поставщик: $cfgVendor" }
|
||||||
|
if ($cfgVersion) { Out "Версия: $cfgVersion" }
|
||||||
|
Out "Совместимость: $cfgCompat"
|
||||||
|
Out "Режим запуска: $cfgDefaultRun"
|
||||||
|
Out "Язык скриптов: $cfgScript"
|
||||||
|
Out "Язык: $cfgDefaultLang"
|
||||||
|
Out "Блокировки: $cfgDataLock"
|
||||||
|
Out "Модальность: $cfgModality"
|
||||||
|
Out "Интерфейс: $cfgIntfCompat"
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# Object counts table
|
||||||
|
Out "--- Состав ($totalObjects объектов) ---"
|
||||||
|
Out ""
|
||||||
|
$maxTypeLen = 0
|
||||||
|
foreach ($typeName in $typeOrder) {
|
||||||
|
if ($objectCounts.Contains($typeName)) {
|
||||||
|
$ruName = $typeRuNames[$typeName]
|
||||||
|
if ($ruName.Length -gt $maxTypeLen) { $maxTypeLen = $ruName.Length }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($maxTypeLen -lt 10) { $maxTypeLen = 10 }
|
||||||
|
|
||||||
|
foreach ($typeName in $typeOrder) {
|
||||||
|
if ($objectCounts.Contains($typeName)) {
|
||||||
|
$count = $objectCounts[$typeName]
|
||||||
|
$ruName = $typeRuNames[$typeName]
|
||||||
|
$padded = $ruName.PadRight($maxTypeLen)
|
||||||
|
Out " $padded $count"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- FULL mode ---
|
||||||
|
if ($Mode -eq "full") {
|
||||||
|
$synPart = if ($cfgSynonym) { " $dash `"$cfgSynonym`"" } else { "" }
|
||||||
|
$verPart = if ($cfgVersion) { " v$cfgVersion" } else { "" }
|
||||||
|
Out "=== Конфигурация: ${cfgName}${synPart}${verPart} ==="
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# --- Section: Identification ---
|
||||||
|
Out "--- Идентификация ---"
|
||||||
|
Out "UUID: $($cfgNode.GetAttribute('uuid'))"
|
||||||
|
Out "Имя: $cfgName"
|
||||||
|
if ($cfgSynonym) { Out "Синоним: $cfgSynonym" }
|
||||||
|
$cfgComment = Get-PropText "Comment"
|
||||||
|
if ($cfgComment) { Out "Комментарий: $cfgComment" }
|
||||||
|
$cfgPrefix = Get-PropText "NamePrefix"
|
||||||
|
if ($cfgPrefix) { Out "Префикс: $cfgPrefix" }
|
||||||
|
if ($cfgVendor) { Out "Поставщик: $cfgVendor" }
|
||||||
|
if ($cfgVersion) { Out "Версия: $cfgVersion" }
|
||||||
|
$cfgUpdateAddr = Get-PropText "UpdateCatalogAddress"
|
||||||
|
if ($cfgUpdateAddr) { Out "Каталог обн.: $cfgUpdateAddr" }
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# --- Section: Modes ---
|
||||||
|
Out "--- Режимы работы ---"
|
||||||
|
Out "Формат: $version"
|
||||||
|
Out "Совместимость: $cfgCompat"
|
||||||
|
Out "Совм. расширений: $cfgExtCompat"
|
||||||
|
Out "Режим запуска: $cfgDefaultRun"
|
||||||
|
Out "Язык скриптов: $cfgScript"
|
||||||
|
Out "Блокировки: $cfgDataLock"
|
||||||
|
Out "Автонумерация: $cfgAutoNum"
|
||||||
|
Out "Модальность: $cfgModality"
|
||||||
|
Out "Синхр. вызовы: $cfgSyncCalls"
|
||||||
|
Out "Интерфейс: $cfgIntfCompat"
|
||||||
|
Out "Табл. пространства: $cfgDbSpaces"
|
||||||
|
Out "Режим окна: $cfgWindowMode"
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# --- Section: Language, roles, purposes ---
|
||||||
|
Out "--- Назначение ---"
|
||||||
|
Out "Язык по умолч.: $cfgDefaultLang"
|
||||||
|
|
||||||
|
# UsePurposes
|
||||||
|
$purposeNode = $propsNode.SelectSingleNode("md:UsePurposes", $ns)
|
||||||
|
if ($purposeNode) {
|
||||||
|
$purposes = @()
|
||||||
|
foreach ($val in $purposeNode.SelectNodes("v8:Value", $ns)) {
|
||||||
|
$purposes += $val.InnerText
|
||||||
|
}
|
||||||
|
if ($purposes.Count -gt 0) { Out "Назначения: $($purposes -join ', ')" }
|
||||||
|
}
|
||||||
|
|
||||||
|
# DefaultRoles
|
||||||
|
$rolesNode = $propsNode.SelectSingleNode("md:DefaultRoles", $ns)
|
||||||
|
if ($rolesNode) {
|
||||||
|
$roles = @()
|
||||||
|
foreach ($item in $rolesNode.SelectNodes("xr:Item", $ns)) {
|
||||||
|
$roles += $item.InnerText
|
||||||
|
}
|
||||||
|
if ($roles.Count -gt 0) {
|
||||||
|
Out "Роли по умолч.: $($roles.Count)"
|
||||||
|
foreach ($r in $roles) { Out " - $r" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Booleans
|
||||||
|
$useMF = Get-PropText "UseManagedFormInOrdinaryApplication"
|
||||||
|
$useOF = Get-PropText "UseOrdinaryFormInManagedApplication"
|
||||||
|
Out "Управл.формы в обычн.: $useMF"
|
||||||
|
Out "Обычн.формы в управл.: $useOF"
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# --- Section: Storages & default forms ---
|
||||||
|
Out "--- Хранилища и формы по умолчанию ---"
|
||||||
|
$storageProps = @("CommonSettingsStorage","ReportsUserSettingsStorage","ReportsVariantsStorage","FormDataSettingsStorage","DynamicListsUserSettingsStorage","URLExternalDataStorage")
|
||||||
|
foreach ($sp in $storageProps) {
|
||||||
|
$val = Get-PropText $sp
|
||||||
|
if ($val) { Out " ${sp}: $val" }
|
||||||
|
}
|
||||||
|
$formProps = @("DefaultReportForm","DefaultReportVariantForm","DefaultReportSettingsForm","DefaultReportAppearanceTemplate","DefaultDynamicListSettingsForm","DefaultSearchForm","DefaultDataHistoryChangeHistoryForm","DefaultDataHistoryVersionDataForm","DefaultDataHistoryVersionDifferencesForm","DefaultCollaborationSystemUsersChoiceForm","DefaultConstantsForm","DefaultInterface","DefaultStyle")
|
||||||
|
foreach ($fp in $formProps) {
|
||||||
|
$val = Get-PropText $fp
|
||||||
|
if ($val) { Out " ${fp}: $val" }
|
||||||
|
}
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
# --- Section: Info ---
|
||||||
|
$cfgBrief = Get-PropML "BriefInformation"
|
||||||
|
$cfgDetail = Get-PropML "DetailedInformation"
|
||||||
|
$cfgCopyright = Get-PropML "Copyright"
|
||||||
|
$cfgVendorAddr = Get-PropML "VendorInformationAddress"
|
||||||
|
$cfgInfoAddr = Get-PropML "ConfigurationInformationAddress"
|
||||||
|
if ($cfgBrief -or $cfgDetail -or $cfgCopyright -or $cfgVendorAddr -or $cfgInfoAddr) {
|
||||||
|
Out "--- Информация ---"
|
||||||
|
if ($cfgBrief) { Out "Краткая: $cfgBrief" }
|
||||||
|
if ($cfgDetail) { Out "Подробная: $cfgDetail" }
|
||||||
|
if ($cfgCopyright) { Out "Copyright: $cfgCopyright" }
|
||||||
|
if ($cfgVendorAddr) { Out "Сайт поставщика: $cfgVendorAddr" }
|
||||||
|
if ($cfgInfoAddr) { Out "Адрес информ.: $cfgInfoAddr" }
|
||||||
|
Out ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Section: Mobile functionalities ---
|
||||||
|
$mobileFunc = $propsNode.SelectSingleNode("md:UsedMobileApplicationFunctionalities", $ns)
|
||||||
|
if ($mobileFunc) {
|
||||||
|
$enabledFuncs = @()
|
||||||
|
$disabledFuncs = @()
|
||||||
|
foreach ($func in $mobileFunc.SelectNodes("app:functionality", $ns)) {
|
||||||
|
$fName = $func.SelectSingleNode("app:functionality", $ns)
|
||||||
|
$fUse = $func.SelectSingleNode("app:use", $ns)
|
||||||
|
if ($fName -and $fUse) {
|
||||||
|
if ($fUse.InnerText -eq "true") {
|
||||||
|
$enabledFuncs += $fName.InnerText
|
||||||
|
} else {
|
||||||
|
$disabledFuncs += $fName.InnerText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$totalFunc = $enabledFuncs.Count + $disabledFuncs.Count
|
||||||
|
Out "--- Мобильные функциональности ($totalFunc, включено: $($enabledFuncs.Count)) ---"
|
||||||
|
if ($enabledFuncs.Count -gt 0) {
|
||||||
|
foreach ($f in $enabledFuncs) { Out " [+] $f" }
|
||||||
|
}
|
||||||
|
foreach ($f in $disabledFuncs) { Out " [-] $f" }
|
||||||
|
Out ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Section: InternalInfo ---
|
||||||
|
$internalInfo = $cfgNode.SelectSingleNode("md:InternalInfo", $ns)
|
||||||
|
if ($internalInfo) {
|
||||||
|
$contained = $internalInfo.SelectNodes("xr:ContainedObject", $ns)
|
||||||
|
Out "--- InternalInfo ($($contained.Count) ContainedObject) ---"
|
||||||
|
foreach ($co in $contained) {
|
||||||
|
$classId = $co.SelectSingleNode("xr:ClassId", $ns).InnerText
|
||||||
|
$objectId = $co.SelectSingleNode("xr:ObjectId", $ns).InnerText
|
||||||
|
Out " $classId -> $objectId"
|
||||||
|
}
|
||||||
|
Out ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Section: ChildObjects (full list) ---
|
||||||
|
Out "--- Состав ($totalObjects объектов) ---"
|
||||||
|
Out ""
|
||||||
|
|
||||||
|
foreach ($typeName in $typeOrder) {
|
||||||
|
if (-not $objectCounts.Contains($typeName)) { continue }
|
||||||
|
$count = $objectCounts[$typeName]
|
||||||
|
$ruName = $typeRuNames[$typeName]
|
||||||
|
Out " $ruName ($typeName): $count"
|
||||||
|
|
||||||
|
# Collect names for this type
|
||||||
|
$names = @()
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $typeName) {
|
||||||
|
$names += $child.InnerText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($n in $names) { Out " $n" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Pagination and output ---
|
||||||
|
$total = $script:lines.Count
|
||||||
|
if ($Offset -gt 0 -or $Limit -lt $total) {
|
||||||
|
$start = [Math]::Min($Offset, $total)
|
||||||
|
$end = [Math]::Min($start + $Limit, $total)
|
||||||
|
$page = $script:lines[$start..($end - 1)]
|
||||||
|
$result = ($page -join "`n")
|
||||||
|
if ($end -lt $total) {
|
||||||
|
$result += "`n`n... ($end of $total lines, use -Offset $end to continue)"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$result = ($script:lines -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host $result
|
||||||
|
|
||||||
|
if ($OutFile) {
|
||||||
|
$utf8Bom = New-Object System.Text.UTF8Encoding $true
|
||||||
|
[System.IO.File]::WriteAllText($OutFile, $result, $utf8Bom)
|
||||||
|
Write-Host "`nWritten to: $OutFile"
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
---
|
||||||
|
name: cf-init
|
||||||
|
description: Создать пустую конфигурацию 1С (scaffold XML-исходников) — Configuration.xml, ConfigDumpInfo.xml, Languages/
|
||||||
|
argument-hint: <Name> [-Synonym <name>] [-OutputDir src]
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /cf-init — Создание пустой конфигурации 1С
|
||||||
|
|
||||||
|
Создаёт scaffold исходников пустой конфигурации 1С: `Configuration.xml`, `ConfigDumpInfo.xml`, `Languages/Русский.xml`.
|
||||||
|
|
||||||
|
## Параметры и команда
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `Name` | Имя конфигурации (обязат.) |
|
||||||
|
| `Synonym` | Синоним (= Name если не указан) |
|
||||||
|
| `OutputDir` | Каталог для создания (default: `src`) |
|
||||||
|
| `Version` | Версия конфигурации |
|
||||||
|
| `Vendor` | Поставщик |
|
||||||
|
| `CompatibilityMode` | Режим совместимости (default: `Version8_3_24`) |
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\cf-init\scripts\cf-init.ps1 -Name "МояКонфигурация"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Что создаётся
|
||||||
|
|
||||||
|
```
|
||||||
|
<OutputDir>/
|
||||||
|
├── Configuration.xml # Корневой файл — все свойства
|
||||||
|
├── ConfigDumpInfo.xml # Служебный файл (минимальный)
|
||||||
|
└── Languages/
|
||||||
|
└── Русский.xml # Язык по умолчанию
|
||||||
|
```
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Базовая конфигурация
|
||||||
|
... -Name МояКонфигурация -Synonym "Моя конфигурация" -OutputDir test-tmp/cf
|
||||||
|
|
||||||
|
# С версией и поставщиком
|
||||||
|
... -Name TestCfg -Synonym "Тестовая" -Version "1.0.0.1" -Vendor "Фирма 1С" -OutputDir test-tmp/cf2
|
||||||
|
|
||||||
|
# Другой режим совместимости
|
||||||
|
... -Name TestCfg -CompatibilityMode Version8_3_27 -OutputDir test-tmp/cf3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Верификация
|
||||||
|
|
||||||
|
```
|
||||||
|
/cf-init TestConfig -OutputDir test-tmp/cf
|
||||||
|
/cf-info test-tmp/cf — проверить созданное
|
||||||
|
/cf-validate test-tmp/cf — валидировать
|
||||||
|
```
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
# cf-init v1.0 — Create empty 1C configuration scaffold
|
||||||
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$Name,
|
||||||
|
[string]$Synonym = $Name,
|
||||||
|
[string]$OutputDir = "src",
|
||||||
|
[string]$Version,
|
||||||
|
[string]$Vendor,
|
||||||
|
[string]$CompatibilityMode = "Version8_3_24"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
# --- Resolve output dir ---
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($OutputDir)) {
|
||||||
|
$OutputDir = Join-Path (Get-Location).Path $OutputDir
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Check existing ---
|
||||||
|
$cfgFile = Join-Path $OutputDir "Configuration.xml"
|
||||||
|
if (Test-Path $cfgFile) {
|
||||||
|
Write-Error "Configuration.xml already exists: $cfgFile"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Generate UUIDs ---
|
||||||
|
$uuidCfg = [guid]::NewGuid().ToString()
|
||||||
|
$uuidLang = [guid]::NewGuid().ToString()
|
||||||
|
# 7 ContainedObject ObjectIds
|
||||||
|
$co1 = [guid]::NewGuid().ToString()
|
||||||
|
$co2 = [guid]::NewGuid().ToString()
|
||||||
|
$co3 = [guid]::NewGuid().ToString()
|
||||||
|
$co4 = [guid]::NewGuid().ToString()
|
||||||
|
$co5 = [guid]::NewGuid().ToString()
|
||||||
|
$co6 = [guid]::NewGuid().ToString()
|
||||||
|
$co7 = [guid]::NewGuid().ToString()
|
||||||
|
|
||||||
|
# --- Mobile functionalities ---
|
||||||
|
$mobileFuncs = @(
|
||||||
|
@("Biometrics","true"), @("Location","false"), @("BackgroundLocation","false"),
|
||||||
|
@("BluetoothPrinters","false"), @("WiFiPrinters","false"), @("Contacts","false"),
|
||||||
|
@("Calendars","false"), @("PushNotifications","false"), @("LocalNotifications","false"),
|
||||||
|
@("InAppPurchases","false"), @("PersonalComputerFileExchange","false"), @("Ads","false"),
|
||||||
|
@("NumberDialing","false"), @("CallProcessing","false"), @("CallLog","false"),
|
||||||
|
@("AutoSendSMS","false"), @("ReceiveSMS","false"), @("SMSLog","false"),
|
||||||
|
@("Camera","false"), @("Microphone","false"), @("MusicLibrary","false"),
|
||||||
|
@("PictureAndVideoLibraries","false"), @("AudioPlaybackAndVibration","false"),
|
||||||
|
@("BackgroundAudioPlaybackAndVibration","false"), @("InstallPackages","false"),
|
||||||
|
@("OSBackup","true"), @("ApplicationUsageStatistics","false"),
|
||||||
|
@("BarcodeScanning","false"), @("BackgroundAudioRecording","false"),
|
||||||
|
@("AllFilesAccess","false"), @("Videoconferences","false"), @("NFC","false"),
|
||||||
|
@("DocumentScanning","false"), @("SpeechToText","false"), @("Geofences","false"),
|
||||||
|
@("IncomingShareRequests","false"), @("AllIncomingShareRequestsTypesProcessing","false")
|
||||||
|
)
|
||||||
|
|
||||||
|
$mobileXml = ""
|
||||||
|
foreach ($mf in $mobileFuncs) {
|
||||||
|
$mobileXml += "`r`n`t`t`t`t<app:functionality>`r`n`t`t`t`t`t<app:functionality>$($mf[0])</app:functionality>`r`n`t`t`t`t`t<app:use>$($mf[1])</app:use>`r`n`t`t`t`t</app:functionality>"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Synonym XML ---
|
||||||
|
$synonymXml = ""
|
||||||
|
if ($Synonym) {
|
||||||
|
$synonymXml = "`r`n`t`t`t`t<v8:item>`r`n`t`t`t`t`t<v8:lang>ru</v8:lang>`r`n`t`t`t`t`t<v8:content>$([System.Security.SecurityElement]::Escape($Synonym))</v8:content>`r`n`t`t`t`t</v8:item>`r`n`t`t`t"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Optional properties ---
|
||||||
|
$vendorXml = if ($Vendor) { [System.Security.SecurityElement]::Escape($Vendor) } else { "" }
|
||||||
|
$versionXml = if ($Version) { [System.Security.SecurityElement]::Escape($Version) } else { "" }
|
||||||
|
|
||||||
|
# --- Configuration.xml ---
|
||||||
|
$cfgXml = @"
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||||
|
<Configuration uuid="$uuidCfg">
|
||||||
|
<InternalInfo>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9cd510cd-abfc-11d4-9434-004095e12fc7</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co1</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9fcd25a0-4822-11d4-9414-008048da11f9</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co2</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>e3687481-0a87-462c-a166-9f34594f9bba</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co3</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>9de14907-ec23-4a07-96f0-85521cb6b53b</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co4</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>51f2d5d8-ea4d-4064-8892-82951750031e</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co5</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>e68182ea-4237-4383-967f-90c1e3370bc7</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co6</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
<xr:ContainedObject>
|
||||||
|
<xr:ClassId>fb282519-d103-4dd3-bc12-cb271d631dfc</xr:ClassId>
|
||||||
|
<xr:ObjectId>$co7</xr:ObjectId>
|
||||||
|
</xr:ContainedObject>
|
||||||
|
</InternalInfo>
|
||||||
|
<Properties>
|
||||||
|
<Name>$([System.Security.SecurityElement]::Escape($Name))</Name>
|
||||||
|
<Synonym>$synonymXml</Synonym>
|
||||||
|
<Comment/>
|
||||||
|
<NamePrefix/>
|
||||||
|
<ConfigurationExtensionCompatibilityMode>$CompatibilityMode</ConfigurationExtensionCompatibilityMode>
|
||||||
|
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||||
|
<UsePurposes>
|
||||||
|
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||||
|
</UsePurposes>
|
||||||
|
<ScriptVariant>Russian</ScriptVariant>
|
||||||
|
<DefaultRoles/>
|
||||||
|
<Vendor>$vendorXml</Vendor>
|
||||||
|
<Version>$versionXml</Version>
|
||||||
|
<UpdateCatalogAddress/>
|
||||||
|
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||||
|
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
|
||||||
|
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
|
||||||
|
<AdditionalFullTextSearchDictionaries/>
|
||||||
|
<CommonSettingsStorage/>
|
||||||
|
<ReportsUserSettingsStorage/>
|
||||||
|
<ReportsVariantsStorage/>
|
||||||
|
<FormDataSettingsStorage/>
|
||||||
|
<DynamicListsUserSettingsStorage/>
|
||||||
|
<URLExternalDataStorage/>
|
||||||
|
<Content/>
|
||||||
|
<DefaultReportForm/>
|
||||||
|
<DefaultReportVariantForm/>
|
||||||
|
<DefaultReportSettingsForm/>
|
||||||
|
<DefaultReportAppearanceTemplate/>
|
||||||
|
<DefaultDynamicListSettingsForm/>
|
||||||
|
<DefaultSearchForm/>
|
||||||
|
<DefaultDataHistoryChangeHistoryForm/>
|
||||||
|
<DefaultDataHistoryVersionDataForm/>
|
||||||
|
<DefaultDataHistoryVersionDifferencesForm/>
|
||||||
|
<DefaultCollaborationSystemUsersChoiceForm/>
|
||||||
|
<RequiredMobileApplicationPermissions/>
|
||||||
|
<UsedMobileApplicationFunctionalities>$mobileXml
|
||||||
|
</UsedMobileApplicationFunctionalities>
|
||||||
|
<StandaloneConfigurationRestrictionRoles/>
|
||||||
|
<MobileApplicationURLs/>
|
||||||
|
<AllowedIncomingShareRequestTypes/>
|
||||||
|
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
|
||||||
|
<DefaultInterface/>
|
||||||
|
<DefaultStyle/>
|
||||||
|
<DefaultLanguage>Language.Русский</DefaultLanguage>
|
||||||
|
<BriefInformation/>
|
||||||
|
<DetailedInformation/>
|
||||||
|
<Copyright/>
|
||||||
|
<VendorInformationAddress/>
|
||||||
|
<ConfigurationInformationAddress/>
|
||||||
|
<DataLockControlMode>Managed</DataLockControlMode>
|
||||||
|
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
|
||||||
|
<ModalityUseMode>DontUse</ModalityUseMode>
|
||||||
|
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
|
||||||
|
<InterfaceCompatibilityMode>Taxi</InterfaceCompatibilityMode>
|
||||||
|
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
|
||||||
|
<CompatibilityMode>$CompatibilityMode</CompatibilityMode>
|
||||||
|
<DefaultConstantsForm/>
|
||||||
|
</Properties>
|
||||||
|
<ChildObjects>
|
||||||
|
<Language>Русский</Language>
|
||||||
|
</ChildObjects>
|
||||||
|
</Configuration>
|
||||||
|
</MetaDataObject>
|
||||||
|
"@
|
||||||
|
|
||||||
|
# --- ConfigDumpInfo.xml ---
|
||||||
|
$dumpInfoXml = @"
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ConfigDumpInfo xmlns="http://v8.1c.ru/8.3/xcf/dumpinfo" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" format="Hierarchical" version="2.17">
|
||||||
|
<ConfigVersions/>
|
||||||
|
</ConfigDumpInfo>
|
||||||
|
"@
|
||||||
|
|
||||||
|
# --- Languages/Русский.xml ---
|
||||||
|
$langXml = @"
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
|
||||||
|
<Language uuid="$uuidLang">
|
||||||
|
<Properties>
|
||||||
|
<Name>Русский</Name>
|
||||||
|
<Synonym>
|
||||||
|
<v8:item>
|
||||||
|
<v8:lang>ru</v8:lang>
|
||||||
|
<v8:content>Русский</v8:content>
|
||||||
|
</v8:item>
|
||||||
|
</Synonym>
|
||||||
|
<Comment/>
|
||||||
|
<LanguageCode>ru</LanguageCode>
|
||||||
|
</Properties>
|
||||||
|
</Language>
|
||||||
|
</MetaDataObject>
|
||||||
|
"@
|
||||||
|
|
||||||
|
# --- Create directories ---
|
||||||
|
if (-not (Test-Path $OutputDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
$langDir = Join-Path $OutputDir "Languages"
|
||||||
|
if (-not (Test-Path $langDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $langDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Write files with UTF-8 BOM ---
|
||||||
|
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||||
|
|
||||||
|
[System.IO.File]::WriteAllText($cfgFile, $cfgXml, $enc)
|
||||||
|
$dumpInfoFile = Join-Path $OutputDir "ConfigDumpInfo.xml"
|
||||||
|
[System.IO.File]::WriteAllText($dumpInfoFile, $dumpInfoXml, $enc)
|
||||||
|
$langFile = Join-Path $langDir "Русский.xml"
|
||||||
|
[System.IO.File]::WriteAllText($langFile, $langXml, $enc)
|
||||||
|
|
||||||
|
# --- Output ---
|
||||||
|
Write-Host "[OK] Создана конфигурация: $Name"
|
||||||
|
Write-Host " Каталог: $OutputDir"
|
||||||
|
Write-Host " Configuration.xml: $cfgFile"
|
||||||
|
Write-Host " ConfigDumpInfo.xml: $dumpInfoFile"
|
||||||
|
Write-Host " Languages: $langFile"
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
name: cf-validate
|
||||||
|
description: Валидация структурной корректности конфигурации 1С (Configuration.xml) — корневая структура, свойства, состав, языки, каталоги
|
||||||
|
argument-hint: <ConfigPath> [-MaxErrors 30]
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
---
|
||||||
|
|
||||||
|
# /cf-validate — валидация конфигурации 1С
|
||||||
|
|
||||||
|
Проверяет Configuration.xml на структурные ошибки: XML well-formedness, InternalInfo, свойства, enum-значения, ChildObjects, DefaultLanguage, файлы языков, каталоги объектов.
|
||||||
|
|
||||||
|
## Параметры и команда
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `ConfigPath` | Путь к Configuration.xml или каталогу выгрузки |
|
||||||
|
| `MaxErrors` | Остановиться после N ошибок (default: 30) |
|
||||||
|
| `OutFile` | Записать результат в файл (UTF-8 BOM) |
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File .claude\skills\cf-validate\scripts\cf-validate.ps1 -ConfigPath "<путь>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Выполняемые проверки
|
||||||
|
|
||||||
|
| # | Проверка | Серьёзность |
|
||||||
|
|---|----------|-------------|
|
||||||
|
| 1 | XML well-formedness, MetaDataObject/Configuration, version 2.17/2.20 | ERROR |
|
||||||
|
| 2 | InternalInfo: 7 ContainedObject, валидные ClassId, уникальность | ERROR |
|
||||||
|
| 3 | Properties: Name непустой, Synonym, DefaultLanguage, DefaultRunMode | ERROR/WARN |
|
||||||
|
| 4 | Properties: enum-значения (11 свойств) | ERROR |
|
||||||
|
| 5 | ChildObjects: валидные имена типов (44 типа), нет дубликатов, порядок типов | ERROR/WARN |
|
||||||
|
| 6 | DefaultLanguage ссылается на существующий Language в ChildObjects | ERROR |
|
||||||
|
| 7 | Файлы языков Languages/<name>.xml существуют | WARN |
|
||||||
|
| 8 | Каталоги объектов из ChildObjects существуют (spot-check) | WARN |
|
||||||
|
|
||||||
|
## Вывод
|
||||||
|
|
||||||
|
```
|
||||||
|
=== Validation: Configuration.МояКонфигурация ===
|
||||||
|
|
||||||
|
[OK] 1. Root structure: MetaDataObject/Configuration, version 2.17
|
||||||
|
[OK] 2. InternalInfo: 7 ContainedObject, all ClassIds valid
|
||||||
|
[OK] 3. Properties: Name="МояКонфигурация", Synonym present
|
||||||
|
[OK] 4. Property values: 11 enum properties checked
|
||||||
|
[OK] 5. ChildObjects: 1 types, 1 objects, order correct
|
||||||
|
[OK] 6. DefaultLanguage "Language.Русский" found in ChildObjects
|
||||||
|
[OK] 7. Language files: 1/1 exist
|
||||||
|
[OK] 8. Object directories: spot-check passed
|
||||||
|
|
||||||
|
=== Result: 0 errors, 0 warnings ===
|
||||||
|
```
|
||||||
|
|
||||||
|
Exit code: 0 = OK, 1 = errors.
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Пустая конфигурация
|
||||||
|
... -ConfigPath upload/cfempty
|
||||||
|
|
||||||
|
# Реальная конфигурация
|
||||||
|
... -ConfigPath C:\WS\tasks\cfsrc\acc_8.3.24
|
||||||
|
|
||||||
|
# С лимитом ошибок
|
||||||
|
... -ConfigPath test-tmp/cf -MaxErrors 10
|
||||||
|
```
|
||||||
@@ -0,0 +1,538 @@
|
|||||||
|
# cf-validate v1.0 — Validate 1C configuration root structure
|
||||||
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string]$ConfigPath,
|
||||||
|
|
||||||
|
[int]$MaxErrors = 30,
|
||||||
|
|
||||||
|
[string]$OutFile
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
# --- Resolve path ---
|
||||||
|
if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) {
|
||||||
|
$ConfigPath = Join-Path (Get-Location).Path $ConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $ConfigPath -PathType Container) {
|
||||||
|
$candidate = Join-Path $ConfigPath "Configuration.xml"
|
||||||
|
if (Test-Path $candidate) {
|
||||||
|
$ConfigPath = $candidate
|
||||||
|
} else {
|
||||||
|
Write-Host "[ERROR] No Configuration.xml found in directory: $ConfigPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $ConfigPath)) {
|
||||||
|
Write-Host "[ERROR] File not found: $ConfigPath"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolvedPath = (Resolve-Path $ConfigPath).Path
|
||||||
|
$configDir = Split-Path $resolvedPath -Parent
|
||||||
|
|
||||||
|
# --- Output infrastructure ---
|
||||||
|
$script:errors = 0
|
||||||
|
$script:warnings = 0
|
||||||
|
$script:stopped = $false
|
||||||
|
$script:output = New-Object System.Text.StringBuilder 8192
|
||||||
|
|
||||||
|
function Out-Line {
|
||||||
|
param([string]$msg)
|
||||||
|
$script:output.AppendLine($msg) | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Report-OK {
|
||||||
|
param([string]$msg)
|
||||||
|
Out-Line "[OK] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Report-Error {
|
||||||
|
param([string]$msg)
|
||||||
|
$script:errors++
|
||||||
|
Out-Line "[ERROR] $msg"
|
||||||
|
if ($script:errors -ge $MaxErrors) {
|
||||||
|
$script:stopped = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Report-Warn {
|
||||||
|
param([string]$msg)
|
||||||
|
$script:warnings++
|
||||||
|
Out-Line "[WARN] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalize = {
|
||||||
|
Out-Line ""
|
||||||
|
Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ==="
|
||||||
|
|
||||||
|
$result = $script:output.ToString()
|
||||||
|
Write-Host $result
|
||||||
|
|
||||||
|
if ($OutFile) {
|
||||||
|
$utf8Bom = New-Object System.Text.UTF8Encoding $true
|
||||||
|
[System.IO.File]::WriteAllText($OutFile, $result, $utf8Bom)
|
||||||
|
Write-Host "Written to: $OutFile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Reference tables ---
|
||||||
|
$guidPattern = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
|
||||||
|
$identPattern = '^[A-Za-z\u0410-\u042F\u0401\u0430-\u044F\u0451_][A-Za-z0-9\u0410-\u042F\u0401\u0430-\u044F\u0451_]*$'
|
||||||
|
|
||||||
|
# 7 fixed ClassIds for Configuration
|
||||||
|
$validClassIds = @(
|
||||||
|
"9cd510cd-abfc-11d4-9434-004095e12fc7", # managed application module
|
||||||
|
"9fcd25a0-4822-11d4-9414-008048da11f9", # ordinary application module
|
||||||
|
"e3687481-0a87-462c-a166-9f34594f9bba", # session module
|
||||||
|
"9de14907-ec23-4a07-96f0-85521cb6b53b", # external connection module
|
||||||
|
"51f2d5d8-ea4d-4064-8892-82951750031e", # command interface
|
||||||
|
"e68182ea-4237-4383-967f-90c1e3370bc7", # main section command interface
|
||||||
|
"fb282519-d103-4dd3-bc12-cb271d631dfc" # home page / client app interface
|
||||||
|
)
|
||||||
|
|
||||||
|
# 44 types in canonical order
|
||||||
|
$childObjectTypes = @(
|
||||||
|
"Language","Subsystem","StyleItem","Style",
|
||||||
|
"CommonPicture","SessionParameter","Role","CommonTemplate",
|
||||||
|
"FilterCriterion","CommonModule","CommonAttribute","ExchangePlan",
|
||||||
|
"XDTOPackage","WebService","HTTPService","WSReference",
|
||||||
|
"EventSubscription","ScheduledJob","SettingsStorage","FunctionalOption",
|
||||||
|
"FunctionalOptionsParameter","DefinedType","CommonCommand","CommandGroup",
|
||||||
|
"Constant","CommonForm","Catalog","Document",
|
||||||
|
"DocumentNumerator","Sequence","DocumentJournal","Enum",
|
||||||
|
"Report","DataProcessor","InformationRegister","AccumulationRegister",
|
||||||
|
"ChartOfCharacteristicTypes","ChartOfAccounts","AccountingRegister",
|
||||||
|
"ChartOfCalculationTypes","CalculationRegister",
|
||||||
|
"BusinessProcess","Task","IntegrationService"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Type -> directory mapping
|
||||||
|
$childTypeDirMap = @{
|
||||||
|
"Language"="Languages"; "Subsystem"="Subsystems"; "StyleItem"="StyleItems"; "Style"="Styles"
|
||||||
|
"CommonPicture"="CommonPictures"; "SessionParameter"="SessionParameters"; "Role"="Roles"
|
||||||
|
"CommonTemplate"="CommonTemplates"; "FilterCriterion"="FilterCriteria"; "CommonModule"="CommonModules"
|
||||||
|
"CommonAttribute"="CommonAttributes"; "ExchangePlan"="ExchangePlans"; "XDTOPackage"="XDTOPackages"
|
||||||
|
"WebService"="WebServices"; "HTTPService"="HTTPServices"; "WSReference"="WSReferences"
|
||||||
|
"EventSubscription"="EventSubscriptions"; "ScheduledJob"="ScheduledJobs"
|
||||||
|
"SettingsStorage"="SettingsStorages"; "FunctionalOption"="FunctionalOptions"
|
||||||
|
"FunctionalOptionsParameter"="FunctionalOptionsParameters"; "DefinedType"="DefinedTypes"
|
||||||
|
"CommonCommand"="CommonCommands"; "CommandGroup"="CommandGroups"; "Constant"="Constants"
|
||||||
|
"CommonForm"="CommonForms"; "Catalog"="Catalogs"; "Document"="Documents"
|
||||||
|
"DocumentNumerator"="DocumentNumerators"; "Sequence"="Sequences"
|
||||||
|
"DocumentJournal"="DocumentJournals"; "Enum"="Enums"; "Report"="Reports"
|
||||||
|
"DataProcessor"="DataProcessors"; "InformationRegister"="InformationRegisters"
|
||||||
|
"AccumulationRegister"="AccumulationRegisters"
|
||||||
|
"ChartOfCharacteristicTypes"="ChartsOfCharacteristicTypes"
|
||||||
|
"ChartOfAccounts"="ChartsOfAccounts"; "AccountingRegister"="AccountingRegisters"
|
||||||
|
"ChartOfCalculationTypes"="ChartsOfCalculationTypes"
|
||||||
|
"CalculationRegister"="CalculationRegisters"
|
||||||
|
"BusinessProcess"="BusinessProcesses"; "Task"="Tasks"
|
||||||
|
"IntegrationService"="IntegrationServices"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Valid enum values for Configuration properties
|
||||||
|
$validEnumValues = @{
|
||||||
|
"ConfigurationExtensionCompatibilityMode" = @("DontUse","Version8_1","Version8_2_13","Version8_2_16","Version8_3_1","Version8_3_2","Version8_3_3","Version8_3_4","Version8_3_5","Version8_3_6","Version8_3_7","Version8_3_8","Version8_3_9","Version8_3_10","Version8_3_11","Version8_3_12","Version8_3_13","Version8_3_14","Version8_3_15","Version8_3_16","Version8_3_17","Version8_3_18","Version8_3_19","Version8_3_20","Version8_3_21","Version8_3_22","Version8_3_23","Version8_3_24","Version8_3_25","Version8_3_26","Version8_3_27","Version8_3_28")
|
||||||
|
"DefaultRunMode" = @("ManagedApplication","OrdinaryApplication","Auto")
|
||||||
|
"ScriptVariant" = @("Russian","English")
|
||||||
|
"DataLockControlMode" = @("Automatic","Managed","AutomaticAndManaged")
|
||||||
|
"ObjectAutonumerationMode" = @("NotAutoFree","AutoFree")
|
||||||
|
"ModalityUseMode" = @("DontUse","Use","UseWithWarnings")
|
||||||
|
"SynchronousPlatformExtensionAndAddInCallUseMode" = @("DontUse","Use","UseWithWarnings")
|
||||||
|
"InterfaceCompatibilityMode" = @("Taxi","TaxiEnableVersion8_2","Version8_2")
|
||||||
|
"DatabaseTablespacesUseMode" = @("DontUse","Use")
|
||||||
|
"MainClientApplicationWindowMode" = @("Normal","Fullscreen","Kiosk")
|
||||||
|
"CompatibilityMode" = @("DontUse","Version8_1","Version8_2_13","Version8_2_16","Version8_3_1","Version8_3_2","Version8_3_3","Version8_3_4","Version8_3_5","Version8_3_6","Version8_3_7","Version8_3_8","Version8_3_9","Version8_3_10","Version8_3_11","Version8_3_12","Version8_3_13","Version8_3_14","Version8_3_15","Version8_3_16","Version8_3_17","Version8_3_18","Version8_3_19","Version8_3_20","Version8_3_21","Version8_3_22","Version8_3_23","Version8_3_24","Version8_3_25","Version8_3_26","Version8_3_27","Version8_3_28")
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 1. Parse XML ---
|
||||||
|
Out-Line ""
|
||||||
|
|
||||||
|
$xmlDoc = $null
|
||||||
|
try {
|
||||||
|
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||||
|
$xmlDoc.PreserveWhitespace = $false
|
||||||
|
$xmlDoc.Load($resolvedPath)
|
||||||
|
} catch {
|
||||||
|
Out-Line "=== Validation: Configuration (parse failed) ==="
|
||||||
|
Out-Line ""
|
||||||
|
Report-Error "1. XML parse failed: $($_.Exception.Message)"
|
||||||
|
& $finalize
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Register namespaces ---
|
||||||
|
$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("app", "http://v8.1c.ru/8.2/managed-application/core")
|
||||||
|
|
||||||
|
$root = $xmlDoc.DocumentElement
|
||||||
|
|
||||||
|
# --- Check 1: Root structure ---
|
||||||
|
$check1Ok = $true
|
||||||
|
$expectedNs = "http://v8.1c.ru/8.3/MDClasses"
|
||||||
|
|
||||||
|
if ($root.LocalName -ne "MetaDataObject") {
|
||||||
|
Report-Error "1. Root element is '$($root.LocalName)', expected 'MetaDataObject'"
|
||||||
|
& $finalize
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($root.NamespaceURI -ne $expectedNs) {
|
||||||
|
Report-Error "1. Root namespace is '$($root.NamespaceURI)', expected '$expectedNs'"
|
||||||
|
$check1Ok = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $root.GetAttribute("version")
|
||||||
|
if (-not $version) {
|
||||||
|
Report-Warn "1. Missing version attribute on MetaDataObject"
|
||||||
|
} elseif ($version -ne "2.17" -and $version -ne "2.20") {
|
||||||
|
Report-Warn "1. Unusual version '$version' (expected 2.17 or 2.20)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Must have Configuration child
|
||||||
|
$cfgNode = $null
|
||||||
|
foreach ($child in $root.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Configuration" -and $child.NamespaceURI -eq $expectedNs) {
|
||||||
|
$cfgNode = $child; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $cfgNode) {
|
||||||
|
Report-Error "1. No <Configuration> element found inside MetaDataObject"
|
||||||
|
& $finalize
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# UUID
|
||||||
|
$cfgUuid = $cfgNode.GetAttribute("uuid")
|
||||||
|
if (-not $cfgUuid) {
|
||||||
|
Report-Error "1. Missing uuid on <Configuration>"
|
||||||
|
$check1Ok = $false
|
||||||
|
} elseif ($cfgUuid -notmatch $guidPattern) {
|
||||||
|
Report-Error "1. Invalid uuid '$cfgUuid' on <Configuration>"
|
||||||
|
$check1Ok = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get name early for header
|
||||||
|
$propsNode = $cfgNode.SelectSingleNode("md:Properties", $ns)
|
||||||
|
$nameNode = if ($propsNode) { $propsNode.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||||
|
$objName = if ($nameNode -and $nameNode.InnerText) { $nameNode.InnerText } else { "(unknown)" }
|
||||||
|
|
||||||
|
$script:output.Insert(0, "=== Validation: Configuration.$objName ===$([Environment]::NewLine)") | Out-Null
|
||||||
|
|
||||||
|
if ($check1Ok) {
|
||||||
|
Report-OK "1. Root structure: MetaDataObject/Configuration, version $version"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 2: InternalInfo ---
|
||||||
|
$internalInfo = $cfgNode.SelectSingleNode("md:InternalInfo", $ns)
|
||||||
|
$check2Ok = $true
|
||||||
|
|
||||||
|
if (-not $internalInfo) {
|
||||||
|
Report-Error "2. InternalInfo: missing"
|
||||||
|
} else {
|
||||||
|
$contained = $internalInfo.SelectNodes("xr:ContainedObject", $ns)
|
||||||
|
if ($contained.Count -ne 7) {
|
||||||
|
Report-Warn "2. InternalInfo: expected 7 ContainedObject, found $($contained.Count)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$foundClassIds = @{}
|
||||||
|
foreach ($co in $contained) {
|
||||||
|
$classId = $co.SelectSingleNode("xr:ClassId", $ns)
|
||||||
|
$objectId = $co.SelectSingleNode("xr:ObjectId", $ns)
|
||||||
|
|
||||||
|
if (-not $classId -or -not $classId.InnerText) {
|
||||||
|
Report-Error "2. ContainedObject missing ClassId"
|
||||||
|
$check2Ok = $false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$cid = $classId.InnerText
|
||||||
|
if ($validClassIds -notcontains $cid) {
|
||||||
|
Report-Error "2. Unknown ClassId: $cid"
|
||||||
|
$check2Ok = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($foundClassIds.ContainsKey($cid)) {
|
||||||
|
Report-Error "2. Duplicate ClassId: $cid"
|
||||||
|
$check2Ok = $false
|
||||||
|
}
|
||||||
|
$foundClassIds[$cid] = $true
|
||||||
|
|
||||||
|
if (-not $objectId -or -not $objectId.InnerText) {
|
||||||
|
Report-Error "2. ContainedObject missing ObjectId for ClassId $cid"
|
||||||
|
$check2Ok = $false
|
||||||
|
} elseif ($objectId.InnerText -notmatch $guidPattern) {
|
||||||
|
Report-Error "2. Invalid ObjectId '$($objectId.InnerText)' for ClassId $cid"
|
||||||
|
$check2Ok = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check missing ClassIds
|
||||||
|
$missingIds = @($validClassIds | Where-Object { -not $foundClassIds.ContainsKey($_) })
|
||||||
|
if ($missingIds.Count -gt 0) {
|
||||||
|
Report-Warn "2. Missing ClassIds: $($missingIds.Count) of 7"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($check2Ok) {
|
||||||
|
Report-OK "2. InternalInfo: $($contained.Count) ContainedObject, all ClassIds valid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 3: Properties — Name, Synonym, DefaultLanguage, DefaultRunMode ---
|
||||||
|
if (-not $propsNode) {
|
||||||
|
Report-Error "3. Properties block missing"
|
||||||
|
} else {
|
||||||
|
$check3Ok = $true
|
||||||
|
|
||||||
|
# Name
|
||||||
|
if (-not $nameNode -or -not $nameNode.InnerText) {
|
||||||
|
Report-Error "3. Properties: Name is missing or empty"
|
||||||
|
$check3Ok = $false
|
||||||
|
} else {
|
||||||
|
$nameVal = $nameNode.InnerText
|
||||||
|
if ($nameVal -notmatch $identPattern) {
|
||||||
|
Report-Error "3. Properties: Name '$nameVal' is not a valid 1C identifier"
|
||||||
|
$check3Ok = $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Synonym
|
||||||
|
$synNode = $propsNode.SelectSingleNode("md:Synonym", $ns)
|
||||||
|
$synPresent = $false
|
||||||
|
if ($synNode) {
|
||||||
|
$synItem = $synNode.SelectSingleNode("v8:item", $ns)
|
||||||
|
if ($synItem) {
|
||||||
|
$synContent = $synItem.SelectSingleNode("v8:content", $ns)
|
||||||
|
if ($synContent -and $synContent.InnerText) { $synPresent = $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# DefaultLanguage
|
||||||
|
$defLangNode = $propsNode.SelectSingleNode("md:DefaultLanguage", $ns)
|
||||||
|
$defLang = if ($defLangNode -and $defLangNode.InnerText) { $defLangNode.InnerText } else { "" }
|
||||||
|
if (-not $defLang) {
|
||||||
|
Report-Error "3. Properties: DefaultLanguage is missing or empty"
|
||||||
|
$check3Ok = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# DefaultRunMode
|
||||||
|
$defRunNode = $propsNode.SelectSingleNode("md:DefaultRunMode", $ns)
|
||||||
|
if (-not $defRunNode -or -not $defRunNode.InnerText) {
|
||||||
|
Report-Warn "3. Properties: DefaultRunMode is missing or empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($check3Ok) {
|
||||||
|
$synInfo = if ($synPresent) { "Synonym present" } else { "no Synonym" }
|
||||||
|
Report-OK "3. Properties: Name=`"$objName`", $synInfo, DefaultLanguage=$defLang"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 4: Property values — enum properties ---
|
||||||
|
if ($propsNode) {
|
||||||
|
$enumChecked = 0
|
||||||
|
$check4Ok = $true
|
||||||
|
|
||||||
|
foreach ($propName in $validEnumValues.Keys) {
|
||||||
|
$propNode = $propsNode.SelectSingleNode("md:$propName", $ns)
|
||||||
|
if ($propNode -and $propNode.InnerText) {
|
||||||
|
$val = $propNode.InnerText
|
||||||
|
$allowed = $validEnumValues[$propName]
|
||||||
|
if ($allowed -notcontains $val) {
|
||||||
|
Report-Error "4. Property '$propName' has invalid value '$val'"
|
||||||
|
$check4Ok = $false
|
||||||
|
}
|
||||||
|
$enumChecked++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($check4Ok) {
|
||||||
|
Report-OK "4. Property values: $enumChecked enum properties checked"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Report-Warn "4. No Properties block to check"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 5: ChildObjects — valid types, no duplicates, order ---
|
||||||
|
$childObjNode = $cfgNode.SelectSingleNode("md:ChildObjects", $ns)
|
||||||
|
|
||||||
|
if (-not $childObjNode) {
|
||||||
|
Report-Error "5. ChildObjects block missing"
|
||||||
|
} else {
|
||||||
|
$check5Ok = $true
|
||||||
|
$totalCount = 0
|
||||||
|
$typeCounts = @{}
|
||||||
|
$duplicates = @{}
|
||||||
|
$typeFirstIndex = @{} # type -> first position index
|
||||||
|
$lastTypeOrder = -1
|
||||||
|
$orderOk = $true
|
||||||
|
$idx = 0
|
||||||
|
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
$typeName = $child.LocalName
|
||||||
|
$objNameVal = $child.InnerText
|
||||||
|
|
||||||
|
# Valid type?
|
||||||
|
$typeIdx = $childObjectTypes.IndexOf($typeName)
|
||||||
|
if ($typeIdx -lt 0) {
|
||||||
|
Report-Error "5. Unknown type '$typeName' in ChildObjects"
|
||||||
|
$check5Ok = $false
|
||||||
|
} else {
|
||||||
|
# Check order
|
||||||
|
if (-not $typeFirstIndex.ContainsKey($typeName)) {
|
||||||
|
$typeFirstIndex[$typeName] = $typeIdx
|
||||||
|
if ($typeIdx -lt $lastTypeOrder) {
|
||||||
|
Report-Warn "5. Type '$typeName' is out of canonical order (after type at position $lastTypeOrder)"
|
||||||
|
$orderOk = $false
|
||||||
|
}
|
||||||
|
$lastTypeOrder = $typeIdx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Count and dedup
|
||||||
|
if (-not $typeCounts.ContainsKey($typeName)) { $typeCounts[$typeName] = @{} }
|
||||||
|
if ($typeCounts[$typeName].ContainsKey($objNameVal)) {
|
||||||
|
if (-not $duplicates.ContainsKey("$typeName.$objNameVal")) {
|
||||||
|
Report-Error "5. Duplicate: $typeName.$objNameVal"
|
||||||
|
$duplicates["$typeName.$objNameVal"] = $true
|
||||||
|
$check5Ok = $false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$typeCounts[$typeName][$objNameVal] = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalCount++
|
||||||
|
$idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
$typeCount = $typeCounts.Count
|
||||||
|
if ($check5Ok) {
|
||||||
|
$orderInfo = if ($orderOk) { ", order correct" } else { "" }
|
||||||
|
Report-OK "5. ChildObjects: $typeCount types, $totalCount objects${orderInfo}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 6: DefaultLanguage references existing Language in ChildObjects ---
|
||||||
|
if ($defLang -and $childObjNode) {
|
||||||
|
# DefaultLanguage is like "Language.Русский"
|
||||||
|
$langName = $defLang
|
||||||
|
if ($langName.StartsWith("Language.")) {
|
||||||
|
$langName = $langName.Substring(9)
|
||||||
|
}
|
||||||
|
|
||||||
|
$found = $false
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Language" -and $child.InnerText -eq $langName) {
|
||||||
|
$found = $true; break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($found) {
|
||||||
|
Report-OK "6. DefaultLanguage `"$defLang`" found in ChildObjects"
|
||||||
|
} else {
|
||||||
|
Report-Error "6. DefaultLanguage `"$defLang`" not found in ChildObjects"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (-not $defLang) {
|
||||||
|
Report-Warn "6. Cannot check DefaultLanguage (empty)"
|
||||||
|
} else {
|
||||||
|
Report-Warn "6. Cannot check DefaultLanguage (no ChildObjects)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 7: Language files exist ---
|
||||||
|
if ($childObjNode) {
|
||||||
|
$langNames = @()
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Language") {
|
||||||
|
$langNames += $child.InnerText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($langNames.Count -gt 0) {
|
||||||
|
$existCount = 0
|
||||||
|
foreach ($ln in $langNames) {
|
||||||
|
$langFile = Join-Path (Join-Path $configDir "Languages") "$ln.xml"
|
||||||
|
if (Test-Path $langFile) {
|
||||||
|
$existCount++
|
||||||
|
} else {
|
||||||
|
Report-Warn "7. Language file missing: Languages/$ln.xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($existCount -eq $langNames.Count) {
|
||||||
|
Report-OK "7. Language files: $existCount/$($langNames.Count) exist"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Report-Warn "7. No Language entries in ChildObjects"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Report-Warn "7. Cannot check language files (no ChildObjects)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($script:stopped) { & $finalize; exit 1 }
|
||||||
|
|
||||||
|
# --- Check 8: Object directories exist (spot-check) ---
|
||||||
|
if ($childObjNode) {
|
||||||
|
$dirsToCheck = @{}
|
||||||
|
foreach ($child in $childObjNode.ChildNodes) {
|
||||||
|
if ($child.NodeType -ne 'Element') { continue }
|
||||||
|
$typeName = $child.LocalName
|
||||||
|
if ($typeName -eq "Language") { continue } # Already checked
|
||||||
|
if ($childTypeDirMap.ContainsKey($typeName)) {
|
||||||
|
$dirName = $childTypeDirMap[$typeName]
|
||||||
|
if (-not $dirsToCheck.ContainsKey($dirName)) {
|
||||||
|
$dirsToCheck[$dirName] = 0
|
||||||
|
}
|
||||||
|
$dirsToCheck[$dirName] = $dirsToCheck[$dirName] + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$missingDirs = @()
|
||||||
|
foreach ($dir in $dirsToCheck.Keys) {
|
||||||
|
$dirPath = Join-Path $configDir $dir
|
||||||
|
if (-not (Test-Path $dirPath -PathType Container)) {
|
||||||
|
$missingDirs += "$dir ($($dirsToCheck[$dir]) objects)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($missingDirs.Count -eq 0) {
|
||||||
|
Report-OK "8. Object directories: $($dirsToCheck.Count) directories, all exist"
|
||||||
|
} else {
|
||||||
|
foreach ($md in $missingDirs) {
|
||||||
|
Report-Warn "8. Missing directory: $md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Report-OK "8. Object directories: N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Final output ---
|
||||||
|
& $finalize
|
||||||
|
|
||||||
|
if ($script:errors -gt 0) {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
| Роли (Role) | 3 навыка `/role-*` | Анализ прав роли, создание из JSON DSL, валидация | [Подробнее](docs/role-guide.md) |
|
| Роли (Role) | 3 навыка `/role-*` | Анализ прав роли, создание из JSON DSL, валидация | [Подробнее](docs/role-guide.md) |
|
||||||
| Схема компоновки (СКД) | 4 навыка `/skd-*` | Анализ, генерация из JSON DSL, точечное редактирование, валидация схем компоновки данных | [Подробнее](docs/skd-guide.md) |
|
| Схема компоновки (СКД) | 4 навыка `/skd-*` | Анализ, генерация из JSON DSL, точечное редактирование, валидация схем компоновки данных | [Подробнее](docs/skd-guide.md) |
|
||||||
| Метаданные конфигурации | 4 навыка `/meta-*` | Создание, анализ, редактирование, валидация объектов метаданных (23 типа) | [Подробнее](docs/meta-guide.md) |
|
| Метаданные конфигурации | 4 навыка `/meta-*` | Создание, анализ, редактирование, валидация объектов метаданных (23 типа) | [Подробнее](docs/meta-guide.md) |
|
||||||
|
| Корневая конфигурация | 4 навыка `/cf-*` | Создание, анализ, редактирование, валидация корневых файлов конфигурации | [Подробнее](docs/cf-guide.md) |
|
||||||
| Подсистемы (Subsystem) | 4 навыка `/subsystem-*` | Анализ, создание, редактирование, валидация подсистем конфигурации | — |
|
| Подсистемы (Subsystem) | 4 навыка `/subsystem-*` | Анализ, создание, редактирование, валидация подсистем конфигурации | — |
|
||||||
| Командный интерфейс (CI) | 2 навыка `/interface-*` | Редактирование и валидация CommandInterface.xml подсистем | — |
|
| Командный интерфейс (CI) | 2 навыка `/interface-*` | Редактирование и валидация CommandInterface.xml подсистем | — |
|
||||||
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
- [SKD DSL](docs/skd-dsl-spec.md) — JSON-формат описания СКД для `/skd-compile`
|
- [SKD DSL](docs/skd-dsl-spec.md) — JSON-формат описания СКД для `/skd-compile`
|
||||||
- [Объекты конфигурации](docs/1c-config-objects-spec.md) — XML-формат объектов метаданных конфигурации (23 типа)
|
- [Объекты конфигурации](docs/1c-config-objects-spec.md) — XML-формат объектов метаданных конфигурации (23 типа)
|
||||||
- [Подсистемы и командный интерфейс](docs/1c-subsystem-spec.md) — XML-формат подсистем, CommandInterface.xml, секции видимости/размещения/порядка
|
- [Подсистемы и командный интерфейс](docs/1c-subsystem-spec.md) — XML-формат подсистем, CommandInterface.xml, секции видимости/размещения/порядка
|
||||||
|
- [Корневая конфигурация](docs/1c-configuration-spec.md) — XML-формат Configuration.xml, ConfigDumpInfo.xml, Languages/, 44 типа ChildObjects
|
||||||
|
|
||||||
## Структура репозитория
|
## Структура репозитория
|
||||||
|
|
||||||
@@ -93,6 +95,10 @@
|
|||||||
├── meta-compile/ # Создание объекта метаданных
|
├── meta-compile/ # Создание объекта метаданных
|
||||||
├── meta-edit/ # Редактирование объекта метаданных
|
├── meta-edit/ # Редактирование объекта метаданных
|
||||||
├── meta-validate/ # Валидация объекта метаданных
|
├── meta-validate/ # Валидация объекта метаданных
|
||||||
|
├── cf-info/ # Анализ структуры конфигурации
|
||||||
|
├── cf-init/ # Создание пустой конфигурации
|
||||||
|
├── cf-edit/ # Редактирование конфигурации
|
||||||
|
├── cf-validate/ # Валидация конфигурации
|
||||||
├── subsystem-info/ # Анализ структуры подсистемы
|
├── subsystem-info/ # Анализ структуры подсистемы
|
||||||
├── subsystem-compile/ # Создание подсистемы из JSON
|
├── subsystem-compile/ # Создание подсистемы из JSON
|
||||||
├── subsystem-edit/ # Редактирование подсистемы
|
├── subsystem-edit/ # Редактирование подсистемы
|
||||||
@@ -107,6 +113,7 @@ docs/
|
|||||||
├── role-guide.md # Гайд: роли
|
├── role-guide.md # Гайд: роли
|
||||||
├── skd-guide.md # Гайд: схема компоновки данных
|
├── skd-guide.md # Гайд: схема компоновки данных
|
||||||
├── meta-guide.md # Гайд: объекты метаданных конфигурации
|
├── meta-guide.md # Гайд: объекты метаданных конфигурации
|
||||||
|
├── cf-guide.md # Гайд: корневые файлы конфигурации
|
||||||
├── 1c-epf-spec.md # Спецификация XML-формата (EPF)
|
├── 1c-epf-spec.md # Спецификация XML-формата (EPF)
|
||||||
├── 1c-erf-spec.md # Спецификация XML-формата (ERF)
|
├── 1c-erf-spec.md # Спецификация XML-формата (ERF)
|
||||||
├── 1c-form-spec.md # Спецификация управляемых форм
|
├── 1c-form-spec.md # Спецификация управляемых форм
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
| `Ext/` | Модули, интерфейс, начальная страница | [1c-configuration-spec.md § 4](1c-configuration-spec.md#4-ext--корневой-каталог-конфигурации) |
|
| `Ext/` | Модули, интерфейс, начальная страница | [1c-configuration-spec.md § 4](1c-configuration-spec.md#4-ext--корневой-каталог-конфигурации) |
|
||||||
| `Languages/` | Языки конфигурации | [1c-configuration-spec.md § 5](1c-configuration-spec.md#5-языки-languages) |
|
| `Languages/` | Языки конфигурации | [1c-configuration-spec.md § 5](1c-configuration-spec.md#5-языки-languages) |
|
||||||
|
|
||||||
|
**Навыки:** `/cf-info` (анализ), `/cf-init` (создание), `/cf-validate` (валидация), `/cf-edit` (редактирование)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Объекты метаданных
|
## 2. Объекты метаданных
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
# Корневые файлы конфигурации
|
||||||
|
|
||||||
|
Навыки группы `/cf-*` позволяют создавать, анализировать, редактировать и проверять корневые файлы конфигурации 1С — `Configuration.xml`, `ConfigDumpInfo.xml`, `Languages/`.
|
||||||
|
|
||||||
|
## Навыки
|
||||||
|
|
||||||
|
| Навык | Параметры | Описание |
|
||||||
|
|-------|-----------|----------|
|
||||||
|
| `/cf-info` | `<ConfigPath> [-Mode overview\|brief\|full]` | Анализ конфигурации: свойства, состав, счётчики объектов (3 режима) |
|
||||||
|
| `/cf-init` | `<Name> [-Synonym] [-OutputDir] [-Version] [-Vendor]` | Создание пустой конфигурации (scaffold XML-исходников) |
|
||||||
|
| `/cf-validate` | `<ConfigPath> [-MaxErrors 30]` | Валидация структурной корректности (8 проверок) |
|
||||||
|
| `/cf-edit` | `<ConfigPath> -Operation <op> -Value "<val>"` | Редактирование свойств, состава ChildObjects, ролей по умолчанию (6 операций) |
|
||||||
|
|
||||||
|
## Рабочий цикл
|
||||||
|
|
||||||
|
```
|
||||||
|
Описание (текст) → /cf-init → XML-исходники → /cf-validate
|
||||||
|
↕ /cf-edit → /cf-info
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `/cf-init` создаёт scaffold пустой конфигурации (Configuration.xml, ConfigDumpInfo.xml, Languages/)
|
||||||
|
2. `/cf-edit` вносит изменения: свойства, объекты в ChildObjects, роли по умолчанию
|
||||||
|
3. `/cf-validate` проверяет корректность XML (структура, enum-значения, ссылки, каталоги)
|
||||||
|
4. `/cf-info` выводит компактную сводку для визуальной проверки
|
||||||
|
|
||||||
|
## cf-info — режимы вывода
|
||||||
|
|
||||||
|
### brief — одна строка
|
||||||
|
|
||||||
|
```
|
||||||
|
Конфигурация: БухгалтерияПредприятия — "Бухгалтерия предприятия" v3.0.181.31 | 2847 объектов | Version8_3_24
|
||||||
|
```
|
||||||
|
|
||||||
|
### overview (по умолчанию) — заголовок + ключевые свойства + счётчики
|
||||||
|
|
||||||
|
```
|
||||||
|
Конфигурация: ТестКонфигурация — "Тестовая конфигурация"
|
||||||
|
Version: 2.0.0.1
|
||||||
|
Vendor: TestCompany
|
||||||
|
Compatibility: Version8_3_24
|
||||||
|
DefaultLanguage: Language.Русский
|
||||||
|
|
||||||
|
Объекты (4 шт.):
|
||||||
|
Language 1
|
||||||
|
Role 1
|
||||||
|
Catalog 1
|
||||||
|
Document 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### full — все свойства + полный список объектов
|
||||||
|
|
||||||
|
Выводит все свойства по категориям (скалярные, enum, ref), полный список ChildObjects поимённо, DefaultRoles и мобильные функциональности.
|
||||||
|
|
||||||
|
## cf-edit — операции
|
||||||
|
|
||||||
|
### Свойства
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Скалярные и enum
|
||||||
|
-Operation modify-property -Value "Version=2.0.0.1 ;; Vendor=Фирма 1С ;; CompatibilityMode=Version8_3_27"
|
||||||
|
|
||||||
|
# Многоязычные (LocalString)
|
||||||
|
-Operation modify-property -Value "Synonym=Моя конфигурация ;; Copyright=ООО Фирма"
|
||||||
|
|
||||||
|
# Ссылка
|
||||||
|
-Operation modify-property -Value "DefaultLanguage=Language.Русский"
|
||||||
|
```
|
||||||
|
|
||||||
|
Поддерживаемые свойства:
|
||||||
|
|
||||||
|
| Категория | Свойства |
|
||||||
|
|-----------|----------|
|
||||||
|
| Скалярные | `Name`, `Version`, `Vendor`, `Comment`, `NamePrefix`, `UpdateCatalogAddress` |
|
||||||
|
| LocalString | `Synonym`, `BriefInformation`, `DetailedInformation`, `Copyright`, `VendorInformationAddress`, `ConfigurationInformationAddress` |
|
||||||
|
| Enum | `CompatibilityMode`, `ConfigurationExtensionCompatibilityMode`, `DefaultRunMode`, `ScriptVariant`, `DataLockControlMode`, `ObjectAutonumerationMode`, `ModalityUseMode`, `SynchronousPlatformExtensionAndAddInCallUseMode`, `InterfaceCompatibilityMode`, `DatabaseTablespacesUseMode`, `MainClientApplicationWindowMode` |
|
||||||
|
| Ref | `DefaultLanguage` |
|
||||||
|
|
||||||
|
### Состав объектов (ChildObjects)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Добавить (вставляется в каноническую позицию — по типу, затем по алфавиту)
|
||||||
|
-Operation add-childObject -Value "Catalog.Товары ;; Document.Заказ ;; Enum.ВидыОплат"
|
||||||
|
|
||||||
|
# Удалить
|
||||||
|
-Operation remove-childObject -Value "Catalog.Устаревший"
|
||||||
|
```
|
||||||
|
|
||||||
|
44 типа объектов поддерживаются в каноническом порядке: Language, Subsystem, StyleItem, CommonPicture, ... IntegrationService.
|
||||||
|
|
||||||
|
### Роли по умолчанию (DefaultRoles)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Добавить
|
||||||
|
-Operation add-defaultRole -Value "ПолныеПрава"
|
||||||
|
|
||||||
|
# Удалить
|
||||||
|
-Operation remove-defaultRole -Value "ПолныеПрава"
|
||||||
|
|
||||||
|
# Заменить список целиком
|
||||||
|
-Operation set-defaultRoles -Value "ПолныеПрава ;; Администратор"
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON mode — комбинированные операции
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "operation": "modify-property", "value": "Version=2.0.0.1 ;; Vendor=Test" },
|
||||||
|
{ "operation": "add-childObject", "value": "Catalog.Товары ;; Document.Заказ" },
|
||||||
|
{ "operation": "add-defaultRole", "value": "ПолныеПрава" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## cf-validate — проверки
|
||||||
|
|
||||||
|
| # | Проверка | Уровень |
|
||||||
|
|---|----------|---------|
|
||||||
|
| 1 | XML well-formedness, MetaDataObject/Configuration, version | ERROR |
|
||||||
|
| 2 | InternalInfo: 7 ContainedObject, валидные ClassId | ERROR |
|
||||||
|
| 3 | Properties: Name, Synonym, DefaultLanguage, DefaultRunMode | ERROR/WARN |
|
||||||
|
| 4 | Enum-значения (11 свойств) | ERROR |
|
||||||
|
| 5 | ChildObjects: валидные типы, нет дубликатов, порядок | ERROR/WARN |
|
||||||
|
| 6 | DefaultLanguage ссылается на существующий Language | ERROR |
|
||||||
|
| 7 | Файлы языков `Languages/<name>.xml` существуют | WARN |
|
||||||
|
| 8 | Каталоги объектов из ChildObjects существуют | WARN |
|
||||||
|
|
||||||
|
## Сценарии использования
|
||||||
|
|
||||||
|
### Обзор существующей конфигурации
|
||||||
|
|
||||||
|
```
|
||||||
|
> Покажи структуру конфигурации C:\WS\cfsrc\acc_8.3.24
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/cf-info` и покажет: имя, синоним, версию, поставщика, количество объектов по типам.
|
||||||
|
|
||||||
|
### Создание новой конфигурации
|
||||||
|
|
||||||
|
```
|
||||||
|
> Создай пустую конфигурацию МойПроект, версия 1.0.0.1, поставщик "ООО Ромашка"
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/cf-init` → `/cf-edit` (Version, Vendor) → `/cf-validate` → `/cf-info`.
|
||||||
|
|
||||||
|
### Добавление объектов в конфигурацию
|
||||||
|
|
||||||
|
```
|
||||||
|
> Добавь в конфигурацию src/ справочник Контрагенты, документ ЗаказКлиента и перечисление ВидыОплат
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/cf-edit` с `add-childObject`, объекты встанут в каноническом порядке.
|
||||||
|
|
||||||
|
### Проверка конфигурации после изменений
|
||||||
|
|
||||||
|
```
|
||||||
|
> Проверь корректность конфигурации src/
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude вызовет `/cf-validate` и покажет ошибки и предупреждения.
|
||||||
|
|
||||||
|
## Структура корневых файлов
|
||||||
|
|
||||||
|
```
|
||||||
|
<OutputDir>/
|
||||||
|
├── Configuration.xml # Свойства и состав конфигурации
|
||||||
|
├── ConfigDumpInfo.xml # Служебный (версии объектов)
|
||||||
|
├── Ext/ # Модули конфигурации
|
||||||
|
│ ├── ManagedApplicationModule.bsl
|
||||||
|
│ ├── SessionModule.bsl
|
||||||
|
│ └── ...
|
||||||
|
└── Languages/
|
||||||
|
└── Русский.xml # Язык конфигурации
|
||||||
|
```
|
||||||
|
|
||||||
|
## Связь с другими навыками
|
||||||
|
|
||||||
|
- `/meta-compile` — при создании объекта автоматически регистрирует его в `Configuration.xml` (вызывает логику `add-childObject`)
|
||||||
|
- `/subsystem-edit` — при добавлении объекта в подсистему объект уже должен быть в ChildObjects
|
||||||
|
- `/cf-edit` + `/meta-compile` — типичная связка: сначала добавить объект в конфигурацию, затем создать его исходники
|
||||||
|
|
||||||
|
## Спецификации
|
||||||
|
|
||||||
|
- [1c-configuration-spec.md](1c-configuration-spec.md) — XML-формат Configuration.xml, ConfigDumpInfo.xml, Languages/, свойства, 44 типа ChildObjects
|
||||||
Reference in New Issue
Block a user