feat(cfe-patch-method): батч -Check/-Actualize контролируемых методов (v2.2)

После обновления КФ контролируемые методы (&ИзменениеИКонтроль) молча уезжают в
рассинхрон — платформа при загрузке не ругается, ошибка лишь в рантайме. Добавлены
два явных режима по всему расширению (или -ModulePath/-MethodName для сужения):

- -Check — отчёт: какие методы дрейфнули (ДРЕЙФ/КОНФЛИКТ/МЕТОД-ИСЧЕЗ), актуальные
  числом; ничего не пишет; exit 1 при наличии дрейфа.
- -Actualize — чинит пачкой: авто-перенос + merge-воркспейс на конфликтах.

Одиночный ресинк вынесен в общую функцию resync_one (report_only), одиночный путь и
батч используют её. Зона ответственности узкая — только тело &ИзменениеИКонтроль.

Merge-воркспейс переработан: тонкий index.md (список конфликтов + пути к .bsl расширения)
+ подпапка на метод (conflict.md с блоком/диффом + base/local/remote), без общей портянки.

cfe-validate: крошка-указатель [INFO] при наличии контролируемых методов -> /cfe-patch-method -Check.

Тесты: +check-clean/check-drift/actualize-batch. 16 кейсов, оба рантайма зелёные,
байтовый паритет ps1<->py. verify-snapshots (реальная 1С) Windows ps+py — 16/16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-18 20:37:58 +03:00
co-authored by Claude Opus 4.8
parent 8399f13e5a
commit 9aaa3a1c1e
29 changed files with 1609 additions and 256 deletions
+27 -3
View File
@@ -30,9 +30,11 @@ allowed-tools:
|----------|----------|--------------| |----------|----------|--------------|
| `ExtensionPath` | Путь к расширению (обязат.) | — | | `ExtensionPath` | Путь к расширению (обязат.) | — |
| `ConfigPath` | Путь к конфигурации-источнику | обязат., кроме случая, когда `ModulePath` — путь к файлу | | `ConfigPath` | Путь к конфигурации-источнику | обязат., кроме случая, когда `ModulePath` — путь к файлу |
| `ModulePath` | Логическое имя (`Тип.Имя.Модуль`) **или** путь к файлу модуля `.bsl` (обязат.) | — | | `ModulePath` | Логическое имя (`Тип.Имя.Модуль`) **или** путь к файлу модуля `.bsl` | обязат. для генерации |
| `MethodName` | Имя перехватываемого метода (обязат.) | — | | `MethodName` | Имя перехватываемого метода | обязат. для генерации |
| `InterceptorType` | `Before` / `After` / `Instead` / `ModificationAndControl` (обязат.) | — | | `InterceptorType` | `Before` / `After` / `Instead` / `ModificationAndControl` | обязат. для генерации |
| `Check` | Проверить контролируемые методы на дрейф (только отчёт) | — |
| `Actualize` | Актуализировать дрейфнувшие контролируемые методы | — |
Директива контекста, `Процедура`/`Функция`, сигнатура и обрамление определяются из оригинала автоматически — вручную задавать не нужно. Директива контекста, `Процедура`/`Функция`, сигнатура и обрамление определяются из оригинала автоматически — вручную задавать не нужно.
@@ -82,6 +84,22 @@ allowed-tools:
Повторный вызов `Before`/`After`/`Instead` для уже перехваченного метода не создаёт дубль (`[ПРОПУЩЕН]`). Повторный вызов `Before`/`After`/`Instead` для уже перехваченного метода не создаёт дубль (`[ПРОПУЩЕН]`).
## Проверка и актуализация пачкой (`-Check` / `-Actualize`)
После обновления конфигурации контролируемые методы (`&ИзменениеИКонтроль`) могут разойтись с
исходником — платформа при загрузке об этом молчит, ошибка всплывает лишь в рантайме. Эти режимы
проходят по **всем** `&ИзменениеИКонтроль` расширения (или сузить: `-ModulePath` — модуль, `+ -MethodName` — один):
- **`-Check`** — только отчёт: какие методы дрейфнули (`ДРЕЙФ` / `КОНФЛИКТ` / `МЕТОД-ИСЧЕЗ`); актуальные
считаются числом (`100/103 актуальны`). Ничего не пишет. Код возврата **1**, если есть что чинить.
- **`-Actualize`** — чинит: авто-переносит правки, для неоднозначных — merge-воркспейс.
Зона ответственности — **только тело `&ИзменениеИКонтроль`** (не свойства/формы/сигнатуры).
Merge-воркспейс конфликтов (`-Actualize`) в системном temp: `index.md` (тонкий список конфликтов с
путями к `.bsl` расширения для правки) + подпапка на каждый конфликтный метод с `conflict.md`
(что перенести + дифф) и `base/local/remote`. Путь печатается в выводе.
## Команда ## Команда
```powershell ```powershell
@@ -105,6 +123,12 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/cfe-patch-method.ps
# ModulePath как путь к файлу модуля-источника (без -ConfigPath) # ModulePath как путь к файлу модуля-источника (без -ConfigPath)
... -ExtensionPath src\cfe\ИмяРасширения -ModulePath "src\cf\CommonModules\ОбщийМодуль\Ext\Module.bsl" -MethodName "ПолучитьДанные" -InterceptorType Instead ... -ExtensionPath src\cfe\ИмяРасширения -ModulePath "src\cf\CommonModules\ОбщийМодуль\Ext\Module.bsl" -MethodName "ПолучитьДанные" -InterceptorType Instead
# Проверить все контролируемые методы расширения на дрейф
... -ExtensionPath src\cfe\ИмяРасширения -ConfigPath src\cf -Check
# Актуализировать дрейфнувшие контролируемые методы пачкой
... -ExtensionPath src\cfe\ИмяРасширения -ConfigPath src\cf -Actualize
``` ```
## Верификация ## Верификация
@@ -1,4 +1,4 @@
# cfe-patch-method v2.1 — Source-aware method interceptor for 1C extension (CFE) # cfe-patch-method v2.2 — Source-aware method interceptor for 1C extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory)]
@@ -6,15 +6,17 @@ param(
[string]$ConfigPath, [string]$ConfigPath,
[Parameter(Mandatory)] # Generation: logical name or path to .bsl. Optional in -Check/-Actualize (scope narrowing).
[string]$ModulePath, [string]$ModulePath,
[Parameter(Mandatory)]
[string]$MethodName, [string]$MethodName,
[Parameter(Mandatory)] [ValidateSet("", "Before", "After", "Instead", "ModificationAndControl")]
[ValidateSet("Before","After","Instead","ModificationAndControl")] [string]$InterceptorType,
[string]$InterceptorType
# Batch modes over &ИзменениеИКонтроль of the extension (no generation):
[switch]$Check, # report drift only, no writes
[switch]$Actualize # actualize drifted controlled methods
) )
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
@@ -475,6 +477,199 @@ function Get-Truncated {
return $t return $t
} }
# Reverse of typeDirMap (plural dir -> singular type) for logical id display
$script:dirToType = @{
"Catalogs"="Catalog"; "Documents"="Document"; "Enums"="Enum"; "CommonModules"="CommonModule"
"Reports"="Report"; "DataProcessors"="DataProcessor"; "ExchangePlans"="ExchangePlan"
"ChartsOfAccounts"="ChartOfAccounts"; "ChartsOfCharacteristicTypes"="ChartOfCharacteristicTypes"
"ChartsOfCalculationTypes"="ChartOfCalculationTypes"; "BusinessProcesses"="BusinessProcess"
"Tasks"="Task"; "InformationRegisters"="InformationRegister"
"AccumulationRegisters"="AccumulationRegister"; "AccountingRegisters"="AccountingRegister"
"CalculationRegisters"="CalculationRegister"
}
# Rel-path segments (relative to ExtensionPath) -> logical ModulePath (Type.Name.Module / CommonModule.Name)
function Get-ModulePathFromRel {
param($relParts)
$dir0 = $relParts[0]; $name = $relParts[1]
$typ = if ($script:dirToType.ContainsKey($dir0)) { $script:dirToType[$dir0] } else { $dir0 }
if ($dir0 -eq "CommonModules") { return "CommonModule.$name" }
if ($relParts.Count -ge 7 -and $relParts[2] -eq "Forms") { return "$typ.$name.Form.$($relParts[3])" }
$mod = $relParts[$relParts.Count - 1]
if ($mod.EndsWith(".bsl")) { $mod = $mod.Substring(0, $mod.Length - 4) }
return "$typ.$name.$mod"
}
function Get-RelPartsUnder {
param([string]$root, [string]$fullPath)
$r = [IO.Path]::GetFullPath($root).TrimEnd('\', '/')
$f = [IO.Path]::GetFullPath($fullPath)
$rel = $f.Substring($r.Length).TrimStart('\', '/')
return (($rel -replace '\\', '/').Split('/') | Where-Object { $_ -ne '' })
}
function Get-ResyncConflictReason {
param($disputed)
$kinds = @($disputed | ForEach-Object { $_.Kind } | Select-Object -Unique)
$parts = @()
if ($kinds -contains 'insert') { $parts += 'якорь вставки изменён' }
if ($kinds -contains 'delete') { $parts += 'удаляемое исчезло' }
return ($parts -join '; ')
}
# Write per-method conflict folder: conflict.md + base/local/remote
function Write-ConflictFolder {
param($folder, $methodId, $extBsl, $existingName, $method, $v1, $markedBody, $v2, $v1norm, $v2norm, $disputed, $enc)
if (-not (Test-Path $folder)) { New-Item -ItemType Directory -Path $folder -Force | Out-Null }
[IO.File]::WriteAllText((Join-Path $folder 'base.bsl'), (($v1 -join "`r`n") + "`r`n"), $enc)
[IO.File]::WriteAllText((Join-Path $folder 'local.bsl'), (($markedBody -join "`r`n") + "`r`n"), $enc)
[IO.File]::WriteAllText((Join-Path $folder 'remote.bsl'), (($v2 -join "`r`n") + "`r`n"), $enc)
$md = @()
$md += "# $methodId"
$md += "Править: $extBsl"
$md += "Метод: $existingName (&ИзменениеИКонтроль(`"$($method.Canonical)`"))"
$md += "Причина: $(Get-ResyncConflictReason $disputed)"
$md += ""
$md += "## Не размещено — перенести в метод вручную"
foreach ($d in $disputed) {
if ($d.Kind -eq 'insert') { $md += '#Вставка'; foreach ($l in $d.Lines) { $md += $l }; $md += '#КонецВставки' }
else { $md += '// не удалось найти для удаления:'; foreach ($l in $d.Lines) { $md += ('// ' + $l.Trim()) } }
}
$md += ""
$md += "## Дифф base→remote (что изменилось в оригинале)"
foreach ($l in $v1) { if ($v2norm -notcontains (Get-Normalized $l)) { $md += "- $l" } }
foreach ($l in $v2) { if ($v1norm -notcontains (Get-Normalized $l)) { $md += "+ $l" } }
$md += ""
$md += "Рядом: base.bsl / local.bsl / remote.bsl"
[IO.File]::WriteAllText((Join-Path $folder 'conflict.md'), (($md -join "`r`n") + "`r`n"), $enc)
}
# Resync one &ИзменениеИКонтроль interceptor. Returns status hashtable.
# ReportOnly: classify without writing. Otherwise: apply to module (+ conflict folder on disputes).
function Invoke-Resync {
param($extBsl, $extLines, $dup, $method, [string]$logicalModule, [string]$conflictFolder, [switch]$ReportOnly, $enc)
$methodId = "$logicalModule.$($method.Canonical)"
$decLine = $dup.Line
$sigLineIdx = $decLine + 1
if ($sigLineIdx -ge $extLines.Count -or $extLines[$sigLineIdx] -notmatch '^\s*(?:Асинх\s+)?(?:Процедура|Функция)\s+([\w]+)\s*\(') {
return @{ Id = $methodId; Status = 'ОШИБКА'; ExtBsl = $extBsl; Reason = 'не разобрать сигнатуру перехватчика' }
}
$existingName = $Matches[1]
$sig = Read-Signature $extLines $sigLineIdx
if (-not $sig) { return @{ Id = $methodId; Status = 'ОШИБКА'; ExtBsl = $extBsl; Reason = 'не разобрать сигнатуру' } }
$sigEnd = $sig.EndLineIdx
$isFunc = ($extLines[$sigLineIdx] -imatch '^\s*(?:Асинх\s+)?Функция\b')
$endRe = if ($isFunc) { '^\s*КонецФункции\b' } else { '^\s*КонецПроцедуры\b' }
$blockEnd = -1
for ($j = $sigEnd + 1; $j -lt $extLines.Count; $j++) { if ($extLines[$j] -imatch $endRe) { $blockEnd = $j; break } }
if ($blockEnd -lt 0) { return @{ Id = $methodId; Status = 'ОШИБКА'; ExtBsl = $extBsl; Reason = 'не найден конец перехватчика' } }
$markedBody = @()
for ($j = $sigEnd + 1; $j -lt $blockEnd; $j++) { $markedBody += $extLines[$j] }
$parsed = Parse-MarkedBody $markedBody
$v1 = $parsed.V1
$ops = $parsed.Ops
$v2 = $method.BodyLines
$v1norm = @($v1 | ForEach-Object { Get-Normalized $_ })
$v2norm = @($v2 | ForEach-Object { Get-Normalized $_ })
if (($v1norm -join "`n") -eq ($v2norm -join "`n")) {
return @{ Id = $methodId; Status = 'АКТУАЛЕН'; ExtBsl = $extBsl }
}
$insertTop = @(); $insertAfter = @{}; $delStart = @{}; $delEnd = @{}; $disputed = @(); $transferred = @()
foreach ($op in $ops) {
if ($op.Kind -eq 'insert') {
$beforeLines = @()
if ($op.After -ge 0) { $bs = [Math]::Max(0, $op.After - 2); for ($m = $bs; $m -le $op.After; $m++) { $beforeLines += $v1norm[$m] } }
$afterLines = @()
$ae = [Math]::Min($v1norm.Count - 1, $op.After + 3)
for ($m = $op.After + 1; $m -le $ae; $m++) { $afterLines += $v1norm[$m] }
$k = Resolve-InsertionPoint $v2norm $beforeLines $afterLines
if ($null -eq $k) { $disputed += @{ Kind = 'insert'; Lines = $op.Lines } }
elseif ($k -lt 0) { $insertTop += ,$op.Lines; $transferred += @{ Kind = 'insert' } }
else { if (-not $insertAfter.ContainsKey($k)) { $insertAfter[$k] = @() }; $insertAfter[$k] += ,$op.Lines; $transferred += @{ Kind = 'insert' } }
} else {
$keys = @(); for ($m = $op.Start; $m -le $op.End; $m++) { $keys += $v1norm[$m] }
$p = Find-UniqueRun $v2norm $keys
if ($p -ge 0) { $delStart[$p] = $true; $delEnd[$p + $keys.Count - 1] = $true; $transferred += @{ Kind = 'delete' } }
else { $disputed += @{ Kind = 'delete'; Lines = $op.Lines } }
}
}
if ($ReportOnly) {
$st = if ($disputed.Count -gt 0) { 'КОНФЛИКТ' } else { 'ДРЕЙФ' }
return @{ Id = $methodId; Status = $st; ExtBsl = $extBsl; Transferred = $transferred.Count; Disputed = $disputed.Count; Reason = (Get-ResyncConflictReason $disputed) }
}
# assemble new marked body
$newBody = @()
foreach ($blk in $insertTop) { $newBody += "#Вставка"; foreach ($l in $blk) { $newBody += $l }; $newBody += "#КонецВставки" }
for ($k = 0; $k -lt $v2.Count; $k++) {
if ($delStart.ContainsKey($k)) { $newBody += "#Удаление" }
$newBody += $v2[$k]
if ($delEnd.ContainsKey($k)) { $newBody += "#КонецУдаления" }
if ($insertAfter.ContainsKey($k)) { foreach ($blk in $insertAfter[$k]) { $newBody += "#Вставка"; foreach ($l in $blk) { $newBody += $l }; $newBody += "#КонецВставки" } }
}
if ($disputed.Count -gt 0) {
$newBody += "`t// [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала."
$newBody += "`t// Материалы: см. index.md / conflict.md в merge-воркспейсе (путь в выводе)."
foreach ($d in $disputed) {
if ($d.Kind -eq 'insert') { $newBody += "#Вставка"; foreach ($l in $d.Lines) { $newBody += $l }; $newBody += "#КонецВставки" }
else { $newBody += "`t// [РЕСИНК-КОНФЛИКТ] не удалось найти для удаления:"; foreach ($l in $d.Lines) { $newBody += ("`t// " + $l.Trim()) } }
}
}
$asyncPrefix = if ($method.IsAsync) { "Асинх " } else { "" }
$keyword = if ($method.IsFunction) { "Функция" } else { "Процедура" }
$endKeyword = if ($method.IsFunction) { "КонецФункции" } else { "КонецПроцедуры" }
$newBlock = @()
if ($method.Context) { $newBlock += $method.Context }
$newBlock += "&ИзменениеИКонтроль(`"$($method.Canonical)`")"
$newBlock += "$asyncPrefix$keyword $existingName($($method.ParamsText))"
$newBlock += $newBody
$newBlock += $endKeyword
$blockStart = $decLine
if ($decLine -ge 1 -and (Test-ContextDirective $extLines[$decLine - 1].Trim())) { $blockStart = $decLine - 1 }
$out = @()
for ($j = 0; $j -lt $blockStart; $j++) { $out += $extLines[$j] }
foreach ($l in $newBlock) { $out += $l }
for ($j = $blockEnd + 1; $j -lt $extLines.Count; $j++) { $out += $extLines[$j] }
[IO.File]::WriteAllText($extBsl, (($out -join "`r`n") + "`r`n"), $enc)
$conflictDir = $null
if ($disputed.Count -gt 0) {
$conflictDir = $conflictFolder
Write-ConflictFolder $conflictFolder $methodId $extBsl $existingName $method $v1 $markedBody $v2 $v1norm $v2norm $disputed $enc
}
$status = if ($disputed.Count -gt 0) { 'ЧАСТИЧНО' } else { 'АКТУАЛИЗИРОВАН' }
return @{ Id = $methodId; Status = $status; ExtBsl = $extBsl; Transferred = $transferred.Count; Disputed = $disputed.Count; ConflictDir = $conflictDir; Reason = (Get-ResyncConflictReason $disputed) }
}
# Write run-root index.md (only if there are conflicts). Returns run-root or $null.
function Write-ResyncIndex {
param($runRoot, $results, [string]$extName, [string]$configPath, [string]$verb, $enc)
$conflicts = @($results | Where-Object { $_.Status -eq 'ЧАСТИЧНО' })
if ($conflicts.Count -eq 0) { return $null }
if (-not (Test-Path $runRoot)) { New-Item -ItemType Directory -Path $runRoot -Force | Out-Null }
$total = $results.Count
$actual = @($results | Where-Object { $_.Status -eq 'АКТУАЛЕН' }).Count
$upd = @($results | Where-Object { $_.Status -eq 'АКТУАЛИЗИРОВАН' }).Count
$lines = @()
$lines += "[$verb] $extName -> $configPath"
$lines += "Итог: $actual/$total актуальны · $upd актуализировано · $($conflicts.Count) конфликтов"
$lines += ""
$lines += "Конфликты — править .bsl расширения:"
foreach ($c in $conflicts) {
$lines += " ЧАСТИЧНО $($c.Id)"
$lines += " -> $($c.ExtBsl) ($($c.Reason))"
}
[IO.File]::WriteAllText((Join-Path $runRoot 'index.md'), (($lines -join "`r`n") + "`r`n"), $enc)
return $runRoot
}
# ============================================================================ # ============================================================================
# Main # Main
# ============================================================================ # ============================================================================
@@ -494,6 +689,93 @@ $cfgNs.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
$propsNode = $cfgDoc.SelectSingleNode("//md:Configuration/md:Properties", $cfgNs) $propsNode = $cfgDoc.SelectSingleNode("//md:Configuration/md:Properties", $cfgNs)
$prefixNode = if ($propsNode) { $propsNode.SelectSingleNode("md:NamePrefix", $cfgNs) } else { $null } $prefixNode = if ($propsNode) { $propsNode.SelectSingleNode("md:NamePrefix", $cfgNs) } else { $null }
$namePrefix = if ($prefixNode -and $prefixNode.InnerText) { $prefixNode.InnerText } else { "Расш_" } $namePrefix = if ($prefixNode -and $prefixNode.InnerText) { $prefixNode.InnerText } else { "Расш_" }
$extNameNode = if ($propsNode) { $propsNode.SelectSingleNode("md:Name", $cfgNs) } else { $null }
$extName = if ($extNameNode -and $extNameNode.InnerText) { $extNameNode.InnerText } else { "Расширение" }
# ============================================================================
# Batch modes: -Check / -Actualize over &ИзменениеИКонтроль of the extension
# ============================================================================
if ($Check -or $Actualize) {
if ($Check -and $Actualize) { Write-Error "Укажите либо -Check, либо -Actualize, не оба."; exit 1 }
if ([string]::IsNullOrEmpty($ConfigPath)) { Write-Error "Для -Check/-Actualize нужен -ConfigPath (сверка с исходником)."; exit 1 }
if (-not [System.IO.Path]::IsPathRooted($ConfigPath)) { $ConfigPath = Join-Path (Get-Location).Path $ConfigPath }
if (Test-Path $ConfigPath -PathType Leaf) { $ConfigPath = Split-Path $ConfigPath -Parent }
if (-not (Test-Path (Join-Path $ConfigPath "Configuration.xml"))) { Write-Error "Configuration.xml не найден в конфигурации-источнике: $ConfigPath"; exit 1 }
$enc = New-Object System.Text.UTF8Encoding($true)
$reportOnly = [bool]$Check
$verb = if ($Check) { "КОНТРОЛЬ" } else { "АКТУАЛИЗАЦИЯ" }
# target .bsl set: -ModulePath → один модуль; иначе — все .bsl расширения
$targetBsls = @()
if (-not [string]::IsNullOrEmpty($ModulePath)) {
$relParts = Get-ModuleRelPath $ModulePath
$mb = $ExtensionPath; foreach ($p in $relParts) { $mb = Join-Path $mb $p }
if (Test-Path $mb) { $targetBsls += $mb }
} else {
$targetBsls = @(Get-ChildItem -Path $ExtensionPath -Recurse -Filter *.bsl -File | ForEach-Object { $_.FullName })
}
$runRoot = Join-Path ([IO.Path]::GetTempPath()) (Join-Path "cfe-resync" ($extName -replace '[\\/:*?"<>|]', '_'))
if ($Actualize -and (Test-Path $runRoot)) { Remove-Item $runRoot -Recurse -Force -ErrorAction SilentlyContinue }
$results = @()
foreach ($tb in $targetBsls) {
$scan = @([IO.File]::ReadAllLines($tb, [Text.Encoding]::UTF8))
$mnames = @(Get-Interceptors $scan | Where-Object { $_.Type -eq 'ИзменениеИКонтроль' } | ForEach-Object { $_.Method })
if (-not [string]::IsNullOrEmpty($MethodName)) { $mnames = @($mnames | Where-Object { $_ -ieq $MethodName }) }
$mnames = @($mnames | Select-Object -Unique)
if ($mnames.Count -eq 0) { continue }
$rel = Get-RelPartsUnder $ExtensionPath $tb
$logicalModule = Get-ModulePathFromRel $rel
$srcBsl = $ConfigPath; foreach ($p in $rel) { $srcBsl = Join-Path $srcBsl $p }
$relJoined = ($rel -join '\') -replace '\.bsl$', ''
foreach ($mname in $mnames) {
$mid = "$logicalModule.$mname"
if (-not (Test-Path $srcBsl)) { $results += @{ Id = $mid; Status = 'ИСТОЧНИК-НЕ-НАЙДЕН'; ExtBsl = $tb }; continue }
$srcL = [IO.File]::ReadAllLines($srcBsl, [Text.Encoding]::UTF8)
$m = Extract-Method $srcL $mname
if (-not $m) { $results += @{ Id = $mid; Status = 'МЕТОД-ИСЧЕЗ'; ExtBsl = $tb }; continue }
# fresh read per method (writes shift line numbers)
$tbLines = @([IO.File]::ReadAllLines($tb, [Text.Encoding]::UTF8))
$ic = @(Get-Interceptors $tbLines | Where-Object { $_.Type -eq 'ИзменениеИКонтроль' -and $_.Method -ieq $mname })[0]
if (-not $ic) { continue }
$folder = Join-Path $runRoot (Join-Path $relJoined $m.Canonical)
$results += (Invoke-Resync $tb $tbLines $ic $m $logicalModule $folder -ReportOnly:$reportOnly $enc)
}
}
# --- report ---
$total = $results.Count
$actual = @($results | Where-Object { $_.Status -eq 'АКТУАЛЕН' }).Count
Write-Host "[$verb] $extName -> $ConfigPath (на контроле: $total)"
$listed = @($results | Where-Object { $_.Status -ne 'АКТУАЛЕН' })
foreach ($p in $listed) {
$line = " {0,-16} {1}" -f $p.Status, $p.Id
if ($p.Reason) { $line += " $($p.Reason)" }
Write-Host $line
}
if ($Check) {
$drift = @($results | Where-Object { $_.Status -eq 'ДРЕЙФ' }).Count
$confl = @($results | Where-Object { $_.Status -eq 'КОНФЛИКТ' }).Count
$gone = @($results | Where-Object { $_.Status -in @('МЕТОД-ИСЧЕЗ', 'ИСТОЧНИК-НЕ-НАЙДЕН') }).Count
Write-Host "Итог: $actual/$total актуальны · $drift дрейф · $confl конфликт · $gone требуют внимания"
if ($listed.Count -gt 0) { Write-Host "Починить: /cfe-patch-method -Actualize -ExtensionPath $ExtensionPath -ConfigPath $ConfigPath"; exit 1 } else { exit 0 }
} else {
$upd = @($results | Where-Object { $_.Status -eq 'АКТУАЛИЗИРОВАН' }).Count
$part = @($results | Where-Object { $_.Status -eq 'ЧАСТИЧНО' }).Count
Write-Host "Итог: $actual/$total актуальны · $upd актуализировано · $part частично"
$idx = Write-ResyncIndex $runRoot $results $extName $ConfigPath $verb $enc
if ($idx) { Write-Host "Merge-воркспейс конфликтов (см. index.md): $idx" }
exit 0
}
}
# --- Generation mode: require ModulePath + MethodName + InterceptorType ---
if ([string]::IsNullOrEmpty($ModulePath) -or [string]::IsNullOrEmpty($MethodName) -or [string]::IsNullOrEmpty($InterceptorType)) {
Write-Error "Нужны -ModulePath, -MethodName, -InterceptorType (генерация перехватчика). Для проверки/актуализации контролируемых методов используйте -Check или -Actualize."
exit 1
}
# --- Resolve module file paths (ModulePath = logical name OR path to a .bsl) --- # --- Resolve module file paths (ModulePath = logical name OR path to a .bsl) ---
$hasConfigPath = -not [string]::IsNullOrEmpty($ConfigPath) $hasConfigPath = -not [string]::IsNullOrEmpty($ConfigPath)
@@ -568,171 +850,23 @@ if ($dup) {
Write-Host " Файл: $extBsl" Write-Host " Файл: $extBsl"
exit 0 exit 0
} }
# ---- RESYNC (&ИзменениеИКонтроль) ---- # ---- RESYNC (&ИзменениеИКонтроль) — via Invoke-Resync ----
# Locate existing block: decorator line -> signature -> body -> Конец* $rel = Get-RelPartsUnder $ExtensionPath $extBsl
$decLine = $dup.Line $logicalModule = Get-ModulePathFromRel $rel
# Interceptor procedure name from existing signature (line after decorator, skipping possible async) $relJoined = ($rel -join '') -replace '.bsl$', ''
$sigLineIdx = $decLine + 1 $runRoot = Join-Path ([IO.Path]::GetTempPath()) (Join-Path "cfe-resync" ($extName -replace '[\/:*?"<>|]', '_'))
if ($sigLineIdx -ge $extLines.Count -or $extLines[$sigLineIdx] -notmatch '^\s*(?:Асинх\s+)?(?:Процедура|Функция)\s+([\w]+)\s*\(') { $folder = Join-Path $runRoot (Join-Path $relJoined $method.Canonical)
Write-Error "Не удалось найти сигнатуру существующего перехватчика &ИзменениеИКонтроль(`"$MethodName`")"; exit 1 $res = Invoke-Resync $extBsl $extLines $dup $method $logicalModule $folder $enc
} switch ($res.Status) {
$existingName = $Matches[1] 'АКТУАЛЕН' { Write-Host "[АКТУАЛЕН] &ИзменениеИКонтроль(`"$MethodName`") — оригинал не менялся, изменений нет." }
$sig = Read-Signature $extLines $sigLineIdx 'АКТУАЛИЗИРОВАН' { Write-Host "[АКТУАЛИЗИРОВАН] &ИзменениеИКонтроль(`"$MethodName`") — тело обновлено, перенесено правок: $($res.Transferred)" }
if (-not $sig) { Write-Error "Не удалось разобрать сигнатуру существующего перехватчика"; exit 1 } 'ЧАСТИЧНО' {
$sigEnd = $sig.EndLineIdx $idx = Write-ResyncIndex $runRoot @($res) $extName $ConfigPath "АКТУАЛИЗАЦИЯ" $enc
$isFunc = ($extLines[$sigLineIdx] -imatch '^\s*(?:Асинх\s+)?Функция\b') Write-Host "[АКТУАЛИЗИРОВАН-ЧАСТИЧНО] &ИзменениеИКонтроль(`"$MethodName`") — перенесено: $($res.Transferred), конфликтов: $($res.Disputed)"
$endRe = if ($isFunc) { '^\s*КонецФункции\b' } else { '^\s*КонецПроцедуры\b' } Write-Host " Конфликт помечен // [РЕСИНК-КОНФЛИКТ]. Папка метода: $($res.ConflictDir)"
$blockEnd = -1 if ($idx) { Write-Host " Индекс: $idx" }
for ($j = $sigEnd + 1; $j -lt $extLines.Count; $j++) { if ($extLines[$j] -imatch $endRe) { $blockEnd = $j; break } }
if ($blockEnd -lt 0) { Write-Error "Не найден конец существующего перехватчика"; exit 1 }
# marked body
$markedBody = @()
for ($j = $sigEnd + 1; $j -lt $blockEnd; $j++) { $markedBody += $extLines[$j] }
# reconstruct v1, ops
$parsed = Parse-MarkedBody $markedBody
$v1 = $parsed.V1
$ops = $parsed.Ops
$v2 = $method.BodyLines
$v1norm = @($v1 | ForEach-Object { Get-Normalized $_ })
$v2norm = @($v2 | ForEach-Object { Get-Normalized $_ })
# no drift?
if (($v1norm -join "`n") -eq ($v2norm -join "`n")) {
Write-Host "[АКТУАЛЕН] &ИзменениеИКонтроль(`"$MethodName`") — оригинал не менялся, изменений нет."
Write-Host " Файл: $extBsl"
exit 0
}
# transfer ops onto v2
$insertTop = @()
$insertAfter = @{} # v2 index -> list of blocks (each = @{Lines=..})
$delStart = @{} # v2 index -> $true
$delEnd = @{}
$disputed = @()
$transferred = @() # for the summary: @{ Kind; Anchor }
foreach ($op in $ops) {
if ($op.Kind -eq 'insert') {
# two-sided context anchor
$beforeLines = @()
if ($op.After -ge 0) {
$bs = [Math]::Max(0, $op.After - 2)
for ($m = $bs; $m -le $op.After; $m++) { $beforeLines += $v1norm[$m] }
}
$afterLines = @()
$ae = [Math]::Min($v1norm.Count - 1, $op.After + 3)
for ($m = $op.After + 1; $m -le $ae; $m++) { $afterLines += $v1norm[$m] }
$k = Resolve-InsertionPoint $v2norm $beforeLines $afterLines
if ($null -eq $k) {
$disputed += @{ Kind = 'insert'; Lines = $op.Lines }
} elseif ($k -lt 0) {
$insertTop += ,$op.Lines
$transferred += @{ Kind = 'insert'; Anchor = '(в начало метода)' }
} else {
if (-not $insertAfter.ContainsKey($k)) { $insertAfter[$k] = @() }
$insertAfter[$k] += ,$op.Lines
$transferred += @{ Kind = 'insert'; Anchor = $v2[$k] }
}
} else {
$keys = @()
for ($m = $op.Start; $m -le $op.End; $m++) { $keys += $v1norm[$m] }
$p = Find-UniqueRun $v2norm $keys
if ($p -ge 0) {
$delStart[$p] = $true
$delEnd[$p + $keys.Count - 1] = $true
$transferred += @{ Kind = 'delete'; Anchor = $op.Lines[0] }
} else {
$disputed += @{ Kind = 'delete'; Lines = $op.Lines }
}
} }
} default { Write-Host "[$($res.Status)] &ИзменениеИКонтроль(`"$MethodName`") — $($res.Reason)" }
# assemble new marked body
$newBody = @()
foreach ($blk in $insertTop) {
$newBody += "#Вставка"; foreach ($l in $blk) { $newBody += $l }; $newBody += "#КонецВставки"
}
for ($k = 0; $k -lt $v2.Count; $k++) {
if ($delStart.ContainsKey($k)) { $newBody += "#Удаление" }
$newBody += $v2[$k]
if ($delEnd.ContainsKey($k)) { $newBody += "#КонецУдаления" }
if ($insertAfter.ContainsKey($k)) {
foreach ($blk in $insertAfter[$k]) {
$newBody += "#Вставка"; foreach ($l in $blk) { $newBody += $l }; $newBody += "#КонецВставки"
}
}
}
# disputed blocks appended with conflict markers (never lost)
if ($disputed.Count -gt 0) {
$newBody += "`t// [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала."
$newBody += "`t// Материалы для анализа см. в выводе команды (файлы base/local/remote/merged)."
foreach ($d in $disputed) {
if ($d.Kind -eq 'insert') {
$newBody += "#Вставка"; foreach ($l in $d.Lines) { $newBody += $l }; $newBody += "#КонецВставки"
} else {
$newBody += "`t// [РЕСИНК-КОНФЛИКТ] не удалось найти для удаления:"
foreach ($l in $d.Lines) { $newBody += ("`t// " + $l.Trim()) }
}
}
}
# rebuild block: keep context (from v2), decorator, signature with existing name and v2 params
$asyncPrefix = if ($method.IsAsync) { "Асинх " } else { "" }
$keyword = if ($method.IsFunction) { "Функция" } else { "Процедура" }
$endKeyword = if ($method.IsFunction) { "КонецФункции" } else { "КонецПроцедуры" }
$newBlock = @()
if ($method.Context) { $newBlock += $method.Context }
$newBlock += "&ИзменениеИКонтроль(`"$($method.Canonical)`")"
$newBlock += "$asyncPrefix$keyword $existingName($($method.ParamsText))"
$newBlock += $newBody
$newBlock += $endKeyword
# block start includes preceding context directive line if present
$blockStart = $decLine
if ($decLine -ge 1 -and (Test-ContextDirective $extLines[$decLine - 1].Trim())) { $blockStart = $decLine - 1 }
$out = @()
for ($j = 0; $j -lt $blockStart; $j++) { $out += $extLines[$j] }
foreach ($l in $newBlock) { $out += $l }
for ($j = $blockEnd + 1; $j -lt $extLines.Count; $j++) { $out += $extLines[$j] }
[System.IO.File]::WriteAllText($extBsl, (($out -join "`r`n") + "`r`n"), $enc)
# transferred summary (shared by full-auto and partial)
function Write-TransferSummary {
param($items)
foreach ($t in $items) {
if ($t.Kind -eq 'insert') { Write-Host " • вставка после «$(Get-Truncated $t.Anchor)»" }
else { Write-Host " • удаление «$(Get-Truncated $t.Anchor)»" }
}
}
if ($disputed.Count -gt 0) {
# merge workspace (git-mergetool convention: base/local/remote/merged)
$tmpRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("cfe-resync\" + ($ModulePath -replace '[\\/:*?"<>|]', '_') + "." + $MethodName)
if (-not (Test-Path $tmpRoot)) { New-Item -ItemType Directory -Path $tmpRoot -Force | Out-Null }
[System.IO.File]::WriteAllText((Join-Path $tmpRoot "base.bsl"), (($v1 -join "`r`n") + "`r`n"), $enc)
[System.IO.File]::WriteAllText((Join-Path $tmpRoot "local.bsl"), (($markedBody -join "`r`n") + "`r`n"), $enc)
[System.IO.File]::WriteAllText((Join-Path $tmpRoot "remote.bsl"), (($v2 -join "`r`n") + "`r`n"), $enc)
[System.IO.File]::WriteAllText((Join-Path $tmpRoot "merged.bsl"), (($newBlock -join "`r`n") + "`r`n"), $enc)
$diff = @()
$diff += "--- base -> remote (что изменилось в оригинале) ---"
foreach ($l in $v1) { if ($v2norm -notcontains (Get-Normalized $l)) { $diff += "- $l" } }
foreach ($l in $v2) { if ($v1norm -notcontains (Get-Normalized $l)) { $diff += "+ $l" } }
[System.IO.File]::WriteAllText((Join-Path $tmpRoot "diff.txt"), (($diff -join "`r`n") + "`r`n"), $enc)
Write-Host "[АКТУАЛИЗИРОВАН-ЧАСТИЧНО] &ИзменениеИКонтроль(`"$MethodName`") — перенесено: $($transferred.Count), конфликтов: $($disputed.Count)"
Write-TransferSummary $transferred
Write-Host " Конфликтные блоки помечены // [РЕСИНК-КОНФЛИКТ] — разместите вручную."
Write-Host " Merge-воркспейс (base/local/remote/merged/diff):"
Write-Host " $tmpRoot"
} else {
Write-Host "[АКТУАЛИЗИРОВАН] &ИзменениеИКонтроль(`"$MethodName`") — тело обновлено, перенесено правок: $($transferred.Count)"
Write-TransferSummary $transferred
} }
Write-Host " Файл: $extBsl" Write-Host " Файл: $extBsl"
exit 0 exit 0
@@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# cfe-patch-method v2.1 — Source-aware method interceptor for 1C extension (CFE) # cfe-patch-method v2.2 — Source-aware method interceptor for 1C extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse import argparse
import os import os
import re import re
import shutil
import sys import sys
import tempfile import tempfile
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@@ -475,10 +476,12 @@ def main():
) )
parser.add_argument("-ExtensionPath", required=True) parser.add_argument("-ExtensionPath", required=True)
parser.add_argument("-ConfigPath", required=False, default="") parser.add_argument("-ConfigPath", required=False, default="")
parser.add_argument("-ModulePath", required=True) parser.add_argument("-ModulePath", required=False, default="")
parser.add_argument("-MethodName", required=True) parser.add_argument("-MethodName", required=False, default="")
parser.add_argument("-InterceptorType", required=True, parser.add_argument("-InterceptorType", required=False, default="",
choices=["Before", "After", "Instead", "ModificationAndControl"]) choices=["", "Before", "After", "Instead", "ModificationAndControl"])
parser.add_argument("-Check", action="store_true")
parser.add_argument("-Actualize", action="store_true")
args = parser.parse_args() args = parser.parse_args()
extension_path = args.ExtensionPath extension_path = args.ExtensionPath
@@ -486,6 +489,8 @@ def main():
module_path = args.ModulePath module_path = args.ModulePath
method_name = args.MethodName method_name = args.MethodName
interceptor_type = args.InterceptorType interceptor_type = args.InterceptorType
check_mode = args.Check
actualize_mode = args.Actualize
# --- Resolve extension path --- # --- Resolve extension path ---
if not os.path.isabs(extension_path): if not os.path.isabs(extension_path):
@@ -506,8 +511,111 @@ def main():
pn = props.find("md:NamePrefix", ns) pn = props.find("md:NamePrefix", ns)
if pn is not None and pn.text: if pn is not None and pn.text:
name_prefix = pn.text name_prefix = pn.text
nn = props.find("md:Name", ns)
if nn is not None and nn.text:
ext_name = nn.text
except ET.ParseError: except ET.ParseError:
pass pass
if 'ext_name' not in dir():
ext_name = "Расширение"
# --- Batch modes: -Check / -Actualize over &ИзменениеИКонтроль ---
if check_mode or actualize_mode:
if check_mode and actualize_mode:
die("Укажите либо -Check, либо -Actualize, не оба.")
if not config_path:
die("Для -Check/-Actualize нужен -ConfigPath (сверка с исходником).")
cp = config_path
if not os.path.isabs(cp):
cp = os.path.join(os.getcwd(), cp)
if os.path.isfile(cp):
cp = os.path.dirname(cp)
if not os.path.isfile(os.path.join(cp, "Configuration.xml")):
die("Configuration.xml не найден в конфигурации-источнике: %s" % cp)
report_only = check_mode
verb = "КОНТРОЛЬ" if check_mode else "АКТУАЛИЗАЦИЯ"
safe = re.sub(r'[\\/:*?"<>|]', "_", ext_name)
run_root = os.path.join(tempfile.gettempdir(), "cfe-resync", safe)
if actualize_mode and os.path.isdir(run_root):
shutil.rmtree(run_root, ignore_errors=True)
targets = []
if module_path:
rp = get_module_rel_path(module_path)
mb = os.path.join(extension_path, *rp)
if os.path.isfile(mb):
targets.append(mb)
else:
for dirpath, _dirs, files in os.walk(extension_path):
for fn in files:
if fn.endswith(".bsl"):
targets.append(os.path.join(dirpath, fn))
results = []
for tb in targets:
scan = read_lines(tb)
mnames = [ic["method"] for ic in get_interceptors(scan) if ic["type"] == "ИзменениеИКонтроль"]
if method_name:
mnames = [m for m in mnames if m.lower() == method_name.lower()]
seen = set(); uniq = []
for m in mnames:
if m.lower() not in seen:
seen.add(m.lower()); uniq.append(m)
if not uniq:
continue
rel = rel_parts_under(extension_path, tb)
logical_module = module_path_from_rel(rel)
src_bsl2 = os.path.join(cp, *rel)
rel_no_bsl = os.path.join(*rel)
if rel_no_bsl.endswith(".bsl"):
rel_no_bsl = rel_no_bsl[:-4]
for mname in uniq:
mid = "%s.%s" % (logical_module, mname)
if not os.path.isfile(src_bsl2):
results.append({"id": mid, "status": "ИСТОЧНИК-НЕ-НАЙДЕН", "ext_bsl": tb}); continue
m = extract_method(read_lines(src_bsl2), mname)
if not m:
results.append({"id": mid, "status": "МЕТОД-ИСЧЕЗ", "ext_bsl": tb}); continue
tb_lines = read_lines(tb)
ic = next((x for x in get_interceptors(tb_lines)
if x["type"] == "ИзменениеИКонтроль" and x["method"].lower() == mname.lower()), None)
if not ic:
continue
folder = os.path.join(run_root, rel_no_bsl, m["canonical"])
results.append(resync_one(tb, tb_lines, ic, m, logical_module, folder, report_only))
total = len(results)
actual = sum(1 for r in results if r["status"] == "АКТУАЛЕН")
print("[%s] %s -> %s (на контроле: %d)" % (verb, ext_name, cp, total))
listed = [r for r in results if r["status"] != "АКТУАЛЕН"]
for pr in listed:
line = " %-16s %s" % (pr["status"], pr["id"])
if pr.get("reason"):
line += " %s" % pr["reason"]
print(line)
if check_mode:
drift = sum(1 for r in results if r["status"] == "ДРЕЙФ")
confl = sum(1 for r in results if r["status"] == "КОНФЛИКТ")
gone = sum(1 for r in results if r["status"] in ("МЕТОД-ИСЧЕЗ", "ИСТОЧНИК-НЕ-НАЙДЕН"))
print("Итог: %d/%d актуальны · %d дрейф · %d конфликт · %d требуют внимания"
% (actual, total, drift, confl, gone))
if listed:
print("Починить: /cfe-patch-method -Actualize -ExtensionPath %s -ConfigPath %s" % (extension_path, cp))
sys.exit(1)
sys.exit(0)
else:
upd = sum(1 for r in results if r["status"] == "АКТУАЛИЗИРОВАН")
part = sum(1 for r in results if r["status"] == "ЧАСТИЧНО")
print("Итог: %d/%d актуальны · %d актуализировано · %d частично" % (actual, total, upd, part))
idx = write_resync_index(run_root, results, ext_name, cp, verb)
if idx:
print("Merge-воркспейс конфликтов (см. index.md): %s" % idx)
sys.exit(0)
# --- Generation mode: require ModulePath + MethodName + InterceptorType ---
if not (module_path and method_name and interceptor_type):
die("Нужны -ModulePath, -MethodName, -InterceptorType (генерация перехватчика). "
"Для проверки/актуализации контролируемых методов используйте -Check или -Actualize.")
# --- Resolve module file paths (ModulePath = logical name OR path to a .bsl) --- # --- Resolve module file paths (ModulePath = logical name OR path to a .bsl) ---
has_config = bool(config_path) has_config = bool(config_path)
@@ -580,7 +688,31 @@ def main():
% (decorator_ru, method_name)) % (decorator_ru, method_name))
print(" Файл: %s" % ext_bsl) print(" Файл: %s" % ext_bsl)
sys.exit(0) sys.exit(0)
resync(ext_bsl, ext_lines, dup, method, method_name, module_path) rel = rel_parts_under(extension_path, ext_bsl)
logical_module = module_path_from_rel(rel)
rel_no_bsl = os.path.join(*rel)
if rel_no_bsl.endswith(".bsl"):
rel_no_bsl = rel_no_bsl[:-4]
safe = re.sub(r'[\\/:*?"<>|]', "_", ext_name)
run_root = os.path.join(tempfile.gettempdir(), "cfe-resync", safe)
folder = os.path.join(run_root, rel_no_bsl, method["canonical"])
res = resync_one(ext_bsl, ext_lines, dup, method, logical_module, folder, False)
st = res["status"]
if st == "АКТУАЛЕН":
print('[АКТУАЛЕН] &ИзменениеИКонтроль("%s") — оригинал не менялся, изменений нет.' % method_name)
elif st == "АКТУАЛИЗИРОВАН":
print('[АКТУАЛИЗИРОВАН] &ИзменениеИКонтроль("%s") — тело обновлено, перенесено правок: %d'
% (method_name, res.get("transferred", 0)))
elif st == "ЧАСТИЧНО":
idx = write_resync_index(run_root, [res], ext_name, config_path, "АКТУАЛИЗАЦИЯ")
print('[АКТУАЛИЗИРОВАН-ЧАСТИЧНО] &ИзменениеИКонтроль("%s") — перенесено: %d, конфликтов: %d'
% (method_name, res.get("transferred", 0), res.get("disputed", 0)))
print(" Конфликт помечен // [РЕСИНК-КОНФЛИКТ]. Папка метода: %s" % res.get("conflict_dir"))
if idx:
print(" Индекс: %s" % idx)
else:
print('[%s] &ИзменениеИКонтроль("%s") — %s' % (st, method_name, res.get("reason", "")))
print(" Файл: %s" % ext_bsl)
sys.exit(0) sys.exit(0)
# --- New interceptor --- # --- New interceptor ---
@@ -688,16 +820,87 @@ def place_new(ext_bsl, ext_lines, ext_exists, chain, core):
place_new.placement = "создан модуль" place_new.placement = "создан модуль"
def resync(ext_bsl, ext_lines, dup, method, method_name, module_path): DIR_TO_TYPE = {
"Catalogs": "Catalog", "Documents": "Document", "Enums": "Enum", "CommonModules": "CommonModule",
"Reports": "Report", "DataProcessors": "DataProcessor", "ExchangePlans": "ExchangePlan",
"ChartsOfAccounts": "ChartOfAccounts", "ChartsOfCharacteristicTypes": "ChartOfCharacteristicTypes",
"ChartsOfCalculationTypes": "ChartOfCalculationTypes", "BusinessProcesses": "BusinessProcess",
"Tasks": "Task", "InformationRegisters": "InformationRegister",
"AccumulationRegisters": "AccumulationRegister", "AccountingRegisters": "AccountingRegister",
"CalculationRegisters": "CalculationRegister",
}
def module_path_from_rel(rel_parts):
dir0, name = rel_parts[0], rel_parts[1]
typ = DIR_TO_TYPE.get(dir0, dir0)
if dir0 == "CommonModules":
return "CommonModule.%s" % name
if len(rel_parts) >= 7 and rel_parts[2] == "Forms":
return "%s.%s.Form.%s" % (typ, name, rel_parts[3])
mod = rel_parts[-1][:-4] if rel_parts[-1].endswith(".bsl") else rel_parts[-1]
return "%s.%s.%s" % (typ, name, mod)
def rel_parts_under(root, full_path):
rel = os.path.relpath(os.path.abspath(full_path), os.path.abspath(root))
return [s for s in rel.replace("\\", "/").split("/") if s and s != "."]
def conflict_reason(disputed):
kinds = set(d["kind"] for d in disputed)
parts = []
if "insert" in kinds:
parts.append("якорь вставки изменён")
if "delete" in kinds:
parts.append("удаляемое исчезло")
return "; ".join(parts)
def write_conflict_folder(folder, method_id, ext_bsl, existing_name, method, v1, marked_body, v2, v1norm, v2norm, disputed):
os.makedirs(folder, exist_ok=True)
write_bsl(os.path.join(folder, "base.bsl"), v1)
write_bsl(os.path.join(folder, "local.bsl"), marked_body)
write_bsl(os.path.join(folder, "remote.bsl"), v2)
md = []
md.append("# %s" % method_id)
md.append("Править: %s" % ext_bsl)
md.append('Метод: %s (&ИзменениеИКонтроль("%s"))' % (existing_name, method["canonical"]))
md.append("Причина: %s" % conflict_reason(disputed))
md.append("")
md.append("## Не размещено — перенести в метод вручную")
for d in disputed:
if d["kind"] == "insert":
md.append("#Вставка"); md.extend(d["lines"]); md.append("#КонецВставки")
else:
md.append("// не удалось найти для удаления:")
for l in d["lines"]:
md.append("// " + l.strip())
md.append("")
md.append("## Дифф base→remote (что изменилось в оригинале)")
for l in v1:
if normalize(l) not in v2norm:
md.append("- " + l)
for l in v2:
if normalize(l) not in v1norm:
md.append("+ " + l)
md.append("")
md.append("Рядом: base.bsl / local.bsl / remote.bsl")
write_bsl(os.path.join(folder, "conflict.md"), md)
def resync_one(ext_bsl, ext_lines, dup, method, logical_module, conflict_folder, report_only):
method_id = "%s.%s" % (logical_module, method["canonical"])
dec_line = dup["line"] dec_line = dup["line"]
sig_line_idx = dec_line + 1 sig_line_idx = dec_line + 1
name_re = re.compile(r'^\s*(?:Асинх\s+)?(?:Процедура|Функция)\s+([\w]+)\s*\(', re.IGNORECASE) name_re = re.compile(r'^\s*(?:Асинх\s+)?(?:Процедура|Функция)\s+([\w]+)\s*\(', re.IGNORECASE)
if sig_line_idx >= len(ext_lines) or not name_re.match(ext_lines[sig_line_idx]): m0 = name_re.match(ext_lines[sig_line_idx]) if sig_line_idx < len(ext_lines) else None
die('Не удалось найти сигнатуру существующего перехватчика &ИзменениеИКонтроль("%s")' % method_name) if not m0:
existing_name = name_re.match(ext_lines[sig_line_idx]).group(1) return {"id": method_id, "status": "ОШИБКА", "ext_bsl": ext_bsl, "reason": "не разобрать сигнатуру перехватчика"}
existing_name = m0.group(1)
sig = read_signature(ext_lines, sig_line_idx) sig = read_signature(ext_lines, sig_line_idx)
if not sig: if not sig:
die("Не удалось разобрать сигнатуру существующего перехватчика") return {"id": method_id, "status": "ОШИБКА", "ext_bsl": ext_bsl, "reason": "не разобрать сигнатуру"}
_params, sig_end = sig _params, sig_end = sig
is_func = bool(re.match(r'^\s*(?:Асинх\s+)?Функция\b', ext_lines[sig_line_idx], re.IGNORECASE)) is_func = bool(re.match(r'^\s*(?:Асинх\s+)?Функция\b', ext_lines[sig_line_idx], re.IGNORECASE))
end_re = re.compile(r'^\s*КонецФункции\b' if is_func else r'^\s*КонецПроцедуры\b', re.IGNORECASE) end_re = re.compile(r'^\s*КонецФункции\b' if is_func else r'^\s*КонецПроцедуры\b', re.IGNORECASE)
@@ -707,29 +910,18 @@ def resync(ext_bsl, ext_lines, dup, method, method_name, module_path):
block_end = j block_end = j
break break
if block_end < 0: if block_end < 0:
die("Не найден конец существующего перехватчика") return {"id": method_id, "status": "ОШИБКА", "ext_bsl": ext_bsl, "reason": "не найден конец перехватчика"}
marked_body = ext_lines[sig_end + 1:block_end] marked_body = ext_lines[sig_end + 1:block_end]
parsed = parse_marked_body(marked_body) parsed = parse_marked_body(marked_body)
v1 = parsed["v1"] v1 = parsed["v1"]; ops = parsed["ops"]; v2 = method["body_lines"]
ops = parsed["ops"]
v2 = method["body_lines"]
v1norm = [normalize(x) for x in v1] v1norm = [normalize(x) for x in v1]
v2norm = [normalize(x) for x in v2] v2norm = [normalize(x) for x in v2]
if "\n".join(v1norm) == "\n".join(v2norm): if "\n".join(v1norm) == "\n".join(v2norm):
print('[АКТУАЛЕН] &ИзменениеИКонтроль("%s") — оригинал не менялся, изменений нет.' % method_name) return {"id": method_id, "status": "АКТУАЛЕН", "ext_bsl": ext_bsl}
print(" Файл: %s" % ext_bsl)
return
insert_top = []
insert_after = {}
del_start = set()
del_end = set()
disputed = []
transferred = [] # for the summary: {"kind", "anchor"}
insert_top = []; insert_after = {}; del_start = set(); del_end = set(); disputed = []; transferred = 0
for op in ops: for op in ops:
if op["kind"] == "insert": if op["kind"] == "insert":
after = op["after"] after = op["after"]
@@ -739,26 +931,25 @@ def resync(ext_bsl, ext_lines, dup, method, method_name, module_path):
if k is None: if k is None:
disputed.append({"kind": "insert", "lines": op["lines"]}) disputed.append({"kind": "insert", "lines": op["lines"]})
elif k < 0: elif k < 0:
insert_top.append(op["lines"]) insert_top.append(op["lines"]); transferred += 1
transferred.append({"kind": "insert", "anchor": "(в начало метода)"})
else: else:
insert_after.setdefault(k, []).append(op["lines"]) insert_after.setdefault(k, []).append(op["lines"]); transferred += 1
transferred.append({"kind": "insert", "anchor": v2[k]})
else: else:
keys = v1norm[op["start"]:op["end"] + 1] keys = v1norm[op["start"]:op["end"] + 1]
p = find_unique_run(v2norm, keys) p = find_unique_run(v2norm, keys)
if p >= 0: if p >= 0:
del_start.add(p) del_start.add(p); del_end.add(p + len(keys) - 1); transferred += 1
del_end.add(p + len(keys) - 1)
transferred.append({"kind": "delete", "anchor": op["lines"][0]})
else: else:
disputed.append({"kind": "delete", "lines": op["lines"]}) disputed.append({"kind": "delete", "lines": op["lines"]})
if report_only:
st = "КОНФЛИКТ" if disputed else "ДРЕЙФ"
return {"id": method_id, "status": st, "ext_bsl": ext_bsl, "transferred": transferred,
"disputed": len(disputed), "reason": conflict_reason(disputed)}
new_body = [] new_body = []
for blk in insert_top: for blk in insert_top:
new_body.append("#Вставка") new_body.append("#Вставка"); new_body.extend(blk); new_body.append("#КонецВставки")
new_body.extend(blk)
new_body.append("#КонецВставки")
for k in range(len(v2)): for k in range(len(v2)):
if k in del_start: if k in del_start:
new_body.append("#Удаление") new_body.append("#Удаление")
@@ -767,18 +958,13 @@ def resync(ext_bsl, ext_lines, dup, method, method_name, module_path):
new_body.append("#КонецУдаления") new_body.append("#КонецУдаления")
if k in insert_after: if k in insert_after:
for blk in insert_after[k]: for blk in insert_after[k]:
new_body.append("#Вставка") new_body.append("#Вставка"); new_body.extend(blk); new_body.append("#КонецВставки")
new_body.extend(blk)
new_body.append("#КонецВставки")
if disputed: if disputed:
new_body.append("\t// [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала.") new_body.append("\t// [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала.")
new_body.append("\t// Материалы для анализа см. в выводе команды (файлы base/local/remote/merged).") new_body.append("\t// Материалы: см. index.md / conflict.md в merge-воркспейсе (путь в выводе).")
for d in disputed: for d in disputed:
if d["kind"] == "insert": if d["kind"] == "insert":
new_body.append("#Вставка") new_body.append("#Вставка"); new_body.extend(d["lines"]); new_body.append("#КонецВставки")
new_body.extend(d["lines"])
new_body.append("#КонецВставки")
else: else:
new_body.append("\t// [РЕСИНК-КОНФЛИКТ] не удалось найти для удаления:") new_body.append("\t// [РЕСИНК-КОНФЛИКТ] не удалось найти для удаления:")
for l in d["lines"]: for l in d["lines"]:
@@ -798,48 +984,36 @@ def resync(ext_bsl, ext_lines, dup, method, method_name, module_path):
block_start = dec_line block_start = dec_line
if dec_line >= 1 and is_context_directive(ext_lines[dec_line - 1].strip()): if dec_line >= 1 and is_context_directive(ext_lines[dec_line - 1].strip()):
block_start = dec_line - 1 block_start = dec_line - 1
out = list(ext_lines[:block_start]) + new_block + list(ext_lines[block_end + 1:])
out = list(ext_lines[:block_start])
out.extend(new_block)
out.extend(ext_lines[block_end + 1:])
write_bsl(ext_bsl, out) write_bsl(ext_bsl, out)
def print_transfer_summary(items): conflict_dir = None
for t in items:
if t["kind"] == "insert":
print(" • вставка после «%s»" % truncate(t["anchor"]))
else:
print(" • удаление «%s»" % truncate(t["anchor"]))
if disputed: if disputed:
# merge workspace (git-mergetool convention: base/local/remote/merged) conflict_dir = conflict_folder
safe = re.sub(r'[\\/:*?"<>|]', '_', module_path) write_conflict_folder(conflict_folder, method_id, ext_bsl, existing_name, method, v1, marked_body, v2, v1norm, v2norm, disputed)
tmp_root = os.path.join(tempfile.gettempdir(), "cfe-resync", safe + "." + method_name) status = "ЧАСТИЧНО" if disputed else "АКТУАЛИЗИРОВАН"
os.makedirs(tmp_root, exist_ok=True) return {"id": method_id, "status": status, "ext_bsl": ext_bsl, "transferred": transferred,
write_bsl(os.path.join(tmp_root, "base.bsl"), v1) "disputed": len(disputed), "conflict_dir": conflict_dir, "reason": conflict_reason(disputed)}
write_bsl(os.path.join(tmp_root, "local.bsl"), marked_body)
write_bsl(os.path.join(tmp_root, "remote.bsl"), v2)
write_bsl(os.path.join(tmp_root, "merged.bsl"), new_block)
diff = ["--- base -> remote (что изменилось в оригинале) ---"]
for l in v1:
if normalize(l) not in v2norm:
diff.append("- " + l)
for l in v2:
if normalize(l) not in v1norm:
diff.append("+ " + l)
write_bsl(os.path.join(tmp_root, "diff.txt"), diff)
print('[АКТУАЛИЗИРОВАН-ЧАСТИЧНО] &ИзменениеИКонтроль("%s") — перенесено: %d, конфликтов: %d'
% (method_name, len(transferred), len(disputed))) def write_resync_index(run_root, results, ext_name, config_path, verb):
print_transfer_summary(transferred) conflicts = [r for r in results if r["status"] == "ЧАСТИЧНО"]
print(" Конфликтные блоки помечены // [РЕСИНК-КОНФЛИКТ] — разместите вручную.") if not conflicts:
print(" Merge-воркспейс (base/local/remote/merged/diff):") return None
print(" %s" % tmp_root) os.makedirs(run_root, exist_ok=True)
else: total = len(results)
print('[АКТУАЛИЗИРОВАН] &ИзменениеИКонтроль("%s") — тело обновлено, перенесено правок: %d' actual = sum(1 for r in results if r["status"] == "АКТУАЛЕН")
% (method_name, len(transferred))) upd = sum(1 for r in results if r["status"] == "АКТУАЛИЗИРОВАН")
print_transfer_summary(transferred) lines = []
print(" Файл: %s" % ext_bsl) lines.append("[%s] %s -> %s" % (verb, ext_name, config_path))
lines.append("Итог: %d/%d актуальны · %d актуализировано · %d конфликтов" % (actual, total, upd, len(conflicts)))
lines.append("")
lines.append("Конфликты — править .bsl расширения:")
for c in conflicts:
lines.append(" ЧАСТИЧНО %s" % c["id"])
lines.append(" -> %s (%s)" % (c["ext_bsl"], c.get("reason", "")))
write_bsl(os.path.join(run_root, "index.md"), lines)
return run_root
if __name__ == "__main__": if __name__ == "__main__":
@@ -930,6 +930,17 @@ if ($script:borrowedFormsWithTree.Count -eq 0) {
Report-OK "13. TypeLink: clean" Report-OK "13. TypeLink: clean"
} }
# --- Breadcrumb: controlled methods (&ИзменениеИКонтроль) drift is not checked here ---
$extRootDir = Split-Path $resolvedPath -Parent
$ctrlCount = 0
foreach ($bslFile in (Get-ChildItem -Path $extRootDir -Recurse -Filter *.bsl -File -ErrorAction SilentlyContinue)) {
$txt = [System.IO.File]::ReadAllText($bslFile.FullName, [System.Text.Encoding]::UTF8)
$ctrlCount += ([regex]::Matches($txt, '(?m)^\s*&ИзменениеИКонтроль\(')).Count
}
if ($ctrlCount -gt 0) {
Out-Line "[INFO] Контролируемых методов (&ИзменениеИКонтроль): $ctrlCount — их актуальность здесь не проверяется. Сверьте: /cfe-patch-method -Check -ExtensionPath <ext> -ConfigPath <cf>"
}
# --- Final output --- # --- Final output ---
& $finalize & $finalize
@@ -885,6 +885,21 @@ def main():
elif check13_ok: elif check13_ok:
r.ok('13. TypeLink: clean') r.ok('13. TypeLink: clean')
# --- Breadcrumb: controlled methods (&ИзменениеИКонтроль) drift is not checked here ---
ctrl_count = 0
for dp, _dn, files in os.walk(config_dir):
for fn in files:
if fn.endswith('.bsl'):
try:
with open(os.path.join(dp, fn), 'r', encoding='utf-8-sig') as f:
for ln in f:
if re.match(r'^\s*&ИзменениеИКонтроль\(', ln):
ctrl_count += 1
except OSError:
pass
if ctrl_count > 0:
r.out('[INFO] Контролируемых методов (&ИзменениеИКонтроль): %d — их актуальность здесь не проверяется. Сверьте: /cfe-patch-method -Check -ExtensionPath <ext> -ConfigPath <cf>' % ctrl_count)
# --- Final output --- # --- Final output ---
r.finalize(out_file) r.finalize(out_file)
sys.exit(1 if r.errors > 0 else 0) sys.exit(1 if r.errors > 0 else 0)
@@ -0,0 +1,33 @@
{
"name": "-Actualize: батч по всему расширению — дрейф чинится авто",
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "CommonModule", "name": "БатчМод", "properties": { "Server": true } },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{
"writeFile": {
"path": "CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n\nФункция РасчётА() Экспорт\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n\nФункция РасчётБ() Экспорт\n\tY = 2;\n\tW = 5;\n\tВозврат Y;\nКонецФункции\n\n#КонецОбласти\n"
}
},
{
"script": "cfe-init/scripts/cfe-init",
"args": { "-Name": "Тест", "-OutputDir": "{workDir}/ext", "-ConfigPath": "{workDir}" }
},
{
"script": "cfe-borrow/scripts/cfe-borrow",
"args": { "-ExtensionPath": "{workDir}/ext", "-ConfigPath": "{workDir}", "-Object": "CommonModule.БатчМод" }
},
{
"writeFile": {
"path": "ext/CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n&ИзменениеИКонтроль(\"РасчётА\")\nФункция Тест_РасчётА()\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n&ИзменениеИКонтроль(\"РасчётБ\")\nФункция Тест_РасчётБ()\n\tY = 2;\n\tВозврат Y;\nКонецФункции\n#КонецОбласти\n"
}
}
],
"params": { "extensionPath": "ext" },
"args_extra": ["-Actualize"],
"expect": { "stdoutContains": "1 актуализировано" }
}
@@ -0,0 +1,33 @@
{
"name": "-Check: все контролируемые методы актуальны (exit 0)",
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "CommonModule", "name": "БатчМод", "properties": { "Server": true } },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{
"writeFile": {
"path": "CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n\nФункция РасчётА() Экспорт\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n\nФункция РасчётБ() Экспорт\n\tY = 2;\n\tВозврат Y;\nКонецФункции\n\n#КонецОбласти\n"
}
},
{
"script": "cfe-init/scripts/cfe-init",
"args": { "-Name": "Тест", "-OutputDir": "{workDir}/ext", "-ConfigPath": "{workDir}" }
},
{
"script": "cfe-borrow/scripts/cfe-borrow",
"args": { "-ExtensionPath": "{workDir}/ext", "-ConfigPath": "{workDir}", "-Object": "CommonModule.БатчМод" }
},
{
"writeFile": {
"path": "ext/CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n&ИзменениеИКонтроль(\"РасчётА\")\nФункция Тест_РасчётА()\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n&ИзменениеИКонтроль(\"РасчётБ\")\nФункция Тест_РасчётБ()\n\tY = 2;\n\tВозврат Y;\nКонецФункции\n#КонецОбласти\n"
}
}
],
"params": { "extensionPath": "ext" },
"args_extra": ["-Check"],
"expect": { "stdoutContains": "2/2 актуальны" }
}
@@ -0,0 +1,33 @@
{
"name": "-Check: дрейф контролируемого метода (exit 1, ничего не пишет)",
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "CommonModule", "name": "БатчМод", "properties": { "Server": true } },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{
"writeFile": {
"path": "CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n\nФункция РасчётА() Экспорт\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n\nФункция РасчётБ() Экспорт\n\tY = 2;\n\tW = 5;\n\tВозврат Y;\nКонецФункции\n\n#КонецОбласти\n"
}
},
{
"script": "cfe-init/scripts/cfe-init",
"args": { "-Name": "Тест", "-OutputDir": "{workDir}/ext", "-ConfigPath": "{workDir}" }
},
{
"script": "cfe-borrow/scripts/cfe-borrow",
"args": { "-ExtensionPath": "{workDir}/ext", "-ConfigPath": "{workDir}", "-Object": "CommonModule.БатчМод" }
},
{
"writeFile": {
"path": "ext/CommonModules/БатчМод/Ext/Module.bsl",
"content": "#Область ПрограммныйИнтерфейс\n&ИзменениеИКонтроль(\"РасчётА\")\nФункция Тест_РасчётА()\n\tХ = 1;\n\tВозврат Х;\nКонецФункции\n&ИзменениеИКонтроль(\"РасчётБ\")\nФункция Тест_РасчётБ()\n\tY = 2;\n\tВозврат Y;\nКонецФункции\n#КонецОбласти\n"
}
}
],
"params": { "extensionPath": "ext" },
"args_extra": ["-Check"],
"expectError": true
}
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<CommonModule uuid="UUID-001">
<Properties>
<Name>БатчМод</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Батч мод</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>false</Server>
<ExternalConnection>false</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>false</ServerCall>
<Privileged>false</Privileged>
<ReturnValuesReuse>DontUse</ReturnValuesReuse>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,14 @@
#Область ПрограммныйИнтерфейс
Функция РасчётА() Экспорт
Х = 1;
Возврат Х;
КонецФункции
Функция РасчётБ() Экспорт
Y = 2;
W = 5;
Возврат Y;
КонецФункции
#КонецОбласти
@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="utf-8"?>
<MetaDataObject 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">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name>TestConfig</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>TestConfig</v8:content>
</v8:item>
</Synonym>
<Comment />
<NamePrefix />
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
<AdditionalFullTextSearchDictionaries />
<CommonSettingsStorage />
<ReportsUserSettingsStorage />
<ReportsVariantsStorage />
<FormDataSettingsStorage />
<DynamicListsUserSettingsStorage />
<URLExternalDataStorage />
<Content />
<DefaultReportForm />
<DefaultReportVariantForm />
<DefaultReportSettingsForm />
<DefaultReportAppearanceTemplate />
<DefaultDynamicListSettingsForm />
<DefaultSearchForm />
<DefaultDataHistoryChangeHistoryForm />
<DefaultDataHistoryVersionDataForm />
<DefaultDataHistoryVersionDifferencesForm />
<DefaultCollaborationSystemUsersChoiceForm />
<RequiredMobileApplicationPermissions />
<UsedMobileApplicationFunctionalities>
<app:functionality>
<app:functionality>Biometrics</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Location</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundLocation</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BluetoothPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>WiFiPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Contacts</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Calendars</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PushNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>LocalNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InAppPurchases</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PersonalComputerFileExchange</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Ads</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NumberDialing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AutoSendSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ReceiveSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SMSLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Camera</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Microphone</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>MusicLibrary</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PictureAndVideoLibraries</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InstallPackages</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>OSBackup</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ApplicationUsageStatistics</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BarcodeScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioRecording</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllFilesAccess</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Videoconferences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NFC</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>DocumentScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SpeechToText</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Geofences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>IncomingShareRequests</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllIncomingShareRequestsTypesProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
</UsedMobileApplicationFunctionalities>
<StandaloneConfigurationRestrictionRoles />
<MobileApplicationURLs />
<AllowedIncomingShareRequestTypes />
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
<DefaultInterface />
<DefaultStyle />
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<DataLockControlMode>Managed</DataLockControlMode>
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
<ModalityUseMode>DontUse</ModalityUseMode>
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultConstantsForm />
</Properties>
<ChildObjects>
<Language>Русский</Language>
<CommonModule>БатчМод</CommonModule>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterfaceLayouter">
<top>
<panel id="UUID-001">
<uuid>UUID-002</uuid>
</panel>
</top>
<left>
<panel id="UUID-003">
<uuid>UUID-004</uuid>
</panel>
</left>
<panelDef id="UUID-004"/>
<panelDef id="UUID-005"/>
<panelDef id="UUID-006"/>
<panelDef id="UUID-002"/>
<panelDef id="UUID-007"/>
</ClientApplicationInterface>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<CommonModule uuid="UUID-001">
<InternalInfo/>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>БатчМод</Name>
<Comment/>
<ExtendedConfigurationObject>UUID-002</ExtendedConfigurationObject>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>false</Server>
<ExternalConnection>false</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>false</ServerCall>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,13 @@
#Область ПрограммныйИнтерфейс
&ИзменениеИКонтроль("РасчётА")
Функция Тест_РасчётА()
Х = 1;
Возврат Х;
КонецФункции
&ИзменениеИКонтроль("РасчётБ")
Функция Тест_РасчётБ()
Y = 2;
W = 5;
Возврат Y;
КонецФункции
#КонецОбласти
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>Тест</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Тест</v8:content>
</v8:item>
</Synonym>
<Comment />
<ConfigurationExtensionPurpose>Customization</ConfigurationExtensionPurpose>
<KeepMappingToExtendedConfigurationObjectsByIDs>true</KeepMappingToExtendedConfigurationObjectsByIDs>
<NamePrefix>Тест_</NamePrefix>
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles>
<xr:Item xsi:type="xr:MDObjectRef">Role.Тест_ОсновнаяРоль</xr:Item>
</DefaultRoles>
<Vendor></Vendor>
<Version></Version>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
</Properties>
<ChildObjects>
<Language>Русский</Language>
<Role>Тест_ОсновнаяРоль</Role>
<CommonModule>БатчМод</CommonModule>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Language uuid="UUID-001">
<InternalInfo/>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>Русский</Name>
<Comment/>
<ExtendedConfigurationObject>UUID-002</ExtendedConfigurationObject>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Role uuid="UUID-001">
<Properties>
<Name>Тест_ОсновнаяРоль</Name>
<Synonym/>
<Comment/>
</Properties>
</Role>
</MetaDataObject>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Language uuid="UUID-001">
<Properties>
<Name>Русский</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Русский</v8:content>
</v8:item>
</Synonym>
<Comment/>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<CommonModule uuid="UUID-001">
<Properties>
<Name>БатчМод</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Батч мод</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>false</Server>
<ExternalConnection>false</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>false</ServerCall>
<Privileged>false</Privileged>
<ReturnValuesReuse>DontUse</ReturnValuesReuse>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,13 @@
#Область ПрограммныйИнтерфейс
Функция РасчётА() Экспорт
Х = 1;
Возврат Х;
КонецФункции
Функция РасчётБ() Экспорт
Y = 2;
Возврат Y;
КонецФункции
#КонецОбласти
@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="utf-8"?>
<MetaDataObject 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">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name>TestConfig</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>TestConfig</v8:content>
</v8:item>
</Synonym>
<Comment />
<NamePrefix />
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles />
<Vendor></Vendor>
<Version></Version>
<UpdateCatalogAddress />
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
<AdditionalFullTextSearchDictionaries />
<CommonSettingsStorage />
<ReportsUserSettingsStorage />
<ReportsVariantsStorage />
<FormDataSettingsStorage />
<DynamicListsUserSettingsStorage />
<URLExternalDataStorage />
<Content />
<DefaultReportForm />
<DefaultReportVariantForm />
<DefaultReportSettingsForm />
<DefaultReportAppearanceTemplate />
<DefaultDynamicListSettingsForm />
<DefaultSearchForm />
<DefaultDataHistoryChangeHistoryForm />
<DefaultDataHistoryVersionDataForm />
<DefaultDataHistoryVersionDifferencesForm />
<DefaultCollaborationSystemUsersChoiceForm />
<RequiredMobileApplicationPermissions />
<UsedMobileApplicationFunctionalities>
<app:functionality>
<app:functionality>Biometrics</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Location</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundLocation</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BluetoothPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>WiFiPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Contacts</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Calendars</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PushNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>LocalNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InAppPurchases</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PersonalComputerFileExchange</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Ads</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NumberDialing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AutoSendSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ReceiveSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SMSLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Camera</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Microphone</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>MusicLibrary</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PictureAndVideoLibraries</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InstallPackages</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>OSBackup</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ApplicationUsageStatistics</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BarcodeScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioRecording</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllFilesAccess</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Videoconferences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NFC</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>DocumentScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SpeechToText</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Geofences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>IncomingShareRequests</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllIncomingShareRequestsTypesProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
</UsedMobileApplicationFunctionalities>
<StandaloneConfigurationRestrictionRoles />
<MobileApplicationURLs />
<AllowedIncomingShareRequestTypes />
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
<DefaultInterface />
<DefaultStyle />
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<DataLockControlMode>Managed</DataLockControlMode>
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
<ModalityUseMode>DontUse</ModalityUseMode>
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultConstantsForm />
</Properties>
<ChildObjects>
<Language>Русский</Language>
<CommonModule>БатчМод</CommonModule>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterfaceLayouter">
<top>
<panel id="UUID-001">
<uuid>UUID-002</uuid>
</panel>
</top>
<left>
<panel id="UUID-003">
<uuid>UUID-004</uuid>
</panel>
</left>
<panelDef id="UUID-004"/>
<panelDef id="UUID-005"/>
<panelDef id="UUID-006"/>
<panelDef id="UUID-002"/>
<panelDef id="UUID-007"/>
</ClientApplicationInterface>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<CommonModule uuid="UUID-001">
<InternalInfo/>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>БатчМод</Name>
<Comment/>
<ExtendedConfigurationObject>UUID-002</ExtendedConfigurationObject>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>false</Server>
<ExternalConnection>false</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>false</ServerCall>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,12 @@
#Область ПрограммныйИнтерфейс
&ИзменениеИКонтроль("РасчётА")
Функция Тест_РасчётА()
Х = 1;
Возврат Х;
КонецФункции
&ИзменениеИКонтроль("РасчётБ")
Функция Тест_РасчётБ()
Y = 2;
Возврат Y;
КонецФункции
#КонецОбласти
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>Тест</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Тест</v8:content>
</v8:item>
</Synonym>
<Comment />
<ConfigurationExtensionPurpose>Customization</ConfigurationExtensionPurpose>
<KeepMappingToExtendedConfigurationObjectsByIDs>true</KeepMappingToExtendedConfigurationObjectsByIDs>
<NamePrefix>Тест_</NamePrefix>
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles>
<xr:Item xsi:type="xr:MDObjectRef">Role.Тест_ОсновнаяРоль</xr:Item>
</DefaultRoles>
<Vendor></Vendor>
<Version></Version>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
</Properties>
<ChildObjects>
<Language>Русский</Language>
<Role>Тест_ОсновнаяРоль</Role>
<CommonModule>БатчМод</CommonModule>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Language uuid="UUID-001">
<InternalInfo/>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>Русский</Name>
<Comment/>
<ExtendedConfigurationObject>UUID-002</ExtendedConfigurationObject>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Role uuid="UUID-001">
<Properties>
<Name>Тест_ОсновнаяРоль</Name>
<Synonym/>
<Comment/>
</Properties>
</Role>
</MetaDataObject>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject 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">
<Language uuid="UUID-001">
<Properties>
<Name>Русский</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Русский</v8:content>
</v8:item>
</Synonym>
<Comment/>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -3,7 +3,7 @@
Функция Тест_Расчёт(А, Б) Функция Тест_Расчёт(А, Б)
Возврат 0; Возврат 0;
// [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала. // [РЕСИНК-КОНФЛИКТ] перенесите блоки ниже вручную — исходный якорь изменился в новой версии оригинала.
// Материалы для анализа см. в выводе команды (файлы base/local/remote/merged). // Материалы: см. index.md / conflict.md в merge-воркспейсе (путь в выводе).
#Вставка #Вставка
Итог = Итог + Б; Итог = Итог + Б;
#КонецВставки #КонецВставки