mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-12 08:54:57 +03:00
Merge branch 'dev'
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: cf-edit
|
||||
description: Точечное редактирование конфигурации 1С. Используй когда нужно изменить свойства конфигурации, добавить или удалить объект из состава, настроить роли по умолчанию
|
||||
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,58 @@
|
||||
---
|
||||
name: cf-init
|
||||
description: Создать пустую конфигурацию 1С (scaffold XML-исходников). Используй когда нужно начать новую конфигурацию с нуля
|
||||
argument-hint: <Name> [-Synonym <name>] [-OutputDir src]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cf-init — Создание пустой конфигурации 1С
|
||||
|
||||
Создаёт scaffold исходников пустой конфигурации 1С: `Configuration.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 # Корневой файл — все свойства
|
||||
└── 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,215 @@
|
||||
# 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>
|
||||
"@
|
||||
|
||||
# --- 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)
|
||||
$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 " Languages: $langFile"
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: cf-validate
|
||||
description: Валидация конфигурации 1С. Используй после создания или модификации конфигурации для проверки корректности
|
||||
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
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: cfe-borrow
|
||||
description: Заимствование объектов из конфигурации 1С в расширение (CFE). Используй когда нужно перехватить метод, изменить форму или добавить реквизит к существующему объекту конфигурации
|
||||
argument-hint: -ExtensionPath <path> -ConfigPath <path> -Object "Catalog.Контрагенты"
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cfe-borrow — Заимствование объектов из конфигурации
|
||||
|
||||
Заимствует объекты из основной конфигурации в расширение. Создаёт XML-файлы с `ObjectBelonging=Adopted` и `ExtendedConfigurationObject`, добавляет запись в ChildObjects расширения.
|
||||
|
||||
## Предусловие
|
||||
|
||||
Расширение должно быть создано (`/cfe-init`) и содержать валидный `Configuration.xml`.
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `ExtensionPath` | Путь к каталогу расширения (обязат.) |
|
||||
| `ConfigPath` | Путь к конфигурации-источнику (обязат.) |
|
||||
| `Object` | Что заимствовать (обязат.), batch через `;;` |
|
||||
|
||||
## Формат -Object
|
||||
|
||||
- `Catalog.Контрагенты` — справочник
|
||||
- `CommonModule.РаботаСФайлами` — общий модуль
|
||||
- `Document.РеализацияТоваров` — документ
|
||||
- `Enum.ВидыОплат` — перечисление
|
||||
- `Catalog.X ;; CommonModule.Y ;; Enum.Z` — несколько объектов
|
||||
|
||||
Поддерживаются все 44 типа объектов конфигурации.
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\cfe-borrow\scripts\cfe-borrow.ps1 -ExtensionPath src -ConfigPath C:\cfsrc\erp -Object "Catalog.Контрагенты"
|
||||
```
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Заимствовать один объект
|
||||
... -ExtensionPath src -ConfigPath C:\cfsrc\erp -Object "Catalog.Контрагенты"
|
||||
|
||||
# Несколько объектов за раз
|
||||
... -ExtensionPath src -ConfigPath C:\cfsrc\erp -Object "Catalog.Контрагенты ;; CommonModule.ОбщийМодуль ;; Enum.ВидыОплат"
|
||||
```
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/cfe-validate <ExtensionPath>
|
||||
```
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
# cfe-borrow v1.0 — Borrow objects from configuration into extension (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ExtensionPath,
|
||||
[Parameter(Mandatory)][string]$ConfigPath,
|
||||
[Parameter(Mandatory)][string]$Object
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
function Info([string]$msg) { Write-Host "[INFO] $msg" }
|
||||
function Warn([string]$msg) { Write-Host "[WARN] $msg" }
|
||||
|
||||
# --- 1. Resolve paths ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($ExtensionPath)) {
|
||||
$ExtensionPath = Join-Path (Get-Location).Path $ExtensionPath
|
||||
}
|
||||
if (Test-Path $ExtensionPath -PathType Container) {
|
||||
$candidate = Join-Path $ExtensionPath "Configuration.xml"
|
||||
if (Test-Path $candidate) { $ExtensionPath = $candidate }
|
||||
else { Write-Error "No Configuration.xml in extension directory: $ExtensionPath"; exit 1 }
|
||||
}
|
||||
if (-not (Test-Path $ExtensionPath)) { Write-Error "Extension file not found: $ExtensionPath"; exit 1 }
|
||||
$extResolvedPath = (Resolve-Path $ExtensionPath).Path
|
||||
$extDir = Split-Path $extResolvedPath -Parent
|
||||
|
||||
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 config directory: $ConfigPath"; exit 1 }
|
||||
}
|
||||
if (-not (Test-Path $ConfigPath)) { Write-Error "Config file not found: $ConfigPath"; exit 1 }
|
||||
$cfgResolvedPath = (Resolve-Path $ConfigPath).Path
|
||||
$cfgDir = Split-Path $cfgResolvedPath -Parent
|
||||
|
||||
# --- 2. Load extension Configuration.xml ---
|
||||
$script:xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$script:xmlDoc.PreserveWhitespace = $true
|
||||
$script:xmlDoc.Load($extResolvedPath)
|
||||
|
||||
$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"
|
||||
|
||||
$root = $script:xmlDoc.DocumentElement
|
||||
|
||||
$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 in extension"; 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 }
|
||||
}
|
||||
|
||||
if (-not $script:propsEl) { Write-Error "No <Properties> element found in extension"; exit 1 }
|
||||
if (-not $script:childObjsEl) { Write-Error "No <ChildObjects> element found in extension"; exit 1 }
|
||||
|
||||
# --- 3. Extract NamePrefix ---
|
||||
$script:namePrefix = ""
|
||||
foreach ($child in $script:propsEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "NamePrefix") {
|
||||
$script:namePrefix = $child.InnerText.Trim(); break
|
||||
}
|
||||
}
|
||||
Info "Extension NamePrefix: $($script:namePrefix)"
|
||||
|
||||
# --- 4. Type mappings ---
|
||||
$childTypeDirMap = @{
|
||||
"Catalog"="Catalogs"; "Document"="Documents"; "Enum"="Enums"
|
||||
"CommonModule"="CommonModules"; "CommonPicture"="CommonPictures"
|
||||
"CommonCommand"="CommonCommands"; "CommonTemplate"="CommonTemplates"
|
||||
"ExchangePlan"="ExchangePlans"; "Report"="Reports"; "DataProcessor"="DataProcessors"
|
||||
"InformationRegister"="InformationRegisters"; "AccumulationRegister"="AccumulationRegisters"
|
||||
"ChartOfCharacteristicTypes"="ChartsOfCharacteristicTypes"
|
||||
"ChartOfAccounts"="ChartsOfAccounts"; "AccountingRegister"="AccountingRegisters"
|
||||
"ChartOfCalculationTypes"="ChartsOfCalculationTypes"; "CalculationRegister"="CalculationRegisters"
|
||||
"BusinessProcess"="BusinessProcesses"; "Task"="Tasks"
|
||||
"Subsystem"="Subsystems"; "Role"="Roles"; "Constant"="Constants"
|
||||
"FunctionalOption"="FunctionalOptions"; "DefinedType"="DefinedTypes"
|
||||
"FunctionalOptionsParameter"="FunctionalOptionsParameters"
|
||||
"CommonForm"="CommonForms"; "DocumentJournal"="DocumentJournals"
|
||||
"SessionParameter"="SessionParameters"; "StyleItem"="StyleItems"
|
||||
"EventSubscription"="EventSubscriptions"; "ScheduledJob"="ScheduledJobs"
|
||||
"SettingsStorage"="SettingsStorages"; "FilterCriterion"="FilterCriteria"
|
||||
"CommandGroup"="CommandGroups"; "DocumentNumerator"="DocumentNumerators"
|
||||
"Sequence"="Sequences"; "IntegrationService"="IntegrationServices"
|
||||
"XDTOPackage"="XDTOPackages"; "WebService"="WebServices"
|
||||
"HTTPService"="HTTPServices"; "WSReference"="WSReferences"
|
||||
"CommonAttribute"="CommonAttributes"; "Style"="Styles"
|
||||
}
|
||||
|
||||
# --- 5. Canonical type order (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"
|
||||
)
|
||||
|
||||
# --- 6. GeneratedType patterns per type ---
|
||||
$script:generatedTypes = @{
|
||||
"Catalog" = @(
|
||||
@{ prefix = "CatalogObject"; category = "Object" }
|
||||
@{ prefix = "CatalogRef"; category = "Ref" }
|
||||
@{ prefix = "CatalogSelection"; category = "Selection" }
|
||||
@{ prefix = "CatalogList"; category = "List" }
|
||||
@{ prefix = "CatalogManager"; category = "Manager" }
|
||||
)
|
||||
"Document" = @(
|
||||
@{ prefix = "DocumentObject"; category = "Object" }
|
||||
@{ prefix = "DocumentRef"; category = "Ref" }
|
||||
@{ prefix = "DocumentSelection"; category = "Selection" }
|
||||
@{ prefix = "DocumentList"; category = "List" }
|
||||
@{ prefix = "DocumentManager"; category = "Manager" }
|
||||
)
|
||||
"Enum" = @(
|
||||
@{ prefix = "EnumRef"; category = "Ref" }
|
||||
@{ prefix = "EnumManager"; category = "Manager" }
|
||||
@{ prefix = "EnumList"; category = "List" }
|
||||
)
|
||||
"Constant" = @(
|
||||
@{ prefix = "ConstantManager"; category = "Manager" }
|
||||
@{ prefix = "ConstantValueManager"; category = "ValueManager" }
|
||||
@{ prefix = "ConstantValueKey"; category = "ValueKey" }
|
||||
)
|
||||
"InformationRegister" = @(
|
||||
@{ prefix = "InformationRegisterRecord"; category = "Record" }
|
||||
@{ prefix = "InformationRegisterManager"; category = "Manager" }
|
||||
@{ prefix = "InformationRegisterSelection"; category = "Selection" }
|
||||
@{ prefix = "InformationRegisterList"; category = "List" }
|
||||
@{ prefix = "InformationRegisterRecordSet"; category = "RecordSet" }
|
||||
@{ prefix = "InformationRegisterRecordKey"; category = "RecordKey" }
|
||||
@{ prefix = "InformationRegisterRecordManager"; category = "RecordManager" }
|
||||
)
|
||||
"AccumulationRegister" = @(
|
||||
@{ prefix = "AccumulationRegisterRecord"; category = "Record" }
|
||||
@{ prefix = "AccumulationRegisterManager"; category = "Manager" }
|
||||
@{ prefix = "AccumulationRegisterSelection"; category = "Selection" }
|
||||
@{ prefix = "AccumulationRegisterList"; category = "List" }
|
||||
@{ prefix = "AccumulationRegisterRecordSet"; category = "RecordSet" }
|
||||
@{ prefix = "AccumulationRegisterRecordKey"; category = "RecordKey" }
|
||||
)
|
||||
"AccountingRegister" = @(
|
||||
@{ prefix = "AccountingRegisterRecord"; category = "Record" }
|
||||
@{ prefix = "AccountingRegisterManager"; category = "Manager" }
|
||||
@{ prefix = "AccountingRegisterSelection"; category = "Selection" }
|
||||
@{ prefix = "AccountingRegisterList"; category = "List" }
|
||||
@{ prefix = "AccountingRegisterRecordSet"; category = "RecordSet" }
|
||||
@{ prefix = "AccountingRegisterRecordKey"; category = "RecordKey" }
|
||||
)
|
||||
"CalculationRegister" = @(
|
||||
@{ prefix = "CalculationRegisterRecord"; category = "Record" }
|
||||
@{ prefix = "CalculationRegisterManager"; category = "Manager" }
|
||||
@{ prefix = "CalculationRegisterSelection"; category = "Selection" }
|
||||
@{ prefix = "CalculationRegisterList"; category = "List" }
|
||||
@{ prefix = "CalculationRegisterRecordSet"; category = "RecordSet" }
|
||||
@{ prefix = "CalculationRegisterRecordKey"; category = "RecordKey" }
|
||||
)
|
||||
"ChartOfAccounts" = @(
|
||||
@{ prefix = "ChartOfAccountsObject"; category = "Object" }
|
||||
@{ prefix = "ChartOfAccountsRef"; category = "Ref" }
|
||||
@{ prefix = "ChartOfAccountsSelection"; category = "Selection" }
|
||||
@{ prefix = "ChartOfAccountsList"; category = "List" }
|
||||
@{ prefix = "ChartOfAccountsManager"; category = "Manager" }
|
||||
)
|
||||
"ChartOfCharacteristicTypes" = @(
|
||||
@{ prefix = "ChartOfCharacteristicTypesObject"; category = "Object" }
|
||||
@{ prefix = "ChartOfCharacteristicTypesRef"; category = "Ref" }
|
||||
@{ prefix = "ChartOfCharacteristicTypesSelection"; category = "Selection" }
|
||||
@{ prefix = "ChartOfCharacteristicTypesList"; category = "List" }
|
||||
@{ prefix = "ChartOfCharacteristicTypesManager"; category = "Manager" }
|
||||
)
|
||||
"ChartOfCalculationTypes" = @(
|
||||
@{ prefix = "ChartOfCalculationTypesObject"; category = "Object" }
|
||||
@{ prefix = "ChartOfCalculationTypesRef"; category = "Ref" }
|
||||
@{ prefix = "ChartOfCalculationTypesSelection"; category = "Selection" }
|
||||
@{ prefix = "ChartOfCalculationTypesList"; category = "List" }
|
||||
@{ prefix = "ChartOfCalculationTypesManager"; category = "Manager" }
|
||||
@{ prefix = "DisplacingCalculationTypes"; category = "DisplacingCalculationTypes" }
|
||||
@{ prefix = "BaseCalculationTypes"; category = "BaseCalculationTypes" }
|
||||
@{ prefix = "LeadingCalculationTypes"; category = "LeadingCalculationTypes" }
|
||||
)
|
||||
"BusinessProcess" = @(
|
||||
@{ prefix = "BusinessProcessObject"; category = "Object" }
|
||||
@{ prefix = "BusinessProcessRef"; category = "Ref" }
|
||||
@{ prefix = "BusinessProcessSelection"; category = "Selection" }
|
||||
@{ prefix = "BusinessProcessList"; category = "List" }
|
||||
@{ prefix = "BusinessProcessManager"; category = "Manager" }
|
||||
)
|
||||
"Task" = @(
|
||||
@{ prefix = "TaskObject"; category = "Object" }
|
||||
@{ prefix = "TaskRef"; category = "Ref" }
|
||||
@{ prefix = "TaskSelection"; category = "Selection" }
|
||||
@{ prefix = "TaskList"; category = "List" }
|
||||
@{ prefix = "TaskManager"; category = "Manager" }
|
||||
)
|
||||
"ExchangePlan" = @(
|
||||
@{ prefix = "ExchangePlanObject"; category = "Object" }
|
||||
@{ prefix = "ExchangePlanRef"; category = "Ref" }
|
||||
@{ prefix = "ExchangePlanSelection"; category = "Selection" }
|
||||
@{ prefix = "ExchangePlanList"; category = "List" }
|
||||
@{ prefix = "ExchangePlanManager"; category = "Manager" }
|
||||
)
|
||||
"DocumentJournal" = @(
|
||||
@{ prefix = "DocumentJournalSelection"; category = "Selection" }
|
||||
@{ prefix = "DocumentJournalList"; category = "List" }
|
||||
@{ prefix = "DocumentJournalManager"; category = "Manager" }
|
||||
)
|
||||
"Report" = @(
|
||||
@{ prefix = "ReportObject"; category = "Object" }
|
||||
)
|
||||
"DataProcessor" = @(
|
||||
@{ prefix = "DataProcessorObject"; category = "Object" }
|
||||
)
|
||||
}
|
||||
|
||||
# Types that need ChildObjects element
|
||||
$typesWithChildObjects = @(
|
||||
"Catalog","Document","ExchangePlan","ChartOfAccounts",
|
||||
"ChartOfCharacteristicTypes","ChartOfCalculationTypes",
|
||||
"BusinessProcess","Task","Enum",
|
||||
"InformationRegister","AccumulationRegister","AccountingRegister","CalculationRegister"
|
||||
)
|
||||
|
||||
# CommonModule properties to copy from source
|
||||
$commonModuleProps = @("Global","ClientManagedApplication","Server","ExternalConnection","ClientOrdinaryApplication","ServerCall")
|
||||
|
||||
# --- 7. XML manipulation helpers (from cf-edit) ---
|
||||
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 Expand-SelfClosingElement($container, $parentIndent) {
|
||||
if (-not $container.HasChildNodes -or $container.IsEmpty) {
|
||||
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$parentIndent")
|
||||
$container.AppendChild($closeWs) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# --- 8. Namespaces declaration for object XML ---
|
||||
$script:xmlnsDecl = '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"'
|
||||
|
||||
# --- 9. Parse -Object into items ---
|
||||
$items = @()
|
||||
foreach ($part in $Object.Split(";;")) {
|
||||
$trimmed = $part.Trim()
|
||||
if ($trimmed) { $items += $trimmed }
|
||||
}
|
||||
|
||||
if ($items.Count -eq 0) {
|
||||
Write-Error "No objects specified in -Object"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- 10. Helper: read source object XML ---
|
||||
function Read-SourceObject {
|
||||
param([string]$typeName, [string]$objName)
|
||||
|
||||
$dirName = $childTypeDirMap[$typeName]
|
||||
if (-not $dirName) {
|
||||
Write-Error "Unknown type '$typeName'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$srcFile = Join-Path (Join-Path $cfgDir $dirName) "${objName}.xml"
|
||||
if (-not (Test-Path $srcFile)) {
|
||||
Write-Error "Source object not found: $srcFile"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$srcDoc = New-Object System.Xml.XmlDocument
|
||||
$srcDoc.PreserveWhitespace = $false
|
||||
$srcDoc.Load($srcFile)
|
||||
|
||||
$srcNs = New-Object System.Xml.XmlNamespaceManager($srcDoc.NameTable)
|
||||
$srcNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$srcNs.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
|
||||
|
||||
# Find the type element (e.g. <Catalog uuid="...">)
|
||||
$srcRoot = $srcDoc.DocumentElement
|
||||
$srcEl = $null
|
||||
foreach ($c in $srcRoot.ChildNodes) {
|
||||
if ($c.NodeType -eq 'Element') { $srcEl = $c; break }
|
||||
}
|
||||
if (-not $srcEl) {
|
||||
Write-Error "No metadata element found in ${dirName}/${objName}.xml"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract uuid
|
||||
$srcUuid = $srcEl.GetAttribute("uuid")
|
||||
if (-not $srcUuid) {
|
||||
Write-Error "No uuid attribute on source element in ${dirName}/${objName}.xml"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Extract properties for CommonModule
|
||||
$srcProps = @{}
|
||||
$propsNode = $srcEl.SelectSingleNode("md:Properties", $srcNs)
|
||||
if ($propsNode) {
|
||||
foreach ($propName in $commonModuleProps) {
|
||||
$propNode = $propsNode.SelectSingleNode("md:${propName}", $srcNs)
|
||||
if ($propNode) {
|
||||
$srcProps[$propName] = $propNode.InnerText.Trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @{
|
||||
Uuid = $srcUuid
|
||||
Properties = $srcProps
|
||||
Element = $srcEl
|
||||
NsManager = $srcNs
|
||||
}
|
||||
}
|
||||
|
||||
# --- 11. Helper: generate InternalInfo XML ---
|
||||
function Build-InternalInfoXml {
|
||||
param([string]$typeName, [string]$objName, [string]$indent)
|
||||
|
||||
$types = $script:generatedTypes[$typeName]
|
||||
if (-not $types -or $types.Count -eq 0) {
|
||||
return "${indent}<InternalInfo/>"
|
||||
}
|
||||
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
$sb.AppendLine("${indent}<InternalInfo>") | Out-Null
|
||||
|
||||
# ExchangePlan: ThisNode UUID before GeneratedTypes
|
||||
if ($typeName -eq "ExchangePlan") {
|
||||
$thisNodeUuid = [guid]::NewGuid().ToString()
|
||||
$sb.AppendLine("${indent}`t<xr:ThisNode>${thisNodeUuid}</xr:ThisNode>") | Out-Null
|
||||
}
|
||||
|
||||
foreach ($gt in $types) {
|
||||
$fullName = "$($gt.prefix).${objName}"
|
||||
$typeId = [guid]::NewGuid().ToString()
|
||||
$valueId = [guid]::NewGuid().ToString()
|
||||
$sb.AppendLine("${indent}`t<xr:GeneratedType name=`"${fullName}`" category=`"$($gt.category)`">") | Out-Null
|
||||
$sb.AppendLine("${indent}`t`t<xr:TypeId>${typeId}</xr:TypeId>") | Out-Null
|
||||
$sb.AppendLine("${indent}`t`t<xr:ValueId>${valueId}</xr:ValueId>") | Out-Null
|
||||
$sb.AppendLine("${indent}`t</xr:GeneratedType>") | Out-Null
|
||||
}
|
||||
|
||||
$sb.Append("${indent}</InternalInfo>") | Out-Null
|
||||
return $sb.ToString()
|
||||
}
|
||||
|
||||
# --- 12. Helper: build borrowed object XML ---
|
||||
function Build-BorrowedObjectXml {
|
||||
param(
|
||||
[string]$typeName,
|
||||
[string]$objName,
|
||||
[string]$sourceUuid,
|
||||
[hashtable]$sourceProps
|
||||
)
|
||||
|
||||
$newUuid = [guid]::NewGuid().ToString()
|
||||
$internalInfoXml = Build-InternalInfoXml $typeName $objName "`t`t"
|
||||
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
$sb.AppendLine("<?xml version=`"1.0`" encoding=`"UTF-8`"?>") | Out-Null
|
||||
$sb.AppendLine("<MetaDataObject $($script:xmlnsDecl) version=`"2.17`">") | Out-Null
|
||||
$sb.AppendLine("`t<${typeName} uuid=`"${newUuid}`">") | Out-Null
|
||||
|
||||
# InternalInfo
|
||||
$sb.AppendLine($internalInfoXml) | Out-Null
|
||||
|
||||
# Properties
|
||||
$sb.AppendLine("`t`t<Properties>") | Out-Null
|
||||
$sb.AppendLine("`t`t`t<ObjectBelonging>Adopted</ObjectBelonging>") | Out-Null
|
||||
$sb.AppendLine("`t`t`t<Name>${objName}</Name>") | Out-Null
|
||||
$sb.AppendLine("`t`t`t<Comment/>") | Out-Null
|
||||
$sb.AppendLine("`t`t`t<ExtendedConfigurationObject>${sourceUuid}</ExtendedConfigurationObject>") | Out-Null
|
||||
|
||||
# CommonModule: extra properties from source
|
||||
if ($typeName -eq "CommonModule") {
|
||||
foreach ($propName in $commonModuleProps) {
|
||||
$propVal = "false"
|
||||
if ($sourceProps.ContainsKey($propName)) {
|
||||
$propVal = $sourceProps[$propName]
|
||||
}
|
||||
$sb.AppendLine("`t`t`t<${propName}>${propVal}</${propName}>") | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
$sb.AppendLine("`t`t</Properties>") | Out-Null
|
||||
|
||||
# ChildObjects (for types that need it)
|
||||
if ($typesWithChildObjects -contains $typeName) {
|
||||
$sb.AppendLine("`t`t<ChildObjects/>") | Out-Null
|
||||
}
|
||||
|
||||
$sb.AppendLine("`t</${typeName}>") | Out-Null
|
||||
$sb.Append("</MetaDataObject>") | Out-Null
|
||||
|
||||
return $sb.ToString()
|
||||
}
|
||||
|
||||
# --- 13. Helper: add object to extension ChildObjects ---
|
||||
function Add-ToChildObjects {
|
||||
param([string]$typeName, [string]$objName)
|
||||
|
||||
$cfgIndent = Get-ChildIndent $script:cfgEl
|
||||
|
||||
# Expand self-closing ChildObjects if needed
|
||||
if (-not $script:childObjsEl.HasChildNodes -or $script:childObjsEl.IsEmpty) {
|
||||
Expand-SelfClosingElement $script:childObjsEl $cfgIndent
|
||||
}
|
||||
$childIndent = Get-ChildIndent $script:childObjsEl
|
||||
|
||||
$typeIdx = $script:typeOrder.IndexOf($typeName)
|
||||
if ($typeIdx -lt 0) {
|
||||
Write-Error "Unknown type '$typeName' for ChildObjects ordering"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Dedup check
|
||||
foreach ($child in $script:childObjsEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $typeName -and $child.InnerText -eq $objName) {
|
||||
Warn "Already in ChildObjects: ${typeName}.${objName}"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Find insertion point: after last element of same type, or before first element of later type
|
||||
$insertBefore = $null
|
||||
$lastSameType = $null
|
||||
|
||||
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 $objName -and -not $insertBefore) {
|
||||
$insertBefore = $child
|
||||
}
|
||||
$lastSameType = $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 = $objName
|
||||
|
||||
if ($insertBefore) {
|
||||
Insert-BeforeElement $script:childObjsEl $newEl $insertBefore $childIndent
|
||||
} else {
|
||||
Insert-BeforeElement $script:childObjsEl $newEl $null $childIndent
|
||||
}
|
||||
|
||||
Info "Added to ChildObjects: ${typeName}.${objName}"
|
||||
}
|
||||
|
||||
# --- 14. Process each item ---
|
||||
$borrowedFiles = @()
|
||||
$borrowedCount = 0
|
||||
|
||||
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)
|
||||
$objName = $item.Substring($dotIdx + 1)
|
||||
|
||||
if (-not $childTypeDirMap.ContainsKey($typeName)) {
|
||||
Write-Error "Unknown type '${typeName}'"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$dirName = $childTypeDirMap[$typeName]
|
||||
|
||||
Info "Borrowing ${typeName}.${objName}..."
|
||||
|
||||
# Read source object
|
||||
$src = Read-SourceObject $typeName $objName
|
||||
Info " Source UUID: $($src.Uuid)"
|
||||
|
||||
# Build borrowed object XML
|
||||
$borrowedXml = Build-BorrowedObjectXml $typeName $objName $src.Uuid $src.Properties
|
||||
|
||||
# Create directory in extension if needed
|
||||
$targetDir = Join-Path $extDir $dirName
|
||||
if (-not (Test-Path $targetDir)) {
|
||||
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
|
||||
}
|
||||
|
||||
# Write borrowed object XML with UTF-8 BOM
|
||||
$targetFile = Join-Path $targetDir "${objName}.xml"
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($targetFile, $borrowedXml, $enc)
|
||||
Info " Created: $targetFile"
|
||||
|
||||
# Add to ChildObjects
|
||||
Add-ToChildObjects $typeName $objName
|
||||
|
||||
$borrowedFiles += $targetFile
|
||||
$borrowedCount++
|
||||
}
|
||||
|
||||
# --- 15. Save modified Configuration.xml ---
|
||||
$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($extResolvedPath, $text, $utf8Bom)
|
||||
Info "Saved: $extResolvedPath"
|
||||
|
||||
# --- 16. Summary ---
|
||||
Write-Host ""
|
||||
Write-Host "=== cfe-borrow summary ==="
|
||||
Write-Host " Extension: $extDir"
|
||||
Write-Host " Config: $cfgDir"
|
||||
Write-Host " Borrowed: $borrowedCount object(s)"
|
||||
foreach ($f in $borrowedFiles) {
|
||||
Write-Host " - $f"
|
||||
}
|
||||
exit 0
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: cfe-diff
|
||||
description: Анализ расширения конфигурации 1С (CFE) — обзор изменений и проверка переноса. Используй для понимания что изменено в расширении или для проверки перенесены ли изменения из расширения в конфигурацию
|
||||
argument-hint: -ExtensionPath <path> -ConfigPath <path> [-Mode A|B]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cfe-diff — Анализ расширения конфигурации
|
||||
|
||||
Анализирует расширение в двух режимах: обзор изменений (Mode A) или проверка переноса (Mode B).
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание | По умолчанию |
|
||||
|----------|----------|--------------|
|
||||
| `ExtensionPath` | Путь к расширению (обязат.) | — |
|
||||
| `ConfigPath` | Путь к конфигурации (обязат.) | — |
|
||||
| `Mode` | `A` (обзор) / `B` (проверка переноса) | `A` |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\cfe-diff\scripts\cfe-diff.ps1 -ExtensionPath src -ConfigPath C:\cfsrc\erp -Mode A
|
||||
```
|
||||
|
||||
## Mode A — обзор расширения
|
||||
|
||||
Для каждого объекта показывает:
|
||||
- `[BORROWED]` — заимствованный: перехватчики (`&Перед`, `&После`, `&ИзменениеИКонтроль`, `&Вместо`), собственные реквизиты/ТЧ/формы
|
||||
- `[OWN]` — собственный: количество реквизитов, ТЧ, форм
|
||||
|
||||
Пример вывода:
|
||||
```
|
||||
[BORROWED] Catalog.Валюты
|
||||
&ИзменениеИКонтроль("РеквизитыРедактируемыеВГрупповойОбработке") — line 4 in ...
|
||||
&Перед("ЗагрузитьКурсыВалют") — line 13 in ...
|
||||
ChildObjects: 1 own attrs, 1 own TS, 3 own forms
|
||||
[OWN] Catalog.Расш5_Справочник1
|
||||
```
|
||||
|
||||
## Mode B — проверка переноса
|
||||
|
||||
Для каждого `&ИзменениеИКонтроль` извлекает блоки `#Вставка`/`#КонецВставки` из расширения и ищет их в соответствующем модуле конфигурации.
|
||||
|
||||
Статусы:
|
||||
- `[TRANSFERRED]` — код найден в конфигурации
|
||||
- `[NOT_TRANSFERRED]` — код не найден
|
||||
- `[NEEDS_REVIEW]` — нет блоков `#Вставка` или модуль конфигурации не найден
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Обзор — что изменено в расширении
|
||||
... -ExtensionPath src -ConfigPath C:\cfsrc\erp -Mode A
|
||||
|
||||
# Проверка переноса — все ли #Вставка перенесены
|
||||
... -ExtensionPath src -ConfigPath C:\cfsrc\erp -Mode B
|
||||
```
|
||||
@@ -0,0 +1,392 @@
|
||||
# cfe-diff v1.0 — Analyze and compare 1C configuration extension (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ExtensionPath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ConfigPath,
|
||||
|
||||
[ValidateSet("A","B")]
|
||||
[string]$Mode = "A"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve paths ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($ExtensionPath)) {
|
||||
$ExtensionPath = Join-Path (Get-Location).Path $ExtensionPath
|
||||
}
|
||||
if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) {
|
||||
$ConfigPath = Join-Path (Get-Location).Path $ConfigPath
|
||||
}
|
||||
if (Test-Path $ExtensionPath -PathType Leaf) { $ExtensionPath = Split-Path $ExtensionPath -Parent }
|
||||
if (Test-Path $ConfigPath -PathType Leaf) { $ConfigPath = Split-Path $ConfigPath -Parent }
|
||||
|
||||
$extCfg = Join-Path $ExtensionPath "Configuration.xml"
|
||||
$srcCfg = Join-Path $ConfigPath "Configuration.xml"
|
||||
if (-not (Test-Path $extCfg)) { Write-Error "Extension Configuration.xml not found: $extCfg"; exit 1 }
|
||||
if (-not (Test-Path $srcCfg)) { Write-Error "Config Configuration.xml not found: $srcCfg"; exit 1 }
|
||||
|
||||
# --- Type -> directory mapping ---
|
||||
$childTypeDirMap = @{
|
||||
"Catalog"="Catalogs"; "Document"="Documents"; "Enum"="Enums"
|
||||
"CommonModule"="CommonModules"; "CommonPicture"="CommonPictures"
|
||||
"CommonCommand"="CommonCommands"; "CommonTemplate"="CommonTemplates"
|
||||
"ExchangePlan"="ExchangePlans"; "Report"="Reports"; "DataProcessor"="DataProcessors"
|
||||
"InformationRegister"="InformationRegisters"; "AccumulationRegister"="AccumulationRegisters"
|
||||
"ChartOfCharacteristicTypes"="ChartsOfCharacteristicTypes"
|
||||
"ChartOfAccounts"="ChartsOfAccounts"; "AccountingRegister"="AccountingRegisters"
|
||||
"ChartOfCalculationTypes"="ChartsOfCalculationTypes"; "CalculationRegister"="CalculationRegisters"
|
||||
"BusinessProcess"="BusinessProcesses"; "Task"="Tasks"
|
||||
"Subsystem"="Subsystems"; "Role"="Roles"; "Constant"="Constants"
|
||||
"FunctionalOption"="FunctionalOptions"; "DefinedType"="DefinedTypes"
|
||||
"FunctionalOptionsParameter"="FunctionalOptionsParameters"
|
||||
"CommonForm"="CommonForms"; "DocumentJournal"="DocumentJournals"
|
||||
"SessionParameter"="SessionParameters"; "StyleItem"="StyleItems"
|
||||
"EventSubscription"="EventSubscriptions"; "ScheduledJob"="ScheduledJobs"
|
||||
"SettingsStorage"="SettingsStorages"; "FilterCriterion"="FilterCriteria"
|
||||
"CommandGroup"="CommandGroups"; "DocumentNumerator"="DocumentNumerators"
|
||||
"Sequence"="Sequences"; "IntegrationService"="IntegrationServices"
|
||||
"CommonAttribute"="CommonAttributes"
|
||||
}
|
||||
|
||||
# --- Parse extension Configuration.xml ---
|
||||
$extDoc = New-Object System.Xml.XmlDocument
|
||||
$extDoc.PreserveWhitespace = $false
|
||||
$extDoc.Load($extCfg)
|
||||
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($extDoc.NameTable)
|
||||
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$ns.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
|
||||
|
||||
$extProps = $extDoc.SelectSingleNode("//md:Configuration/md:Properties", $ns)
|
||||
$extNameNode = $extProps.SelectSingleNode("md:Name", $ns)
|
||||
$extName = if ($extNameNode) { $extNameNode.InnerText } else { "?" }
|
||||
$prefixNode = $extProps.SelectSingleNode("md:NamePrefix", $ns)
|
||||
$namePrefix = if ($prefixNode -and $prefixNode.InnerText) { $prefixNode.InnerText } else { "" }
|
||||
$purposeNode = $extProps.SelectSingleNode("md:ConfigurationExtensionPurpose", $ns)
|
||||
$purpose = if ($purposeNode) { $purposeNode.InnerText } else { "?" }
|
||||
|
||||
Write-Host "=== cfe-diff Mode ${Mode}: $extName (${purpose}) ==="
|
||||
Write-Host " NamePrefix: $namePrefix"
|
||||
Write-Host ""
|
||||
|
||||
# --- Collect ChildObjects ---
|
||||
$childObjNode = $extDoc.SelectSingleNode("//md:Configuration/md:ChildObjects", $ns)
|
||||
if (-not $childObjNode) {
|
||||
Write-Host "[WARN] No ChildObjects in extension"
|
||||
exit 0
|
||||
}
|
||||
|
||||
$objects = @()
|
||||
foreach ($child in $childObjNode.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
if ($child.LocalName -eq "Language") { continue }
|
||||
$objects += @{ Type = $child.LocalName; Name = $child.InnerText }
|
||||
}
|
||||
|
||||
if ($objects.Count -eq 0) {
|
||||
Write-Host "No objects (besides Language) in extension."
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- Helper: check if object is borrowed ---
|
||||
function Get-ObjectInfo {
|
||||
param([string]$objType, [string]$objName)
|
||||
|
||||
if (-not $childTypeDirMap.ContainsKey($objType)) { return $null }
|
||||
$dirName = $childTypeDirMap[$objType]
|
||||
$objFile = Join-Path (Join-Path $ExtensionPath $dirName) "${objName}.xml"
|
||||
|
||||
if (-not (Test-Path $objFile)) { return @{ Borrowed = $false; File = $objFile; Exists = $false } }
|
||||
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $false
|
||||
$doc.Load($objFile)
|
||||
|
||||
$objNs = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$objNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
|
||||
$objEl = $null
|
||||
foreach ($c in $doc.DocumentElement.ChildNodes) {
|
||||
if ($c.NodeType -eq 'Element') { $objEl = $c; break }
|
||||
}
|
||||
if (-not $objEl) { return @{ Borrowed = $false; File = $objFile; Exists = $true } }
|
||||
|
||||
$propsEl = $objEl.SelectSingleNode("md:Properties", $objNs)
|
||||
$obNode = if ($propsEl) { $propsEl.SelectSingleNode("md:ObjectBelonging", $objNs) } else { $null }
|
||||
|
||||
$info = @{
|
||||
Borrowed = ($obNode -and $obNode.InnerText -eq "Adopted")
|
||||
File = $objFile
|
||||
Exists = $true
|
||||
Type = $objType
|
||||
Name = $objName
|
||||
DirName = $dirName
|
||||
ObjElement = $objEl
|
||||
ObjNs = $objNs
|
||||
}
|
||||
return $info
|
||||
}
|
||||
|
||||
# --- Helper: find .bsl files for object ---
|
||||
function Get-BslFiles {
|
||||
param([string]$objType, [string]$objName)
|
||||
|
||||
if (-not $childTypeDirMap.ContainsKey($objType)) { return @() }
|
||||
$dirName = $childTypeDirMap[$objType]
|
||||
$objDir = Join-Path (Join-Path $ExtensionPath $dirName) $objName
|
||||
|
||||
if (-not (Test-Path $objDir -PathType Container)) { return @() }
|
||||
|
||||
$bslFiles = @()
|
||||
$extDir = Join-Path $objDir "Ext"
|
||||
if (Test-Path $extDir) {
|
||||
$items = Get-ChildItem -Path $extDir -Filter "*.bsl" -ErrorAction SilentlyContinue
|
||||
foreach ($item in $items) { $bslFiles += $item.FullName }
|
||||
}
|
||||
|
||||
# Forms
|
||||
$formsDir = Join-Path $objDir "Forms"
|
||||
if (Test-Path $formsDir) {
|
||||
$formModules = Get-ChildItem -Path $formsDir -Recurse -Filter "Module.bsl" -ErrorAction SilentlyContinue
|
||||
foreach ($fm in $formModules) { $bslFiles += $fm.FullName }
|
||||
}
|
||||
|
||||
return $bslFiles
|
||||
}
|
||||
|
||||
# --- Helper: parse interceptors from .bsl ---
|
||||
function Get-Interceptors {
|
||||
param([string]$bslPath)
|
||||
|
||||
if (-not (Test-Path $bslPath)) { return @() }
|
||||
$lines = [System.IO.File]::ReadAllLines($bslPath, [System.Text.Encoding]::UTF8)
|
||||
$interceptors = @()
|
||||
$i = 0
|
||||
while ($i -lt $lines.Count) {
|
||||
$line = $lines[$i].Trim()
|
||||
if ($line -match '^&(Перед|После|ИзменениеИКонтроль|Вместо)\("([^"]+)"\)') {
|
||||
$type = $Matches[1]
|
||||
$method = $Matches[2]
|
||||
$interceptors += @{ Type = $type; Method = $method; Line = $i + 1; File = $bslPath }
|
||||
}
|
||||
$i++
|
||||
}
|
||||
return $interceptors
|
||||
}
|
||||
|
||||
# --- Helper: extract #Вставка blocks from .bsl ---
|
||||
function Get-InsertionBlocks {
|
||||
param([string]$bslPath)
|
||||
|
||||
if (-not (Test-Path $bslPath)) { return @() }
|
||||
$lines = [System.IO.File]::ReadAllLines($bslPath, [System.Text.Encoding]::UTF8)
|
||||
$blocks = @()
|
||||
$inBlock = $false
|
||||
$blockLines = @()
|
||||
$startLine = 0
|
||||
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) {
|
||||
$line = $lines[$i].Trim()
|
||||
if ($line -eq "#Вставка") {
|
||||
$inBlock = $true
|
||||
$blockLines = @()
|
||||
$startLine = $i + 1
|
||||
} elseif ($line -eq "#КонецВставки" -and $inBlock) {
|
||||
$inBlock = $false
|
||||
$blocks += @{
|
||||
StartLine = $startLine
|
||||
EndLine = $i + 1
|
||||
Code = ($blockLines -join "`n").Trim()
|
||||
File = $bslPath
|
||||
}
|
||||
} elseif ($inBlock) {
|
||||
$blockLines += $lines[$i]
|
||||
}
|
||||
}
|
||||
return $blocks
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE A: Extension overview
|
||||
# ============================================================
|
||||
if ($Mode -eq "A") {
|
||||
$borrowedList = @()
|
||||
$ownList = @()
|
||||
|
||||
foreach ($obj in $objects) {
|
||||
$info = Get-ObjectInfo $obj.Type $obj.Name
|
||||
if (-not $info) {
|
||||
Write-Host " [?] $($obj.Type).$($obj.Name) — unknown type"
|
||||
continue
|
||||
}
|
||||
if (-not $info.Exists) {
|
||||
Write-Host " [?] $($obj.Type).$($obj.Name) — file not found"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($info.Borrowed) {
|
||||
$borrowedList += $obj
|
||||
|
||||
Write-Host " [BORROWED] $($obj.Type).$($obj.Name)"
|
||||
|
||||
# Find .bsl files and interceptors
|
||||
$bslFiles = Get-BslFiles $obj.Type $obj.Name
|
||||
foreach ($bsl in $bslFiles) {
|
||||
$relPath = $bsl.Replace($ExtensionPath, "").TrimStart("\", "/")
|
||||
$interceptors = Get-Interceptors $bsl
|
||||
if ($interceptors.Count -gt 0) {
|
||||
foreach ($ic in $interceptors) {
|
||||
Write-Host " &$($ic.Type)(`"$($ic.Method)`") — line $($ic.Line) in $relPath"
|
||||
}
|
||||
} else {
|
||||
Write-Host " $relPath (no interceptors)"
|
||||
}
|
||||
}
|
||||
|
||||
# Check for own attributes/forms in ChildObjects
|
||||
if ($info.ObjElement) {
|
||||
$childObj = $info.ObjElement.SelectSingleNode("md:ChildObjects", $info.ObjNs)
|
||||
if ($childObj) {
|
||||
$ownAttrs = 0
|
||||
$ownForms = 0
|
||||
$ownTS = 0
|
||||
$borrowedItems = 0
|
||||
foreach ($c in $childObj.ChildNodes) {
|
||||
if ($c.NodeType -ne 'Element') { continue }
|
||||
$cProps = $c.SelectSingleNode("md:Properties", $info.ObjNs)
|
||||
if ($cProps) {
|
||||
$cOb = $cProps.SelectSingleNode("md:ObjectBelonging", $info.ObjNs)
|
||||
if ($cOb -and $cOb.InnerText -eq "Adopted") {
|
||||
$borrowedItems++
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch ($c.LocalName) {
|
||||
"Attribute" { $ownAttrs++ }
|
||||
"TabularSection" { $ownTS++ }
|
||||
"Form" { $ownForms++ }
|
||||
}
|
||||
}
|
||||
$parts = @()
|
||||
if ($ownAttrs -gt 0) { $parts += "$ownAttrs own attrs" }
|
||||
if ($ownTS -gt 0) { $parts += "$ownTS own TS" }
|
||||
if ($ownForms -gt 0) { $parts += "$ownForms own forms" }
|
||||
if ($borrowedItems -gt 0) { $parts += "$borrowedItems borrowed items" }
|
||||
if ($parts.Count -gt 0) {
|
||||
Write-Host " ChildObjects: $($parts -join ', ')"
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ownList += $obj
|
||||
Write-Host " [OWN] $($obj.Type).$($obj.Name)"
|
||||
|
||||
# Brief info for own objects
|
||||
if ($info.ObjElement) {
|
||||
$childObj = $info.ObjElement.SelectSingleNode("md:ChildObjects", $info.ObjNs)
|
||||
if ($childObj) {
|
||||
$attrs = 0; $forms = 0; $ts = 0
|
||||
foreach ($c in $childObj.ChildNodes) {
|
||||
if ($c.NodeType -ne 'Element') { continue }
|
||||
switch ($c.LocalName) {
|
||||
"Attribute" { $attrs++ }
|
||||
"TabularSection" { $ts++ }
|
||||
"Form" { $forms++ }
|
||||
}
|
||||
}
|
||||
$parts = @()
|
||||
if ($attrs -gt 0) { $parts += "$attrs attrs" }
|
||||
if ($ts -gt 0) { $parts += "$ts TS" }
|
||||
if ($forms -gt 0) { $parts += "$forms forms" }
|
||||
if ($parts.Count -gt 0) {
|
||||
Write-Host " $($parts -join ', ')"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== Summary: $($borrowedList.Count) borrowed, $($ownList.Count) own objects ==="
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MODE B: Transfer check
|
||||
# ============================================================
|
||||
if ($Mode -eq "B") {
|
||||
$transferred = 0
|
||||
$notTransferred = 0
|
||||
$needsReview = 0
|
||||
|
||||
foreach ($obj in $objects) {
|
||||
$info = Get-ObjectInfo $obj.Type $obj.Name
|
||||
if (-not $info -or -not $info.Exists -or -not $info.Borrowed) { continue }
|
||||
|
||||
# Find .bsl files with &ИзменениеИКонтроль
|
||||
$bslFiles = Get-BslFiles $obj.Type $obj.Name
|
||||
foreach ($bsl in $bslFiles) {
|
||||
$interceptors = Get-Interceptors $bsl
|
||||
$macInterceptors = @($interceptors | Where-Object { $_.Type -eq "ИзменениеИКонтроль" })
|
||||
|
||||
if ($macInterceptors.Count -eq 0) { continue }
|
||||
|
||||
foreach ($ic in $macInterceptors) {
|
||||
$methodName = $ic.Method
|
||||
$relBsl = $bsl.Replace($ExtensionPath, "").TrimStart("\", "/")
|
||||
|
||||
# Find #Вставка blocks in this file
|
||||
$insertBlocks = Get-InsertionBlocks $bsl
|
||||
|
||||
if ($insertBlocks.Count -eq 0) {
|
||||
Write-Host " [NEEDS_REVIEW] $($obj.Type).$($obj.Name) — &ИзменениеИКонтроль(`"$methodName`") — no #Вставка blocks"
|
||||
$needsReview++
|
||||
continue
|
||||
}
|
||||
|
||||
# Find corresponding module in config
|
||||
if (-not $childTypeDirMap.ContainsKey($obj.Type)) { continue }
|
||||
$dirName = $childTypeDirMap[$obj.Type]
|
||||
$configBsl = $bsl.Replace($ExtensionPath, $ConfigPath)
|
||||
|
||||
if (-not (Test-Path $configBsl)) {
|
||||
Write-Host " [NEEDS_REVIEW] $($obj.Type).$($obj.Name) — &ИзменениеИКонтроль(`"$methodName`") — config module not found"
|
||||
$needsReview++
|
||||
continue
|
||||
}
|
||||
|
||||
$configContent = [System.IO.File]::ReadAllText($configBsl, [System.Text.Encoding]::UTF8)
|
||||
|
||||
$allTransferred = $true
|
||||
foreach ($block in $insertBlocks) {
|
||||
$code = $block.Code
|
||||
if (-not $code) { continue }
|
||||
|
||||
# Normalize whitespace for comparison
|
||||
$codeNorm = $code -replace '\s+', ' '
|
||||
$configNorm = $configContent -replace '\s+', ' '
|
||||
|
||||
if ($configNorm.Contains($codeNorm)) {
|
||||
# Found in config
|
||||
} else {
|
||||
$allTransferred = $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($allTransferred) {
|
||||
Write-Host " [TRANSFERRED] $($obj.Type).$($obj.Name) — &ИзменениеИКонтроль(`"$methodName`") — $($insertBlocks.Count) block(s)"
|
||||
$transferred++
|
||||
} else {
|
||||
Write-Host " [NOT_TRANSFERRED] $($obj.Type).$($obj.Name) — &ИзменениеИКонтроль(`"$methodName`") — some blocks not found in config"
|
||||
$notTransferred++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== Transfer check: $transferred transferred, $notTransferred not transferred, $needsReview needs review ==="
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: cfe-init
|
||||
description: Создать расширение конфигурации 1С (CFE) — scaffold XML-исходников. Используй когда нужно создать новое расширение для исправления, доработки или дополнения конфигурации
|
||||
argument-hint: <Name> [-Purpose Patch|Customization|AddOn] [-CompatibilityMode Version8_3_24]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cfe-init — Создание расширения конфигурации 1С
|
||||
|
||||
Создаёт scaffold расширения: `Configuration.xml`, `Languages/Русский.xml`, опционально `Roles/`.
|
||||
|
||||
## Подготовка
|
||||
|
||||
Перед созданием расширения рекомендуется получить версию и режим совместимости базовой конфигурации:
|
||||
|
||||
```
|
||||
/cf-info <ConfigPath> -Mode brief
|
||||
```
|
||||
|
||||
Это даст `CompatibilityMode` (передать в `-CompatibilityMode`) и версию конфигурации (для `-Version`, например `<ВерсияКонфигурации>.1`).
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание | По умолчанию |
|
||||
|----------|----------|--------------|
|
||||
| `Name` | Имя расширения (обязат.) | — |
|
||||
| `Synonym` | Синоним | = Name |
|
||||
| `NamePrefix` | Префикс собственных объектов | = Name + "_" |
|
||||
| `OutputDir` | Каталог для создания | `src` |
|
||||
| `Purpose` | `Patch` (исправление) / `Customization` (доработка) / `AddOn` (дополнение) | `Customization` |
|
||||
| `Version` | Версия расширения | — |
|
||||
| `Vendor` | Поставщик | — |
|
||||
| `CompatibilityMode` | Режим совместимости | `Version8_3_24` |
|
||||
| `NoRole` | Без основной роли | false |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\cfe-init\scripts\cfe-init.ps1 -Name "МоёРасширение"
|
||||
```
|
||||
|
||||
## Что создаётся
|
||||
|
||||
```
|
||||
<OutputDir>/
|
||||
├── Configuration.xml # Свойства расширения
|
||||
├── Languages/
|
||||
│ └── Русский.xml # Язык (заимствованный)
|
||||
└── Roles/ # Если не -NoRole
|
||||
└── <Prefix>ОсновнаяРоль.xml
|
||||
```
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Расширение-исправление для ERP
|
||||
... -Name Расш1 -Purpose Patch -CompatibilityMode Version8_3_17 -OutputDir src
|
||||
|
||||
# Расширение-доработка с версией
|
||||
... -Name МоёРасширение -Version "1.0.0.1" -Vendor "Компания" -OutputDir src
|
||||
|
||||
# Без роли, с явным префиксом
|
||||
... -Name ИсправлениеБага -NamePrefix "ИБ_" -Purpose Patch -NoRole -OutputDir src
|
||||
```
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/cfe-validate <OutputDir>
|
||||
```
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
# cfe-init v1.0 — Create 1C configuration extension scaffold (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Name,
|
||||
[string]$Synonym = $Name,
|
||||
[string]$NamePrefix,
|
||||
[string]$OutputDir = "src",
|
||||
[ValidateSet("Patch","Customization","AddOn")]
|
||||
[string]$Purpose = "Customization",
|
||||
[string]$Version,
|
||||
[string]$Vendor,
|
||||
[string]$CompatibilityMode = "Version8_3_24",
|
||||
[switch]$NoRole
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Default NamePrefix ---
|
||||
if (-not $NamePrefix) {
|
||||
$NamePrefix = "${Name}_"
|
||||
}
|
||||
|
||||
# --- 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()
|
||||
$uuidRole = [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()
|
||||
|
||||
# --- 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 { "" }
|
||||
|
||||
# --- Role name ---
|
||||
$roleName = "${NamePrefix}ОсновнаяРоль"
|
||||
|
||||
# --- DefaultRoles XML ---
|
||||
$defaultRolesXml = ""
|
||||
if (-not $NoRole) {
|
||||
$defaultRolesXml = "`r`n`t`t`t`t<xr:Item xsi:type=`"xr:MDObjectRef`">Role.$roleName</xr:Item>`r`n`t`t`t"
|
||||
}
|
||||
|
||||
# --- ChildObjects ---
|
||||
$childObjectsXml = "`r`n`t`t`t<Language>Русский</Language>"
|
||||
if (-not $NoRole) {
|
||||
$childObjectsXml += "`r`n`t`t`t<Role>$roleName</Role>"
|
||||
}
|
||||
$childObjectsXml += "`r`n`t`t"
|
||||
|
||||
# --- 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>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>$([System.Security.SecurityElement]::Escape($Name))</Name>
|
||||
<Synonym>$synonymXml</Synonym>
|
||||
<Comment/>
|
||||
<ConfigurationExtensionPurpose>$Purpose</ConfigurationExtensionPurpose>
|
||||
<KeepMappingToExtendedConfigurationObjectsByIDs>true</KeepMappingToExtendedConfigurationObjectsByIDs>
|
||||
<NamePrefix>$([System.Security.SecurityElement]::Escape($NamePrefix))</NamePrefix>
|
||||
<ConfigurationExtensionCompatibilityMode>$CompatibilityMode</ConfigurationExtensionCompatibilityMode>
|
||||
<DefaultRunMode>ManagedApplication</DefaultRunMode>
|
||||
<UsePurposes>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||
</UsePurposes>
|
||||
<ScriptVariant>Russian</ScriptVariant>
|
||||
<DefaultRoles>$defaultRolesXml</DefaultRoles>
|
||||
<Vendor>$vendorXml</Vendor>
|
||||
<Version>$versionXml</Version>
|
||||
<DefaultLanguage>Language.Русский</DefaultLanguage>
|
||||
<BriefInformation/>
|
||||
<DetailedInformation/>
|
||||
<Copyright/>
|
||||
<VendorInformationAddress/>
|
||||
<ConfigurationInformationAddress/>
|
||||
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
|
||||
</Properties>
|
||||
<ChildObjects>$childObjectsXml</ChildObjects>
|
||||
</Configuration>
|
||||
</MetaDataObject>
|
||||
"@
|
||||
|
||||
# --- Languages/Русский.xml (adopted format) ---
|
||||
$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">
|
||||
<InternalInfo/>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Русский</Name>
|
||||
<Comment/>
|
||||
<ExtendedConfigurationObject>00000000-0000-0000-0000-000000000000</ExtendedConfigurationObject>
|
||||
<LanguageCode>ru</LanguageCode>
|
||||
</Properties>
|
||||
</Language>
|
||||
</MetaDataObject>
|
||||
"@
|
||||
|
||||
# --- Role XML ---
|
||||
$roleXml = @"
|
||||
<?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">
|
||||
<Role uuid="$uuidRole">
|
||||
<Properties>
|
||||
<Name>$([System.Security.SecurityElement]::Escape($roleName))</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
</Properties>
|
||||
</Role>
|
||||
</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)
|
||||
$langFile = Join-Path $langDir "Русский.xml"
|
||||
[System.IO.File]::WriteAllText($langFile, $langXml, $enc)
|
||||
|
||||
# --- Role ---
|
||||
if (-not $NoRole) {
|
||||
$roleDir = Join-Path $OutputDir "Roles"
|
||||
if (-not (Test-Path $roleDir)) {
|
||||
New-Item -ItemType Directory -Path $roleDir -Force | Out-Null
|
||||
}
|
||||
$roleFile = Join-Path $roleDir "$roleName.xml"
|
||||
[System.IO.File]::WriteAllText($roleFile, $roleXml, $enc)
|
||||
}
|
||||
|
||||
# --- Output ---
|
||||
Write-Host "[OK] Создано расширение: $Name"
|
||||
Write-Host " Каталог: $OutputDir"
|
||||
Write-Host " Назначение: $Purpose"
|
||||
Write-Host " Префикс: $NamePrefix"
|
||||
Write-Host " Configuration.xml: $cfgFile"
|
||||
Write-Host " Languages: $langFile"
|
||||
if (-not $NoRole) {
|
||||
Write-Host " Role: $roleFile"
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: cfe-patch-method
|
||||
description: Генерация перехватчика метода в расширении 1С (CFE). Используй когда нужно перехватить метод заимствованного объекта — вставить код до, после или вместо оригинального
|
||||
argument-hint: -ExtensionPath <path> -ModulePath "Catalog.X.ObjectModule" -MethodName "ПриЗаписи" -InterceptorType Before
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cfe-patch-method — Генерация перехватчика метода
|
||||
|
||||
Генерирует `.bsl` файл с декоратором перехвата для заимствованного объекта расширения. Создаёт файл или дописывает в существующий.
|
||||
|
||||
## Предусловие
|
||||
|
||||
Объект должен быть заимствован в расширение (`/cfe-borrow`). Скрипт читает `NamePrefix` из `Configuration.xml` расширения для формирования имени процедуры.
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание | По умолчанию |
|
||||
|----------|----------|--------------|
|
||||
| `ExtensionPath` | Путь к расширению (обязат.) | — |
|
||||
| `ModulePath` | Путь к модулю (обязат.) | — |
|
||||
| `MethodName` | Имя перехватываемого метода (обязат.) | — |
|
||||
| `InterceptorType` | `Before` / `After` / `ModificationAndControl` (обязат.) | — |
|
||||
| `Context` | Директива контекста | `НаСервере` |
|
||||
| `IsFunction` | Метод — функция (добавит `Возврат`) | false |
|
||||
|
||||
## Формат ModulePath
|
||||
|
||||
| ModulePath | Файл |
|
||||
|------------|------|
|
||||
| `Catalog.X.ObjectModule` | `Catalogs/X/Ext/ObjectModule.bsl` |
|
||||
| `Catalog.X.ManagerModule` | `Catalogs/X/Ext/ManagerModule.bsl` |
|
||||
| `Catalog.X.Form.Y` | `Catalogs/X/Forms/Y/Ext/Form/Module.bsl` |
|
||||
| `CommonModule.X` | `CommonModules/X/Ext/Module.bsl` |
|
||||
| `Document.X.ObjectModule` | `Documents/X/Ext/ObjectModule.bsl` |
|
||||
| `Document.X.Form.Y` | `Documents/X/Forms/Y/Ext/Form/Module.bsl` |
|
||||
|
||||
Аналогично для Report, DataProcessor, InformationRegister и других типов.
|
||||
|
||||
## Типы перехвата
|
||||
|
||||
| InterceptorType | Декоратор | Назначение |
|
||||
|-----------------|-----------|------------|
|
||||
| `Before` | `&Перед` | Код до вызова оригинального метода |
|
||||
| `After` | `&После` | Код после вызова оригинального метода |
|
||||
| `ModificationAndControl` | `&ИзменениеИКонтроль` | Копия тела метода с маркерами `#Вставка`/`#Удаление` |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\cfe-patch-method\scripts\cfe-patch-method.ps1 -ExtensionPath src -ModulePath "Catalog.Контрагенты.ObjectModule" -MethodName "ПриЗаписи" -InterceptorType Before
|
||||
```
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Перехват &Перед на сервере
|
||||
... -ExtensionPath src -ModulePath "Catalog.Контрагенты.ObjectModule" -MethodName "ПриЗаписи" -InterceptorType Before
|
||||
|
||||
# Перехват &После на клиенте
|
||||
... -ExtensionPath src -ModulePath "Document.Заказ.Form.ФормаДокумента" -MethodName "ПослеЗаписиНаСервере" -InterceptorType After -Context "НаКлиенте"
|
||||
|
||||
# ИзменениеИКонтроль для функции
|
||||
... -ExtensionPath src -ModulePath "CommonModule.ОбщийМодуль" -MethodName "ПолучитьДанные" -InterceptorType ModificationAndControl -IsFunction
|
||||
```
|
||||
|
||||
## Генерируемый код (Before)
|
||||
|
||||
```bsl
|
||||
&НаСервере
|
||||
&Перед("ПриЗаписи")
|
||||
Процедура Расш1_ПриЗаписи()
|
||||
// TODO: код перед вызовом оригинального метода
|
||||
КонецПроцедуры
|
||||
```
|
||||
@@ -0,0 +1,186 @@
|
||||
# cfe-patch-method v1.0 — Generate method interceptor for 1C extension (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ExtensionPath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ModulePath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$MethodName,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet("Before","After","ModificationAndControl")]
|
||||
[string]$InterceptorType,
|
||||
|
||||
[string]$Context = "НаСервере",
|
||||
|
||||
[switch]$IsFunction
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve extension path ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($ExtensionPath)) {
|
||||
$ExtensionPath = Join-Path (Get-Location).Path $ExtensionPath
|
||||
}
|
||||
if (Test-Path $ExtensionPath -PathType Leaf) {
|
||||
$ExtensionPath = Split-Path $ExtensionPath -Parent
|
||||
}
|
||||
$cfgFile = Join-Path $ExtensionPath "Configuration.xml"
|
||||
if (-not (Test-Path $cfgFile)) {
|
||||
Write-Error "Configuration.xml not found in: $ExtensionPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Read NamePrefix from Configuration.xml ---
|
||||
$cfgDoc = New-Object System.Xml.XmlDocument
|
||||
$cfgDoc.PreserveWhitespace = $false
|
||||
$cfgDoc.Load($cfgFile)
|
||||
|
||||
$cfgNs = New-Object System.Xml.XmlNamespaceManager($cfgDoc.NameTable)
|
||||
$cfgNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
|
||||
$propsNode = $cfgDoc.SelectSingleNode("//md:Configuration/md:Properties", $cfgNs)
|
||||
$prefixNode = if ($propsNode) { $propsNode.SelectSingleNode("md:NamePrefix", $cfgNs) } else { $null }
|
||||
$namePrefix = if ($prefixNode -and $prefixNode.InnerText) { $prefixNode.InnerText } else { "Расш_" }
|
||||
|
||||
# --- Map ModulePath to file path ---
|
||||
# ModulePath formats:
|
||||
# Catalog.X.ObjectModule -> Catalogs/X/Ext/ObjectModule.bsl
|
||||
# Catalog.X.ManagerModule -> Catalogs/X/Ext/ManagerModule.bsl
|
||||
# Catalog.X.Form.Y -> Catalogs/X/Forms/Y/Ext/Form/Module.bsl
|
||||
# CommonModule.X -> CommonModules/X/Ext/Module.bsl
|
||||
# Document.X.ObjectModule -> Documents/X/Ext/ObjectModule.bsl
|
||||
# Document.X.ManagerModule -> Documents/X/Ext/ManagerModule.bsl
|
||||
# Document.X.Form.Y -> Documents/X/Forms/Y/Ext/Form/Module.bsl
|
||||
|
||||
$typeDirMap = @{
|
||||
"Catalog"="Catalogs"; "Document"="Documents"; "Enum"="Enums"
|
||||
"CommonModule"="CommonModules"; "Report"="Reports"; "DataProcessor"="DataProcessors"
|
||||
"ExchangePlan"="ExchangePlans"; "ChartOfAccounts"="ChartsOfAccounts"
|
||||
"ChartOfCharacteristicTypes"="ChartsOfCharacteristicTypes"
|
||||
"ChartOfCalculationTypes"="ChartsOfCalculationTypes"
|
||||
"BusinessProcess"="BusinessProcesses"; "Task"="Tasks"
|
||||
"InformationRegister"="InformationRegisters"; "AccumulationRegister"="AccumulationRegisters"
|
||||
"AccountingRegister"="AccountingRegisters"; "CalculationRegister"="CalculationRegisters"
|
||||
}
|
||||
|
||||
$parts = $ModulePath.Split(".")
|
||||
if ($parts.Count -lt 2) {
|
||||
Write-Error "Invalid ModulePath format: $ModulePath. Expected: Type.Name.Module or CommonModule.Name"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$objType = $parts[0]
|
||||
$objName = $parts[1]
|
||||
|
||||
if (-not $typeDirMap.ContainsKey($objType)) {
|
||||
Write-Error "Unknown object type: $objType"
|
||||
exit 1
|
||||
}
|
||||
$dirName = $typeDirMap[$objType]
|
||||
|
||||
$bslFile = $null
|
||||
if ($objType -eq "CommonModule") {
|
||||
# CommonModule.X -> CommonModules/X/Ext/Module.bsl
|
||||
$bslFile = Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Ext") "Module.bsl"
|
||||
} elseif ($parts.Count -ge 4 -and $parts[2] -eq "Form") {
|
||||
# Type.X.Form.Y -> Types/X/Forms/Y/Ext/Form/Module.bsl
|
||||
$formName = $parts[3]
|
||||
$bslFile = Join-Path (Join-Path (Join-Path (Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Forms") $formName) "Ext") "Form") "Module.bsl"
|
||||
} elseif ($parts.Count -ge 3) {
|
||||
# Type.X.ObjectModule -> Types/X/Ext/ObjectModule.bsl
|
||||
$moduleName = $parts[2]
|
||||
$moduleFileName = switch ($moduleName) {
|
||||
"ObjectModule" { "ObjectModule.bsl" }
|
||||
"ManagerModule" { "ManagerModule.bsl" }
|
||||
"RecordSetModule" { "RecordSetModule.bsl" }
|
||||
"CommandModule" { "CommandModule.bsl" }
|
||||
default { "$moduleName.bsl" }
|
||||
}
|
||||
$bslFile = Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) (Join-Path "Ext" $moduleFileName)
|
||||
} else {
|
||||
Write-Error "Invalid ModulePath format: $ModulePath. Expected: Type.Name.Module, Type.Name.Form.FormName, or CommonModule.Name"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Map InterceptorType to decorator ---
|
||||
$decorator = switch ($InterceptorType) {
|
||||
"Before" { "&Перед" }
|
||||
"After" { "&После" }
|
||||
"ModificationAndControl" { "&ИзменениеИКонтроль" }
|
||||
}
|
||||
|
||||
# --- Map Context to annotation ---
|
||||
$contextAnnotation = switch ($Context) {
|
||||
"НаСервере" { "&НаСервере" }
|
||||
"НаКлиенте" { "&НаКлиенте" }
|
||||
"НаСервереБезКонтекста" { "&НаСервереБезКонтекста" }
|
||||
default { "&$Context" }
|
||||
}
|
||||
|
||||
# --- Procedure name ---
|
||||
$procName = "${namePrefix}${MethodName}"
|
||||
|
||||
# --- Generate BSL code ---
|
||||
$keyword = if ($IsFunction) { "Функция" } else { "Процедура" }
|
||||
$endKeyword = if ($IsFunction) { "КонецФункции" } else { "КонецПроцедуры" }
|
||||
|
||||
$bodyLines = @()
|
||||
switch ($InterceptorType) {
|
||||
"Before" {
|
||||
$bodyLines += "`t// TODO: код перед вызовом оригинального метода"
|
||||
}
|
||||
"After" {
|
||||
$bodyLines += "`t// TODO: код после вызова оригинального метода"
|
||||
}
|
||||
"ModificationAndControl" {
|
||||
$bodyLines += "`t// Скопируйте тело оригинального метода и внесите изменения,"
|
||||
$bodyLines += "`t// используя маркеры #Удаление / #КонецУдаления и #Вставка / #КонецВставки"
|
||||
}
|
||||
}
|
||||
|
||||
if ($IsFunction) {
|
||||
$bodyLines += "`t"
|
||||
$bodyLines += "`tВозврат Неопределено; // TODO: заменить на реальное возвращаемое значение"
|
||||
}
|
||||
|
||||
$bslCode = @()
|
||||
$bslCode += "$contextAnnotation"
|
||||
$bslCode += "${decorator}(`"$MethodName`")"
|
||||
$bslCode += "$keyword ${procName}()"
|
||||
$bslCode += $bodyLines
|
||||
$bslCode += "$endKeyword"
|
||||
|
||||
$bslText = ($bslCode -join "`r`n") + "`r`n"
|
||||
|
||||
# --- Check if file exists and append ---
|
||||
$bslDir = Split-Path $bslFile -Parent
|
||||
if (-not (Test-Path $bslDir)) {
|
||||
New-Item -ItemType Directory -Path $bslDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
|
||||
if (Test-Path $bslFile) {
|
||||
# Append to existing file
|
||||
$existing = [System.IO.File]::ReadAllText($bslFile, $enc)
|
||||
$separator = "`r`n"
|
||||
if ($existing -and -not $existing.EndsWith("`n")) {
|
||||
$separator = "`r`n`r`n"
|
||||
}
|
||||
$newContent = $existing + $separator + $bslText
|
||||
[System.IO.File]::WriteAllText($bslFile, $newContent, $enc)
|
||||
Write-Host "[OK] Добавлен перехватчик в существующий файл"
|
||||
} else {
|
||||
[System.IO.File]::WriteAllText($bslFile, $bslText, $enc)
|
||||
Write-Host "[OK] Создан файл модуля"
|
||||
}
|
||||
|
||||
Write-Host " Файл: $bslFile"
|
||||
Write-Host " Декоратор: $decorator(`"$MethodName`")"
|
||||
Write-Host " Процедура: ${procName}()"
|
||||
Write-Host " Контекст: $contextAnnotation"
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: cfe-validate
|
||||
description: Валидация расширения конфигурации 1С (CFE). Используй после создания или модификации расширения для проверки корректности
|
||||
argument-hint: <ExtensionPath> [-MaxErrors 30]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /cfe-validate — Валидация расширения конфигурации
|
||||
|
||||
Проверяет структурную корректность расширения: XML-формат, свойства, состав, заимствованные объекты. Аналог `/cf-validate`, но для расширений.
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание | По умолчанию |
|
||||
|----------|----------|--------------|
|
||||
| `ExtensionPath` | Путь к каталогу или Configuration.xml расширения (обязат.) | — |
|
||||
| `MaxErrors` | Лимит ошибок | 30 |
|
||||
| `OutFile` | Записать результат в файл | — |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\cfe-validate\scripts\cfe-validate.ps1 -ExtensionPath src
|
||||
```
|
||||
|
||||
## Проверки (9 шагов)
|
||||
|
||||
| # | Проверка | Уровень |
|
||||
|---|----------|---------|
|
||||
| 1 | XML well-formedness, MetaDataObject/Configuration, version | ERROR |
|
||||
| 2 | InternalInfo: 7 ContainedObject, валидные ClassId | ERROR |
|
||||
| 3 | Extension properties: ObjectBelonging=Adopted, Name, Purpose, NamePrefix, KeepMapping | ERROR |
|
||||
| 4 | Enum-значения: ConfigurationExtensionCompatibilityMode, DefaultRunMode, ScriptVariant, InterfaceCompatibilityMode | ERROR |
|
||||
| 5 | ChildObjects: валидные типы (44), нет дубликатов, каноничный порядок | ERROR/WARN |
|
||||
| 6 | DefaultLanguage ссылается на Language в ChildObjects | ERROR |
|
||||
| 7 | Файлы языков существуют | WARN |
|
||||
| 8 | Каталоги объектов существуют | WARN |
|
||||
| 9 | Заимствованные объекты: ObjectBelonging=Adopted, ExtendedConfigurationObject UUID | ERROR/WARN |
|
||||
|
||||
## Пример вывода
|
||||
|
||||
```
|
||||
=== Validation: Extension.МоёРасширение ===
|
||||
[OK] 1. Root structure: MetaDataObject/Configuration, version 2.17
|
||||
[OK] 2. InternalInfo: 7 ContainedObject, all ClassIds valid
|
||||
...
|
||||
=== Result: 0 errors, 0 warnings ===
|
||||
```
|
||||
@@ -0,0 +1,607 @@
|
||||
# cfe-validate v1.0 — Validate 1C configuration extension structure (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ExtensionPath,
|
||||
|
||||
[int]$MaxErrors = 30,
|
||||
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve path ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($ExtensionPath)) {
|
||||
$ExtensionPath = Join-Path (Get-Location).Path $ExtensionPath
|
||||
}
|
||||
|
||||
if (Test-Path $ExtensionPath -PathType Container) {
|
||||
$candidate = Join-Path $ExtensionPath "Configuration.xml"
|
||||
if (Test-Path $candidate) {
|
||||
$ExtensionPath = $candidate
|
||||
} else {
|
||||
Write-Host "[ERROR] No Configuration.xml found in directory: $ExtensionPath"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $ExtensionPath)) {
|
||||
Write-Host "[ERROR] File not found: $ExtensionPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$resolvedPath = (Resolve-Path $ExtensionPath).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",
|
||||
"9fcd25a0-4822-11d4-9414-008048da11f9",
|
||||
"e3687481-0a87-462c-a166-9f34594f9bba",
|
||||
"9de14907-ec23-4a07-96f0-85521cb6b53b",
|
||||
"51f2d5d8-ea4d-4064-8892-82951750031e",
|
||||
"e68182ea-4237-4383-967f-90c1e3370bc7",
|
||||
"fb282519-d103-4dd3-bc12-cb271d631dfc"
|
||||
)
|
||||
|
||||
# 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 extension 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")
|
||||
"InterfaceCompatibilityMode" = @("Taxi","TaxiEnableVersion8_2","Version8_2")
|
||||
}
|
||||
|
||||
# --- 1. Parse XML ---
|
||||
Out-Line ""
|
||||
|
||||
$xmlDoc = $null
|
||||
try {
|
||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$xmlDoc.PreserveWhitespace = $false
|
||||
$xmlDoc.Load($resolvedPath)
|
||||
} catch {
|
||||
Out-Line "=== Validation: Extension (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: Extension.$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
|
||||
}
|
||||
}
|
||||
|
||||
$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: Extension-specific properties ---
|
||||
if (-not $propsNode) {
|
||||
Report-Error "3. Properties block missing"
|
||||
} else {
|
||||
$check3Ok = $true
|
||||
|
||||
# ObjectBelonging = Adopted
|
||||
$obNode = $propsNode.SelectSingleNode("md:ObjectBelonging", $ns)
|
||||
if (-not $obNode -or $obNode.InnerText -ne "Adopted") {
|
||||
Report-Error "3. ObjectBelonging must be 'Adopted', got '$($obNode.InnerText)'"
|
||||
$check3Ok = $false
|
||||
}
|
||||
|
||||
# Name
|
||||
if (-not $nameNode -or -not $nameNode.InnerText) {
|
||||
Report-Error "3. Name is missing or empty"
|
||||
$check3Ok = $false
|
||||
} else {
|
||||
$nameVal = $nameNode.InnerText
|
||||
if ($nameVal -notmatch $identPattern) {
|
||||
Report-Error "3. Name '$nameVal' is not a valid 1C identifier"
|
||||
$check3Ok = $false
|
||||
}
|
||||
}
|
||||
|
||||
# ConfigurationExtensionPurpose
|
||||
$purposeNode = $propsNode.SelectSingleNode("md:ConfigurationExtensionPurpose", $ns)
|
||||
$validPurposes = @("Patch","Customization","AddOn")
|
||||
if (-not $purposeNode -or -not $purposeNode.InnerText) {
|
||||
Report-Error "3. ConfigurationExtensionPurpose is missing"
|
||||
$check3Ok = $false
|
||||
} elseif ($validPurposes -notcontains $purposeNode.InnerText) {
|
||||
Report-Error "3. ConfigurationExtensionPurpose '$($purposeNode.InnerText)' invalid (expected: Patch, Customization, AddOn)"
|
||||
$check3Ok = $false
|
||||
}
|
||||
|
||||
# NamePrefix
|
||||
$prefixNode = $propsNode.SelectSingleNode("md:NamePrefix", $ns)
|
||||
if (-not $prefixNode -or -not $prefixNode.InnerText) {
|
||||
Report-Warn "3. NamePrefix is empty"
|
||||
}
|
||||
|
||||
# KeepMappingToExtendedConfigurationObjectsByIDs
|
||||
$keepMapNode = $propsNode.SelectSingleNode("md:KeepMappingToExtendedConfigurationObjectsByIDs", $ns)
|
||||
if (-not $keepMapNode) {
|
||||
Report-Warn "3. KeepMappingToExtendedConfigurationObjectsByIDs is missing"
|
||||
}
|
||||
|
||||
# DefaultLanguage
|
||||
$defLangNode = $propsNode.SelectSingleNode("md:DefaultLanguage", $ns)
|
||||
$defLang = if ($defLangNode -and $defLangNode.InnerText) { $defLangNode.InnerText } else { "" }
|
||||
|
||||
if ($check3Ok) {
|
||||
$purposeVal = if ($purposeNode) { $purposeNode.InnerText } else { "?" }
|
||||
$prefixVal = if ($prefixNode -and $prefixNode.InnerText) { $prefixNode.InnerText } else { "(empty)" }
|
||||
Report-OK "3. Extension properties: Name=`"$objName`", Purpose=$purposeVal, Prefix=$prefixVal"
|
||||
}
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 4: Enum property values ---
|
||||
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 = @{}
|
||||
$lastTypeOrder = -1
|
||||
$orderOk = $true
|
||||
|
||||
foreach ($child in $childObjNode.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
$typeName = $child.LocalName
|
||||
$objNameVal = $child.InnerText
|
||||
|
||||
$typeIdx = $childObjectTypes.IndexOf($typeName)
|
||||
if ($typeIdx -lt 0) {
|
||||
Report-Error "5. Unknown type '$typeName' in ChildObjects"
|
||||
$check5Ok = $false
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
|
||||
$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) {
|
||||
$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 ---
|
||||
if ($childObjNode) {
|
||||
$dirsToCheck = @{}
|
||||
foreach ($child in $childObjNode.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
$typeName = $child.LocalName
|
||||
if ($typeName -eq "Language") { continue }
|
||||
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"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 9: Borrowed objects validation ---
|
||||
if ($childObjNode) {
|
||||
$borrowedCount = 0
|
||||
$borrowedOk = 0
|
||||
$check9Ok = $true
|
||||
|
||||
foreach ($child in $childObjNode.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
$typeName = $child.LocalName
|
||||
$childName = $child.InnerText
|
||||
if ($typeName -eq "Language") { continue }
|
||||
|
||||
if (-not $childTypeDirMap.ContainsKey($typeName)) { continue }
|
||||
$dirName = $childTypeDirMap[$typeName]
|
||||
$objFile = Join-Path (Join-Path $configDir $dirName) "$childName.xml"
|
||||
|
||||
if (-not (Test-Path $objFile)) { continue }
|
||||
|
||||
# Parse object XML
|
||||
$objDoc = $null
|
||||
try {
|
||||
$objDoc = New-Object System.Xml.XmlDocument
|
||||
$objDoc.PreserveWhitespace = $false
|
||||
$objDoc.Load($objFile)
|
||||
} catch {
|
||||
Report-Warn "9. Cannot parse $dirName/$childName.xml: $($_.Exception.Message)"
|
||||
continue
|
||||
}
|
||||
|
||||
$objNs = New-Object System.Xml.XmlNamespaceManager($objDoc.NameTable)
|
||||
$objNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$objNs.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
|
||||
|
||||
# Find the object element (Catalog, Document, etc.)
|
||||
$objRoot = $objDoc.DocumentElement
|
||||
$objEl = $null
|
||||
foreach ($c in $objRoot.ChildNodes) {
|
||||
if ($c.NodeType -eq 'Element') { $objEl = $c; break }
|
||||
}
|
||||
if (-not $objEl) { continue }
|
||||
|
||||
$objProps = $objEl.SelectSingleNode("md:Properties", $objNs)
|
||||
if (-not $objProps) { continue }
|
||||
|
||||
$obNode = $objProps.SelectSingleNode("md:ObjectBelonging", $objNs)
|
||||
if ($obNode -and $obNode.InnerText -eq "Adopted") {
|
||||
$borrowedCount++
|
||||
|
||||
# Check ExtendedConfigurationObject
|
||||
$extObj = $objProps.SelectSingleNode("md:ExtendedConfigurationObject", $objNs)
|
||||
if (-not $extObj -or -not $extObj.InnerText) {
|
||||
Report-Error "9. Borrowed ${typeName}.${childName}: missing ExtendedConfigurationObject"
|
||||
$check9Ok = $false
|
||||
} elseif ($extObj.InnerText -notmatch $guidPattern) {
|
||||
Report-Error "9. Borrowed ${typeName}.${childName}: invalid ExtendedConfigurationObject UUID '$($extObj.InnerText)'"
|
||||
$check9Ok = $false
|
||||
} else {
|
||||
$borrowedOk++
|
||||
}
|
||||
}
|
||||
|
||||
if ($script:stopped) { break }
|
||||
}
|
||||
|
||||
if ($borrowedCount -eq 0) {
|
||||
Report-OK "9. Borrowed objects: none found"
|
||||
} elseif ($check9Ok) {
|
||||
Report-OK "9. Borrowed objects: $borrowedOk/$borrowedCount validated"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Final output ---
|
||||
& $finalize
|
||||
|
||||
if ($script:errors -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
@@ -204,5 +204,5 @@ allowed-tools:
|
||||
|
||||
- Добавить ещё команду: `/epf-bsp-add-command`
|
||||
- Добавить форму: `/epf-add-form`
|
||||
- Добавить макет: `/epf-add-template`
|
||||
- Добавить макет: `/template-add`
|
||||
- Собрать EPF: `/epf-build`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: epf-build
|
||||
description: Собрать внешнюю обработку 1С (EPF) из XML-исходников
|
||||
description: Собрать внешнюю обработку 1С (EPF/ERF) из XML-исходников
|
||||
argument-hint: <ProcessorName>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
@@ -11,7 +11,7 @@ allowed-tools:
|
||||
|
||||
# /epf-build — Сборка обработки
|
||||
|
||||
Собирает EPF-файл из XML-исходников с помощью платформы 1С.
|
||||
Собирает EPF-файл из XML-исходников с помощью платформы 1С. Та же команда CLI работает и для внешних отчётов (ERF) — см. `/erf-build`.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: epf-dump
|
||||
description: Разобрать EPF-файл обработки 1С в XML-исходники
|
||||
description: Разобрать EPF-файл обработки 1С (EPF/ERF) в XML-исходники
|
||||
argument-hint: <EpfFile>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
@@ -11,7 +11,7 @@ allowed-tools:
|
||||
|
||||
# /epf-dump — Разборка обработки
|
||||
|
||||
Разбирает EPF-файл во XML-исходники с помощью платформы 1С (иерархический формат).
|
||||
Разбирает EPF-файл во XML-исходники с помощью платформы 1С (иерархический формат). Та же команда CLI работает и для внешних отчётов (ERF) — см. `/erf-dump`.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -50,5 +50,6 @@ pwsh -NoProfile -File .claude/skills/epf-init/scripts/init.ps1 -Name "<Name>" [-
|
||||
## Дальнейшие шаги
|
||||
|
||||
- Добавить форму: `/epf-add-form`
|
||||
- Добавить макет: `/epf-add-template`
|
||||
- Добавить макет: `/template-add`
|
||||
- Добавить справку: `/help-add`
|
||||
- Собрать EPF: `/epf-build`
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
name: epf-remove-form
|
||||
description: Удалить форму из внешней обработки 1С
|
||||
argument-hint: <ProcessorName> <FormName>
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /epf-remove-form — Удаление формы
|
||||
|
||||
Удаляет форму и убирает её регистрацию из корневого XML обработки.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/epf-remove-form <ProcessorName> <FormName>
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|---------------|:------------:|--------------|-------------------------------------|
|
||||
| ProcessorName | да | — | Имя обработки |
|
||||
| FormName | да | — | Имя формы для удаления |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
pwsh -NoProfile -File .claude/skills/epf-remove-form/scripts/remove-form.ps1 -ProcessorName "<ProcessorName>" -FormName "<FormName>" [-SrcDir "<SrcDir>"]
|
||||
```
|
||||
|
||||
## Что удаляется
|
||||
|
||||
```
|
||||
<SrcDir>/<ProcessorName>/Forms/<FormName>.xml # Метаданные формы
|
||||
<SrcDir>/<ProcessorName>/Forms/<FormName>/ # Каталог формы (рекурсивно)
|
||||
```
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- `<SrcDir>/<ProcessorName>.xml` — убирается `<Form>` из `ChildObjects`
|
||||
- Если удаляемая форма была DefaultForm — очищается значение DefaultForm
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
name: epf-remove-template
|
||||
description: Удалить макет из внешней обработки 1С
|
||||
argument-hint: <ProcessorName> <TemplateName>
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /epf-remove-template — Удаление макета
|
||||
|
||||
Удаляет макет и убирает его регистрацию из корневого XML обработки.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/epf-remove-template <ProcessorName> <TemplateName>
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|---------------|:------------:|--------------|-------------------------------------|
|
||||
| ProcessorName | да | — | Имя обработки |
|
||||
| TemplateName | да | — | Имя макета для удаления |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
pwsh -NoProfile -File .claude/skills/epf-remove-template/scripts/remove-template.ps1 -ProcessorName "<ProcessorName>" -TemplateName "<TemplateName>" [-SrcDir "<SrcDir>"]
|
||||
```
|
||||
|
||||
## Что удаляется
|
||||
|
||||
```
|
||||
<SrcDir>/<ProcessorName>/Templates/<TemplateName>.xml # Метаданные макета
|
||||
<SrcDir>/<ProcessorName>/Templates/<TemplateName>/ # Каталог макета (рекурсивно)
|
||||
```
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- `<SrcDir>/<ProcessorName>.xml` — убирается `<Template>` из `ChildObjects`
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: erf-build
|
||||
description: Собрать внешний отчёт 1С (ERF) из XML-исходников
|
||||
argument-hint: <ReportName>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /erf-build — Сборка отчёта
|
||||
|
||||
Собирает ERF-файл из XML-исходников с помощью платформы 1С. Использует ту же команду CLI, что и `/epf-build`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/erf-build <ReportName> [SrcDir] [OutDir]
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|------------|:------------:|--------------|--------------------------------------|
|
||||
| ReportName | да | — | Имя отчёта (имя корневого XML) |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
| OutDir | нет | `build` | Каталог для результата |
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
| Переменная | Описание | Пример |
|
||||
|------------|---------------------------------------|-----------------------------------------------|
|
||||
| V8_PATH | Каталог bin платформы 1С | `C:\Program Files\1cv8\8.3.25.1257\bin` |
|
||||
| V8_BASE | Путь к пустой файловой ИБ | `.\base` |
|
||||
|
||||
## Команды
|
||||
|
||||
### 1. Создать пустую ИБ (если нет)
|
||||
|
||||
```cmd
|
||||
"%V8_PATH%\1cv8.exe" CREATEINFOBASE File="%V8_BASE%"
|
||||
```
|
||||
|
||||
### 2. Сборка ERF из XML
|
||||
|
||||
```cmd
|
||||
"%V8_PATH%\1cv8.exe" DESIGNER /F "%V8_BASE%" /DisableStartupDialogs /LoadExternalDataProcessorOrReportFromFiles "<SrcDir>\<ReportName>.xml" "<OutDir>\<ReportName>.erf" /Out "<OutDir>\build.log"
|
||||
```
|
||||
|
||||
## Коды возврата
|
||||
|
||||
| Код | Описание |
|
||||
|-----|-----------------------------|
|
||||
| 0 | Успешная сборка |
|
||||
| 1 | Ошибка (см. лог) |
|
||||
|
||||
## Автоопределение платформы (Windows)
|
||||
|
||||
Если `V8_PATH` не задан, можно найти автоматически:
|
||||
|
||||
```powershell
|
||||
$v8 = Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort-Object -Descending | Select-Object -First 1
|
||||
```
|
||||
|
||||
## Ссылочные типы и выбор базы
|
||||
|
||||
Пустая ИБ (`V8_BASE`) подходит для сборки, если формы используют только базовые типы (`xs:string`, `xs:boolean` и т.д.) или тип самого отчёта (`ExternalReportObject.Имя`).
|
||||
|
||||
Если отчёт использует ссылочные типы конфигурации (`CatalogRef.XXX`, `DocumentRef.XXX` и т.д.) — в реквизитах объекта, табличных частях или реквизитах форм — **сборка в пустой базе упадёт** с ошибкой XDTO. Платформа не может резолвить типы, отсутствующие в конфигурации базы.
|
||||
|
||||
**Решение**: собирать в базе с целевой конфигурацией. Если конфигурация неизвестна — спросить пользователя путь к базе.
|
||||
|
||||
## Пример полного цикла
|
||||
|
||||
```powershell
|
||||
$env:V8_PATH = "C:\Program Files\1cv8\8.3.25.1257\bin"
|
||||
$env:V8_BASE = ".\base"
|
||||
|
||||
# Создать ИБ
|
||||
& "$env:V8_PATH\1cv8.exe" CREATEINFOBASE "File=$env:V8_BASE"
|
||||
|
||||
# Собрать
|
||||
& "$env:V8_PATH\1cv8.exe" DESIGNER /F $env:V8_BASE /DisableStartupDialogs /LoadExternalDataProcessorOrReportFromFiles "src\МойОтчёт.xml" "build\МойОтчёт.erf" /Out "build\build.log"
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: erf-dump
|
||||
description: Разобрать ERF-файл отчёта 1С в XML-исходники
|
||||
argument-hint: <ErfFile>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /erf-dump — Разборка отчёта
|
||||
|
||||
Разбирает ERF-файл во XML-исходники с помощью платформы 1С (иерархический формат). Использует ту же команду CLI, что и `/epf-dump`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/erf-dump <ErfFile> [OutDir]
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|----------|:------------:|--------------|-------------------------------------|
|
||||
| ErfFile | да | — | Путь к ERF-файлу |
|
||||
| OutDir | нет | `src` | Каталог для выгрузки исходников |
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
| Переменная | Описание | Пример |
|
||||
|------------|---------------------------------------|-----------------------------------------------|
|
||||
| V8_PATH | Каталог bin платформы 1С | `C:\Program Files\1cv8\8.3.25.1257\bin` |
|
||||
| V8_BASE | Путь к пустой файловой ИБ | `.\base` |
|
||||
|
||||
## Команды
|
||||
|
||||
### 1. Создать пустую ИБ (если нет)
|
||||
|
||||
```cmd
|
||||
"%V8_PATH%\1cv8.exe" CREATEINFOBASE File="%V8_BASE%"
|
||||
```
|
||||
|
||||
### 2. Разборка ERF в XML
|
||||
|
||||
```cmd
|
||||
"%V8_PATH%\1cv8.exe" DESIGNER /F "%V8_BASE%" /DisableStartupDialogs /DumpExternalDataProcessorOrReportToFiles "<OutDir>" "<ErfFile>" -Format Hierarchical /Out "<OutDir>\dump.log"
|
||||
```
|
||||
|
||||
## Коды возврата
|
||||
|
||||
| Код | Описание |
|
||||
|-----|-----------------------------|
|
||||
| 0 | Успешная разборка |
|
||||
| 1 | Ошибка (см. лог) |
|
||||
|
||||
## Формат `-Format Hierarchical`
|
||||
|
||||
Ключ `-Format Hierarchical` создаёт структуру каталогов:
|
||||
|
||||
```
|
||||
<OutDir>/
|
||||
├── <Name>.xml # Корневой файл
|
||||
└── <Name>/
|
||||
├── Ext/
|
||||
│ └── ObjectModule.bsl # Модуль объекта (если есть)
|
||||
├── Forms/
|
||||
│ ├── <FormName>.xml
|
||||
│ └── <FormName>/
|
||||
│ └── Ext/
|
||||
│ ├── Form.xml
|
||||
│ └── Form/
|
||||
│ └── Module.bsl
|
||||
└── Templates/
|
||||
├── <TemplateName>.xml
|
||||
└── <TemplateName>/
|
||||
└── Ext/
|
||||
└── Template.<ext>
|
||||
```
|
||||
|
||||
## Пример полного цикла
|
||||
|
||||
```powershell
|
||||
$env:V8_PATH = "C:\Program Files\1cv8\8.3.25.1257\bin"
|
||||
$env:V8_BASE = ".\base"
|
||||
|
||||
# Создать ИБ
|
||||
& "$env:V8_PATH\1cv8.exe" CREATEINFOBASE "File=$env:V8_BASE"
|
||||
|
||||
# Разобрать
|
||||
& "$env:V8_PATH\1cv8.exe" DESIGNER /F $env:V8_BASE /DisableStartupDialogs /DumpExternalDataProcessorOrReportToFiles "src" "build\МойОтчёт.erf" -Format Hierarchical /Out "build\dump.log"
|
||||
```
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: erf-init
|
||||
description: Создать пустой внешний отчёт 1С (scaffold XML-исходников)
|
||||
argument-hint: <Name> [Synonym] [--with-skd]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /erf-init — Создание нового отчёта
|
||||
|
||||
Генерирует минимальный набор XML-исходников для внешнего отчёта 1С: корневой файл метаданных и каталог отчёта.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/erf-init <Name> [Synonym] [SrcDir] [--with-skd]
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|-----------|:------------:|--------------|---------------------------------------|
|
||||
| Name | да | — | Имя отчёта (латиница/кириллица) |
|
||||
| Synonym | нет | = Name | Синоним (отображаемое имя) |
|
||||
| SrcDir | нет | `src` | Каталог исходников относительно CWD |
|
||||
| --WithSKD | нет | — | Создать пустую СКД и привязать к MainDataCompositionSchema |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\erf-init\scripts\init.ps1 -Name "<Name>" [-Synonym "<Synonym>"] [-SrcDir "<SrcDir>"] [-WithSKD]
|
||||
```
|
||||
|
||||
## Что создаётся
|
||||
|
||||
```
|
||||
<SrcDir>/
|
||||
├── <Name>.xml # Корневой файл метаданных (4 UUID)
|
||||
└── <Name>/
|
||||
└── Ext/
|
||||
└── ObjectModule.bsl # Модуль объекта с 3 регионами
|
||||
```
|
||||
|
||||
При `--WithSKD` дополнительно:
|
||||
|
||||
```
|
||||
<SrcDir>/<Name>/
|
||||
Templates/
|
||||
├── ОсновнаяСхемаКомпоновкиДанных.xml # Метаданные макета
|
||||
└── ОсновнаяСхемаКомпоновкиДанных/
|
||||
└── Ext/
|
||||
└── Template.xml # Пустая СКД
|
||||
```
|
||||
|
||||
- Корневой XML содержит `MetaDataObject/ExternalReport` с пустыми `DefaultForm`, `MainDataCompositionSchema` и `ChildObjects`
|
||||
- При `--WithSKD` — `MainDataCompositionSchema` заполняется ссылкой на макет, `ChildObjects` содержит `<Template>`
|
||||
- ClassId фиксирован: `e41aff26-25cf-4bb6-b6c1-3f478a75f374`
|
||||
- Файл создаётся в UTF-8 с BOM
|
||||
|
||||
## Дальнейшие шаги
|
||||
|
||||
- Добавить форму: `/form-add`
|
||||
- Добавить макет: `/template-add`
|
||||
- Добавить справку: `/help-add`
|
||||
- Собрать ERF: `/erf-build`
|
||||
@@ -0,0 +1,178 @@
|
||||
# erf-init v1.0 — Init 1C external report scaffold
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Name,
|
||||
|
||||
[string]$Synonym = $Name,
|
||||
|
||||
[string]$SrcDir = "src",
|
||||
|
||||
[switch]$WithSKD
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$uuid1 = [guid]::NewGuid().ToString()
|
||||
$uuid2 = [guid]::NewGuid().ToString()
|
||||
$uuid3 = [guid]::NewGuid().ToString()
|
||||
$uuid4 = [guid]::NewGuid().ToString()
|
||||
|
||||
# --- Формируем Properties ---
|
||||
|
||||
$mainDCSValue = ""
|
||||
$childObjectsContent = ""
|
||||
|
||||
if ($WithSKD) {
|
||||
$mainDCSValue = "ExternalReport.$Name.Template.ОсновнаяСхемаКомпоновкиДанных"
|
||||
$childObjectsContent = @"
|
||||
|
||||
<Template>ОсновнаяСхемаКомпоновкиДанных</Template>
|
||||
|
||||
"@
|
||||
}
|
||||
|
||||
$mainDCSElement = if ($mainDCSValue) {
|
||||
"<MainDataCompositionSchema>$mainDCSValue</MainDataCompositionSchema>"
|
||||
} else {
|
||||
"<MainDataCompositionSchema/>"
|
||||
}
|
||||
|
||||
$childObjectsXml = if ($childObjectsContent) {
|
||||
"<ChildObjects>$childObjectsContent</ChildObjects>"
|
||||
} else {
|
||||
"<ChildObjects/>"
|
||||
}
|
||||
|
||||
$xml = @"
|
||||
<?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">
|
||||
<ExternalReport uuid="$uuid1">
|
||||
<InternalInfo>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>e41aff26-25cf-4bb6-b6c1-3f478a75f374</xr:ClassId>
|
||||
<xr:ObjectId>$uuid2</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:GeneratedType name="ExternalReportObject.$Name" category="Object">
|
||||
<xr:TypeId>$uuid3</xr:TypeId>
|
||||
<xr:ValueId>$uuid4</xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>$Name</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>$Synonym</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
<DefaultForm/>
|
||||
<AuxiliaryForm/>
|
||||
$mainDCSElement
|
||||
<DefaultSettingsForm/>
|
||||
<AuxiliarySettingsForm/>
|
||||
<DefaultVariantForm/>
|
||||
<VariantsStorage/>
|
||||
<SettingsStorage/>
|
||||
</Properties>
|
||||
$childObjectsXml
|
||||
</ExternalReport>
|
||||
</MetaDataObject>
|
||||
"@
|
||||
|
||||
$rootFile = Join-Path $SrcDir "$Name.xml"
|
||||
$reportDir = Join-Path $SrcDir $Name
|
||||
|
||||
if (Test-Path $rootFile) {
|
||||
Write-Error "Файл уже существует: $rootFile"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path $SrcDir)) {
|
||||
New-Item -ItemType Directory -Path $SrcDir -Force | Out-Null
|
||||
}
|
||||
$extDir = Join-Path $reportDir "Ext"
|
||||
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
|
||||
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText((Resolve-Path $SrcDir | Join-Path -ChildPath "$Name.xml"), $xml, $enc)
|
||||
|
||||
# --- Модуль объекта ---
|
||||
|
||||
$moduleBsl = @"
|
||||
#Область ОписаниеПеременных
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область ПрограммныйИнтерфейс
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область СлужебныеПроцедурыИФункции
|
||||
|
||||
#КонецОбласти
|
||||
"@
|
||||
|
||||
$modulePath = Join-Path $extDir "ObjectModule.bsl"
|
||||
[System.IO.File]::WriteAllText($modulePath, $moduleBsl, $enc)
|
||||
|
||||
Write-Host "[OK] Создан отчёт: $rootFile"
|
||||
Write-Host " Каталог: $reportDir"
|
||||
Write-Host " Модуль: $modulePath"
|
||||
|
||||
# --- СКД-макет (если --WithSKD) ---
|
||||
|
||||
if ($WithSKD) {
|
||||
$templatesDir = Join-Path $reportDir "Templates"
|
||||
$skdName = "ОсновнаяСхемаКомпоновкиДанных"
|
||||
$skdMetaPath = Join-Path $templatesDir "$skdName.xml"
|
||||
$skdExtDir = Join-Path (Join-Path $templatesDir $skdName) "Ext"
|
||||
New-Item -ItemType Directory -Path $skdExtDir -Force | Out-Null
|
||||
|
||||
$skdUuid = [guid]::NewGuid().ToString()
|
||||
|
||||
$skdMetaXml = @"
|
||||
<?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">
|
||||
<Template uuid="$skdUuid">
|
||||
<Properties>
|
||||
<Name>$skdName</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основная схема компоновки данных</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
<TemplateType>DataCompositionSchema</TemplateType>
|
||||
</Properties>
|
||||
</Template>
|
||||
</MetaDataObject>
|
||||
"@
|
||||
|
||||
[System.IO.File]::WriteAllText($skdMetaPath, $skdMetaXml, $enc)
|
||||
|
||||
$skdContent = @"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
</DataCompositionSchema>
|
||||
"@
|
||||
|
||||
$skdFilePath = Join-Path $skdExtDir "Template.xml"
|
||||
[System.IO.File]::WriteAllText($skdFilePath, $skdContent, $enc)
|
||||
|
||||
Write-Host " СКД: $skdMetaPath"
|
||||
Write-Host " Тело: $skdFilePath"
|
||||
}
|
||||
@@ -39,7 +39,7 @@ powershell.exe -NoProfile -File .claude\skills\form-add\scripts\form-add.ps1 -Ob
|
||||
|
||||
| Purpose | Допустимые типы объектов | Основной реквизит | DefaultForm-свойство |
|
||||
|---------|-------------------------|-------------------|---------------------|
|
||||
| Object | Document, Catalog, DataProcessor, Report, ChartOf*, ExchangePlan, BusinessProcess, Task | Объект (тип: *Object.Имя) | DefaultObjectForm (DefaultForm для DataProcessor/Report) |
|
||||
| Object | Document, Catalog, DataProcessor, Report, ExternalDataProcessor, ExternalReport, ChartOf*, ExchangePlan, BusinessProcess, Task | Объект (тип: *Object.Имя) | DefaultObjectForm (DefaultForm для DataProcessor/Report/ExternalDataProcessor/ExternalReport) |
|
||||
| List | Все кроме DataProcessor | Список (DynamicList) | DefaultListForm |
|
||||
| Choice | Document, Catalog, ChartOf*, ExchangePlan, BusinessProcess, Task | Список (DynamicList) | DefaultChoiceForm |
|
||||
| Record | InformationRegister | Запись (InformationRegisterRecordManager) | DefaultRecordForm |
|
||||
@@ -62,7 +62,7 @@ powershell.exe -NoProfile -File .claude\skills\form-add\scripts\form-add.ps1 -Ob
|
||||
|
||||
## Поддерживаемые типы объектов
|
||||
|
||||
Document, Catalog, DataProcessor, Report, InformationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ExchangePlan, BusinessProcess, Task
|
||||
Document, Catalog, DataProcessor, Report, ExternalDataProcessor, ExternalReport, InformationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ExchangePlan, BusinessProcess, Task
|
||||
|
||||
## Примеры
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ if (-not $metaDataObject) {
|
||||
|
||||
$supportedTypes = @(
|
||||
"Document", "Catalog", "DataProcessor", "Report",
|
||||
"ExternalDataProcessor", "ExternalReport",
|
||||
"InformationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes",
|
||||
"ExchangePlan", "BusinessProcess", "Task"
|
||||
)
|
||||
@@ -89,7 +90,7 @@ switch ($Purpose) {
|
||||
}
|
||||
|
||||
$objectLikeTypes = @("Document", "Catalog", "ChartOfAccounts", "ChartOfCharacteristicTypes", "ExchangePlan", "BusinessProcess", "Task")
|
||||
$processorLikeTypes = @("DataProcessor", "Report")
|
||||
$processorLikeTypes = @("DataProcessor", "Report", "ExternalDataProcessor", "ExternalReport")
|
||||
|
||||
switch ($Purpose) {
|
||||
"Object" {
|
||||
@@ -235,6 +236,8 @@ if ($Purpose -eq "List" -or $Purpose -eq "Choice") {
|
||||
"Catalog" = "CatalogObject"
|
||||
"DataProcessor" = "DataProcessorObject"
|
||||
"Report" = "ReportObject"
|
||||
"ExternalDataProcessor" = "ExternalDataProcessorObject"
|
||||
"ExternalReport" = "ExternalReportObject"
|
||||
"ChartOfAccounts" = "ChartOfAccountsObject"
|
||||
"ChartOfCharacteristicTypes" = "ChartOfCharacteristicTypesObject"
|
||||
"ExchangePlan" = "ExchangePlanObject"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: form-compile
|
||||
description: Компиляция управляемой формы 1С (Form.xml) из компактного JSON-определения
|
||||
description: Компиляция управляемой формы 1С из компактного JSON-определения. Используй когда нужно создать форму с нуля по описанию элементов
|
||||
argument-hint: <JsonPath> <OutputPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: form-edit
|
||||
description: Добавление элементов, реквизитов и команд в существующую управляемую форму 1С (Form.xml)
|
||||
description: Добавление элементов, реквизитов и команд в существующую управляемую форму 1С. Используй когда нужно точечно модифицировать готовую форму
|
||||
argument-hint: <FormPath> <JsonPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: form-info
|
||||
description: Анализ структуры управляемой формы 1С (Form.xml) — элементы, реквизиты, команды, события
|
||||
description: Анализ структуры управляемой формы 1С (Form.xml) — элементы, реквизиты, команды, события. Используй для понимания формы — при написании модуля формы, анализе обработчиков и элементов
|
||||
argument-hint: <FormPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: form-patterns
|
||||
description: Справочник паттернов компоновки управляемых форм 1С — архетипы, конвенции именования, продвинутые приёмы
|
||||
description: Справочник паттернов компоновки управляемых форм 1С. Используй как справочник при проектировании форм — архетипы, конвенции, продвинутые приёмы
|
||||
argument-hint: (no arguments)
|
||||
allowed-tools: []
|
||||
---
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: form-remove
|
||||
description: Удалить форму из объекта 1С (обработка, отчёт, справочник, документ и др.)
|
||||
argument-hint: <ObjectName> <FormName>
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /form-remove — Удаление формы
|
||||
|
||||
Удаляет форму и убирает её регистрацию из корневого XML объекта.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/form-remove <ObjectName> <FormName>
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|------------|:------------:|--------------|-------------------------------------|
|
||||
| ObjectName | да | — | Имя объекта |
|
||||
| FormName | да | — | Имя формы для удаления |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\form-remove\scripts\remove-form.ps1 -ObjectName "<ObjectName>" -FormName "<FormName>" [-SrcDir "<SrcDir>"]
|
||||
```
|
||||
|
||||
## Что удаляется
|
||||
|
||||
```
|
||||
<SrcDir>/<ObjectName>/Forms/<FormName>.xml # Метаданные формы
|
||||
<SrcDir>/<ObjectName>/Forms/<FormName>/ # Каталог формы (рекурсивно)
|
||||
```
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- `<SrcDir>/<ObjectName>.xml` — убирается `<Form>` из `ChildObjects`
|
||||
- Если удаляемая форма была DefaultForm — очищается значение DefaultForm
|
||||
+5
-4
@@ -1,8 +1,9 @@
|
||||
# epf-remove-form v1.0 — Remove form from 1C processor
|
||||
# form-remove v1.1 — Remove form from 1C object
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ProcessorName,
|
||||
[Alias("ProcessorName")]
|
||||
[string]$ObjectName,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$FormName,
|
||||
@@ -14,13 +15,13 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
# --- Проверки ---
|
||||
|
||||
$rootXmlPath = Join-Path $SrcDir "$ProcessorName.xml"
|
||||
$rootXmlPath = Join-Path $SrcDir "$ObjectName.xml"
|
||||
if (-not (Test-Path $rootXmlPath)) {
|
||||
Write-Error "Корневой файл обработки не найден: $rootXmlPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$processorDir = Join-Path $SrcDir $ProcessorName
|
||||
$processorDir = Join-Path $SrcDir $ObjectName
|
||||
$formsDir = Join-Path $processorDir "Forms"
|
||||
$formMetaPath = Join-Path $formsDir "$FormName.xml"
|
||||
$formDir = Join-Path $formsDir $FormName
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: form-validate
|
||||
description: Валидация структурной корректности управляемой формы 1С (Form.xml)
|
||||
description: Валидация управляемой формы 1С. Используй после создания или модификации формы для проверки корректности
|
||||
argument-hint: <FormPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: epf-add-help
|
||||
description: Добавить встроенную справку к внешней обработке 1С
|
||||
argument-hint: <ProcessorName>
|
||||
name: help-add
|
||||
description: Добавить встроенную справку к объекту 1С (обработка, отчёт, справочник, документ и др.)
|
||||
argument-hint: <ObjectName>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -11,32 +11,32 @@ allowed-tools:
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /epf-add-help — Добавление справки
|
||||
# /help-add — Добавление справки
|
||||
|
||||
Добавляет встроенную справку к обработке: файл метаданных `Help.xml`, HTML-страницу и при необходимости обновляет метаданные форм.
|
||||
Добавляет встроенную справку к объекту: файл метаданных `Help.xml`, HTML-страницу и при необходимости обновляет метаданные форм.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/epf-add-help <ProcessorName> [Lang] [SrcDir]
|
||||
/help-add <ObjectName> [Lang] [SrcDir]
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|---------------|:------------:|--------------|-------------------------------------|
|
||||
| ProcessorName | да | — | Имя обработки |
|
||||
| Lang | нет | `ru` | Код языка справки |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|------------|:------------:|--------------|-------------------------------------|
|
||||
| ObjectName | да | — | Имя объекта |
|
||||
| Lang | нет | `ru` | Код языка справки |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude/skills/epf-add-help/scripts/add-help.ps1 -ProcessorName "<ProcessorName>" [-Lang "<Lang>"] [-SrcDir "<SrcDir>"]
|
||||
powershell.exe -NoProfile -File .claude\skills\help-add\scripts\add-help.ps1 -ObjectName "<ObjectName>" [-Lang "<Lang>"] [-SrcDir "<SrcDir>"]
|
||||
```
|
||||
|
||||
## Что создаётся
|
||||
|
||||
```
|
||||
<SrcDir>/<ProcessorName>/
|
||||
<SrcDir>/<ObjectName>/
|
||||
Ext/
|
||||
Help.xml # Метаданные справки (namespace extrnprops)
|
||||
Help/
|
||||
@@ -49,13 +49,13 @@ powershell.exe -NoProfile -File .claude/skills/epf-add-help/scripts/add-help.ps1
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- Если в метаданных формы (`Forms/<FormName>.xml`) отсутствует `<IncludeHelpInContents>` — скрипт добавит `<IncludeHelpInContents>false</IncludeHelpInContents>` после `<FormType>`. Для форм, созданных через `/epf-add-form`, элемент уже есть.
|
||||
- Если в метаданных формы (`Forms/<FormName>.xml`) отсутствует `<IncludeHelpInContents>` — скрипт добавит `<IncludeHelpInContents>false</IncludeHelpInContents>` после `<FormType>`. Для форм, созданных через `/form-add`, элемент уже есть.
|
||||
|
||||
## Кнопка справки на форме
|
||||
|
||||
После создания справки для её вызова нужна кнопка на форме. Добавь кнопку `Form.StandardCommand.Help` в AutoCommandBar формы (`Forms/<FormName>/Ext/Form.xml`).
|
||||
|
||||
### Текущая структура AutoCommandBar (созданная epf-add-form)
|
||||
### Текущая структура AutoCommandBar (созданная form-add)
|
||||
|
||||
```xml
|
||||
<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">
|
||||
@@ -89,4 +89,4 @@ powershell.exe -NoProfile -File .claude/skills/epf-add-help/scripts/add-help.ps1
|
||||
|
||||
## Редактирование справки
|
||||
|
||||
После создания содержимое справки — обычный HTML. Отредактируй `Ext/Help/ru.html` в соответствии с назначением обработки. Поддерживается стандартная HTML-разметка: `<h1>`..`<h4>`, `<p>`, `<ul>`, `<ol>`, `<table>`, `<strong>`, `<em>`, `<a>`, `<pre>`.
|
||||
После создания содержимое справки — обычный HTML. Отредактируй `Ext/Help/ru.html` в соответствии с назначением объекта. Поддерживается стандартная HTML-разметка: `<h1>`..`<h4>`, `<p>`, `<ul>`, `<ol>`, `<table>`, `<strong>`, `<em>`, `<a>`, `<pre>`.
|
||||
+6
-5
@@ -1,8 +1,9 @@
|
||||
# epf-add-help v1.0 — Add built-in help to 1C processor
|
||||
# help-add v1.1 — Add built-in help to 1C object
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ProcessorName,
|
||||
[Alias("ProcessorName")]
|
||||
[string]$ObjectName,
|
||||
|
||||
[string]$Lang = "ru",
|
||||
|
||||
@@ -13,7 +14,7 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
# --- Проверки ---
|
||||
|
||||
$processorDir = Join-Path $SrcDir $ProcessorName
|
||||
$processorDir = Join-Path $SrcDir $ObjectName
|
||||
$extDir = Join-Path $processorDir "Ext"
|
||||
|
||||
if (-not (Test-Path $extDir)) {
|
||||
@@ -57,7 +58,7 @@ $helpHtml = @"
|
||||
<link rel="stylesheet" type="text/css" href="v8help://service_book/service_style"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>$ProcessorName</h1>
|
||||
<h1>$ObjectName</h1>
|
||||
<p>Описание обработки.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -112,6 +113,6 @@ if (Test-Path $formsDir) {
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[OK] Создана справка: $ProcessorName"
|
||||
Write-Host "[OK] Создана справка: $ObjectName"
|
||||
Write-Host " Метаданные: $helpXmlPath"
|
||||
Write-Host " Страница: $helpHtmlPath"
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: interface-edit
|
||||
description: Настройка командного интерфейса подсистемы 1С. Используй когда нужно скрыть или показать команды, разместить в группах, настроить порядок
|
||||
argument-hint: <CIPath> <Operation> <Value>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /interface-edit — редактирование CommandInterface.xml
|
||||
|
||||
Операции: hide, show, place, order, subsystem-order, group-order. Подробнее: `.claude/skills/interface-edit/reference.md`
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File '.claude\skills\interface-edit\scripts\interface-edit.ps1' -CIPath '<path>' -Operation hide -Value '<cmd>'
|
||||
```
|
||||
@@ -0,0 +1,48 @@
|
||||
# /interface-edit — редактирование CommandInterface.xml
|
||||
|
||||
Точечное редактирование файла командного интерфейса подсистемы 1С.
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| CIPath | Путь к CommandInterface.xml |
|
||||
| DefinitionFile | JSON-файл с массивом операций |
|
||||
| Operation | Одна операция: hide, show, place, order, subsystem-order, group-order |
|
||||
| Value | Значение для операции |
|
||||
| CreateIfMissing | Создать файл если не существует |
|
||||
| NoValidate | Пропустить авто-валидацию |
|
||||
|
||||
## Операции
|
||||
|
||||
| Операция | Значение | Описание |
|
||||
|----------|----------|----------|
|
||||
| hide | Cmd.Name или массив | Скрыть команду (CommandsVisibility, false) |
|
||||
| show | Cmd.Name или массив | Показать команду (visibility, true) |
|
||||
| place | {"command":"...","group":"CommandGroup.X"} | Разместить команду в группе |
|
||||
| order | {"group":"...","commands":[...]} | Задать порядок команд в группе |
|
||||
| subsystem-order | ["Subsystem.X.Subsystem.A",...] | Порядок дочерних подсистем |
|
||||
| group-order | ["NavigationPanelOrdinary",...] | Порядок групп |
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Скрыть команду
|
||||
... -CIPath Subsystems/Продажи/Ext/CommandInterface.xml -Operation hide -Value "Catalog.Товары.StandardCommand.OpenList"
|
||||
|
||||
# Показать команду
|
||||
... -Operation show -Value "Report.Продажи.Command.Отчёт"
|
||||
|
||||
# Разместить в группе
|
||||
... -Operation place -Value '{"command":"Report.X.Command.Y","group":"CommandGroup.Отчеты"}'
|
||||
|
||||
# Задать порядок подсистем
|
||||
... -Operation subsystem-order -Value '["Subsystem.X.Subsystem.A","Subsystem.X.Subsystem.B"]'
|
||||
|
||||
# Создать новый CI
|
||||
... -CIPath <new-path> -Operation subsystem-order -Value '[...]' -CreateIfMissing
|
||||
```
|
||||
|
||||
## Авто-валидация
|
||||
|
||||
После каждой операции автоматически запускается `/interface-validate`. Подавить: `-NoValidate`.
|
||||
@@ -0,0 +1,490 @@
|
||||
# interface-edit v1.0 — Edit 1C CommandInterface.xml
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$CIPath,
|
||||
[string]$DefinitionFile,
|
||||
[ValidateSet("hide","show","place","order","subsystem-order","group-order")]
|
||||
[string]$Operation,
|
||||
[string]$Value,
|
||||
[switch]$CreateIfMissing,
|
||||
[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($CIPath)) {
|
||||
$CIPath = Join-Path (Get-Location).Path $CIPath
|
||||
}
|
||||
$resolvedPath = $CIPath
|
||||
|
||||
# --- Namespaces ---
|
||||
$script:ciNs = "http://v8.1c.ru/8.3/xcf/extrnprops"
|
||||
$script:xrNs = "http://v8.1c.ru/8.3/xcf/readable"
|
||||
$script:xsiNs = "http://www.w3.org/2001/XMLSchema-instance"
|
||||
$script:xsNs = "http://www.w3.org/2001/XMLSchema"
|
||||
|
||||
# --- Create if missing ---
|
||||
if (-not (Test-Path $CIPath)) {
|
||||
if ($CreateIfMissing) {
|
||||
$parentDir = [System.IO.Path]::GetDirectoryName($CIPath)
|
||||
if (-not (Test-Path $parentDir)) {
|
||||
New-Item -ItemType Directory -Path $parentDir -Force | Out-Null
|
||||
}
|
||||
$emptyCI = @"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CommandInterface xmlns="$($script:ciNs)"
|
||||
xmlns:xr="$($script:xrNs)"
|
||||
xmlns:xs="$($script:xsNs)"
|
||||
xmlns:xsi="$($script:xsiNs)"
|
||||
version="2.17">
|
||||
</CommandInterface>
|
||||
"@
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($CIPath, $emptyCI, $utf8Bom)
|
||||
Write-Host "[INFO] Created new CommandInterface.xml: $CIPath"
|
||||
} else {
|
||||
Write-Error "File not found: $CIPath (use -CreateIfMissing to create)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
$resolvedPath = (Resolve-Path $CIPath).Path
|
||||
|
||||
# --- Load XML ---
|
||||
$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
|
||||
if ($root.LocalName -ne "CommandInterface") {
|
||||
Write-Error "Expected <CommandInterface> root element, got <$($root.LocalName)>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Section canonical order
|
||||
$script:sectionOrder = @("CommandsVisibility","CommandsPlacement","CommandsOrder","SubsystemsOrder","GroupsOrder")
|
||||
|
||||
# --- XML manipulation helpers ---
|
||||
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 Import-CIFragment([string]$xmlString) {
|
||||
$wrapper = "<_W xmlns=`"$($script:ciNs)`" xmlns:xr=`"$($script:xrNs)`" xmlns:xsi=`"$($script:xsiNs)`" xmlns:xs=`"$($script:xsNs)`">$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
|
||||
}
|
||||
|
||||
# --- Ensure section exists, creating it in correct order if needed ---
|
||||
function Ensure-Section([string]$sectionName) {
|
||||
# Find existing
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $sectionName) {
|
||||
return $child
|
||||
}
|
||||
}
|
||||
|
||||
# Create new section
|
||||
$newSection = $script:xmlDoc.CreateElement($sectionName, $script:ciNs)
|
||||
|
||||
# Find the correct insertion point: before the first section that comes AFTER us in canonical order
|
||||
$myIdx = [array]::IndexOf($script:sectionOrder, $sectionName)
|
||||
$refNode = $null
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
$childIdx = [array]::IndexOf($script:sectionOrder, $child.LocalName)
|
||||
if ($childIdx -gt $myIdx) {
|
||||
# Find the whitespace before this element to insert before it
|
||||
$prev = $child.PreviousSibling
|
||||
if ($prev -and ($prev.NodeType -eq 'Whitespace' -or $prev.NodeType -eq 'SignificantWhitespace')) {
|
||||
$refNode = $prev
|
||||
} else {
|
||||
$refNode = $child
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
$rootIndent = Get-ChildIndent $root
|
||||
# Add closing whitespace inside the new section
|
||||
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$rootIndent")
|
||||
$newSection.AppendChild($closeWs) | Out-Null
|
||||
|
||||
if ($refNode) {
|
||||
$ws = $script:xmlDoc.CreateWhitespace("`r`n$rootIndent")
|
||||
$root.InsertBefore($ws, $refNode) | Out-Null
|
||||
$root.InsertBefore($newSection, $ws) | Out-Null
|
||||
} else {
|
||||
Insert-BeforeElement $root $newSection $null $rootIndent
|
||||
}
|
||||
return $newSection
|
||||
}
|
||||
|
||||
# --- Parse value: string or JSON array ---
|
||||
function Parse-ValueList([string]$val) {
|
||||
$val = $val.Trim()
|
||||
if ($val.StartsWith("[")) {
|
||||
$arr = $val | ConvertFrom-Json
|
||||
$result = @(); foreach ($item in $arr) { $result += "$item" }
|
||||
return ,$result
|
||||
}
|
||||
return @($val)
|
||||
}
|
||||
|
||||
# --- Find Command element by name in a section ---
|
||||
function Find-CommandByName($section, [string]$cmdName) {
|
||||
foreach ($child in $section.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Command") {
|
||||
if ($child.GetAttribute("name") -eq $cmdName) { return $child }
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# --- Operations ---
|
||||
|
||||
function Do-Hide([string[]]$commands) {
|
||||
$section = Ensure-Section "CommandsVisibility"
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
|
||||
foreach ($cmd in $commands) {
|
||||
$existing = Find-CommandByName $section $cmd
|
||||
if ($existing) {
|
||||
# Check if already false
|
||||
$commonEl = $null
|
||||
foreach ($vis in $existing.ChildNodes) {
|
||||
if ($vis.NodeType -eq 'Element' -and $vis.LocalName -eq "Visibility") {
|
||||
foreach ($c in $vis.ChildNodes) {
|
||||
if ($c.NodeType -eq 'Element' -and $c.LocalName -eq "Common") { $commonEl = $c; break }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($commonEl -and $commonEl.InnerText.Trim() -eq "false") {
|
||||
Warn "Already hidden: $cmd"
|
||||
continue
|
||||
}
|
||||
# Change true -> false
|
||||
if ($commonEl) {
|
||||
$commonEl.InnerText = "false"
|
||||
$script:modifyCount++
|
||||
Info "Changed to hidden: $cmd"
|
||||
continue
|
||||
}
|
||||
}
|
||||
# Add new entry
|
||||
$fragXml = "<Command name=`"$cmd`"><Visibility><xr:Common>false</xr:Common></Visibility></Command>"
|
||||
$nodes = Import-CIFragment $fragXml
|
||||
if ($nodes.Count -gt 0) {
|
||||
Insert-BeforeElement $section $nodes[0] $null $sectionIndent
|
||||
$script:addCount++
|
||||
Info "Hidden: $cmd"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Do-Show([string[]]$commands) {
|
||||
$section = $null
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "CommandsVisibility") {
|
||||
$section = $child; break
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cmd in $commands) {
|
||||
if (-not $section) {
|
||||
# No CommandsVisibility section — showing means adding with true
|
||||
$section = Ensure-Section "CommandsVisibility"
|
||||
}
|
||||
$existing = Find-CommandByName $section $cmd
|
||||
if ($existing) {
|
||||
$commonEl = $null
|
||||
foreach ($vis in $existing.ChildNodes) {
|
||||
if ($vis.NodeType -eq 'Element' -and $vis.LocalName -eq "Visibility") {
|
||||
foreach ($c in $vis.ChildNodes) {
|
||||
if ($c.NodeType -eq 'Element' -and $c.LocalName -eq "Common") { $commonEl = $c; break }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($commonEl -and $commonEl.InnerText.Trim() -eq "true") {
|
||||
Warn "Already shown: $cmd"
|
||||
continue
|
||||
}
|
||||
if ($commonEl -and $commonEl.InnerText.Trim() -eq "false") {
|
||||
# Change false -> true
|
||||
$commonEl.InnerText = "true"
|
||||
$script:modifyCount++
|
||||
Info "Changed to shown: $cmd"
|
||||
continue
|
||||
}
|
||||
}
|
||||
# Add new entry with true
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
$fragXml = "<Command name=`"$cmd`"><Visibility><xr:Common>true</xr:Common></Visibility></Command>"
|
||||
$nodes = Import-CIFragment $fragXml
|
||||
if ($nodes.Count -gt 0) {
|
||||
Insert-BeforeElement $section $nodes[0] $null $sectionIndent
|
||||
$script:addCount++
|
||||
Info "Shown: $cmd"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Do-Place([string]$jsonVal) {
|
||||
$def = $jsonVal | ConvertFrom-Json
|
||||
$cmdName = "$($def.command)"
|
||||
$groupName = "$($def.group)"
|
||||
if (-not $cmdName -or -not $groupName) { Write-Error "place requires {command, group}"; exit 1 }
|
||||
|
||||
$section = Ensure-Section "CommandsPlacement"
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
|
||||
# Check existing
|
||||
$existing = Find-CommandByName $section $cmdName
|
||||
if ($existing) {
|
||||
# Update group
|
||||
foreach ($child in $existing.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "CommandGroup") {
|
||||
$child.InnerText = $groupName
|
||||
$script:modifyCount++
|
||||
Info "Updated placement: $cmdName -> $groupName"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Add new
|
||||
$fragXml = "<Command name=`"$cmdName`"><CommandGroup>$groupName</CommandGroup><Placement>Auto</Placement></Command>"
|
||||
$nodes = Import-CIFragment $fragXml
|
||||
if ($nodes.Count -gt 0) {
|
||||
Insert-BeforeElement $section $nodes[0] $null $sectionIndent
|
||||
$script:addCount++
|
||||
Info "Placed: $cmdName -> $groupName"
|
||||
}
|
||||
}
|
||||
|
||||
function Do-Order([string]$jsonVal) {
|
||||
$def = $jsonVal | ConvertFrom-Json
|
||||
$groupName = "$($def.group)"
|
||||
$commands = @($def.commands | ForEach-Object { "$_" })
|
||||
if (-not $groupName -or $commands.Count -eq 0) { Write-Error "order requires {group, commands:[...]}"; exit 1 }
|
||||
|
||||
$section = Ensure-Section "CommandsOrder"
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
|
||||
# Remove existing entries for this group
|
||||
$toRemove = @()
|
||||
foreach ($child in $section.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
if ($child.LocalName -ne "Command") { continue }
|
||||
foreach ($gc in $child.ChildNodes) {
|
||||
if ($gc.NodeType -eq 'Element' -and $gc.LocalName -eq "CommandGroup" -and $gc.InnerText.Trim() -eq $groupName) {
|
||||
$toRemove += $child
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($node in $toRemove) {
|
||||
Remove-NodeWithWhitespace $node
|
||||
$script:removeCount++
|
||||
}
|
||||
|
||||
# Add new entries in order
|
||||
foreach ($cmdName in $commands) {
|
||||
$fragXml = "<Command name=`"$cmdName`"><CommandGroup>$groupName</CommandGroup></Command>"
|
||||
$nodes = Import-CIFragment $fragXml
|
||||
if ($nodes.Count -gt 0) {
|
||||
Insert-BeforeElement $section $nodes[0] $null $sectionIndent
|
||||
$script:addCount++
|
||||
}
|
||||
}
|
||||
Info "Set order for $groupName : $($commands.Count) commands"
|
||||
}
|
||||
|
||||
function Do-SubsystemOrder([string]$jsonVal) {
|
||||
$parsed = $jsonVal | ConvertFrom-Json
|
||||
$subsystems = @(); foreach ($s in $parsed) { $subsystems += "$s" }
|
||||
if ($subsystems.Count -eq 0) { Write-Error "subsystem-order requires array of subsystem paths"; exit 1 }
|
||||
|
||||
$section = Ensure-Section "SubsystemsOrder"
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
|
||||
# Clear existing
|
||||
$toRemove = @()
|
||||
foreach ($child in @($section.ChildNodes)) {
|
||||
if ($child.NodeType -eq 'Element') { $toRemove += $child }
|
||||
}
|
||||
foreach ($node in $toRemove) {
|
||||
Remove-NodeWithWhitespace $node
|
||||
$script:removeCount++
|
||||
}
|
||||
|
||||
# Add new entries
|
||||
foreach ($sub in $subsystems) {
|
||||
$newEl = $script:xmlDoc.CreateElement("Subsystem", $script:ciNs)
|
||||
$newEl.InnerText = $sub
|
||||
Insert-BeforeElement $section $newEl $null $sectionIndent
|
||||
$script:addCount++
|
||||
}
|
||||
Info "Set subsystem order: $($subsystems.Count) entries"
|
||||
}
|
||||
|
||||
function Do-GroupOrder([string]$jsonVal) {
|
||||
$parsed = $jsonVal | ConvertFrom-Json
|
||||
$groups = @(); foreach ($g in $parsed) { $groups += "$g" }
|
||||
if ($groups.Count -eq 0) { Write-Error "group-order requires array of group names"; exit 1 }
|
||||
|
||||
$section = Ensure-Section "GroupsOrder"
|
||||
$sectionIndent = Get-ChildIndent $section
|
||||
|
||||
# Clear existing
|
||||
$toRemove = @()
|
||||
foreach ($child in @($section.ChildNodes)) {
|
||||
if ($child.NodeType -eq 'Element') { $toRemove += $child }
|
||||
}
|
||||
foreach ($node in $toRemove) {
|
||||
Remove-NodeWithWhitespace $node
|
||||
$script:removeCount++
|
||||
}
|
||||
|
||||
# Add new entries
|
||||
foreach ($grp in $groups) {
|
||||
$newEl = $script:xmlDoc.CreateElement("Group", $script:ciNs)
|
||||
$newEl.InnerText = $grp
|
||||
Insert-BeforeElement $section $newEl $null $sectionIndent
|
||||
$script:addCount++
|
||||
}
|
||||
Info "Set group order: $($groups.Count) entries"
|
||||
}
|
||||
|
||||
# --- 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) {
|
||||
"hide" { Do-Hide (Parse-ValueList $opValue) }
|
||||
"show" { Do-Show (Parse-ValueList $opValue) }
|
||||
"place" { Do-Place $opValue }
|
||||
"order" { Do-Order $opValue }
|
||||
"subsystem-order" { Do-SubsystemOrder $opValue }
|
||||
"group-order" { Do-GroupOrder $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 "..\..\interface-validate") "scripts\interface-validate.ps1"
|
||||
$validateScript = [System.IO.Path]::GetFullPath($validateScript)
|
||||
if (Test-Path $validateScript) {
|
||||
Write-Host ""
|
||||
Write-Host "--- Running interface-validate ---"
|
||||
& powershell.exe -NoProfile -File $validateScript -CIPath $resolvedPath
|
||||
}
|
||||
}
|
||||
|
||||
# --- Summary ---
|
||||
Write-Host ""
|
||||
Write-Host "=== interface-edit summary ==="
|
||||
Write-Host " Added: $($script:addCount)"
|
||||
Write-Host " Removed: $($script:removeCount)"
|
||||
Write-Host " Modified: $($script:modifyCount)"
|
||||
exit 0
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: interface-validate
|
||||
description: Валидация командного интерфейса 1С. Используй после настройки командного интерфейса подсистемы для проверки корректности
|
||||
argument-hint: <CIPath> [-MaxErrors 30]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /interface-validate — валидация CommandInterface.xml
|
||||
|
||||
Проверяет XML командного интерфейса из выгрузки конфигурации на структурные ошибки: корневой элемент, допустимые секции, порядок, формат ссылок на команды, дубликаты.
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|-----------|:------------:|--------------|------------------------------------|
|
||||
| CIPath | да | — | Путь к CommandInterface.xml |
|
||||
| MaxErrors | нет | 30 | Остановиться после N ошибок |
|
||||
| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File '.claude\skills\interface-validate\scripts\interface-validate.ps1' -CIPath '<path>'
|
||||
```
|
||||
|
||||
## Проверки (13)
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|----|--------------------------------------------------------------|-------------|
|
||||
| 1 | XML well-formedness + root element (CommandInterface, version, namespace) | ERROR |
|
||||
| 2 | Допустимые дочерние элементы (только 5 секций) | ERROR |
|
||||
| 3 | Порядок секций корректен | ERROR |
|
||||
| 4 | Нет дублирующихся секций | ERROR |
|
||||
| 5 | CommandsVisibility — Command.name + Visibility/xr:Common | ERROR |
|
||||
| 6 | CommandsVisibility — нет дубликатов по name | WARN |
|
||||
| 7 | CommandsPlacement — Command.name + CommandGroup + Placement | ERROR |
|
||||
| 8 | CommandsOrder — Command.name + CommandGroup | ERROR |
|
||||
| 9 | SubsystemsOrder — Subsystem непустой, формат Subsystem.X | ERROR |
|
||||
| 10 | SubsystemsOrder — нет дубликатов | WARN |
|
||||
| 11 | GroupsOrder — Group непустой | ERROR |
|
||||
| 12 | GroupsOrder — нет дубликатов | WARN |
|
||||
| 13 | Формат ссылок на команды | WARN |
|
||||
|
||||
## Вывод
|
||||
|
||||
```
|
||||
=== Validation: CommandInterface (Продажи) ===
|
||||
|
||||
[OK] 1. Root structure: CommandInterface, version 2.17, namespace valid
|
||||
[OK] 2. Child elements: 5 valid sections
|
||||
[OK] 3. Section order: correct
|
||||
[OK] 4. No duplicate sections
|
||||
[OK] 5. CommandsVisibility: 55 entries, all valid
|
||||
[OK] 6. CommandsVisibility: no duplicates
|
||||
[OK] 7. CommandsPlacement: 3 entries, all valid
|
||||
[OK] 8. CommandsOrder: 12 entries, all valid
|
||||
[OK] 9. SubsystemsOrder: 9 entries, all valid format
|
||||
[OK] 10. SubsystemsOrder: no duplicates
|
||||
[OK] 11. GroupsOrder: 7 entries, all valid
|
||||
[OK] 12. GroupsOrder: no duplicates
|
||||
[OK] 13. Command reference format: all valid
|
||||
---
|
||||
Errors: 0, Warnings: 0
|
||||
```
|
||||
|
||||
Код возврата: 0 = все проверки пройдены, 1 = есть ошибки.
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# CommandInterface подсистемы
|
||||
... -CIPath upload/acc_8.3.24/Subsystems/Продажи/Ext/CommandInterface.xml
|
||||
|
||||
# Корневой CommandInterface конфигурации
|
||||
... -CIPath upload/acc_8.3.24/Ext/CommandInterface.xml
|
||||
|
||||
# С лимитом ошибок
|
||||
... -CIPath <path> -MaxErrors 10
|
||||
```
|
||||
@@ -0,0 +1,382 @@
|
||||
# interface-validate v1.0 — Validate 1C CommandInterface.xml structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$CIPath,
|
||||
[int]$MaxErrors = 30,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve path ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($CIPath)) {
|
||||
$CIPath = Join-Path (Get-Location).Path $CIPath
|
||||
}
|
||||
if (-not (Test-Path $CIPath)) {
|
||||
Write-Host "[ERROR] File not found: $CIPath"
|
||||
exit 1
|
||||
}
|
||||
$resolvedPath = (Resolve-Path $CIPath).Path
|
||||
|
||||
# --- Derive context name from path ---
|
||||
$contextName = ""
|
||||
$parentParts = $resolvedPath -split '[/\\]'
|
||||
for ($i = 0; $i -lt $parentParts.Count; $i++) {
|
||||
if ($parentParts[$i] -eq "Subsystems" -and ($i + 1) -lt $parentParts.Count) {
|
||||
$contextName = $parentParts[$i + 1]
|
||||
}
|
||||
}
|
||||
if (-not $contextName) { $contextName = "Root" }
|
||||
|
||||
# --- Output infrastructure ---
|
||||
$script:errors = 0
|
||||
$script:warnings = 0
|
||||
$script:stopped = $false
|
||||
$script:output = New-Object System.Text.StringBuilder 8192
|
||||
$script:allCommandNames = @()
|
||||
|
||||
function Out-Line([string]$msg) { $script:output.AppendLine($msg) | Out-Null }
|
||||
function Report-OK([string]$msg) { Out-Line "[OK] $msg" }
|
||||
function Report-Error([string]$msg) {
|
||||
$script:errors++
|
||||
Out-Line "[ERROR] $msg"
|
||||
if ($script:errors -ge $MaxErrors) { $script:stopped = $true }
|
||||
}
|
||||
function Report-Warn([string]$msg) {
|
||||
$script:warnings++
|
||||
Out-Line "[WARN] $msg"
|
||||
}
|
||||
|
||||
Out-Line "=== Validation: CommandInterface ($contextName) ==="
|
||||
Out-Line ""
|
||||
|
||||
# --- Valid section names and order ---
|
||||
$validSections = @("CommandsVisibility","CommandsPlacement","CommandsOrder","SubsystemsOrder","GroupsOrder")
|
||||
|
||||
# Command reference patterns
|
||||
$stdCmdPattern = '^[A-Za-z]+\.[^\s\.]+\.StandardCommand\.\w+$'
|
||||
$customCmdPattern = '^[A-Za-z]+\.[^\s\.]+\.Command\.\w+$'
|
||||
$commonCmdPattern = '^CommonCommand\.\w+$'
|
||||
$uuidCmdPattern = '^0:[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}$'
|
||||
|
||||
# --- 1. XML well-formedness + root structure ---
|
||||
$xmlDoc = $null
|
||||
try {
|
||||
[xml]$xmlDoc = Get-Content -Path $resolvedPath -Encoding UTF8
|
||||
} catch {
|
||||
Report-Error "1. XML parse error: $($_.Exception.Message)"
|
||||
$script:stopped = $true
|
||||
}
|
||||
|
||||
if (-not $script:stopped) {
|
||||
$root = $xmlDoc.DocumentElement
|
||||
|
||||
if ($root.LocalName -ne "CommandInterface") {
|
||||
Report-Error "1. Root element: expected <CommandInterface>, got <$($root.LocalName)>"
|
||||
$script:stopped = $true
|
||||
} else {
|
||||
$nsUri = $root.NamespaceURI
|
||||
$version = $root.GetAttribute("version")
|
||||
$expectedNs = "http://v8.1c.ru/8.3/xcf/extrnprops"
|
||||
if ($nsUri -ne $expectedNs) {
|
||||
Report-Error "1. Root namespace: expected $expectedNs, got $nsUri"
|
||||
} elseif (-not $version) {
|
||||
Report-Warn "1. Root structure: CommandInterface, namespace valid, but no version attribute"
|
||||
} else {
|
||||
Report-OK "1. Root structure: CommandInterface, version $version, namespace valid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Setup namespace manager ---
|
||||
$ns = $null
|
||||
if (-not $script:stopped) {
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
|
||||
$ns.AddNamespace("ci", "http://v8.1c.ru/8.3/xcf/extrnprops")
|
||||
$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")
|
||||
}
|
||||
|
||||
# --- 2. Valid child elements ---
|
||||
if (-not $script:stopped) {
|
||||
$root = $xmlDoc.DocumentElement
|
||||
$foundSections = @()
|
||||
$invalidElements = @()
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
if ($child.LocalName -in $validSections) {
|
||||
$foundSections += $child.LocalName
|
||||
} else {
|
||||
$invalidElements += $child.LocalName
|
||||
}
|
||||
}
|
||||
if ($invalidElements.Count -gt 0) {
|
||||
Report-Error "2. Invalid child elements: $($invalidElements -join ', ')"
|
||||
} else {
|
||||
Report-OK "2. Child elements: $($foundSections.Count) valid sections"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 3. Section order ---
|
||||
if (-not $script:stopped) {
|
||||
$orderOk = $true
|
||||
$lastIdx = -1
|
||||
foreach ($sec in $foundSections) {
|
||||
$idx = [array]::IndexOf($validSections, $sec)
|
||||
if ($idx -lt $lastIdx) {
|
||||
Report-Error "3. Section order: '$sec' appears after a later section (expected: CommandsVisibility -> CommandsPlacement -> CommandsOrder -> SubsystemsOrder -> GroupsOrder)"
|
||||
$orderOk = $false
|
||||
break
|
||||
}
|
||||
$lastIdx = $idx
|
||||
}
|
||||
if ($orderOk) { Report-OK "3. Section order: correct" }
|
||||
}
|
||||
|
||||
# --- 4. No duplicate sections ---
|
||||
if (-not $script:stopped) {
|
||||
$dupes = $foundSections | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Error "4. Duplicate sections: $dupeNames"
|
||||
} else {
|
||||
Report-OK "4. No duplicate sections"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 5. CommandsVisibility ---
|
||||
if (-not $script:stopped) {
|
||||
$visSection = $root.SelectSingleNode("ci:CommandsVisibility", $ns)
|
||||
if ($visSection) {
|
||||
$visOk = $true
|
||||
$visNames = @()
|
||||
$visCount = 0
|
||||
foreach ($cmd in $visSection.ChildNodes) {
|
||||
if ($cmd.NodeType -ne 'Element') { continue }
|
||||
$visCount++
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
if (-not $cmdName) {
|
||||
Report-Error "5. CommandsVisibility: Command element without 'name' attribute"
|
||||
$visOk = $false; continue
|
||||
}
|
||||
$visNames += $cmdName
|
||||
$script:allCommandNames += $cmdName
|
||||
$visibility = $cmd.SelectSingleNode("ci:Visibility", $ns)
|
||||
if (-not $visibility) {
|
||||
Report-Error "5. CommandsVisibility[$cmdName]: missing <Visibility>"
|
||||
$visOk = $false; continue
|
||||
}
|
||||
$common = $visibility.SelectSingleNode("xr:Common", $ns)
|
||||
if (-not $common) {
|
||||
Report-Error "5. CommandsVisibility[$cmdName]: missing <xr:Common>"
|
||||
$visOk = $false; continue
|
||||
}
|
||||
$val = $common.InnerText.Trim()
|
||||
if ($val -ne "true" -and $val -ne "false") {
|
||||
Report-Error "5. CommandsVisibility[$cmdName]: xr:Common='$val' (expected true/false)"
|
||||
$visOk = $false
|
||||
}
|
||||
}
|
||||
if ($visOk) { Report-OK "5. CommandsVisibility: $visCount entries, all valid" }
|
||||
} else {
|
||||
Report-OK "5. CommandsVisibility: not present"
|
||||
$visNames = @()
|
||||
}
|
||||
}
|
||||
|
||||
# --- 6. CommandsVisibility duplicates ---
|
||||
if (-not $script:stopped) {
|
||||
if ($visNames.Count -gt 0) {
|
||||
$dupes = $visNames | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Warn "6. CommandsVisibility: duplicates: $dupeNames"
|
||||
} else {
|
||||
Report-OK "6. CommandsVisibility: no duplicates"
|
||||
}
|
||||
} else {
|
||||
Report-OK "6. CommandsVisibility: no duplicates (empty)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 7. CommandsPlacement ---
|
||||
if (-not $script:stopped) {
|
||||
$plcSection = $root.SelectSingleNode("ci:CommandsPlacement", $ns)
|
||||
if ($plcSection) {
|
||||
$plcOk = $true
|
||||
$plcCount = 0
|
||||
foreach ($cmd in $plcSection.ChildNodes) {
|
||||
if ($cmd.NodeType -ne 'Element') { continue }
|
||||
$plcCount++
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
if (-not $cmdName) {
|
||||
Report-Error "7. CommandsPlacement: Command without 'name' attribute"
|
||||
$plcOk = $false; continue
|
||||
}
|
||||
$script:allCommandNames += $cmdName
|
||||
$grpEl = $cmd.SelectSingleNode("ci:CommandGroup", $ns)
|
||||
if (-not $grpEl -or -not $grpEl.InnerText.Trim()) {
|
||||
Report-Error "7. CommandsPlacement[$cmdName]: missing or empty <CommandGroup>"
|
||||
$plcOk = $false; continue
|
||||
}
|
||||
$placementEl = $cmd.SelectSingleNode("ci:Placement", $ns)
|
||||
if (-not $placementEl) {
|
||||
Report-Error "7. CommandsPlacement[$cmdName]: missing <Placement>"
|
||||
$plcOk = $false
|
||||
} elseif ($placementEl.InnerText.Trim() -ne "Auto") {
|
||||
Report-Warn "7. CommandsPlacement[$cmdName]: Placement='$($placementEl.InnerText.Trim())' (expected Auto)"
|
||||
}
|
||||
}
|
||||
if ($plcOk) { Report-OK "7. CommandsPlacement: $plcCount entries, all valid" }
|
||||
} else {
|
||||
Report-OK "7. CommandsPlacement: not present"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 8. CommandsOrder ---
|
||||
if (-not $script:stopped) {
|
||||
$ordSection = $root.SelectSingleNode("ci:CommandsOrder", $ns)
|
||||
if ($ordSection) {
|
||||
$ordOk = $true
|
||||
$ordCount = 0
|
||||
foreach ($cmd in $ordSection.ChildNodes) {
|
||||
if ($cmd.NodeType -ne 'Element') { continue }
|
||||
$ordCount++
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
if (-not $cmdName) {
|
||||
Report-Error "8. CommandsOrder: Command without 'name' attribute"
|
||||
$ordOk = $false; continue
|
||||
}
|
||||
$script:allCommandNames += $cmdName
|
||||
$grpEl = $cmd.SelectSingleNode("ci:CommandGroup", $ns)
|
||||
if (-not $grpEl -or -not $grpEl.InnerText.Trim()) {
|
||||
Report-Error "8. CommandsOrder[$cmdName]: missing or empty <CommandGroup>"
|
||||
$ordOk = $false
|
||||
}
|
||||
}
|
||||
if ($ordOk) { Report-OK "8. CommandsOrder: $ordCount entries, all valid" }
|
||||
} else {
|
||||
Report-OK "8. CommandsOrder: not present"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 9. SubsystemsOrder format ---
|
||||
if (-not $script:stopped) {
|
||||
$subSection = $root.SelectSingleNode("ci:SubsystemsOrder", $ns)
|
||||
$subNames = @()
|
||||
if ($subSection) {
|
||||
$subOk = $true
|
||||
$subCount = 0
|
||||
foreach ($sub in $subSection.ChildNodes) {
|
||||
if ($sub.NodeType -ne 'Element') { continue }
|
||||
$subCount++
|
||||
$text = $sub.InnerText.Trim()
|
||||
$subNames += $text
|
||||
if (-not $text) {
|
||||
Report-Error "9. SubsystemsOrder: empty <Subsystem> element"
|
||||
$subOk = $false
|
||||
} elseif ($text -notmatch '^Subsystem\.') {
|
||||
Report-Error "9. SubsystemsOrder: '$text' - expected format Subsystem.X..."
|
||||
$subOk = $false
|
||||
}
|
||||
}
|
||||
if ($subOk) { Report-OK "9. SubsystemsOrder: $subCount entries, all valid format" }
|
||||
} else {
|
||||
Report-OK "9. SubsystemsOrder: not present"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 10. SubsystemsOrder duplicates ---
|
||||
if (-not $script:stopped) {
|
||||
if ($subNames.Count -gt 0) {
|
||||
$dupes = $subNames | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Warn "10. SubsystemsOrder: duplicates: $dupeNames"
|
||||
} else {
|
||||
Report-OK "10. SubsystemsOrder: no duplicates"
|
||||
}
|
||||
} else {
|
||||
Report-OK "10. SubsystemsOrder: no duplicates (empty)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 11. GroupsOrder entries ---
|
||||
if (-not $script:stopped) {
|
||||
$grpSection = $root.SelectSingleNode("ci:GroupsOrder", $ns)
|
||||
$grpNames = @()
|
||||
if ($grpSection) {
|
||||
$grpOk = $true
|
||||
$grpCount = 0
|
||||
foreach ($grp in $grpSection.ChildNodes) {
|
||||
if ($grp.NodeType -ne 'Element') { continue }
|
||||
$grpCount++
|
||||
$text = $grp.InnerText.Trim()
|
||||
$grpNames += $text
|
||||
if (-not $text) {
|
||||
Report-Error "11. GroupsOrder: empty <Group> element"
|
||||
$grpOk = $false
|
||||
}
|
||||
}
|
||||
if ($grpOk) { Report-OK "11. GroupsOrder: $grpCount entries, all valid" }
|
||||
} else {
|
||||
Report-OK "11. GroupsOrder: not present"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 12. GroupsOrder duplicates ---
|
||||
if (-not $script:stopped) {
|
||||
if ($grpNames.Count -gt 0) {
|
||||
$dupes = $grpNames | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Warn "12. GroupsOrder: duplicates: $dupeNames"
|
||||
} else {
|
||||
Report-OK "12. GroupsOrder: no duplicates"
|
||||
}
|
||||
} else {
|
||||
Report-OK "12. GroupsOrder: no duplicates (empty)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 13. Command reference format ---
|
||||
if (-not $script:stopped) {
|
||||
if ($script:allCommandNames.Count -gt 0) {
|
||||
$badRefs = @()
|
||||
foreach ($ref in $script:allCommandNames) {
|
||||
if ($ref -match $stdCmdPattern) { continue }
|
||||
if ($ref -match $customCmdPattern) { continue }
|
||||
if ($ref -match $commonCmdPattern) { continue }
|
||||
if ($ref -match $uuidCmdPattern) { continue }
|
||||
$badRefs += $ref
|
||||
}
|
||||
if ($badRefs.Count -eq 0) {
|
||||
Report-OK "13. Command reference format: all $($script:allCommandNames.Count) valid"
|
||||
} else {
|
||||
$shown = $badRefs[0..([Math]::Min(4, $badRefs.Count - 1))]
|
||||
Report-Warn "13. Command reference format: $($badRefs.Count) unrecognized: $($shown -join ', ')$(if($badRefs.Count -gt 5){' ...'})"
|
||||
}
|
||||
} else {
|
||||
Report-OK "13. Command reference format: n/a (no commands)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Finalize ---
|
||||
Out-Line "---"
|
||||
Out-Line "Errors: $($script:errors), Warnings: $($script:warnings)"
|
||||
|
||||
$result = $script:output.ToString()
|
||||
Write-Host $result
|
||||
|
||||
if ($OutFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
|
||||
$OutFile = Join-Path (Get-Location).Path $OutFile
|
||||
}
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($OutFile, $result, $utf8Bom)
|
||||
Write-Host "Written to: $OutFile"
|
||||
}
|
||||
|
||||
if ($script:errors -gt 0) { exit 1 } else { exit 0 }
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
name: meta-compile
|
||||
description: Создать исходники объекта метаданных 1С (справочник, документ, регистр, перечисление, константа, общий модуль, обработка, HTTP-сервис и др.) в выгрузке конфигурации. Используй когда пользователь просит добавить или создать объект конфигурации
|
||||
argument-hint: <JsonPath> <OutputDir>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /meta-compile — генерация объектов метаданных из JSON DSL
|
||||
|
||||
Принимает JSON-определение объекта метаданных → генерирует XML + модули в структуре выгрузки конфигурации + регистрирует в Configuration.xml.
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `JsonPath` | Путь к JSON-определению объекта |
|
||||
| `OutputDir` | Корневая директория выгрузки конфигурации (где `Catalogs/`, `Documents/` и т.д.) |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-compile\scripts\meta-compile.ps1 -JsonPath "<json>" -OutputDir "<ConfigDir>"
|
||||
```
|
||||
|
||||
`OutputDir` — директория, содержащая подпапки типов (`Catalogs/`, `Documents/`, ...) и `Configuration.xml`.
|
||||
|
||||
## Поддерживаемые типы (23)
|
||||
|
||||
### Ссылочные
|
||||
Catalog (Справочник), Document (Документ), Enum (Перечисление), ExchangePlan (ПланОбмена), ChartOfAccounts (ПланСчетов), ChartOfCharacteristicTypes (ПВХ), ChartOfCalculationTypes (ПВР), BusinessProcess (БизнесПроцесс), Task (Задача)
|
||||
|
||||
### Регистры
|
||||
InformationRegister (РегистрСведений), AccumulationRegister (РегистрНакопления), AccountingRegister (РегистрБухгалтерии), CalculationRegister (РегистрРасчёта)
|
||||
|
||||
### Отчёты/Обработки
|
||||
Report (Отчёт), DataProcessor (Обработка)
|
||||
|
||||
### Сервисные
|
||||
Constant (Константа), DefinedType (ОпределяемыйТип), CommonModule (ОбщийМодуль), ScheduledJob (РегламентноеЗадание), EventSubscription (ПодпискаНаСобытие), DocumentJournal (ЖурналДокументов), HTTPService (HTTPСервис), WebService (ВебСервис)
|
||||
|
||||
## JSON DSL — краткий справочник
|
||||
|
||||
Полная спецификация: `docs/meta-dsl-spec.md`.
|
||||
|
||||
### Корневая структура
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "Catalog",
|
||||
"name": "Номенклатура",
|
||||
"synonym": "авто из name",
|
||||
...type-specific...,
|
||||
"attributes": [...],
|
||||
"tabularSections": {...}
|
||||
}
|
||||
```
|
||||
|
||||
### Реквизиты — shorthand
|
||||
|
||||
```
|
||||
"ИмяРеквизита" — String без квалификаторов
|
||||
"ИмяРеквизита: Тип" — с типом
|
||||
"ИмяРеквизита: Тип | req, index" — с флагами
|
||||
```
|
||||
|
||||
Типы: `String(100)`, `Number(15,2)`, `Boolean`, `Date`, `DateTime`, `CatalogRef.Xxx`, `DocumentRef.Xxx`, `EnumRef.Xxx`, `ChartOfAccountsRef.Xxx`, `ChartOfCharacteristicTypesRef.Xxx`, `ChartOfCalculationTypesRef.Xxx`, `ExchangePlanRef.Xxx`, `BusinessProcessRef.Xxx`, `TaskRef.Xxx`, `DefinedType.Xxx`.
|
||||
|
||||
Русские синонимы типов: `Строка`, `Число`, `Булево`, `Дата`, `СправочникСсылка.Xxx`, `ДокументСсылка.Xxx`, `ПланСчетовСсылка.Xxx`.
|
||||
|
||||
Флаги: `req`, `index`, `indexAdditional`, `nonneg`, `master`, `mainFilter`, `denyIncomplete`, `useInTotals`.
|
||||
|
||||
## Примеры
|
||||
|
||||
### Справочник
|
||||
|
||||
```json
|
||||
{ "type": "Catalog", "name": "Валюты" }
|
||||
```
|
||||
|
||||
### Перечисление
|
||||
|
||||
```json
|
||||
{ "type": "Enum", "name": "Статусы", "values": ["Новый", "Закрыт"] }
|
||||
```
|
||||
|
||||
### Константа
|
||||
|
||||
```json
|
||||
{ "type": "Constant", "name": "ОсновнаяВалюта", "valueType": "CatalogRef.Валюты" }
|
||||
```
|
||||
|
||||
### Определяемый тип
|
||||
|
||||
```json
|
||||
{ "type": "DefinedType", "name": "ДенежныеСредства", "valueTypes": ["CatalogRef.БанковскиеСчета", "CatalogRef.Кассы"] }
|
||||
```
|
||||
|
||||
### Общий модуль
|
||||
|
||||
```json
|
||||
{ "type": "CommonModule", "name": "ОбменДаннымиСервер", "context": "server", "returnValuesReuse": "DuringRequest" }
|
||||
```
|
||||
|
||||
Шорткаты context: `"server"` → Server+ServerCall, `"client"` → ClientManagedApplication, `"serverClient"` → Server+ClientManagedApplication.
|
||||
|
||||
### Регистр сведений
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "InformationRegister", "name": "КурсыВалют", "periodicity": "Day",
|
||||
"dimensions": ["Валюта: CatalogRef.Валюты | master, mainFilter, denyIncomplete"],
|
||||
"resources": ["Курс: Number(15,4)", "Кратность: Number(10,0)"]
|
||||
}
|
||||
```
|
||||
|
||||
### План обмена
|
||||
|
||||
```json
|
||||
{ "type": "ExchangePlan", "name": "ОбменССайтом", "attributes": ["АдресСервера: String(200)"] }
|
||||
```
|
||||
|
||||
### Журнал документов
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "DocumentJournal", "name": "Взаимодействия",
|
||||
"registeredDocuments": ["Document.Встреча", "Document.ТелефонныйЗвонок"],
|
||||
"columns": [{ "name": "Организация", "indexing": "Index", "references": ["Document.Встреча.Attribute.Организация"] }]
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP-сервис
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "HTTPService", "name": "API", "rootURL": "api",
|
||||
"urlTemplates": { "Users": { "template": "/v1/users", "methods": { "Get": "GET", "Create": "POST" } } }
|
||||
}
|
||||
```
|
||||
|
||||
### Веб-сервис
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "WebService", "name": "DataExchange", "namespace": "http://www.1c.ru/DataExchange",
|
||||
"operations": { "TestConnection": { "returnType": "xs:boolean", "handler": "ПроверкаПодключения", "parameters": { "ErrorMessage": { "type": "xs:string", "direction": "Out" } } } }
|
||||
}
|
||||
```
|
||||
|
||||
### План счетов
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "ChartOfAccounts", "name": "Хозрасчетный",
|
||||
"extDimensionTypes": "ChartOfCharacteristicTypes.ВидыСубконто", "maxExtDimensionCount": 3,
|
||||
"codeMask": "@@@.@@.@", "codeLength": 8,
|
||||
"accountingFlags": ["Валютный", "Количественный"],
|
||||
"extDimensionAccountingFlags": ["Суммовой", "Валютный"]
|
||||
}
|
||||
```
|
||||
|
||||
### Бизнес-процесс
|
||||
|
||||
```json
|
||||
{ "type": "BusinessProcess", "name": "Задание", "attributes": ["Описание: String(200)"] }
|
||||
```
|
||||
|
||||
## Что генерируется
|
||||
|
||||
- `{OutputDir}/{TypePlural}/{Name}.xml` — метаданные объекта
|
||||
- `{OutputDir}/{TypePlural}/{Name}/Ext/ObjectModule.bsl` — модуль объекта (Catalog, Document, Report, DataProcessor, ExchangePlan, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes, BusinessProcess, Task)
|
||||
- `{OutputDir}/{TypePlural}/{Name}/Ext/RecordSetModule.bsl` — модуль набора записей (4 типа регистров)
|
||||
- `{OutputDir}/{TypePlural}/{Name}/Ext/Module.bsl` — модуль (CommonModule, HTTPService, WebService)
|
||||
- `{OutputDir}/{TypePlural}/{Name}/Ext/Content.xml` — состав плана обмена (ExchangePlan)
|
||||
- `{OutputDir}/{TypePlural}/{Name}/Ext/Flowchart.xml` — карта маршрута (BusinessProcess)
|
||||
- `Configuration.xml` — автоматическая регистрация в `<ChildObjects>`
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/meta-info <OutputDir>/<TypePlural>/<Name>.xml — проверка структуры
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
---
|
||||
name: meta-edit
|
||||
description: Точечное редактирование объекта метаданных 1С. Используй когда нужно добавить, удалить или изменить реквизиты, табличные части, измерения, ресурсы или свойства существующего объекта конфигурации
|
||||
argument-hint: <ObjectPath> -Operation <op> -Value "<val>" | -DefinitionFile <json> [-NoValidate]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /meta-edit — точечное редактирование метаданных 1С
|
||||
|
||||
Атомарные операции модификации существующих XML объектов метаданных.
|
||||
|
||||
## Команда
|
||||
|
||||
### Inline mode (простые операции)
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -ObjectPath "<path>" -Operation <op> -Value "<val>"
|
||||
```
|
||||
|
||||
### JSON mode (сложные/комбинированные)
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -DefinitionFile "<json>" -ObjectPath "<path>"
|
||||
```
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| ObjectPath | XML-файл или директория объекта (обязательный, авторезолв `<dirName>.xml`) |
|
||||
| Operation | Inline-операция (альтернатива DefinitionFile) |
|
||||
| Value | Значение для inline-операции |
|
||||
| DefinitionFile | JSON-файл с операциями (альтернатива Operation) |
|
||||
| NoValidate | Не запускать meta-validate после правки |
|
||||
|
||||
## Операции — сводная таблица
|
||||
|
||||
Batch через `;;` во всех операциях. Подробный синтаксис — в файлах по ссылкам.
|
||||
|
||||
### Дочерние элементы — [child-operations.md](child-operations.md)
|
||||
|
||||
| Операция | Формат Value | Пример |
|
||||
|----------|-------------|--------|
|
||||
| `add-attribute` | `Имя: Тип \| флаги` | `"Сумма: Число(15,2) \| req, index"` |
|
||||
| `add-ts` | `ТЧ: Рекв1: Тип1, Рекв2: Тип2` | `"Товары: Ном: CatalogRef.Ном, Кол: Число(15,3)"` |
|
||||
| `add-dimension` | `Имя: Тип \| флаги` | `"Организация: CatalogRef.Организации \| master"` |
|
||||
| `add-resource` | `Имя: Тип` | `"Сумма: Число(15,2)"` |
|
||||
| `add-enumValue` | `Имя` | `"Значение1 ;; Значение2"` |
|
||||
| `add-column` | `Имя: Тип` | `"Тип: EnumRef.ТипыДокументов"` |
|
||||
| `add-form` / `add-template` / `add-command` | `Имя` | `"ФормаЭлемента"` |
|
||||
| `add-ts-attribute` | `ТЧ.Имя: Тип` | `"Товары.Скидка: Число(15,2)"` |
|
||||
| `remove-*` | `Имя` | `"СтарыйРеквизит ;; ЕщёОдин"` |
|
||||
| `remove-ts-attribute` | `ТЧ.Имя` | `"Товары.УстаревшийРекв"` |
|
||||
| `modify-attribute` | `Имя: ключ=значение` | `"СтароеИмя: name=НовоеИмя, type=Строка(500)"` |
|
||||
| `modify-ts-attribute` | `ТЧ.Имя: ключ=значение` | `"Товары.Рекв: name=НовоеИмя"` |
|
||||
| `modify-ts` | `ТЧ: ключ=значение` | `"Товары: synonym=Товарный состав"` |
|
||||
|
||||
Позиционная вставка: `"Склад: CatalogRef.Склады >> after Организация"`.
|
||||
|
||||
### Свойства объекта — [properties-reference.md](properties-reference.md)
|
||||
|
||||
| Операция | Формат Value | Пример |
|
||||
|----------|-------------|--------|
|
||||
| `modify-property` | `Ключ=Значение` | `"CodeLength=11 ;; DescriptionLength=150"` |
|
||||
| `add-owner` | `MetaType.Name` | `"Catalog.Контрагенты ;; Catalog.Организации"` |
|
||||
| `add-registerRecord` | `MetaType.Name` | `"AccumulationRegister.ОстаткиТоваров"` |
|
||||
| `add-basedOn` | `MetaType.Name` | `"Document.ЗаказКлиента"` |
|
||||
| `add-inputByString` | `Путь поля` | `"StandardAttribute.Description"` |
|
||||
| `set-owners` / `set-registerRecords` / `set-basedOn` / `set-inputByString` | Замена всего списка | `"Catalog.Орг ;; Catalog.Контр"` |
|
||||
| `remove-owner` / `remove-registerRecord` / ... | Удаление из списка | `"Catalog.Контрагенты"` |
|
||||
|
||||
### JSON DSL — [json-dsl.md](json-dsl.md)
|
||||
|
||||
Для комбинированных операций (add + remove + modify в одном файле), синонимы ключей/типов, таблица поддерживаемых объектов.
|
||||
|
||||
## Быстрые примеры
|
||||
|
||||
```powershell
|
||||
# Добавить реквизиты
|
||||
-Operation add-attribute -Value "Комментарий: Строка(200) ;; Сумма: Число(15,2) | index"
|
||||
|
||||
# Добавить ТЧ с реквизитами
|
||||
-Operation add-ts -Value "Товары: Ном: CatalogRef.Ном | req, Кол: Число(15,3), Цена: Число(15,2)"
|
||||
|
||||
# Удалить реквизит
|
||||
-Operation remove-attribute -Value "УстаревшийРеквизит"
|
||||
|
||||
# Переименовать + сменить тип
|
||||
-Operation modify-attribute -Value "СтароеИмя: name=НовоеИмя, type=Строка(500)"
|
||||
|
||||
# Изменить свойства объекта
|
||||
-Operation modify-property -Value "CodeLength=11 ;; DescriptionLength=150"
|
||||
|
||||
# Владельцы справочника
|
||||
-Operation set-owners -Value "Catalog.Контрагенты ;; Catalog.Организации"
|
||||
```
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/meta-validate <ObjectPath> — валидация после редактирования
|
||||
/meta-info <ObjectPath> — визуальная сводка
|
||||
```
|
||||
@@ -0,0 +1,102 @@
|
||||
# Inline-операции над дочерними элементами
|
||||
|
||||
Подробный справочник операций `add-*` / `remove-*` / `modify-*` для дочерних элементов объекта метаданных.
|
||||
|
||||
## Общие правила
|
||||
|
||||
**Batch-режим** — несколько элементов через `;;`:
|
||||
```
|
||||
-Value "Комментарий: Строка(200) ;; Сумма: Число(15,2) | index"
|
||||
```
|
||||
|
||||
**Shorthand-формат** реквизитов: `ИмяРеквизита: Тип | флаги`
|
||||
|
||||
Флаги: `req` (FillChecking=ShowError), `index` (Indexing=Index), `master` (Master=true, только dimensions), `mainFilter` (MainFilterOperand, только dimensions).
|
||||
|
||||
**Позиционная вставка**: `>> after ИмяЭлемента` или `<< before ИмяЭлемента`:
|
||||
```powershell
|
||||
-Operation add-attribute -Value "Склад: CatalogRef.Склады >> after Организация"
|
||||
```
|
||||
|
||||
## add-attribute / add-dimension / add-resource / add-column
|
||||
|
||||
```powershell
|
||||
-Operation add-attribute -Value "Комментарий: Строка(200)"
|
||||
-Operation add-attribute -Value "Сумма: Число(15,2) | req, index"
|
||||
-Operation add-attribute -Value "Ном: CatalogRef.Номенклатура | req ;; Кол: Число(15,3)"
|
||||
-Operation add-dimension -Value "Организация: CatalogRef.Организации | master, mainFilter"
|
||||
-Operation add-resource -Value "Сумма: Число(15,2)"
|
||||
-Operation add-column -Value "Тип: EnumRef.ТипыДокументов"
|
||||
```
|
||||
|
||||
## add-ts
|
||||
|
||||
Формат: `ИмяТЧ: Реквизит1: Тип1, Реквизит2: Тип2, ...`
|
||||
|
||||
```powershell
|
||||
-Operation add-ts -Value "Товары: Ном: CatalogRef.Ном | req, Кол: Число(15,3), Цена: Число(15,2), Сумма: Число(15,2)"
|
||||
```
|
||||
|
||||
## add-ts-attribute / remove-ts-attribute / modify-ts-attribute
|
||||
|
||||
Операции над реквизитами **внутри существующей ТЧ**. Формат: `ИмяТЧ.ОпределениеРеквизита` (dot-нотация).
|
||||
|
||||
```powershell
|
||||
# Добавить реквизит в ТЧ
|
||||
-Operation add-ts-attribute -Value "Товары.СтавкаНДС: EnumRef.СтавкиНДС"
|
||||
-Operation add-ts-attribute -Value "Товары.Скидка: Число(15,2) ;; Товары.Бонус: Число(15,2)"
|
||||
|
||||
# Позиционная вставка в ТЧ
|
||||
-Operation add-ts-attribute -Value "Товары.Скидка: Число(15,2) >> after Цена"
|
||||
|
||||
# Удалить реквизит из ТЧ
|
||||
-Operation remove-ts-attribute -Value "Товары.УстаревшийРекв"
|
||||
-Operation remove-ts-attribute -Value "Товары.Рекв1 ;; Товары.Рекв2"
|
||||
|
||||
# Изменить реквизит в ТЧ (rename, type change и т.д.)
|
||||
-Operation modify-ts-attribute -Value "Товары.СтароеИмя: name=НовоеИмя, type=Строка(500)"
|
||||
```
|
||||
|
||||
Batch через `;;` — можно указать разные ТЧ: `"Товары.А: Строка(50) ;; Услуги.Б: Число(10)"`.
|
||||
|
||||
## modify-ts
|
||||
|
||||
Изменение свойств **самой табличной части** (Synonym, FillChecking, Use и др.):
|
||||
|
||||
```powershell
|
||||
-Operation modify-ts -Value "Товары: synonym=Товарный состав"
|
||||
-Operation modify-ts -Value "Товары: fillChecking=ShowError"
|
||||
```
|
||||
|
||||
Формат аналогичен `modify-attribute`: `ИмяТЧ: ключ=значение, ключ=значение`.
|
||||
|
||||
## add-enumValue / add-form / add-template / add-command
|
||||
|
||||
Просто имена (batch через `;;`):
|
||||
```powershell
|
||||
-Operation add-enumValue -Value "Значение1 ;; Значение2 ;; Значение3"
|
||||
-Operation add-form -Value "ФормаЭлемента ;; ФормаСписка"
|
||||
-Operation add-template -Value "ПечатнаяФорма"
|
||||
-Operation add-command -Value "Команда1"
|
||||
```
|
||||
|
||||
## remove-*
|
||||
|
||||
Имя элемента (или несколько через `;;`):
|
||||
```powershell
|
||||
-Operation remove-attribute -Value "СтарыйРеквизит ;; ЕщёОдин"
|
||||
-Operation remove-ts -Value "УстаревшаяТЧ"
|
||||
-Operation remove-enumValue -Value "НеиспользуемоеЗначение"
|
||||
```
|
||||
|
||||
## modify-attribute / modify-dimension / modify-resource / modify-enumValue / modify-column
|
||||
|
||||
Формат: `ИмяЭлемента: ключ=значение, ключ=значение`
|
||||
|
||||
Ключи: `name` (rename), `type`, `synonym`, `indexing`, `fillChecking`, `use` и др.
|
||||
|
||||
```powershell
|
||||
-Operation modify-attribute -Value "СтароеИмя: name=НовоеИмя, type=Строка(500)"
|
||||
-Operation modify-attribute -Value "Комментарий: indexing=Index"
|
||||
-Operation modify-enumValue -Value "СтароеЗначение: name=НовоеЗначение"
|
||||
```
|
||||
@@ -0,0 +1,135 @@
|
||||
# JSON DSL — режим определений
|
||||
|
||||
Для сложных и комбинированных операций используйте JSON-файл вместо inline-режима.
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-edit\scripts\meta-edit.ps1 -DefinitionFile "<json>" -ObjectPath "<path>"
|
||||
```
|
||||
|
||||
## add — добавить элементы
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"attributes": [
|
||||
{ "name": "Комментарий", "type": "Строка(200)" },
|
||||
{ "name": "Сумма", "type": "Число(15,2)", "indexing": "Index" }
|
||||
],
|
||||
"tabularSections": [{
|
||||
"name": "Товары",
|
||||
"attrs": [
|
||||
{ "name": "Номенклатура", "type": "CatalogRef.Номенклатура" },
|
||||
{ "name": "Количество", "type": "Число(15,3)" }
|
||||
]
|
||||
}],
|
||||
"forms": ["ФормаЭлемента"],
|
||||
"templates": ["ПечатнаяФорма"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Реквизиты можно задавать shorthand-строками: `"Сумма: Число(15,2) | req, index"`.
|
||||
|
||||
## remove — удалить элементы
|
||||
|
||||
```json
|
||||
{
|
||||
"remove": {
|
||||
"attributes": ["СтарыйРеквизит"],
|
||||
"tabularSections": ["УстаревшаяТЧ"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## modify — изменить существующие
|
||||
|
||||
```json
|
||||
{
|
||||
"modify": {
|
||||
"properties": {
|
||||
"CodeLength": 11,
|
||||
"Hierarchical": true,
|
||||
"Owners": ["Catalog.Контрагенты", "Catalog.Организации"],
|
||||
"RegisterRecords": ["AccumulationRegister.Продажи"],
|
||||
"InputByString": ["StandardAttribute.Description"]
|
||||
},
|
||||
"attributes": {
|
||||
"Комментарий": { "type": "Строка(500)" },
|
||||
"СтароеИмя": { "name": "НовоеИмя" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## modify — реквизиты внутри ТЧ
|
||||
|
||||
```json
|
||||
{
|
||||
"modify": {
|
||||
"tabularSections": {
|
||||
"Товары": {
|
||||
"add": ["СтавкаНДС: EnumRef.СтавкиНДС", "Скидка: Число(15,2)"],
|
||||
"remove": ["УстаревшийРекв"],
|
||||
"modify": {
|
||||
"СтароеИмя": { "name": "НовоеИмя", "type": "Строка(500)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Комбинирование
|
||||
|
||||
Все три операции (`add`, `remove`, `modify`) можно указать в одном JSON-файле:
|
||||
|
||||
```json
|
||||
{
|
||||
"add": { "tabularSections": [{ "name": "НоваяТЧ", "attrs": ["Имя: Строка(100)"] }] },
|
||||
"modify": {
|
||||
"tabularSections": {
|
||||
"СуществующаяТЧ": {
|
||||
"add": ["НовыйРекв: Число(15,2)"],
|
||||
"remove": ["СтарыйРекв"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Позиционная вставка
|
||||
|
||||
```json
|
||||
{ "name": "Склад", "type": "CatalogRef.Склады", "after": "Организация" }
|
||||
```
|
||||
|
||||
## Синонимы ключей (case-insensitive)
|
||||
|
||||
**Операции:** `add`/`добавить`, `remove`/`удалить`, `modify`/`изменить`
|
||||
|
||||
| Каноническое | Синонимы |
|
||||
|-------------|----------|
|
||||
| attributes | реквизиты, attrs |
|
||||
| tabularSections | табличныеЧасти, тч, ts |
|
||||
| dimensions | измерения, dims |
|
||||
| resources | ресурсы, res |
|
||||
| enumValues | значения, values |
|
||||
| columns | графы, колонки |
|
||||
| forms | формы |
|
||||
| templates | макеты |
|
||||
| commands | команды |
|
||||
| properties | свойства |
|
||||
|
||||
## Синонимы типов
|
||||
|
||||
`Строка(200)`, `Число(15,2)`, `Булево`, `Дата`, `ДатаВремя`, `ХранилищеЗначения`, `СправочникСсылка.XXX`, `ДокументСсылка.XXX`, `ПеречислениеСсылка.XXX`, `ОпределяемыйТип.XXX`.
|
||||
|
||||
## Поддерживаемые типы объектов
|
||||
|
||||
| Тип объекта | Допустимые add-типы |
|
||||
|-------------|-------------------|
|
||||
| Catalog, Document, ExchangePlan, ChartOf*, BP, Task, Report, DP | attributes, tabularSections, forms, templates, commands |
|
||||
| Enum | enumValues, forms, templates, commands |
|
||||
| *Register (4 типа) | dimensions, resources, attributes, forms, templates, commands |
|
||||
| DocumentJournal | columns, forms, templates, commands |
|
||||
| Constant | forms |
|
||||
@@ -0,0 +1,54 @@
|
||||
# Свойства объекта и complex properties
|
||||
|
||||
Справочник операций для скалярных свойств объекта и свойств со вложенной XML-структурой (Owners, RegisterRecords, BasedOn, InputByString).
|
||||
|
||||
## modify-property
|
||||
|
||||
Изменение скалярных свойств объекта. Формат: `Ключ=Значение` (batch через `;;`):
|
||||
```powershell
|
||||
-Operation modify-property -Value "CodeLength=11 ;; DescriptionLength=150"
|
||||
-Operation modify-property -Value "Hierarchical=true"
|
||||
```
|
||||
|
||||
## Complex properties
|
||||
|
||||
Свойства со вложенной XML-структурой. Поддерживаются через inline `add-*` / `remove-*` / `set-*` и через JSON `modify.properties`.
|
||||
|
||||
| Свойство | Объекты | Inline-значение |
|
||||
|----------|---------|-----------------|
|
||||
| Owners | Catalog, ChartOfCharacteristicTypes | `Catalog.XXX` |
|
||||
| RegisterRecords | Document | `AccumulationRegister.XXX` |
|
||||
| BasedOn | Document, Catalog, BP, Task | `Document.XXX` |
|
||||
| InputByString | Catalog, ChartOf*, Task | `StandardAttribute.Description` |
|
||||
|
||||
### add-owner / add-registerRecord / add-basedOn
|
||||
|
||||
Полное имя метаданных `MetaType.Name`:
|
||||
```powershell
|
||||
-Operation add-owner -Value "Catalog.Контрагенты ;; Catalog.Организации"
|
||||
-Operation add-registerRecord -Value "AccumulationRegister.ОстаткиТоваров"
|
||||
-Operation add-basedOn -Value "Document.ЗаказКлиента"
|
||||
```
|
||||
|
||||
### add-inputByString
|
||||
|
||||
Пути полей (префикс `MetaType.Name.` добавляется автоматически):
|
||||
```powershell
|
||||
-Operation add-inputByString -Value "StandardAttribute.Description ;; StandardAttribute.Code"
|
||||
```
|
||||
|
||||
### remove-owner / remove-registerRecord / remove-basedOn / remove-inputByString
|
||||
|
||||
```powershell
|
||||
-Operation remove-owner -Value "Catalog.Контрагенты"
|
||||
-Operation remove-inputByString -Value "Catalog.МойСпр.StandardAttribute.Code"
|
||||
```
|
||||
|
||||
### set-owners / set-registerRecords / set-basedOn / set-inputByString
|
||||
|
||||
Заменяют **весь список** (в отличие от add/remove):
|
||||
```powershell
|
||||
-Operation set-owners -Value "Catalog.Организации ;; Catalog.Контрагенты"
|
||||
-Operation set-registerRecords -Value "AccumulationRegister.Продажи ;; AccumulationRegister.ОстаткиТоваров"
|
||||
-Operation set-inputByString -Value "StandardAttribute.Description ;; StandardAttribute.Code"
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
---
|
||||
name: meta-info
|
||||
description: Анализ структуры объекта метаданных 1С из XML-выгрузки — реквизиты, табличные части, формы, движения, типы. Используй для изучения структуры объектов и как подготовительный шаг при написании запросов и кода, работающего с объектами
|
||||
argument-hint: <ObjectPath> [-Mode overview|brief|full] [-Name <элемент>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /meta-info — Структура объекта метаданных 1С
|
||||
|
||||
Читает XML объекта метаданных из выгрузки конфигурации 1С и выводит компактное описание структуры.
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `ObjectPath` | Путь к XML-файлу объекта или каталогу (авто-резолв `<name>/<name>.xml`) |
|
||||
| `Mode` | Режим: `overview` (default), `brief`, `full` |
|
||||
| `Name` | Drill-down по имени элемента (реквизит, ТЧ, значение перечисления, шаблон URL, операция) |
|
||||
| `Limit` / `Offset` | Пагинация (по умолчанию 150 строк) |
|
||||
| `OutFile` | Записать результат в файл (UTF-8 BOM) |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-info\scripts\meta-info.ps1 -ObjectPath "<путь>"
|
||||
```
|
||||
|
||||
## Три режима
|
||||
|
||||
| Режим | Что показывает |
|
||||
|---|---|
|
||||
| `overview` *(default)* | Заголовок + ключевые свойства + структура без раскрытия деталей |
|
||||
| `brief` | Всё одной-двумя строками: имена полей, счётчики |
|
||||
| `full` | Всё раскрыто: колонки ТЧ, список источников подписки, движения, формы |
|
||||
|
||||
`-Name` — drill-down: раскрыть конкретный элемент объекта (ТЧ, реквизит, шаблон URL, операцию веб-сервиса).
|
||||
|
||||
## Поддерживаемые типы (23)
|
||||
|
||||
**Ссылочные:** Справочник, Документ, Перечисление, Бизнес-процесс, Задача, План обмена, План счетов, ПВХ, ПВР
|
||||
**Регистры:** Регистр сведений, Регистр накопления, Регистр бухгалтерии, Регистр расчёта
|
||||
**Сервисные:** Отчёт, Обработка, HTTP-сервис, Веб-сервис, Общий модуль, Регламентное задание, Подписка на событие
|
||||
**Прочие:** Константа, Журнал документов, Определяемый тип
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Справочник — overview
|
||||
... -ObjectPath Catalogs/Валюты/Валюты.xml
|
||||
|
||||
# Документ — полная сводка с колонками ТЧ, движениями, формами
|
||||
... -ObjectPath Documents/АвансовыйОтчет/АвансовыйОтчет.xml -Mode full
|
||||
|
||||
# Регистр сведений — краткая сводка
|
||||
... -ObjectPath InformationRegisters/КурсыВалют/КурсыВалют.xml -Mode brief
|
||||
|
||||
# Drill-down в ТЧ документа
|
||||
... -ObjectPath Documents/АвансовыйОтчет/АвансовыйОтчет.xml -Name Товары
|
||||
|
||||
# Drill-down в реквизит
|
||||
... -ObjectPath Catalogs/Валюты/Валюты.xml -Name ОсновнаяВалюта
|
||||
|
||||
# Общий модуль — флаги контекста и повторное использование
|
||||
... -ObjectPath CommonModules/ОбщегоНазначения/ОбщегоНазначения.xml
|
||||
|
||||
# HTTP-сервис — шаблоны URL и методы
|
||||
... -ObjectPath HTTPServices/ExternalAPI/ExternalAPI.xml
|
||||
|
||||
# HTTP-сервис — drill-down в шаблон URL
|
||||
... -ObjectPath HTTPServices/ExternalAPI/ExternalAPI.xml -Name АктуальныеЗадачи
|
||||
|
||||
# Веб-сервис — операции с параметрами
|
||||
... -ObjectPath WebServices/EnterpriseDataUpload_1_0_1_1/EnterpriseDataUpload_1_0_1_1.xml
|
||||
|
||||
# Веб-сервис — drill-down в операцию
|
||||
... -ObjectPath WebServices/EnterpriseDataUpload_1_0_1_1/EnterpriseDataUpload_1_0_1_1.xml -Name TestConnection
|
||||
|
||||
# Подписка на событие — full раскрывает список источников
|
||||
... -ObjectPath EventSubscriptions/ПолныйРегистрацияУдаления/ПолныйРегистрацияУдаления.xml -Mode full
|
||||
|
||||
# Регламентное задание
|
||||
... -ObjectPath ScheduledJobs/АвтоматическоеЗакрытиеМесяца/АвтоматическоеЗакрытиеМесяца.xml
|
||||
|
||||
# Определяемый тип
|
||||
... -ObjectPath DefinedTypes/GLN/GLN.xml
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
---
|
||||
name: meta-validate
|
||||
description: Валидация объекта метаданных 1С. Используй после создания или модификации объекта конфигурации для проверки корректности
|
||||
argument-hint: <ObjectPath> [-MaxErrors 30]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /meta-validate — валидация объекта метаданных 1С
|
||||
|
||||
Проверяет XML объекта метаданных из выгрузки конфигурации на структурные ошибки: корневую структуру, InternalInfo, свойства, допустимые значения, StandardAttributes, ChildObjects, уникальность имён, табличные части, кросс-свойства, вложенные структуры HTTP/Web-сервисов.
|
||||
|
||||
## Использование
|
||||
|
||||
```
|
||||
/meta-validate <ObjectPath>
|
||||
```
|
||||
|
||||
## Параметры
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|------------|:------------:|--------------|-------------------------------------------------|
|
||||
| ObjectPath | да | — | Путь к XML-файлу или каталогу объекта |
|
||||
| MaxErrors | нет | 30 | Остановиться после N ошибок |
|
||||
| OutFile | нет | — | Записать результат в файл (UTF-8 BOM) |
|
||||
|
||||
`ObjectPath` авторезолв: если указана директория — ищет `<dirName>/<dirName>.xml`.
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\meta-validate\scripts\meta-validate.ps1 -ObjectPath "<путь>"
|
||||
```
|
||||
|
||||
## Поддерживаемые типы (23)
|
||||
|
||||
**Ссылочные:** Catalog, Document, Enum, ExchangePlan, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes, BusinessProcess, Task
|
||||
**Регистры:** InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister
|
||||
**Отчёты/Обработки:** Report, DataProcessor
|
||||
**Сервисные:** CommonModule, ScheduledJob, EventSubscription, HTTPService, WebService
|
||||
**Прочие:** Constant, DocumentJournal, DefinedType
|
||||
|
||||
## Выполняемые проверки
|
||||
|
||||
| # | Проверка | Серьёзность |
|
||||
|----|------------------------------------------|--------------|
|
||||
| 1 | XML well-formedness + root structure | ERROR |
|
||||
| 2 | InternalInfo / GeneratedType | ERROR / WARN |
|
||||
| 3 | Properties — Name, Synonym | ERROR / WARN |
|
||||
| 4 | Properties — enum-значения свойств | ERROR |
|
||||
| 5 | StandardAttributes | ERROR / WARN |
|
||||
| 6 | ChildObjects — допустимые элементы | ERROR |
|
||||
| 7 | Attributes/Dimensions/Resources — UUID, Name, Type | ERROR |
|
||||
| 8 | Уникальность имён | ERROR |
|
||||
| 9 | TabularSections — внутренняя структура | ERROR / WARN |
|
||||
| 10 | Кросс-свойства | ERROR / WARN |
|
||||
| 11 | HTTPService/WebService — вложенная структура | ERROR |
|
||||
|
||||
## Вывод
|
||||
|
||||
```
|
||||
=== Validation: Catalog.Номенклатура ===
|
||||
|
||||
[OK] 1. Root structure: MetaDataObject/Catalog, version 2.17
|
||||
[OK] 2. InternalInfo: 5 GeneratedType (Object, Ref, Selection, List, Manager)
|
||||
[OK] 3. Properties: Name="Номенклатура", Synonym present
|
||||
[OK] 4. Property values: 12 enum properties checked
|
||||
[ERROR] 5. StandardAttributes: missing "PredefinedDataName"
|
||||
[OK] 6. ChildObjects types: Attribute(15), TabularSection(3), Form(4)
|
||||
[OK] 7. Attributes/Dimensions: all valid
|
||||
[WARN] 8. Name uniqueness: duplicate attribute "Комментарий" at positions 5, 12
|
||||
[OK] 9. TabularSections: 3 sections, structure valid
|
||||
[OK] 10. Cross-property consistency
|
||||
[OK] 11. N/A (not HTTPService/WebService)
|
||||
---
|
||||
Errors: 1, Warnings: 1
|
||||
```
|
||||
|
||||
Код возврата: 0 = все проверки пройдены, 1 = есть ошибки.
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Справочник из выгрузки конфигурации
|
||||
... -ObjectPath upload/acc_8.3.24/Catalogs/Банки/Банки.xml
|
||||
|
||||
# Авторезолв из директории
|
||||
... -ObjectPath upload/acc_8.3.24/Documents/АвансовыйОтчет
|
||||
|
||||
# С лимитом ошибок
|
||||
... -ObjectPath Catalogs/Номенклатура.xml -MaxErrors 10
|
||||
|
||||
# С записью в файл
|
||||
... -ObjectPath Catalogs/Номенклатура.xml -OutFile result.txt
|
||||
```
|
||||
|
||||
## Верификация
|
||||
|
||||
```
|
||||
/meta-compile <JsonPath> <OutputDir> — генерация XML
|
||||
/meta-validate <OutputDir>/<Type>/<Name>.xml — проверка результата
|
||||
/meta-info <OutputDir>/<Type>/<Name>.xml — визуальная сводка
|
||||
```
|
||||
|
||||
## Когда использовать
|
||||
|
||||
- **После `/meta-compile`**: проверить корректность сгенерированного XML
|
||||
- **После ручного редактирования**: убедиться что структура не нарушена
|
||||
- **После merge/импорта**: выявить конфликты и битые ссылки
|
||||
- **При отладке**: найти структурные ошибки до сборки EPF
|
||||
@@ -0,0 +1,988 @@
|
||||
# meta-validate v1.0 — Validate 1C metadata object structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ObjectPath,
|
||||
|
||||
[int]$MaxErrors = 30,
|
||||
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve path ---
|
||||
|
||||
if (-not [System.IO.Path]::IsPathRooted($ObjectPath)) {
|
||||
$ObjectPath = Join-Path (Get-Location).Path $ObjectPath
|
||||
}
|
||||
|
||||
if (Test-Path $ObjectPath -PathType Container) {
|
||||
$dirName = Split-Path $ObjectPath -Leaf
|
||||
$candidate = Join-Path $ObjectPath "$dirName.xml"
|
||||
if (Test-Path $candidate) {
|
||||
$ObjectPath = $candidate
|
||||
} else {
|
||||
$xmlFiles = @(Get-ChildItem $ObjectPath -Filter "*.xml" -File | Select-Object -First 1)
|
||||
if ($xmlFiles.Count -gt 0) {
|
||||
$ObjectPath = $xmlFiles[0].FullName
|
||||
} else {
|
||||
Write-Host "[ERROR] No XML file found in directory: $ObjectPath"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $ObjectPath)) {
|
||||
Write-Host "[ERROR] File not found: $ObjectPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$resolvedPath = (Resolve-Path $ObjectPath).Path
|
||||
|
||||
# --- 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_]*$'
|
||||
|
||||
$validTypes = @(
|
||||
"Catalog","Document","Enum","Constant",
|
||||
"InformationRegister","AccumulationRegister","AccountingRegister","CalculationRegister",
|
||||
"ChartOfAccounts","ChartOfCharacteristicTypes","ChartOfCalculationTypes",
|
||||
"BusinessProcess","Task","ExchangePlan","DocumentJournal",
|
||||
"Report","DataProcessor",
|
||||
"CommonModule","ScheduledJob","EventSubscription",
|
||||
"HTTPService","WebService","DefinedType"
|
||||
)
|
||||
|
||||
# GeneratedType categories by type
|
||||
$generatedTypeCategories = @{
|
||||
"Catalog" = @("Object","Ref","Selection","List","Manager")
|
||||
"Document" = @("Object","Ref","Selection","List","Manager")
|
||||
"Enum" = @("Ref","Manager","List")
|
||||
"Constant" = @("Manager","ValueManager","ValueKey")
|
||||
"InformationRegister" = @("Record","Manager","Selection","List","RecordSet","RecordKey","RecordManager")
|
||||
"AccumulationRegister" = @("Record","Manager","Selection","List","RecordSet","RecordKey")
|
||||
"AccountingRegister" = @("Record","Manager","Selection","List","RecordSet","RecordKey","ExtDimensions")
|
||||
"CalculationRegister" = @("Record","Manager","Selection","List","RecordSet","RecordKey","Recalcs")
|
||||
"ChartOfAccounts" = @("Object","Ref","Selection","List","Manager","ExtDimensionTypes","ExtDimensionTypesRow")
|
||||
"ChartOfCharacteristicTypes" = @("Object","Ref","Selection","List","Manager","Characteristic")
|
||||
"ChartOfCalculationTypes" = @("Object","Ref","Selection","List","Manager","DisplacingCalculationTypes","DisplacingCalculationTypesRow","BaseCalculationTypes","BaseCalculationTypesRow","LeadingCalculationTypes","LeadingCalculationTypesRow")
|
||||
"BusinessProcess" = @("Object","Ref","Selection","List","Manager","RoutePointRef")
|
||||
"Task" = @("Object","Ref","Selection","List","Manager")
|
||||
"ExchangePlan" = @("Object","Ref","Selection","List","Manager")
|
||||
"DocumentJournal" = @("Selection","List","Manager")
|
||||
"Report" = @("Object","Manager")
|
||||
"DataProcessor" = @("Object","Manager")
|
||||
"DefinedType" = @("DefinedType")
|
||||
}
|
||||
|
||||
# Types that have NO InternalInfo / GeneratedType
|
||||
$typesWithoutInternalInfo = @("CommonModule","ScheduledJob","EventSubscription")
|
||||
|
||||
# StandardAttributes by type
|
||||
$standardAttributesByType = @{
|
||||
"Catalog" = @("PredefinedDataName","Predefined","Ref","DeletionMark","IsFolder","Owner","Parent","Description","Code")
|
||||
"Document" = @("Posted","Ref","DeletionMark","Date","Number")
|
||||
"Enum" = @("Order","Ref")
|
||||
"InformationRegister" = @("Active","LineNumber","Recorder","Period")
|
||||
"AccumulationRegister" = @("Active","LineNumber","Recorder","Period","RecordType")
|
||||
"AccountingRegister" = @("Active","Period","Recorder","LineNumber","Account")
|
||||
"CalculationRegister" = @("Active","Recorder","LineNumber","RegistrationPeriod","CalculationType","ReversingEntry","ActionPeriod","BegOfActionPeriod","EndOfActionPeriod","BegOfBasePeriod","EndOfBasePeriod")
|
||||
"ChartOfAccounts" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","Order","Type","OffBalance")
|
||||
"ChartOfCharacteristicTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","IsFolder","ValueType")
|
||||
"ChartOfCalculationTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","ActionPeriodIsBasic")
|
||||
"BusinessProcess" = @("Ref","DeletionMark","Date","Number","Started","Completed","HeadTask")
|
||||
"Task" = @("Ref","DeletionMark","Date","Number","Executed","Description","RoutePoint","BusinessProcess")
|
||||
"ExchangePlan" = @("Ref","DeletionMark","Code","Description","ThisNode","SentNo","ReceivedNo")
|
||||
"DocumentJournal" = @("Type","Ref","Date","Posted","DeletionMark","Number")
|
||||
}
|
||||
|
||||
# Types that have StandardAttributes block
|
||||
$typesWithStdAttrs = @(
|
||||
"Catalog","Document","Enum",
|
||||
"InformationRegister","AccumulationRegister","AccountingRegister","CalculationRegister",
|
||||
"ChartOfAccounts","ChartOfCharacteristicTypes","ChartOfCalculationTypes",
|
||||
"BusinessProcess","Task","ExchangePlan","DocumentJournal"
|
||||
)
|
||||
|
||||
# ChildObjects rules: what child element types are valid for each metadata type
|
||||
$childObjectRules = @{
|
||||
"Catalog" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"Document" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"ExchangePlan" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"ChartOfAccounts" = @("Attribute","TabularSection","Form","Template","Command","AccountingFlag","ExtDimensionAccountingFlag")
|
||||
"ChartOfCharacteristicTypes" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"ChartOfCalculationTypes" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"BusinessProcess" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"Task" = @("Attribute","TabularSection","Form","Template","Command","AddressingAttribute")
|
||||
"Report" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"DataProcessor" = @("Attribute","TabularSection","Form","Template","Command")
|
||||
"Enum" = @("EnumValue","Form","Template","Command")
|
||||
"InformationRegister" = @("Dimension","Resource","Attribute","Form","Template","Command")
|
||||
"AccumulationRegister" = @("Dimension","Resource","Attribute","Form","Template","Command")
|
||||
"AccountingRegister" = @("Dimension","Resource","Attribute","Form","Template","Command")
|
||||
"CalculationRegister" = @("Dimension","Resource","Attribute","Form","Template","Command","Recalculation")
|
||||
"DocumentJournal" = @("Column","Form","Template","Command")
|
||||
"HTTPService" = @("URLTemplate")
|
||||
"WebService" = @("Operation")
|
||||
"Constant" = @("Form")
|
||||
"DefinedType" = @()
|
||||
"CommonModule" = @()
|
||||
"ScheduledJob" = @()
|
||||
"EventSubscription" = @()
|
||||
}
|
||||
|
||||
# Valid enum property values
|
||||
$validPropertyValues = @{
|
||||
"CodeType" = @("String","Number")
|
||||
"CodeAllowedLength" = @("Variable","Fixed")
|
||||
"NumberType" = @("String","Number")
|
||||
"NumberAllowedLength" = @("Variable","Fixed")
|
||||
"Posting" = @("Allow","Deny")
|
||||
"RealTimePosting" = @("Allow","Deny")
|
||||
"RegisterRecordsDeletion" = @("AutoDelete","AutoDeleteOnUnpost","AutoDeleteOff")
|
||||
"RegisterRecordsWritingOnPost" = @("WriteModified","WriteSelected","WriteAll")
|
||||
"DataLockControlMode" = @("Automatic","Managed")
|
||||
"FullTextSearch" = @("Use","DontUse")
|
||||
"DefaultPresentation" = @("AsDescription","AsCode")
|
||||
"HierarchyType" = @("HierarchyFoldersAndItems","HierarchyItemsOnly")
|
||||
"EditType" = @("InDialog","InList","BothWays")
|
||||
"WriteMode" = @("Independent","RecorderSubordinate")
|
||||
"InformationRegisterPeriodicity" = @("Nonperiodical","Second","Day","Month","Quarter","Year","RecorderPosition")
|
||||
"RegisterType" = @("Balance","Turnovers")
|
||||
"ReturnValuesReuse" = @("DontUse","DuringRequest","DuringSession")
|
||||
"ReuseSessions" = @("DontUse","AutoUse")
|
||||
"FillChecking" = @("DontCheck","ShowError","ShowWarning")
|
||||
"Indexing" = @("DontIndex","Index","IndexWithAdditionalOrder")
|
||||
"DataHistory" = @("Use","DontUse")
|
||||
}
|
||||
|
||||
# --- 1. Parse XML ---
|
||||
|
||||
Out-Line ""
|
||||
|
||||
$xmlDoc = $null
|
||||
try {
|
||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$xmlDoc.PreserveWhitespace = $false
|
||||
$xmlDoc.Load($resolvedPath)
|
||||
} catch {
|
||||
Out-Line "=== Validation: (parse failed) ==="
|
||||
Out-Line ""
|
||||
Report-Error "1. XML parse failed: $($_.Exception.Message)"
|
||||
& $finalize
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- 2. 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("cfg", "http://v8.1c.ru/8.1/data/enterprise/current-config")
|
||||
|
||||
$root = $xmlDoc.DocumentElement
|
||||
|
||||
# --- Check 1: Root structure ---
|
||||
|
||||
$check1Ok = $true
|
||||
|
||||
# Root must be MetaDataObject
|
||||
if ($root.LocalName -ne "MetaDataObject") {
|
||||
Report-Error "1. Root element is '$($root.LocalName)', expected 'MetaDataObject'"
|
||||
& $finalize
|
||||
exit 1
|
||||
}
|
||||
|
||||
$expectedNs = "http://v8.1c.ru/8.3/MDClasses"
|
||||
if ($root.NamespaceURI -ne $expectedNs) {
|
||||
Report-Error "1. Root namespace is '$($root.NamespaceURI)', expected '$expectedNs'"
|
||||
$check1Ok = $false
|
||||
}
|
||||
|
||||
# Version attribute
|
||||
$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)"
|
||||
}
|
||||
|
||||
# Detect type element — exactly one child element in md namespace
|
||||
$typeNode = $null
|
||||
$mdType = ""
|
||||
$childElements = @()
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.NamespaceURI -eq $expectedNs) {
|
||||
$childElements += $child
|
||||
}
|
||||
}
|
||||
|
||||
if ($childElements.Count -eq 0) {
|
||||
Report-Error "1. No metadata type element found inside MetaDataObject"
|
||||
& $finalize
|
||||
exit 1
|
||||
} elseif ($childElements.Count -gt 1) {
|
||||
Report-Error "1. Multiple type elements found: $($childElements | ForEach-Object { $_.LocalName })"
|
||||
$check1Ok = $false
|
||||
}
|
||||
|
||||
$typeNode = $childElements[0]
|
||||
$mdType = $typeNode.LocalName
|
||||
|
||||
if ($validTypes -notcontains $mdType) {
|
||||
Report-Error "1. Unrecognized metadata type: $mdType"
|
||||
& $finalize
|
||||
exit 1
|
||||
}
|
||||
|
||||
# UUID on type element
|
||||
$typeUuid = $typeNode.GetAttribute("uuid")
|
||||
if (-not $typeUuid) {
|
||||
Report-Error "1. Missing uuid on <$mdType> element"
|
||||
$check1Ok = $false
|
||||
} elseif ($typeUuid -notmatch $guidPattern) {
|
||||
Report-Error "1. Invalid uuid '$typeUuid' on <$mdType>"
|
||||
$check1Ok = $false
|
||||
}
|
||||
|
||||
# Get object name early for header
|
||||
$propsNode = $typeNode.SelectSingleNode("md:Properties", $ns)
|
||||
$nameNode = if ($propsNode) { $propsNode.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||
$objName = if ($nameNode -and $nameNode.InnerText) { $nameNode.InnerText } else { "(unknown)" }
|
||||
|
||||
# Now emit header
|
||||
$script:output.Insert(0, "=== Validation: $mdType.$objName ===$([Environment]::NewLine)") | Out-Null
|
||||
|
||||
if ($check1Ok) {
|
||||
Report-OK "1. Root structure: MetaDataObject/$mdType, version $version"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 2: InternalInfo ---
|
||||
|
||||
$internalInfo = $typeNode.SelectSingleNode("md:InternalInfo", $ns)
|
||||
|
||||
if ($typesWithoutInternalInfo -contains $mdType) {
|
||||
# These types should NOT have InternalInfo with GeneratedType
|
||||
if ($internalInfo) {
|
||||
$genTypes = $internalInfo.SelectNodes("xr:GeneratedType", $ns)
|
||||
if ($genTypes.Count -gt 0) {
|
||||
Report-Warn "2. InternalInfo: $mdType should not have GeneratedType entries, found $($genTypes.Count)"
|
||||
} else {
|
||||
Report-OK "2. InternalInfo: absent or empty (correct for $mdType)"
|
||||
}
|
||||
} else {
|
||||
Report-OK "2. InternalInfo: absent (correct for $mdType)"
|
||||
}
|
||||
} elseif ($generatedTypeCategories.ContainsKey($mdType)) {
|
||||
$expectedCategories = $generatedTypeCategories[$mdType]
|
||||
if (-not $internalInfo) {
|
||||
Report-Error "2. InternalInfo: missing (expected $($expectedCategories.Count) GeneratedType)"
|
||||
} else {
|
||||
$genTypes = $internalInfo.SelectNodes("xr:GeneratedType", $ns)
|
||||
$check2Ok = $true
|
||||
$foundCategories = @()
|
||||
|
||||
foreach ($gt in $genTypes) {
|
||||
$gtName = $gt.GetAttribute("name")
|
||||
$gtCategory = $gt.GetAttribute("category")
|
||||
$foundCategories += $gtCategory
|
||||
|
||||
# Validate name format: Prefix.ObjectName
|
||||
if ($gtName -and $objName -ne "(unknown)") {
|
||||
if (-not $gtName.EndsWith(".$objName")) {
|
||||
Report-Error "2. GeneratedType name '$gtName' does not end with '.$objName'"
|
||||
$check2Ok = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Validate category
|
||||
if ($expectedCategories -notcontains $gtCategory) {
|
||||
Report-Warn "2. Unexpected GeneratedType category '$gtCategory' for $mdType"
|
||||
}
|
||||
|
||||
# Validate TypeId and ValueId UUIDs
|
||||
$typeId = $gt.SelectSingleNode("xr:TypeId", $ns)
|
||||
$valueId = $gt.SelectSingleNode("xr:ValueId", $ns)
|
||||
if ($typeId -and $typeId.InnerText -notmatch $guidPattern) {
|
||||
Report-Error "2. Invalid TypeId UUID in GeneratedType '$gtCategory'"
|
||||
$check2Ok = $false
|
||||
}
|
||||
if ($valueId -and $valueId.InnerText -notmatch $guidPattern) {
|
||||
Report-Error "2. Invalid ValueId UUID in GeneratedType '$gtCategory'"
|
||||
$check2Ok = $false
|
||||
}
|
||||
}
|
||||
|
||||
# ExchangePlan: check for ThisNode
|
||||
if ($mdType -eq "ExchangePlan") {
|
||||
$thisNode = $internalInfo.SelectSingleNode("xr:ThisNode", $ns)
|
||||
if (-not $thisNode) {
|
||||
Report-Warn "2. ExchangePlan missing xr:ThisNode in InternalInfo"
|
||||
} elseif ($thisNode.InnerText -notmatch $guidPattern) {
|
||||
Report-Error "2. ExchangePlan xr:ThisNode has invalid UUID"
|
||||
$check2Ok = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Check count mismatch
|
||||
$missingCats = @($expectedCategories | Where-Object { $foundCategories -notcontains $_ })
|
||||
if ($missingCats.Count -gt 0) {
|
||||
Report-Warn "2. Missing GeneratedType categories: $($missingCats -join ', ')"
|
||||
}
|
||||
|
||||
if ($check2Ok) {
|
||||
$catList = ($foundCategories | Sort-Object) -join ", "
|
||||
Report-OK "2. InternalInfo: $($genTypes.Count) GeneratedType ($catList)"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Report-OK "2. InternalInfo: N/A for $mdType"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 3: Properties — Name, Synonym ---
|
||||
|
||||
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
|
||||
}
|
||||
if ($nameVal.Length -gt 80) {
|
||||
Report-Warn "3. Properties: Name '$nameVal' is longer than 80 characters ($($nameVal.Length))"
|
||||
}
|
||||
}
|
||||
|
||||
# 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check3Ok) {
|
||||
$synInfo = if ($synPresent) { "Synonym present" } else { "no Synonym" }
|
||||
Report-OK "3. Properties: Name=`"$objName`", $synInfo"
|
||||
}
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 4: Property values — enum properties ---
|
||||
|
||||
if ($propsNode) {
|
||||
$enumChecked = 0
|
||||
$check4Ok = $true
|
||||
|
||||
foreach ($propName in $validPropertyValues.Keys) {
|
||||
$propNode = $propsNode.SelectSingleNode("md:$propName", $ns)
|
||||
if ($propNode -and $propNode.InnerText) {
|
||||
$val = $propNode.InnerText
|
||||
$allowed = $validPropertyValues[$propName]
|
||||
if ($allowed -notcontains $val) {
|
||||
Report-Error "4. Property '$propName' has invalid value '$val' (allowed: $($allowed -join ', '))"
|
||||
$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: StandardAttributes ---
|
||||
|
||||
if ($typesWithStdAttrs -contains $mdType) {
|
||||
$stdAttrNode = $propsNode.SelectSingleNode("md:StandardAttributes", $ns)
|
||||
if (-not $stdAttrNode) {
|
||||
# StandardAttributes block is optional for some types (e.g. Enum)
|
||||
Report-OK "5. StandardAttributes: absent (optional for $mdType)"
|
||||
} else {
|
||||
$stdAttrs = $stdAttrNode.SelectNodes("xr:StandardAttribute", $ns)
|
||||
$expectedStdAttrs = $standardAttributesByType[$mdType]
|
||||
$check5Ok = $true
|
||||
|
||||
$foundNames = @()
|
||||
foreach ($sa in $stdAttrs) {
|
||||
$saName = $sa.GetAttribute("name")
|
||||
if ($saName) {
|
||||
$foundNames += $saName
|
||||
if ($expectedStdAttrs -notcontains $saName) {
|
||||
# AccountingRegister has dynamic ExtDimension{N}/ExtDimensionType{N} and optional PeriodAdjustment
|
||||
$isDynamic = ($mdType -eq "AccountingRegister" -and ($saName -match '^ExtDimension\d+$' -or $saName -match '^ExtDimensionType\d+$' -or $saName -eq "PeriodAdjustment"))
|
||||
# CalculationRegister has conditional period attrs
|
||||
$isCalcDynamic = ($mdType -eq "CalculationRegister" -and $saName -in @("ActionPeriod","BegOfActionPeriod","EndOfActionPeriod","BegOfBasePeriod","EndOfBasePeriod"))
|
||||
if (-not $isDynamic -and -not $isCalcDynamic) {
|
||||
Report-Warn "5. Unexpected StandardAttribute '$saName' for $mdType"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Report-Error "5. StandardAttribute without 'name' attribute"
|
||||
$check5Ok = $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($expectedStdAttrs) {
|
||||
$missingAttrs = @($expectedStdAttrs | Where-Object { $foundNames -notcontains $_ })
|
||||
if ($missingAttrs.Count -gt 0) {
|
||||
Report-Warn "5. Missing StandardAttributes: $($missingAttrs -join ', ')"
|
||||
}
|
||||
}
|
||||
|
||||
if ($check5Ok) {
|
||||
Report-OK "5. StandardAttributes: $($stdAttrs.Count) entries"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Report-OK "5. StandardAttributes: N/A for $mdType"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 6: ChildObjects — allowed element types ---
|
||||
|
||||
$childObjNode = $typeNode.SelectSingleNode("md:ChildObjects", $ns)
|
||||
$allowedChildren = $childObjectRules[$mdType]
|
||||
|
||||
if ($childObjNode) {
|
||||
$check6Ok = $true
|
||||
$childCounts = @{}
|
||||
|
||||
foreach ($child in $childObjNode.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
$childTag = $child.LocalName
|
||||
|
||||
if ($allowedChildren -notcontains $childTag) {
|
||||
Report-Error "6. ChildObjects: disallowed element '$childTag' for $mdType"
|
||||
$check6Ok = $false
|
||||
}
|
||||
|
||||
if (-not $childCounts.ContainsKey($childTag)) {
|
||||
$childCounts[$childTag] = 0
|
||||
}
|
||||
$childCounts[$childTag]++
|
||||
}
|
||||
|
||||
if ($check6Ok) {
|
||||
$summary = ($childCounts.GetEnumerator() | Sort-Object Name | ForEach-Object { "$($_.Name)($($_.Value))" }) -join ", "
|
||||
if ($summary) {
|
||||
Report-OK "6. ChildObjects types: $summary"
|
||||
} else {
|
||||
Report-OK "6. ChildObjects: empty (valid for $mdType)"
|
||||
}
|
||||
}
|
||||
} elseif ($allowedChildren.Count -eq 0) {
|
||||
Report-OK "6. ChildObjects: absent (correct for $mdType)"
|
||||
} else {
|
||||
# Some types may have no children — that's OK
|
||||
Report-OK "6. ChildObjects: absent"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 7: Attributes/Dimensions/Resources/EnumValues/Columns — UUID, Name, Type ---
|
||||
|
||||
function Check-ChildElement {
|
||||
param(
|
||||
[System.Xml.XmlNode]$node,
|
||||
[string]$kind,
|
||||
[bool]$requireType
|
||||
)
|
||||
|
||||
$uuid = $node.GetAttribute("uuid")
|
||||
if (-not $uuid) {
|
||||
Report-Error "7. $kind missing uuid"
|
||||
return $false
|
||||
} elseif ($uuid -notmatch $guidPattern) {
|
||||
Report-Error "7. $kind has invalid uuid '$uuid'"
|
||||
return $false
|
||||
}
|
||||
|
||||
$elProps = $node.SelectSingleNode("md:Properties", $ns)
|
||||
if (-not $elProps) {
|
||||
Report-Error "7. $kind (uuid=$uuid) missing Properties"
|
||||
return $false
|
||||
}
|
||||
|
||||
$elName = $elProps.SelectSingleNode("md:Name", $ns)
|
||||
if (-not $elName -or -not $elName.InnerText) {
|
||||
Report-Error "7. $kind (uuid=$uuid) missing or empty Name"
|
||||
return $false
|
||||
}
|
||||
|
||||
$nameVal = $elName.InnerText
|
||||
if ($nameVal -notmatch $identPattern) {
|
||||
Report-Error "7. $kind '$nameVal' has invalid identifier"
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($requireType) {
|
||||
$typeEl = $elProps.SelectSingleNode("md:Type", $ns)
|
||||
if (-not $typeEl) {
|
||||
Report-Error "7. $kind '$nameVal' missing Type block"
|
||||
return $false
|
||||
}
|
||||
$v8Types = $typeEl.SelectNodes("v8:Type", $ns)
|
||||
$v8TypeSets = $typeEl.SelectNodes("v8:TypeSet", $ns)
|
||||
if ($v8Types.Count -eq 0 -and $v8TypeSets.Count -eq 0) {
|
||||
Report-Error "7. $kind '$nameVal' Type block has no v8:Type or v8:TypeSet"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
if ($childObjNode) {
|
||||
$check7Ok = $true
|
||||
$check7Count = 0
|
||||
$elementKinds = @("Attribute","Dimension","Resource","EnumValue","Column")
|
||||
|
||||
foreach ($kind in $elementKinds) {
|
||||
$elements = $childObjNode.SelectNodes("md:$kind", $ns)
|
||||
$requireType = ($kind -ne "EnumValue" -and $kind -ne "Column")
|
||||
foreach ($el in $elements) {
|
||||
if ($script:stopped) { break }
|
||||
$ok = Check-ChildElement -node $el -kind $kind -requireType $requireType
|
||||
if (-not $ok) { $check7Ok = $false }
|
||||
$check7Count++
|
||||
}
|
||||
}
|
||||
|
||||
if ($check7Ok -and $check7Count -gt 0) {
|
||||
Report-OK "7. Child elements: $check7Count items checked (UUID, Name, Type)"
|
||||
} elseif ($check7Count -eq 0) {
|
||||
Report-OK "7. Child elements: none to check"
|
||||
}
|
||||
} else {
|
||||
Report-OK "7. Child elements: N/A (no ChildObjects)"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 8: Name uniqueness ---
|
||||
|
||||
function Check-Uniqueness {
|
||||
param(
|
||||
[System.Xml.XmlNodeList]$nodes,
|
||||
[string]$kind
|
||||
)
|
||||
|
||||
$names = @{}
|
||||
$hasDupes = $false
|
||||
|
||||
foreach ($node in $nodes) {
|
||||
$elProps = $node.SelectSingleNode("md:Properties", $ns)
|
||||
if (-not $elProps) { continue }
|
||||
$elName = $elProps.SelectSingleNode("md:Name", $ns)
|
||||
if (-not $elName -or -not $elName.InnerText) { continue }
|
||||
|
||||
$nameVal = $elName.InnerText
|
||||
if ($names.ContainsKey($nameVal)) {
|
||||
Report-Error "8. Duplicate $kind name: '$nameVal'"
|
||||
$hasDupes = $true
|
||||
} else {
|
||||
$names[$nameVal] = $true
|
||||
}
|
||||
}
|
||||
|
||||
return (-not $hasDupes)
|
||||
}
|
||||
|
||||
if ($childObjNode) {
|
||||
$check8Ok = $true
|
||||
|
||||
# Attributes
|
||||
$attrs = $childObjNode.SelectNodes("md:Attribute", $ns)
|
||||
if ($attrs.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $attrs -kind "Attribute")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# TabularSections
|
||||
$tss = $childObjNode.SelectNodes("md:TabularSection", $ns)
|
||||
if ($tss.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $tss -kind "TabularSection")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# Dimensions
|
||||
$dims = $childObjNode.SelectNodes("md:Dimension", $ns)
|
||||
if ($dims.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $dims -kind "Dimension")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# Resources
|
||||
$ress = $childObjNode.SelectNodes("md:Resource", $ns)
|
||||
if ($ress.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $ress -kind "Resource")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# EnumValues
|
||||
$evs = $childObjNode.SelectNodes("md:EnumValue", $ns)
|
||||
if ($evs.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $evs -kind "EnumValue")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# Columns (DocumentJournal)
|
||||
$cols = $childObjNode.SelectNodes("md:Column", $ns)
|
||||
if ($cols.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $cols -kind "Column")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# URLTemplates (HTTPService)
|
||||
$urlTs = $childObjNode.SelectNodes("md:URLTemplate", $ns)
|
||||
if ($urlTs.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $urlTs -kind "URLTemplate")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
# Operations (WebService)
|
||||
$ops = $childObjNode.SelectNodes("md:Operation", $ns)
|
||||
if ($ops.Count -gt 0) {
|
||||
if (-not (Check-Uniqueness -nodes $ops -kind "Operation")) { $check8Ok = $false }
|
||||
}
|
||||
|
||||
if ($check8Ok) {
|
||||
Report-OK "8. Name uniqueness: all names unique"
|
||||
}
|
||||
} else {
|
||||
Report-OK "8. Name uniqueness: N/A"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 9: TabularSections — internal structure ---
|
||||
|
||||
if ($childObjNode) {
|
||||
$tsSections = $childObjNode.SelectNodes("md:TabularSection", $ns)
|
||||
if ($tsSections.Count -gt 0) {
|
||||
$check9Ok = $true
|
||||
$tsCount = 0
|
||||
|
||||
foreach ($ts in $tsSections) {
|
||||
if ($script:stopped) { break }
|
||||
$tsCount++
|
||||
|
||||
# UUID
|
||||
$tsUuid = $ts.GetAttribute("uuid")
|
||||
if (-not $tsUuid -or $tsUuid -notmatch $guidPattern) {
|
||||
Report-Error "9. TabularSection #${tsCount}: invalid or missing uuid"
|
||||
$check9Ok = $false
|
||||
}
|
||||
|
||||
# Name
|
||||
$tsProps = $ts.SelectSingleNode("md:Properties", $ns)
|
||||
$tsNameNode = if ($tsProps) { $tsProps.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||
$tsName = if ($tsNameNode) { $tsNameNode.InnerText } else { "(unnamed)" }
|
||||
|
||||
if (-not $tsNameNode -or -not $tsNameNode.InnerText) {
|
||||
Report-Error "9. TabularSection #${tsCount}: missing or empty Name"
|
||||
$check9Ok = $false
|
||||
}
|
||||
|
||||
# InternalInfo with 2 GeneratedType (TabularSection + TabularSectionRow)
|
||||
$tsIntInfo = $ts.SelectSingleNode("md:InternalInfo", $ns)
|
||||
if ($tsIntInfo) {
|
||||
$tsGens = $tsIntInfo.SelectNodes("xr:GeneratedType", $ns)
|
||||
if ($tsGens.Count -lt 2) {
|
||||
Report-Warn "9. TabularSection '$tsName': expected 2 GeneratedType, found $($tsGens.Count)"
|
||||
}
|
||||
}
|
||||
|
||||
# Attributes inside TS
|
||||
$tsChildObj = $ts.SelectSingleNode("md:ChildObjects", $ns)
|
||||
if ($tsChildObj) {
|
||||
$tsAttrs = $tsChildObj.SelectNodes("md:Attribute", $ns)
|
||||
$tsAttrNames = @{}
|
||||
foreach ($ta in $tsAttrs) {
|
||||
$taOk = Check-ChildElement -node $ta -kind "TabularSection '$tsName'.Attribute" -requireType $true
|
||||
if (-not $taOk) { $check9Ok = $false }
|
||||
|
||||
# Check name uniqueness within TS
|
||||
$taProps = $ta.SelectSingleNode("md:Properties", $ns)
|
||||
$taName = if ($taProps) { $taProps.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||
if ($taName -and $taName.InnerText) {
|
||||
if ($tsAttrNames.ContainsKey($taName.InnerText)) {
|
||||
Report-Error "9. Duplicate attribute '$($taName.InnerText)' in TabularSection '$tsName'"
|
||||
$check9Ok = $false
|
||||
} else {
|
||||
$tsAttrNames[$taName.InnerText] = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# StandardAttributes of TS: expect LineNumber
|
||||
$tsStdAttr = $tsProps.SelectSingleNode("md:StandardAttributes", $ns)
|
||||
if ($tsStdAttr) {
|
||||
$tsStdAttrs = $tsStdAttr.SelectNodes("xr:StandardAttribute", $ns)
|
||||
$hasLineNumber = $false
|
||||
foreach ($tsa in $tsStdAttrs) {
|
||||
if ($tsa.GetAttribute("name") -eq "LineNumber") { $hasLineNumber = $true }
|
||||
}
|
||||
if (-not $hasLineNumber) {
|
||||
Report-Warn "9. TabularSection '$tsName': missing LineNumber StandardAttribute"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check9Ok) {
|
||||
Report-OK "9. TabularSections: $tsCount sections, structure valid"
|
||||
}
|
||||
} else {
|
||||
Report-OK "9. TabularSections: none present"
|
||||
}
|
||||
} else {
|
||||
Report-OK "9. TabularSections: N/A"
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 10: Cross-property consistency ---
|
||||
|
||||
$check10Ok = $true
|
||||
$check10Issues = 0
|
||||
|
||||
if ($propsNode) {
|
||||
# HierarchyType set but Hierarchical = false
|
||||
$hierarchical = $propsNode.SelectSingleNode("md:Hierarchical", $ns)
|
||||
$hierarchyType = $propsNode.SelectSingleNode("md:HierarchyType", $ns)
|
||||
if ($hierarchical -and $hierarchyType -and $hierarchical.InnerText -eq "false" -and $hierarchyType.InnerText) {
|
||||
Report-Warn "10. HierarchyType='$($hierarchyType.InnerText)' but Hierarchical=false"
|
||||
$check10Issues++
|
||||
}
|
||||
|
||||
# CommonModule: no context enabled
|
||||
if ($mdType -eq "CommonModule") {
|
||||
$contexts = @("Server","ClientManagedApplication","ClientOrdinaryApplication","ExternalConnection","ServerCall","Global")
|
||||
$anyEnabled = $false
|
||||
foreach ($ctx in $contexts) {
|
||||
$ctxNode = $propsNode.SelectSingleNode("md:$ctx", $ns)
|
||||
if ($ctxNode -and $ctxNode.InnerText -eq "true") {
|
||||
$anyEnabled = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $anyEnabled) {
|
||||
Report-Warn "10. CommonModule: no execution context enabled"
|
||||
$check10Issues++
|
||||
}
|
||||
}
|
||||
|
||||
# EventSubscription: empty Handler
|
||||
if ($mdType -eq "EventSubscription") {
|
||||
$handler = $propsNode.SelectSingleNode("md:Handler", $ns)
|
||||
if (-not $handler -or -not $handler.InnerText.Trim()) {
|
||||
Report-Error "10. EventSubscription: empty Handler"
|
||||
$check10Ok = $false
|
||||
$check10Issues++
|
||||
}
|
||||
|
||||
# Empty Source
|
||||
$source = $propsNode.SelectSingleNode("md:Source", $ns)
|
||||
$hasSource = $false
|
||||
if ($source) {
|
||||
$sourceTypes = $source.SelectNodes("v8:Type", $ns)
|
||||
if ($sourceTypes.Count -gt 0) { $hasSource = $true }
|
||||
}
|
||||
if (-not $hasSource) {
|
||||
Report-Warn "10. EventSubscription: no Source types specified"
|
||||
$check10Issues++
|
||||
}
|
||||
}
|
||||
|
||||
# ScheduledJob: empty MethodName
|
||||
if ($mdType -eq "ScheduledJob") {
|
||||
$method = $propsNode.SelectSingleNode("md:MethodName", $ns)
|
||||
if (-not $method -or -not $method.InnerText.Trim()) {
|
||||
Report-Error "10. ScheduledJob: empty MethodName"
|
||||
$check10Ok = $false
|
||||
$check10Issues++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check10Ok -and $check10Issues -eq 0) {
|
||||
Report-OK "10. Cross-property consistency"
|
||||
} elseif ($check10Ok) {
|
||||
# Had warnings but no errors — already reported
|
||||
}
|
||||
|
||||
if ($script:stopped) { & $finalize; exit 1 }
|
||||
|
||||
# --- Check 11: HTTPService/WebService nested structure ---
|
||||
|
||||
if ($mdType -eq "HTTPService" -and $childObjNode) {
|
||||
$urlTemplates = $childObjNode.SelectNodes("md:URLTemplate", $ns)
|
||||
$check11Ok = $true
|
||||
$methodCount = 0
|
||||
|
||||
$validHTTPMethods = @("GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS","MERGE","CONNECT")
|
||||
|
||||
foreach ($ut in $urlTemplates) {
|
||||
if ($script:stopped) { break }
|
||||
|
||||
$utProps = $ut.SelectSingleNode("md:Properties", $ns)
|
||||
$utNameNode = if ($utProps) { $utProps.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||
$utName = if ($utNameNode) { $utNameNode.InnerText } else { "(unnamed)" }
|
||||
|
||||
# Template property
|
||||
$tpl = if ($utProps) { $utProps.SelectSingleNode("md:Template", $ns) } else { $null }
|
||||
if (-not $tpl -or -not $tpl.InnerText.Trim()) {
|
||||
Report-Error "11. HTTPService URLTemplate '$utName': empty Template"
|
||||
$check11Ok = $false
|
||||
}
|
||||
|
||||
# Methods inside URLTemplate
|
||||
$utChildObj = $ut.SelectSingleNode("md:ChildObjects", $ns)
|
||||
if ($utChildObj) {
|
||||
$methods = $utChildObj.SelectNodes("md:Method", $ns)
|
||||
foreach ($m in $methods) {
|
||||
$methodCount++
|
||||
$mProps = $m.SelectSingleNode("md:Properties", $ns)
|
||||
if ($mProps) {
|
||||
$httpMethod = $mProps.SelectSingleNode("md:HTTPMethod", $ns)
|
||||
if ($httpMethod -and $httpMethod.InnerText) {
|
||||
if ($validHTTPMethods -notcontains $httpMethod.InnerText) {
|
||||
Report-Error "11. HTTPService URLTemplate '$utName': invalid HTTPMethod '$($httpMethod.InnerText)'"
|
||||
$check11Ok = $false
|
||||
}
|
||||
} else {
|
||||
Report-Error "11. HTTPService URLTemplate '$utName': Method missing HTTPMethod"
|
||||
$check11Ok = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check11Ok) {
|
||||
Report-OK "11. HTTPService: $($urlTemplates.Count) URLTemplate(s), $methodCount method(s)"
|
||||
}
|
||||
} elseif ($mdType -eq "WebService" -and $childObjNode) {
|
||||
$operations = $childObjNode.SelectNodes("md:Operation", $ns)
|
||||
$check11Ok = $true
|
||||
$paramCount = 0
|
||||
|
||||
$validDirections = @("In","Out","InOut")
|
||||
|
||||
foreach ($op in $operations) {
|
||||
if ($script:stopped) { break }
|
||||
|
||||
$opProps = $op.SelectSingleNode("md:Properties", $ns)
|
||||
$opNameNode = if ($opProps) { $opProps.SelectSingleNode("md:Name", $ns) } else { $null }
|
||||
$opName = if ($opNameNode) { $opNameNode.InnerText } else { "(unnamed)" }
|
||||
|
||||
# ReturnType — XDTOReturningValueType
|
||||
$retType = if ($opProps) { $opProps.SelectSingleNode("md:XDTOReturningValueType", $ns) } else { $null }
|
||||
if (-not $retType -or -not $retType.InnerText.Trim()) {
|
||||
Report-Warn "11. WebService Operation '$opName': no XDTOReturningValueType"
|
||||
}
|
||||
|
||||
# Parameters inside Operation
|
||||
$opChildObj = $op.SelectSingleNode("md:ChildObjects", $ns)
|
||||
if ($opChildObj) {
|
||||
$params = $opChildObj.SelectNodes("md:Parameter", $ns)
|
||||
foreach ($p in $params) {
|
||||
$paramCount++
|
||||
$pProps = $p.SelectSingleNode("md:Properties", $ns)
|
||||
if ($pProps) {
|
||||
$dir = $pProps.SelectSingleNode("md:TransferDirection", $ns)
|
||||
if ($dir -and $dir.InnerText -and $validDirections -notcontains $dir.InnerText) {
|
||||
Report-Error "11. WebService Operation '$opName': Parameter has invalid TransferDirection '$($dir.InnerText)'"
|
||||
$check11Ok = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check11Ok) {
|
||||
Report-OK "11. WebService: $($operations.Count) operation(s), $paramCount parameter(s)"
|
||||
}
|
||||
} else {
|
||||
Report-OK "11. HTTPService/WebService: N/A"
|
||||
}
|
||||
|
||||
# --- Final output ---
|
||||
|
||||
& $finalize
|
||||
|
||||
if ($script:errors -gt 0) {
|
||||
exit 1
|
||||
}
|
||||
exit 0
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: mxl-compile
|
||||
description: Компиляция табличного документа (MXL) из JSON-определения
|
||||
description: Компиляция табличного документа (MXL) из JSON-определения. Используй когда нужно создать макет печатной формы
|
||||
argument-hint: <JsonPath> <OutputPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: mxl-decompile
|
||||
description: Декомпиляция табличного документа (MXL) в JSON-определение
|
||||
description: Декомпиляция табличного документа (MXL) в JSON-определение. Используй когда нужно получить редактируемое описание существующего макета
|
||||
argument-hint: <TemplatePath> [OutputPath]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: mxl-info
|
||||
description: Анализ структуры макета табличного документа (MXL) — области, параметры, наборы колонок
|
||||
description: Анализ структуры макета табличного документа (MXL) — области, параметры, наборы колонок. Используй при разработке печати — получить области и заполняемые параметры макета
|
||||
argument-hint: <TemplatePath> или <ProcessorName> <TemplateName>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: mxl-validate
|
||||
description: Валидация структурной корректности макета табличного документа (MXL)
|
||||
description: Валидация макета табличного документа (MXL). Используй после создания или модификации макета для проверки корректности
|
||||
argument-hint: <TemplatePath> или <ProcessorName> <TemplateName>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: role-compile
|
||||
description: Создание роли 1С — метаданные и Rights.xml из описания прав
|
||||
description: Создание роли 1С из описания прав. Используй когда нужно создать новую роль с набором прав на объекты
|
||||
argument-hint: <JsonPath> <RolesDir>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: role-info
|
||||
description: Компактная сводка прав роли 1С из Rights.xml — объекты, права, RLS, шаблоны ограничений
|
||||
description: Компактная сводка прав роли 1С из Rights.xml — объекты, права, RLS, шаблоны ограничений. Используй для аудита прав — какие объекты и действия доступны, ограничения RLS
|
||||
argument-hint: <RightsPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: role-validate
|
||||
description: Валидация структурной корректности роли 1С (Rights.xml) — формат, права, RLS, шаблоны
|
||||
description: Валидация роли 1С. Используй после создания или модификации роли для проверки корректности
|
||||
argument-hint: <RightsPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skd-compile
|
||||
description: Компиляция схемы компоновки данных 1С (СКД) — Template.xml из компактного JSON-определения
|
||||
description: Компиляция схемы компоновки данных 1С (СКД) из компактного JSON-определения. Используй когда нужно создать СКД с нуля
|
||||
argument-hint: <JsonPath> <OutputPath>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skd-edit
|
||||
description: Точечное редактирование схемы компоновки данных 1С (СКД) — добавление/удаление полей, итогов, фильтров, параметров, вычисляемых полей
|
||||
description: Точечное редактирование схемы компоновки данных 1С (СКД). Используй когда нужно модифицировать существующую СКД — добавить поля, итоги, фильтры, параметры
|
||||
argument-hint: <TemplatePath> -Operation <op> -Value <value>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skd-info
|
||||
description: Анализ структуры схемы компоновки данных 1С (СКД) — наборы, поля, параметры, варианты
|
||||
description: Анализ структуры схемы компоновки данных 1С (СКД) — наборы, поля, параметры, варианты. Используй для понимания отчёта — источник данных (запрос), доступные поля, параметры
|
||||
argument-hint: <TemplatePath> [-Mode overview|query|fields|links|calculated|resources|params|variant|templates|trace] [-Name <dataset|variant|field|group>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: skd-validate
|
||||
description: Валидация структурной корректности схемы компоновки данных 1С (СКД) — Template.xml
|
||||
description: Валидация схемы компоновки данных 1С (СКД). Используй после создания или модификации СКД для проверки корректности
|
||||
argument-hint: <TemplatePath> [-MaxErrors 20]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
name: subsystem-compile
|
||||
description: Создать подсистему 1С — XML-исходники из JSON-определения. Используй когда пользователь просит добавить подсистему (раздел) в конфигурацию
|
||||
argument-hint: [-DefinitionFile <json> | -Value <json-string>] -OutputDir <ConfigDir> [-Parent <path>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /subsystem-compile — генерация подсистемы из JSON
|
||||
|
||||
Принимает JSON-определение подсистемы → генерирует XML + файловую структуру + регистрирует в родителе (Configuration.xml или родительская подсистема).
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `DefinitionFile` | Путь к JSON-файлу определения |
|
||||
| `Value` | Инлайн JSON-строка (альтернатива DefinitionFile) |
|
||||
| `OutputDir` | Корень выгрузки (где `Subsystems/`, `Configuration.xml`) |
|
||||
| `Parent` | Путь к XML родительской подсистемы (для вложенных) |
|
||||
| `NoValidate` | Пропустить авто-валидацию |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File '.claude\skills\subsystem-compile\scripts\subsystem-compile.ps1' -Value '<json>' -OutputDir '<ConfigDir>'
|
||||
```
|
||||
|
||||
## JSON-определение
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "МояПодсистема",
|
||||
"synonym": "Моя подсистема",
|
||||
"comment": "",
|
||||
"includeInCommandInterface": true,
|
||||
"useOneCommand": false,
|
||||
"explanation": "Описание раздела",
|
||||
"picture": "CommonPicture.МояКартинка",
|
||||
"content": ["Catalog.Товары", "Document.Заказ"],
|
||||
"children": ["ДочерняяА", "ДочерняяБ"]
|
||||
}
|
||||
```
|
||||
|
||||
Минимально: только `name`. Остальное — дефолты.
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Минимальная подсистема
|
||||
... -Value '{"name":"Тест"}' -OutputDir config/
|
||||
|
||||
# С составом и картинкой
|
||||
... -Value '{"name":"Продажи","content":["Catalog.Товары","Report.Продажи"],"picture":"CommonPicture.Продажи"}' -OutputDir config/
|
||||
|
||||
# Вложенная подсистема
|
||||
... -Value '{"name":"Дочерняя"}' -OutputDir config/ -Parent config/Subsystems/Продажи.xml
|
||||
```
|
||||
|
||||
## Что генерируется
|
||||
|
||||
- `{OutputDir}/Subsystems/{Name}.xml` — определение подсистемы
|
||||
- `{OutputDir}/Subsystems/{Name}/` — каталог (если есть children)
|
||||
- `Configuration.xml` или родительская подсистема — регистрация в `<ChildObjects>`
|
||||
@@ -0,0 +1,338 @@
|
||||
# subsystem-compile v1.0 — Create 1C subsystem from JSON definition
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$DefinitionFile,
|
||||
[string]$Value,
|
||||
[Parameter(Mandatory)][string]$OutputDir,
|
||||
[string]$Parent,
|
||||
[switch]$NoValidate
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- 1. Load JSON ---
|
||||
if ($DefinitionFile -and $Value) {
|
||||
Write-Error "Cannot use both -DefinitionFile and -Value"
|
||||
exit 1
|
||||
}
|
||||
if (-not $DefinitionFile -and -not $Value) {
|
||||
Write-Error "Either -DefinitionFile or -Value is required"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($DefinitionFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($DefinitionFile)) {
|
||||
$DefinitionFile = Join-Path (Get-Location).Path $DefinitionFile
|
||||
}
|
||||
if (-not (Test-Path $DefinitionFile)) {
|
||||
Write-Error "Definition file not found: $DefinitionFile"
|
||||
exit 1
|
||||
}
|
||||
$json = Get-Content -Raw -Encoding UTF8 $DefinitionFile
|
||||
} else {
|
||||
$json = $Value
|
||||
}
|
||||
|
||||
$def = $json | ConvertFrom-Json
|
||||
|
||||
if (-not $def.name) {
|
||||
Write-Error "JSON must have 'name' field"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$objName = "$($def.name)"
|
||||
|
||||
# Resolve OutputDir
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutputDir)) {
|
||||
$OutputDir = Join-Path (Get-Location).Path $OutputDir
|
||||
}
|
||||
|
||||
# --- 2. XML helpers ---
|
||||
$script:xml = New-Object System.Text.StringBuilder 8192
|
||||
|
||||
function X([string]$text) {
|
||||
$script:xml.AppendLine($text) | Out-Null
|
||||
}
|
||||
|
||||
function Esc-Xml([string]$s) {
|
||||
return $s.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"')
|
||||
}
|
||||
|
||||
function Split-CamelCase([string]$name) {
|
||||
if (-not $name) { return $name }
|
||||
$result = [regex]::Replace($name, '([a-z\u0430-\u044F\u0451])([A-Z\u0410-\u042F\u0401])', '$1 $2')
|
||||
if ($result.Length -gt 1) {
|
||||
$result = $result.Substring(0,1) + $result.Substring(1).ToLower()
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
function Emit-MLText([string]$indent, [string]$tag, [string]$text) {
|
||||
if (-not $text) {
|
||||
X "$indent<$tag/>"
|
||||
return
|
||||
}
|
||||
X "$indent<$tag>"
|
||||
X "$indent`t<v8:item>"
|
||||
X "$indent`t`t<v8:lang>ru</v8:lang>"
|
||||
X "$indent`t`t<v8:content>$(Esc-Xml $text)</v8:content>"
|
||||
X "$indent`t</v8:item>"
|
||||
X "$indent</$tag>"
|
||||
}
|
||||
|
||||
function New-Guid-String {
|
||||
return [System.Guid]::NewGuid().ToString()
|
||||
}
|
||||
|
||||
# --- 3. Resolve defaults ---
|
||||
$synonym = if ($def.synonym) { "$($def.synonym)" } else { Split-CamelCase $objName }
|
||||
$comment = if ($def.comment) { "$($def.comment)" } else { "" }
|
||||
$includeHelpInContents = "true"
|
||||
$includeInCI = if ($null -ne $def.includeInCommandInterface) { "$($def.includeInCommandInterface)".ToLower() } else { "true" }
|
||||
$useOneCommand = if ($null -ne $def.useOneCommand) { "$($def.useOneCommand)".ToLower() } else { "false" }
|
||||
$explanation = if ($def.explanation) { "$($def.explanation)" } else { "" }
|
||||
$picture = if ($def.picture) { "$($def.picture)" } else { "" }
|
||||
|
||||
$contentItems = @()
|
||||
if ($def.content) {
|
||||
foreach ($c in $def.content) { $contentItems += "$c" }
|
||||
}
|
||||
|
||||
$children = @()
|
||||
if ($def.children) {
|
||||
foreach ($ch in $def.children) { $children += "$ch" }
|
||||
}
|
||||
|
||||
# --- 4. Build XML ---
|
||||
$uuid = New-Guid-String
|
||||
$indent = "`t`t`t"
|
||||
|
||||
X '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
X '<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">'
|
||||
X "`t<Subsystem uuid=`"$uuid`">"
|
||||
X "`t`t<Properties>"
|
||||
|
||||
# Name
|
||||
X "`t`t`t<Name>$(Esc-Xml $objName)</Name>"
|
||||
|
||||
# Synonym
|
||||
Emit-MLText "`t`t`t" "Synonym" $synonym
|
||||
|
||||
# Comment
|
||||
if ($comment) {
|
||||
X "`t`t`t<Comment>$(Esc-Xml $comment)</Comment>"
|
||||
} else {
|
||||
X "`t`t`t<Comment/>"
|
||||
}
|
||||
|
||||
# Boolean properties
|
||||
X "`t`t`t<IncludeHelpInContents>$includeHelpInContents</IncludeHelpInContents>"
|
||||
X "`t`t`t<IncludeInCommandInterface>$includeInCI</IncludeInCommandInterface>"
|
||||
X "`t`t`t<UseOneCommand>$useOneCommand</UseOneCommand>"
|
||||
|
||||
# Explanation
|
||||
Emit-MLText "`t`t`t" "Explanation" $explanation
|
||||
|
||||
# Picture
|
||||
if ($picture) {
|
||||
X "`t`t`t<Picture>"
|
||||
X "`t`t`t`t<xr:Ref>$picture</xr:Ref>"
|
||||
X "`t`t`t`t<xr:LoadTransparent>false</xr:LoadTransparent>"
|
||||
X "`t`t`t</Picture>"
|
||||
} else {
|
||||
X "`t`t`t<Picture/>"
|
||||
}
|
||||
|
||||
# Content
|
||||
if ($contentItems.Count -gt 0) {
|
||||
X "`t`t`t<Content>"
|
||||
foreach ($item in $contentItems) {
|
||||
X "`t`t`t`t<xr:Item xsi:type=`"xr:MDObjectRef`">$(Esc-Xml $item)</xr:Item>"
|
||||
}
|
||||
X "`t`t`t</Content>"
|
||||
} else {
|
||||
X "`t`t`t<Content/>"
|
||||
}
|
||||
|
||||
X "`t`t</Properties>"
|
||||
|
||||
# ChildObjects
|
||||
if ($children.Count -gt 0) {
|
||||
X "`t`t<ChildObjects>"
|
||||
foreach ($ch in $children) {
|
||||
X "`t`t`t<Subsystem>$(Esc-Xml $ch)</Subsystem>"
|
||||
}
|
||||
X "`t`t</ChildObjects>"
|
||||
} else {
|
||||
X "`t`t<ChildObjects/>"
|
||||
}
|
||||
|
||||
X "`t</Subsystem>"
|
||||
X '</MetaDataObject>'
|
||||
|
||||
# --- 5. Write files ---
|
||||
|
||||
# Determine target directory
|
||||
if ($Parent) {
|
||||
# Nested subsystem
|
||||
if (-not [System.IO.Path]::IsPathRooted($Parent)) {
|
||||
$Parent = Join-Path (Get-Location).Path $Parent
|
||||
}
|
||||
if (-not (Test-Path $Parent)) {
|
||||
Write-Error "Parent subsystem not found: $Parent"
|
||||
exit 1
|
||||
}
|
||||
$parentDir = [System.IO.Path]::GetDirectoryName($Parent)
|
||||
$parentBaseName = [System.IO.Path]::GetFileNameWithoutExtension($Parent)
|
||||
$subsDir = Join-Path (Join-Path $parentDir $parentBaseName) "Subsystems"
|
||||
} else {
|
||||
# Top-level subsystem
|
||||
$subsDir = Join-Path $OutputDir "Subsystems"
|
||||
}
|
||||
|
||||
if (-not (Test-Path $subsDir)) {
|
||||
New-Item -ItemType Directory -Path $subsDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$targetXml = Join-Path $subsDir "$objName.xml"
|
||||
|
||||
# Write XML
|
||||
$xmlContent = $script:xml.ToString()
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($targetXml, $xmlContent, $utf8Bom)
|
||||
Write-Host "[OK] Created: $targetXml"
|
||||
|
||||
# Create subdirectory if children exist
|
||||
if ($children.Count -gt 0) {
|
||||
$childSubsDir = Join-Path (Join-Path $subsDir $objName) "Subsystems"
|
||||
if (-not (Test-Path $childSubsDir)) {
|
||||
New-Item -ItemType Directory -Path $childSubsDir -Force | Out-Null
|
||||
Write-Host "[OK] Created directory: $childSubsDir"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 6. Register in parent ---
|
||||
$parentXmlPath = $null
|
||||
if ($Parent) {
|
||||
$parentXmlPath = $Parent
|
||||
} else {
|
||||
$configXml = Join-Path $OutputDir "Configuration.xml"
|
||||
if (Test-Path $configXml) {
|
||||
$parentXmlPath = $configXml
|
||||
}
|
||||
}
|
||||
|
||||
if ($parentXmlPath -and (Test-Path $parentXmlPath)) {
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
$doc.Load($parentXmlPath)
|
||||
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$ns.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
|
||||
# Find ChildObjects
|
||||
$childObjects = $null
|
||||
if ($Parent) {
|
||||
$childObjects = $doc.SelectSingleNode("//md:Subsystem/md:ChildObjects", $ns)
|
||||
} else {
|
||||
$childObjects = $doc.SelectSingleNode("//md:Configuration/md:ChildObjects", $ns)
|
||||
}
|
||||
|
||||
if ($childObjects) {
|
||||
# Check for self-closing tag
|
||||
$isSelfClosing = (-not $childObjects.HasChildNodes) -or ($childObjects.IsEmpty)
|
||||
|
||||
# Check if already registered
|
||||
$alreadyExists = $false
|
||||
foreach ($child in $childObjects.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem" -and $child.InnerText -eq $objName) {
|
||||
$alreadyExists = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $alreadyExists) {
|
||||
$newEl = $doc.CreateElement("Subsystem", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$newEl.InnerText = $objName
|
||||
|
||||
if ($isSelfClosing) {
|
||||
# Expand self-closing tag
|
||||
$parentIndent = ""
|
||||
$prev = $childObjects.PreviousSibling
|
||||
if ($prev -and ($prev.NodeType -eq 'Whitespace' -or $prev.NodeType -eq 'SignificantWhitespace')) {
|
||||
if ($prev.Value -match '(\t+)$') { $parentIndent = $Matches[1] }
|
||||
}
|
||||
$childIndent = "$parentIndent`t"
|
||||
$ws1 = $doc.CreateWhitespace("`r`n$childIndent")
|
||||
$ws2 = $doc.CreateWhitespace("`r`n$parentIndent")
|
||||
$childObjects.AppendChild($ws1) | Out-Null
|
||||
$childObjects.AppendChild($newEl) | Out-Null
|
||||
$childObjects.AppendChild($ws2) | Out-Null
|
||||
} else {
|
||||
# Insert before trailing whitespace
|
||||
$childIndent = "`t`t`t"
|
||||
foreach ($child in $childObjects.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Whitespace' -or $child.NodeType -eq 'SignificantWhitespace') {
|
||||
if ($child.Value -match '^\r?\n(\t+)') { $childIndent = $Matches[1]; break }
|
||||
}
|
||||
}
|
||||
$trailing = $childObjects.LastChild
|
||||
$ws = $doc.CreateWhitespace("`r`n$childIndent")
|
||||
if ($trailing -and ($trailing.NodeType -eq 'Whitespace' -or $trailing.NodeType -eq 'SignificantWhitespace')) {
|
||||
$childObjects.InsertBefore($ws, $trailing) | Out-Null
|
||||
$childObjects.InsertBefore($newEl, $trailing) | Out-Null
|
||||
} else {
|
||||
$childObjects.AppendChild($ws) | Out-Null
|
||||
$childObjects.AppendChild($newEl) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# Save parent XML
|
||||
$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)
|
||||
$doc.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"')
|
||||
[System.IO.File]::WriteAllText($parentXmlPath, $text, $utf8Bom)
|
||||
|
||||
Write-Host "[OK] Registered in: $parentXmlPath"
|
||||
} else {
|
||||
Write-Host "[SKIP] Already registered in: $parentXmlPath"
|
||||
}
|
||||
} else {
|
||||
Write-Host "[WARN] ChildObjects not found in: $parentXmlPath"
|
||||
}
|
||||
} else {
|
||||
Write-Host "[INFO] No parent XML to register in"
|
||||
}
|
||||
|
||||
# --- 7. Auto-validate ---
|
||||
if (-not $NoValidate) {
|
||||
$validateScript = Join-Path (Join-Path $PSScriptRoot "..\..\subsystem-validate") "scripts\subsystem-validate.ps1"
|
||||
$validateScript = [System.IO.Path]::GetFullPath($validateScript)
|
||||
if (Test-Path $validateScript) {
|
||||
Write-Host ""
|
||||
Write-Host "--- Running subsystem-validate ---"
|
||||
& powershell.exe -NoProfile -File $validateScript -SubsystemPath $targetXml
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== subsystem-compile summary ==="
|
||||
Write-Host " Name: $objName"
|
||||
Write-Host " UUID: $uuid"
|
||||
Write-Host " Content: $($contentItems.Count) objects"
|
||||
Write-Host " Children: $($children.Count)"
|
||||
Write-Host " File: $targetXml"
|
||||
exit 0
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: subsystem-edit
|
||||
description: Точечное редактирование подсистемы 1С. Используй когда нужно добавить или удалить объекты из подсистемы, управлять дочерними подсистемами или изменить свойства
|
||||
argument-hint: -SubsystemPath <path> -Operation <op> -Value <value>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /subsystem-edit — редактирование подсистемы 1С
|
||||
|
||||
Точечное редактирование XML подсистемы: состав, дочерние подсистемы, свойства.
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `SubsystemPath` | Путь к XML-файлу подсистемы |
|
||||
| `DefinitionFile` | JSON-файл с массивом операций |
|
||||
| `Operation` | Одна операция (альтернатива DefinitionFile) |
|
||||
| `Value` | Значение для операции |
|
||||
| `NoValidate` | Пропустить авто-валидацию |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File '.claude\skills\subsystem-edit\scripts\subsystem-edit.ps1' -SubsystemPath '<path>' -Operation add-content -Value 'Catalog.Товары'
|
||||
```
|
||||
|
||||
## Операции
|
||||
|
||||
| Операция | Значение | Описание |
|
||||
|----------|----------|----------|
|
||||
| `add-content` | `"Catalog.X"` или `["Catalog.X","Document.Y"]` | Добавить объекты в Content |
|
||||
| `remove-content` | `"Catalog.X"` или `["Catalog.X"]` | Удалить объекты из Content |
|
||||
| `add-child` | `"ИмяПодсистемы"` | Добавить дочернюю подсистему в ChildObjects |
|
||||
| `remove-child` | `"ИмяПодсистемы"` | Удалить дочернюю подсистему |
|
||||
| `set-property` | `{"name":"prop","value":"val"}` | Изменить свойство (Synonym, IncludeInCommandInterface, UseOneCommand, etc.) |
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Добавить объект в состав
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Operation add-content -Value "Document.Заказ"
|
||||
|
||||
# Добавить несколько объектов
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Operation add-content -Value '["Catalog.Товары","Report.Продажи"]'
|
||||
|
||||
# Удалить объект из состава
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Operation remove-content -Value "Report.Старый"
|
||||
|
||||
# Добавить дочернюю подсистему
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Operation add-child -Value "НоваяДочерняя"
|
||||
|
||||
# Изменить свойство
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Operation set-property -Value '{"name":"IncludeInCommandInterface","value":"false"}'
|
||||
```
|
||||
@@ -0,0 +1,403 @@
|
||||
# subsystem-edit v1.0 — Edit existing 1C subsystem XML
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$SubsystemPath,
|
||||
[string]$DefinitionFile,
|
||||
[ValidateSet("add-content","remove-content","add-child","remove-child","set-property")]
|
||||
[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($SubsystemPath)) {
|
||||
$SubsystemPath = Join-Path (Get-Location).Path $SubsystemPath
|
||||
}
|
||||
if (Test-Path $SubsystemPath -PathType Container) {
|
||||
$dirName = Split-Path $SubsystemPath -Leaf
|
||||
$candidate = Join-Path $SubsystemPath "$dirName.xml"
|
||||
if (Test-Path $candidate) { $SubsystemPath = $candidate }
|
||||
else { Write-Error "No $dirName.xml found in directory"; exit 1 }
|
||||
}
|
||||
if (-not (Test-Path $SubsystemPath)) { Write-Error "File not found: $SubsystemPath"; exit 1 }
|
||||
$resolvedPath = (Resolve-Path $SubsystemPath).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:sub = $null
|
||||
foreach ($child in $root.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem") {
|
||||
$script:sub = $child; break
|
||||
}
|
||||
}
|
||||
if (-not $script:sub) { Write-Error "No <Subsystem> element found"; exit 1 }
|
||||
|
||||
$script:propsEl = $null
|
||||
$script:childObjsEl = $null
|
||||
foreach ($child in $script:sub.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 "Subsystem: $($script:objName)"
|
||||
|
||||
# --- XML manipulation helpers (from meta-edit pattern) ---
|
||||
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
|
||||
}
|
||||
|
||||
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 the element is self-closing (empty), add whitespace for children
|
||||
if (-not $container.HasChildNodes -or $container.IsEmpty) {
|
||||
$childIndent = "$parentIndent`t"
|
||||
# The element is self-closing; we need to add something to make it non-empty
|
||||
# Adding a whitespace node will force opening+closing tags
|
||||
$closeWs = $script:xmlDoc.CreateWhitespace("`r`n$parentIndent")
|
||||
$container.AppendChild($closeWs) | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# --- Parse value: string or JSON array ---
|
||||
function Parse-ValueList([string]$val) {
|
||||
$val = $val.Trim()
|
||||
if ($val.StartsWith("[")) {
|
||||
$arr = $val | ConvertFrom-Json
|
||||
$result = @(); foreach ($item in $arr) { $result += "$item" }
|
||||
return ,$result
|
||||
}
|
||||
return @($val)
|
||||
}
|
||||
|
||||
# --- Operations ---
|
||||
function Do-AddContent([string[]]$items) {
|
||||
$contentEl = $null
|
||||
foreach ($child in $script:propsEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Content") {
|
||||
$contentEl = $child; break
|
||||
}
|
||||
}
|
||||
if (-not $contentEl) { Write-Error "No <Content> element found"; exit 1 }
|
||||
|
||||
# Get existing items for dedup
|
||||
$existing = @()
|
||||
foreach ($child in $contentEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Item") {
|
||||
$existing += $child.InnerText.Trim()
|
||||
}
|
||||
}
|
||||
|
||||
# Determine indentation
|
||||
$propsIndent = Get-ChildIndent $script:propsEl
|
||||
$contentIndent = "$propsIndent`t"
|
||||
|
||||
# Expand self-closing if needed
|
||||
if (-not $contentEl.HasChildNodes -or $contentEl.IsEmpty) {
|
||||
Expand-SelfClosingElement $contentEl $propsIndent
|
||||
$contentIndent = "$propsIndent`t"
|
||||
} else {
|
||||
$contentIndent = Get-ChildIndent $contentEl
|
||||
}
|
||||
|
||||
foreach ($item in $items) {
|
||||
if ($item -in $existing) {
|
||||
Warn "Content already contains: $item"
|
||||
continue
|
||||
}
|
||||
$fragXml = "<xr:Item xsi:type=`"xr:MDObjectRef`">$item</xr:Item>"
|
||||
$nodes = Import-Fragment $fragXml
|
||||
if ($nodes.Count -gt 0) {
|
||||
Insert-BeforeElement $contentEl $nodes[0] $null $contentIndent
|
||||
$script:addCount++
|
||||
Info "Added content: $item"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Do-RemoveContent([string[]]$items) {
|
||||
$contentEl = $null
|
||||
foreach ($child in $script:propsEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Content") {
|
||||
$contentEl = $child; break
|
||||
}
|
||||
}
|
||||
if (-not $contentEl) { Write-Error "No <Content> element found"; exit 1 }
|
||||
|
||||
foreach ($item in $items) {
|
||||
$found = $false
|
||||
foreach ($child in @($contentEl.ChildNodes)) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Item" -and $child.InnerText.Trim() -eq $item) {
|
||||
Remove-NodeWithWhitespace $child
|
||||
$script:removeCount++
|
||||
Info "Removed content: $item"
|
||||
$found = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $found) { Warn "Content item not found: $item" }
|
||||
}
|
||||
}
|
||||
|
||||
function Do-AddChild([string]$childName) {
|
||||
if (-not $script:childObjsEl) { Write-Error "No <ChildObjects> element found"; exit 1 }
|
||||
|
||||
# Dedup check
|
||||
foreach ($child in $script:childObjsEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem" -and $child.InnerText.Trim() -eq $childName) {
|
||||
Warn "ChildObjects already contains: $childName"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$subIndent = Get-ChildIndent $script:sub
|
||||
if (-not $script:childObjsEl.HasChildNodes -or $script:childObjsEl.IsEmpty) {
|
||||
Expand-SelfClosingElement $script:childObjsEl $subIndent
|
||||
}
|
||||
$childIndent = Get-ChildIndent $script:childObjsEl
|
||||
|
||||
$newEl = $script:xmlDoc.CreateElement("Subsystem", $script:mdNs)
|
||||
$newEl.InnerText = $childName
|
||||
Insert-BeforeElement $script:childObjsEl $newEl $null $childIndent
|
||||
$script:addCount++
|
||||
Info "Added child subsystem: $childName"
|
||||
}
|
||||
|
||||
function Do-RemoveChild([string]$childName) {
|
||||
if (-not $script:childObjsEl) { Write-Error "No <ChildObjects> element found"; exit 1 }
|
||||
|
||||
$found = $false
|
||||
foreach ($child in @($script:childObjsEl.ChildNodes)) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem" -and $child.InnerText.Trim() -eq $childName) {
|
||||
Remove-NodeWithWhitespace $child
|
||||
$script:removeCount++
|
||||
Info "Removed child subsystem: $childName"
|
||||
$found = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $found) { Warn "Child subsystem not found: $childName" }
|
||||
}
|
||||
|
||||
function Do-SetProperty([string]$jsonVal) {
|
||||
$propDef = $jsonVal | ConvertFrom-Json
|
||||
$propName = "$($propDef.name)"
|
||||
$propValue = "$($propDef.value)"
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
$boolProps = @("IncludeInCommandInterface","UseOneCommand","IncludeHelpInContents")
|
||||
if ($propName -in $boolProps) {
|
||||
$propEl.InnerText = $propValue.ToLower()
|
||||
$script:modifyCount++
|
||||
Info "Set $propName = $propValue"
|
||||
return
|
||||
}
|
||||
|
||||
$mlProps = @("Synonym","Explanation")
|
||||
if ($propName -in $mlProps) {
|
||||
if (-not $propValue) {
|
||||
# Clear - make self-closing
|
||||
$propEl.InnerXml = ""
|
||||
$script:modifyCount++
|
||||
Info "Cleared $propName"
|
||||
} else {
|
||||
$indent = Get-ChildIndent $script:propsEl
|
||||
$mlXml = "`r`n$indent`t<v8:item>`r`n$indent`t`t<v8:lang>ru</v8:lang>`r`n$indent`t`t<v8:content>$([System.Security.SecurityElement]::Escape($propValue))</v8:content>`r`n$indent`t</v8:item>`r`n$indent"
|
||||
$propEl.InnerXml = $mlXml
|
||||
$script:modifyCount++
|
||||
Info "Set $propName = `"$propValue`""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ($propName -eq "Comment") {
|
||||
if (-not $propValue) { $propEl.InnerXml = "" }
|
||||
else { $propEl.InnerText = $propValue }
|
||||
$script:modifyCount++
|
||||
Info "Set Comment = `"$propValue`""
|
||||
return
|
||||
}
|
||||
|
||||
if ($propName -eq "Picture") {
|
||||
if (-not $propValue) {
|
||||
$propEl.InnerXml = ""
|
||||
} else {
|
||||
$indent = Get-ChildIndent $script:propsEl
|
||||
$picXml = "`r`n$indent`t<xr:Ref>$propValue</xr:Ref>`r`n$indent`t<xr:LoadTransparent>false</xr:LoadTransparent>`r`n$indent"
|
||||
$propEl.InnerXml = $picXml
|
||||
}
|
||||
$script:modifyCount++
|
||||
Info "Set Picture = `"$propValue`""
|
||||
return
|
||||
}
|
||||
|
||||
# Generic text property
|
||||
$propEl.InnerText = $propValue
|
||||
$script:modifyCount++
|
||||
Info "Set $propName = `"$propValue`""
|
||||
}
|
||||
|
||||
# --- 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) {
|
||||
"add-content" { Do-AddContent (Parse-ValueList $opValue) }
|
||||
"remove-content" { Do-RemoveContent (Parse-ValueList $opValue) }
|
||||
"add-child" { Do-AddChild $opValue }
|
||||
"remove-child" { Do-RemoveChild $opValue }
|
||||
"set-property" { Do-SetProperty $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 "..\..\subsystem-validate") "scripts\subsystem-validate.ps1"
|
||||
$validateScript = [System.IO.Path]::GetFullPath($validateScript)
|
||||
if (Test-Path $validateScript) {
|
||||
Write-Host ""
|
||||
Write-Host "--- Running subsystem-validate ---"
|
||||
& powershell.exe -NoProfile -File $validateScript -SubsystemPath $resolvedPath
|
||||
}
|
||||
}
|
||||
|
||||
# --- Summary ---
|
||||
Write-Host ""
|
||||
Write-Host "=== subsystem-edit summary ==="
|
||||
Write-Host " Subsystem: $($script:objName)"
|
||||
Write-Host " Added: $($script:addCount)"
|
||||
Write-Host " Removed: $($script:removeCount)"
|
||||
Write-Host " Modified: $($script:modifyCount)"
|
||||
exit 0
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: subsystem-info
|
||||
description: Анализ структуры подсистемы 1С из XML-выгрузки — состав, дочерние подсистемы, командный интерфейс, дерево иерархии. Используй для изучения структуры подсистем и навигации по конфигурации
|
||||
argument-hint: <SubsystemPath> [-Mode overview|content|ci|tree] [-Name <элемент>]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /subsystem-info — Структура подсистемы 1С
|
||||
|
||||
Читает XML подсистемы из выгрузки конфигурации 1С и выводит компактное описание структуры.
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `SubsystemPath` | Путь к XML-файлу подсистемы, каталогу подсистемы или каталогу `Subsystems/` (для tree) |
|
||||
| `Mode` | Режим: `overview` (default), `content`, `ci`, `tree` |
|
||||
| `Name` | Drill-down: тип объекта в content, секция в ci, имя подсистемы в tree |
|
||||
| `Limit` / `Offset` | Пагинация (по умолчанию 150 строк) |
|
||||
| `OutFile` | Записать результат в файл (UTF-8 BOM) |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\subsystem-info\scripts\subsystem-info.ps1 -SubsystemPath "<путь>"
|
||||
```
|
||||
|
||||
## Четыре режима
|
||||
|
||||
| Режим | Что показывает |
|
||||
|---|---|
|
||||
| `overview` *(default)* | Компактная сводка: свойства, состав (сгруппирован по типам), дочерние подсистемы, наличие CI |
|
||||
| `content` | Список Content с группировкой по типу объекта. `-Name Catalog` — только каталоги |
|
||||
| `ci` | Разбор CommandInterface.xml: видимость, размещение, порядок команд/подсистем/групп |
|
||||
| `tree` | Рекурсивное дерево иерархии подсистем с маркерами [CI], [OneCmd], [Скрыт] |
|
||||
|
||||
## Примеры
|
||||
|
||||
```powershell
|
||||
# Обзор подсистемы
|
||||
... -SubsystemPath Subsystems/Продажи.xml
|
||||
|
||||
# Состав подсистемы
|
||||
... -SubsystemPath Subsystems/Администрирование.xml -Mode content
|
||||
|
||||
# Только документы в составе
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Mode content -Name Document
|
||||
|
||||
# Командный интерфейс подсистемы
|
||||
... -SubsystemPath Subsystems/Продажи.xml -Mode ci
|
||||
|
||||
# Дерево подсистем от корня
|
||||
... -SubsystemPath Subsystems -Mode tree
|
||||
|
||||
# Дерево от конкретной подсистемы
|
||||
... -SubsystemPath Subsystems/Администрирование.xml -Mode tree
|
||||
|
||||
# Дерево только для одной подсистемы
|
||||
... -SubsystemPath Subsystems -Mode tree -Name Администрирование
|
||||
```
|
||||
@@ -0,0 +1,483 @@
|
||||
# subsystem-info v1.0 — Compact summary of 1C subsystem structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$SubsystemPath,
|
||||
[ValidateSet("overview","content","ci","tree")]
|
||||
[string]$Mode = "overview",
|
||||
[string]$Name,
|
||||
[int]$Limit = 150,
|
||||
[int]$Offset = 0,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Output helper ---
|
||||
$script:lines = @()
|
||||
function Out([string]$text) { $script:lines += $text }
|
||||
|
||||
# --- Resolve path ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($SubsystemPath)) {
|
||||
$SubsystemPath = Join-Path (Get-Location).Path $SubsystemPath
|
||||
}
|
||||
|
||||
# --- Helper: get LocalString text ---
|
||||
function Get-MLText($node) {
|
||||
if (-not $node -or -not $node.HasChildNodes) { return "" }
|
||||
foreach ($item in $node.ChildNodes) {
|
||||
if ($item.NodeType -ne 'Element') { continue }
|
||||
$lang = ""; $content = ""
|
||||
foreach ($c in $item.ChildNodes) {
|
||||
if ($c.NodeType -ne 'Element') { continue }
|
||||
if ($c.LocalName -eq "lang") { $lang = $c.InnerText }
|
||||
if ($c.LocalName -eq "content") { $content = $c.InnerText }
|
||||
}
|
||||
if ($lang -eq "ru" -and $content) { return $content }
|
||||
}
|
||||
# fallback: first item
|
||||
foreach ($item in $node.ChildNodes) {
|
||||
if ($item.NodeType -ne 'Element') { continue }
|
||||
foreach ($c in $item.ChildNodes) {
|
||||
if ($c.NodeType -ne 'Element') { continue }
|
||||
if ($c.LocalName -eq "content" -and $c.InnerText) { return $c.InnerText }
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
# --- Helper: load subsystem XML ---
|
||||
function Load-SubsystemXml([string]$xmlPath) {
|
||||
[xml]$doc = Get-Content -Path $xmlPath -Encoding UTF8
|
||||
$ns = New-Object System.Xml.XmlNamespaceManager($doc.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")
|
||||
$sub = $doc.SelectSingleNode("/md:MetaDataObject/md:Subsystem", $ns)
|
||||
if (-not $sub) {
|
||||
Write-Host "[ERROR] Not a valid subsystem XML: $xmlPath"
|
||||
exit 1
|
||||
}
|
||||
return @{ Doc=$doc; Ns=$ns; Sub=$sub }
|
||||
}
|
||||
|
||||
# --- Helper: get content items ---
|
||||
function Get-ContentItems($props, $ns) {
|
||||
$items = @()
|
||||
$contentNode = $props.SelectSingleNode("md:Content", $ns)
|
||||
if (-not $contentNode -or -not $contentNode.HasChildNodes) { return $items }
|
||||
foreach ($item in $contentNode.SelectNodes("xr:Item", $ns)) {
|
||||
$items += $item.InnerText
|
||||
}
|
||||
return $items
|
||||
}
|
||||
|
||||
# --- Helper: get child subsystem names ---
|
||||
function Get-ChildNames($sub, $ns) {
|
||||
$names = @()
|
||||
$co = $sub.SelectSingleNode("md:ChildObjects", $ns)
|
||||
if (-not $co -or -not $co.HasChildNodes) { return $names }
|
||||
foreach ($child in $co.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq "Subsystem") {
|
||||
$names += $child.InnerText
|
||||
}
|
||||
}
|
||||
return $names
|
||||
}
|
||||
|
||||
# --- Helper: group content by type ---
|
||||
function Group-ContentByType($items) {
|
||||
$groups = [ordered]@{}
|
||||
foreach ($item in $items) {
|
||||
if ($item -match '^([^.]+)\.(.+)$') {
|
||||
$type = $Matches[1]
|
||||
$name = $Matches[2]
|
||||
} elseif ($item -match '^[0-9a-fA-F]{8}-') {
|
||||
$type = "[UUID]"
|
||||
$name = $item
|
||||
} else {
|
||||
$type = "[Other]"
|
||||
$name = $item
|
||||
}
|
||||
if (-not $groups.Contains($type)) { $groups[$type] = @() }
|
||||
$groups[$type] += $name
|
||||
}
|
||||
return $groups
|
||||
}
|
||||
|
||||
# --- Helper: find subsystem dir from XML path ---
|
||||
function Get-SubsystemDir([string]$xmlPath) {
|
||||
$dir = [System.IO.Path]::GetDirectoryName($xmlPath)
|
||||
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($xmlPath)
|
||||
return Join-Path $dir $baseName
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Mode: tree
|
||||
# ============================================================
|
||||
if ($Mode -eq "tree") {
|
||||
$isDir = Test-Path $SubsystemPath -PathType Container
|
||||
$rootDir = $null
|
||||
$rootXml = $null
|
||||
|
||||
if ($isDir) {
|
||||
# Subsystems/ directory — show all top-level subsystems
|
||||
$rootDir = $SubsystemPath
|
||||
} else {
|
||||
# Specific subsystem XML — show tree from this subsystem
|
||||
if (-not (Test-Path $SubsystemPath)) {
|
||||
Write-Host "[ERROR] File not found: $SubsystemPath"
|
||||
exit 1
|
||||
}
|
||||
$rootXml = $SubsystemPath
|
||||
}
|
||||
|
||||
# Box-drawing chars (PS 5.1 compatible)
|
||||
$script:T_BRANCH = [char]0x251C + [char]0x2500 + [char]0x2500 + " " # ├──
|
||||
$script:T_LAST = [char]0x2514 + [char]0x2500 + [char]0x2500 + " " # └──
|
||||
$script:T_PIPE = [char]0x2502 + " " # │
|
||||
$script:T_ARROW = [char]0x2192 # →
|
||||
|
||||
function Get-TreeLine([string]$xmlPath) {
|
||||
$parsed = Load-SubsystemXml $xmlPath
|
||||
$sub = $parsed.Sub; $ns = $parsed.Ns
|
||||
$props = $sub.SelectSingleNode("md:Properties", $ns)
|
||||
$name = $props.SelectSingleNode("md:Name", $ns).InnerText
|
||||
|
||||
$markers = @()
|
||||
$subDir = Get-SubsystemDir $xmlPath
|
||||
$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
|
||||
if (Test-Path $ciPath) { $markers += "CI" }
|
||||
$useOne = $props.SelectSingleNode("md:UseOneCommand", $ns)
|
||||
if ($useOne -and $useOne.InnerText -eq "true") { $markers += "OneCmd" }
|
||||
$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns)
|
||||
if ($inclCI -and $inclCI.InnerText -eq "false") { $markers += "Скрыт" }
|
||||
$markerStr = if ($markers.Count -gt 0) { " [$($markers -join ', ')]" } else { "" }
|
||||
|
||||
$contentItems = @(Get-ContentItems $props $ns)
|
||||
$childNames = @(Get-ChildNames $sub $ns)
|
||||
$childStr = if ($childNames.Count -gt 0) { ", $($childNames.Count) дочерних" } else { "" }
|
||||
|
||||
return @{
|
||||
Label = "$name$markerStr ($($contentItems.Count) объектов$childStr)"
|
||||
SubDir = $subDir
|
||||
ChildNames = $childNames
|
||||
}
|
||||
}
|
||||
|
||||
function Build-TreeEntry([string]$xmlPath, [string]$prefix, [bool]$isLast, [bool]$isRoot) {
|
||||
$info = Get-TreeLine $xmlPath
|
||||
|
||||
$connector = if ($isRoot) { "" } elseif ($isLast) { $script:T_LAST } else { $script:T_BRANCH }
|
||||
Out "$prefix$connector$($info.Label)"
|
||||
|
||||
if ($info.ChildNames.Count -gt 0) {
|
||||
$childPrefix = if ($isRoot) { "" } elseif ($isLast) { "$prefix " } else { "$prefix$($script:T_PIPE)" }
|
||||
$subsDir = Join-Path $info.SubDir "Subsystems"
|
||||
for ($i = 0; $i -lt $info.ChildNames.Count; $i++) {
|
||||
$childXml = Join-Path $subsDir "$($info.ChildNames[$i]).xml"
|
||||
$childIsLast = ($i -eq $info.ChildNames.Count - 1)
|
||||
if (Test-Path $childXml) {
|
||||
Build-TreeEntry $childXml $childPrefix $childIsLast $false
|
||||
} else {
|
||||
$conn2 = if ($childIsLast) { $script:T_LAST } else { $script:T_BRANCH }
|
||||
Out "$childPrefix$conn2$($info.ChildNames[$i]) [NOT FOUND]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($rootDir) {
|
||||
$label = Split-Path $rootDir -Leaf
|
||||
Out "Дерево подсистем от: $label/"
|
||||
Out ""
|
||||
$xmlFiles = @(Get-ChildItem $rootDir -Filter "*.xml" -File | Sort-Object Name)
|
||||
if ($Name) {
|
||||
$xmlFiles = @($xmlFiles | Where-Object { $_.BaseName -eq $Name })
|
||||
if ($xmlFiles.Count -eq 0) {
|
||||
Write-Host "[ERROR] Subsystem '$Name' not found in $rootDir"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i -lt $xmlFiles.Count; $i++) {
|
||||
Build-TreeEntry $xmlFiles[$i].FullName "" ($i -eq $xmlFiles.Count - 1) $true
|
||||
}
|
||||
} else {
|
||||
Build-TreeEntry $rootXml "" $true $true
|
||||
}
|
||||
|
||||
} elseif ($Mode -eq "ci") {
|
||||
# ============================================================
|
||||
# Mode: ci — CommandInterface.xml
|
||||
# ============================================================
|
||||
if (Test-Path $SubsystemPath -PathType Container) {
|
||||
Write-Host "[ERROR] ci mode requires a subsystem .xml file, not a directory"
|
||||
exit 1
|
||||
}
|
||||
if (-not (Test-Path $SubsystemPath)) {
|
||||
Write-Host "[ERROR] File not found: $SubsystemPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$parsed = Load-SubsystemXml $SubsystemPath
|
||||
$sub = $parsed.Sub; $ns = $parsed.Ns
|
||||
$props = $sub.SelectSingleNode("md:Properties", $ns)
|
||||
$subName = $props.SelectSingleNode("md:Name", $ns).InnerText
|
||||
|
||||
$subDir = Get-SubsystemDir $SubsystemPath
|
||||
$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
|
||||
|
||||
if (-not (Test-Path $ciPath)) {
|
||||
Out "Командный интерфейс: $subName"
|
||||
Out ""
|
||||
Out "Файл CommandInterface.xml не найден."
|
||||
Out "Путь: $ciPath"
|
||||
} else {
|
||||
[xml]$ciDoc = Get-Content -Path $ciPath -Encoding UTF8
|
||||
$ciNs = New-Object System.Xml.XmlNamespaceManager($ciDoc.NameTable)
|
||||
$ciNs.AddNamespace("ci", "http://v8.1c.ru/8.3/xcf/extrnprops")
|
||||
$ciNs.AddNamespace("xr", "http://v8.1c.ru/8.3/xcf/readable")
|
||||
|
||||
$ciRoot = $ciDoc.DocumentElement
|
||||
|
||||
Out "Командный интерфейс: $subName"
|
||||
Out ""
|
||||
|
||||
# --- CommandsVisibility ---
|
||||
$visSection = $ciRoot.SelectSingleNode("ci:CommandsVisibility", $ciNs)
|
||||
if ($visSection) {
|
||||
$hidden = @(); $shown = @()
|
||||
foreach ($cmd in $visSection.SelectNodes("ci:Command", $ciNs)) {
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
$vis = $cmd.SelectSingleNode("ci:Visibility/xr:Common", $ciNs)
|
||||
if ($vis -and $vis.InnerText -eq "false") { $hidden += $cmdName }
|
||||
else { $shown += $cmdName }
|
||||
}
|
||||
$total = $hidden.Count + $shown.Count
|
||||
if (-not $Name -or $Name -eq "visibility") {
|
||||
Out "Видимость ($total):"
|
||||
if ($hidden.Count -gt 0) {
|
||||
Out " СКРЫТО ($($hidden.Count)):"
|
||||
foreach ($h in $hidden) { Out " $h" }
|
||||
}
|
||||
if ($shown.Count -gt 0) {
|
||||
Out " ПОКАЗАНО ($($shown.Count)):"
|
||||
foreach ($s in $shown) { Out " $s" }
|
||||
}
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
|
||||
# --- CommandsPlacement ---
|
||||
$placeSection = $ciRoot.SelectSingleNode("ci:CommandsPlacement", $ciNs)
|
||||
if ($placeSection) {
|
||||
$placements = @()
|
||||
foreach ($cmd in $placeSection.SelectNodes("ci:Command", $ciNs)) {
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
|
||||
$pl = $cmd.SelectSingleNode("ci:Placement", $ciNs)
|
||||
$grpText = if ($grp) { $grp.InnerText } else { "?" }
|
||||
$plText = if ($pl) { $pl.InnerText } else { "?" }
|
||||
$placements += @{ Name=$cmdName; Group=$grpText; Placement=$plText }
|
||||
}
|
||||
if ((-not $Name -or $Name -eq "placement") -and $placements.Count -gt 0) {
|
||||
Out "Размещение ($($placements.Count)):"
|
||||
$arrow = [char]0x2192
|
||||
foreach ($p in $placements) {
|
||||
Out " $($p.Name) $arrow $($p.Group) ($($p.Placement))"
|
||||
}
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
|
||||
# --- CommandsOrder ---
|
||||
$orderSection = $ciRoot.SelectSingleNode("ci:CommandsOrder", $ciNs)
|
||||
if ($orderSection) {
|
||||
$orderGroups = [ordered]@{}
|
||||
foreach ($cmd in $orderSection.SelectNodes("ci:Command", $ciNs)) {
|
||||
$cmdName = $cmd.GetAttribute("name")
|
||||
$grp = $cmd.SelectSingleNode("ci:CommandGroup", $ciNs)
|
||||
$grpText = if ($grp) { $grp.InnerText } else { "?" }
|
||||
if (-not $orderGroups.Contains($grpText)) { $orderGroups[$grpText] = @() }
|
||||
$orderGroups[$grpText] += $cmdName
|
||||
}
|
||||
$totalOrder = 0
|
||||
foreach ($k in $orderGroups.Keys) { $totalOrder += $orderGroups[$k].Count }
|
||||
if ((-not $Name -or $Name -eq "order") -and $totalOrder -gt 0) {
|
||||
Out "Порядок команд ($totalOrder):"
|
||||
foreach ($grpName in $orderGroups.Keys) {
|
||||
Out " [$grpName]:"
|
||||
foreach ($c in $orderGroups[$grpName]) { Out " $c" }
|
||||
}
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
|
||||
# --- SubsystemsOrder ---
|
||||
$subOrderSection = $ciRoot.SelectSingleNode("ci:SubsystemsOrder", $ciNs)
|
||||
if ($subOrderSection) {
|
||||
$subOrder = @()
|
||||
foreach ($s in $subOrderSection.SelectNodes("ci:Subsystem", $ciNs)) {
|
||||
$subOrder += $s.InnerText
|
||||
}
|
||||
if ((-not $Name -or $Name -eq "subsystems") -and $subOrder.Count -gt 0) {
|
||||
Out "Порядок подсистем ($($subOrder.Count)):"
|
||||
for ($i = 0; $i -lt $subOrder.Count; $i++) {
|
||||
Out " $($i+1). $($subOrder[$i])"
|
||||
}
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
|
||||
# --- GroupsOrder ---
|
||||
$grpOrderSection = $ciRoot.SelectSingleNode("ci:GroupsOrder", $ciNs)
|
||||
if ($grpOrderSection) {
|
||||
$grpOrder = @()
|
||||
foreach ($g in $grpOrderSection.SelectNodes("ci:Group", $ciNs)) {
|
||||
$grpOrder += $g.InnerText
|
||||
}
|
||||
if ((-not $Name -or $Name -eq "groups") -and $grpOrder.Count -gt 0) {
|
||||
Out "Порядок групп ($($grpOrder.Count)):"
|
||||
foreach ($g in $grpOrder) { Out " $g" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
# ============================================================
|
||||
# Mode: overview / content — requires a subsystem XML file
|
||||
# ============================================================
|
||||
if (Test-Path $SubsystemPath -PathType Container) {
|
||||
$dirName = Split-Path $SubsystemPath -Leaf
|
||||
$candidate = Join-Path $SubsystemPath "$dirName.xml"
|
||||
if (Test-Path $candidate) {
|
||||
$SubsystemPath = $candidate
|
||||
} else {
|
||||
Write-Host "[ERROR] No $dirName.xml found in directory. Use -Mode tree for directory listing."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path $SubsystemPath)) {
|
||||
Write-Host "[ERROR] File not found: $SubsystemPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$parsed = Load-SubsystemXml $SubsystemPath
|
||||
$sub = $parsed.Sub; $ns = $parsed.Ns
|
||||
$props = $sub.SelectSingleNode("md:Properties", $ns)
|
||||
|
||||
$subName = $props.SelectSingleNode("md:Name", $ns).InnerText
|
||||
$synonym = Get-MLText $props.SelectSingleNode("md:Synonym", $ns)
|
||||
$comment = $props.SelectSingleNode("md:Comment", $ns)
|
||||
$commentText = if ($comment -and $comment.InnerText) { $comment.InnerText } else { "" }
|
||||
$inclHelp = $props.SelectSingleNode("md:IncludeHelpInContents", $ns).InnerText
|
||||
$inclCI = $props.SelectSingleNode("md:IncludeInCommandInterface", $ns).InnerText
|
||||
$useOneCmd = $props.SelectSingleNode("md:UseOneCommand", $ns).InnerText
|
||||
$explanation = Get-MLText $props.SelectSingleNode("md:Explanation", $ns)
|
||||
|
||||
# Picture
|
||||
$picNode = $props.SelectSingleNode("md:Picture", $ns)
|
||||
$picText = ""
|
||||
if ($picNode -and $picNode.HasChildNodes) {
|
||||
$picRef = $picNode.SelectSingleNode("xr:Ref", $ns)
|
||||
if ($picRef -and $picRef.InnerText) { $picText = $picRef.InnerText }
|
||||
}
|
||||
|
||||
# Content
|
||||
$contentItems = @(Get-ContentItems $props $ns)
|
||||
$groups = Group-ContentByType $contentItems
|
||||
|
||||
# Children
|
||||
$childNames = @(Get-ChildNames $sub $ns)
|
||||
|
||||
# CI presence
|
||||
$subDir = Get-SubsystemDir $SubsystemPath
|
||||
$ciPath = Join-Path (Join-Path $subDir "Ext") "CommandInterface.xml"
|
||||
$hasCI = Test-Path $ciPath
|
||||
|
||||
if ($Mode -eq "overview") {
|
||||
Out "Подсистема: $subName"
|
||||
if ($synonym -and $synonym -ne $subName) { Out "Синоним: $synonym" }
|
||||
if ($commentText) { Out "Комментарий: $commentText" }
|
||||
Out "ВключатьВКомандныйИнтерфейс: $inclCI"
|
||||
Out "ИспользоватьОднуКоманду: $useOneCmd"
|
||||
if ($explanation) { Out "Пояснение: $explanation" }
|
||||
if ($picText) { Out "Картинка: $picText" }
|
||||
|
||||
# Content summary
|
||||
if ($contentItems.Count -gt 0) {
|
||||
$parts = @()
|
||||
foreach ($type in $groups.Keys) {
|
||||
$parts += "$type`: $($groups[$type].Count)"
|
||||
}
|
||||
Out "Состав: $($contentItems.Count) объектов ($($parts -join ', '))"
|
||||
} else {
|
||||
Out "Состав: пусто"
|
||||
}
|
||||
|
||||
# Children
|
||||
if ($childNames.Count -gt 0) {
|
||||
Out "Дочерние подсистемы ($($childNames.Count)): $($childNames -join ', ')"
|
||||
}
|
||||
|
||||
# CI
|
||||
if ($hasCI) {
|
||||
Out "Командный интерфейс: есть"
|
||||
}
|
||||
|
||||
} elseif ($Mode -eq "content") {
|
||||
Out "Состав подсистемы $subName ($($contentItems.Count) объектов):"
|
||||
Out ""
|
||||
|
||||
if ($Name) {
|
||||
# Filter by type
|
||||
if ($groups.Contains($Name)) {
|
||||
$filtered = $groups[$Name]
|
||||
Out "$Name ($($filtered.Count)):"
|
||||
foreach ($n in $filtered) { Out " $n" }
|
||||
} else {
|
||||
Out "[INFO] Тип '$Name' не найден в составе."
|
||||
Out "Доступные типы: $($groups.Keys -join ', ')"
|
||||
}
|
||||
} else {
|
||||
foreach ($type in $groups.Keys) {
|
||||
Out "$type ($($groups[$type].Count)):"
|
||||
foreach ($n in $groups[$type]) { Out " $n" }
|
||||
Out ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Pagination and output ---
|
||||
$totalLines = $script:lines.Count
|
||||
$outLines = $script:lines
|
||||
|
||||
if ($Offset -gt 0) {
|
||||
if ($Offset -ge $totalLines) {
|
||||
Write-Host "[INFO] Offset $Offset exceeds total lines ($totalLines). Nothing to show."
|
||||
exit 0
|
||||
}
|
||||
$outLines = $outLines[$Offset..($totalLines - 1)]
|
||||
}
|
||||
|
||||
if ($Limit -gt 0 -and $outLines.Count -gt $Limit) {
|
||||
$shown = $outLines[0..($Limit - 1)]
|
||||
$remaining = $totalLines - $Offset - $Limit
|
||||
$shown += ""
|
||||
$shown += "[ОБРЕЗАНО] Показано $Limit из $totalLines строк. Используйте -Offset $($Offset + $Limit) для продолжения."
|
||||
$outLines = $shown
|
||||
}
|
||||
|
||||
if ($OutFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
|
||||
$OutFile = Join-Path (Get-Location).Path $OutFile
|
||||
}
|
||||
$utf8 = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllLines($OutFile, $outLines, $utf8)
|
||||
Write-Host "Output written to $OutFile"
|
||||
} else {
|
||||
foreach ($l in $outLines) { Write-Host $l }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: subsystem-validate
|
||||
description: Валидация подсистемы 1С. Используй после создания или модификации подсистемы для проверки корректности
|
||||
argument-hint: <SubsystemPath> [-MaxErrors 30]
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Glob
|
||||
---
|
||||
|
||||
# /subsystem-validate — валидация подсистемы 1С
|
||||
|
||||
Проверяет структурную корректность XML-файла подсистемы из выгрузки конфигурации.
|
||||
|
||||
## Параметры и команда
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `SubsystemPath` | Путь к XML-файлу подсистемы |
|
||||
| `MaxErrors` | Максимум ошибок до остановки (по умолчанию 30) |
|
||||
| `OutFile` | Записать результат в файл |
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File '.claude\skills\subsystem-validate\scripts\subsystem-validate.ps1' -SubsystemPath '<путь>'
|
||||
```
|
||||
|
||||
## Проверки (13)
|
||||
|
||||
1. XML well-formedness + root structure (MetaDataObject/Subsystem)
|
||||
2. Properties — 9 обязательных свойств
|
||||
3. Name — непустой, валидный идентификатор
|
||||
4. Synonym — непустой (хотя бы один v8:item)
|
||||
5. Булевы свойства — содержат true/false
|
||||
6. Content — формат xr:Item, xsi:type
|
||||
7. Content — нет дубликатов
|
||||
8. ChildObjects — элементы непустые
|
||||
9. ChildObjects — нет дубликатов
|
||||
10. ChildObjects → файлы существуют
|
||||
11. CommandInterface.xml — well-formedness
|
||||
12. Picture — формат ссылки
|
||||
13. UseOneCommand=true → ровно 1 элемент в Content
|
||||
@@ -0,0 +1,314 @@
|
||||
# subsystem-validate v1.0 — Validate 1C subsystem XML structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$SubsystemPath,
|
||||
[int]$MaxErrors = 30,
|
||||
[string]$OutFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Resolve path ---
|
||||
if (-not [System.IO.Path]::IsPathRooted($SubsystemPath)) {
|
||||
$SubsystemPath = Join-Path (Get-Location).Path $SubsystemPath
|
||||
}
|
||||
if (Test-Path $SubsystemPath -PathType Container) {
|
||||
$dirName = Split-Path $SubsystemPath -Leaf
|
||||
$candidate = Join-Path $SubsystemPath "$dirName.xml"
|
||||
if (Test-Path $candidate) { $SubsystemPath = $candidate }
|
||||
else {
|
||||
Write-Host "[ERROR] No $dirName.xml found in directory: $SubsystemPath"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
if (-not (Test-Path $SubsystemPath)) {
|
||||
Write-Host "[ERROR] File not found: $SubsystemPath"
|
||||
exit 1
|
||||
}
|
||||
$resolvedPath = (Resolve-Path $SubsystemPath).Path
|
||||
|
||||
# --- Output infrastructure ---
|
||||
$script:errors = 0
|
||||
$script:warnings = 0
|
||||
$script:stopped = $false
|
||||
$script:output = New-Object System.Text.StringBuilder 8192
|
||||
|
||||
function Out-Line([string]$msg) { $script:output.AppendLine($msg) | Out-Null }
|
||||
function Report-OK([string]$msg) { Out-Line "[OK] $msg" }
|
||||
function Report-Error([string]$msg) {
|
||||
$script:errors++
|
||||
Out-Line "[ERROR] $msg"
|
||||
if ($script:errors -ge $MaxErrors) { $script:stopped = $true }
|
||||
}
|
||||
function Report-Warn([string]$msg) {
|
||||
$script:warnings++
|
||||
Out-Line "[WARN] $msg"
|
||||
}
|
||||
|
||||
$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_]*$'
|
||||
|
||||
# --- 1. XML well-formedness + root structure ---
|
||||
$xmlDoc = $null
|
||||
try {
|
||||
[xml]$xmlDoc = Get-Content -Path $resolvedPath -Encoding UTF8
|
||||
} catch {
|
||||
Report-Error "1. XML parse error: $($_.Exception.Message)"
|
||||
$script:stopped = $true
|
||||
}
|
||||
|
||||
if (-not $script:stopped) {
|
||||
$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")
|
||||
|
||||
$root = $xmlDoc.DocumentElement
|
||||
$sub = $xmlDoc.SelectSingleNode("/md:MetaDataObject/md:Subsystem", $ns)
|
||||
$version = $root.GetAttribute("version")
|
||||
|
||||
if (-not $sub) {
|
||||
Report-Error "1. Root structure: expected MetaDataObject/Subsystem, not found"
|
||||
$script:stopped = $true
|
||||
} else {
|
||||
$uuid = $sub.GetAttribute("uuid")
|
||||
if ($uuid -and $uuid -match $guidPattern) {
|
||||
Report-OK "1. Root structure: MetaDataObject/Subsystem, uuid=$uuid, version $version"
|
||||
} else {
|
||||
Report-Error "1. Root structure: invalid or missing uuid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Properties checks ---
|
||||
if (-not $script:stopped) {
|
||||
$props = $sub.SelectSingleNode("md:Properties", $ns)
|
||||
if (-not $props) {
|
||||
Report-Error "2. Properties: <Properties> element not found"
|
||||
$script:stopped = $true
|
||||
}
|
||||
}
|
||||
|
||||
$subName = ""
|
||||
if (-not $script:stopped) {
|
||||
# --- 2. Required properties ---
|
||||
$requiredProps = @("Name","Synonym","Comment","IncludeHelpInContents","IncludeInCommandInterface","UseOneCommand","Explanation","Picture","Content")
|
||||
$missing = @()
|
||||
foreach ($p in $requiredProps) {
|
||||
$el = $props.SelectSingleNode("md:$p", $ns)
|
||||
if (-not $el) { $missing += $p }
|
||||
}
|
||||
if ($missing.Count -eq 0) {
|
||||
Report-OK "2. Properties: all 9 required properties present"
|
||||
} else {
|
||||
Report-Error "2. Properties: missing: $($missing -join ', ')"
|
||||
}
|
||||
|
||||
# --- 3. Name ---
|
||||
$nameEl = $props.SelectSingleNode("md:Name", $ns)
|
||||
$subName = if ($nameEl) { $nameEl.InnerText.Trim() } else { "" }
|
||||
Out-Line ""
|
||||
Out-Line "=== Validation: Subsystem.$subName ==="
|
||||
# Re-insert header at position 0
|
||||
$headerLine = "=== Validation: Subsystem.$subName ==="
|
||||
$script:output.Insert(0, "$headerLine`r`n`r`n") | Out-Null
|
||||
|
||||
if ($subName -and $subName -match $identPattern) {
|
||||
Report-OK "3. Name: `"$subName`" - valid identifier"
|
||||
} elseif (-not $subName) {
|
||||
Report-Error "3. Name: empty"
|
||||
} else {
|
||||
Report-Error "3. Name: `"$subName`" - invalid identifier"
|
||||
}
|
||||
|
||||
# --- 4. Synonym ---
|
||||
$synEl = $props.SelectSingleNode("md:Synonym", $ns)
|
||||
if ($synEl -and $synEl.HasChildNodes) {
|
||||
$items = $synEl.SelectNodes("v8:item", $ns)
|
||||
if ($items.Count -gt 0) {
|
||||
$firstContent = ""
|
||||
foreach ($item in $items) {
|
||||
$c = $item.SelectSingleNode("v8:content", $ns)
|
||||
if ($c -and $c.InnerText) { $firstContent = $c.InnerText; break }
|
||||
}
|
||||
Report-OK "4. Synonym: `"$firstContent`" ($($items.Count) lang(s))"
|
||||
} else {
|
||||
Report-Warn "4. Synonym: element exists but no v8:item children"
|
||||
}
|
||||
} else {
|
||||
Report-Warn "4. Synonym: empty or missing"
|
||||
}
|
||||
|
||||
# --- 5. Boolean properties ---
|
||||
$boolProps = @("IncludeHelpInContents","IncludeInCommandInterface","UseOneCommand")
|
||||
$boolOk = $true
|
||||
$boolVals = @{}
|
||||
foreach ($bp in $boolProps) {
|
||||
$el = $props.SelectSingleNode("md:$bp", $ns)
|
||||
if ($el) {
|
||||
$val = $el.InnerText.Trim()
|
||||
$boolVals[$bp] = $val
|
||||
if ($val -ne "true" -and $val -ne "false") {
|
||||
Report-Error "5. Boolean property $bp = `"$val`" (expected true/false)"
|
||||
$boolOk = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($boolOk) { Report-OK "5. Boolean properties: valid" }
|
||||
|
||||
# --- 6. Content items format ---
|
||||
$contentEl = $props.SelectSingleNode("md:Content", $ns)
|
||||
$contentItems = @()
|
||||
if ($contentEl -and $contentEl.HasChildNodes) {
|
||||
$xrItems = $contentEl.SelectNodes("xr:Item", $ns)
|
||||
$contentOk = $true
|
||||
foreach ($item in $xrItems) {
|
||||
$typeAttr = $item.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
$text = $item.InnerText.Trim()
|
||||
$contentItems += $text
|
||||
if ($typeAttr -ne "xr:MDObjectRef") {
|
||||
Report-Error "6. Content item `"$text`": xsi:type=`"$typeAttr`" (expected xr:MDObjectRef)"
|
||||
$contentOk = $false
|
||||
}
|
||||
if ($text -notmatch '^[A-Za-z]+\..+$' -and $text -notmatch $guidPattern) {
|
||||
Report-Error "6. Content item `"$text`": invalid format (expected Type.Name or UUID)"
|
||||
$contentOk = $false
|
||||
}
|
||||
}
|
||||
if ($contentOk) { Report-OK "6. Content: $($xrItems.Count) items, all valid MDObjectRef format" }
|
||||
} else {
|
||||
Report-OK "6. Content: empty (no items)"
|
||||
}
|
||||
|
||||
# --- 7. Content duplicates ---
|
||||
if ($contentItems.Count -gt 0) {
|
||||
$dupes = $contentItems | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Warn "7. Content: duplicates found: $dupeNames"
|
||||
} else {
|
||||
Report-OK "7. Content: no duplicates"
|
||||
}
|
||||
} else {
|
||||
Report-OK "7. Content: no duplicates (empty)"
|
||||
}
|
||||
|
||||
# --- 8. ChildObjects entries non-empty ---
|
||||
$childObjs = $sub.SelectSingleNode("md:ChildObjects", $ns)
|
||||
$childNames = @()
|
||||
if ($childObjs -and $childObjs.HasChildNodes) {
|
||||
$childOk = $true
|
||||
foreach ($child in $childObjs.ChildNodes) {
|
||||
if ($child.NodeType -ne 'Element') { continue }
|
||||
if ($child.LocalName -ne "Subsystem") {
|
||||
Report-Error "8. ChildObjects: unexpected element <$($child.LocalName)>"
|
||||
$childOk = $false
|
||||
} elseif (-not $child.InnerText.Trim()) {
|
||||
Report-Error "8. ChildObjects: empty <Subsystem> element"
|
||||
$childOk = $false
|
||||
} else {
|
||||
$childNames += $child.InnerText.Trim()
|
||||
}
|
||||
}
|
||||
if ($childOk) { Report-OK "8. ChildObjects: $($childNames.Count) entries, all non-empty" }
|
||||
} else {
|
||||
Report-OK "8. ChildObjects: empty (leaf subsystem)"
|
||||
}
|
||||
|
||||
# --- 9. ChildObjects duplicates ---
|
||||
if ($childNames.Count -gt 0) {
|
||||
$dupes = $childNames | Group-Object | Where-Object { $_.Count -gt 1 }
|
||||
if ($dupes) {
|
||||
$dupeNames = ($dupes | ForEach-Object { $_.Name }) -join ", "
|
||||
Report-Error "9. ChildObjects: duplicates: $dupeNames"
|
||||
} else {
|
||||
Report-OK "9. ChildObjects: no duplicates"
|
||||
}
|
||||
} else {
|
||||
Report-OK "9. ChildObjects: no duplicates (empty)"
|
||||
}
|
||||
|
||||
# --- 10. ChildObjects files exist ---
|
||||
if ($childNames.Count -gt 0) {
|
||||
$parentDir = [System.IO.Path]::GetDirectoryName($resolvedPath)
|
||||
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($resolvedPath)
|
||||
$subsDir = Join-Path (Join-Path $parentDir $baseName) "Subsystems"
|
||||
$missingFiles = @()
|
||||
foreach ($cn in $childNames) {
|
||||
$childXml = Join-Path $subsDir "$cn.xml"
|
||||
if (-not (Test-Path $childXml)) { $missingFiles += $cn }
|
||||
}
|
||||
if ($missingFiles.Count -eq 0) {
|
||||
Report-OK "10. ChildObjects files: all $($childNames.Count) files exist"
|
||||
} else {
|
||||
Report-Warn "10. ChildObjects files: missing: $($missingFiles -join ', ')"
|
||||
}
|
||||
} else {
|
||||
Report-OK "10. ChildObjects files: n/a (no children)"
|
||||
}
|
||||
|
||||
# --- 11. CommandInterface.xml ---
|
||||
$parentDir2 = [System.IO.Path]::GetDirectoryName($resolvedPath)
|
||||
$baseName2 = [System.IO.Path]::GetFileNameWithoutExtension($resolvedPath)
|
||||
$ciPath = Join-Path (Join-Path (Join-Path $parentDir2 $baseName2) "Ext") "CommandInterface.xml"
|
||||
if (Test-Path $ciPath) {
|
||||
try {
|
||||
[xml]$ciDoc = Get-Content -Path $ciPath -Encoding UTF8
|
||||
Report-OK "11. CommandInterface: exists, well-formed"
|
||||
} catch {
|
||||
Report-Warn "11. CommandInterface: exists but NOT well-formed: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Report-OK "11. CommandInterface: not present"
|
||||
}
|
||||
|
||||
# --- 12. Picture format ---
|
||||
$picEl = $props.SelectSingleNode("md:Picture", $ns)
|
||||
if ($picEl -and $picEl.HasChildNodes) {
|
||||
$picRef = $picEl.SelectSingleNode("xr:Ref", $ns)
|
||||
if ($picRef -and $picRef.InnerText) {
|
||||
$refText = $picRef.InnerText
|
||||
if ($refText -match '^CommonPicture\.') {
|
||||
Report-OK "12. Picture: $refText"
|
||||
} else {
|
||||
Report-Warn "12. Picture: `"$refText`" (expected CommonPicture.XXX)"
|
||||
}
|
||||
} else {
|
||||
Report-Warn "12. Picture: has children but no xr:Ref content"
|
||||
}
|
||||
} else {
|
||||
Report-OK "12. Picture: empty (not set)"
|
||||
}
|
||||
|
||||
# --- 13. UseOneCommand constraint ---
|
||||
$useOne = $boolVals["UseOneCommand"]
|
||||
if ($useOne -eq "true") {
|
||||
if ($contentItems.Count -eq 1) {
|
||||
Report-OK "13. UseOneCommand: true, Content has exactly 1 item"
|
||||
} else {
|
||||
Report-Warn "13. UseOneCommand: true but Content has $($contentItems.Count) items (expected 1)"
|
||||
}
|
||||
} else {
|
||||
Report-OK "13. UseOneCommand: false (no constraint)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Finalize ---
|
||||
Out-Line "---"
|
||||
Out-Line "Errors: $($script:errors), Warnings: $($script:warnings)"
|
||||
|
||||
$result = $script:output.ToString()
|
||||
Write-Host $result
|
||||
|
||||
if ($OutFile) {
|
||||
if (-not [System.IO.Path]::IsPathRooted($OutFile)) {
|
||||
$OutFile = Join-Path (Get-Location).Path $OutFile
|
||||
}
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($OutFile, $result, $utf8Bom)
|
||||
Write-Host "Written to: $OutFile"
|
||||
}
|
||||
|
||||
if ($script:errors -gt 0) { exit 1 } else { exit 0 }
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: epf-add-template
|
||||
description: Добавить макет к внешней обработке 1С
|
||||
argument-hint: <ProcessorName> <TemplateName> <TemplateType>
|
||||
name: template-add
|
||||
description: Добавить макет к объекту 1С (обработка, отчёт, справочник, документ и др.)
|
||||
argument-hint: <ObjectName> <TemplateName> <TemplateType>
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
@@ -11,28 +11,29 @@ allowed-tools:
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /epf-add-template — Добавление макета
|
||||
# /template-add — Добавление макета
|
||||
|
||||
Создаёт макет указанного типа и регистрирует его в корневом XML обработки.
|
||||
Создаёт макет указанного типа и регистрирует его в корневом XML объекта.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/epf-add-template <ProcessorName> <TemplateName> <TemplateType>
|
||||
/template-add <ObjectName> <TemplateName> <TemplateType>
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|---------------|:------------:|-----------------|--------------------------------------------------|
|
||||
| ProcessorName | да | — | Имя обработки |
|
||||
| ObjectName | да | — | Имя объекта |
|
||||
| TemplateName | да | — | Имя макета |
|
||||
| TemplateType | да | — | Тип: HTML, Text, SpreadsheetDocument, BinaryData, DataCompositionSchema |
|
||||
| Synonym | нет | = TemplateName | Синоним макета |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
| --SetMainSKD | нет | — | Принудительно установить MainDataCompositionSchema |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
pwsh -NoProfile -File .claude/skills/epf-add-template/scripts/add-template.ps1 -ProcessorName "<ProcessorName>" -TemplateName "<TemplateName>" -TemplateType "<TemplateType>" [-Synonym "<Synonym>"] [-SrcDir "<SrcDir>"]
|
||||
powershell.exe -NoProfile -File .claude\skills\template-add\scripts\add-template.ps1 -ObjectName "<ObjectName>" -TemplateName "<TemplateName>" -TemplateType "<TemplateType>" [-Synonym "<Synonym>"] [-SrcDir "<SrcDir>"] [-SetMainSKD]
|
||||
```
|
||||
|
||||
## Маппинг типов
|
||||
@@ -58,10 +59,16 @@ pwsh -NoProfile -File .claude/skills/epf-add-template/scripts/add-template.ps1 -
|
||||
|
||||
Если пользователь указал имя макета без префикса, но контекст — печатная форма, **добавь префикс `ПФ_MXL_` автоматически** и сообщи об этом.
|
||||
|
||||
## MainDataCompositionSchema (авто)
|
||||
|
||||
При добавлении макета типа `DataCompositionSchema` к `ExternalReport` или `Report`:
|
||||
- Если `MainDataCompositionSchema` пуст — автоматически заполняется ссылкой на макет
|
||||
- Используй `--SetMainSKD` чтобы перезаписать существующее значение
|
||||
|
||||
## Что создаётся
|
||||
|
||||
```
|
||||
<SrcDir>/<ProcessorName>/Templates/
|
||||
<SrcDir>/<ObjectName>/Templates/
|
||||
├── <TemplateName>.xml # Метаданные макета (1 UUID)
|
||||
└── <TemplateName>/
|
||||
└── Ext/
|
||||
@@ -70,4 +77,5 @@ pwsh -NoProfile -File .claude/skills/epf-add-template/scripts/add-template.ps1 -
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- `<SrcDir>/<ProcessorName>.xml` — добавляется `<Template>` в конец `ChildObjects`
|
||||
- `<SrcDir>/<ObjectName>.xml` — добавляется `<Template>` в конец `ChildObjects`
|
||||
- Для ExternalReport/Report: может обновляться `MainDataCompositionSchema`
|
||||
+41
-5
@@ -1,8 +1,9 @@
|
||||
# epf-add-template v1.0 — Add template to 1C processor
|
||||
# template-add v1.1 — Add template to 1C object
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ProcessorName,
|
||||
[Alias("ProcessorName")]
|
||||
[string]$ObjectName,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$TemplateName,
|
||||
@@ -13,7 +14,9 @@ param(
|
||||
|
||||
[string]$Synonym = $TemplateName,
|
||||
|
||||
[string]$SrcDir = "src"
|
||||
[string]$SrcDir = "src",
|
||||
|
||||
[switch]$SetMainSKD
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
@@ -32,13 +35,13 @@ $tmpl = $typeMap[$TemplateType]
|
||||
|
||||
# --- Проверки ---
|
||||
|
||||
$rootXmlPath = Join-Path $SrcDir "$ProcessorName.xml"
|
||||
$rootXmlPath = Join-Path $SrcDir "$ObjectName.xml"
|
||||
if (-not (Test-Path $rootXmlPath)) {
|
||||
Write-Error "Корневой файл обработки не найден: $rootXmlPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$processorDir = Join-Path $SrcDir $ProcessorName
|
||||
$processorDir = Join-Path $SrcDir $ObjectName
|
||||
$templatesDir = Join-Path $processorDir "Templates"
|
||||
$templateMetaPath = Join-Path $templatesDir "$TemplateName.xml"
|
||||
|
||||
@@ -172,6 +175,36 @@ if ($childObjects.ChildNodes.Count -eq 0) {
|
||||
}
|
||||
}
|
||||
|
||||
# --- 4. MainDataCompositionSchema (для ExternalReport / Report) ---
|
||||
|
||||
$mainDCSUpdated = $false
|
||||
if ($TemplateType -eq "DataCompositionSchema") {
|
||||
# Определяем корневой элемент объекта
|
||||
$reportLikeTypes = @("ExternalReport", "Report")
|
||||
$objectTypeNode = $null
|
||||
$objectTypeName = $null
|
||||
foreach ($rt in $reportLikeTypes) {
|
||||
$node = $xmlDoc.SelectSingleNode("//md:$rt", $nsMgr)
|
||||
if ($node) {
|
||||
$objectTypeNode = $node
|
||||
$objectTypeName = $rt
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($objectTypeNode) {
|
||||
$mainDCS = $xmlDoc.SelectSingleNode("//md:${objectTypeName}/md:Properties/md:MainDataCompositionSchema", $nsMgr)
|
||||
if ($mainDCS) {
|
||||
$isEmpty = [string]::IsNullOrWhiteSpace($mainDCS.InnerText)
|
||||
if ($isEmpty -or $SetMainSKD) {
|
||||
$objName = $xmlDoc.SelectSingleNode("//md:${objectTypeName}/md:Properties/md:Name", $nsMgr).InnerText
|
||||
$mainDCS.InnerText = "$objectTypeName.$objName.Template.$TemplateName"
|
||||
$mainDCSUpdated = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Сохранить с BOM
|
||||
$settings = New-Object System.Xml.XmlWriterSettings
|
||||
$settings.Encoding = $encBom
|
||||
@@ -186,3 +219,6 @@ $stream.Close()
|
||||
Write-Host "[OK] Создан макет: $TemplateName ($TemplateType)"
|
||||
Write-Host " Метаданные: $templateMetaPath"
|
||||
Write-Host " Содержимое: $templateFilePath"
|
||||
if ($mainDCSUpdated) {
|
||||
Write-Host " MainDataCompositionSchema: $($mainDCS.InnerText)"
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: template-remove
|
||||
description: Удалить макет из объекта 1С (обработка, отчёт, справочник, документ и др.)
|
||||
argument-hint: <ObjectName> <TemplateName>
|
||||
disable-model-invocation: true
|
||||
allowed-tools:
|
||||
- Bash
|
||||
- Read
|
||||
- Write
|
||||
- Edit
|
||||
- Glob
|
||||
- Grep
|
||||
---
|
||||
|
||||
# /template-remove — Удаление макета
|
||||
|
||||
Удаляет макет и убирает его регистрацию из корневого XML объекта.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/template-remove <ObjectName> <TemplateName>
|
||||
```
|
||||
|
||||
| Параметр | Обязательный | По умолчанию | Описание |
|
||||
|--------------|:------------:|--------------|-------------------------------------|
|
||||
| ObjectName | да | — | Имя объекта |
|
||||
| TemplateName | да | — | Имя макета для удаления |
|
||||
| SrcDir | нет | `src` | Каталог исходников |
|
||||
|
||||
## Команда
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File .claude\skills\template-remove\scripts\remove-template.ps1 -ObjectName "<ObjectName>" -TemplateName "<TemplateName>" [-SrcDir "<SrcDir>"]
|
||||
```
|
||||
|
||||
## Что удаляется
|
||||
|
||||
```
|
||||
<SrcDir>/<ObjectName>/Templates/<TemplateName>.xml # Метаданные макета
|
||||
<SrcDir>/<ObjectName>/Templates/<TemplateName>/ # Каталог макета (рекурсивно)
|
||||
```
|
||||
|
||||
## Что модифицируется
|
||||
|
||||
- `<SrcDir>/<ObjectName>.xml` — убирается `<Template>` из `ChildObjects`
|
||||
- Для ExternalReport/Report: если удалённый макет был указан в `MainDataCompositionSchema` — значение очищается
|
||||
+12
-4
@@ -1,8 +1,9 @@
|
||||
# epf-remove-template v1.0 — Remove template from 1C processor
|
||||
# template-remove v1.1 — Remove template from 1C object
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ProcessorName,
|
||||
[Alias("ProcessorName")]
|
||||
[string]$ObjectName,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$TemplateName,
|
||||
@@ -14,13 +15,13 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
# --- Проверки ---
|
||||
|
||||
$rootXmlPath = Join-Path $SrcDir "$ProcessorName.xml"
|
||||
$rootXmlPath = Join-Path $SrcDir "$ObjectName.xml"
|
||||
if (-not (Test-Path $rootXmlPath)) {
|
||||
Write-Error "Корневой файл обработки не найден: $rootXmlPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$processorDir = Join-Path $SrcDir $ProcessorName
|
||||
$processorDir = Join-Path $SrcDir $ObjectName
|
||||
$templatesDir = Join-Path $processorDir "Templates"
|
||||
$templateMetaPath = Join-Path $templatesDir "$TemplateName.xml"
|
||||
$templateDir = Join-Path $templatesDir $TemplateName
|
||||
@@ -65,6 +66,13 @@ foreach ($node in $templateNodes) {
|
||||
}
|
||||
}
|
||||
|
||||
# Очистить MainDataCompositionSchema если указывала на этот макет
|
||||
$mainDCS = $xmlDoc.SelectSingleNode("//md:MainDataCompositionSchema", $nsMgr)
|
||||
if ($mainDCS -and $mainDCS.InnerText -match "Template\.$([regex]::Escape($TemplateName))$") {
|
||||
$mainDCS.InnerText = ""
|
||||
Write-Host "[OK] Очищён MainDataCompositionSchema"
|
||||
}
|
||||
|
||||
# Сохранить с BOM
|
||||
$encBom = New-Object System.Text.UTF8Encoding($true)
|
||||
$settings = New-Object System.Xml.XmlWriterSettings
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Work in progress** — проект в стадии активной разработки. Набор навыков и операций расширяется.
|
||||
|
||||
Набор [Claude Code Skills](https://docs.anthropic.com/en/docs/claude-code/skills) для работы с артефактами 1С:Предприятия 8.3. Позволяет создавать и модифицировать обработки, макеты печатных форм и другие объекты из XML-исходников, не запоминая детали формата.
|
||||
Набор [Claude Code Skills](https://docs.anthropic.com/en/docs/claude-code/skills) для работы с артефактами 1С:Предприятия 8.3. Позволяет создавать и модифицировать обработки, отчёты, объекты конфигурации, макеты печатных форм и другие объекты из XML-исходников, не запоминая детали формата.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
@@ -21,24 +21,32 @@
|
||||
|
||||
| Группа | Навыки | Описание | Гайд |
|
||||
|--------|--------|----------|------|
|
||||
| Внешние обработки (EPF) | 10 навыков `/epf-*` | Создание, модификация, сборка обработок из XML-исходников | [Подробнее](docs/epf-guide.md) |
|
||||
| Внешние обработки (EPF) | 6 навыков `/epf-*` | Создание, сборка, разборка обработок из XML-исходников | [Подробнее](docs/epf-guide.md) |
|
||||
| Внешние отчёты (ERF) | 3 навыка `/erf-*` | Создание, сборка, разборка внешних отчётов | [Подробнее](docs/epf-guide.md#внешние-отчёты-erf) |
|
||||
| Универсальные операции | `/template-add`, `/template-remove`, `/help-add`, `/form-remove` | Добавление/удаление макетов, форм, справки для любых объектов | [Подробнее](docs/epf-guide.md#универсальные-навыки) |
|
||||
| Табличный документ (MXL) | 4 навыка `/mxl-*` | Анализ, создание, компиляция макетов печатных форм | [Подробнее](docs/mxl-guide.md) |
|
||||
| Управляемые формы (Form) | 6 навыков `/form-*` | Создание, анализ, генерация, модификация, валидация управляемых форм | [Подробнее](docs/form-guide.md) |
|
||||
| Роли (Role) | 3 навыка `/role-*` | Анализ прав роли, создание из JSON DSL, валидация | [Подробнее](docs/role-guide.md) |
|
||||
| Схема компоновки (СКД) | 4 навыка `/skd-*` | Анализ, генерация из JSON DSL, точечное редактирование, валидация схем компоновки данных | [Подробнее](docs/skd-guide.md) |
|
||||
| Метаданные конфигурации | 4 навыка `/meta-*` | Создание, анализ, редактирование, валидация объектов метаданных (23 типа) | [Подробнее](docs/meta-guide.md) |
|
||||
| Корневая конфигурация | 4 навыка `/cf-*` | Создание, анализ, редактирование, валидация корневых файлов конфигурации | [Подробнее](docs/cf-guide.md) |
|
||||
| Расширения (CFE) | 5 навыков `/cfe-*` | Создание, заимствование, перехват методов, валидация, анализ расширений | [Подробнее](docs/cfe-guide.md) |
|
||||
| Подсистемы (Subsystem) | 4 навыка `/subsystem-*` | Анализ, создание, редактирование, валидация подсистем конфигурации | — |
|
||||
| Командный интерфейс (CI) | 2 навыка `/interface-*` | Редактирование и валидация CommandInterface.xml подсистем | — |
|
||||
| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — |
|
||||
|
||||
## Требования
|
||||
|
||||
- **Windows** с PowerShell 5.1+ (входит в Windows)
|
||||
- **1С:Предприятие 8.3** — для сборки/разборки EPF (навыки генерации XML работают без платформы)
|
||||
- **1С:Предприятие 8.3** — для сборки/разборки EPF/ERF (навыки генерации XML работают без платформы)
|
||||
|
||||
## Спецификации
|
||||
|
||||
- [XML-формат выгрузки обработок](docs/1c-xml-format-spec.md) — структура XML-файлов, namespace, элементы форм
|
||||
- [XML-формат выгрузки обработок](docs/1c-epf-spec.md) — структура XML-файлов, namespace, элементы форм
|
||||
- [XML-формат внешних отчётов](docs/1c-erf-spec.md) — отличия ERF от EPF, Properties, MainDataCompositionSchema
|
||||
- [Управляемая форма](docs/1c-form-spec.md) — Form.xml, элементы, команды, реквизиты
|
||||
- [Встроенная справка](docs/1c-help-spec.md) — Help.xml, HTML-страницы, кнопка справки на форме
|
||||
- [Сборка и разборка EPF](docs/build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
||||
- [Сборка и разборка EPF/ERF](docs/build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
||||
- [Табличный документ (MXL)](docs/1c-spreadsheet-spec.md) — XML-формат SpreadsheetDocument, совместимость версий
|
||||
- [MXL DSL](docs/mxl-dsl-spec.md) — JSON-формат описания макета для `/mxl-compile` и `/mxl-decompile`
|
||||
- [Form DSL](docs/form-dsl-spec.md) — JSON-формат описания формы для `/form-compile`
|
||||
@@ -46,30 +54,37 @@
|
||||
- [Role DSL](docs/role-dsl-spec.md) — JSON-формат описания ролей для `/role-compile`
|
||||
- [Схема компоновки данных (DCS)](docs/1c-dcs-spec.md) — XML-формат DataCompositionSchema, 930 схем проанализировано
|
||||
- [SKD DSL](docs/skd-dsl-spec.md) — JSON-формат описания СКД для `/skd-compile`
|
||||
- [Объекты конфигурации](docs/1c-config-objects-spec.md) — XML-формат объектов метаданных конфигурации (23 типа)
|
||||
- [Подсистемы и командный интерфейс](docs/1c-subsystem-spec.md) — XML-формат подсистем, CommandInterface.xml, секции видимости/размещения/порядка
|
||||
- [Корневая конфигурация](docs/1c-configuration-spec.md) — XML-формат Configuration.xml, ConfigDumpInfo.xml, Languages/, 44 типа ChildObjects
|
||||
- [Расширения конфигурации (CFE)](docs/1c-extension-spec.md) — XML-формат выгрузки расширений конфигурации
|
||||
|
||||
## Структура репозитория
|
||||
|
||||
```
|
||||
.claude/skills/ # Навыки Claude Code
|
||||
├── epf-init/ # Создание обработки
|
||||
├── epf-add-form/ # Добавление формы
|
||||
├── epf-add-template/ # Добавление макета
|
||||
├── epf-add-help/ # Добавление справки
|
||||
├── epf-remove-form/ # Удаление формы
|
||||
├── epf-remove-template/ # Удаление макета
|
||||
├── epf-add-form/ # Добавление формы к обработке
|
||||
├── epf-build/ # Сборка EPF
|
||||
├── epf-dump/ # Разборка EPF
|
||||
├── epf-bsp-init/ # Регистрация БСП
|
||||
├── epf-bsp-add-command/ # Команда БСП
|
||||
├── erf-init/ # Создание внешнего отчёта
|
||||
├── erf-build/ # Сборка ERF
|
||||
├── erf-dump/ # Разборка ERF
|
||||
├── template-add/ # Добавление макета (универсальный)
|
||||
├── template-remove/ # Удаление макета (универсальный)
|
||||
├── form-add/ # Добавление формы (универсальный)
|
||||
├── form-remove/ # Удаление формы (универсальный)
|
||||
├── help-add/ # Добавление справки (универсальный)
|
||||
├── mxl-info/ # Анализ макета
|
||||
├── mxl-validate/ # Валидация макета
|
||||
├── mxl-compile/ # Компиляция макета из JSON
|
||||
├── mxl-decompile/ # Декомпиляция макета в JSON
|
||||
├── form-add/ # Добавление формы к объекту конфигурации
|
||||
├── form-info/ # Анализ структуры управляемой формы
|
||||
├── form-compile/ # Компиляция формы из JSON
|
||||
├── form-validate/ # Валидация формы
|
||||
├── form-edit/ # Добавление элементов в форму
|
||||
├── form-edit/ # Добавление элементов в форму
|
||||
├── form-patterns/ # Справочник паттернов компоновки форм
|
||||
├── role-info/ # Анализ прав роли
|
||||
├── role-compile/ # Создание роли из JSON DSL
|
||||
@@ -78,22 +93,49 @@
|
||||
├── skd-compile/ # Компиляция СКД из JSON DSL
|
||||
├── skd-edit/ # Точечное редактирование СКД (25 операций)
|
||||
├── skd-validate/ # Валидация СКД
|
||||
├── meta-info/ # Структура объекта метаданных
|
||||
├── meta-compile/ # Создание объекта метаданных
|
||||
├── meta-edit/ # Редактирование объекта метаданных
|
||||
├── meta-validate/ # Валидация объекта метаданных
|
||||
├── cf-info/ # Анализ структуры конфигурации
|
||||
├── cf-init/ # Создание пустой конфигурации
|
||||
├── cf-edit/ # Редактирование конфигурации
|
||||
├── cf-validate/ # Валидация конфигурации
|
||||
├── cfe-init/ # Создание расширения
|
||||
├── cfe-borrow/ # Заимствование объектов
|
||||
├── cfe-patch-method/ # Перехват методов
|
||||
├── cfe-validate/ # Валидация расширения
|
||||
├── cfe-diff/ # Анализ и сравнение
|
||||
├── subsystem-info/ # Анализ структуры подсистемы
|
||||
├── subsystem-compile/ # Создание подсистемы из JSON
|
||||
├── subsystem-edit/ # Редактирование подсистемы
|
||||
├── subsystem-validate/ # Валидация подсистемы
|
||||
├── interface-edit/ # Редактирование CommandInterface.xml
|
||||
├── interface-validate/ # Валидация CommandInterface.xml
|
||||
└── img-grid/ # Сетка для анализа изображений
|
||||
docs/
|
||||
├── epf-guide.md # Гайд: внешние обработки
|
||||
├── epf-guide.md # Гайд: внешние обработки и отчёты
|
||||
├── mxl-guide.md # Гайд: табличный документ
|
||||
├── form-guide.md # Гайд: управляемые формы
|
||||
├── role-guide.md # Гайд: роли
|
||||
├── skd-guide.md # Гайд: схема компоновки данных
|
||||
├── 1c-xml-format-spec.md # Спецификация XML-формата
|
||||
├── meta-guide.md # Гайд: объекты метаданных конфигурации
|
||||
├── cf-guide.md # Гайд: корневые файлы конфигурации
|
||||
├── cfe-guide.md # Гайд: расширения конфигурации (CFE)
|
||||
├── 1c-epf-spec.md # Спецификация XML-формата (EPF)
|
||||
├── 1c-erf-spec.md # Спецификация XML-формата (ERF)
|
||||
├── 1c-form-spec.md # Спецификация управляемых форм
|
||||
├── 1c-help-spec.md # Спецификация встроенной справки
|
||||
├── 1c-config-objects-spec.md # Спецификация объектов конфигурации
|
||||
├── build-spec.md # Спецификация сборки/разборки
|
||||
├── 1c-spreadsheet-spec.md # Спецификация табличного документа
|
||||
├── mxl-dsl-spec.md # Спецификация MXL DSL
|
||||
├── form-dsl-spec.md # Спецификация Form DSL
|
||||
├── meta-dsl-spec.md # Спецификация Meta DSL
|
||||
├── 1c-role-spec.md # Спецификация ролей (Rights.xml)
|
||||
├── 1c-dcs-spec.md # Спецификация СКД (DataCompositionSchema)
|
||||
├── skd-dsl-spec.md # Спецификация SKD DSL
|
||||
└── role-dsl-spec.md # Спецификация Role DSL
|
||||
├── role-dsl-spec.md # Спецификация Role DSL
|
||||
├── 1c-extension-spec.md # Спецификация расширений конфигурации (CFE)
|
||||
└── 1c-subsystem-spec.md # Спецификация подсистем и командного интерфейса
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,992 @@
|
||||
# Спецификация корневой структуры конфигурации 1С
|
||||
|
||||
Формат: XML-выгрузка конфигурации 1С:Предприятие 8.3 (Конфигуратор → Конфигурация → Выгрузить конфигурацию в файлы).
|
||||
Версии формата: `2.17` (платформа 8.3.20–8.3.24), `2.20` (платформа 8.3.27+).
|
||||
|
||||
Источники: выгрузки Бухгалтерия предприятия (платформы 8.3.20, 8.3.24, 8.3.27), ERP 2 (8.3.24).
|
||||
|
||||
> **Связанные спецификации:**
|
||||
> - Объекты метаданных — [1c-config-objects-spec.md](1c-config-objects-spec.md)
|
||||
> - Подсистемы и командный интерфейс — [1c-subsystem-spec.md](1c-subsystem-spec.md)
|
||||
> - Сводный индекс — [1c-specs-index.md](1c-specs-index.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Общая структура выгрузки
|
||||
|
||||
```
|
||||
Configuration.xml # Корневой файл — свойства и состав конфигурации
|
||||
ConfigDumpInfo.xml # Служебный файл — версии объектов
|
||||
Ext/ # Корневой каталог модулей и интерфейса
|
||||
Languages/ # Языки конфигурации
|
||||
Subsystems/ # Подсистемы
|
||||
Catalogs/ # Справочники
|
||||
Documents/ # Документы
|
||||
... # Каталоги всех типов объектов (см. раздел 2.4)
|
||||
```
|
||||
|
||||
Полный перечень каталогов объектов и их формат — [1c-config-objects-spec.md § 1](1c-config-objects-spec.md#1-общая-структура-выгрузки).
|
||||
|
||||
---
|
||||
|
||||
## 2. Configuration.xml — корневой файл конфигурации
|
||||
|
||||
### 2.1. Общая структура
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
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"
|
||||
xmlns:app="http://v8.1c.ru/8.2/managed-application/core"
|
||||
... version="2.17">
|
||||
<Configuration uuid="e0666db2-...">
|
||||
<InternalInfo>...</InternalInfo>
|
||||
<Properties>...</Properties>
|
||||
<ChildObjects>...</ChildObjects>
|
||||
</Configuration>
|
||||
</MetaDataObject>
|
||||
```
|
||||
|
||||
Атрибут `version` корневого элемента `MetaDataObject` определяет версию формата выгрузки.
|
||||
|
||||
### 2.2. InternalInfo
|
||||
|
||||
Содержит набор `xr:ContainedObject` — пары ClassId/ObjectId, идентифицирующие внутренние компоненты конфигурации (модули, интерфейс, справка и т.д.). Количество записей фиксировано (7 в типичной конфигурации).
|
||||
|
||||
```xml
|
||||
<InternalInfo>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>9cd510cd-abfc-11d4-9434-004095e12fc7</xr:ClassId>
|
||||
<xr:ObjectId>f0ba0954-a66b-4085-9df1-b8a4283bdbd3</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<!-- ещё 6 записей -->
|
||||
</InternalInfo>
|
||||
```
|
||||
|
||||
ClassId — фиксированные идентификаторы классов платформы. ObjectId — уникальные для каждой конфигурации.
|
||||
|
||||
### 2.3. Properties — свойства конфигурации
|
||||
|
||||
Свойства идут строго в фиксированном порядке. Пустые свойства записываются как самозакрывающийся элемент (`<Comment/>`) или с пробелом (`<Comment />`).
|
||||
|
||||
#### Идентификация и общие
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Name` | `xs:string` | Имя конфигурации (идентификатор) |
|
||||
| `Synonym` | `LocalString` | Отображаемое имя |
|
||||
| `Comment` | `xs:string` | Комментарий |
|
||||
| `NamePrefix` | `xs:string` | Префикс имён объектов |
|
||||
| `Vendor` | `xs:string` | Поставщик |
|
||||
| `Version` | `xs:string` | Версия конфигурации (напр. `3.0.181.31`) |
|
||||
| `UpdateCatalogAddress` | `xs:string` | URL каталога обновлений |
|
||||
| `BriefInformation` | `LocalString` | Краткая информация |
|
||||
| `DetailedInformation` | `LocalString` | Подробная информация |
|
||||
| `Copyright` | `LocalString` | Авторские права |
|
||||
| `VendorInformationAddress` | `LocalString` | Адрес сайта поставщика |
|
||||
| `ConfigurationInformationAddress` | `LocalString` | Адрес информации о конфигурации |
|
||||
|
||||
#### Режимы работы и совместимость
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `ConfigurationExtensionCompatibilityMode` | enum | Совместимость расширений (`Version8_3_24`, ...) |
|
||||
| `DefaultRunMode` | enum | Режим запуска (`ManagedApplication`) |
|
||||
| `ScriptVariant` | enum | Язык скриптов (`Russian` / `English`) |
|
||||
| `CompatibilityMode` | enum | Режим совместимости (`Version8_3_24`, ...) |
|
||||
| `DataLockControlMode` | enum | Управление блокировками (`Managed` / `Automatic`) |
|
||||
| `ObjectAutonumerationMode` | enum | Автонумерация (`NotAutoFree` / `AutoFree`) |
|
||||
| `ModalityUseMode` | enum | Модальность (`DontUse` / `Use` / `UseWithWarnings`) |
|
||||
| `SynchronousPlatformExtensionAndAddInCallUseMode` | enum | Синхр. вызовы (`DontUse` / `Use`) |
|
||||
| `InterfaceCompatibilityMode` | enum | Совместимость интерфейса (`Taxi` / `TaxiEnableVersion8_2`) |
|
||||
| `DatabaseTablespacesUseMode` | enum | Табличные пространства (`DontUse` / `Use`) |
|
||||
| `MainClientApplicationWindowMode` | enum | Режим окна (`Normal` / `Fullscreen` / `Kiosk`) |
|
||||
|
||||
#### Назначение и использование
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `UsePurposes` | list | Назначения: `PlatformApplication`, `MobilePlatformApplication` |
|
||||
| `DefaultRoles` | list | Роли по умолчанию: `<xr:Item xsi:type="xr:MDObjectRef">Role.XXX</xr:Item>` |
|
||||
| `DefaultLanguage` | ref | Язык по умолчанию: `Language.Русский` |
|
||||
| `IncludeHelpInContents` | `xs:boolean` | Включить справку в оглавление |
|
||||
| `UseManagedFormInOrdinaryApplication` | `xs:boolean` | Управл. формы в обычном приложении |
|
||||
| `UseOrdinaryFormInManagedApplication` | `xs:boolean` | Обычные формы в управл. приложении |
|
||||
| `Content` | list | Состав конфигурации (обычно пуст — используется при расширениях) |
|
||||
| `StandaloneConfigurationRestrictionRoles` | list | Роли ограничения автономной конфигурации |
|
||||
|
||||
#### Хранилища настроек
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `CommonSettingsStorage` | ref | Хранилище общих настроек |
|
||||
| `ReportsUserSettingsStorage` | ref | Хранилище пользовательских настроек отчётов |
|
||||
| `ReportsVariantsStorage` | ref | Хранилище вариантов отчётов (напр. `SettingsStorage.XXX`) |
|
||||
| `FormDataSettingsStorage` | ref | Хранилище данных форм |
|
||||
| `DynamicListsUserSettingsStorage` | ref | Хранилище настроек динамических списков |
|
||||
| `URLExternalDataStorage` | ref | Хранилище внешних данных URL |
|
||||
|
||||
#### Формы по умолчанию
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `DefaultReportForm` | ref | Форма отчёта по умолчанию (напр. `CommonForm.ФормаОтчета`) |
|
||||
| `DefaultReportVariantForm` | ref | Форма варианта отчёта |
|
||||
| `DefaultReportSettingsForm` | ref | Форма настроек отчёта |
|
||||
| `DefaultReportAppearanceTemplate` | ref | Шаблон оформления отчёта |
|
||||
| `DefaultDynamicListSettingsForm` | ref | Форма настроек динамического списка |
|
||||
| `DefaultSearchForm` | ref | Форма поиска |
|
||||
| `DefaultDataHistoryChangeHistoryForm` | ref | Форма истории изменений |
|
||||
| `DefaultDataHistoryVersionDataForm` | ref | Форма данных версии |
|
||||
| `DefaultDataHistoryVersionDifferencesForm` | ref | Форма различий версий |
|
||||
| `DefaultCollaborationSystemUsersChoiceForm` | ref | Форма выбора пользователей |
|
||||
| `DefaultConstantsForm` | ref | Форма констант |
|
||||
| `DefaultInterface` | ref | Интерфейс по умолчанию (устаревший) |
|
||||
| `DefaultStyle` | ref | Стиль по умолчанию (устаревший) |
|
||||
|
||||
#### Полнотекстовый поиск
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `AdditionalFullTextSearchDictionaries` | `xs:string` | Дополнительные словари |
|
||||
|
||||
#### Мобильные настройки
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `RequiredMobileApplicationPermissions` | list | Обязательные разрешения |
|
||||
| `UsedMobileApplicationFunctionalities` | list | Используемые функциональности (см. ниже) |
|
||||
| `MobileApplicationURLs` | list | URL мобильного приложения |
|
||||
| `AllowedIncomingShareRequestTypes` | list | Разрешённые типы входящих share-запросов |
|
||||
|
||||
**UsedMobileApplicationFunctionalities** — список из `app:functionality` с подэлементами `app:functionality` (имя) и `app:use` (boolean):
|
||||
|
||||
```xml
|
||||
<UsedMobileApplicationFunctionalities>
|
||||
<app:functionality>
|
||||
<app:functionality>Biometrics</app:functionality>
|
||||
<app:use>true</app:use>
|
||||
</app:functionality>
|
||||
<!-- ... -->
|
||||
</UsedMobileApplicationFunctionalities>
|
||||
```
|
||||
|
||||
Известные функциональности: `Biometrics`, `Location`, `BackgroundLocation`, `BluetoothPrinters`, `WiFiPrinters`, `Contacts`, `Calendars`, `PushNotifications`, `LocalNotifications`, `InAppPurchases`, `PersonalComputerFileExchange`, `Ads`, `NumberDialing`, `CallProcessing`, `CallLog`, `AutoSendSMS`, `ReceiveSMS`, `SMSLog`, `Camera`, `Microphone`, `MusicLibrary`, `PictureAndVideoLibraries`, `AudioPlaybackAndVibration`, `BackgroundAudioPlaybackAndVibration`, `InstallPackages`, `OSBackup`, `ApplicationUsageStatistics`, `BarcodeScanning`, `BackgroundAudioRecording`, `AllFilesAccess`, `Videoconferences`, `NFC`, `DocumentScanning`, `SpeechToText`, `Geofences`, `IncomingShareRequests`, `AllIncomingShareRequestsTypesProcessing`, `TextToSpeech` (v2.20+).
|
||||
|
||||
### 2.4. ChildObjects — состав конфигурации
|
||||
|
||||
Перечисляет все объекты метаданных, сгруппированные по типу. Имя XML-элемента = тип объекта, текстовое содержимое = имя объекта. Порядок типов фиксирован:
|
||||
|
||||
```xml
|
||||
<ChildObjects>
|
||||
<Language>Русский</Language>
|
||||
<Subsystem>Администрирование</Subsystem>
|
||||
<Subsystem>Продажи</Subsystem>
|
||||
<StyleItem>АктуальнаяПодпискаЦвет</StyleItem>
|
||||
<!-- Style (только ERP и аналогичные) -->
|
||||
<CommonPicture>AppStore</CommonPicture>
|
||||
<SessionParameter>АвторизованныйПользователь</SessionParameter>
|
||||
<Role>ПолныеПрава</Role>
|
||||
<CommonTemplate>fresh</CommonTemplate>
|
||||
<FilterCriterion>ДокументыПоВидуОплаты</FilterCriterion>
|
||||
<CommonModule>АвтоматическиеСкидки</CommonModule>
|
||||
<CommonAttribute>КомментарийЯзык1</CommonAttribute>
|
||||
<ExchangePlan>ОбновлениеИнформационнойБазы</ExchangePlan>
|
||||
<XDTOPackage>AgentScripts</XDTOPackage>
|
||||
<WebService>EnterpriseDataExchange_1_0_1_2</WebService>
|
||||
<HTTPService>RegApi</HTTPService>
|
||||
<WSReference>WSСборОтчетностиРосстата</WSReference>
|
||||
<EventSubscription>ВстраиваниеОбщихФорм</EventSubscription>
|
||||
<ScheduledJob>АвтоматическаяВыгрузкаЧеков</ScheduledJob>
|
||||
<SettingsStorage>БуферыОбменаНовостей</SettingsStorage>
|
||||
<FunctionalOption>ИспользоватьВалюту</FunctionalOption>
|
||||
<FunctionalOptionsParameter>Организация</FunctionalOptionsParameter>
|
||||
<DefinedType>ОписаниеТаблицОбъекта</DefinedType>
|
||||
<CommonCommand>АвтономнаяРабота</CommonCommand>
|
||||
<CommandGroup>Документы</CommandGroup>
|
||||
<Constant>АдресОбработкиОповещений</Constant>
|
||||
<CommonForm>ФормаОтчета</CommonForm>
|
||||
<Catalog>Банки</Catalog>
|
||||
<Document>АвансовыйОтчет</Document>
|
||||
<DocumentNumerator>ПерсонифицированныйУчет</DocumentNumerator>
|
||||
<Sequence>ДокументыОрганизаций</Sequence>
|
||||
<DocumentJournal>ЖурналДокументовЕГАИС</DocumentJournal>
|
||||
<Enum>АвтоОперацииСПодотчетником</Enum>
|
||||
<Report>АктСверки</Report>
|
||||
<DataProcessor>АвансовыйОтчет</DataProcessor>
|
||||
<InformationRegister>АвторизованныеПодключения</InformationRegister>
|
||||
<AccumulationRegister>ВозвратыТоваров</AccumulationRegister>
|
||||
<ChartOfCharacteristicTypes>ВидыСубконтоХозрасчетные</ChartOfCharacteristicTypes>
|
||||
<ChartOfAccounts>Хозрасчетный</ChartOfAccounts>
|
||||
<AccountingRegister>Хозрасчетный</AccountingRegister>
|
||||
<ChartOfCalculationTypes>Начисления</ChartOfCalculationTypes>
|
||||
<!-- CalculationRegister (только ERP и аналогичные) -->
|
||||
<BusinessProcess>Задание</BusinessProcess>
|
||||
<Task>ЗадачаИсполнителя</Task>
|
||||
<IntegrationService>ОбменСообщениями</IntegrationService>
|
||||
</ChildObjects>
|
||||
```
|
||||
|
||||
#### Порядок типов в ChildObjects
|
||||
|
||||
| № | XML-элемент | Каталог | Описание |
|
||||
|---|-------------|---------|----------|
|
||||
| 1 | `Language` | `Languages/` | Языки |
|
||||
| 2 | `Subsystem` | `Subsystems/` | Подсистемы |
|
||||
| 3 | `StyleItem` | `StyleItems/` | Элементы стиля |
|
||||
| 4 | `Style` | `Styles/` | Стили (устаревший тип) |
|
||||
| 5 | `CommonPicture` | `CommonPictures/` | Общие картинки |
|
||||
| 6 | `SessionParameter` | `SessionParameters/` | Параметры сеанса |
|
||||
| 7 | `Role` | `Roles/` | Роли |
|
||||
| 8 | `CommonTemplate` | `CommonTemplates/` | Общие макеты |
|
||||
| 9 | `FilterCriterion` | `FilterCriteria/` | Критерии отбора |
|
||||
| 10 | `CommonModule` | `CommonModules/` | Общие модули |
|
||||
| 11 | `CommonAttribute` | `CommonAttributes/` | Общие реквизиты |
|
||||
| 12 | `ExchangePlan` | `ExchangePlans/` | Планы обмена |
|
||||
| 13 | `XDTOPackage` | `XDTOPackages/` | XDTO-пакеты |
|
||||
| 14 | `WebService` | `WebServices/` | Веб-сервисы |
|
||||
| 15 | `HTTPService` | `HTTPServices/` | HTTP-сервисы |
|
||||
| 16 | `WSReference` | `WSReferences/` | WS-ссылки |
|
||||
| 17 | `EventSubscription` | `EventSubscriptions/` | Подписки на события |
|
||||
| 18 | `ScheduledJob` | `ScheduledJobs/` | Регламентные задания |
|
||||
| 19 | `SettingsStorage` | `SettingsStorages/` | Хранилища настроек |
|
||||
| 20 | `FunctionalOption` | `FunctionalOptions/` | Функциональные опции |
|
||||
| 21 | `FunctionalOptionsParameter` | `FunctionalOptionsParameters/` | Параметры ФО |
|
||||
| 22 | `DefinedType` | `DefinedTypes/` | Определяемые типы |
|
||||
| 23 | `CommonCommand` | `CommonCommands/` | Общие команды |
|
||||
| 24 | `CommandGroup` | `CommandGroups/` | Группы команд |
|
||||
| 25 | `Constant` | `Constants/` | Константы |
|
||||
| 26 | `CommonForm` | `CommonForms/` | Общие формы |
|
||||
| 27 | `Catalog` | `Catalogs/` | Справочники |
|
||||
| 28 | `Document` | `Documents/` | Документы |
|
||||
| 29 | `DocumentNumerator` | `DocumentNumerators/` | Нумераторы документов |
|
||||
| 30 | `Sequence` | `Sequences/` | Последовательности |
|
||||
| 31 | `DocumentJournal` | `DocumentJournals/` | Журналы документов |
|
||||
| 32 | `Enum` | `Enums/` | Перечисления |
|
||||
| 33 | `Report` | `Reports/` | Отчёты |
|
||||
| 34 | `DataProcessor` | `DataProcessors/` | Обработки |
|
||||
| 35 | `InformationRegister` | `InformationRegisters/` | Регистры сведений |
|
||||
| 36 | `AccumulationRegister` | `AccumulationRegisters/` | Регистры накопления |
|
||||
| 37 | `ChartOfCharacteristicTypes` | `ChartsOfCharacteristicTypes/` | Планы видов характеристик |
|
||||
| 38 | `ChartOfAccounts` | `ChartsOfAccounts/` | Планы счетов |
|
||||
| 39 | `AccountingRegister` | `AccountingRegisters/` | Регистры бухгалтерии |
|
||||
| 40 | `ChartOfCalculationTypes` | `ChartsOfCalculationTypes/` | Планы видов расчёта |
|
||||
| 41 | `CalculationRegister` | `CalculationRegisters/` | Регистры расчёта |
|
||||
| 42 | `BusinessProcess` | `BusinessProcesses/` | Бизнес-процессы |
|
||||
| 43 | `Task` | `Tasks/` | Задачи |
|
||||
| 44 | `IntegrationService` | `IntegrationServices/` | Сервисы интеграции |
|
||||
|
||||
Внутри одного типа объекты отсортированы по имени (алфавитный порядок). Типы, для которых нет объектов, в ChildObjects не записываются.
|
||||
|
||||
---
|
||||
|
||||
## 3. ConfigDumpInfo.xml — служебный файл выгрузки
|
||||
|
||||
Содержит информацию о версиях всех объектов конфигурации. Используется платформой для определения изменений при загрузке.
|
||||
|
||||
### 3.1. Общая структура
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
<Metadata name="..." id="..." configVersion="...">
|
||||
<Metadata name="..." id="..."/>
|
||||
...
|
||||
</Metadata>
|
||||
...
|
||||
</ConfigVersions>
|
||||
</ConfigDumpInfo>
|
||||
```
|
||||
|
||||
### 3.2. Атрибуты корневого элемента
|
||||
|
||||
| Атрибут | Описание |
|
||||
|---------|----------|
|
||||
| `format` | Формат выгрузки (`Hierarchical`) |
|
||||
| `version` | Версия формата (`2.17` / `2.20`) — совпадает с Configuration.xml |
|
||||
|
||||
### 3.3. Структура записей Metadata
|
||||
|
||||
Каждый `<Metadata>` описывает один объект или его компоненту:
|
||||
|
||||
| Атрибут | Описание |
|
||||
|---------|----------|
|
||||
| `name` | Полное имя в dot-нотации (напр. `Catalog.Банки.Attribute.Код`) |
|
||||
| `id` | UUID объекта (с суффиксом `.N` для модулей/форм/справки) |
|
||||
| `configVersion` | Хеш версии (32 hex-символа + `00000000`), только у записей с файлами |
|
||||
|
||||
**Правила:**
|
||||
- Корневой объект содержит вложенные `<Metadata>` для реквизитов, измерений, ресурсов (без `configVersion`, т.к. они не имеют отдельных файлов)
|
||||
- Формы, модули, справка — отдельные `<Metadata>` верхнего уровня с `configVersion`
|
||||
- Суффиксы id для модулей: `.0` — форма, `.1` — модуль набора записей, `.2` — модуль менеджера, `.5` — справка, `.6` — модуль набора записей (альт.), `.7` — модуль менеджера (альт.)
|
||||
|
||||
```xml
|
||||
<!-- Объект с вложенными реквизитами -->
|
||||
<Metadata name="AccountingRegister.Хозрасчетный" id="7b248429-..." configVersion="eda4...">
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.Attribute.Содержание" id="17c87c43-..."/>
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.Resource.Сумма" id="3656a8da-..."/>
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.Dimension.Организация" id="4d42e16e-..."/>
|
||||
</Metadata>
|
||||
<!-- Форма — отдельная запись -->
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.Form.ФормаСписка" id="5a682c7f-..." configVersion="1362..."/>
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.Form.ФормаСписка.Form" id="5a682c7f-....0" configVersion="e384..."/>
|
||||
<!-- Модули — отдельные записи -->
|
||||
<Metadata name="AccountingRegister.Хозрасчетный.ManagerModule" id="7b248429-....7" configVersion="0387..."/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Ext/ — корневой каталог конфигурации
|
||||
|
||||
Каталог `Ext/` содержит файлы, относящиеся к конфигурации в целом (не к отдельным объектам).
|
||||
|
||||
### 4.1. Модули (BSL)
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `ManagedApplicationModule.bsl` | Модуль управляемого приложения |
|
||||
| `OrdinaryApplicationModule.bsl` | Модуль обычного приложения |
|
||||
| `SessionModule.bsl` | Модуль сеанса |
|
||||
| `ExternalConnectionModule.bsl` | Модуль внешнего соединения |
|
||||
|
||||
Все модули — текстовые файлы в кодировке UTF-8 с BOM, содержащие код на языке 1С (BSL).
|
||||
|
||||
### 4.2. Командный интерфейс
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `CommandInterface.xml` | Корневой командный интерфейс (порядок подсистем, видимость) |
|
||||
| `MainSectionCommandInterface.xml` | Командный интерфейс главного раздела |
|
||||
| `ClientApplicationInterface.xml` | Интерфейс клиентского приложения (расположение панелей) |
|
||||
|
||||
**CommandInterface.xml** — описывает порядок подсистем и видимость команд для главного окна:
|
||||
|
||||
```xml
|
||||
<CommandInterface xmlns="http://v8.1c.ru/8.3/xcf/extrnprops" ... version="2.17">
|
||||
<SubsystemsOrder>
|
||||
<Subsystem>Subsystem.Руководителю</Subsystem>
|
||||
<Subsystem>Subsystem.БанкИКасса</Subsystem>
|
||||
...
|
||||
</SubsystemsOrder>
|
||||
</CommandInterface>
|
||||
```
|
||||
|
||||
Подробнее: [1c-subsystem-spec.md § 4](1c-subsystem-spec.md#4-формат-командного-интерфейса-commandinterfacexml).
|
||||
|
||||
**ClientApplicationInterface.xml** — расположение панелей (top/left/bottom/right):
|
||||
|
||||
```xml
|
||||
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" ...>
|
||||
<top>
|
||||
<group id="...">
|
||||
<group><panel id="..."><uuid>...</uuid></panel></group>
|
||||
...
|
||||
</group>
|
||||
</top>
|
||||
<left>...</left>
|
||||
</ClientApplicationInterface>
|
||||
```
|
||||
|
||||
### 4.3. Начальная страница
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `HomePageWorkArea.xml` | Рабочая область начальной страницы |
|
||||
|
||||
```xml
|
||||
<HomePageWorkArea xmlns="http://v8.1c.ru/8.3/xcf/extrnprops" ... version="2.17">
|
||||
<WorkingAreaTemplate>TwoColumnsVariableWidth</WorkingAreaTemplate>
|
||||
<LeftColumn>
|
||||
<Item>
|
||||
<Form>CommonForm.НачалоРаботы</Form>
|
||||
<Height>100</Height>
|
||||
<Visibility>
|
||||
<xr:Common>true</xr:Common>
|
||||
<xr:Value name="Role.ОператорОтправки...">false</xr:Value>
|
||||
</Visibility>
|
||||
</Item>
|
||||
...
|
||||
</LeftColumn>
|
||||
<RightColumn>...</RightColumn>
|
||||
</HomePageWorkArea>
|
||||
```
|
||||
|
||||
Шаблон рабочей области: `TwoColumnsVariableWidth`, `OneColumn` и др.
|
||||
|
||||
### 4.4. Картинки
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `Splash.xml` + `Splash/Picture.png` | Заставка при запуске |
|
||||
| `MainSectionPicture.xml` + `MainSectionPicture/Picture.svg` | Картинка главного раздела |
|
||||
|
||||
Формат XML-описания картинки:
|
||||
|
||||
```xml
|
||||
<ExtPicture xmlns="http://v8.1c.ru/8.3/xcf/extrnprops" ... version="2.17">
|
||||
<Picture>
|
||||
<xr:Abs>Picture.png</xr:Abs>
|
||||
<xr:LoadTransparent>false</xr:LoadTransparent>
|
||||
</Picture>
|
||||
</ExtPicture>
|
||||
```
|
||||
|
||||
### 4.5. Бинарные файлы
|
||||
|
||||
| Файл | Описание |
|
||||
|------|----------|
|
||||
| `ParentConfigurations.bin` | Информация о родительских конфигурациях (поставки) |
|
||||
| `MobileClientSignature.bin` | Подпись мобильного клиента |
|
||||
|
||||
---
|
||||
|
||||
## 5. Языки (Languages)
|
||||
|
||||
Языки — простейший тип объекта конфигурации. Каталог `Languages/`, один XML-файл на язык.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" ... version="2.17">
|
||||
<Language uuid="db4a9ccb-9ef5-4b3c-8577-b6fe5db1b62e">
|
||||
<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>
|
||||
```
|
||||
|
||||
| Свойство | Описание |
|
||||
|----------|----------|
|
||||
| `Name` | Имя (идентификатор) |
|
||||
| `Synonym` | Отображаемое имя |
|
||||
| `Comment` | Комментарий |
|
||||
| `LanguageCode` | Код языка (`ru`, `en`, и т.д.) |
|
||||
|
||||
Язык, указанный в `Properties.DefaultLanguage` конфигурации как `Language.Русский`, является основным.
|
||||
|
||||
---
|
||||
|
||||
## 6. Дополнительные типы объектов
|
||||
|
||||
Ниже описаны типы, не покрытые в [1c-config-objects-spec.md](1c-config-objects-spec.md). Все объекты следуют стандартной структуре `MetaDataObject / <Type> / Properties` с обязательными `Name`, `Synonym`, `Comment`.
|
||||
|
||||
### 6.1. CommonPicture — общая картинка
|
||||
|
||||
Каталог: `CommonPictures/`. Файлы: `<Имя>.xml` + `<Имя>/Ext/Picture/` (файлы картинок).
|
||||
|
||||
```xml
|
||||
<CommonPicture uuid="...">
|
||||
<Properties>
|
||||
<Name>AppStore</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<AvailabilityForChoice>false</AvailabilityForChoice>
|
||||
<AvailabilityForAppearance>false</AvailabilityForAppearance>
|
||||
</Properties>
|
||||
</CommonPicture>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `AvailabilityForChoice` | `xs:boolean` | Доступность для выбора в интерфейсе |
|
||||
| `AvailabilityForAppearance` | `xs:boolean` | Доступность для оформления |
|
||||
|
||||
### 6.2. CommonTemplate — общий макет
|
||||
|
||||
Каталог: `CommonTemplates/`. Файлы: `<Имя>.xml` (метаданные) + `<Имя>/Ext/Template.xml` (содержимое).
|
||||
|
||||
```xml
|
||||
<CommonTemplate uuid="...">
|
||||
<Properties>
|
||||
<Name>fresh</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<TemplateType>BinaryData</TemplateType>
|
||||
</Properties>
|
||||
</CommonTemplate>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `TemplateType` | enum | Тип макета: `SpreadsheetDocument`, `BinaryData`, `HTMLDocument`, `TextDocument`, `ActiveDocument`, `DataCompositionSchema`, `DataCompositionAppearanceTemplate`, `GraphicalSchema`, `AddIn` |
|
||||
|
||||
### 6.3. CommonAttribute — общий реквизит
|
||||
|
||||
Каталог: `CommonAttributes/`. Один XML-файл на объект.
|
||||
|
||||
```xml
|
||||
<CommonAttribute uuid="...">
|
||||
<Properties>
|
||||
<Name>КомментарийЯзык1</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
<!-- Свойства аналогичны Attribute объекта: PasswordMode, Format, EditFormat, ... -->
|
||||
<AutoUse>DontUse</AutoUse>
|
||||
<DataSeparation>DontUse</DataSeparation>
|
||||
<SeparatedDataUse>IndependentlyAndSimultaneously</SeparatedDataUse>
|
||||
<DataSeparationValue/>
|
||||
<DataSeparationUse/>
|
||||
<ConditionalSeparation/>
|
||||
<UsersSeparation>DontUse</UsersSeparation>
|
||||
<AuthenticationSeparation>DontUse</AuthenticationSeparation>
|
||||
<ConfigurationExtensionsSeparation>DontUse</ConfigurationExtensionsSeparation>
|
||||
<Content>...</Content>
|
||||
</Properties>
|
||||
</CommonAttribute>
|
||||
```
|
||||
|
||||
Специфичные свойства (помимо стандартных реквизитных): `AutoUse`, `DataSeparation`, `SeparatedDataUse`, `DataSeparationValue`, `DataSeparationUse`, `Content` (список объектов, к которым применяется).
|
||||
|
||||
### 6.4. CommonForm — общая форма
|
||||
|
||||
Каталог: `CommonForms/`. Файлы: `<Имя>.xml` (метаданные) + `<Имя>/Ext/Form.xml` + `<Имя>/Ext/Form/Module.bsl`.
|
||||
|
||||
```xml
|
||||
<CommonForm uuid="...">
|
||||
<Properties>
|
||||
<Name>АварийныйРежимИСМП</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<FormType>Managed</FormType>
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<UsePurposes>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
|
||||
<v8:Value xsi:type="app:ApplicationUsePurpose">MobilePlatformApplication</v8:Value>
|
||||
</UsePurposes>
|
||||
<UseStandardCommands>false</UseStandardCommands>
|
||||
<ExtendedPresentation/>
|
||||
<Explanation/>
|
||||
</Properties>
|
||||
</CommonForm>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `FormType` | enum | `Managed` / `Ordinary` |
|
||||
| `IncludeHelpInContents` | `xs:boolean` | Включить справку в оглавление |
|
||||
| `UsePurposes` | list | Назначения |
|
||||
| `UseStandardCommands` | `xs:boolean` | Использовать стандартные команды |
|
||||
| `ExtendedPresentation` | `LocalString` | Расширенное представление |
|
||||
| `Explanation` | `LocalString` | Пояснение |
|
||||
|
||||
### 6.5. CommonCommand — общая команда
|
||||
|
||||
Каталог: `CommonCommands/`. Файлы: `<Имя>.xml` + `<Имя>/Ext/CommandModule.bsl`.
|
||||
|
||||
```xml
|
||||
<CommonCommand uuid="...">
|
||||
<Properties>
|
||||
<Name>АвтономнаяРабота</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Group>NavigationPanelOrdinary</Group>
|
||||
<Representation>Auto</Representation>
|
||||
<ToolTip/>
|
||||
<Picture/>
|
||||
<Shortcut/>
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<CommandParameterType/>
|
||||
<ParameterUseMode>Single</ParameterUseMode>
|
||||
<ModifiesData>false</ModifiesData>
|
||||
<OnMainServerUnavalableBehavior>Auto</OnMainServerUnavalableBehavior>
|
||||
</Properties>
|
||||
</CommonCommand>
|
||||
```
|
||||
|
||||
Подробнее: [1c-subsystem-spec.md § 6](1c-subsystem-spec.md#6-формат-общей-команды-commoncommand).
|
||||
|
||||
### 6.6. SessionParameter — параметр сеанса
|
||||
|
||||
Каталог: `SessionParameters/`. Один XML-файл на объект.
|
||||
|
||||
```xml
|
||||
<SessionParameter uuid="...">
|
||||
<Properties>
|
||||
<Name>АвторизованныйПользователь</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>cfg:CatalogRef.ВнешниеПользователи</v8:Type>
|
||||
<v8:Type>cfg:CatalogRef.Пользователи</v8:Type>
|
||||
</Type>
|
||||
</Properties>
|
||||
</SessionParameter>
|
||||
```
|
||||
|
||||
Единственное специфичное свойство — `Type` (составной тип).
|
||||
|
||||
### 6.7. FunctionalOption — функциональная опция
|
||||
|
||||
Каталог: `FunctionalOptions/`. Один XML-файл на объект.
|
||||
|
||||
```xml
|
||||
<FunctionalOption uuid="...">
|
||||
<Properties>
|
||||
<Name>АвансыВключаютсяВДоходыВПериодеПолучения</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Location>InformationRegister.НастройкиУчетаНДФЛ.Resource.АвансыВключаютсяВДоходыВПериодеПолучения</Location>
|
||||
<PrivilegedGetMode>true</PrivilegedGetMode>
|
||||
<Content/>
|
||||
</Properties>
|
||||
</FunctionalOption>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Location` | ref | Где хранится значение (ссылка на реквизит/ресурс/константу) |
|
||||
| `PrivilegedGetMode` | `xs:boolean` | Привилегированный режим получения |
|
||||
| `Content` | list | Состав (объекты, зависящие от опции) |
|
||||
|
||||
### 6.8. FunctionalOptionsParameter — параметр функциональных опций
|
||||
|
||||
Каталог: `FunctionalOptionsParameters/`. Один XML-файл.
|
||||
|
||||
```xml
|
||||
<FunctionalOptionsParameter uuid="...">
|
||||
<Properties>
|
||||
<Name>ДополнительныеОтчетыИОбработкиОбъектНазначения</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Use>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">InformationRegister.Назначение...Dimension.ОбъектНазначения</xr:Item>
|
||||
</Use>
|
||||
</Properties>
|
||||
</FunctionalOptionsParameter>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Use` | list | Список измерений/реквизитов, используемых как параметр |
|
||||
|
||||
### 6.9. Sequence — последовательность документов
|
||||
|
||||
Каталог: `Sequences/`. Один XML-файл.
|
||||
|
||||
```xml
|
||||
<Sequence uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="SequenceRecord.ДокументыОрганизаций" category="Record">...</xr:GeneratedType>
|
||||
<xr:GeneratedType name="SequenceManager.ДокументыОрганизаций" category="Manager">...</xr:GeneratedType>
|
||||
<xr:GeneratedType name="SequenceRecordSet.ДокументыОрганизаций" category="RecordSet">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>ДокументыОрганизаций</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<MoveBoundaryOnPosting>DontMove</MoveBoundaryOnPosting>
|
||||
<Documents>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">Document.АвансовыйОтчет</xr:Item>
|
||||
...
|
||||
</Documents>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Dimension>Организация</Dimension>
|
||||
</ChildObjects>
|
||||
</Sequence>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `MoveBoundaryOnPosting` | enum | Перемещение границы при проведении: `DontMove` / `Move` |
|
||||
| `Documents` | list | Список документов, входящих в последовательность |
|
||||
|
||||
ChildObjects могут содержать `Dimension` (измерения последовательности).
|
||||
|
||||
### 6.10. SettingsStorage — хранилище настроек
|
||||
|
||||
Каталог: `SettingsStorages/`. Файлы: `<Имя>.xml` + `<Имя>/` (формы, модули).
|
||||
|
||||
```xml
|
||||
<SettingsStorage uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="SettingsStorageManager.БуферыОбменаНовостей" category="Manager">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>БуферыОбменаНовостей</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<DefaultSaveForm/>
|
||||
<DefaultLoadForm/>
|
||||
<AuxiliarySaveForm/>
|
||||
<AuxiliaryLoadForm/>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Form>ФормаУправленияБуферамиОбмена</Form>
|
||||
</ChildObjects>
|
||||
</SettingsStorage>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `DefaultSaveForm` | ref | Форма сохранения по умолчанию |
|
||||
| `DefaultLoadForm` | ref | Форма загрузки по умолчанию |
|
||||
| `AuxiliarySaveForm` | ref | Вспомогательная форма сохранения |
|
||||
| `AuxiliaryLoadForm` | ref | Вспомогательная форма загрузки |
|
||||
|
||||
ChildObjects: `Form` (формы), `Template` (макеты).
|
||||
|
||||
### 6.11. FilterCriterion — критерий отбора
|
||||
|
||||
Каталог: `FilterCriteria/`. Один XML-файл.
|
||||
|
||||
```xml
|
||||
<FilterCriterion uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="FilterCriterionManager.ДокументыПоВидуОплаты" category="Manager">...</xr:GeneratedType>
|
||||
<xr:GeneratedType name="FilterCriterionList.ДокументыПоВидуОплаты" category="List">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>ДокументыПоВидуОплаты</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>cfg:CatalogRef.ВидыОплатОрганизаций</v8:Type>
|
||||
</Type>
|
||||
<UseStandardCommands>true</UseStandardCommands>
|
||||
<Content>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">Document.ОплатаПлатежнойКартой.Attribute.ВидОплаты</xr:Item>
|
||||
...
|
||||
</Content>
|
||||
</Properties>
|
||||
</FilterCriterion>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Type` | type-def | Тип значения критерия (обычно ссылочный) |
|
||||
| `UseStandardCommands` | `xs:boolean` | Использовать стандартные команды |
|
||||
| `Content` | list | Реквизиты объектов, по которым выполняется отбор |
|
||||
|
||||
ChildObjects: `Form` (формы), `Command` (команды).
|
||||
|
||||
### 6.12. DocumentNumerator — нумератор документов
|
||||
|
||||
Каталог: `DocumentNumerators/`. Один XML-файл.
|
||||
|
||||
```xml
|
||||
<DocumentNumerator uuid="...">
|
||||
<Properties>
|
||||
<Name>ПерсонифицированныйУчет</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<NumberType>String</NumberType>
|
||||
<NumberLength>11</NumberLength>
|
||||
<NumberAllowedLength>Variable</NumberAllowedLength>
|
||||
<NumberPeriodicity>Year</NumberPeriodicity>
|
||||
<CheckUnique>true</CheckUnique>
|
||||
</Properties>
|
||||
</DocumentNumerator>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `NumberType` | enum | Тип номера: `String` / `Number` |
|
||||
| `NumberLength` | `xs:decimal` | Длина номера |
|
||||
| `NumberAllowedLength` | enum | Допустимая длина: `Variable` / `Fixed` |
|
||||
| `NumberPeriodicity` | enum | Периодичность: `Nonperiodical` / `Year` / `Quarter` / `Month` / `Day` |
|
||||
| `CheckUnique` | `xs:boolean` | Контроль уникальности |
|
||||
|
||||
### 6.13. IntegrationService — сервис интеграции
|
||||
|
||||
Каталог: `IntegrationServices/`. Файлы: `<Имя>.xml` + `<Имя>/Ext/Module.bsl`.
|
||||
|
||||
```xml
|
||||
<IntegrationService uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="IntegrationServiceManager.ОбменСообщениями" category="Manager">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>ОбменСообщениями</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<ExternalIntegrationServiceAddress/>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<IntegrationServiceChannel uuid="...">
|
||||
<InternalInfo>...</InternalInfo>
|
||||
<Properties>
|
||||
<Name>input_from_SM_normal_priority</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<ExternalIntegrationServiceChannelName>e1c::FreshBus::Main::...</ExternalIntegrationServiceChannelName>
|
||||
<MessageDirection>Receive</MessageDirection>
|
||||
<ReceiveMessageProcessing>ОбработатьСообщениеОбычныйПриоритет</ReceiveMessageProcessing>
|
||||
<Transactioned>false</Transactioned>
|
||||
</Properties>
|
||||
</IntegrationServiceChannel>
|
||||
</ChildObjects>
|
||||
</IntegrationService>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `ExternalIntegrationServiceAddress` | `xs:string` | Адрес внешнего сервиса интеграции |
|
||||
|
||||
ChildObjects содержат `IntegrationServiceChannel` (каналы) — inline-определения с uuid, InternalInfo и Properties (Name, ExternalIntegrationServiceChannelName, MessageDirection: `Send`/`Receive`, ReceiveMessageProcessing, Transactioned).
|
||||
|
||||
### 6.14. XDTOPackage — XDTO-пакет
|
||||
|
||||
Каталог: `XDTOPackages/`. Файлы: `<Имя>.xml` (метаданные) + `<Имя>/Ext/Package.xdto` (схема).
|
||||
|
||||
```xml
|
||||
<XDTOPackage uuid="...">
|
||||
<Properties>
|
||||
<Name>AgentScripts</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Namespace>http://v8.1c.ru/agent/scripts/1.0</Namespace>
|
||||
</Properties>
|
||||
</XDTOPackage>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Namespace` | `xs:string` | URI пространства имён XDTO-пакета |
|
||||
|
||||
### 6.15. WSReference — WS-ссылка
|
||||
|
||||
Каталог: `WSReferences/`. Файлы: `<Имя>.xml` + `<Имя>/Ext/WSDefinition.wsdl`.
|
||||
|
||||
```xml
|
||||
<WSReference uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="WSReferenceManager.WSСборОтчетностиРосстата" category="Manager">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>WSСборОтчетностиРосстата</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<LocationURL>file://C:/TEMP/ECCOwsdl.xml</LocationURL>
|
||||
</Properties>
|
||||
</WSReference>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `LocationURL` | `xs:string` | URL WSDL-описания сервиса |
|
||||
|
||||
### 6.16. StyleItem — элемент стиля
|
||||
|
||||
Каталог: `StyleItems/`. Один XML-файл.
|
||||
|
||||
```xml
|
||||
<StyleItem uuid="...">
|
||||
<Properties>
|
||||
<Name>АктуальнаяПодпискаЦвет</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Type>Color</Type>
|
||||
<Value xsi:type="v8ui:Color">#009646</Value>
|
||||
</Properties>
|
||||
</StyleItem>
|
||||
```
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Type` | enum | Тип элемента стиля: `Color`, `Font`, `Border` |
|
||||
| `Value` | varies | Значение: цвет `#RRGGBB`, шрифт (`v8ui:FontInfo`), рамка (`v8ui:BorderInfo`) |
|
||||
|
||||
### 6.17. Style — стиль (устаревший)
|
||||
|
||||
Каталог: `Styles/`. Файлы: `<Имя>.xml` + `<Имя>/Ext/Style.xml`.
|
||||
|
||||
```xml
|
||||
<Style uuid="...">
|
||||
<Properties>
|
||||
<Name>Основной</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
</Properties>
|
||||
</Style>
|
||||
```
|
||||
|
||||
Присутствует только в конфигурациях с поддержкой устаревших стилей (ERP). Не имеет специфичных свойств в метаданных; содержимое в `Ext/Style.xml`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Различия версий 2.17 → 2.20
|
||||
|
||||
### 7.1. Атрибут version
|
||||
|
||||
```xml
|
||||
<!-- 2.17 -->
|
||||
<MetaDataObject ... version="2.17">
|
||||
<!-- 2.20 -->
|
||||
<MetaDataObject ... version="2.20">
|
||||
```
|
||||
|
||||
### 7.2. Configuration.xml — Properties
|
||||
|
||||
Набор свойств Properties **идентичен** в обеих версиях. Отличия только в значениях:
|
||||
|
||||
| Свойство | Изменение |
|
||||
|----------|-----------|
|
||||
| `CompatibilityMode` | `Version8_3_24` → `Version8_3_24` / `Version8_3_27` (зависит от конфигурации) |
|
||||
| `ConfigurationExtensionCompatibilityMode` | аналогично |
|
||||
| `UsedMobileApplicationFunctionalities` | В v2.20 добавлена функциональность `TextToSpeech` |
|
||||
|
||||
### 7.3. Configuration.xml — ChildObjects
|
||||
|
||||
Набор типов объектов в ChildObjects **идентичен** между версиями 2.17 и 2.20.
|
||||
|
||||
### 7.4. ConfigDumpInfo.xml
|
||||
|
||||
Атрибут `version` меняется на `2.20`. Структура записей не изменилась.
|
||||
|
||||
### 7.5. Ext/ файлы
|
||||
|
||||
В файлах `CommandInterface.xml`, `HomePageWorkArea.xml` атрибут `version` меняется на `2.20`. Структура не изменилась.
|
||||
|
||||
### 7.6. Форматирование XML
|
||||
|
||||
В v2.20 пустые элементы записываются без пробела: `<Comment/>` вместо `<Comment />`. Это косметическое отличие, не влияющее на парсинг.
|
||||
|
||||
---
|
||||
|
||||
## 8. Пространства имён XML
|
||||
|
||||
Полный набор namespace, используемых в Configuration.xml:
|
||||
|
||||
| Префикс | URI | Назначение |
|
||||
|---------|-----|------------|
|
||||
| *(default)* | `http://v8.1c.ru/8.3/MDClasses` | Метаданные объектов |
|
||||
| `v8` | `http://v8.1c.ru/8.1/data/core` | Ядро данных (типы, LocalString) |
|
||||
| `xr` | `http://v8.1c.ru/8.3/xcf/readable` | Человекочитаемые ссылки |
|
||||
| `xs` | `http://www.w3.org/2001/XMLSchema` | XML Schema типы |
|
||||
| `xsi` | `http://www.w3.org/2001/XMLSchema-instance` | Атрибуты xsi:type, xsi:nil |
|
||||
| `app` | `http://v8.1c.ru/8.2/managed-application/core` | Управляемое приложение |
|
||||
| `cfg` | `http://v8.1c.ru/8.1/data/enterprise/current-config` | Ссылочные типы конфигурации |
|
||||
| `v8ui` | `http://v8.1c.ru/8.1/data/ui` | UI-элементы (цвета, шрифты) |
|
||||
| `style` | `http://v8.1c.ru/8.1/data/ui/style` | Стили |
|
||||
| `sys` | `http://v8.1c.ru/8.1/data/ui/fonts/system` | Системные шрифты |
|
||||
| `web` | `http://v8.1c.ru/8.1/data/ui/colors/web` | Web-цвета |
|
||||
| `win` | `http://v8.1c.ru/8.1/data/ui/colors/windows` | Windows-цвета |
|
||||
| `xen` | `http://v8.1c.ru/8.3/xcf/enums` | Перечисления формата |
|
||||
| `xpr` | `http://v8.1c.ru/8.3/xcf/predef` | Предопределённые элементы |
|
||||
| `ent` | `http://v8.1c.ru/8.1/data/enterprise` | Предприятие |
|
||||
| `cmi` | `http://v8.1c.ru/8.2/managed-application/cmi` | Командный интерфейс |
|
||||
| `lf` | `http://v8.1c.ru/8.2/managed-application/logform` | Логические формы |
|
||||
@@ -3,6 +3,8 @@
|
||||
Формат: XML-выгрузка внешней обработки (ExternalDataProcessor) из конфигуратора 1С:Предприятие 8.3.
|
||||
Версия формата: `2.17`.
|
||||
|
||||
> **Связанная спецификация**: Для внешних отчётов (ExternalReport / ERF) см. [1c-erf-spec.md](1c-erf-spec.md). Формат отчётов основан на формате обработок с дополнительными свойствами для СКД и вариантов.
|
||||
|
||||
## 1. Структура каталогов
|
||||
|
||||
```
|
||||
@@ -28,11 +30,12 @@
|
||||
```
|
||||
|
||||
Обработка может содержать:
|
||||
- 0..N реквизитов объекта (описаны в корневом XML)
|
||||
- 0..N табличных частей (описаны в корневом XML)
|
||||
- 0..N форм (каталог `Forms/`)
|
||||
- 0..N макетов (каталог `Templates/`)
|
||||
- 0..1 модуль объекта (`Ext/ObjectModule.bsl`)
|
||||
- 0..1 встроенная справка (`Ext/Help.xml` + `Ext/Help/<язык>.html`), см. [1c-help-spec.md](1c-help-spec.md)
|
||||
- 0..N табличных частей (описаны в корневом XML)
|
||||
|
||||
## 2. Пространства имён XML
|
||||
|
||||
@@ -132,6 +135,8 @@ xmlns="http://v8.1c.ru/8.3/xcf/logform"
|
||||
<AuxiliaryForm/>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<!-- Реквизиты объекта (опционально) -->
|
||||
<Attribute uuid="<UUID>">...</Attribute>
|
||||
<!-- Табличные части (опционально) -->
|
||||
<TabularSection uuid="<UUID>">...</TabularSection>
|
||||
<!-- Формы -->
|
||||
@@ -151,8 +156,102 @@ xmlns="http://v8.1c.ru/8.3/xcf/logform"
|
||||
| `ObjectId`, `TypeId`, `ValueId` | Уникальные UUID, генерируются при создании |
|
||||
| `DefaultForm` | Полный путь: `ExternalDataProcessor.<Имя>.Form.<ИмяФормы>` |
|
||||
| `<Form>`, `<Template>` | Только имена (без путей), соответствуют именам подкаталогов в `Forms/` и `Templates/` |
|
||||
| `<Attribute>` | Реквизиты объекта обработки (полное описание с типами) |
|
||||
| `<TabularSection>` | Полное описание табличных частей объекта (включая реквизиты ТЧ с типами) |
|
||||
|
||||
### Порядок элементов в ChildObjects
|
||||
|
||||
Порядок дочерних объектов **фиксирован**:
|
||||
|
||||
1. `<Attribute>` — реквизиты объекта (0..N)
|
||||
2. `<TabularSection>` — табличные части (0..N)
|
||||
3. `<Form>` — формы (0..N)
|
||||
4. `<Template>` — макеты (0..N)
|
||||
|
||||
### Реквизиты объекта
|
||||
|
||||
Если обработка имеет реквизиты объекта, они описываются в `<ChildObjects>` корневого файла:
|
||||
|
||||
```xml
|
||||
<Attribute uuid="<UUID>">
|
||||
<Properties>
|
||||
<Name><ИмяРеквизита></Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>10</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
<PasswordMode>false</PasswordMode>
|
||||
<Format/>
|
||||
<EditFormat/>
|
||||
<ToolTip/>
|
||||
<MarkNegatives>false</MarkNegatives>
|
||||
<Mask/>
|
||||
<MultiLine>false</MultiLine>
|
||||
<ExtendedEdit>false</ExtendedEdit>
|
||||
<MinValue xsi:nil="true"/>
|
||||
<MaxValue xsi:nil="true"/>
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
|
||||
<ChoiceParameterLinks/>
|
||||
<ChoiceParameters/>
|
||||
<QuickChoice>Auto</QuickChoice>
|
||||
<CreateOnInput>Auto</CreateOnInput>
|
||||
<ChoiceForm/>
|
||||
<LinkByType/>
|
||||
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
|
||||
</Properties>
|
||||
</Attribute>
|
||||
```
|
||||
|
||||
#### Свойства реквизита объекта (полный перечень)
|
||||
|
||||
Порядок фиксирован:
|
||||
|
||||
| Свойство | Тип | Описание | Значение по умолчанию |
|
||||
|----------|-----|----------|----------------------|
|
||||
| `Name` | string | Имя реквизита | — |
|
||||
| `Synonym` | LocalString | Синоним (отображаемое имя) | — |
|
||||
| `Comment` | string | Комментарий | пустой |
|
||||
| `Type` | TypeDescription | Тип данных | — |
|
||||
| `PasswordMode` | boolean | Режим пароля | `false` |
|
||||
| `Format` | string | Формат вывода | пустой |
|
||||
| `EditFormat` | string | Формат редактирования | пустой |
|
||||
| `ToolTip` | LocalString | Подсказка | пустой |
|
||||
| `MarkNegatives` | boolean | Выделять отрицательные | `false` |
|
||||
| `Mask` | string | Маска ввода | пустой |
|
||||
| `MultiLine` | boolean | Многострочный | `false` |
|
||||
| `ExtendedEdit` | boolean | Расширенное редактирование | `false` |
|
||||
| `MinValue` | any | Минимальное значение | `xsi:nil="true"` |
|
||||
| `MaxValue` | any | Максимальное значение | `xsi:nil="true"` |
|
||||
| `FillChecking` | enum | Проверка заполнения | `DontCheck` |
|
||||
| `ChoiceFoldersAndItems` | enum | Выбор групп и элементов | `Items` |
|
||||
| `ChoiceParameterLinks` | list | Связи параметров выбора | пустой |
|
||||
| `ChoiceParameters` | list | Параметры выбора | пустой |
|
||||
| `QuickChoice` | enum | Быстрый выбор | `Auto` |
|
||||
| `CreateOnInput` | enum | Создание при вводе | `Auto` |
|
||||
| `ChoiceForm` | string | Форма выбора | пустой |
|
||||
| `LinkByType` | ref | Связь по типу | пустой |
|
||||
| `ChoiceHistoryOnInput` | enum | История выбора при вводе | `Auto` |
|
||||
|
||||
#### Типы реквизитов
|
||||
|
||||
| v8:Type | Описание | Квалификаторы |
|
||||
|---------|----------|---------------|
|
||||
| `xs:string` | Строка | `v8:StringQualifiers`: `Length`, `AllowedLength` (Variable/Fixed) |
|
||||
| `xs:boolean` | Булево | — |
|
||||
| `xs:decimal` | Число | `v8:NumberQualifiers`: `Digits`, `FractionDigits`, `AllowedSign` (Any/Nonnegative) |
|
||||
| `xs:dateTime` | Дата | `v8:DateQualifiers`: `DateFractions` (Date/Time/DateTime) |
|
||||
| `cfg:CatalogRef.<Имя>` | Ссылка на справочник | — |
|
||||
| `cfg:DocumentRef.<Имя>` | Ссылка на документ | — |
|
||||
| `cfg:EnumRef.<Имя>` | Ссылка на перечисление | — |
|
||||
|
||||
> **Примечание**: Ссылочные типы (`cfg:CatalogRef.*` и т.д.) работают **только** при наличии в информационной базе конфигурации с соответствующими объектами.
|
||||
|
||||
### Табличные части объекта
|
||||
|
||||
Если обработка имеет табличные части, они описываются в `<ChildObjects>` корневого файла:
|
||||
@@ -185,14 +284,38 @@ xmlns="http://v8.1c.ru/8.3/xcf/logform"
|
||||
<Attribute uuid="<UUID>">
|
||||
<Properties>
|
||||
<Name><ИмяРеквизита></Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<Type>...</Type>
|
||||
<!-- Остальные свойства -->
|
||||
<PasswordMode>false</PasswordMode>
|
||||
<Format/>
|
||||
<EditFormat/>
|
||||
<ToolTip/>
|
||||
<MarkNegatives>false</MarkNegatives>
|
||||
<Mask/>
|
||||
<MultiLine>false</MultiLine>
|
||||
<ExtendedEdit>false</ExtendedEdit>
|
||||
<MinValue xsi:nil="true"/>
|
||||
<MaxValue xsi:nil="true"/>
|
||||
<FillFromFillingValue>false</FillFromFillingValue>
|
||||
<FillValue xsi:nil="true"/>
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
|
||||
<ChoiceParameterLinks/>
|
||||
<ChoiceParameters/>
|
||||
<QuickChoice>Auto</QuickChoice>
|
||||
<CreateOnInput>Auto</CreateOnInput>
|
||||
<ChoiceForm/>
|
||||
<LinkByType/>
|
||||
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
|
||||
</Properties>
|
||||
</Attribute>
|
||||
</ChildObjects>
|
||||
</TabularSection>
|
||||
```
|
||||
|
||||
> **Важно**: Реквизиты табличных частей имеют 2 дополнительных свойства по сравнению с реквизитами объекта: `FillFromFillingValue` и `FillValue`. Они вставляются между `MaxValue` и `FillChecking`.
|
||||
|
||||
## 4. Метаданные формы (`Forms/<Имя>.xml`)
|
||||
|
||||
```xml
|
||||
@@ -247,6 +370,7 @@ xmlns="http://v8.1c.ru/8.3/xcf/logform"
|
||||
| Значение `TemplateType` | Расширение файла тела | Описание |
|
||||
|---|---|---|
|
||||
| `SpreadsheetDocument` | `.xml` | Табличный документ (MXL в XML) |
|
||||
| `DataCompositionSchema` | `.xml` | Схема компоновки данных (СКД), см. [1c-dcs-spec.md](1c-dcs-spec.md) |
|
||||
| `HTMLDocument` | `.html` | HTML-документ |
|
||||
| `TextDocument` | `.txt` | Текстовый документ |
|
||||
| `BinaryData` | `.bin` | Двоичные данные |
|
||||
@@ -640,9 +764,11 @@ xmlns="http://v8.1c.ru/8.3/xcf/logform"
|
||||
|
||||
## 10. Чеклист для создания новой обработки
|
||||
|
||||
1. Сгенерировать UUID для каждого объекта (обработка, формы, макеты, ТЧ)
|
||||
1. Сгенерировать UUID для каждого объекта (обработка, реквизиты, формы, макеты, ТЧ)
|
||||
2. Создать структуру каталогов (раздел 1)
|
||||
3. Создать корневой XML (раздел 3) с правильными ChildObjects
|
||||
3. Создать корневой XML (раздел 3) с правильными ChildObjects:
|
||||
- Порядок: Attribute → TabularSection → Form → Template
|
||||
- GeneratedType для ТЧ: `DataProcessorTabularSection.<Имя>.<ТЧ>` (не `ExternalDataProcessorTabularSection`!)
|
||||
4. Для каждой формы:
|
||||
- Создать `<Имя>.xml` (раздел 4)
|
||||
- Создать `Form.xml` (раздел 6) — проверить пространство имён!
|
||||
@@ -0,0 +1,628 @@
|
||||
# Спецификация XML-формата выгрузки внешнего отчёта 1С
|
||||
|
||||
Формат: XML-выгрузка внешнего отчёта (ExternalReport) из конфигуратора 1С:Предприятие 8.3.
|
||||
Версия формата: `2.17`.
|
||||
|
||||
> **Связь с другими спецификациями**:
|
||||
> - Структура каталогов, пространства имён, формат форм и макетов — идентичны [спецификации внешней обработки (EPF)](1c-epf-spec.md).
|
||||
> - Формат СКД-макетов — см. [спецификацию СКД](1c-dcs-spec.md).
|
||||
> - Формат форм — см. [спецификацию форм](1c-form-spec.md).
|
||||
> - Формат MXL-макетов — см. [спецификацию табличного документа](mxl-dsl-spec.md).
|
||||
>
|
||||
> Данный документ описывает **только** отличия внешнего отчёта от внешней обработки.
|
||||
|
||||
## 1. Структура каталогов
|
||||
|
||||
```
|
||||
<ИмяОтчёта>.xml # Корневой файл метаданных
|
||||
<ИмяОтчёта>/
|
||||
Ext/
|
||||
ObjectModule.bsl # Модуль объекта (опционально)
|
||||
Help.xml # Метаданные справки (опционально)
|
||||
Help/
|
||||
ru.html # HTML-страница справки
|
||||
Forms/
|
||||
<ИмяФормы>.xml # Метаданные формы
|
||||
<ИмяФормы>/
|
||||
Ext/
|
||||
Form.xml # Описание формы
|
||||
Form/
|
||||
Module.bsl # Модуль формы
|
||||
Templates/
|
||||
<ИмяМакета>.xml # Метаданные макета
|
||||
<ИмяМакета>/
|
||||
Ext/
|
||||
Template.<расш> # Тело макета
|
||||
```
|
||||
|
||||
Структура полностью совпадает с EPF. Отчёт может содержать:
|
||||
- 0..N реквизитов объекта (описаны в корневом XML)
|
||||
- 0..N табличных частей (описаны в корневом XML)
|
||||
- 0..N форм (каталог `Forms/`)
|
||||
- 0..N макетов (каталог `Templates/`) — включая СКД и MXL-макеты печатных форм
|
||||
- 0..1 модуль объекта (`Ext/ObjectModule.bsl`)
|
||||
- 0..1 встроенная справка (`Ext/Help.xml` + `Ext/Help/<язык>.html`)
|
||||
|
||||
## 2. Корневой файл отчёта (`<Имя>.xml`)
|
||||
|
||||
### 2.1. Общая структура
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="..." version="2.17">
|
||||
<ExternalReport uuid="<UUID>">
|
||||
<InternalInfo>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>e41aff26-25cf-4bb6-b6c1-3f478a75f374</xr:ClassId>
|
||||
<xr:ObjectId><UUID></xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:GeneratedType name="ExternalReportObject.<Имя>" category="Object">
|
||||
<xr:TypeId><UUID></xr:TypeId>
|
||||
<xr:ValueId><UUID></xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name><Имя></Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<DefaultForm/>
|
||||
<AuxiliaryForm/>
|
||||
<MainDataCompositionSchema/>
|
||||
<DefaultSettingsForm/>
|
||||
<AuxiliarySettingsForm/>
|
||||
<DefaultVariantForm/>
|
||||
<VariantsStorage/>
|
||||
<SettingsStorage/>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<!-- Реквизиты объекта (опционально) -->
|
||||
<Attribute uuid="<UUID>">...</Attribute>
|
||||
<!-- Табличные части (опционально) -->
|
||||
<TabularSection uuid="<UUID>">...</TabularSection>
|
||||
<!-- Формы -->
|
||||
<Form><ИмяФормы></Form>
|
||||
<!-- Макеты -->
|
||||
<Template><ИмяМакета></Template>
|
||||
</ChildObjects>
|
||||
</ExternalReport>
|
||||
</MetaDataObject>
|
||||
```
|
||||
|
||||
### 2.2. Отличия от EPF
|
||||
|
||||
| Элемент | EPF | ERF |
|
||||
|---------|-----|-----|
|
||||
| Корневой элемент | `<ExternalDataProcessor>` | `<ExternalReport>` |
|
||||
| ClassId | `c3831ec8-d8d5-4f93-8a22-f9bfae07327f` | `e41aff26-25cf-4bb6-b6c1-3f478a75f374` |
|
||||
| GeneratedType (Object) | `ExternalDataProcessorObject.<Имя>` | `ExternalReportObject.<Имя>` |
|
||||
| GeneratedType (ТЧ) | `DataProcessorTabularSection.<Имя>.<ТЧ>` | `ReportTabularSection.<Имя>.<ТЧ>` |
|
||||
| GeneratedType (строка ТЧ) | `DataProcessorTabularSectionRow.<Имя>.<ТЧ>` | `ReportTabularSectionRow.<Имя>.<ТЧ>` |
|
||||
| Путь к форме | `ExternalDataProcessor.<Имя>.Form.<Форма>` | `ExternalReport.<Имя>.Form.<Форма>` |
|
||||
| Путь к макету | `ExternalDataProcessor.<Имя>.Template.<Макет>` | `ExternalReport.<Имя>.Template.<Макет>` |
|
||||
| Тип реквизита формы | `cfg:ExternalDataProcessorObject.<Имя>` | `cfg:ExternalReportObject.<Имя>` |
|
||||
|
||||
### 2.3. Свойства (Properties)
|
||||
|
||||
Свойства EPF (`Name`, `Synonym`, `Comment`, `DefaultForm`, `AuxiliaryForm`) сохраняются. Добавляются **6 свойств**, специфичных для отчёта:
|
||||
|
||||
| Свойство | Описание | Пример значения |
|
||||
|----------|----------|-----------------|
|
||||
| `MainDataCompositionSchema` | Основная СКД отчёта. Полный путь к макету-СКД | `ExternalReport.<Имя>.Template.ОсновнаяСхемаКомпоновкиДанных` |
|
||||
| `DefaultSettingsForm` | Форма настроек отчёта | `ExternalReport.<Имя>.Form.ФормаНастроек` |
|
||||
| `AuxiliarySettingsForm` | Дополнительная форма настроек | (обычно пустой) |
|
||||
| `DefaultVariantForm` | Форма вариантов отчёта | `ExternalReport.<Имя>.Form.ФормаВарианта` |
|
||||
| `VariantsStorage` | Хранилище вариантов отчёта | `SettingsStorage.ХранилищеВариантовОтчетов` |
|
||||
| `SettingsStorage` | Хранилище настроек | (обычно пустой) |
|
||||
|
||||
**Порядок свойств фиксирован**: Name → Synonym → Comment → DefaultForm → AuxiliaryForm → MainDataCompositionSchema → DefaultSettingsForm → AuxiliarySettingsForm → DefaultVariantForm → VariantsStorage → SettingsStorage.
|
||||
|
||||
Если значение отсутствует, элемент остаётся пустым (самозакрывающимся):
|
||||
```xml
|
||||
<DefaultForm/>
|
||||
<MainDataCompositionSchema>ExternalReport.МойОтчёт.Template.ОсновнаяСхемаКомпоновкиДанных</MainDataCompositionSchema>
|
||||
<VariantsStorage/>
|
||||
```
|
||||
|
||||
## 3. Реквизиты объекта (Attribute)
|
||||
|
||||
В отличие от EPF (где реквизиты не документированы), внешний отчёт часто имеет реквизиты объекта. Они размещаются в `<ChildObjects>` корневого файла **перед** `<TabularSection>`, `<Form>` и `<Template>`:
|
||||
|
||||
```xml
|
||||
<Attribute uuid="<UUID>">
|
||||
<Properties>
|
||||
<Name>Реквизит1</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>10</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
<PasswordMode>false</PasswordMode>
|
||||
<Format/>
|
||||
<EditFormat/>
|
||||
<ToolTip/>
|
||||
<MarkNegatives>false</MarkNegatives>
|
||||
<Mask/>
|
||||
<MultiLine>false</MultiLine>
|
||||
<ExtendedEdit>false</ExtendedEdit>
|
||||
<MinValue xsi:nil="true"/>
|
||||
<MaxValue xsi:nil="true"/>
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
|
||||
<ChoiceParameterLinks/>
|
||||
<ChoiceParameters/>
|
||||
<QuickChoice>Auto</QuickChoice>
|
||||
<CreateOnInput>Auto</CreateOnInput>
|
||||
<ChoiceForm/>
|
||||
<LinkByType/>
|
||||
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
|
||||
</Properties>
|
||||
</Attribute>
|
||||
```
|
||||
|
||||
### Типы реквизитов
|
||||
|
||||
Типы реквизитов объекта аналогичны типам реквизитов форм:
|
||||
|
||||
| v8:Type | Описание | Квалификаторы |
|
||||
|---------|----------|---------------|
|
||||
| `xs:string` | Строка | `v8:StringQualifiers`: `Length`, `AllowedLength` |
|
||||
| `xs:boolean` | Булево | — |
|
||||
| `xs:decimal` | Число | `v8:NumberQualifiers`: `Digits`, `FractionDigits`, `AllowedSign` |
|
||||
| `xs:dateTime` | Дата | `v8:DateQualifiers`: `DateFractions` |
|
||||
| `cfg:CatalogRef.<Имя>` | Ссылка на справочник | — |
|
||||
| `cfg:DocumentRef.<Имя>` | Ссылка на документ | — |
|
||||
| `cfg:EnumRef.<Имя>` | Ссылка на перечисление | — |
|
||||
|
||||
> **Примечание**: Ссылочные типы (`cfg:CatalogRef.*` и т.д.) в контексте внешнего отчёта работают **только** при наличии в информационной базе конфигурации с соответствующими объектами.
|
||||
|
||||
### Свойства реквизита объекта (полный перечень)
|
||||
|
||||
Порядок фиксирован:
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Name` | string | Имя реквизита |
|
||||
| `Synonym` | LocalString | Синоним (отображаемое имя) |
|
||||
| `Comment` | string | Комментарий |
|
||||
| `Type` | TypeDescription | Тип данных (см. таблицу типов выше) |
|
||||
| `PasswordMode` | boolean | Режим пароля (`false`) |
|
||||
| `Format` | string | Формат вывода |
|
||||
| `EditFormat` | string | Формат редактирования |
|
||||
| `ToolTip` | LocalString | Подсказка |
|
||||
| `MarkNegatives` | boolean | Выделять отрицательные (`false`) |
|
||||
| `Mask` | string | Маска ввода |
|
||||
| `MultiLine` | boolean | Многострочный (`false`) |
|
||||
| `ExtendedEdit` | boolean | Расширенное редактирование (`false`) |
|
||||
| `MinValue` | any | Минимальное значение (`xsi:nil="true"`) |
|
||||
| `MaxValue` | any | Максимальное значение (`xsi:nil="true"`) |
|
||||
| `FillChecking` | enum | Проверка заполнения (`DontCheck`) |
|
||||
| `ChoiceFoldersAndItems` | enum | Выбор групп и элементов (`Items`) |
|
||||
| `ChoiceParameterLinks` | list | Связи параметров выбора |
|
||||
| `ChoiceParameters` | list | Параметры выбора |
|
||||
| `QuickChoice` | enum | Быстрый выбор (`Auto`) |
|
||||
| `CreateOnInput` | enum | Создание при вводе (`Auto`) |
|
||||
| `ChoiceForm` | string | Форма выбора |
|
||||
| `LinkByType` | ref | Связь по типу |
|
||||
| `ChoiceHistoryOnInput` | enum | История выбора при вводе (`Auto`) |
|
||||
|
||||
## 4. Табличные части (TabularSection)
|
||||
|
||||
Структура полностью аналогична EPF, отличаются только имена GeneratedType:
|
||||
|
||||
```xml
|
||||
<TabularSection uuid="<UUID>">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="ReportTabularSection.<ИмяОтчёта>.<ИмяТЧ>" category="TabularSection">
|
||||
<xr:TypeId><UUID></xr:TypeId>
|
||||
<xr:ValueId><UUID></xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
<xr:GeneratedType name="ReportTabularSectionRow.<ИмяОтчёта>.<ИмяТЧ>" category="TabularSectionRow">
|
||||
<xr:TypeId><UUID></xr:TypeId>
|
||||
<xr:ValueId><UUID></xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name><ИмяТЧ></Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<ToolTip/>
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Attribute uuid="<UUID>">
|
||||
<Properties>
|
||||
<!-- Те же свойства, что у реквизитов объекта, -->
|
||||
<!-- плюс два дополнительных: -->
|
||||
<FillFromFillingValue>false</FillFromFillingValue>
|
||||
<FillValue xsi:nil="true"/>
|
||||
<!-- ... остальные совпадают -->
|
||||
</Properties>
|
||||
</Attribute>
|
||||
</ChildObjects>
|
||||
</TabularSection>
|
||||
```
|
||||
|
||||
**Важно**: Реквизиты табличной части имеют 2 дополнительных свойства по сравнению с реквизитами объекта:
|
||||
- `FillFromFillingValue` — заполнять из значения заполнения (`false`)
|
||||
- `FillValue` — значение заполнения (`xsi:nil="true"` или `xsi:type="xs:string"` и т.д.)
|
||||
|
||||
Эти свойства вставляются между `MaxValue` и `FillChecking`.
|
||||
|
||||
## 5. Порядок элементов в ChildObjects
|
||||
|
||||
Порядок дочерних объектов **фиксирован**:
|
||||
|
||||
1. `<Attribute>` — реквизиты объекта (0..N)
|
||||
2. `<TabularSection>` — табличные части (0..N)
|
||||
3. `<Form>` — формы (0..N)
|
||||
4. `<Template>` — макеты (0..N)
|
||||
|
||||
## 6. Формы отчёта
|
||||
|
||||
### 6.1. Метаданные формы (`Forms/<Имя>.xml`)
|
||||
|
||||
Формат метаданных формы полностью совпадает с EPF — см. [спецификацию EPF, раздел 4](1c-epf-spec.md).
|
||||
|
||||
### 6.2. Специфика Form.xml для отчётов
|
||||
|
||||
Формы отчёта имеют дополнительные свойства в `<Form>`, которых нет у форм обработки:
|
||||
|
||||
| Свойство | Описание | Допустимые значения |
|
||||
|----------|----------|---------------------|
|
||||
| `ReportFormType` | Тип формы отчёта | `Main`, `Settings`, `Variant` |
|
||||
| `ReportResult` | Имя реквизита-результата | Имя реквизита типа SpreadsheetDocument |
|
||||
| `DetailsData` | Имя реквизита данных расшифровки | Имя строкового реквизита |
|
||||
| `CustomSettingsFolder` | Группа пользовательских настроек | Имя элемента UsualGroup на форме |
|
||||
|
||||
Эти свойства размещаются в начале `<Form>`, после `<CommandBarLocation>` и до `<AutoCommandBar>`.
|
||||
|
||||
### 6.3. Основная форма отчёта (ReportFormType = Main)
|
||||
|
||||
Форма для отображения результата отчёта.
|
||||
|
||||
```xml
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" ... version="2.17">
|
||||
<CommandBarLocation>None</CommandBarLocation>
|
||||
<ReportResult>Результат</ReportResult>
|
||||
<DetailsData>ДанныеРасшифровки</DetailsData>
|
||||
<ReportFormType>Main</ReportFormType>
|
||||
<CustomSettingsFolder>КомпоновщикНастроекПользовательскиеНастройки</CustomSettingsFolder>
|
||||
<AutoCommandBar name="" id="-1">
|
||||
<Autofill>false</Autofill>
|
||||
</AutoCommandBar>
|
||||
<ChildItems>
|
||||
<CommandBar name="ОсновнаяКоманднаяПанель" id="1">
|
||||
<Title>...</Title>
|
||||
<CommandSource>Form</CommandSource>
|
||||
<ExtendedTooltip name="ОсновнаяКоманднаяПанельРасширеннаяПодсказка" id="2"/>
|
||||
</CommandBar>
|
||||
<UsualGroup name="КомпоновщикНастроекПользовательскиеНастройки" id="3">
|
||||
<Title>...</Title>
|
||||
<VerticalStretch>false</VerticalStretch>
|
||||
<Group>Vertical</Group>
|
||||
<ShowTitle>false</ShowTitle>
|
||||
<ExtendedTooltip name="КомпоновщикНастроекПользовательскиеНастройкиРасширеннаяПодсказка" id="4"/>
|
||||
</UsualGroup>
|
||||
<SpreadSheetDocumentField name="Результат" id="5">
|
||||
<DataPath>Результат</DataPath>
|
||||
<DefaultItem>true</DefaultItem>
|
||||
<TitleLocation>None</TitleLocation>
|
||||
<Width>100</Width>
|
||||
<ContextMenu name="РезультатКонтекстноеМеню" id="6"/>
|
||||
<ExtendedTooltip name="РезультатРасширеннаяПодсказка" id="7"/>
|
||||
</SpreadSheetDocumentField>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Отчет" id="1">
|
||||
<Type>
|
||||
<v8:Type>cfg:ExternalReportObject.<ИмяОтчёта></v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
<Attribute name="Результат" id="2">
|
||||
<Title>...</Title>
|
||||
<Type>
|
||||
<v8:Type xmlns:mxl="http://v8.1c.ru/8.2/data/spreadsheet">mxl:SpreadsheetDocument</v8:Type>
|
||||
</Type>
|
||||
</Attribute>
|
||||
<Attribute name="ДанныеРасшифровки" id="3">
|
||||
<Type>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>0</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</Type>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Form>
|
||||
```
|
||||
|
||||
**Ключевые элементы основной формы отчёта:**
|
||||
|
||||
| Элемент | Описание |
|
||||
|---------|----------|
|
||||
| `SpreadSheetDocumentField` | Поле табличного документа для вывода результата. Привязано к реквизиту типа `mxl:SpreadsheetDocument` |
|
||||
| Реквизит `Отчет` | Основной реквизит (`MainAttribute=true`), тип `cfg:ExternalReportObject.<Имя>` |
|
||||
| Реквизит `Результат` | Табличный документ. Тип `mxl:SpreadsheetDocument` (требует дополнительный namespace `xmlns:mxl`) |
|
||||
| Реквизит `ДанныеРасшифровки` | Строка неограниченной длины для данных расшифровки |
|
||||
| Группа `КомпоновщикНастроекПользовательскиеНастройки` | Контейнер для пользовательских настроек СКД |
|
||||
|
||||
### 6.4. Форма настроек (ReportFormType = Settings)
|
||||
|
||||
Форма настроек отчёта для пользователя.
|
||||
|
||||
```xml
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" ... version="2.17">
|
||||
<CommandBarLocation>Bottom</CommandBarLocation>
|
||||
<VerticalScroll>useIfNecessary</VerticalScroll>
|
||||
<ReportFormType>Settings</ReportFormType>
|
||||
<CustomSettingsFolder>КомпоновщикНастроекПользовательскиеНастройки</CustomSettingsFolder>
|
||||
<AutoCommandBar name="" id="-1">
|
||||
<HorizontalAlign>Right</HorizontalAlign>
|
||||
</AutoCommandBar>
|
||||
<ChildItems>
|
||||
<UsualGroup name="КомпоновщикНастроекПользовательскиеНастройки" id="1">
|
||||
<Title>...</Title>
|
||||
<Group>Vertical</Group>
|
||||
<Representation>None</Representation>
|
||||
<ShowTitle>false</ShowTitle>
|
||||
<ExtendedTooltip name="КомпоновщикНастроекПользовательскиеНастройкиРасширеннаяПодсказка" id="2"/>
|
||||
</UsualGroup>
|
||||
</ChildItems>
|
||||
<Attributes>
|
||||
<Attribute name="Отчет" id="1">
|
||||
<Type>
|
||||
<v8:Type>cfg:ExternalReportObject.<ИмяОтчёта></v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Form>
|
||||
```
|
||||
|
||||
### 6.5. Форма варианта (ReportFormType = Variant)
|
||||
|
||||
Форма для настройки варианта отчёта (структуры, группировок, фильтров).
|
||||
|
||||
```xml
|
||||
<Form xmlns="http://v8.1c.ru/8.3/xcf/logform" ... version="2.17">
|
||||
<CommandBarLocation>Bottom</CommandBarLocation>
|
||||
<CollapseItemsByImportanceVariant>DontUse</CollapseItemsByImportanceVariant>
|
||||
<ReportFormType>Variant</ReportFormType>
|
||||
<AutoCommandBar name="" id="-1">
|
||||
<HorizontalAlign>Right</HorizontalAlign>
|
||||
</AutoCommandBar>
|
||||
<ChildItems>
|
||||
<Table name="КомпоновщикНастроекНастройки" id="1">
|
||||
<Representation>Tree</Representation>
|
||||
<DataPath>Отчет.SettingsComposer.Settings</DataPath>
|
||||
<!-- ... элементы управления деревом настроек -->
|
||||
</Table>
|
||||
<!-- Страницы настроек (параметры, поля, фильтры и т.д.) -->
|
||||
</ChildItems>
|
||||
<!-- ... -->
|
||||
</Form>
|
||||
```
|
||||
|
||||
Форма варианта обычно содержит сложную структуру с деревом настроек (`Table` с `Representation=Tree` и `DataPath=Отчет.SettingsComposer.Settings`) и множеством страниц для редактирования параметров, полей, фильтров, сортировки и условного оформления.
|
||||
|
||||
### 6.6. Элемент SpreadSheetDocumentField
|
||||
|
||||
Специфичный для отчётов элемент формы — поле табличного документа. Используется для отображения результата отчёта.
|
||||
|
||||
```xml
|
||||
<SpreadSheetDocumentField name="Результат" id="5">
|
||||
<DataPath>Результат</DataPath>
|
||||
<DefaultItem>true</DefaultItem>
|
||||
<TitleLocation>None</TitleLocation>
|
||||
<Width>100</Width>
|
||||
<ContextMenu name="РезультатКонтекстноеМеню" id="6"/>
|
||||
<ExtendedTooltip name="РезультатРасширеннаяПодсказка" id="7"/>
|
||||
</SpreadSheetDocumentField>
|
||||
```
|
||||
|
||||
| Свойство | Описание |
|
||||
|----------|----------|
|
||||
| `DataPath` | Путь к реквизиту типа SpreadsheetDocument |
|
||||
| `DefaultItem` | Элемент по умолчанию (`true`) |
|
||||
| `TitleLocation` | Расположение заголовка (`None`) |
|
||||
| `Width` | Ширина в символах |
|
||||
|
||||
## 7. Модуль объекта
|
||||
|
||||
### 7.1. Событие ПриКомпоновкеРезультата
|
||||
|
||||
Основное событие модуля объекта отчёта. Вызывается платформой при формировании результата. Позволяет перехватить стандартную обработку СКД.
|
||||
|
||||
```bsl
|
||||
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
|
||||
// СтандартнаяОбработка = Ложь; // отключить стандартное формирование по СКД
|
||||
// Собственная логика формирования отчёта
|
||||
КонецПроцедуры
|
||||
```
|
||||
|
||||
Параметры:
|
||||
- `ДокументРезультат` — табличный документ для вывода результата
|
||||
- `ДанныеРасшифровки` — данные расшифровки
|
||||
- `СтандартнаяОбработка` — если установить `Ложь`, платформа не будет сама формировать отчёт по СКД
|
||||
|
||||
### 7.2. Директива условной компиляции
|
||||
|
||||
В ERP-отчётах модуль объекта часто обёрнут в директиву условной компиляции:
|
||||
|
||||
```bsl
|
||||
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
|
||||
|
||||
// ... весь код модуля ...
|
||||
|
||||
#КонецЕсли
|
||||
```
|
||||
|
||||
### 7.3. Типичные процедуры модуля объекта отчёта
|
||||
|
||||
| Процедура | Описание |
|
||||
|-----------|----------|
|
||||
| `ПриКомпоновкеРезультата` | Событие формирования результата |
|
||||
| `ИнициализироватьОтчет` | Инициализация отчёта (экспорт, для БСП) |
|
||||
| `ОпределитьНастройкиФормы` | Определение настроек формы (экспорт, для БСП) |
|
||||
| `ПередЗагрузкойНастроекВКомпоновщик` | Предобработка настроек (экспорт, для БСП) |
|
||||
|
||||
## 8. Макеты (Templates)
|
||||
|
||||
### 8.1. СКД-макет (DataCompositionSchema)
|
||||
|
||||
Основной макет отчёта. Обязателен, если указан `MainDataCompositionSchema`.
|
||||
|
||||
```xml
|
||||
<!-- Метаданные: Templates/ОсновнаяСхемаКомпоновкиДанных.xml -->
|
||||
<Template uuid="<UUID>">
|
||||
<Properties>
|
||||
<Name>ОсновнаяСхемаКомпоновкиДанных</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<TemplateType>DataCompositionSchema</TemplateType>
|
||||
</Properties>
|
||||
</Template>
|
||||
```
|
||||
|
||||
Тело СКД: `Templates/ОсновнаяСхемаКомпоновкиДанных/Ext/Template.xml` — формат описан в [спецификации СКД](1c-dcs-spec.md).
|
||||
|
||||
### 8.2. MXL-макеты печатных форм (SpreadsheetDocument)
|
||||
|
||||
Отчёты часто содержат MXL-макеты для вывода печатных форм.
|
||||
|
||||
```xml
|
||||
<!-- Метаданные: Templates/ПФ_MXL_КарточкаУчета.xml -->
|
||||
<Template uuid="<UUID>">
|
||||
<Properties>
|
||||
<Name>ПФ_MXL_КарточкаУчета</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<TemplateType>SpreadsheetDocument</TemplateType>
|
||||
</Properties>
|
||||
</Template>
|
||||
```
|
||||
|
||||
Тело макета: `Templates/ПФ_MXL_КарточкаУчета/Ext/Template.xml` — формат MXL.
|
||||
|
||||
Конвенция именования MXL-макетов: `ПФ_MXL_<НазваниеПечатнойФормы>`.
|
||||
|
||||
## 9. Сравнение с отчётом конфигурации (Report)
|
||||
|
||||
| Аспект | ExternalReport (ERF) | Report (в конфигурации) |
|
||||
|--------|---------------------|------------------------|
|
||||
| Корневой элемент | `<ExternalReport>` | `<Report>` |
|
||||
| ClassId | `e41aff26-25cf-4bb6-b6c1-3f478a75f374` | (нет ContainedObject) |
|
||||
| GeneratedType (Object) | `ExternalReportObject.<Имя>` | `ReportObject.<Имя>` |
|
||||
| GeneratedType (Manager) | — | `ReportManager.<Имя>` |
|
||||
| Тип реквизита формы | `cfg:ExternalReportObject.<Имя>` | `cfg:ReportObject.<Имя>` |
|
||||
| Дополнительные свойства | — | `UseStandardCommands`, `IncludeHelpInContents`, `ExtendedPresentation`, `Explanation` |
|
||||
| Формы по умолчанию | Могут ссылаться на свои формы | Могут ссылаться на `CommonForm.*` |
|
||||
| Хранение | Файл `.erf` | В составе конфигурации |
|
||||
|
||||
## 10. Минимальный пример
|
||||
|
||||
### Пустой отчёт (без СКД, форм, модулей)
|
||||
|
||||
```xml
|
||||
<?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">
|
||||
<ExternalReport uuid="b38bc179-9b8a-4eb3-9422-96c6eded1ac3">
|
||||
<InternalInfo>
|
||||
<xr:ContainedObject>
|
||||
<xr:ClassId>e41aff26-25cf-4bb6-b6c1-3f478a75f374</xr:ClassId>
|
||||
<xr:ObjectId>38f084a4-47ce-4e67-ab4b-ac6323b9da08</xr:ObjectId>
|
||||
</xr:ContainedObject>
|
||||
<xr:GeneratedType name="ExternalReportObject.МойОтчёт" category="Object">
|
||||
<xr:TypeId>1fd37c7e-ade2-47ac-8dae-3fafeec96943</xr:TypeId>
|
||||
<xr:ValueId>b85e1756-f044-4907-b4bd-75a57649c486</xr:ValueId>
|
||||
</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>МойОтчёт</Name>
|
||||
<Synonym>
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Мой отчёт</v8:content>
|
||||
</v8:item>
|
||||
</Synonym>
|
||||
<Comment/>
|
||||
<DefaultForm/>
|
||||
<AuxiliaryForm/>
|
||||
<MainDataCompositionSchema/>
|
||||
<DefaultSettingsForm/>
|
||||
<AuxiliarySettingsForm/>
|
||||
<DefaultVariantForm/>
|
||||
<VariantsStorage/>
|
||||
<SettingsStorage/>
|
||||
</Properties>
|
||||
<ChildObjects/>
|
||||
</ExternalReport>
|
||||
</MetaDataObject>
|
||||
```
|
||||
|
||||
### Отчёт с СКД
|
||||
|
||||
Добавляется ссылка на СКД в `MainDataCompositionSchema` и макет-СКД в `ChildObjects`:
|
||||
|
||||
```xml
|
||||
<Properties>
|
||||
...
|
||||
<MainDataCompositionSchema>ExternalReport.МойОтчёт.Template.ОсновнаяСхемаКомпоновкиДанных</MainDataCompositionSchema>
|
||||
...
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Template>ОсновнаяСхемаКомпоновкиДанных</Template>
|
||||
</ChildObjects>
|
||||
```
|
||||
|
||||
Плюс файлы:
|
||||
- `Templates/ОсновнаяСхемаКомпоновкиДанных.xml` (метаданные, `TemplateType=DataCompositionSchema`)
|
||||
- `Templates/ОсновнаяСхемаКомпоновкиДанных/Ext/Template.xml` (тело СКД)
|
||||
|
||||
## 11. Чеклист для создания внешнего отчёта
|
||||
|
||||
1. Сгенерировать UUID для каждого объекта (отчёт, реквизиты, ТЧ, формы, макеты)
|
||||
2. Создать структуру каталогов (раздел 1)
|
||||
3. Создать корневой XML (раздел 2) с:
|
||||
- `ClassId = e41aff26-25cf-4bb6-b6c1-3f478a75f374`
|
||||
- `GeneratedType name="ExternalReportObject.<Имя>"`
|
||||
- Корректными путями в `MainDataCompositionSchema`, `DefaultForm` и др.
|
||||
- Правильным порядком элементов в `ChildObjects`
|
||||
4. Создать СКД-макет (раздел 8.1):
|
||||
- Метаданные с `TemplateType=DataCompositionSchema`
|
||||
- Тело СКД по [спецификации СКД](1c-dcs-spec.md)
|
||||
5. При необходимости создать формы (раздел 6):
|
||||
- Указать `ReportFormType` (`Main` / `Settings` / `Variant`)
|
||||
- Основная форма: `ReportResult`, `DetailsData`, `SpreadSheetDocumentField`
|
||||
- Основной реквизит: `cfg:ExternalReportObject.<Имя>`
|
||||
6. При необходимости создать `ObjectModule.bsl` (раздел 7)
|
||||
7. При необходимости создать MXL-макеты печатных форм (раздел 8.2)
|
||||
8. Проверить:
|
||||
- Все пути используют префикс `ExternalReport.<Имя>` (не `ExternalDataProcessor`)
|
||||
- Тип основного реквизита формы: `cfg:ExternalReportObject.<Имя>`
|
||||
- `MainDataCompositionSchema` соответствует реальному макету в `ChildObjects`
|
||||
- Порядок в `ChildObjects`: Attribute → TabularSection → Form → Template
|
||||
- Все UUID уникальны
|
||||
@@ -0,0 +1,960 @@
|
||||
# Спецификация формата выгрузки расширений конфигурации 1С (CFE)
|
||||
|
||||
Формат: XML-выгрузка расширения конфигурации 1С:Предприятие 8.3 (Конфигуратор → Конфигурация → Расширения → Выгрузить расширение в файлы).
|
||||
Версия формата: `2.17` (платформа 8.3.17–8.3.24).
|
||||
|
||||
> **Связанные спецификации:**
|
||||
> - Корневая структура конфигурации — [1c-configuration-spec.md](1c-configuration-spec.md)
|
||||
> - Объекты метаданных — [1c-config-objects-spec.md](1c-config-objects-spec.md)
|
||||
> - Подсистемы — [1c-subsystem-spec.md](1c-subsystem-spec.md)
|
||||
> - Управляемые формы — [1c-form-spec.md](1c-form-spec.md)
|
||||
> - Роли — [1c-role-spec.md](1c-role-spec.md)
|
||||
> - Сводный индекс — [1c-specs-index.md](1c-specs-index.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Общая структура выгрузки расширения
|
||||
|
||||
```
|
||||
Configuration.xml # Корневой файл — свойства и состав расширения
|
||||
ConfigDumpInfo.xml # Служебный файл — версии объектов
|
||||
Languages/ # Языки (всегда заимствованные)
|
||||
Roles/ # Роли (собственные)
|
||||
Subsystems/ # Подсистемы (собственные или заимствованные)
|
||||
CommonModules/ # Общие модули
|
||||
CommonPictures/ # Общие картинки
|
||||
CommonCommands/ # Общие команды
|
||||
Catalogs/ # Справочники
|
||||
Documents/ # Документы
|
||||
Enums/ # Перечисления
|
||||
... # Другие типы объектов
|
||||
```
|
||||
|
||||
### Ключевые отличия от конфигурации
|
||||
|
||||
| Аспект | Конфигурация | Расширение |
|
||||
|--------|-------------|------------|
|
||||
| Корневой `Ext/` | Есть (модули, интерфейс, справка) | **Нет** |
|
||||
| `ObjectBelonging` в Properties | Нет | `Adopted` (всегда) |
|
||||
| `ConfigurationExtensionPurpose` | Нет | `Patch` / `Customization` / `AddOn` |
|
||||
| `KeepMappingToExtendedConfigurationObjectsByIDs` | Нет | `true` / `false` |
|
||||
| `NamePrefix` | Пустой или нет | Префикс для собственных объектов |
|
||||
| `CompatibilityMode` | Да | Нет (используется `ConfigurationExtensionCompatibilityMode`) |
|
||||
| Свойства режимов работы | Полный набор | Сокращённый набор |
|
||||
| Объекты в ChildObjects | Только собственные | Собственные **и заимствованные** |
|
||||
|
||||
---
|
||||
|
||||
## 2. Configuration.xml — корневой файл расширения
|
||||
|
||||
### 2.1. Общая структура
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
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"
|
||||
xmlns:app="http://v8.1c.ru/8.2/managed-application/core"
|
||||
... version="2.17">
|
||||
<Configuration uuid="...">
|
||||
<InternalInfo>...</InternalInfo>
|
||||
<Properties>...</Properties>
|
||||
<ChildObjects>...</ChildObjects>
|
||||
</Configuration>
|
||||
</MetaDataObject>
|
||||
```
|
||||
|
||||
Пространства имён и корневой элемент идентичны конфигурации. Атрибут `version` соответствует версии формата выгрузки.
|
||||
|
||||
### 2.2. InternalInfo
|
||||
|
||||
Содержит 7 записей `xr:ContainedObject` — аналогично конфигурации. ClassId фиксированные, ObjectId уникальны для каждого расширения.
|
||||
|
||||
### 2.3. Properties — свойства расширения
|
||||
|
||||
Свойства идут в фиксированном порядке. Набор свойств **отличается** от конфигурации — часть свойств специфична для расширений, часть свойств конфигурации отсутствует.
|
||||
|
||||
#### Специфичные свойства расширения
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `ObjectBelonging` | enum | Всегда `Adopted` — расширение «принято» к основной конфигурации |
|
||||
| `ConfigurationExtensionPurpose` | enum | Назначение расширения: `Patch` (исправление), `Customization` (адаптация), `AddOn` (дополнение) |
|
||||
| `KeepMappingToExtendedConfigurationObjectsByIDs` | `xs:boolean` | Сохранять привязку к объектам по идентификаторам |
|
||||
| `NamePrefix` | `xs:string` | Префикс имён собственных объектов (напр. `Расш1_`, `МоёРасш_`) |
|
||||
| `ConfigurationExtensionCompatibilityMode` | enum | Режим совместимости расширения (`Version8_3_17`, `Version8_3_24`, ...) |
|
||||
|
||||
#### Общие свойства (совпадают с конфигурацией)
|
||||
|
||||
| Свойство | Тип | Описание |
|
||||
|----------|-----|----------|
|
||||
| `Name` | `xs:string` | Имя расширения (идентификатор) |
|
||||
| `Synonym` | `LocalString` | Отображаемое имя |
|
||||
| `Comment` | `xs:string` | Комментарий |
|
||||
| `DefaultRunMode` | enum | Режим запуска (`ManagedApplication`) |
|
||||
| `UsePurposes` | list | Назначения (`PlatformApplication`) |
|
||||
| `ScriptVariant` | enum | Язык скриптов (`Russian` / `English`) |
|
||||
| `DefaultRoles` | list | Роли по умолчанию |
|
||||
| `Vendor` | `xs:string` | Поставщик |
|
||||
| `Version` | `xs:string` | Версия расширения |
|
||||
| `DefaultLanguage` | ref | Язык по умолчанию (`Language.Русский`) |
|
||||
| `BriefInformation` | `LocalString` | Краткая информация |
|
||||
| `DetailedInformation` | `LocalString` | Подробная информация |
|
||||
| `Copyright` | `LocalString` | Авторские права |
|
||||
| `VendorInformationAddress` | `LocalString` | Адрес поставщика |
|
||||
| `ConfigurationInformationAddress` | `LocalString` | Адрес информации |
|
||||
| `InterfaceCompatibilityMode` | enum | Совместимость интерфейса |
|
||||
|
||||
> **Примечание:** Свойства `DefaultRunMode`, `UsePurposes`, `DefaultRoles`, `DefaultLanguage`, `InterfaceCompatibilityMode` **опциональны** — могут отсутствовать в расширении (в отличие от конфигурации, где они обязательны).
|
||||
|
||||
#### Свойства конфигурации, отсутствующие в расширении
|
||||
|
||||
В расширениях **нет** следующих свойств:
|
||||
- `CompatibilityMode` (заменено на `ConfigurationExtensionCompatibilityMode`)
|
||||
- `DataLockControlMode`
|
||||
- `ObjectAutonumerationMode`
|
||||
- `ModalityUseMode`
|
||||
- `SynchronousPlatformExtensionAndAddInCallUseMode`
|
||||
- `DatabaseTablespacesUseMode`
|
||||
- `MainClientApplicationWindowMode`
|
||||
- `UpdateCatalogAddress`
|
||||
- `IncludeHelpInContents`
|
||||
- `UseManagedFormInOrdinaryApplication`
|
||||
- `UseOrdinaryFormInManagedApplication`
|
||||
- `Content`
|
||||
- `StandaloneConfigurationRestrictionRoles`
|
||||
|
||||
### 2.4. Порядок свойств
|
||||
|
||||
```xml
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяРасширения</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<ConfigurationExtensionPurpose>Patch</ConfigurationExtensionPurpose>
|
||||
<KeepMappingToExtendedConfigurationObjectsByIDs>true</KeepMappingToExtendedConfigurationObjectsByIDs>
|
||||
<NamePrefix>Расш1_</NamePrefix>
|
||||
<ConfigurationExtensionCompatibilityMode>Version8_3_17</ConfigurationExtensionCompatibilityMode>
|
||||
<DefaultRunMode>ManagedApplication</DefaultRunMode> <!-- опционально -->
|
||||
<UsePurposes>...</UsePurposes> <!-- опционально -->
|
||||
<ScriptVariant>Russian</ScriptVariant>
|
||||
<DefaultRoles>...</DefaultRoles> <!-- опционально -->
|
||||
<Vendor/>
|
||||
<Version/>
|
||||
<DefaultLanguage>Language.Русский</DefaultLanguage> <!-- опционально -->
|
||||
<BriefInformation/>
|
||||
<DetailedInformation/>
|
||||
<Copyright/>
|
||||
<VendorInformationAddress/>
|
||||
<ConfigurationInformationAddress/>
|
||||
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode> <!-- опционально -->
|
||||
</Properties>
|
||||
```
|
||||
|
||||
### 2.5. ChildObjects — состав расширения
|
||||
|
||||
Содержит как **собственные** объекты расширения, так и **заимствованные** из основной конфигурации. Порядок типов аналогичен конфигурации.
|
||||
|
||||
```xml
|
||||
<ChildObjects>
|
||||
<Language>Русский</Language> <!-- заимствованный -->
|
||||
<Subsystem>Расш1_МояПодсистема</Subsystem> <!-- собственный -->
|
||||
<CommonPicture>Расш1_МояКартинка</CommonPicture> <!-- собственный -->
|
||||
<Role>Расш1_ОсновнаяРоль</Role> <!-- собственный -->
|
||||
<CommonModule>Расш1_МодульСервер</CommonModule> <!-- собственный -->
|
||||
<CommonModule>ОбщийМодульКонфигурации</CommonModule> <!-- заимствованный -->
|
||||
<Catalog>Контрагенты</Catalog> <!-- заимствованный -->
|
||||
<Catalog>Расш1_Проекты</Catalog> <!-- собственный -->
|
||||
<Enum>Расш1_ВидыДокументов</Enum> <!-- собственный -->
|
||||
<InformationRegister>Расш1_ДатыРабот</InformationRegister> <!-- собственный -->
|
||||
</ChildObjects>
|
||||
```
|
||||
|
||||
В `ChildObjects` не видно различие между собственными и заимствованными — оно определяется по содержимому XML-файла объекта (свойство `ObjectBelonging` и наличие `ExtendedConfigurationObject`).
|
||||
|
||||
**Правило именования:** собственные объекты расширения обычно имеют `NamePrefix` в начале имени (напр. `Расш1_Справочник1`, `Расш1_Проекты`), заимствованные — имя объекта из основной конфигурации без префикса (напр. `Контрагенты`, `Валюты`).
|
||||
|
||||
---
|
||||
|
||||
## 3. ConfigDumpInfo.xml
|
||||
|
||||
Формат идентичен конфигурации:
|
||||
|
||||
```xml
|
||||
<?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>
|
||||
<Metadata name="Configuration.ИмяРасширения" id="uuid" configVersion="sha1"/>
|
||||
<Metadata name="Language.Русский" id="uuid" configVersion="sha1"/>
|
||||
<Metadata name="Role.Расш1_ОсновнаяРоль" id="uuid" configVersion="sha1"/>
|
||||
<!-- ... все объекты расширения ... -->
|
||||
</ConfigVersions>
|
||||
</ConfigDumpInfo>
|
||||
```
|
||||
|
||||
Включает записи для **всех** объектов расширения (и собственных, и заимствованных). Атрибут `configVersion` — 40-символьный SHA1-хеш версии объекта.
|
||||
|
||||
---
|
||||
|
||||
## 4. Заимствованные и собственные объекты
|
||||
|
||||
Расширение может содержать два типа объектов:
|
||||
|
||||
### 4.1. Заимствованные объекты (Adopted)
|
||||
|
||||
Объекты, существующие в основной конфигурации, которые расширение модифицирует или дополняет.
|
||||
|
||||
**Признаки:**
|
||||
- `<ObjectBelonging>Adopted</ObjectBelonging>` в Properties
|
||||
- `<ExtendedConfigurationObject>uuid</ExtendedConfigurationObject>` — UUID объекта в основной конфигурации
|
||||
- Минимальный набор свойств (только те, что изменяются)
|
||||
|
||||
```xml
|
||||
<Catalog uuid="81de7e56-...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="CatalogObject.Валюты" category="Object">...</xr:GeneratedType>
|
||||
<!-- ... стандартные GeneratedType ... -->
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Валюты</Name>
|
||||
<Comment/>
|
||||
<ExtendedConfigurationObject>7aadbb67-...</ExtendedConfigurationObject>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<!-- заимствованные и собственные реквизиты, формы -->
|
||||
</ChildObjects>
|
||||
</Catalog>
|
||||
```
|
||||
|
||||
Заимствованные объекты **не содержат** полного набора свойств — только `ObjectBelonging`, `Name`, `Comment`, `ExtendedConfigurationObject` и те свойства, которые расширение изменяет (напр. `CodeLength`, `DefaultListForm`).
|
||||
|
||||
### 4.2. Собственные объекты (Own)
|
||||
|
||||
Объекты, созданные непосредственно в расширении.
|
||||
|
||||
**Признаки:**
|
||||
- **Нет** элемента `ObjectBelonging`
|
||||
- **Нет** элемента `ExtendedConfigurationObject`
|
||||
- Полный набор свойств (как в объектах конфигурации)
|
||||
- Имя обычно начинается с `NamePrefix` расширения
|
||||
|
||||
```xml
|
||||
<Catalog uuid="7dcd4d14-...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="CatalogObject.Расш5_Справочник1" category="Object">...</xr:GeneratedType>
|
||||
<!-- ... -->
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<Name>Расш5_Справочник1</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<Hierarchical>false</Hierarchical>
|
||||
<CodeLength>9</CodeLength>
|
||||
<!-- ... полный набор свойств как в конфигурации ... -->
|
||||
</Properties>
|
||||
<ChildObjects/>
|
||||
</Catalog>
|
||||
```
|
||||
|
||||
Формат полностью совпадает с форматом объектов конфигурации (см. [1c-config-objects-spec.md](1c-config-objects-spec.md)).
|
||||
|
||||
---
|
||||
|
||||
## 5. Заимствованные дочерние элементы
|
||||
|
||||
Дочерние элементы заимствованных объектов (реквизиты, табличные части, значения перечислений) также маркируются как заимствованные или собственные.
|
||||
|
||||
### 5.1. Заимствованные реквизиты
|
||||
|
||||
```xml
|
||||
<Attribute uuid="259e5f94-...">
|
||||
<InternalInfo/>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ОсновнаяВалюта</Name>
|
||||
<Comment/>
|
||||
<ExtendedConfigurationObject>206abcd3-...</ExtendedConfigurationObject>
|
||||
<Type>
|
||||
<v8:Type>cfg:CatalogRef.Валюты</v8:Type>
|
||||
</Type>
|
||||
</Properties>
|
||||
</Attribute>
|
||||
```
|
||||
|
||||
Заимствованный реквизит содержит `ObjectBelonging: Adopted` и `ExtendedConfigurationObject`. Набор свойств минимальный (Name, Comment, ExtendedConfigurationObject, Type).
|
||||
|
||||
### 5.2. Собственные реквизиты в заимствованном объекте
|
||||
|
||||
```xml
|
||||
<Attribute uuid="7fabdcb4-...">
|
||||
<Properties>
|
||||
<Name>Расш5_Реквизит1</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
<Type>
|
||||
<v8:Type>cfg:CatalogRef.Расш5_Справочник1</v8:Type>
|
||||
</Type>
|
||||
<PasswordMode>false</PasswordMode>
|
||||
<!-- ... полный набор свойств ... -->
|
||||
</Properties>
|
||||
</Attribute>
|
||||
```
|
||||
|
||||
Собственный реквизит **не имеет** `ObjectBelonging` и `ExtendedConfigurationObject`. Содержит полный набор свойств.
|
||||
|
||||
### 5.3. Заимствованные значения перечислений
|
||||
|
||||
```xml
|
||||
<EnumValue uuid="9bc7380f-...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>НаценкаНаКурсДругойВалюты</Name>
|
||||
<ExtendedConfigurationObject>c9ab3890-...</ExtendedConfigurationObject>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
</Properties>
|
||||
</EnumValue>
|
||||
```
|
||||
|
||||
### 5.4. Заимствованные формы
|
||||
|
||||
Метаданные формы (файл `.xml` в каталоге `Forms/`):
|
||||
|
||||
```xml
|
||||
<Form uuid="8fcebcc1-...">
|
||||
<InternalInfo/>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ФормаСписка</Name>
|
||||
<Comment/>
|
||||
<ExtendedConfigurationObject>5f91b00f-...</ExtendedConfigurationObject>
|
||||
<FormType>Managed</FormType>
|
||||
</Properties>
|
||||
</Form>
|
||||
```
|
||||
|
||||
Содержимое формы (расширение) хранится в `Forms/ФормаСписка/Ext/Form.xml`, модуль формы — в `Forms/ФормаСписка/Ext/Form/Module.bsl`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Расширение свойств (xr:PropertyState и xr:ExtendedProperty)
|
||||
|
||||
Расширения могут изменять свойства заимствованных реквизитов. Для этого используются специальные XML-конструкции.
|
||||
|
||||
### 6.1. PropertyState — уведомление об изменении
|
||||
|
||||
Элемент `xr:PropertyState` в `InternalInfo` реквизита указывает, что свойство было изменено расширением.
|
||||
|
||||
```xml
|
||||
<Attribute uuid="a1752169-...">
|
||||
<InternalInfo>
|
||||
<xr:PropertyState>
|
||||
<xr:Property>Type</xr:Property>
|
||||
<xr:State>Notify</xr:State>
|
||||
</xr:PropertyState>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Наценка</Name>
|
||||
<ExtendedConfigurationObject>87429f11-...</ExtendedConfigurationObject>
|
||||
<Type>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>10</v8:Digits>
|
||||
<v8:FractionDigits>2</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</Type>
|
||||
</Properties>
|
||||
</Attribute>
|
||||
```
|
||||
|
||||
Значения `xr:State`:
|
||||
| Значение | Описание |
|
||||
|----------|----------|
|
||||
| `Notify` | Свойство изменено расширением, платформа выводит предупреждение |
|
||||
| `MultiState` | Свойство расширено (тип отличается от основной конфигурации) |
|
||||
|
||||
### 6.2. ExtendedProperty — расширение типа
|
||||
|
||||
Когда расширение изменяет тип реквизита, используется конструкция `xr:ExtendedProperty`:
|
||||
|
||||
```xml
|
||||
<Type xsi:type="xr:ExtendedProperty">
|
||||
<xr:ExtendValue xsi:type="v8:TypeDescription">
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>60</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</xr:ExtendValue>
|
||||
</Type>
|
||||
```
|
||||
|
||||
Свойство `Type` получает атрибут `xsi:type="xr:ExtendedProperty"`, а значение оборачивается в `xr:ExtendValue`. При этом в `InternalInfo` указывается `<xr:State>MultiState</xr:State>`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Модули в расширениях
|
||||
|
||||
### 7.1. Типы модулей
|
||||
|
||||
Расширения поддерживают те же типы модулей, что и конфигурация:
|
||||
|
||||
| Модуль | Файл | Для каких объектов |
|
||||
|--------|------|--------------------|
|
||||
| Модуль объекта | `Ext/ObjectModule.bsl` | Справочники, документы, обработки |
|
||||
| Модуль менеджера | `Ext/ManagerModule.bsl` | Справочники, документы, регистры |
|
||||
| Модуль набора записей | `Ext/RecordSetModule.bsl` | Регистры |
|
||||
| Модуль формы | `Forms/Имя/Ext/Form/Module.bsl` | Формы |
|
||||
| Общий модуль | `CommonModules/Имя/Ext/Module.bsl` | Общие модули |
|
||||
| Модуль команды | `Commands/Имя/Ext/CommandModule.bsl` | Команды |
|
||||
|
||||
### 7.2. Декораторы перехвата
|
||||
|
||||
Модули расширений используют специальные **аннотации-декораторы** для перехвата вызовов процедур основной конфигурации:
|
||||
|
||||
| Декоратор | Описание |
|
||||
|-----------|----------|
|
||||
| `&Перед("ИмяПроцедуры")` | Выполняется **до** оригинальной процедуры |
|
||||
| `&После("ИмяПроцедуры")` | Выполняется **после** оригинальной процедуры |
|
||||
| `&Вместо("ИмяПроцедуры")` | **Заменяет** оригинальную процедуру |
|
||||
| `&ИзменениеИКонтроль("ИмяПроцедуры")` | Копия с контролем изменений (diff-маркеры) |
|
||||
|
||||
#### Пример &Перед / &После
|
||||
|
||||
```bsl
|
||||
&НаКлиенте
|
||||
&Перед("ПодборИзКлассификатора")
|
||||
Процедура Расш5_ПодборИзКлассификатораПеред(Команда)
|
||||
// Код выполняется ДО оригинальной процедуры
|
||||
КонецПроцедуры
|
||||
|
||||
&НаКлиенте
|
||||
&После("ПодборИзКлассификатора")
|
||||
Процедура Расш5_ПодборИзКлассификатораПосле(Команда)
|
||||
// Код выполняется ПОСЛЕ оригинальной процедуры
|
||||
КонецПроцедуры
|
||||
```
|
||||
|
||||
#### Пример &Вместо
|
||||
|
||||
```bsl
|
||||
&НаСервере
|
||||
&Вместо("ЗаполнитьПодменюПараметрыПрописиВалюты")
|
||||
Процедура Расш5_ЗаполнитьПодменюПараметрыПрописиВалюты()
|
||||
// Полная замена оригинальной процедуры
|
||||
// ПродолжитьВызов() — вызов оригинальной реализации
|
||||
ПродолжитьВызов();
|
||||
КонецПроцедуры
|
||||
```
|
||||
|
||||
#### Пример &ИзменениеИКонтроль
|
||||
|
||||
```bsl
|
||||
&ИзменениеИКонтроль("РеквизитыРедактируемыеВГрупповойОбработке")
|
||||
Функция Расш5_РеквизитыРедактируемыеВГрупповойОбработке()
|
||||
Результат = Новый Массив;
|
||||
Результат.Добавить("СпособУстановкиКурса");
|
||||
#Удаление
|
||||
Результат.Добавить("ФормулаРасчетаКурса");
|
||||
#КонецУдаления
|
||||
#Вставка
|
||||
Результат.Добавить("НоваяФормулаРасчетаКурса");
|
||||
#КонецВставки
|
||||
Возврат Результат;
|
||||
КонецФункции
|
||||
```
|
||||
|
||||
### 7.3. Diff-маркеры в коде
|
||||
|
||||
Внутри процедур с декоратором `&ИзменениеИКонтроль` используются diff-маркеры для отслеживания изменений:
|
||||
|
||||
| Маркер | Описание |
|
||||
|--------|----------|
|
||||
| `#Удаление` | Начало блока удалённого кода |
|
||||
| `#КонецУдаления` | Конец блока удалённого кода |
|
||||
| `#Вставка` | Начало блока вставленного кода |
|
||||
| `#КонецВставки` | Конец блока вставленного кода |
|
||||
|
||||
Маркеры могут быть вложенными и чередующимися — типичный паттерн «удалить → вставить»:
|
||||
|
||||
```bsl
|
||||
#Удаление
|
||||
ИначеЕсли Выборка.Дата > Дата Тогда
|
||||
#КонецУдаления
|
||||
#Вставка
|
||||
ИначеЕсли Выборка.Дата < Дата Тогда
|
||||
#КонецВставки
|
||||
```
|
||||
|
||||
### 7.4. Именование процедур расширения
|
||||
|
||||
Собственные процедуры расширения именуются с `NamePrefix`:
|
||||
- `Расш5_ПрочитатьАрхивВнутр()` — при `NamePrefix = Расш5_`
|
||||
- `МоёРасш_ПроверитьДату()` — при `NamePrefix = МоёРасш_`
|
||||
- `Расш1_ПриОпределенииНастроек()` — при `NamePrefix = Расш1_`
|
||||
|
||||
---
|
||||
|
||||
## 8. Предопределённые элементы в расширениях
|
||||
|
||||
Расширения могут добавлять предопределённые элементы к заимствованным справочникам. Файл: `Каталог/Ext/Predefined.xml`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PredefinedData xmlns="http://v8.1c.ru/8.3/xcf/predef"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
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"
|
||||
xsi:type="CatalogPredefinedItems" version="2.17">
|
||||
<Item id="9b751d8b-...">
|
||||
<Name>НовыйЭлемент</Name>
|
||||
<Code>000000001</Code>
|
||||
<Description>Новый элемент</Description>
|
||||
<IsFolder>false</IsFolder>
|
||||
<ExtensionState>Native</ExtensionState>
|
||||
</Item>
|
||||
</PredefinedData>
|
||||
```
|
||||
|
||||
### Отличие от Predefined.xml конфигурации
|
||||
|
||||
| Аспект | Конфигурация | Расширение |
|
||||
|--------|-------------|------------|
|
||||
| Пространство имён | `http://v8.1c.ru/8.3/xcf/predef` (то же) | То же |
|
||||
| `ExtensionState` | Нет | `Native` — элемент создан расширением |
|
||||
|
||||
---
|
||||
|
||||
## 9. Языки (Languages/)
|
||||
|
||||
Язык в расширении **всегда** заимствованный:
|
||||
|
||||
```xml
|
||||
<Language uuid="9453bb96-...">
|
||||
<InternalInfo/>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Русский</Name>
|
||||
<Comment/>
|
||||
<ExtendedConfigurationObject>0663bf5b-...</ExtendedConfigurationObject>
|
||||
<LanguageCode>ru</LanguageCode>
|
||||
</Properties>
|
||||
</Language>
|
||||
```
|
||||
|
||||
UUID в `ExtendedConfigurationObject` одинаков для всех расширений одной конфигурации — это UUID языка в основной конфигурации.
|
||||
|
||||
---
|
||||
|
||||
## 10. Роли (Roles/)
|
||||
|
||||
### 10.1. Собственная роль расширения (без прав)
|
||||
|
||||
Минимальная роль без `Ext/Rights.xml`:
|
||||
|
||||
```xml
|
||||
<Role uuid="c630865b-...">
|
||||
<Properties>
|
||||
<Name>Расш1_ОсновнаяРоль</Name>
|
||||
<Synonym/>
|
||||
<Comment/>
|
||||
</Properties>
|
||||
</Role>
|
||||
```
|
||||
|
||||
Собственные роли расширений в изученных примерах **не имеют** каталога `Ext/` с `Rights.xml`. Права могут задаваться через конфигуратор.
|
||||
|
||||
### 10.2. DefaultRoles
|
||||
|
||||
Ссылки на роли по умолчанию в Configuration.xml:
|
||||
|
||||
```xml
|
||||
<DefaultRoles>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">Role.Расш1_ОсновнаяРоль</xr:Item>
|
||||
</DefaultRoles>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Подсистемы (Subsystems/)
|
||||
|
||||
### 11.1. Собственная подсистема расширения
|
||||
|
||||
```xml
|
||||
<Subsystem uuid="...">
|
||||
<Properties>
|
||||
<Name>Расш1_МояПодсистема</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Comment/>
|
||||
<Picture>
|
||||
<xr:Ref>CommonPicture.Расш1_МояКартинка</xr:Ref>
|
||||
</Picture>
|
||||
<IncludeHelpInContents>false</IncludeHelpInContents>
|
||||
<IncludeInCommandInterface>true</IncludeInCommandInterface>
|
||||
<Content>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">Catalog.Расш1_Проекты</xr:Item>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">Catalog.Расш1_Задачи</xr:Item>
|
||||
<xr:Item xsi:type="xr:MDObjectRef">DataProcessor.Расш1_Обработка1</xr:Item>
|
||||
<!-- ... -->
|
||||
</Content>
|
||||
</Properties>
|
||||
<ChildObjects/>
|
||||
</Subsystem>
|
||||
```
|
||||
|
||||
### 11.2. Заимствованная подсистема
|
||||
|
||||
```xml
|
||||
<Subsystem uuid="...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Закупки</Name>
|
||||
<ExtendedConfigurationObject>f230c0c7-...</ExtendedConfigurationObject>
|
||||
<Content>
|
||||
<!-- пустой или с добавленными объектами -->
|
||||
</Content>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Subsystem>Расш1_Документы</Subsystem> <!-- дочерняя подсистема -->
|
||||
</ChildObjects>
|
||||
</Subsystem>
|
||||
```
|
||||
|
||||
Заимствованная подсистема может содержать элементы `Content` (добавленные расширением команды/объекты) и дочерние подсистемы в `ChildObjects`.
|
||||
|
||||
### 11.3. CommandInterface в расширении
|
||||
|
||||
Командный интерфейс подсистемы в расширении: `Subsystems/Имя/Ext/CommandInterface.xml`.
|
||||
|
||||
```xml
|
||||
<CommandInterface version="2.17">
|
||||
<CommandsVisibility>
|
||||
<xr:Command>
|
||||
<xr:CommandID>Document.ЗаказНаПеремещение.StandardCommand.OpenList</xr:CommandID>
|
||||
<xr:Visibility>
|
||||
<xr:Common>false</xr:Common>
|
||||
<xr:Value name="Role.Расш1_ОсновнаяРоль">true</xr:Value>
|
||||
</xr:Visibility>
|
||||
</xr:Command>
|
||||
</CommandsVisibility>
|
||||
<CommandsOrder>
|
||||
<xr:Group name="NavigationPanelOrdinary">
|
||||
<xr:CommandID>...</xr:CommandID>
|
||||
</xr:Group>
|
||||
</CommandsOrder>
|
||||
</CommandInterface>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Общие модули (CommonModules/)
|
||||
|
||||
### 12.1. Заимствованный общий модуль
|
||||
|
||||
```xml
|
||||
<CommonModule uuid="a32b77fa-...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ZipАрхивы</Name>
|
||||
<ExtendedConfigurationObject>b92e2bb8-...</ExtendedConfigurationObject>
|
||||
</Properties>
|
||||
</CommonModule>
|
||||
```
|
||||
|
||||
Модуль расширения: `CommonModules/ZipАрхивы/Ext/Module.bsl` — содержит процедуры с декораторами перехвата.
|
||||
|
||||
### 12.2. Собственный общий модуль
|
||||
|
||||
Формат аналогичен конфигурации (без `ObjectBelonging` и `ExtendedConfigurationObject`), со всеми свойствами (Server, ExternalConnection, ClientManagedApplication и т.д.).
|
||||
|
||||
---
|
||||
|
||||
## 13. Другие типы заимствованных объектов
|
||||
|
||||
### 13.1. Константы
|
||||
|
||||
```xml
|
||||
<Constant uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="ConstantManager.ИмяКонстанты" category="ConstantManager">...</xr:GeneratedType>
|
||||
<xr:GeneratedType name="ConstantValueManager.ИмяКонстанты" category="ConstantValueManager">...</xr:GeneratedType>
|
||||
<xr:GeneratedType name="ConstantValueKey.ИмяКонстанты" category="ConstantValueKey">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяКонстанты</Name>
|
||||
<ExtendedConfigurationObject>81d26b82-...</ExtendedConfigurationObject>
|
||||
<Type>
|
||||
<v8:Type>xs:boolean</v8:Type>
|
||||
</Type>
|
||||
</Properties>
|
||||
</Constant>
|
||||
```
|
||||
|
||||
### 13.2. Функциональные опции
|
||||
|
||||
```xml
|
||||
<FunctionalOption uuid="...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяФункциональнойОпции</Name>
|
||||
<ExtendedConfigurationObject>d2699502-...</ExtendedConfigurationObject>
|
||||
<Location>Constant.ИмяКонстанты</Location>
|
||||
</Properties>
|
||||
</FunctionalOption>
|
||||
```
|
||||
|
||||
### 13.3. Определяемые типы
|
||||
|
||||
Заимствованный (минимальный):
|
||||
```xml
|
||||
<DefinedType uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType name="DefinedType.ИмяОпределяемогоТипа" category="DefinedType">...</xr:GeneratedType>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяОпределяемогоТипа</Name>
|
||||
</Properties>
|
||||
</DefinedType>
|
||||
```
|
||||
|
||||
Собственный (с полным описанием типа):
|
||||
```xml
|
||||
<DefinedType uuid="...">
|
||||
<Properties>
|
||||
<Name>Расш1_Координата</Name>
|
||||
<Synonym>...</Synonym>
|
||||
<Type>
|
||||
<v8:Type>xs:decimal</v8:Type>
|
||||
<v8:NumberQualifiers>
|
||||
<v8:Digits>15</v8:Digits>
|
||||
<v8:FractionDigits>10</v8:FractionDigits>
|
||||
<v8:AllowedSign>Any</v8:AllowedSign>
|
||||
</v8:NumberQualifiers>
|
||||
</Type>
|
||||
</Properties>
|
||||
</DefinedType>
|
||||
```
|
||||
|
||||
### 13.4. Элементы стиля
|
||||
|
||||
```xml
|
||||
<StyleItem uuid="...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяЭлементаСтиля</Name>
|
||||
<ExtendedConfigurationObject>3d428bdf-...</ExtendedConfigurationObject>
|
||||
<Type>Font</Type>
|
||||
</Properties>
|
||||
</StyleItem>
|
||||
```
|
||||
|
||||
Тип: `Color`, `Font`, `Border`.
|
||||
|
||||
### 13.5. Общие картинки
|
||||
|
||||
```xml
|
||||
<CommonPicture uuid="...">
|
||||
<InternalInfo/>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяКартинки</Name>
|
||||
</Properties>
|
||||
</CommonPicture>
|
||||
```
|
||||
|
||||
Собственные картинки могут иметь каталог `Ext/` с `Picture.xml` и файлом изображения:
|
||||
|
||||
```
|
||||
CommonPictures/Расш1_МояКартинка/
|
||||
Ext/
|
||||
Picture.xml # <Picture><xr:Abs>Picture.png</xr:Abs>...</Picture>
|
||||
Picture/
|
||||
Picture.png # Файл изображения
|
||||
```
|
||||
|
||||
### 13.6. Общие команды
|
||||
|
||||
```xml
|
||||
<CommonCommand uuid="...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяКоманды</Name>
|
||||
<Group>FormCommandBarImportant</Group>
|
||||
</Properties>
|
||||
</CommonCommand>
|
||||
```
|
||||
|
||||
### 13.7. Планы обмена
|
||||
|
||||
```xml
|
||||
<ExchangePlan uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:ThisNode>c335c2b8-...</xr:ThisNode>
|
||||
<xr:GeneratedType .../>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяПланаОбмена</Name>
|
||||
<ExtendedConfigurationObject>0c01b26a-...</ExtendedConfigurationObject>
|
||||
</Properties>
|
||||
<ChildObjects/>
|
||||
</ExchangePlan>
|
||||
```
|
||||
|
||||
### 13.8. Планы счетов
|
||||
|
||||
```xml
|
||||
<ChartOfAccounts uuid="...">
|
||||
<InternalInfo>
|
||||
<xr:GeneratedType .../>
|
||||
</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>Хозрасчетный</Name>
|
||||
<ExtendedConfigurationObject>3796bdf5-...</ExtendedConfigurationObject>
|
||||
</Properties>
|
||||
<ChildObjects/>
|
||||
</ChartOfAccounts>
|
||||
```
|
||||
|
||||
### 13.9. Регистры сведений
|
||||
|
||||
Заимствованный (минимальный):
|
||||
```xml
|
||||
<InformationRegister uuid="...">
|
||||
<InternalInfo>...</InternalInfo>
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяРегистра</Name>
|
||||
<ExtendedConfigurationObject>...</ExtendedConfigurationObject>
|
||||
<InformationRegisterPeriodicity>Quarter</InformationRegisterPeriodicity>
|
||||
<WriteMode>Independent</WriteMode>
|
||||
</Properties>
|
||||
<ChildObjects>
|
||||
<Resource uuid="...">
|
||||
<Properties>
|
||||
<ObjectBelonging>Adopted</ObjectBelonging>
|
||||
<Name>ИмяРесурса</Name>
|
||||
<ExtendedConfigurationObject>...</ExtendedConfigurationObject>
|
||||
<Type>...</Type>
|
||||
</Properties>
|
||||
</Resource>
|
||||
</ChildObjects>
|
||||
</InformationRegister>
|
||||
```
|
||||
|
||||
Собственный регистр — полный набор свойств, аналогично конфигурации.
|
||||
|
||||
---
|
||||
|
||||
## 14. Назначения расширений (ConfigurationExtensionPurpose)
|
||||
|
||||
| Значение | Русское название | Описание |
|
||||
|----------|-----------------|----------|
|
||||
| `Patch` | Исправление | Минимальные исправления ошибок. Наибольшие ограничения |
|
||||
| `Customization` | Адаптация | Доработка под требования заказчика. Средний уровень ограничений |
|
||||
| `AddOn` | Дополнение | Добавление новой функциональности. Минимальные ограничения |
|
||||
|
||||
Назначение влияет на то, какие модификации допускает платформа при подключении расширения.
|
||||
|
||||
---
|
||||
|
||||
## 15. Структура каталогов (сводка)
|
||||
|
||||
### 15.1. Собственный объект (полная структура)
|
||||
|
||||
```
|
||||
Catalogs/Расш1_Проекты.xml # Метаданные (полные)
|
||||
Catalogs/Расш1_Проекты/
|
||||
Ext/
|
||||
ObjectModule.bsl # Модуль объекта
|
||||
ManagerModule.bsl # Модуль менеджера
|
||||
Predefined.xml # Предопределённые (опц.)
|
||||
Forms/
|
||||
ФормаЭлемента.xml # Метаданные формы
|
||||
ФормаЭлемента/
|
||||
Ext/
|
||||
Form.xml # Содержимое формы
|
||||
Form/
|
||||
Module.bsl # Модуль формы
|
||||
ФормаСписка.xml
|
||||
ФормаСписка/
|
||||
Ext/
|
||||
Form.xml
|
||||
Form/
|
||||
Module.bsl
|
||||
Templates/
|
||||
ПФ_MXL_Акт.xml # Метаданные макета
|
||||
ПФ_MXL_Акт/
|
||||
Ext/
|
||||
Template.xml # Содержимое макета
|
||||
Commands/
|
||||
ИмяКоманды.xml # Метаданные команды
|
||||
ИмяКоманды/
|
||||
Ext/
|
||||
CommandModule.bsl # Модуль команды
|
||||
```
|
||||
|
||||
### 15.2. Заимствованный объект (минимальная структура)
|
||||
|
||||
```
|
||||
Catalogs/Валюты.xml # Метаданные (сокращённые)
|
||||
Catalogs/Валюты/
|
||||
Ext/
|
||||
ManagerModule.bsl # Расширение модуля менеджера
|
||||
Predefined.xml # Предопред. элементы (опц.)
|
||||
Forms/
|
||||
ФормаСписка.xml # Метаданные (сокращённые)
|
||||
ФормаСписка/
|
||||
Ext/
|
||||
Form.xml # Расширение формы
|
||||
Form/
|
||||
Module.bsl # Расширение модуля формы
|
||||
```
|
||||
|
||||
### 15.3. Минимальное расширение (пустое)
|
||||
|
||||
```
|
||||
Configuration.xml # Корневой файл
|
||||
ConfigDumpInfo.xml # Версии объектов
|
||||
Languages/
|
||||
Русский.xml # Язык (заимствованный)
|
||||
```
|
||||
|
||||
### 15.4. Типичное расширение с ролью
|
||||
|
||||
```
|
||||
Configuration.xml
|
||||
ConfigDumpInfo.xml
|
||||
Languages/
|
||||
Русский.xml
|
||||
Roles/
|
||||
Расш1_ОсновнаяРоль.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. Отличия заимствованного объекта от обычного (сводная таблица)
|
||||
|
||||
| Аспект | Обычный (конфигурация) | Заимствованный (расширение) | Собственный (расширение) |
|
||||
|--------|----------------------|--------------------------|------------------------|
|
||||
| `ObjectBelonging` | Нет | `Adopted` | Нет |
|
||||
| `ExtendedConfigurationObject` | Нет | UUID объекта конфигурации | Нет |
|
||||
| Набор Properties | Полный | Минимальный + изменённые | Полный |
|
||||
| InternalInfo | GeneratedType | GeneratedType + PropertyState | GeneratedType |
|
||||
| Реквизиты в ChildObjects | Полные | Заимствованные + собственные | Полные |
|
||||
| Модули | Полные | С декораторами перехвата | Полные |
|
||||
| Формы | Полные | С расширениями (Ext/) | Полные |
|
||||
@@ -0,0 +1,172 @@
|
||||
# Сводный индекс спецификаций формата XML конфигурации 1С
|
||||
|
||||
Единая точка входа ко всем спецификациям XML-формата выгрузки 1С:Предприятие 8.3.
|
||||
|
||||
---
|
||||
|
||||
## 1. Корневые файлы конфигурации
|
||||
|
||||
| Файл | Описание | Спецификация |
|
||||
|------|----------|--------------|
|
||||
| `Configuration.xml` | Свойства и состав конфигурации | [1c-configuration-spec.md § 2](1c-configuration-spec.md#2-configurationxml--корневой-файл-конфигурации) |
|
||||
| `ConfigDumpInfo.xml` | Версии объектов (служебный) | [1c-configuration-spec.md § 3](1c-configuration-spec.md#3-configdumpinfoxml--служебный-файл-выгрузки) |
|
||||
| `Ext/` | Модули, интерфейс, начальная страница | [1c-configuration-spec.md § 4](1c-configuration-spec.md#4-ext--корневой-каталог-конфигурации) |
|
||||
| `Languages/` | Языки конфигурации | [1c-configuration-spec.md § 5](1c-configuration-spec.md#5-языки-languages) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Объекты метаданных
|
||||
|
||||
Все типы объектов, встречающиеся в `ChildObjects` корневого `Configuration.xml`. Порядок соответствует порядку в XML.
|
||||
|
||||
### Служебные и интерфейсные
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `Language` | `Languages/` | Языки | [1c-configuration-spec.md § 5](1c-configuration-spec.md#5-языки-languages) |
|
||||
| `Subsystem` | `Subsystems/` | Подсистемы | [1c-subsystem-spec.md § 3](1c-subsystem-spec.md#3-формат-подсистемы) |
|
||||
| `StyleItem` | `StyleItems/` | Элементы стиля | [1c-configuration-spec.md § 6.16](1c-configuration-spec.md#616-styleitem--элемент-стиля) |
|
||||
| `Style` | `Styles/` | Стили (устаревший) | [1c-configuration-spec.md § 6.17](1c-configuration-spec.md#617-style--стиль-устаревший) |
|
||||
| `CommandGroup` | `CommandGroups/` | Группы команд | [1c-subsystem-spec.md § 5](1c-subsystem-spec.md#5-формат-группы-команд-commandgroup) |
|
||||
|
||||
### Общие объекты
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `CommonPicture` | `CommonPictures/` | Общие картинки | [1c-configuration-spec.md § 6.1](1c-configuration-spec.md#61-commonpicture--общая-картинка) |
|
||||
| `SessionParameter` | `SessionParameters/` | Параметры сеанса | [1c-configuration-spec.md § 6.6](1c-configuration-spec.md#66-sessionparameter--параметр-сеанса) |
|
||||
| `Role` | `Roles/` | Роли | [1c-role-spec.md § Файл метаданных](1c-role-spec.md#файл-метаданных-rolesимяролиxml) |
|
||||
| `CommonTemplate` | `CommonTemplates/` | Общие макеты | [1c-configuration-spec.md § 6.2](1c-configuration-spec.md#62-commontemplate--общий-макет) |
|
||||
| `FilterCriterion` | `FilterCriteria/` | Критерии отбора | [1c-configuration-spec.md § 6.11](1c-configuration-spec.md#611-filtercriterion--критерий-отбора) |
|
||||
| `CommonModule` | `CommonModules/` | Общие модули | [1c-config-objects-spec.md § 21](1c-config-objects-spec.md#21-общие-модули-commonmodules) |
|
||||
| `CommonAttribute` | `CommonAttributes/` | Общие реквизиты | [1c-configuration-spec.md § 6.3](1c-configuration-spec.md#63-commonattribute--общий-реквизит) |
|
||||
| `CommonCommand` | `CommonCommands/` | Общие команды | [1c-subsystem-spec.md § 6](1c-subsystem-spec.md#6-формат-общей-команды-commoncommand) |
|
||||
| `CommonForm` | `CommonForms/` | Общие формы | [1c-configuration-spec.md § 6.4](1c-configuration-spec.md#64-commonform--общая-форма) |
|
||||
|
||||
### Интеграция и сервисы
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `ExchangePlan` | `ExchangePlans/` | Планы обмена | [1c-config-objects-spec.md § 15](1c-config-objects-spec.md#15-планы-обмена-exchangeplans) |
|
||||
| `XDTOPackage` | `XDTOPackages/` | XDTO-пакеты | [1c-configuration-spec.md § 6.14](1c-configuration-spec.md#614-xdtopackage--xdto-пакет) |
|
||||
| `WebService` | `WebServices/` | Веб-сервисы | [1c-config-objects-spec.md § 25](1c-config-objects-spec.md#25-веб-сервисы-webservices) |
|
||||
| `HTTPService` | `HTTPServices/` | HTTP-сервисы | [1c-config-objects-spec.md § 24](1c-config-objects-spec.md#24-http-сервисы-httpservices) |
|
||||
| `WSReference` | `WSReferences/` | WS-ссылки | [1c-configuration-spec.md § 6.15](1c-configuration-spec.md#615-wsreference--ws-ссылка) |
|
||||
| `IntegrationService` | `IntegrationServices/` | Сервисы интеграции | [1c-configuration-spec.md § 6.13](1c-configuration-spec.md#613-integrationservice--сервис-интеграции) |
|
||||
|
||||
### Поведение и параметризация
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `EventSubscription` | `EventSubscriptions/` | Подписки на события | [1c-config-objects-spec.md § 23](1c-config-objects-spec.md#23-подписки-на-события-eventsubscriptions) |
|
||||
| `ScheduledJob` | `ScheduledJobs/` | Регламентные задания | [1c-config-objects-spec.md § 22](1c-config-objects-spec.md#22-регламентные-задания-scheduledjobs) |
|
||||
| `SettingsStorage` | `SettingsStorages/` | Хранилища настроек | [1c-configuration-spec.md § 6.10](1c-configuration-spec.md#610-settingsstorage--хранилище-настроек) |
|
||||
| `FunctionalOption` | `FunctionalOptions/` | Функциональные опции | [1c-configuration-spec.md § 6.7](1c-configuration-spec.md#67-functionaloption--функциональная-опция) |
|
||||
| `FunctionalOptionsParameter` | `FunctionalOptionsParameters/` | Параметры ФО | [1c-configuration-spec.md § 6.8](1c-configuration-spec.md#68-functionaloptionsparameter--параметр-функциональных-опций) |
|
||||
| `DefinedType` | `DefinedTypes/` | Определяемые типы | [1c-config-objects-spec.md § 20](1c-config-objects-spec.md#20-определяемые-типы-definedtypes) |
|
||||
| `Constant` | `Constants/` | Константы | [1c-config-objects-spec.md § 17](1c-config-objects-spec.md#17-константы-constants) |
|
||||
|
||||
### Прикладные объекты
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `Catalog` | `Catalogs/` | Справочники | [1c-config-objects-spec.md § 7](1c-config-objects-spec.md#7-справочники-catalogs) |
|
||||
| `Document` | `Documents/` | Документы | [1c-config-objects-spec.md § 8](1c-config-objects-spec.md#8-документы-documents) |
|
||||
| `DocumentNumerator` | `DocumentNumerators/` | Нумераторы документов | [1c-configuration-spec.md § 6.12](1c-configuration-spec.md#612-documentnumerator--нумератор-документов) |
|
||||
| `Sequence` | `Sequences/` | Последовательности | [1c-configuration-spec.md § 6.9](1c-configuration-spec.md#69-sequence--последовательность-документов) |
|
||||
| `DocumentJournal` | `DocumentJournals/` | Журналы документов | [1c-config-objects-spec.md § 19](1c-config-objects-spec.md#19-журналы-документов-documentjournals) |
|
||||
| `Enum` | `Enums/` | Перечисления | [1c-config-objects-spec.md § 16](1c-config-objects-spec.md#16-перечисления-enums) |
|
||||
| `Report` | `Reports/` | Отчёты | [1c-config-objects-spec.md § 18](1c-config-objects-spec.md#18-отчёты-и-обработки) |
|
||||
| `DataProcessor` | `DataProcessors/` | Обработки | [1c-config-objects-spec.md § 18](1c-config-objects-spec.md#18-отчёты-и-обработки) |
|
||||
|
||||
### Регистры
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `InformationRegister` | `InformationRegisters/` | Регистры сведений | [1c-config-objects-spec.md § 9.1](1c-config-objects-spec.md#91-регистры-сведений-informationregisters) |
|
||||
| `AccumulationRegister` | `AccumulationRegisters/` | Регистры накопления | [1c-config-objects-spec.md § 9.4](1c-config-objects-spec.md#94-регистры-накопления-accumulationregisters) |
|
||||
| `AccountingRegister` | `AccountingRegisters/` | Регистры бухгалтерии | [1c-config-objects-spec.md § 9.5](1c-config-objects-spec.md#95-регистры-бухгалтерии-accountingregisters) |
|
||||
| `CalculationRegister` | `CalculationRegisters/` | Регистры расчёта | [1c-config-objects-spec.md § 9.6](1c-config-objects-spec.md#96-регистры-расчёта-calculationregisters) |
|
||||
|
||||
### Планы
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `ChartOfCharacteristicTypes` | `ChartsOfCharacteristicTypes/` | Планы видов характеристик | [1c-config-objects-spec.md § 11](1c-config-objects-spec.md#11-планы-видов-характеристик-chartsofcharacteristictypes) |
|
||||
| `ChartOfAccounts` | `ChartsOfAccounts/` | Планы счетов | [1c-config-objects-spec.md § 10](1c-config-objects-spec.md#10-планы-счетов-chartsofaccounts) |
|
||||
| `ChartOfCalculationTypes` | `ChartsOfCalculationTypes/` | Планы видов расчёта | [1c-config-objects-spec.md § 12](1c-config-objects-spec.md#12-планы-видов-расчёта-chartsofcalculationtypes) |
|
||||
|
||||
### Бизнес-процессы
|
||||
|
||||
| XML-элемент | Каталог | Русское название | Спецификация |
|
||||
|-------------|---------|-----------------|--------------|
|
||||
| `BusinessProcess` | `BusinessProcesses/` | Бизнес-процессы | [1c-config-objects-spec.md § 13](1c-config-objects-spec.md#13-бизнес-процессы-businessprocesses) |
|
||||
| `Task` | `Tasks/` | Задачи | [1c-config-objects-spec.md § 14](1c-config-objects-spec.md#14-задачи-tasks) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Вложенные форматы
|
||||
|
||||
Форматы файлов, вложенных в каталоги объектов метаданных.
|
||||
|
||||
| Формат | Файл | Описание | Спецификация |
|
||||
|--------|------|----------|--------------|
|
||||
| Управляемая форма | `Ext/Form.xml` | Элементы, реквизиты, команды | [1c-form-spec.md § 1](1c-form-spec.md#1-корневой-элемент) |
|
||||
| СКД (DataCompositionSchema) | `Ext/Template.xml` | Схема компоновки данных | [1c-dcs-spec.md § 2](1c-dcs-spec.md#2-общая-структура-datacompositionschema) |
|
||||
| Табличный документ (MXL) | `Ext/Template.xml` | Печатная форма | [1c-spreadsheet-spec.md](1c-spreadsheet-spec.md#структура-документа) |
|
||||
| Роль (Rights) | `Ext/Rights.xml` | Права доступа и RLS | [1c-role-spec.md](1c-role-spec.md#файл-прав-rolesимяролиextrightsxml) |
|
||||
| Справка (Help) | `Ext/Help/` | Встроенная справка | [1c-help-spec.md § 1](1c-help-spec.md#1-структура-файлов) |
|
||||
| Предопределённые элементы | `Predefined.xml` | Предопределённые справочники/ПВХ | [1c-config-objects-spec.md § 7.2](1c-config-objects-spec.md#72-предопределённые-элементы-predefinedxml) |
|
||||
| Состав плана обмена | `Content.xml` | Объекты синхронизации | [1c-config-objects-spec.md § 15.4](1c-config-objects-spec.md#154-состав-плана-обмена-contentxml) |
|
||||
| Карта маршрута | `Flowchart.xml` | Маршрут бизнес-процесса | [1c-config-objects-spec.md § 13.3](1c-config-objects-spec.md#133-карта-маршрута-flowchartxml) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Расширения конфигурации (CFE)
|
||||
|
||||
| Тема | Описание | Спецификация |
|
||||
|------|----------|--------------|
|
||||
| Общая структура выгрузки | Каталоги, отличия от конфигурации | [1c-extension-spec.md § 1](1c-extension-spec.md#1-общая-структура-выгрузки-расширения) |
|
||||
| Configuration.xml расширения | Свойства, назначение, ChildObjects | [1c-extension-spec.md § 2](1c-extension-spec.md#2-configurationxml--корневой-файл-расширения) |
|
||||
| Заимствованные / собственные объекты | ObjectBelonging, ExtendedConfigurationObject | [1c-extension-spec.md § 4](1c-extension-spec.md#4-заимствованные-и-собственные-объекты) |
|
||||
| Расширение свойств (xr:PropertyState) | MultiState, ExtendedProperty | [1c-extension-spec.md § 6](1c-extension-spec.md#6-расширение-свойств-xrpropertystate-и-xrextendedproperty) |
|
||||
| Модули и декораторы перехвата | &Перед, &После, &Вместо, diff-маркеры | [1c-extension-spec.md § 7](1c-extension-spec.md#7-модули-в-расширениях) |
|
||||
| Предопределённые элементы | ExtensionState: Native | [1c-extension-spec.md § 8](1c-extension-spec.md#8-предопределённые-элементы-в-расширениях) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Внешние обработки и отчёты
|
||||
|
||||
| Формат | Описание | Спецификация |
|
||||
|--------|----------|--------------|
|
||||
| EPF (внешняя обработка) | ExternalDataProcessor | [1c-epf-spec.md § 1](1c-epf-spec.md#1-структура-каталогов) |
|
||||
| ERF (внешний отчёт) | ExternalReport | [1c-erf-spec.md § 1](1c-erf-spec.md#1-структура-каталогов) |
|
||||
|
||||
---
|
||||
|
||||
## 6. Общие элементы формата
|
||||
|
||||
Общие для всех типов объектов структуры XML описаны в спецификации объектов:
|
||||
|
||||
| Тема | Спецификация |
|
||||
|------|--------------|
|
||||
| Корневой элемент MetaDataObject | [1c-config-objects-spec.md § 2](1c-config-objects-spec.md#2-общий-формат-xml) |
|
||||
| Пространства имён XML | [1c-config-objects-spec.md § 2.2](1c-config-objects-spec.md#22-пространства-имён) |
|
||||
| InternalInfo / GeneratedType | [1c-config-objects-spec.md § 3](1c-config-objects-spec.md#3-internalinfo--внутренняя-информация) |
|
||||
| Общие свойства Properties | [1c-config-objects-spec.md § 4](1c-config-objects-spec.md#4-общие-элементы-properties) |
|
||||
| Стандартные реквизиты | [1c-config-objects-spec.md § 5](1c-config-objects-spec.md#5-стандартные-реквизиты-standardattributes) |
|
||||
| Дочерние объекты (Attribute, TabularSection, Form, Template, Command) | [1c-config-objects-spec.md § 6](1c-config-objects-spec.md#6-дочерние-объекты-childobjects) |
|
||||
| Формат ссылок на объекты | [1c-config-objects-spec.md § 28](1c-config-objects-spec.md#28-формат-ссылок-на-объекты-метаданных) |
|
||||
| Различия версий 2.17 → 2.20 | [1c-config-objects-spec.md § 26](1c-config-objects-spec.md#26-различия-версий-платформы) |
|
||||
|
||||
---
|
||||
|
||||
## 7. DSL-спецификации (компактный формат ввода)
|
||||
|
||||
| DSL | Описание | Спецификация |
|
||||
|-----|----------|--------------|
|
||||
| Meta DSL | JSON-формат для создания/редактирования объектов | [meta-dsl-spec.md](meta-dsl-spec.md) |
|
||||
| Form DSL | JSON-формат для компиляции форм | [form-dsl-spec.md](form-dsl-spec.md) |
|
||||
| SKD DSL | JSON-формат для компиляции СКД | [skd-dsl-spec.md](skd-dsl-spec.md) |
|
||||
| MXL DSL | JSON-формат для компиляции табличных документов | [mxl-dsl-spec.md](mxl-dsl-spec.md) |
|
||||
| Role DSL | JSON-формат для компиляции ролей | [role-dsl-spec.md](role-dsl-spec.md) |
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -0,0 +1,175 @@
|
||||
# Расширения конфигурации (CFE)
|
||||
|
||||
Навыки группы `/cfe-*` позволяют создавать, заимствовать объекты, перехватывать методы, проверять и анализировать расширения конфигурации 1С.
|
||||
|
||||
## Навыки
|
||||
|
||||
| Навык | Параметры | Описание |
|
||||
|-------|-----------|----------|
|
||||
| `/cfe-init` | `<Name> [-Purpose Patch\|Customization\|AddOn] [-CompatibilityMode]` | Создание расширения (scaffold XML-исходников) |
|
||||
| `/cfe-borrow` | `-ExtensionPath <path> -ConfigPath <path> -Object "Type.Name"` | Заимствование объектов из конфигурации |
|
||||
| `/cfe-patch-method` | `-ExtensionPath <path> -ModulePath "Type.Name.Module" -MethodName "X" -InterceptorType Before` | Генерация перехватчика метода |
|
||||
| `/cfe-validate` | `<ExtensionPath> [-MaxErrors 30]` | Валидация структурной корректности (9 проверок) |
|
||||
| `/cfe-diff` | `-ExtensionPath <path> -ConfigPath <path> [-Mode A\|B]` | Анализ расширения и проверка переноса |
|
||||
|
||||
## Рабочий цикл
|
||||
|
||||
```
|
||||
cf-info (версия, совместимость)
|
||||
↓
|
||||
/cfe-init → scaffold расширения
|
||||
↓
|
||||
/cfe-borrow → заимствование объектов из конфигурации
|
||||
↓
|
||||
/cfe-patch-method → перехват методов
|
||||
↓
|
||||
/cfe-validate → проверка корректности
|
||||
↓
|
||||
/cfe-diff Mode A → обзор изменений
|
||||
```
|
||||
|
||||
## Типичные сценарии
|
||||
|
||||
### Создание расширения для исправления бага
|
||||
|
||||
```
|
||||
> Создай расширение для исправления бага в справочнике Контрагенты,
|
||||
конфигурация ERP в C:\cfsrc\erp
|
||||
```
|
||||
|
||||
Claude выполнит:
|
||||
1. `/cf-info C:\cfsrc\erp -Mode brief` — получить версию и режим совместимости
|
||||
2. `/cfe-init` — создать расширение с нужным `CompatibilityMode`
|
||||
3. `/cfe-borrow` — заимствовать `Catalog.Контрагенты`
|
||||
4. `/cfe-patch-method` — создать перехватчик нужного метода
|
||||
5. `/cfe-validate` — проверить результат
|
||||
|
||||
### Анализ существующего расширения
|
||||
|
||||
```
|
||||
> Покажи что изменено в расширении src/
|
||||
```
|
||||
|
||||
Claude вызовет `/cfe-diff -Mode A` и покажет: заимствованные объекты, перехватчики, собственные объекты.
|
||||
|
||||
### Проверка переноса изменений
|
||||
|
||||
```
|
||||
> Проверь, все ли изменения из расширения перенесены в конфигурацию
|
||||
```
|
||||
|
||||
Claude вызовет `/cfe-diff -Mode B` — найдёт блоки `#Вставка` и проверит их наличие в конфигурации.
|
||||
|
||||
## cfe-init — создание расширения
|
||||
|
||||
Параметры:
|
||||
|
||||
| Параметр | Описание | По умолчанию |
|
||||
|----------|----------|--------------|
|
||||
| `Name` | Имя расширения (обязат.) | — |
|
||||
| `Synonym` | Синоним | = Name |
|
||||
| `NamePrefix` | Префикс собственных объектов | = Name + "_" |
|
||||
| `OutputDir` | Каталог | `src` |
|
||||
| `Purpose` | Назначение | `Customization` |
|
||||
| `Version` | Версия | — |
|
||||
| `Vendor` | Поставщик | — |
|
||||
| `CompatibilityMode` | Режим совместимости | `Version8_3_24` |
|
||||
| `NoRole` | Без основной роли | false |
|
||||
|
||||
Создаёт:
|
||||
```
|
||||
<OutputDir>/
|
||||
├── Configuration.xml # Свойства расширения
|
||||
├── Languages/
|
||||
│ └── Русский.xml # Язык (Adopted)
|
||||
└── Roles/ # Если не -NoRole
|
||||
└── <Prefix>ОсновнаяРоль.xml
|
||||
```
|
||||
|
||||
Назначение расширения (`Purpose`):
|
||||
- `Patch` — исправление ошибок (минимальные изменения, только перехватчики)
|
||||
- `Customization` — доработка (реквизиты, формы, модули)
|
||||
- `AddOn` — дополнение (полноценный функционал)
|
||||
|
||||
## cfe-borrow — заимствование объектов
|
||||
|
||||
Заимствует объекты из основной конфигурации в расширение. Создаёт минимальные XML-файлы с `ObjectBelonging=Adopted` и `ExtendedConfigurationObject`.
|
||||
|
||||
Формат `-Object`:
|
||||
- `Catalog.Контрагенты` — справочник
|
||||
- `CommonModule.РаботаСФайлами` — общий модуль
|
||||
- `Enum.ВидыОплат` — перечисление
|
||||
- `Document.Заказ ;; Catalog.Товары` — несколько объектов через `;;`
|
||||
|
||||
Поддерживаемые типы: Catalog, Document, Enum, CommonModule, Report, DataProcessor, ExchangePlan, InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes, BusinessProcess, Task, и другие (44 типа).
|
||||
|
||||
## cfe-patch-method — перехват методов
|
||||
|
||||
Генерирует `.bsl` файл с декоратором перехвата для заимствованного объекта.
|
||||
|
||||
Параметры:
|
||||
|
||||
| Параметр | Описание |
|
||||
|----------|----------|
|
||||
| `ModulePath` | `Catalog.X.ObjectModule`, `CommonModule.Y`, `Catalog.X.Form.Z` |
|
||||
| `MethodName` | Имя перехватываемого метода |
|
||||
| `InterceptorType` | `Before` / `After` / `ModificationAndControl` |
|
||||
| `Context` | `НаСервере` / `НаКлиенте` / `НаСервереБезКонтекста` |
|
||||
| `IsFunction` | Добавить `Возврат` |
|
||||
|
||||
Типы перехватчиков:
|
||||
|
||||
| Тип | Декоратор | Когда использовать |
|
||||
|-----|-----------|-------------------|
|
||||
| `Before` | `&Перед` | Выполнить код до вызова оригинального метода |
|
||||
| `After` | `&После` | Выполнить код после вызова оригинального метода |
|
||||
| `ModificationAndControl` | `&ИзменениеИКонтроль` | Полная замена тела метода с маркерами `#Вставка`/`#Удаление` |
|
||||
|
||||
Пример генерируемого кода (`Before`):
|
||||
```bsl
|
||||
&НаСервере
|
||||
&Перед("ПриЗаписи")
|
||||
Процедура Расш1_ПриЗаписи()
|
||||
// TODO: код перед вызовом оригинального метода
|
||||
КонецПроцедуры
|
||||
```
|
||||
|
||||
## cfe-validate — проверки
|
||||
|
||||
| # | Проверка | Уровень |
|
||||
|---|----------|---------|
|
||||
| 1 | XML well-formedness, MetaDataObject/Configuration, version | ERROR |
|
||||
| 2 | InternalInfo: 7 ContainedObject, валидные ClassId | ERROR |
|
||||
| 3 | Extension properties: ObjectBelonging=Adopted, Name, Purpose, NamePrefix, KeepMapping | ERROR |
|
||||
| 4 | Enum-значения (4 свойства) | ERROR |
|
||||
| 5 | ChildObjects: валидные типы, нет дубликатов, порядок | ERROR/WARN |
|
||||
| 6 | DefaultLanguage ссылается на существующий Language | ERROR |
|
||||
| 7 | Файлы языков существуют | WARN |
|
||||
| 8 | Каталоги объектов существуют | WARN |
|
||||
| 9 | Заимствованные объекты: ObjectBelonging=Adopted, ExtendedConfigurationObject UUID | ERROR/WARN |
|
||||
|
||||
## cfe-diff — режимы
|
||||
|
||||
### Mode A — обзор расширения
|
||||
|
||||
Для каждого объекта показывает:
|
||||
- `[BORROWED]` — заимствованный: перехватчики, собственные реквизиты/формы
|
||||
- `[OWN]` — собственный: количество реквизитов, ТЧ, форм
|
||||
|
||||
### Mode B — проверка переноса
|
||||
|
||||
Для каждого `&ИзменениеИКонтроль` проверяет, перенесены ли блоки `#Вставка` в конфигурацию:
|
||||
- `[TRANSFERRED]` — код найден в конфигурации
|
||||
- `[NOT_TRANSFERRED]` — код не найден
|
||||
- `[NEEDS_REVIEW]` — нет блоков `#Вставка` или модуль конфигурации не найден
|
||||
|
||||
## Связь с другими навыками
|
||||
|
||||
- `/cf-info` — получение версии и совместимости конфигурации перед `cfe-init`
|
||||
- `/meta-compile` — создание собственных объектов расширения (реквизиты, ТЧ)
|
||||
- `/form-compile`, `/form-edit` — создание и модификация форм расширения
|
||||
- `/cfe-validate` — всегда проверяйте расширение после изменений
|
||||
|
||||
## Спецификации
|
||||
|
||||
- [1c-extension-spec.md](1c-extension-spec.md) — XML-формат выгрузки расширений конфигурации (CFE)
|
||||
+66
-17
@@ -1,23 +1,42 @@
|
||||
# Внешние обработки (EPF)
|
||||
# Внешние обработки и отчёты (EPF / ERF)
|
||||
|
||||
Навыки группы `/epf-*` позволяют создавать, модифицировать и собирать внешние обработки 1С:Предприятия 8.3 (`.epf`) из XML-исходников, не запоминая детали формата.
|
||||
Навыки для создания, модификации и сборки внешних обработок (`.epf`) и внешних отчётов (`.erf`) 1С:Предприятия 8.3 из XML-исходников.
|
||||
|
||||
## Навыки
|
||||
## Навыки обработок (EPF)
|
||||
|
||||
| Навык | Параметры | Описание |
|
||||
|-------|-----------|----------|
|
||||
| `/epf-init` | `<Name> [Synonym]` | Создать новую обработку (корневой XML + модуль объекта) |
|
||||
| `/epf-add-form` | `<ProcessorName> <FormName> [Synonym]` | Добавить управляемую форму |
|
||||
| `/epf-add-template` | `<ProcessorName> <TemplateName> <TemplateType>` | Добавить макет (HTML, Text, SpreadsheetDocument, BinaryData) |
|
||||
| `/epf-add-help` | `<ProcessorName>` | Добавить встроенную справку (Help.xml + HTML) |
|
||||
| `/epf-remove-form` | `<ProcessorName> <FormName>` | Удалить форму |
|
||||
| `/epf-remove-template` | `<ProcessorName> <TemplateName>` | Удалить макет |
|
||||
| `/epf-add-form` | `<ProcessorName> <FormName> [Synonym]` | Добавить управляемую форму к обработке |
|
||||
| `/epf-build` | `<ProcessorName>` | Собрать EPF из XML (через 1cv8.exe) |
|
||||
| `/epf-dump` | `<EpfFile>` | Разобрать EPF в XML (через 1cv8.exe) |
|
||||
| `/epf-bsp-init` | `<ProcessorName> <Вид>` | Добавить регистрацию БСП (СведенияОВнешнейОбработке) |
|
||||
| `/epf-bsp-add-command` | `<ProcessorName> <Идентификатор>` | Добавить команду в дополнительную обработку БСП |
|
||||
|
||||
Навыки удаления (`epf-remove-*`) не вызываются Claude автоматически — только по явной команде пользователя.
|
||||
## Внешние отчёты (ERF)
|
||||
|
||||
| Навык | Параметры | Описание |
|
||||
|-------|-----------|----------|
|
||||
| `/erf-init` | `<ReportName> [Synonym] [--WithSKD]` | Создать новый отчёт (корневой XML + модуль объекта + опционально СКД) |
|
||||
| `/erf-build` | `<ReportName>` | Собрать ERF из XML (через 1cv8.exe) |
|
||||
| `/erf-dump` | `<ErfFile>` | Разобрать ERF в XML (через 1cv8.exe) |
|
||||
|
||||
Флаг `--WithSKD` создаёт макет `ОсновнаяСхемаКомпоновкиДанных` и привязывает его к `MainDataCompositionSchema`.
|
||||
|
||||
## Универсальные навыки
|
||||
|
||||
Работают с любыми объектами — обработками, отчётами, справочниками, документами и др.
|
||||
|
||||
| Навык | Параметры | Описание |
|
||||
|-------|-----------|----------|
|
||||
| `/template-add` | `<ObjectName> <TemplateName> <TemplateType>` | Добавить макет (HTML, Text, SpreadsheetDocument, BinaryData, DataCompositionSchema) |
|
||||
| `/template-remove` | `<ObjectName> <TemplateName>` | Удалить макет |
|
||||
| `/help-add` | `<ObjectName>` | Добавить встроенную справку (Help.xml + HTML) |
|
||||
| `/form-remove` | `<ObjectName> <FormName>` | Удалить форму |
|
||||
|
||||
Для отчётов: при добавлении макета типа DataCompositionSchema автоматически заполняется `MainDataCompositionSchema` (если пуст).
|
||||
|
||||
Навыки удаления (`template-remove`, `form-remove`) не вызываются Claude автоматически — только по явной команде пользователя.
|
||||
|
||||
## Сценарии использования
|
||||
|
||||
@@ -43,13 +62,21 @@ Claude выполнит `/epf-init` и `/epf-add-form` с правильными
|
||||
|
||||
Claude создаст обработку, добавит макет SpreadsheetDocument, вызовет `/epf-bsp-init` с видом ПечатнаяФорма и назначением, сгенерирует `СведенияОВнешнейОбработке()` и процедуру `Печать()`.
|
||||
|
||||
### Внешний отчёт с СКД
|
||||
|
||||
```
|
||||
> Создай внешний отчёт ОстаткиНаСкладе с СКД
|
||||
```
|
||||
|
||||
Claude выполнит `/erf-init ОстаткиНаСкладе --WithSKD`, затем предложит заполнить схему компоновки через `/skd-compile`.
|
||||
|
||||
### Доработка существующей обработки
|
||||
|
||||
```
|
||||
> Добавь справку с описанием как пользоваться обработкой
|
||||
```
|
||||
|
||||
Claude вызовет `/epf-add-help` и предложит отредактировать HTML.
|
||||
Claude вызовет `/help-add` и предложит отредактировать HTML.
|
||||
|
||||
```
|
||||
> Добавь ещё одну команду печати — накладная
|
||||
@@ -61,7 +88,7 @@ Claude вызовет `/epf-bsp-add-command`, добавит команду в `
|
||||
> Собери
|
||||
```
|
||||
|
||||
Claude вызовет `/epf-build`.
|
||||
Claude вызовет `/epf-build` или `/erf-build` в зависимости от типа объекта.
|
||||
|
||||
### Примеры слеш-команд
|
||||
|
||||
@@ -70,9 +97,13 @@ Claude вызовет `/epf-build`.
|
||||
```
|
||||
> /epf-init МояОбработка "Моя обработка"
|
||||
> /epf-add-form МояОбработка Форма
|
||||
> /epf-add-template МояОбработка Макет HTML
|
||||
> /epf-add-help МояОбработка
|
||||
> /template-add МояОбработка Макет HTML
|
||||
> /help-add МояОбработка
|
||||
> /epf-build МояОбработка
|
||||
|
||||
> /erf-init МойОтчёт --WithSKD
|
||||
> /template-add МойОтчёт ДопМакет SpreadsheetDocument
|
||||
> /erf-build МойОтчёт
|
||||
```
|
||||
|
||||
## Структура каталогов
|
||||
@@ -87,7 +118,7 @@ src/
|
||||
└── ObjectModule.bsl # Модуль объекта
|
||||
```
|
||||
|
||||
После `/epf-add-form` и `/epf-add-template`:
|
||||
После `/epf-add-form` и `/template-add`:
|
||||
|
||||
```
|
||||
src/
|
||||
@@ -109,6 +140,21 @@ src/
|
||||
└── Template.html # Содержимое макета
|
||||
```
|
||||
|
||||
После `/erf-init МойОтчёт --WithSKD`:
|
||||
|
||||
```
|
||||
src/
|
||||
├── МойОтчёт.xml # Корневой файл (ExternalReport)
|
||||
└── МойОтчёт/
|
||||
├── Ext/
|
||||
│ └── ObjectModule.bsl # Модуль объекта
|
||||
└── Templates/
|
||||
├── ОсновнаяСхемаКомпоновкиДанных.xml
|
||||
└── ОсновнаяСхемаКомпоновкиДанных/
|
||||
└── Ext/
|
||||
└── Template.xml # Пустая СКД
|
||||
```
|
||||
|
||||
Первая добавленная форма автоматически становится основной (DefaultForm). Флаг `--main` нужен только для переназначения основной формы на другую.
|
||||
|
||||
## Технические детали
|
||||
@@ -116,13 +162,16 @@ src/
|
||||
- Все XML-файлы создаются в **UTF-8 с BOM** (как в реальных выгрузках 1С)
|
||||
- PowerShell-скрипты используют `System.Xml.XmlDocument` для модификации корневого XML
|
||||
- UUID генерируются через `[guid]::NewGuid()`
|
||||
- ClassId обработки фиксирован: `c3831ec8-d8d5-4f93-8a22-f9bfae07327f`
|
||||
- ClassId обработки: `c3831ec8-d8d5-4f93-8a22-f9bfae07327f`
|
||||
- ClassId отчёта: `e41aff26-25cf-4bb6-b6c1-3f478a75f374`
|
||||
- Порядок элементов в `ChildObjects`: TabularSections → Forms → Templates
|
||||
- Первая форма автоматически назначается основной (DefaultForm)
|
||||
- Навыки БСП (`epf-bsp-*`) не используют скрипты — Claude модифицирует код напрямую через Read/Edit
|
||||
- Для отчётов: `/template-add` с типом DataCompositionSchema автоматически заполняет `MainDataCompositionSchema`
|
||||
|
||||
## Спецификации
|
||||
|
||||
- [XML-формат выгрузки обработок](1c-xml-format-spec.md) — структура XML-файлов, namespace, элементы форм
|
||||
- [XML-формат выгрузки обработок](1c-epf-spec.md) — структура XML-файлов, namespace, элементы форм
|
||||
- [XML-формат внешних отчётов](1c-erf-spec.md) — отличия ERF от EPF, Properties, MainDataCompositionSchema
|
||||
- [Встроенная справка](1c-help-spec.md) — Help.xml, HTML-страницы, кнопка справки на форме
|
||||
- [Сборка и разборка EPF](build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
||||
- [Сборка и разборка EPF/ERF](build-spec.md) — команды `1cv8.exe`, параметры, коды возврата
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
||||
# Объекты метаданных конфигурации
|
||||
|
||||
Навыки группы `/meta-*` позволяют создавать, анализировать, редактировать и проверять объекты метаданных конфигурации 1С — справочники, документы, регистры, перечисления и ещё 19 типов объектов из XML-выгрузки.
|
||||
|
||||
## Навыки
|
||||
|
||||
| Навык | Параметры | Описание |
|
||||
|-------|-----------|----------|
|
||||
| `/meta-info` | `<ObjectPath> [-Mode] [-Name]` | Анализ структуры объекта: реквизиты, ТЧ, формы, движения, типы (8 режимов) |
|
||||
| `/meta-compile` | `<JsonPath> <OutputPath>` | Создание объекта метаданных из JSON DSL: реквизиты, ТЧ, свойства, формы |
|
||||
| `/meta-edit` | `<ObjectPath> -Operation <op> -Value "<val>"` | Точечное редактирование: 30+ атомарных операций (add/remove/modify/set) |
|
||||
| `/meta-validate` | `<ObjectPath> [-MaxErrors 20]` | Валидация структурной корректности: ~40 проверок |
|
||||
|
||||
## Рабочий цикл
|
||||
|
||||
```
|
||||
Описание объекта (текст) → JSON DSL → /meta-compile → XML-исходники → /meta-validate
|
||||
↕ /meta-edit → /meta-info
|
||||
```
|
||||
|
||||
1. Claude формирует JSON-определение объекта (тип, реквизиты, ТЧ, свойства)
|
||||
2. `/meta-compile` генерирует XML-исходники с корректными UUID, namespace, структурой ChildObjects
|
||||
3. `/meta-edit` вносит точечные изменения: добавление/удаление реквизитов, ТЧ, владельцев, движений и т.д.
|
||||
4. `/meta-validate` проверяет корректность XML
|
||||
5. `/meta-info` выводит компактную сводку для визуальной проверки
|
||||
|
||||
## Поддерживаемые типы объектов (23 типа)
|
||||
|
||||
| Группа | Типы |
|
||||
|--------|------|
|
||||
| Прикладные | Catalog, Document, Enum, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes |
|
||||
| Процессы | BusinessProcess, Task |
|
||||
| Регистры | InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister |
|
||||
| Отчёты/обработки | Report, DataProcessor |
|
||||
| Интеграция | ExchangePlan, HTTPService, WebService |
|
||||
| Журналы | DocumentJournal, Sequence |
|
||||
| Прочие | Constant, CommonModule, SessionParameter, FunctionalOption, DefinedType |
|
||||
|
||||
## Inline mode — типовые операции
|
||||
|
||||
### Реквизиты
|
||||
|
||||
```powershell
|
||||
# Добавить
|
||||
-Operation add-attribute -Value "Комментарий: Строка(200) ;; Сумма: Число(15,2) | req, index"
|
||||
|
||||
# С позиционной вставкой
|
||||
-Operation add-attribute -Value "Склад: CatalogRef.Склады >> after Организация"
|
||||
|
||||
# Удалить
|
||||
-Operation remove-attribute -Value "УстаревшийРеквизит"
|
||||
|
||||
# Переименовать + сменить тип
|
||||
-Operation modify-attribute -Value "СтароеИмя: name=НовоеИмя, type=Строка(500)"
|
||||
```
|
||||
|
||||
### Табличные части
|
||||
|
||||
```powershell
|
||||
# Создать ТЧ с реквизитами
|
||||
-Operation add-ts -Value "Товары: Ном: CatalogRef.Ном | req, Кол: Число(15,3), Цена: Число(15,2)"
|
||||
|
||||
# Добавить реквизит в существующую ТЧ
|
||||
-Operation add-ts-attribute -Value "Товары.СтавкаНДС: EnumRef.СтавкиНДС"
|
||||
|
||||
# Изменить свойства ТЧ
|
||||
-Operation modify-ts -Value "Товары: synonym=Товарный состав"
|
||||
```
|
||||
|
||||
### Свойства объекта
|
||||
|
||||
```powershell
|
||||
# Скалярные свойства
|
||||
-Operation modify-property -Value "CodeLength=11 ;; DescriptionLength=150"
|
||||
|
||||
# Владельцы справочника
|
||||
-Operation set-owners -Value "Catalog.Контрагенты ;; Catalog.Организации"
|
||||
|
||||
# Движения документа
|
||||
-Operation add-registerRecord -Value "AccumulationRegister.Продажи ;; AccumulationRegister.ОстаткиТоваров"
|
||||
```
|
||||
|
||||
### Регистры
|
||||
|
||||
```powershell
|
||||
-Operation add-dimension -Value "Организация: CatalogRef.Организации | master, mainFilter"
|
||||
-Operation add-resource -Value "Сумма: Число(15,2)"
|
||||
```
|
||||
|
||||
## JSON mode — комбинированные операции
|
||||
|
||||
Для сложных сценариев (несколько типов изменений в одном вызове) используйте JSON-файл:
|
||||
|
||||
```json
|
||||
{
|
||||
"add": {
|
||||
"attributes": ["Комментарий: Строка(200)"],
|
||||
"tabularSections": [{
|
||||
"name": "Товары",
|
||||
"attrs": ["Ном: CatalogRef.Ном | req", "Кол: Число(15,3)"]
|
||||
}]
|
||||
},
|
||||
"remove": { "attributes": ["УстаревшийРеквизит"] },
|
||||
"modify": {
|
||||
"properties": { "DescriptionLength": 150 },
|
||||
"attributes": { "СтароеИмя": { "name": "НовоеИмя" } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
JSON поддерживает русские синонимы ключей (`реквизиты`, `тч`, `измерения` и др.) и типов (`Строка`, `Число`, `СправочникСсылка` и др.).
|
||||
|
||||
## Сценарии использования
|
||||
|
||||
### Анализ существующего объекта
|
||||
|
||||
```
|
||||
> Покажи структуру справочника Catalogs/Номенклатура
|
||||
```
|
||||
|
||||
Claude вызовет `/meta-info` и покажет: реквизиты с типами, табличные части, формы, владельцев, ввод по строке.
|
||||
|
||||
### Создание нового объекта
|
||||
|
||||
```
|
||||
> Создай справочник Контрагенты: код 9, наименование 150, реквизиты ИНН(12), КПП(9),
|
||||
> иерархический с группами, владелец — Catalog.Организации
|
||||
```
|
||||
|
||||
Claude сформирует JSON и вызовет `/meta-compile` → `/meta-validate` → `/meta-info`.
|
||||
|
||||
### Добавление реквизитов к существующему объекту
|
||||
|
||||
```
|
||||
> Добавь в документ Documents/ЗаказКлиента реквизиты Склад (CatalogRef.Склады)
|
||||
> и ТЧ Товары с реквизитами Номенклатура, Количество, Цена, Сумма
|
||||
```
|
||||
|
||||
Claude вызовет `/meta-edit` дважды: `add-attribute` для реквизита и `add-ts` для ТЧ.
|
||||
|
||||
### Настройка движений документа
|
||||
|
||||
```
|
||||
> Документ Documents/ПриходТоваров должен делать движения
|
||||
> по AccumulationRegister.ОстаткиТоваров и AccumulationRegister.Партии
|
||||
```
|
||||
|
||||
Claude вызовет `/meta-edit` с операцией `set-registerRecords`.
|
||||
|
||||
### Проверка объекта после изменений
|
||||
|
||||
```
|
||||
> Проверь корректность Documents/ЗаказКлиента
|
||||
```
|
||||
|
||||
Claude вызовет `/meta-validate` и покажет ошибки и предупреждения.
|
||||
|
||||
## Структура файлов объекта метаданных
|
||||
|
||||
```
|
||||
<MetaType>/<ObjectName>/
|
||||
├── <ObjectName>.xml # Основной XML (Properties, ChildObjects)
|
||||
├── Ext/
|
||||
│ └── ObjectModule.bsl # Модуль объекта (опционально)
|
||||
├── Forms/
|
||||
│ └── <FormName>/ # Формы объекта
|
||||
├── Templates/
|
||||
│ └── <TemplateName>/ # Макеты
|
||||
└── Commands/
|
||||
└── <CommandName>/ # Команды
|
||||
```
|
||||
|
||||
## Спецификации
|
||||
|
||||
- [1c-config-objects-spec.md](1c-config-objects-spec.md) — XML-формат объектов метаданных, Properties, ChildObjects, типы
|
||||
- [meta-dsl-spec.md](meta-dsl-spec.md) — JSON DSL для описания объектов (`/meta-compile`)
|
||||
+1
-1
@@ -100,7 +100,7 @@ Claude вызовет `/mxl-validate`, который проверит инде
|
||||
Макет табличного документа — часть обработки. Типичный сценарий создания печатной формы:
|
||||
|
||||
1. `/epf-init` — создать обработку
|
||||
2. `/epf-add-template` — добавить макет SpreadsheetDocument (с именем `ПФ_MXL_...`)
|
||||
2. `/template-add` — добавить макет SpreadsheetDocument (с именем `ПФ_MXL_...`)
|
||||
3. `/mxl-compile` — заполнить макет содержимым из JSON-определения
|
||||
4. `/epf-bsp-init` — добавить регистрацию БСП с видом ПечатнаяФорма
|
||||
5. `/epf-bsp-add-command` — добавить команду печати
|
||||
|
||||
Reference in New Issue
Block a user