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$tag>"
+ }
+ }
+
+ $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
+ $($info.tag)>
+
+"@
+ [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}{tag}>'
+
+ 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{tag}>
+
+"""
+ 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`
+> `*` — опционально. Если не указано — автоматически создаётся временная пустая база
## Коды возврата