diff --git a/.claude/skills/epf-build/SKILL.md b/.claude/skills/epf-build/SKILL.md index 58e1419e..3cd3cf72 100644 --- a/.claude/skills/epf-build/SKILL.md +++ b/.claude/skills/epf-build/SKILL.md @@ -25,14 +25,17 @@ allowed-tools: | SrcDir | нет | `src` | Каталог исходников | | OutDir | нет | `build` | Каталог для результата | -## Параметры подключения +## Параметры подключения (опционально) + +Предпочтительно использовать конкретную базу — это надёжнее и не требует создания временной базы. + +1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую +3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` +4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` +5. Если ветка не совпала — используй `default` +6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную базу. Для EPF со ссылочными типами (CatalogRef, DocumentRef и т.д.) генерируются заглушки метаданных. Временная база удаляется после сборки. -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу для сборки: -1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую -2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` -3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` -4. Если ветка не совпала — используй `default` -5. Если `.v8-project.json` нет или баз нет — создай пустую ИБ в `./base` Если `v8path` не задан — автоопределение: `Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort -Desc | Select -First 1` Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. @@ -55,7 +58,7 @@ powershell.exe -NoProfile -File .claude/skills/epf-build/scripts/epf-build.ps1 < | `-SourceFile <путь>` | да | Путь к корневому XML-файлу исходников | | `-OutputFile <путь>` | да | Путь к выходному EPF/ERF-файлу | -> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` +> `*` — опционально. Если не указано — автоматически создаётся временная база со заглушками метаданных ## Коды возврата @@ -64,10 +67,6 @@ powershell.exe -NoProfile -File .claude/skills/epf-build/scripts/epf-build.ps1 < | 0 | Успешная сборка | | 1 | Ошибка (см. лог) | -## Ссылочные типы - -Если обработка использует ссылочные типы конфигурации (`CatalogRef.XXX`, `DocumentRef.XXX`) — сборка в пустой базе упадёт с ошибкой XDTO. Зарегистрируй базу с целевой конфигурацией через `/db-list add`. - ## Примеры ```powershell diff --git a/.claude/skills/epf-build/scripts/epf-build.ps1 b/.claude/skills/epf-build/scripts/epf-build.ps1 index 36e92486..d099615d 100644 --- a/.claude/skills/epf-build/scripts/epf-build.ps1 +++ b/.claude/skills/epf-build/scripts/epf-build.ps1 @@ -87,10 +87,21 @@ if (-not (Test-Path $V8Path)) { exit 1 } -# --- Validate connection --- +# --- Auto-create stub database if no connection specified --- +$autoCreatedBase = $null if (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) { - Write-Host "Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef" -ForegroundColor Red - exit 1 + $sourceDir = Split-Path $SourceFile -Parent + $autoBasePath = Join-Path $env:TEMP "epf_stub_db_$(Get-Random)" + $stubScript = Join-Path $PSScriptRoot "stub-db-create.ps1" + Write-Host "No database specified. Creating temporary stub database..." + $stubArgs = "-SourceDir `"$sourceDir`" -V8Path `"$V8Path`" -TempBasePath `"$autoBasePath`"" + $stubProc = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -File `"$stubScript`" $stubArgs" -NoNewWindow -Wait -PassThru + if ($stubProc.ExitCode -ne 0) { + Write-Host "Error: failed to create stub database" -ForegroundColor Red + exit 1 + } + $InfoBasePath = $autoBasePath + $autoCreatedBase = $autoBasePath } # --- Validate source file --- @@ -156,4 +167,7 @@ try { if (Test-Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } + if ($autoCreatedBase -and (Test-Path $autoCreatedBase)) { + Remove-Item -Path $autoCreatedBase -Recurse -Force -ErrorAction SilentlyContinue + } } diff --git a/.claude/skills/epf-build/scripts/epf-build.py b/.claude/skills/epf-build/scripts/epf-build.py index 69ab174e..c4ff7d11 100644 --- a/.claude/skills/epf-build/scripts/epf-build.py +++ b/.claude/skills/epf-build/scripts/epf-build.py @@ -52,10 +52,22 @@ def main(): # --- Resolve V8Path --- v8path = resolve_v8path(args.V8Path) - # --- Validate connection --- + # --- Auto-create stub database if no connection specified --- + auto_created_base = None if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef): - print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr) - sys.exit(1) + source_dir = os.path.dirname(os.path.abspath(args.SourceFile)) + auto_base_path = os.path.join(tempfile.gettempdir(), f"epf_stub_db_{random.randint(0, 999999)}") + stub_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), "stub-db-create.py") + print("No database specified. Creating temporary stub database...") + result = subprocess.run( + [sys.executable, stub_script, "-SourceDir", source_dir, "-V8Path", v8path, "-TempBasePath", auto_base_path], + capture_output=False, + ) + if result.returncode != 0: + print("Error: failed to create stub database", file=sys.stderr) + sys.exit(1) + args.InfoBasePath = auto_base_path + auto_created_base = auto_base_path # --- Validate source file --- if not os.path.isfile(args.SourceFile): @@ -123,6 +135,8 @@ def main(): finally: if os.path.exists(temp_dir): shutil.rmtree(temp_dir, ignore_errors=True) + if auto_created_base and os.path.exists(auto_created_base): + shutil.rmtree(auto_created_base, ignore_errors=True) if __name__ == "__main__": diff --git a/.claude/skills/epf-build/scripts/stub-db-create.ps1 b/.claude/skills/epf-build/scripts/stub-db-create.ps1 new file mode 100644 index 00000000..c8723f13 --- /dev/null +++ b/.claude/skills/epf-build/scripts/stub-db-create.ps1 @@ -0,0 +1,1124 @@ +# stub-db-create v1.0 — Create temp 1C infobase with metadata stubs for EPF/ERF build +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +param( + [Parameter(Mandatory)] + [string]$SourceDir, + + [Parameter(Mandatory)] + [string]$V8Path, + + [string]$TempBasePath +) + +$ErrorActionPreference = "Stop" +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- 1. Scan XML files for reference types --- + +$typeMap = @{} # MetadataType -> @(Name1, Name2, ...) + +$xmlFiles = Get-ChildItem -Path $SourceDir -Filter "*.xml" -Recurse -File +foreach ($f in $xmlFiles) { + $content = [System.IO.File]::ReadAllText($f.FullName, [System.Text.Encoding]::UTF8) + + # Ref types: cfg:CatalogRef.XXX or d5p1:CatalogRef.XXX (and similar depth prefixes d4p1, d3p1, etc.) + $refPattern = '(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.([A-Za-z\u0400-\u04FF\d_]+)' + foreach ($m in [regex]::Matches($content, $refPattern)) { + $prefix = $m.Groups[1].Value + $name = $m.Groups[2].Value + $metaType = switch ($prefix) { + "CatalogRef" { "Catalog" } + "DocumentRef" { "Document" } + "EnumRef" { "Enum" } + "ChartOfAccountsRef" { "ChartOfAccounts" } + "ChartOfCharacteristicTypesRef" { "ChartOfCharacteristicTypes" } + "ChartOfCalculationTypesRef" { "ChartOfCalculationTypes" } + "ExchangePlanRef" { "ExchangePlan" } + "BusinessProcessRef" { "BusinessProcess" } + "TaskRef" { "Task" } + } + if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} } + $typeMap[$metaType][$name] = $true + } + + # Object types: cfg:CatalogObject.XXX etc. + $objPattern = '(?:cfg:|d\dp1:)(CatalogObject|DocumentObject|ChartOfAccountsObject|ChartOfCharacteristicTypesObject|ChartOfCalculationTypesObject|ExchangePlanObject|BusinessProcessObject|TaskObject)\.([A-Za-z\u0400-\u04FF\d_]+)' + foreach ($m in [regex]::Matches($content, $objPattern)) { + $prefix = $m.Groups[1].Value + $name = $m.Groups[2].Value + $metaType = switch ($prefix) { + "CatalogObject" { "Catalog" } + "DocumentObject" { "Document" } + "ChartOfAccountsObject" { "ChartOfAccounts" } + "ChartOfCharacteristicTypesObject" { "ChartOfCharacteristicTypes" } + "ChartOfCalculationTypesObject" { "ChartOfCalculationTypes" } + "ExchangePlanObject" { "ExchangePlan" } + "BusinessProcessObject" { "BusinessProcess" } + "TaskObject" { "Task" } + } + if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} } + $typeMap[$metaType][$name] = $true + } + + # RecordSet types: cfg:InformationRegisterRecordSet.XXX etc. + $rsPattern = '(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet|AccountingRegisterRecordSet|CalculationRegisterRecordSet)\.([A-Za-z\u0400-\u04FF\d_]+)' + foreach ($m in [regex]::Matches($content, $rsPattern)) { + $prefix = $m.Groups[1].Value + $name = $m.Groups[2].Value + $metaType = switch ($prefix) { + "InformationRegisterRecordSet" { "InformationRegister" } + "AccumulationRegisterRecordSet" { "AccumulationRegister" } + "AccountingRegisterRecordSet" { "AccountingRegister" } + "CalculationRegisterRecordSet" { "CalculationRegister" } + } + if (-not $typeMap.ContainsKey($metaType)) { $typeMap[$metaType] = @{} } + $typeMap[$metaType][$name] = $true + } + + # Characteristic TypeSet: cfg:Characteristic.XXX + $charPattern = 'cfg:Characteristic\.([A-Za-z\u0400-\u04FF\d_]+)' + foreach ($m in [regex]::Matches($content, $charPattern)) { + $name = $m.Groups[1].Value + if (-not $typeMap.ContainsKey("ChartOfCharacteristicTypes")) { $typeMap["ChartOfCharacteristicTypes"] = @{} } + $typeMap["ChartOfCharacteristicTypes"][$name] = $true + } + + # DefinedType TypeSet: cfg:DefinedType.XXX + $dtPattern = 'cfg:DefinedType\.([A-Za-z\u0400-\u04FF\d_]+)' + foreach ($m in [regex]::Matches($content, $dtPattern)) { + $name = $m.Groups[1].Value + if (-not $typeMap.ContainsKey("DefinedType")) { $typeMap["DefinedType"] = @{} } + $typeMap["DefinedType"][$name] = $true + } +} + +$hasRefTypes = $typeMap.Count -gt 0 + +# --- 2. Determine TempBasePath --- +if (-not $TempBasePath) { + $TempBasePath = Join-Path $env:TEMP "epf_stub_db_$(Get-Random)" +} + +# --- 3. If registers need a registrator, add stub document --- +$registratorTypes = @("AccumulationRegister","AccountingRegister","CalculationRegister") +$needsRegistrator = $false +foreach ($rt in $registratorTypes) { + if ($typeMap.ContainsKey($rt) -and $typeMap[$rt].Count -gt 0) { + $needsRegistrator = $true + break + } +} +if ($needsRegistrator) { + if (-not $typeMap.ContainsKey("Document")) { $typeMap["Document"] = @{} } + $typeMap["Document"]["ЗаглушкаРегистратора"] = $true +} + +# --- 4. Generate configuration XML --- + +if ($hasRefTypes) { + $enc = New-Object System.Text.UTF8Encoding($true) + $cfgDir = Join-Path $TempBasePath "cfg" + New-Item -ItemType Directory -Path $cfgDir -Force | Out-Null + + $ns = '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"' + + # GeneratedType definitions per metadata type + $gtDefs = @{ + "Catalog" = @( + @{p="CatalogObject";c="Object"},@{p="CatalogRef";c="Ref"},@{p="CatalogSelection";c="Selection"}, + @{p="CatalogList";c="List"},@{p="CatalogManager";c="Manager"} + ) + "Document" = @( + @{p="DocumentObject";c="Object"},@{p="DocumentRef";c="Ref"},@{p="DocumentSelection";c="Selection"}, + @{p="DocumentList";c="List"},@{p="DocumentManager";c="Manager"} + ) + "Enum" = @( + @{p="EnumRef";c="Ref"},@{p="EnumManager";c="Manager"},@{p="EnumList";c="List"} + ) + "ChartOfAccounts" = @( + @{p="ChartOfAccountsObject";c="Object"},@{p="ChartOfAccountsRef";c="Ref"},@{p="ChartOfAccountsSelection";c="Selection"}, + @{p="ChartOfAccountsList";c="List"},@{p="ChartOfAccountsManager";c="Manager"} + ) + "ChartOfCharacteristicTypes" = @( + @{p="ChartOfCharacteristicTypesObject";c="Object"},@{p="ChartOfCharacteristicTypesRef";c="Ref"},@{p="ChartOfCharacteristicTypesSelection";c="Selection"}, + @{p="ChartOfCharacteristicTypesList";c="List"},@{p="Characteristic";c="Characteristic"},@{p="ChartOfCharacteristicTypesManager";c="Manager"} + ) + "ChartOfCalculationTypes" = @( + @{p="ChartOfCalculationTypesObject";c="Object"},@{p="ChartOfCalculationTypesRef";c="Ref"},@{p="ChartOfCalculationTypesSelection";c="Selection"}, + @{p="ChartOfCalculationTypesList";c="List"},@{p="ChartOfCalculationTypesManager";c="Manager"} + ) + "ExchangePlan" = @( + @{p="ExchangePlanObject";c="Object"},@{p="ExchangePlanRef";c="Ref"},@{p="ExchangePlanSelection";c="Selection"}, + @{p="ExchangePlanList";c="List"},@{p="ExchangePlanManager";c="Manager"} + ) + "BusinessProcess" = @( + @{p="BusinessProcessObject";c="Object"},@{p="BusinessProcessRef";c="Ref"},@{p="BusinessProcessSelection";c="Selection"}, + @{p="BusinessProcessList";c="List"},@{p="BusinessProcessManager";c="Manager"} + ) + "Task" = @( + @{p="TaskObject";c="Object"},@{p="TaskRef";c="Ref"},@{p="TaskSelection";c="Selection"}, + @{p="TaskList";c="List"},@{p="TaskManager";c="Manager"} + ) + "InformationRegister" = @( + @{p="InformationRegisterRecord";c="Record"},@{p="InformationRegisterManager";c="Manager"}, + @{p="InformationRegisterSelection";c="Selection"},@{p="InformationRegisterList";c="List"}, + @{p="InformationRegisterRecordSet";c="RecordSet"},@{p="InformationRegisterRecordKey";c="RecordKey"}, + @{p="InformationRegisterRecordManager";c="RecordManager"} + ) + "AccumulationRegister" = @( + @{p="AccumulationRegisterRecord";c="Record"},@{p="AccumulationRegisterManager";c="Manager"}, + @{p="AccumulationRegisterSelection";c="Selection"},@{p="AccumulationRegisterList";c="List"}, + @{p="AccumulationRegisterRecordSet";c="RecordSet"},@{p="AccumulationRegisterRecordKey";c="RecordKey"} + ) + "AccountingRegister" = @( + @{p="AccountingRegisterRecord";c="Record"},@{p="AccountingRegisterManager";c="Manager"}, + @{p="AccountingRegisterSelection";c="Selection"},@{p="AccountingRegisterExtDimensionTypes";c="ExtDimensionTypes"}, + @{p="AccountingRegisterList";c="List"},@{p="AccountingRegisterRecordSet";c="RecordSet"}, + @{p="AccountingRegisterRecordKey";c="RecordKey"} + ) + "CalculationRegister" = @( + @{p="CalculationRegisterRecord";c="Record"},@{p="CalculationRegisterManager";c="Manager"}, + @{p="CalculationRegisterSelection";c="Selection"},@{p="CalculationRegisterList";c="List"}, + @{p="CalculationRegisterRecordSet";c="RecordSet"},@{p="CalculationRegisterRecordKey";c="RecordKey"} + ) + "DefinedType" = @( + @{p="DefinedType";c="DefinedType"} + ) + } + + # Metadata type -> XML tag and directory + $metaInfo = @{ + "Catalog" = @{tag="Catalog";dir="Catalogs"} + "Document" = @{tag="Document";dir="Documents"} + "Enum" = @{tag="Enum";dir="Enums"} + "ChartOfAccounts" = @{tag="ChartOfAccounts";dir="ChartsOfAccounts"} + "ChartOfCharacteristicTypes" = @{tag="ChartOfCharacteristicTypes";dir="ChartsOfCharacteristicTypes"} + "ChartOfCalculationTypes" = @{tag="ChartOfCalculationTypes";dir="ChartsOfCalculationTypes"} + "ExchangePlan" = @{tag="ExchangePlan";dir="ExchangePlans"} + "BusinessProcess" = @{tag="BusinessProcess";dir="BusinessProcesses"} + "Task" = @{tag="Task";dir="Tasks"} + "InformationRegister" = @{tag="InformationRegister";dir="InformationRegisters"} + "AccumulationRegister" = @{tag="AccumulationRegister";dir="AccumulationRegisters"} + "AccountingRegister" = @{tag="AccountingRegister";dir="AccountingRegisters"} + "CalculationRegister" = @{tag="CalculationRegister";dir="CalculationRegisters"} + "DefinedType" = @{tag="DefinedType";dir="DefinedTypes"} + } + + # StandardAttribute boilerplate + $stdAttrXml = @' + + DontCheck + false + false + Auto + + + false + + + Auto + Auto + + false + Use + false + + + + Use + + + + +'@ + + $stdAttrsByType = @{ + "Catalog" = @("PredefinedDataName","Predefined","Ref","DeletionMark","IsFolder","Owner","Parent","Description","Code") + "Document" = @("Posted","Ref","DeletionMark","Date","Number") + "Enum" = @("Order","Ref") + "ChartOfAccounts" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","Order","Type","OffBalance") + "ChartOfCharacteristicTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","Parent","ValueType") + "ChartOfCalculationTypes" = @("PredefinedDataName","Predefined","Ref","DeletionMark","Description","Code","ActionPeriodIsBasic") + "ExchangePlan" = @("Ref","DeletionMark","Code","Description","ThisNode","SentNo","ReceivedNo") + "BusinessProcess" = @("Ref","DeletionMark","Date","Number","Started","Completed","HeadTask") + "Task" = @("Ref","DeletionMark","Date","Number","Executed","Description","RoutePoint","BusinessProcess") + "InformationRegister" = @("Active","LineNumber","Recorder","Period") + "AccumulationRegister" = @("Active","LineNumber","Recorder","Period") + "AccountingRegister" = @("Active","Period","Recorder","LineNumber","Account") + "CalculationRegister" = @("Active","Recorder","LineNumber","RegistrationPeriod","CalculationType","ReversingEntry") + } + + function Build-StdAttrs([string]$metaType) { + $attrs = $stdAttrsByType[$metaType] + if (-not $attrs) { return "" } + $sb = New-Object System.Text.StringBuilder + $sb.AppendLine("`t`t`t") | Out-Null + foreach ($a in $attrs) { + $sb.AppendLine("`t`t`t`t") | Out-Null + $sb.AppendLine($stdAttrXml) | Out-Null + $sb.AppendLine("`t`t`t`t") | Out-Null + } + $sb.AppendLine("`t`t`t") | Out-Null + return $sb.ToString() + } + + # --- 4a. Configuration.xml --- + $uuidCfg = [guid]::NewGuid().ToString() + $uuidLang = [guid]::NewGuid().ToString() + + $coIds = @() + for ($i = 0; $i -lt 7; $i++) { $coIds += [guid]::NewGuid().ToString() } + $classIds = @( + "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" + ) + + $coXml = "" + for ($i = 0; $i -lt 7; $i++) { + $coXml += "`r`n`t`t`t`r`n`t`t`t`t$($classIds[$i])`r`n`t`t`t`t$($coIds[$i])`r`n`t`t`t" + } + + # ChildObjects entries + $childXml = "`r`n`t`t`tРусский" + foreach ($metaType in $typeMap.Keys) { + if (-not $metaInfo.ContainsKey($metaType)) { continue } + $tag = $metaInfo[$metaType].tag + foreach ($name in $typeMap[$metaType].Keys) { + $childXml += "`r`n`t`t`t<$tag>$name" + } + } + + $cfgXml = @" + + + + $coXml + + + StubConfig + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + Taxi + DontUse + Version8_3_24 + + + $childXml + + + +"@ + + [System.IO.File]::WriteAllText((Join-Path $cfgDir "Configuration.xml"), $cfgXml, $enc) + + # --- 4b. Language --- + $langDir = Join-Path $cfgDir "Languages" + New-Item -ItemType Directory -Path $langDir -Force | Out-Null + + $langXml = @" + + + + + Русский + + + ru + Русский + + + + ru + + + +"@ + [System.IO.File]::WriteAllText((Join-Path $langDir "Русский.xml"), $langXml, $enc) + + # --- 4c. Metadata object stubs --- + foreach ($metaType in $typeMap.Keys) { + if (-not $metaInfo.ContainsKey($metaType)) { continue } + $info = $metaInfo[$metaType] + $objDir = Join-Path $cfgDir $info.dir + New-Item -ItemType Directory -Path $objDir -Force | Out-Null + + foreach ($objName in $typeMap[$metaType].Keys) { + $uuid = [guid]::NewGuid().ToString() + + # InternalInfo with GeneratedTypes + $internalXml = "" + $gts = $gtDefs[$metaType] + if ($gts) { + $internalXml = "`r`n`t`t" + if ($metaType -eq "ExchangePlan") { + $internalXml += "`r`n`t`t`t$([guid]::NewGuid().ToString())" + } + foreach ($gt in $gts) { + $fullName = "$($gt.p).$objName" + $tid = [guid]::NewGuid().ToString() + $vid = [guid]::NewGuid().ToString() + $internalXml += "`r`n`t`t`t" + $internalXml += "`r`n`t`t`t`t$tid" + $internalXml += "`r`n`t`t`t`t$vid" + $internalXml += "`r`n`t`t`t" + } + $internalXml += "`r`n`t`t" + } + + # Properties + ChildObjects depending on type + $propsXml = "" + $childObjXml = "" + + switch ($metaType) { + "Catalog" { + $stdAttrs = Build-StdAttrs "Catalog" + $propsXml = @" + $objName + + + false + HierarchyFoldersAndItems + false + 2 + true + false + + ToItems + 9 + 25 + String + Variable + WholeCatalog + false + true + AsDescription +$stdAttrs + Auto + InDialog + true + BothWays + + Begin + DontUse + Directly + + + + + + + + + + + false + + + Automatic + Use + + + + + + DontUse + Auto + DontUse + false + false +"@ + } + "Document" { + $stdAttrs = Build-StdAttrs "Document" + $regRecordsXml = "" + # If this is the stub registrator, set register records + if ($objName -eq "ЗаглушкаРегистратора") { + $rrLines = @() + foreach ($rt in $registratorTypes) { + if ($typeMap.ContainsKey($rt) -and $typeMap[$rt].Count -gt 0) { + foreach ($rn in $typeMap[$rt].Keys) { + $rrLines += "`t`t`t`t$rt.$rn" + } + } + } + if ($rrLines.Count -gt 0) { + $regRecordsXml = "`r`n$($rrLines -join "`r`n")`r`n`t`t`t" + } + } + $propsXml = @" + $objName + + + false + + String + 11 + Variable + Year + false + true +$stdAttrs + + + DontUse + Begin + DontUse + Directly + + + + + + + Allow + Deny + AutoDelete + WriteModified + AutoFill + $regRecordsXml + true + true + false + + Automatic + Use + + + + + + Auto + DontUse + false + false +"@ + } + "Enum" { + $stdAttrs = Build-StdAttrs "Enum" + $propsXml = @" + $objName + + + false +$stdAttrs + true + BothWays + + + + + + + + Auto +"@ + } + "InformationRegister" { + $stdAttrs = Build-StdAttrs "InformationRegister" + $propsXml = @" + $objName + + + false + InDialog + + + + +$stdAttrs Nonperiodical + Independent + false + false + Automatic + Use + false + false + + + + + + DontUse + false + false +"@ + } + "AccumulationRegister" { + $stdAttrs = Build-StdAttrs "AccumulationRegister" + $propsXml = @" + $objName + + + false + + + Balance + false +$stdAttrs Automatic + Use + true + + + +"@ + } + "AccountingRegister" { + $stdAttrs = Build-StdAttrs "AccountingRegister" + $propsXml = @" + $objName + + + false + + + false + + false +$stdAttrs Automatic + Use + true + + + +"@ + } + "CalculationRegister" { + $stdAttrs = Build-StdAttrs "CalculationRegister" + $propsXml = @" + $objName + + + false + + + false + +$stdAttrs Automatic + Use + + + +"@ + } + "ChartOfAccounts" { + $stdAttrs = Build-StdAttrs "ChartOfAccounts" + $propsXml = @" + $objName + + + false + + 20 + 100 + WholeCatalog + false + true + AsDescription +$stdAttrs + Auto + InDialog + true + BothWays + + Begin + DontUse + Directly + + + + + + + + + + + true + 5 + 0 + false + + + Automatic + Use + + + + + + DontUse + Auto + DontUse + false + false +"@ + } + "ChartOfCharacteristicTypes" { + $stdAttrs = Build-StdAttrs "ChartOfCharacteristicTypes" + $propsXml = @" + $objName + + + false + 9 + Variable + 25 + false + true + AsDescription + + + xs:boolean + xs:string + + 0 + Variable + + xs:decimal + + 15 + 2 + Any + + xs:dateTime + + DateTime + + + false + true +$stdAttrs + Auto + InDialog + true + BothWays + + Begin + DontUse + Directly + + + + + + + + + + + false + + + Automatic + Use + + + + + + DontUse + Auto + DontUse + false + false +"@ + } + "ChartOfCalculationTypes" { + $stdAttrs = Build-StdAttrs "ChartOfCalculationTypes" + $propsXml = @" + $objName + + + false + 9 + 25 + String + Variable + WholeCatalog + false + true + AsDescription +$stdAttrs + Auto + InDialog + true + BothWays + + Begin + DontUse + Directly + NotDepend + + false + + + + + + + false + + + Automatic + Use + + + + + + DontUse + Auto + DontUse + false + false +"@ + } + "ExchangePlan" { + $stdAttrs = Build-StdAttrs "ExchangePlan" + $propsXml = @" + $objName + + + false + 9 + 25 + Variable +$stdAttrs AsDescription + + Auto + InDialog + true + BothWays + + Begin + DontUse + Directly + + + + + + + false + + + Automatic + Use + + + + + + DontUse + Auto + false + DontUse + false + false +"@ + } + "BusinessProcess" { + $stdAttrs = Build-StdAttrs "BusinessProcess" + $propsXml = @" + $objName + + + false + + String + 11 + Variable + Year + false + true +$stdAttrs + + false + + + + + + + false + + + Automatic + Use + + + + + + Auto + DontUse + false + false +"@ + } + "Task" { + $stdAttrs = Build-StdAttrs "Task" + $propsXml = @" + $objName + + + false + + String + 11 + Variable + Year + false + true + 25 +$stdAttrs + + Begin + DontUse + Directly + + + + + + + false + + + Automatic + Use + + + + + + + + + + Auto + DontUse + false + false +"@ + } + "DefinedType" { + $propsXml = @" + $objName + + + + xs:string + + 0 + Variable + + +"@ + } + } + + $childObjLine = "`n`t`t" + if ($metaType -eq "DefinedType") { + $childObjLine = "" + } elseif ($metaType -eq "InformationRegister") { + $dimUuid = [guid]::NewGuid().ToString() + $childObjLine = @" + + + + + Заглушка + + + + xs:string + + 10 + Variable + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + false + true + false + DontIndex + Use + Use + + + +"@ + } elseif ($metaType -in @("AccumulationRegister","AccountingRegister","CalculationRegister")) { + $resUuid = [guid]::NewGuid().ToString() + $childObjLine = @" + + + + + Заглушка + + + + xs:decimal + + 15 + 2 + Any + + + false + + + + false + + false + false + + + DontCheck + Items + + + Auto + Auto + + + Auto + Use + + + +"@ + } + $objXml = @" + + + <$($info.tag) uuid="$uuid">$internalXml + +$propsXml $childObjLine + + +"@ + [System.IO.File]::WriteAllText((Join-Path $objDir "$objName.xml"), $objXml, $enc) + } + } + + Write-Host "Generated stub configuration with $($typeMap.Count) metadata types" +} + +# --- 5. Create infobase --- +Write-Host "Creating infobase: $TempBasePath" +$createArgs = "CREATEINFOBASE File=`"$TempBasePath`" /DisableStartupDialogs" +$proc = Start-Process -FilePath $V8Path -ArgumentList $createArgs -NoNewWindow -Wait -PassThru +if ($proc.ExitCode -ne 0) { + Write-Error "Failed to create infobase (code: $($proc.ExitCode))" + exit 1 +} + +# --- 6. Load config and update DB if ref types exist --- +if ($hasRefTypes) { + $cfgDir = Join-Path $TempBasePath "cfg" + # LoadConfigFromFiles + Write-Host "Loading configuration from files..." + $loadLog = Join-Path $env:TEMP "stub_load_log.txt" + $loadArgs = "DESIGNER /F`"$TempBasePath`" /LoadConfigFromFiles `"$cfgDir`" /Out `"$loadLog`" /DisableStartupDialogs" + $proc = Start-Process -FilePath $V8Path -ArgumentList $loadArgs -NoNewWindow -Wait -PassThru + if ($proc.ExitCode -ne 0) { + if (Test-Path $loadLog) { Get-Content $loadLog -Raw -ErrorAction SilentlyContinue | Write-Host } + Write-Error "Failed to load config (code: $($proc.ExitCode))" + exit 1 + } + + # UpdateDBCfg + Write-Host "Updating database configuration..." + $updateArgs = "DESIGNER /F`"$TempBasePath`" /UpdateDBCfg /DisableStartupDialogs" + $proc = Start-Process -FilePath $V8Path -ArgumentList $updateArgs -NoNewWindow -Wait -PassThru + if ($proc.ExitCode -ne 0) { + Write-Error "Failed to update DB config (code: $($proc.ExitCode))" + exit 1 + } + + # Cleanup cfg dir + Remove-Item -Path $cfgDir -Recurse -Force -ErrorAction SilentlyContinue +} + +# --- 7. Output base path --- +Write-Host "[OK] Stub database created: $TempBasePath" +Write-Host $TempBasePath diff --git a/.claude/skills/epf-build/scripts/stub-db-create.py b/.claude/skills/epf-build/scripts/stub-db-create.py new file mode 100644 index 00000000..d7ada5dd --- /dev/null +++ b/.claude/skills/epf-build/scripts/stub-db-create.py @@ -0,0 +1,1027 @@ +#!/usr/bin/env python3 +# stub-db-create v1.0 — Create temp 1C infobase with metadata stubs for EPF/ERF build +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills + +import argparse +import os +import random +import re +import subprocess +import sys +import tempfile +import uuid + + +def new_uuid(): + return str(uuid.uuid4()) + + +def scan_ref_types(source_dir): + """Scan XML files for reference/object/recordset types. Returns {metaType: {name: True}}.""" + type_map = {} + + ref_pattern = re.compile( + r'(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef' + r'|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef' + r'|ExchangePlanRef|BusinessProcessRef|TaskRef)' + r'\.([A-Za-z\u0400-\u04FF\d_]+)' + ) + obj_pattern = re.compile( + r'(?:cfg:|d\dp1:)(CatalogObject|DocumentObject|ChartOfAccountsObject' + r'|ChartOfCharacteristicTypesObject|ChartOfCalculationTypesObject' + r'|ExchangePlanObject|BusinessProcessObject|TaskObject)' + r'\.([A-Za-z\u0400-\u04FF\d_]+)' + ) + rs_pattern = re.compile( + r'(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet' + r'|AccountingRegisterRecordSet|CalculationRegisterRecordSet)' + r'\.([A-Za-z\u0400-\u04FF\d_]+)' + ) + char_pattern = re.compile(r'cfg:Characteristic\.([A-Za-z\u0400-\u04FF\d_]+)') + dt_pattern = re.compile(r'cfg:DefinedType\.([A-Za-z\u0400-\u04FF\d_]+)') + + ref_map = { + 'CatalogRef': 'Catalog', 'DocumentRef': 'Document', 'EnumRef': 'Enum', + 'ChartOfAccountsRef': 'ChartOfAccounts', + 'ChartOfCharacteristicTypesRef': 'ChartOfCharacteristicTypes', + 'ChartOfCalculationTypesRef': 'ChartOfCalculationTypes', + 'ExchangePlanRef': 'ExchangePlan', 'BusinessProcessRef': 'BusinessProcess', 'TaskRef': 'Task', + } + obj_map = { + 'CatalogObject': 'Catalog', 'DocumentObject': 'Document', + 'ChartOfAccountsObject': 'ChartOfAccounts', + 'ChartOfCharacteristicTypesObject': 'ChartOfCharacteristicTypes', + 'ChartOfCalculationTypesObject': 'ChartOfCalculationTypes', + 'ExchangePlanObject': 'ExchangePlan', 'BusinessProcessObject': 'BusinessProcess', 'TaskObject': 'Task', + } + rs_map = { + 'InformationRegisterRecordSet': 'InformationRegister', + 'AccumulationRegisterRecordSet': 'AccumulationRegister', + 'AccountingRegisterRecordSet': 'AccountingRegister', + 'CalculationRegisterRecordSet': 'CalculationRegister', + } + + for dirpath, _, filenames in os.walk(source_dir): + for fn in filenames: + if not fn.endswith('.xml'): + continue + fp = os.path.join(dirpath, fn) + try: + with open(fp, 'r', encoding='utf-8-sig') as f: + content = f.read() + except Exception: + continue + + for m in ref_pattern.finditer(content): + mt = ref_map[m.group(1)] + type_map.setdefault(mt, {})[m.group(2)] = True + for m in obj_pattern.finditer(content): + mt = obj_map[m.group(1)] + type_map.setdefault(mt, {})[m.group(2)] = True + for m in rs_pattern.finditer(content): + mt = rs_map[m.group(1)] + type_map.setdefault(mt, {})[m.group(2)] = True + for m in char_pattern.finditer(content): + type_map.setdefault('ChartOfCharacteristicTypes', {})[m.group(1)] = True + for m in dt_pattern.finditer(content): + type_map.setdefault('DefinedType', {})[m.group(1)] = True + + return type_map + + +NS = ( + '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"' +) + +CLASS_IDS = [ + "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", +] + +GT_DEFS = { + 'Catalog': [('CatalogObject','Object'),('CatalogRef','Ref'),('CatalogSelection','Selection'),('CatalogList','List'),('CatalogManager','Manager')], + 'Document': [('DocumentObject','Object'),('DocumentRef','Ref'),('DocumentSelection','Selection'),('DocumentList','List'),('DocumentManager','Manager')], + 'Enum': [('EnumRef','Ref'),('EnumManager','Manager'),('EnumList','List')], + 'ChartOfAccounts': [('ChartOfAccountsObject','Object'),('ChartOfAccountsRef','Ref'),('ChartOfAccountsSelection','Selection'),('ChartOfAccountsList','List'),('ChartOfAccountsManager','Manager')], + 'ChartOfCharacteristicTypes': [('ChartOfCharacteristicTypesObject','Object'),('ChartOfCharacteristicTypesRef','Ref'),('ChartOfCharacteristicTypesSelection','Selection'),('ChartOfCharacteristicTypesList','List'),('Characteristic','Characteristic'),('ChartOfCharacteristicTypesManager','Manager')], + 'ChartOfCalculationTypes': [('ChartOfCalculationTypesObject','Object'),('ChartOfCalculationTypesRef','Ref'),('ChartOfCalculationTypesSelection','Selection'),('ChartOfCalculationTypesList','List'),('ChartOfCalculationTypesManager','Manager')], + 'ExchangePlan': [('ExchangePlanObject','Object'),('ExchangePlanRef','Ref'),('ExchangePlanSelection','Selection'),('ExchangePlanList','List'),('ExchangePlanManager','Manager')], + 'BusinessProcess': [('BusinessProcessObject','Object'),('BusinessProcessRef','Ref'),('BusinessProcessSelection','Selection'),('BusinessProcessList','List'),('BusinessProcessManager','Manager')], + 'Task': [('TaskObject','Object'),('TaskRef','Ref'),('TaskSelection','Selection'),('TaskList','List'),('TaskManager','Manager')], + 'InformationRegister': [('InformationRegisterRecord','Record'),('InformationRegisterManager','Manager'),('InformationRegisterSelection','Selection'),('InformationRegisterList','List'),('InformationRegisterRecordSet','RecordSet'),('InformationRegisterRecordKey','RecordKey'),('InformationRegisterRecordManager','RecordManager')], + 'AccumulationRegister': [('AccumulationRegisterRecord','Record'),('AccumulationRegisterManager','Manager'),('AccumulationRegisterSelection','Selection'),('AccumulationRegisterList','List'),('AccumulationRegisterRecordSet','RecordSet'),('AccumulationRegisterRecordKey','RecordKey')], + 'AccountingRegister': [('AccountingRegisterRecord','Record'),('AccountingRegisterManager','Manager'),('AccountingRegisterSelection','Selection'),('AccountingRegisterExtDimensionTypes','ExtDimensionTypes'),('AccountingRegisterList','List'),('AccountingRegisterRecordSet','RecordSet'),('AccountingRegisterRecordKey','RecordKey')], + 'CalculationRegister': [('CalculationRegisterRecord','Record'),('CalculationRegisterManager','Manager'),('CalculationRegisterSelection','Selection'),('CalculationRegisterList','List'),('CalculationRegisterRecordSet','RecordSet'),('CalculationRegisterRecordKey','RecordKey')], + 'DefinedType': [('DefinedType','DefinedType')], +} + +META_INFO = { + 'Catalog': ('Catalog', 'Catalogs'), + 'Document': ('Document', 'Documents'), + 'Enum': ('Enum', 'Enums'), + 'ChartOfAccounts': ('ChartOfAccounts', 'ChartsOfAccounts'), + 'ChartOfCharacteristicTypes': ('ChartOfCharacteristicTypes', 'ChartsOfCharacteristicTypes'), + 'ChartOfCalculationTypes': ('ChartOfCalculationTypes', 'ChartsOfCalculationTypes'), + 'ExchangePlan': ('ExchangePlan', 'ExchangePlans'), + 'BusinessProcess': ('BusinessProcess', 'BusinessProcesses'), + 'Task': ('Task', 'Tasks'), + 'InformationRegister': ('InformationRegister', 'InformationRegisters'), + 'AccumulationRegister': ('AccumulationRegister', 'AccumulationRegisters'), + 'AccountingRegister': ('AccountingRegister', 'AccountingRegisters'), + 'CalculationRegister': ('CalculationRegister', 'CalculationRegisters'), + 'DefinedType': ('DefinedType', 'DefinedTypes'), +} + +STD_ATTRS_BY_TYPE = { + 'Catalog': ['PredefinedDataName','Predefined','Ref','DeletionMark','IsFolder','Owner','Parent','Description','Code'], + 'Document': ['Posted','Ref','DeletionMark','Date','Number'], + 'Enum': ['Order','Ref'], + 'ChartOfAccounts': ['PredefinedDataName','Predefined','Ref','DeletionMark','Description','Code','Parent','Order','Type','OffBalance'], + 'ChartOfCharacteristicTypes': ['PredefinedDataName','Predefined','Ref','DeletionMark','Description','Code','Parent','ValueType'], + 'ChartOfCalculationTypes': ['PredefinedDataName','Predefined','Ref','DeletionMark','Description','Code','ActionPeriodIsBasic'], + 'ExchangePlan': ['Ref','DeletionMark','Code','Description','ThisNode','SentNo','ReceivedNo'], + 'BusinessProcess': ['Ref','DeletionMark','Date','Number','Started','Completed','HeadTask'], + 'Task': ['Ref','DeletionMark','Date','Number','Executed','Description','RoutePoint','BusinessProcess'], + 'InformationRegister': ['Active','LineNumber','Recorder','Period'], + 'AccumulationRegister': ['Active','LineNumber','Recorder','Period'], + 'AccountingRegister': ['Active','Period','Recorder','LineNumber','Account'], + 'CalculationRegister': ['Active','Recorder','LineNumber','RegistrationPeriod','CalculationType','ReversingEntry'], +} + +STD_ATTR_BODY = """\t\t\t\t +\t\t\t\tDontCheck +\t\t\t\tfalse +\t\t\t\tfalse +\t\t\t\tAuto +\t\t\t\t +\t\t\t\t +\t\t\t\tfalse +\t\t\t\t +\t\t\t\t +\t\t\t\tAuto +\t\t\t\tAuto +\t\t\t\t +\t\t\t\tfalse +\t\t\t\tUse +\t\t\t\tfalse +\t\t\t\t +\t\t\t\t +\t\t\t\t +\t\t\t\tUse +\t\t\t\t +\t\t\t\t +\t\t\t\t +\t\t\t\t""" + + +def build_std_attrs(meta_type): + attrs = STD_ATTRS_BY_TYPE.get(meta_type) + if not attrs: + return '' + lines = ['\t\t\t'] + for a in attrs: + lines.append(f'\t\t\t\t') + lines.append(STD_ATTR_BODY) + lines.append(f'\t\t\t\t') + lines.append('\t\t\t') + return '\n'.join(lines) + '\n' + + +def build_internal_info(meta_type, obj_name): + gts = GT_DEFS.get(meta_type) + if not gts: + return '' + lines = ['\t\t'] + if meta_type == 'ExchangePlan': + lines.append(f'\t\t\t{new_uuid()}') + for prefix, cat in gts: + full = f'{prefix}.{obj_name}' + lines.append(f'\t\t\t') + lines.append(f'\t\t\t\t{new_uuid()}') + lines.append(f'\t\t\t\t{new_uuid()}') + lines.append(f'\t\t\t') + lines.append('\t\t') + return '\n'.join(lines) + + +# Properties templates per type — returns the Properties content (without tags) +PROPS = {} + +PROPS['Catalog'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\tHierarchyFoldersAndItems +\t\t\tfalse +\t\t\t2 +\t\t\ttrue +\t\t\tfalse +\t\t\t +\t\t\tToItems +\t\t\t9 +\t\t\t25 +\t\t\tString +\t\t\tVariable +\t\t\tWholeCatalog +\t\t\tfalse +\t\t\ttrue +\t\t\tAsDescription +{sa}\t\t\t +\t\t\tAuto +\t\t\tInDialog +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['Enum'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +{sa}\t\t\t +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tAuto""" + +PROPS['InformationRegister'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\tInDialog +\t\t\t +\t\t\t +\t\t\t +\t\t\t +{sa}\t\t\tNonperiodical +\t\t\tIndependent +\t\t\tfalse +\t\t\tfalse +\t\t\tAutomatic +\t\t\tUse +\t\t\tfalse +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['AccumulationRegister'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tBalance +\t\t\tfalse +{sa}\t\t\tAutomatic +\t\t\tUse +\t\t\ttrue +\t\t\t +\t\t\t +\t\t\t""" + +PROPS['AccountingRegister'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\tfalse +{sa}\t\t\tAutomatic +\t\t\tUse +\t\t\ttrue +\t\t\t +\t\t\t +\t\t\t""" + +PROPS['CalculationRegister'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +{sa}\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t""" + +PROPS['ChartOfAccounts'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t20 +\t\t\t100 +\t\t\tWholeCatalog +\t\t\tfalse +\t\t\ttrue +\t\t\tAsDescription +{sa}\t\t\t +\t\t\tAuto +\t\t\tInDialog +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\ttrue +\t\t\t5 +\t\t\t0 +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['ChartOfCharacteristicTypes'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t9 +\t\t\tVariable +\t\t\t25 +\t\t\tfalse +\t\t\ttrue +\t\t\tAsDescription +\t\t\t +\t\t\t +\t\t\t\txs:boolean +\t\t\t\txs:string +\t\t\t\t +\t\t\t\t\t0 +\t\t\t\t\tVariable +\t\t\t\t +\t\t\t\txs:decimal +\t\t\t\t +\t\t\t\t\t15 +\t\t\t\t\t2 +\t\t\t\t\tAny +\t\t\t\t +\t\t\t\txs:dateTime +\t\t\t\t +\t\t\t\t\tDateTime +\t\t\t\t +\t\t\t +\t\t\tfalse +\t\t\ttrue +{sa}\t\t\t +\t\t\tAuto +\t\t\tInDialog +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['ChartOfCalculationTypes'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t9 +\t\t\t25 +\t\t\tString +\t\t\tVariable +\t\t\tWholeCatalog +\t\t\tfalse +\t\t\ttrue +\t\t\tAsDescription +{sa}\t\t\t +\t\t\tAuto +\t\t\tInDialog +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\tNotDepend +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['ExchangePlan'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t9 +\t\t\t25 +\t\t\tVariable +{sa}\t\t\tAsDescription +\t\t\t +\t\t\tAuto +\t\t\tInDialog +\t\t\ttrue +\t\t\tBothWays +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tAuto +\t\t\tfalse +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['BusinessProcess'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\tString +\t\t\t11 +\t\t\tVariable +\t\t\tYear +\t\t\tfalse +\t\t\ttrue +{sa}\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['Task'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\tString +\t\t\t11 +\t\t\tVariable +\t\t\tYear +\t\t\tfalse +\t\t\ttrue +\t\t\t25 +{sa}\t\t\t +\t\t\t +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + +PROPS['DefinedType'] = lambda n, sa: f"""\t\t\t{n} +\t\t\t +\t\t\t +\t\t\t +\t\t\t\txs:string +\t\t\t\t +\t\t\t\t\t0 +\t\t\t\t\tVariable +\t\t\t\t +\t\t\t""" + + +def build_doc_props(obj_name, std_attrs, reg_records_xml): + return f"""\t\t\t{obj_name} +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\t +\t\t\tString +\t\t\t11 +\t\t\tVariable +\t\t\tYear +\t\t\tfalse +\t\t\ttrue +{std_attrs}\t\t\t +\t\t\t +\t\t\t +\t\t\tDontUse +\t\t\tBegin +\t\t\tDontUse +\t\t\tDirectly +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tAllow +\t\t\tDeny +\t\t\tAutoDelete +\t\t\tWriteModified +\t\t\tAutoFill +\t\t\t{reg_records_xml} +\t\t\ttrue +\t\t\ttrue +\t\t\tfalse +\t\t\t +\t\t\tAutomatic +\t\t\tUse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tAuto +\t\t\tDontUse +\t\t\tfalse +\t\t\tfalse""" + + +def write_bom(path, content): + with open(path, 'w', encoding='utf-8-sig', newline='') as f: + f.write(content) + + +def main(): + sys.stdout.reconfigure(encoding='utf-8') + sys.stderr.reconfigure(encoding='utf-8') + + parser = argparse.ArgumentParser(description='Create temp 1C infobase with metadata stubs') + parser.add_argument('-SourceDir', required=True) + parser.add_argument('-V8Path', required=True) + parser.add_argument('-TempBasePath', default='') + args = parser.parse_args() + + type_map = scan_ref_types(args.SourceDir) + has_ref_types = len(type_map) > 0 + + temp_base = args.TempBasePath or os.path.join(tempfile.gettempdir(), f'epf_stub_db_{random.randint(0,999999)}') + + # Add registrator stub document if needed + registrator_types = ['AccumulationRegister', 'AccountingRegister', 'CalculationRegister'] + needs_registrator = any(rt in type_map and len(type_map[rt]) > 0 for rt in registrator_types) + if needs_registrator: + type_map.setdefault('Document', {})['\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430'] = True # ЗаглушкаРегистратора + + if has_ref_types: + cfg_dir = os.path.join(temp_base, 'cfg') + os.makedirs(cfg_dir, exist_ok=True) + + # Configuration.xml + uuid_cfg = new_uuid() + uuid_lang = new_uuid() + co_ids = [new_uuid() for _ in range(7)] + + co_xml = '' + for i in range(7): + co_xml += f'\n\t\t\t\n\t\t\t\t{CLASS_IDS[i]}\n\t\t\t\t{co_ids[i]}\n\t\t\t' + + child_xml = '\n\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439' # Русский + for meta_type, names in type_map.items(): + if meta_type not in META_INFO: + continue + tag = META_INFO[meta_type][0] + for name in names: + child_xml += f'\n\t\t\t<{tag}>{name}' + + cfg_xml = f""" + +\t +\t\t{co_xml} +\t\t +\t\t +\t\t\tStubConfig +\t\t\t +\t\t\t +\t\t\t +\t\t\tVersion8_3_24 +\t\t\tManagedApplication +\t\t\t +\t\t\t\tPlatformApplication +\t\t\t +\t\t\tRussian +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tfalse +\t\t\tfalse +\t\t\tfalse +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tNormal +\t\t\t +\t\t\t +\t\t\tLanguage.\u0420\u0443\u0441\u0441\u043a\u0438\u0439 +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\t +\t\t\tManaged +\t\t\tNotAutoFree +\t\t\tDontUse +\t\t\tDontUse +\t\t\tTaxi +\t\t\tDontUse +\t\t\tVersion8_3_24 +\t\t\t +\t\t +\t\t{child_xml} +\t\t +\t + +""" + write_bom(os.path.join(cfg_dir, 'Configuration.xml'), cfg_xml) + + # Language + lang_dir = os.path.join(cfg_dir, 'Languages') + os.makedirs(lang_dir, exist_ok=True) + lang_xml = f""" + +\t +\t\t +\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439 +\t\t\t +\t\t\t\t +\t\t\t\t\tru +\t\t\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439 +\t\t\t\t +\t\t\t +\t\t\t +\t\t\tru +\t\t +\t + +""" + write_bom(os.path.join(lang_dir, '\u0420\u0443\u0441\u0441\u043a\u0438\u0439.xml'), lang_xml) + + # Metadata stubs + for meta_type, names in type_map.items(): + if meta_type not in META_INFO: + continue + tag, dirname = META_INFO[meta_type] + obj_dir = os.path.join(cfg_dir, dirname) + os.makedirs(obj_dir, exist_ok=True) + + for obj_name in names: + obj_uuid = new_uuid() + internal_xml = build_internal_info(meta_type, obj_name) + if internal_xml: + internal_xml = '\n' + internal_xml + + sa = build_std_attrs(meta_type) + + if meta_type == 'Document': + rr_xml = '' + if obj_name == '\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430': # ЗаглушкаРегистратора + rr_lines = [] + for rt in registrator_types: + if rt in type_map: + for rn in type_map[rt]: + rr_lines.append(f'\t\t\t\t{rt}.{rn}') + if rr_lines: + rr_xml = '\n' + '\n'.join(rr_lines) + '\n\t\t\t' + props_xml = build_doc_props(obj_name, sa, rr_xml) + elif meta_type in PROPS: + props_xml = PROPS[meta_type](obj_name, sa) + else: + props_xml = f'\t\t\t{obj_name}\n\t\t\t\n\t\t\t' + + # ChildObjects — varies by type + if meta_type == 'DefinedType': + child_obj_xml = '' + elif meta_type == 'InformationRegister': + dim_uuid = new_uuid() + child_obj_xml = f""" +\t\t +\t\t\t +\t\t\t\t +\t\t\t\t\t\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t\txs:string +\t\t\t\t\t\t +\t\t\t\t\t\t\t10 +\t\t\t\t\t\t\tVariable +\t\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\tDontCheck +\t\t\t\t\tItems +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tAuto +\t\t\t\t\tAuto +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tAuto +\t\t\t\t\tfalse +\t\t\t\t\ttrue +\t\t\t\t\tfalse +\t\t\t\t\tDontIndex +\t\t\t\t\tUse +\t\t\t\t\tUse +\t\t\t\t +\t\t\t +\t\t""" + elif meta_type in ('AccumulationRegister', 'AccountingRegister', 'CalculationRegister'): + res_uuid = new_uuid() + child_obj_xml = f""" +\t\t +\t\t\t +\t\t\t\t +\t\t\t\t\t\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t\txs:decimal +\t\t\t\t\t\t +\t\t\t\t\t\t\t15 +\t\t\t\t\t\t\t2 +\t\t\t\t\t\t\tAny +\t\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\tfalse +\t\t\t\t\tfalse +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tDontCheck +\t\t\t\t\tItems +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tAuto +\t\t\t\t\tAuto +\t\t\t\t\t +\t\t\t\t\t +\t\t\t\t\tAuto +\t\t\t\t\tUse +\t\t\t\t +\t\t\t +\t\t""" + else: + child_obj_xml = '\n\t\t' + + obj_xml = f""" + +\t<{tag} uuid="{obj_uuid}">{internal_xml} +\t\t +{props_xml} +\t\t{child_obj_xml} +\t + +""" + write_bom(os.path.join(obj_dir, f'{obj_name}.xml'), obj_xml) + + print(f'Generated stub configuration with {len(type_map)} metadata types') + + # Create infobase + print(f'Creating infobase: {temp_base}') + result = subprocess.run( + [args.V8Path, 'CREATEINFOBASE', f'File={temp_base}', '/DisableStartupDialogs'], + capture_output=True, text=True, + ) + if result.returncode != 0: + print(f'Failed to create infobase (code: {result.returncode})', file=sys.stderr) + sys.exit(1) + + if has_ref_types: + cfg_dir = os.path.join(temp_base, 'cfg') + # LoadConfigFromFiles + print('Loading configuration from files...') + result = subprocess.run( + [args.V8Path, 'DESIGNER', f'/F{temp_base}', '/LoadConfigFromFiles', cfg_dir, '/DisableStartupDialogs'], + capture_output=True, text=True, + ) + if result.returncode != 0: + print(f'Failed to load config (code: {result.returncode})', file=sys.stderr) + sys.exit(1) + + # UpdateDBCfg + print('Updating database configuration...') + result = subprocess.run( + [args.V8Path, 'DESIGNER', f'/F{temp_base}', '/UpdateDBCfg', '/DisableStartupDialogs'], + capture_output=True, text=True, + ) + if result.returncode != 0: + print(f'Failed to update DB config (code: {result.returncode})', file=sys.stderr) + sys.exit(1) + + # Cleanup cfg dir + import shutil + shutil.rmtree(cfg_dir, ignore_errors=True) + + print(f'[OK] Stub database created: {temp_base}') + print(temp_base) + + +if __name__ == '__main__': + main() diff --git a/.claude/skills/epf-dump/SKILL.md b/.claude/skills/epf-dump/SKILL.md index f7acf49e..6f675faf 100644 --- a/.claude/skills/epf-dump/SKILL.md +++ b/.claude/skills/epf-dump/SKILL.md @@ -24,14 +24,17 @@ allowed-tools: | EpfFile | да | — | Путь к EPF-файлу | | OutDir | нет | `src` | Каталог для выгрузки исходников | -## Параметры подключения +## Параметры подключения (опционально) + +Предпочтительно использовать конкретную базу — это надёжнее и не требует создания временной базы. + +1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую +3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` +4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` +5. Если ветка не совпала — используй `default` +6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную пустую базу. **Важно:** в пустой базе ссылочные типы (CatalogRef, DocumentRef и т.д.) сбрасываются в строки. Для сохранения типов нужна реальная база с конфигурацией. Предупреди пользователя об этом. -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: -1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую -2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` -3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` -4. Если ветка не совпала — используй `default` -5. Если `.v8-project.json` нет или баз нет — создай пустую ИБ в `./base` Если `v8path` не задан — автоопределение: `Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort -Desc | Select -First 1` Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. @@ -55,7 +58,7 @@ powershell.exe -NoProfile -File .claude/skills/epf-dump/scripts/epf-dump.ps1 <п | `-OutputDir <путь>` | да | Каталог для выгрузки исходников | | `-Format <формат>` | нет | `Hierarchical` (по умолч.) / `Plain` | -> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` +> `*` — опционально. Если не указано — автоматически создаётся временная пустая база. Ссылочные типы при этом сбрасываются в строки ## Коды возврата diff --git a/.claude/skills/epf-dump/scripts/epf-dump.ps1 b/.claude/skills/epf-dump/scripts/epf-dump.ps1 index 4932d82d..13007b0b 100644 --- a/.claude/skills/epf-dump/scripts/epf-dump.ps1 +++ b/.claude/skills/epf-dump/scripts/epf-dump.ps1 @@ -94,10 +94,20 @@ if (-not (Test-Path $V8Path)) { exit 1 } -# --- Validate connection --- +# --- Auto-create empty database if no connection specified --- +$autoCreatedBase = $null if (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) { - Write-Host "Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef" -ForegroundColor Red - exit 1 + $autoBasePath = Join-Path $env:TEMP "epf_dump_db_$(Get-Random)" + Write-Host "No database specified. Creating temporary empty database..." + Write-Host "WARNING: Reference types (CatalogRef, DocumentRef, etc.) will be lost - converted to strings. Use a real database to preserve types." -ForegroundColor Yellow + $createArgs = "CREATEINFOBASE File=`"$autoBasePath`" /DisableStartupDialogs" + $createProc = Start-Process -FilePath $V8Path -ArgumentList $createArgs -NoNewWindow -Wait -PassThru + if ($createProc.ExitCode -ne 0) { + Write-Host "Error: failed to create temporary database" -ForegroundColor Red + exit 1 + } + $InfoBasePath = $autoBasePath + $autoCreatedBase = $autoBasePath } # --- Validate input file --- @@ -163,4 +173,7 @@ try { if (Test-Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } + if ($autoCreatedBase -and (Test-Path $autoCreatedBase)) { + Remove-Item -Path $autoCreatedBase -Recurse -Force -ErrorAction SilentlyContinue + } } diff --git a/.claude/skills/epf-dump/scripts/epf-dump.py b/.claude/skills/epf-dump/scripts/epf-dump.py index 268b8b82..35a8b149 100644 --- a/.claude/skills/epf-dump/scripts/epf-dump.py +++ b/.claude/skills/epf-dump/scripts/epf-dump.py @@ -58,10 +58,21 @@ def main(): # --- Resolve V8Path --- v8path = resolve_v8path(args.V8Path) - # --- Validate connection --- + # --- Auto-create empty database if no connection specified --- + auto_created_base = None if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef): - print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr) - sys.exit(1) + auto_base_path = os.path.join(tempfile.gettempdir(), f"epf_dump_db_{random.randint(0, 999999)}") + print("No database specified. Creating temporary empty database...") + print("WARNING: Reference types (CatalogRef, DocumentRef, etc.) will be lost - converted to strings. Use a real database to preserve types.") + result = subprocess.run( + [v8path, "CREATEINFOBASE", f'File={auto_base_path}', "/DisableStartupDialogs"], + capture_output=True, text=True, + ) + if result.returncode != 0: + print("Error: failed to create temporary database", file=sys.stderr) + sys.exit(1) + args.InfoBasePath = auto_base_path + auto_created_base = auto_base_path # --- Validate input file --- if not os.path.isfile(args.InputFile): @@ -129,6 +140,8 @@ def main(): finally: if os.path.exists(temp_dir): shutil.rmtree(temp_dir, ignore_errors=True) + if auto_created_base and os.path.exists(auto_created_base): + shutil.rmtree(auto_created_base, ignore_errors=True) if __name__ == "__main__": diff --git a/.claude/skills/erf-build/SKILL.md b/.claude/skills/erf-build/SKILL.md index b49645f9..6cee638d 100644 --- a/.claude/skills/erf-build/SKILL.md +++ b/.claude/skills/erf-build/SKILL.md @@ -25,14 +25,17 @@ allowed-tools: | SrcDir | нет | `src` | Каталог исходников | | OutDir | нет | `build` | Каталог для результата | -## Параметры подключения +## Параметры подключения (опционально) + +Предпочтительно использовать конкретную базу — это надёжнее и не требует создания временной базы. + +1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую +3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` +4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` +5. Если ветка не совпала — используй `default` +6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную базу. Для ERF со ссылочными типами (CatalogRef, DocumentRef и т.д.) генерируются заглушки метаданных. Временная база удаляется после сборки. -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу для сборки: -1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую -2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` -3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` -4. Если ветка не совпала — используй `default` -5. Если `.v8-project.json` нет или баз нет — создай пустую ИБ в `./base` Если `v8path` не задан — автоопределение: `Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort -Desc | Select -First 1` Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. @@ -57,7 +60,7 @@ powershell.exe -NoProfile -File .claude/skills/epf-build/scripts/epf-build.ps1 < | `-SourceFile <путь>` | да | Путь к корневому XML-файлу исходников | | `-OutputFile <путь>` | да | Путь к выходному ERF-файлу | -> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` +> `*` — опционально. Если не указано — автоматически создаётся временная база со заглушками метаданных ## Коды возврата @@ -66,10 +69,6 @@ powershell.exe -NoProfile -File .claude/skills/epf-build/scripts/epf-build.ps1 < | 0 | Успешная сборка | | 1 | Ошибка (см. лог) | -## Ссылочные типы - -Если отчёт использует ссылочные типы конфигурации (`CatalogRef.XXX`, `DocumentRef.XXX`) — сборка в пустой базе упадёт с ошибкой XDTO. Зарегистрируй базу с целевой конфигурацией через `/db-list add`. - ## Примеры ```powershell diff --git a/.claude/skills/erf-dump/SKILL.md b/.claude/skills/erf-dump/SKILL.md index 8f74399c..b64e3b1a 100644 --- a/.claude/skills/erf-dump/SKILL.md +++ b/.claude/skills/erf-dump/SKILL.md @@ -24,14 +24,17 @@ allowed-tools: | ErfFile | да | — | Путь к ERF-файлу | | OutDir | нет | `src` | Каталог для выгрузки исходников | -## Параметры подключения +## Параметры подключения (опционально) + +Предпочтительно использовать конкретную базу — это надёжнее и не требует создания временной базы. + +1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую +3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` +4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` +5. Если ветка не совпала — используй `default` +6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную пустую базу. **Важно:** в пустой базе ссылочные типы (CatalogRef, DocumentRef и т.д.) сбрасываются в строки. Для сохранения типов нужна реальная база с конфигурацией. Предупреди пользователя об этом. -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: -1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую -2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` -3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` -4. Если ветка не совпала — используй `default` -5. Если `.v8-project.json` нет или баз нет — создай пустую ИБ в `./base` Если `v8path` не задан — автоопределение: `Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort -Desc | Select -First 1` Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. @@ -57,7 +60,7 @@ powershell.exe -NoProfile -File .claude/skills/epf-dump/scripts/epf-dump.ps1 <п | `-OutputDir <путь>` | да | Каталог для выгрузки исходников | | `-Format <формат>` | нет | `Hierarchical` (по умолч.) / `Plain` | -> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` +> `*` — опционально. Если не указано — автоматически создаётся временная пустая база ## Коды возврата