diff --git a/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1 b/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1
index eddd9afa..a9789188 100644
--- a/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1
+++ b/.claude/skills/cfe-borrow/scripts/cfe-borrow.ps1
@@ -1,4 +1,4 @@
-# cfe-borrow v1.27 — Borrow objects from configuration into extension (CFE)
+# cfe-borrow v1.28 — Borrow objects from configuration into extension (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)][string]$ExtensionPath,
@@ -75,6 +75,50 @@ function Rewrite-ChoiceParameterLinks {
return $xml
}
+# Имена ПРЯМЫХ детей собственного объекта — для дедупа при повторном
+# заимствовании. Текстом это не снять: regex «первый до первого »
+# у объекта с табличными частями обрывается на закрытии первой ТЧ, забирает имена её колонок и
+# теряет то, что идёт после неё.
+function Get-OwnChildObjectNames {
+ param([string]$objFile)
+
+ $names = @{}
+ if (-not (Test-Path -LiteralPath $objFile)) { return $names }
+ $doc = New-Object System.Xml.XmlDocument
+ $doc.PreserveWhitespace = $false
+ try { $doc.Load($objFile) } catch { return $names }
+ $objEl = $null
+ foreach ($c in $doc.DocumentElement.ChildNodes) {
+ if ($c.NodeType -eq 'Element') { $objEl = $c; break }
+ }
+ if (-not $objEl) { return $names }
+ $childObjs = $objEl.SelectSingleNode("*[local-name()='ChildObjects']")
+ if (-not $childObjs) { return $names }
+ foreach ($child in $childObjs.ChildNodes) {
+ if ($child.NodeType -ne 'Element') { continue }
+ $nameNode = $child.SelectSingleNode("*[local-name()='Properties']/*[local-name()='Name']")
+ if ($nameNode) { $names[$nameNode.InnerText.Trim()] = $true }
+ }
+ return $names
+}
+
+# Вставка в СОБСТВЕННЫЙ объекта. Свой контейнер закрывается в файле последним:
+# объект в файле один, а вложенные табличных частей закрываются раньше. Замена по
+# всем вхождениям раскидывала реквизиты по каждой ТЧ — ps1 рвал XML, py прятал ТЧ внутрь ТЧ.
+function Insert-IntoOwnChildObjects {
+ param([string]$text, [string]$content)
+
+ $closeIdx = $text.LastIndexOf('')
+ if ($closeIdx -ge 0) {
+ return $text.Substring(0, $closeIdx) + "${content}`r`n`t`t" + $text.Substring($closeIdx)
+ }
+ # Своего закрывающего тега нет — значит контейнер самозакрытый (детей у него нет, вложенных тоже)
+ $selfMatches = [regex]::Matches($text, '')
+ if ($selfMatches.Count -eq 0) { return $text }
+ $m = $selfMatches[$selfMatches.Count - 1]
+ return $text.Substring(0, $m.Index) + "${content}`r`n`t`t" + $text.Substring($m.Index + $m.Length)
+}
+
# --- 1. Resolve paths ---
if (-not [System.IO.Path]::IsPathRooted($ExtensionPath)) {
$ExtensionPath = Join-Path (Get-Location).Path $ExtensionPath
@@ -1597,14 +1641,9 @@ function Merge-AttributesIntoObject {
if ($text3.Length -gt 0 -and $text3[0] -eq [char]0xFEFF) { $text3 = $text3.Substring(1) }
$text3 = $text3.Replace('encoding="utf-8"', 'encoding="UTF-8"')
- # Insert attributes — handle both and ....
- # Самозакрытый элемент раскрывается здесь же, а не пробельным узлом в DOM: тот давал
+ # Самозакрытый элемент раскрывается текстом, а не пробельным узлом в DOM: тот давал
# лишнюю строку с табуляцией перед первым (у Конфигуратора пустых строк нет).
- if ($text3 -match '') {
- $text3 = [regex]::Replace($text3, '', "${allAttrXml}`r`n`t`t")
- } else {
- $text3 = $text3.Replace('', "${allAttrXml}`r`n`t`t")
- }
+ $text3 = Insert-IntoOwnChildObjects $text3 $allAttrXml
# Пустой элемент: XmlWriter отдаёт ``, Конфигуратор пишет ``. Внутри
# CDATA/комментария ` />` может быть содержимым (там `>` не экранируется),
@@ -1664,12 +1703,7 @@ function Borrow-MainAttribute {
$objContent = [System.IO.File]::ReadAllText($objFile, (New-Object System.Text.UTF8Encoding($true)))
# Dedup: skip attributes/TS already present in object's ChildObjects (idempotent re-borrow)
- $existingChildNames = @{}
- if ($objContent -match '(?s)(.*?)') {
- foreach ($nm in [regex]::Matches($Matches[1], '(\w+)')) {
- $existingChildNames[$nm.Groups[1].Value] = $true
- }
- }
+ $existingChildNames = Get-OwnChildObjectNames $objFile
$insertAttrs = @($srcAttrs | Where-Object { -not $existingChildNames.ContainsKey($_.Name) })
$insertTS = @($srcTS | Where-Object { -not $existingChildNames.ContainsKey($_.Name) })
@@ -1701,17 +1735,9 @@ function Borrow-MainAttribute {
}
}
- # Replace empty ChildObjects with adopted content
+ # Добавить заимствованное содержимое в ChildObjects объекта (там уже может лежать