From 250978c2fdd3c22ae25b8678ae8ee729952bc2dc Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 28 Mar 2026 17:25:52 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20resolve=20FINDINGS=20=E2=80=94=20synonym?= =?UTF-8?q?s,=20path=20resolution,=20exit=20codes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skill fixes (all ps1+py, version bumped to v1.1): - role-compile: accept "rights" as synonym for "objects" - subsystem-compile: accept "objects" as synonym for "content" - form-add: resolve directory path to .xml (like meta-validate) - form-info: resolve directory path to Ext/Form.xml (like form-validate) - mxl-compile: support absolute OutputPath (IsPathRooted check) - meta-remove: exit 1 when object not found; v1.1 - cfe-patch-method: accept plural type names (Catalogs → Catalog) Test fixes: - basic-role.json: use canonical "objects" key - basic.json (subsystem): use canonical "content" key - catalog-form.json: fix outdated DSL format - New synonym test cases for role-compile and subsystem-compile - error-not-found.json: expect error (exit 1) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../scripts/cfe-patch-method.ps1 | 410 ++--- .../scripts/cfe-patch-method.py | 18 +- .claude/skills/form-add/scripts/form-add.ps1 | 908 +++++----- .claude/skills/form-add/scripts/form-add.py | 13 +- .../skills/form-info/scripts/form-info.ps1 | 1180 ++++++------- .claude/skills/form-info/scripts/form-info.py | 24 +- .../meta-remove/scripts/meta-remove.ps1 | 970 +++++------ .../skills/meta-remove/scripts/meta-remove.py | 15 +- .../mxl-compile/scripts/mxl-compile.ps1 | 1465 +++++++++-------- .../skills/mxl-compile/scripts/mxl-compile.py | 2 +- .../role-compile/scripts/role-compile.ps1 | 1433 ++++++++-------- .../role-compile/scripts/role-compile.py | 2 +- .../scripts/subsystem-compile.ps1 | 679 ++++---- .../scripts/subsystem-compile.py | 2 +- .../cases/form-compile/catalog-form.json | 4 +- .../Товары/Forms/ФормаЭлемента/Ext/Form.xml | 24 +- .../snapshots/basic/Subsystems/Склад.xml | 4 +- .../snapshots/valid/Subsystems/Склад.xml | 4 +- .../cases/meta-remove/error-not-found.json | 1 + .../skills/cases/role-compile/basic-role.json | 4 +- .../basic-role/Кладовщик/Ext/Rights.xml | 7 + .../synonym-rights/Catalogs/Товары.xml | 327 ++++ .../Catalogs/Товары/Ext/ObjectModule.bsl | 0 .../synonym-rights/Configuration.xml | 252 +++ .../synonym-rights/Languages/Русский.xml | 16 + .../snapshots/synonym-rights/Кладовщик.xml | 32 + .../synonym-rights/Кладовщик/Ext/Rights.xml | 16 + .../cases/role-compile/synonym-rights.json | 19 + .../skills/cases/subsystem-compile/basic.json | 2 +- .../snapshots/basic/Subsystems/Склад.xml | 4 +- .../synonym-objects/Catalogs/Товары.xml | 327 ++++ .../Catalogs/Товары/Ext/ObjectModule.bsl | 0 .../synonym-objects/Configuration.xml | 253 +++ .../synonym-objects/Languages/Русский.xml | 16 + .../synonym-objects/Subsystems/Склад.xml | 24 + .../subsystem-compile/synonym-objects.json | 18 + .../snapshots/overview/Subsystems/Склад.xml | 4 +- .../snapshots/valid/Subsystems/Склад.xml | 4 +- 38 files changed, 4975 insertions(+), 3508 deletions(-) create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Catalogs/Товары.xml create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Catalogs/Товары/Ext/ObjectModule.bsl create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Configuration.xml create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Languages/Русский.xml create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Кладовщик.xml create mode 100644 tests/skills/cases/role-compile/snapshots/synonym-rights/Кладовщик/Ext/Rights.xml create mode 100644 tests/skills/cases/role-compile/synonym-rights.json create mode 100644 tests/skills/cases/subsystem-compile/snapshots/synonym-objects/Catalogs/Товары.xml create mode 100644 tests/skills/cases/subsystem-compile/snapshots/synonym-objects/Catalogs/Товары/Ext/ObjectModule.bsl create mode 100644 tests/skills/cases/subsystem-compile/snapshots/synonym-objects/Configuration.xml create mode 100644 tests/skills/cases/subsystem-compile/snapshots/synonym-objects/Languages/Русский.xml create mode 100644 tests/skills/cases/subsystem-compile/snapshots/synonym-objects/Subsystems/Склад.xml create mode 100644 tests/skills/cases/subsystem-compile/synonym-objects.json diff --git a/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.ps1 b/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.ps1 index d101d326..96324c3b 100644 --- a/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.ps1 +++ b/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.ps1 @@ -1,201 +1,209 @@ -# 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 form borrowing for .Form. paths --- -if ($parts.Count -ge 4 -and $parts[2] -eq "Form") { - $formName = $parts[3] - $dirName = $typeDirMap[$objType] - $formMetaFile = Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Forms") "${formName}.xml" - $formXmlFile = Join-Path (Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Forms") $formName) "Ext/Form.xml" - - if (-not (Test-Path $formMetaFile) -or -not (Test-Path $formXmlFile)) { - Write-Host "[WARN] Form '$formName' metadata or Form.xml not found in extension." - Write-Host " Run /cfe-borrow first:" - Write-Host " /cfe-borrow -ExtensionPath $ExtensionPath -ConfigPath -Object `"$objType.$objName.Form.$formName`"" - Write-Host "" - } -} - -# --- 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" +# cfe-patch-method v1.1 — 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" + "Catalogs"="Catalogs"; "Documents"="Documents"; "Enums"="Enums" + "CommonModules"="CommonModules"; "Reports"="Reports"; "DataProcessors"="DataProcessors" + "ExchangePlans"="ExchangePlans"; "ChartsOfAccounts"="ChartsOfAccounts" + "ChartsOfCharacteristicTypes"="ChartsOfCharacteristicTypes" + "ChartsOfCalculationTypes"="ChartsOfCalculationTypes" + "BusinessProcesses"="BusinessProcesses"; "Tasks"="Tasks" + "InformationRegisters"="InformationRegisters"; "AccumulationRegisters"="AccumulationRegisters" + "AccountingRegisters"="AccountingRegisters"; "CalculationRegisters"="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 form borrowing for .Form. paths --- +if ($parts.Count -ge 4 -and $parts[2] -eq "Form") { + $formName = $parts[3] + $dirName = $typeDirMap[$objType] + $formMetaFile = Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Forms") "${formName}.xml" + $formXmlFile = Join-Path (Join-Path (Join-Path (Join-Path (Join-Path $ExtensionPath $dirName) $objName) "Forms") $formName) "Ext/Form.xml" + + if (-not (Test-Path $formMetaFile) -or -not (Test-Path $formXmlFile)) { + Write-Host "[WARN] Form '$formName' metadata or Form.xml not found in extension." + Write-Host " Run /cfe-borrow first:" + Write-Host " /cfe-borrow -ExtensionPath $ExtensionPath -ConfigPath -Object `"$objType.$objName.Form.$formName`"" + Write-Host "" + } +} + +# --- 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" diff --git a/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.py b/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.py index 490fb938..5a1ac770 100644 --- a/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.py +++ b/.claude/skills/cfe-patch-method/scripts/cfe-patch-method.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# cfe-patch-method v1.0 — Generate method interceptor for 1C extension (CFE) +# cfe-patch-method v1.1 — Generate method interceptor for 1C extension (CFE) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -84,6 +84,22 @@ def main(): "AccumulationRegister": "AccumulationRegisters", "AccountingRegister": "AccountingRegisters", "CalculationRegister": "CalculationRegisters", + "Catalogs": "Catalogs", + "Documents": "Documents", + "Enums": "Enums", + "CommonModules": "CommonModules", + "Reports": "Reports", + "DataProcessors": "DataProcessors", + "ExchangePlans": "ExchangePlans", + "ChartsOfAccounts": "ChartsOfAccounts", + "ChartsOfCharacteristicTypes": "ChartsOfCharacteristicTypes", + "ChartsOfCalculationTypes": "ChartsOfCalculationTypes", + "BusinessProcesses": "BusinessProcesses", + "Tasks": "Tasks", + "InformationRegisters": "InformationRegisters", + "AccumulationRegisters": "AccumulationRegisters", + "AccountingRegisters": "AccountingRegisters", + "CalculationRegisters": "CalculationRegisters", } parts = module_path.split(".") diff --git a/.claude/skills/form-add/scripts/form-add.ps1 b/.claude/skills/form-add/scripts/form-add.ps1 index dfb8dc1f..7a640df5 100644 --- a/.claude/skills/form-add/scripts/form-add.ps1 +++ b/.claude/skills/form-add/scripts/form-add.ps1 @@ -1,448 +1,460 @@ -# form-add v1.0 — Add managed form to 1C config object -# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills -param( - [Parameter(Mandatory)] - [string]$ObjectPath, - - [Parameter(Mandatory)] - [string]$FormName, - - [string]$Synonym = $FormName, - - [string]$Purpose = "Object", - - [switch]$SetDefault -) - -$ErrorActionPreference = "Stop" - -# --- Фаза 1: Определение типа объекта --- - -if (-not (Test-Path $ObjectPath)) { - Write-Error "Файл объекта не найден: $ObjectPath" - exit 1 -} - -$objectXmlFull = Resolve-Path $ObjectPath -$xmlDoc = New-Object System.Xml.XmlDocument -$xmlDoc.PreserveWhitespace = $true -$xmlDoc.Load($objectXmlFull.Path) - -$nsMgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) -$nsMgr.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") -$nsMgr.AddNamespace("v8", "http://v8.1c.ru/8.1/data/core") - -# Определяем тип объекта по корневому тегу внутри MetaDataObject -$metaDataObject = $xmlDoc.SelectSingleNode("//md:MetaDataObject", $nsMgr) -if (-not $metaDataObject) { - # Пробуем без namespace (fallback) - $metaDataObject = $xmlDoc.DocumentElement -} - -$supportedTypes = @( - "Document", "Catalog", "DataProcessor", "Report", - "ExternalDataProcessor", "ExternalReport", - "InformationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes", - "ExchangePlan", "BusinessProcess", "Task" -) - -$objectType = $null -$objectNode = $null -foreach ($t in $supportedTypes) { - $node = $xmlDoc.SelectSingleNode("//md:$t", $nsMgr) - if ($node) { - $objectType = $t - $objectNode = $node - break - } -} - -if (-not $objectType) { - Write-Error "Не удалось определить тип объекта. Поддерживаемые типы: $($supportedTypes -join ', ')" - exit 1 -} - -# Имя объекта из Properties/Name -$objectName = $xmlDoc.SelectSingleNode("//md:${objectType}/md:Properties/md:Name", $nsMgr).InnerText -if (-not $objectName) { - Write-Error "Не удалось определить имя объекта из Properties/Name" - exit 1 -} - -Write-Host "" -Write-Host "=== form-add ===" -Write-Host "" -Write-Host "Object: $objectType.$objectName" - -# --- Фаза 2: Валидация Purpose --- - -$Purpose = $Purpose.Substring(0,1).ToUpper() + $Purpose.Substring(1).ToLower() -# Нормализация -switch ($Purpose) { - "Object" { } - "List" { } - "Choice" { } - "Record" { } - default { - Write-Error "Недопустимое назначение: $Purpose. Допустимые: Object, List, Choice, Record" - exit 1 - } -} - -$objectLikeTypes = @("Document", "Catalog", "ChartOfAccounts", "ChartOfCharacteristicTypes", "ExchangePlan", "BusinessProcess", "Task") -$processorLikeTypes = @("DataProcessor", "Report", "ExternalDataProcessor", "ExternalReport") - -switch ($Purpose) { - "Object" { - # допустимо для всех типов - } - "List" { - if ($objectType -eq "DataProcessor") { - Write-Error "Purpose=List недопустим для DataProcessor" - exit 1 - } - } - "Choice" { - if ($objectType -in $processorLikeTypes -or $objectType -eq "InformationRegister") { - Write-Error "Purpose=Choice недопустим для $objectType" - exit 1 - } - } - "Record" { - if ($objectType -ne "InformationRegister") { - Write-Error "Purpose=Record допустим только для InformationRegister" - exit 1 - } - } -} - -# --- Фаза 3: Создание файлов --- - -$objectDir = [System.IO.Path]::ChangeExtension($objectXmlFull.Path, $null).TrimEnd('.') -$formsDir = Join-Path $objectDir "Forms" -$formMetaPath = Join-Path $formsDir "$FormName.xml" - -if (Test-Path $formMetaPath) { - Write-Error "Форма уже существует: $formMetaPath" - exit 1 -} - -$formDir = Join-Path $formsDir $FormName -$formExtDir = Join-Path $formDir "Ext" -$formModuleDir = Join-Path $formExtDir "Form" - -New-Item -ItemType Directory -Path $formModuleDir -Force | Out-Null - -$encBom = New-Object System.Text.UTF8Encoding($true) - -# --- 3a. Метаданные формы --- - -$formUuid = [guid]::NewGuid().ToString() - -$formMetaXml = @" - - -
- - $FormName - - - ru - $Synonym - - - - Managed - false - - PlatformApplication - MobilePlatformApplication - - - -
-
-"@ - -[System.IO.File]::WriteAllText($formMetaPath, $formMetaXml, $encBom) - -# --- 3b. Form.xml --- - -$formXmlPath = Join-Path $formExtDir "Form.xml" - -$formNsDecl = 'xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" 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: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: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"' - -if ($Purpose -eq "List" -or $Purpose -eq "Choice") { - # Динамический список - # MainTable: тип.имя - $mainTable = "$objectType.$objectName" - - $formXml = @" - -
- - true - - - ПриСозданииНаСервере - - - - - - cfg:DynamicList - - true - - $mainTable - - - - -"@ -} elseif ($Purpose -eq "Record") { - # Запись регистра сведений - $mainAttrName = "Запись" - $mainAttrType = "InformationRegisterRecordManager.$objectName" - - $formXml = @" - -
- - true - - - ПриСозданииНаСервере - - - - - - cfg:$mainAttrType - - true - true - - - -"@ -} else { - # Object — форма объекта - $mainAttrName = "Объект" - - # Маппинг типа объекта на тип реквизита - $attrTypeMap = @{ - "Document" = "DocumentObject" - "Catalog" = "CatalogObject" - "DataProcessor" = "DataProcessorObject" - "Report" = "ReportObject" - "ExternalDataProcessor" = "ExternalDataProcessorObject" - "ExternalReport" = "ExternalReportObject" - "ChartOfAccounts" = "ChartOfAccountsObject" - "ChartOfCharacteristicTypes" = "ChartOfCharacteristicTypesObject" - "ExchangePlan" = "ExchangePlanObject" - "BusinessProcess" = "BusinessProcessObject" - "Task" = "TaskObject" - "InformationRegister" = "InformationRegisterRecordManager" - } - - $mainAttrType = "$($attrTypeMap[$objectType]).$objectName" - - $formXml = @" - -
- - true - - - ПриСозданииНаСервере - - - - - - cfg:$mainAttrType - - true - true - - - -"@ -} - -if (Test-Path $formXmlPath) { - Write-Host "[SKIP] Form.xml already exists: $formXmlPath — not overwriting" -} else { - [System.IO.File]::WriteAllText($formXmlPath, $formXml, $encBom) -} - -# --- 3c. Module.bsl --- - -$modulePath = Join-Path $formModuleDir "Module.bsl" - -$moduleBsl = @" -#Область ОбработчикиСобытийФормы - -&НаСервере -Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) - -КонецПроцедуры - -#КонецОбласти - -#Область ОбработчикиСобытийЭлементовФормы - -#КонецОбласти - -#Область ОбработчикиКомандФормы - -#КонецОбласти - -#Область ОбработчикиОповещений - -#КонецОбласти - -#Область СлужебныеПроцедурыИФункции - -#КонецОбласти -"@ - -if (Test-Path $modulePath) { - Write-Host "[SKIP] Module.bsl already exists: $modulePath — not overwriting" -} else { - [System.IO.File]::WriteAllText($modulePath, $moduleBsl, $encBom) -} - -# --- Фаза 4: Регистрация в родительском объекте --- - -$childObjects = $xmlDoc.SelectSingleNode("//md:${objectType}/md:ChildObjects", $nsMgr) -if (-not $childObjects) { - Write-Error "Не найден элемент ChildObjects в $ObjectPath" - exit 1 -} - -# Добавить
$FormName
-$formElem = $xmlDoc.CreateElement("Form", "http://v8.1c.ru/8.3/MDClasses") -$formElem.InnerText = $FormName - -# Ищем первый