# form-add v1.5 — 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" [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::InputEncoding = [System.Text.Encoding]::UTF8 # --- Detect XML format version --- function Detect-FormatVersion([string]$dir) { $d = $dir while ($d) { $cfgPath = Join-Path $d "Configuration.xml" if (Test-Path $cfgPath) { $head = [System.IO.File]::ReadAllText($cfgPath, [System.Text.Encoding]::UTF8).Substring(0, [Math]::Min(2000, (Get-Item $cfgPath).Length)) if ($head -match ']+version="(\d+\.\d+)"') { return $Matches[1] } } $parent = Split-Path $d -Parent if ($parent -eq $d) { break } $d = $parent } return "2.17" } # --- Фаза 1: Определение типа объекта --- # Resolve ObjectPath (directory → .xml) 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" $sibling = Join-Path (Split-Path $ObjectPath) "$dirName.xml" if (Test-Path $candidate) { $ObjectPath = $candidate } elseif (Test-Path $sibling) { $ObjectPath = $sibling } } if (-not (Test-Path $ObjectPath)) { Write-Error "Файл объекта не найден: $ObjectPath" exit 1 } $objectXmlFull = Resolve-Path $ObjectPath $script:formatVersion = Detect-FormatVersion (Split-Path $objectXmlFull.Path -Parent) $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", "AccumulationRegister", "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() # ExtendedPresentation — only for DataProcessor, Report, ExternalDataProcessor, ExternalReport forms $extPresentationLine = "" if ($objectType -in $processorLikeTypes) { $extPresentationLine = "`n`t`t`t" } $formMetaXml = @"
$FormName ru $Synonym Managed false PlatformApplication MobilePlatformApplication $extPresentationLine
"@ [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" "AccumulationRegister" = "AccumulationRegisterRecordSet" } $mainAttrType = "$($attrTypeMap[$objectType]).$objectName" # SavedData: standard for Catalog/Document/etc, but not for processor-like (DataProcessor/Report/External*) $savedDataLine = "" if ($objectType -notin $processorLikeTypes) { $savedDataLine = "`n`t`t`ttrue" } $formXml = @"
true cfg:$mainAttrType true$savedDataLine "@ } 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 # Ищем первый