diff --git a/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1 b/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1 index 1a6a7314..98eb50c4 100644 --- a/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1 +++ b/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1 @@ -249,10 +249,12 @@ $script:generatedTypes = @{ @{ prefix = "DocumentJournalManager"; category = "Manager" } ) "Report" = @( - @{ prefix = "ReportObject"; category = "Object" } + @{ prefix = "ReportObject"; category = "Object" } + @{ prefix = "ReportManager"; category = "Manager" } ) "DataProcessor" = @( - @{ prefix = "DataProcessorObject"; category = "Object" } + @{ prefix = "DataProcessorObject"; category = "Object" } + @{ prefix = "DataProcessorManager"; category = "Manager" } ) } @@ -464,35 +466,96 @@ function Borrow-Form { [System.IO.File]::WriteAllText($formMetaFile, $formMetaSb.ToString(), $enc) Info " Created: $formMetaFile" - # 5. Generate Form.xml with BaseForm - # Extract inner content from source (everything between
and
) - $innerContent = "" - $formVersion = "2.17" - if ($srcFormContent -match '(?s)]*version="([^"]*)"[^>]*>(.*)') { - $formVersion = $Matches[1] - $innerContent = $Matches[2] - } elseif ($srcFormContent -match '(?s)]*>(.*)') { - $innerContent = $Matches[1] + # 5. Generate Form.xml with BaseForm (visual elements only) + # Parse source Form.xml as XmlDocument + $srcFormDoc = New-Object System.Xml.XmlDocument + $srcFormDoc.PreserveWhitespace = $true + $srcFormDoc.Load($srcFormXmlPath) + $srcFormEl = $srcFormDoc.DocumentElement + + $formVersion = $srcFormEl.GetAttribute("version") + if (-not $formVersion) { $formVersion = "2.17" } + + # Find direct children: AutoCommandBar, ChildItems (visual elements only) + $srcAutoCmd = $null + $srcChildItems = $null + foreach ($fc in $srcFormEl.ChildNodes) { + if ($fc.NodeType -ne 'Element') { continue } + if ($fc.LocalName -eq 'AutoCommandBar' -and -not $srcAutoCmd) { $srcAutoCmd = $fc } + elseif ($fc.LocalName -eq 'ChildItems' -and -not $srcChildItems) { $srcChildItems = $fc } } - # Build the extension Form.xml: resultant form + - $formXmlSb = New-Object System.Text.StringBuilder - # Copy the original XML declaration and
opening tag - if ($srcFormContent -match '(?s)^(.*?]*>)') { - $formXmlSb.Append($Matches[1]) | Out-Null + # Get OuterXml and strip redundant namespace redeclarations (they're on root ) + $nsStripPattern = '\s+xmlns(?::\w+)?="[^"]*"' + + $autoCmdXml = "" + if ($srcAutoCmd) { + $autoCmdXml = $srcAutoCmd.OuterXml + $autoCmdXml = [regex]::Replace($autoCmdXml, $nsStripPattern, '') + # Replace all CommandName values with 0 (base form buttons lose command refs) + $autoCmdXml = [regex]::Replace($autoCmdXml, '[^<]*', '0') + # Replace Autofill true → false + $autoCmdXml = $autoCmdXml -replace 'true', 'false' } - # Resultant form content (same as source initially) - $formXmlSb.Append($innerContent) | Out-Null - # BaseForm section - $formXmlSb.AppendLine("`t") | Out-Null - # Inner content for BaseForm (trim leading newline) - $baseInner = $innerContent.TrimStart("`r", "`n") - $formXmlSb.Append("`t") | Out-Null - $formXmlSb.Append($baseInner) | Out-Null - # Close BaseForm — ensure it's on its own line - $lastChar = $formXmlSb.ToString()[-1] - if ($lastChar -ne "`n") { $formXmlSb.AppendLine() | Out-Null } - $formXmlSb.AppendLine("`t") | Out-Null + + $childItemsXml = "" + if ($srcChildItems) { + $childItemsXml = $srcChildItems.OuterXml + $childItemsXml = [regex]::Replace($childItemsXml, $nsStripPattern, '') + # Replace all CommandName values with 0 in ChildItems too + $childItemsXml = [regex]::Replace($childItemsXml, '[^<]*', '0') + } else { + $childItemsXml = "" + } + + # Extract the opening tag from source text (preserves namespace declarations) + $xmlDecl = '' + $formTag = "" + if ($srcFormContent -match '(?s)^(<\?xml[^?]*\?>)') { $xmlDecl = $Matches[1] } + if ($srcFormContent -match '(]*>)') { $formTag = $Matches[1] } + + # Build output Form.xml + $formXmlSb = New-Object System.Text.StringBuilder + $formXmlSb.Append($xmlDecl) | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + $formXmlSb.Append($formTag) | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + + # Part 1: visual elements (add leading tab to first line of each block) + if ($autoCmdXml) { + $formXmlSb.Append("`t$autoCmdXml") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + } + $formXmlSb.Append("`t$childItemsXml") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + $formXmlSb.Append("`t") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + + # BaseForm: same visual elements, indented one more level + $formXmlSb.Append("`t") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + + if ($autoCmdXml) { + # Reindent for BaseForm: first line gets 2 tabs, other lines get +1 tab + $acLines = $autoCmdXml -split "`r`n" + for ($li = 0; $li -lt $acLines.Count; $li++) { + if ($li -eq 0) { $formXmlSb.Append("`t`t$($acLines[$li])") | Out-Null } + else { $formXmlSb.Append("`t$($acLines[$li])") | Out-Null } + $formXmlSb.Append("`r`n") | Out-Null + } + } + + $ciLines = $childItemsXml -split "`r`n" + for ($li = 0; $li -lt $ciLines.Count; $li++) { + if ($li -eq 0) { $formXmlSb.Append("`t`t$($ciLines[$li])") | Out-Null } + else { $formXmlSb.Append("`t$($ciLines[$li])") | Out-Null } + $formXmlSb.Append("`r`n") | Out-Null + } + + $formXmlSb.Append("`t`t") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null + $formXmlSb.Append("`t") | Out-Null + $formXmlSb.Append("`r`n") | Out-Null $formXmlSb.Append("") | Out-Null # Write Form.xml diff --git a/.claude/skills/cfe-init/SKILL.md b/.claude/skills/cfe-init/SKILL.md index 647035fb..c1c73602 100644 --- a/.claude/skills/cfe-init/SKILL.md +++ b/.claude/skills/cfe-init/SKILL.md @@ -1,7 +1,7 @@ --- name: cfe-init description: Создать расширение конфигурации 1С (CFE) — scaffold XML-исходников. Используй когда нужно создать новое расширение для исправления, доработки или дополнения конфигурации -argument-hint: [-Purpose Patch|Customization|AddOn] [-CompatibilityMode Version8_3_24] +argument-hint: [-ConfigPath ] [-Purpose Patch|Customization|AddOn] [-CompatibilityMode Version8_3_24] allowed-tools: - Bash - Read @@ -14,14 +14,14 @@ allowed-tools: ## Подготовка -Перед созданием расширения рекомендуется получить версию и режим совместимости базовой конфигурации: +Если есть выгрузка базовой конфигурации, передай `-ConfigPath` — скрипт автоматически определит `CompatibilityMode` и UUID языка из базовой конфигурации. + +Если `-ConfigPath` не задан, рекомендуется предварительно получить режим совместимости: ``` /cf-info -Mode brief ``` -Это даст `CompatibilityMode` (передать в `-CompatibilityMode`) и версию конфигурации (для `-Version`, например `<ВерсияКонфигурации>.1`). - ## Параметры | Параметр | Описание | По умолчанию | @@ -34,6 +34,7 @@ allowed-tools: | `Version` | Версия расширения | — | | `Vendor` | Поставщик | — | | `CompatibilityMode` | Режим совместимости | `Version8_3_24` | +| `ConfigPath` | Путь к выгрузке базовой конфигурации (авто-определяет CompatibilityMode и Language UUID) | — | | `NoRole` | Без основной роли | false | ## Команда @@ -56,7 +57,10 @@ powershell.exe -NoProfile -File .claude/skills/cfe-init/scripts/cfe-init.ps1 -Na ## Примеры ```powershell -# Расширение-исправление для ERP +# Расширение для ERP с авто-определением совместимости из базовой конфигурации +... -Name Расш1 -ConfigPath C:\WS\tasks\cfsrc\erp_8.3.24 -OutputDir src + +# Расширение-исправление с явным режимом совместимости ... -Name Расш1 -Purpose Patch -CompatibilityMode Version8_3_17 -OutputDir src # Расширение-доработка с версией diff --git a/.claude/skills/cfe-init/scripts/cfe-init.ps1 b/.claude/skills/cfe-init/scripts/cfe-init.ps1 index b8f449ad..a74efba3 100644 --- a/.claude/skills/cfe-init/scripts/cfe-init.ps1 +++ b/.claude/skills/cfe-init/scripts/cfe-init.ps1 @@ -11,6 +11,7 @@ param( [string]$Version, [string]$Vendor, [string]$CompatibilityMode = "Version8_3_24", + [string]$ConfigPath, [switch]$NoRole ) @@ -34,6 +35,57 @@ if (Test-Path $cfgFile) { exit 1 } +# --- Resolve ConfigPath --- +$baseLangUuid = "00000000-0000-0000-0000-000000000000" +if ($ConfigPath) { + 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 } + $cfgDir = Split-Path (Resolve-Path $ConfigPath).Path -Parent + + # 3a. Read Language UUID from base config + $baseLangFile = Join-Path (Join-Path $cfgDir "Languages") "Русский.xml" + if (Test-Path $baseLangFile) { + $baseLangDoc = New-Object System.Xml.XmlDocument + $baseLangDoc.PreserveWhitespace = $false + $baseLangDoc.Load($baseLangFile) + $langEl = $null + foreach ($c in $baseLangDoc.DocumentElement.ChildNodes) { + if ($c.NodeType -eq 'Element' -and $c.LocalName -eq 'Language') { $langEl = $c; break } + } + if ($langEl) { + $baseLangUuid = $langEl.GetAttribute("uuid") + Write-Host "[INFO] Base config Language UUID: $baseLangUuid" + } else { + Write-Host "[WARN] No element in $baseLangFile" + } + } else { + Write-Host "[WARN] Base config language not found: $baseLangFile" + } + + # 3b. Read CompatibilityMode from base config + $baseCfgDoc = New-Object System.Xml.XmlDocument + $baseCfgDoc.PreserveWhitespace = $false + $baseCfgDoc.Load((Resolve-Path $ConfigPath).Path) + $baseCfgNs = New-Object System.Xml.XmlNamespaceManager($baseCfgDoc.NameTable) + $baseCfgNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses") + $compatNode = $baseCfgDoc.SelectSingleNode("//md:Configuration/md:Properties/md:CompatibilityMode", $baseCfgNs) + if ($compatNode -and $compatNode.InnerText) { + $CompatibilityMode = $compatNode.InnerText.Trim() + Write-Host "[INFO] Base config CompatibilityMode: $CompatibilityMode" + } else { + Write-Host "[WARN] CompatibilityMode not found in base config, using default: $CompatibilityMode" + } +} else { + Write-Host "[WARN] Language ExtendedConfigurationObject set to zeros. Use -ConfigPath to auto-resolve from base config, or fix manually before loading." +} + # --- Generate UUIDs --- $uuidCfg = [guid]::NewGuid().ToString() $uuidLang = [guid]::NewGuid().ToString() @@ -149,7 +201,7 @@ $langXml = @" Adopted Русский - 00000000-0000-0000-0000-000000000000 + $baseLangUuid ru @@ -201,6 +253,7 @@ Write-Host "[OK] Создано расширение: $Name" Write-Host " Каталог: $OutputDir" Write-Host " Назначение: $Purpose" Write-Host " Префикс: $NamePrefix" +Write-Host " Совместимость: $CompatibilityMode" Write-Host " Configuration.xml: $cfgFile" Write-Host " Languages: $langFile" if (-not $NoRole) { diff --git a/.claude/skills/form-add/scripts/form-add.ps1 b/.claude/skills/form-add/scripts/form-add.ps1 index 871aad1b..dfb8dc1f 100644 --- a/.claude/skills/form-add/scripts/form-add.ps1 +++ b/.claude/skills/form-add/scripts/form-add.ps1 @@ -271,7 +271,11 @@ if ($Purpose -eq "List" -or $Purpose -eq "Choice") { "@ } -[System.IO.File]::WriteAllText($formXmlPath, $formXml, $encBom) +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 --- @@ -304,7 +308,11 @@ $moduleBsl = @" #КонецОбласти "@ -[System.IO.File]::WriteAllText($modulePath, $moduleBsl, $encBom) +if (Test-Path $modulePath) { + Write-Host "[SKIP] Module.bsl already exists: $modulePath — not overwriting" +} else { + [System.IO.File]::WriteAllText($modulePath, $moduleBsl, $encBom) +} # --- Фаза 4: Регистрация в родительском объекте --- diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 943716e1..09ebcf5e 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -478,10 +478,12 @@ $script:generatedTypes = @{ @{ prefix = "DocumentJournalManager"; category = "Manager" } ) "Report" = @( - @{ prefix = "ReportObject"; category = "Object" } + @{ prefix = "ReportObject"; category = "Object" } + @{ prefix = "ReportManager"; category = "Manager" } ) "DataProcessor" = @( - @{ prefix = "DataProcessorObject"; category = "Object" } + @{ prefix = "DataProcessorObject"; category = "Object" } + @{ prefix = "DataProcessorManager"; category = "Manager" } ) } diff --git a/docs/1c-config-objects-spec.md b/docs/1c-config-objects-spec.md index e23a44f5..c70b4ece 100644 --- a/docs/1c-config-objects-spec.md +++ b/docs/1c-config-objects-spec.md @@ -257,8 +257,8 @@ Ext/ # Расширение конфигураци | Task | Object, Ref, Selection, List, Manager | | ExchangePlan | Object, Ref, Selection, List, Manager | | DocumentJournal | Selection, List, Manager | -| Report | Object | -| DataProcessor | Object | +| Report | Object, Manager | +| DataProcessor | Object, Manager | Формат имени: `{ТипОбъектаEng}.{ИмяОбъекта}` (напр. `CatalogObject.Номенклатура`, `DocumentRef.АвансовыйОтчет`). diff --git a/docs/1c-extension-spec.md b/docs/1c-extension-spec.md index 43c00156..465ee2f4 100644 --- a/docs/1c-extension-spec.md +++ b/docs/1c-extension-spec.md @@ -356,7 +356,7 @@ Enums/ # Перечисления #### 5.4.2. Структура Form.xml заимствованной формы -Form.xml заимствованной формы — **двухчастный файл**: +Form.xml заимствованной формы — **двухчастный файл**: Part 1 (результирующая форма) и BaseForm (исходная форма). Обе части содержат **только визуальные элементы** — атрибуты, события, параметры и команды базовой конфигурации **НЕ включаются**. ```xml
@@ -364,38 +364,49 @@ Form.xml заимствованной формы — **двухчастный ф - - ... - + + + + + + + + Расш1_ПриСозданииПосле - - - - - ... - + Расш1_НоваяКомандаВместо - + - ... + + + + + + - + - - ... + + @@ -403,11 +414,13 @@ Form.xml заимствованной формы — **двухчастный ф **Ключевые правила:** -1. **Часть 1** (до ``) — **результирующая форма**, содержит ВСЕ элементы: и базовые, и добавленные расширением. Именно эта часть определяет итоговую форму при запуске. +1. **Часть 1** (до ``) — **результирующая форма**. Содержит визуальные элементы (AutoCommandBar + ChildItems) из базовой конфигурации плюс элементы расширения. Атрибуты базовой конфигурации (DynamicList, QueryText и др.) **не включаются** — только реквизиты расширения (id ≥ 1000000) или пустой ``. Events и Commands — только добавленные расширением (с `callType`). -2. **Часть 2** (``) — **полная копия оригинальной формы** из базовой конфигурации. Используется платформой для контроля совместимости — при обновлении конфигурации платформа сравнивает `` с текущей формой конфигурации и предупреждает о расхождениях. +2. **Часть 2** (``) — **визуальный снимок исходной формы**. Содержит только AutoCommandBar + ChildItems + пустой ``. НЕ содержит Events, Commands, Parameters. Все `` в кнопках заменены на `0`. Платформа использует BaseForm для контроля совместимости при обновлении конфигурации. -3. Элемент `` всегда идёт **последним** в `
` и имеет атрибут `version`. +3. **Правило `0`**: во всех кнопках базовой формы (как в Part 1, так и в BaseForm) значение `` заменяется на `0`. Ссылки на команды конфигурации не сохраняются. Только кнопки, добавленные расширением, сохраняют ссылку на команду (напр. `Form.Command.XXX`). + +4. Элемент `` всегда идёт **последним** в `` и имеет атрибут `version`. #### 5.4.3. Нумерация ID элементов