mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-20 09:15:52 +03:00
fix(role-compile,role-edit): шесть находок ревью
1. Резолв uuid вложенного объекта переписан со сквозной регулярки на спуск по дереву. Поиск по всему файлу брал первый узел с подходящим именем, а реквизит шапки и реквизит табличной части сплошь и рядом называются одинаково — ключ сортировки был чужим и вдобавок дублировался, из-за чего порты могли разложить узлы по-разному. Дочерние подсистемы лежат отдельными файлами и не резолвились вовсе: 431 узел в ролях ACC. Теперь на реальной роли резолвится 90% узлов, остальное — стандартные реквизиты, у которых uuid в выгрузке нет. Для них предупреждение больше не печатается: это норма, а не потеря. 2. Фильтр умолчаний уносил право вместе с его ограничением RLS: условие пропадало молча. Право с ограничением отличается от умолчания самим ограничением и остаётся. 3. modify-property не обновлял кэш умолчаний, и следующие операции того же вызова фильтровали по старому флагу — запись попадала в файл ровно вопреки тому, о чём навык сам предупреждает. 4. Edit-RoleMetadata перечитывал Roles/Имя.xml на каждой операции, и в пакете первая правка молча терялась: set-synonym + set-comment сохранял только комментарий. В py-порте такого не было — расхождение портов закрыто. 5. Атрибуция сообщения в PS шла подстрокой по всему списку отброшенного, из-за чего причина приписывалась чужому объекту. Теперь префиксное сравнение, как в py. Три новых кейса, семьи get_rights_object_uuid и is_standard_kind заведены в check-inline-drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FGkXwoXTuafcu1SXMsauFq
This commit is contained in:
co-authored by
Claude Opus 5
parent
5ec732e12b
commit
5b00410485
@@ -1,4 +1,4 @@
|
||||
# role-compile v1.44 — Compile 1C role from JSON
|
||||
# role-compile v1.45 — Compile 1C role from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
[CmdletBinding(PositionalBinding=$false)]
|
||||
param(
|
||||
@@ -780,7 +780,20 @@ function Sort-RightsCanonical {
|
||||
return $sorted
|
||||
}
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — из его узла.
|
||||
# У стандартных реквизитов и стандартных табличных частей uuid в выгрузке нет: они системные.
|
||||
# Отсутствие uuid для них — норма, а не потерянный объект.
|
||||
function Test-StandardKind {
|
||||
param([string]$objName)
|
||||
$parts = $objName -split '\\.'
|
||||
if ($parts.Count -lt 3) { return $false }
|
||||
return $parts[$parts.Count-2].StartsWith("Standard")
|
||||
}
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — спуском по дереву.
|
||||
# Искать регуляркой по всему файлу нельзя: реквизит шапки и реквизит табличной части часто
|
||||
# называются одинаково, и поиск нашёл бы первый попавшийся. Дочерние подсистемы лежат
|
||||
# отдельными файлами, поэтому для них спуск идёт по каталогам.
|
||||
# У стандартных реквизитов uuid в выгрузке нет вовсе — для них возвращаем $null молча.
|
||||
function Get-RightsObjectUuid {
|
||||
param([string]$objName, [string]$configRoot)
|
||||
$parts = $objName -split '\.'
|
||||
@@ -793,18 +806,33 @@ function Get-RightsObjectUuid {
|
||||
}
|
||||
$dir = $script:typeDirs[$parts[0]]
|
||||
if (-not $dir -or $parts.Count -lt 2) { return $null }
|
||||
# Подсистемы вложены каталогами: Subsystems/Родитель/Subsystems/Ребёнок.xml
|
||||
$ownerPath = Join-Path (Join-Path $configRoot $dir) "$($parts[1]).xml"
|
||||
if (-not (Test-Path $ownerPath)) { return $null }
|
||||
$text = [System.IO.File]::ReadAllText($ownerPath)
|
||||
if ($parts.Count -eq 2) {
|
||||
if ($text -match "<$($parts[0]) uuid=`"([0-9a-fA-F-]+)`"") { return $Matches[1] }
|
||||
return $null
|
||||
$i = 2
|
||||
while ($parts.Count -gt $i + 1 -and $parts[$i] -eq 'Subsystem') {
|
||||
$ownerDir = [System.IO.Path]::Combine($configRoot, $dir, ($parts[1..($i-1)] -join [System.IO.Path]::DirectorySeparatorChar + 'Subsystems' + [System.IO.Path]::DirectorySeparatorChar))
|
||||
$ownerPath = Join-Path (Join-Path ([System.IO.Path]::GetDirectoryName($ownerPath)) ([System.IO.Path]::GetFileNameWithoutExtension($ownerPath))) (Join-Path "Subsystems" "$($parts[$i+1]).xml")
|
||||
$i += 2
|
||||
}
|
||||
# Вложенный: вид — предпоследний сегмент, имя — последний.
|
||||
$kind = [regex]::Escape($parts[$parts.Count-2])
|
||||
$name = [regex]::Escape($parts[$parts.Count-1])
|
||||
$rx = "<$kind uuid=`"([0-9a-fA-F-]+)`"[^>]*>\s*<Properties>\s*<Name>$name</Name>"
|
||||
if ($text -match $rx) { return $Matches[1] }
|
||||
if (-not (Test-Path $ownerPath)) { return $null }
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
try { $doc.Load($ownerPath) } catch { return $null }
|
||||
$nsm = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$nsm.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$node = $doc.DocumentElement.FirstChild
|
||||
while ($node -and $node.NodeType -ne 'Element') { $node = $node.NextSibling }
|
||||
if (-not $node) { return $null }
|
||||
# Оставшиеся пары «вид, имя» ищем строго внутри текущего узла.
|
||||
while ($i + 1 -lt $parts.Count) {
|
||||
$kind = $parts[$i]
|
||||
$name = $parts[$i+1]
|
||||
$child = $node.SelectSingleNode("md:ChildObjects/md:$kind[md:Properties/md:Name='$name']", $nsm)
|
||||
if (-not $child) { return $null }
|
||||
$node = $child
|
||||
$i += 2
|
||||
}
|
||||
if ($node.HasAttribute("uuid")) { return $node.GetAttribute("uuid") }
|
||||
return $null
|
||||
}
|
||||
|
||||
@@ -817,7 +845,9 @@ function Sort-ObjectsByUuid {
|
||||
$uuid = Get-RightsObjectUuid -objName $o.Name -configRoot $configRoot
|
||||
if ($uuid) { $known += ,[pscustomobject]@{ Uuid = $uuid; Obj = $o } }
|
||||
else {
|
||||
[Console]::Error.WriteLine("[role-compile] $($o.Name): объект не найден в выгрузке, uuid неизвестен — узел записан в конец (платформа переставит его при первой выгрузке)")
|
||||
if (-not (Test-StandardKind $o.Name)) {
|
||||
[Console]::Error.WriteLine("[role-compile] $($o.Name): объект не найден в выгрузке, uuid неизвестен — узел записан в конец (платформа переставит его при первой выгрузке)")
|
||||
}
|
||||
$unknown += ,$o
|
||||
}
|
||||
}
|
||||
@@ -1526,7 +1556,9 @@ foreach ($obj in $parsedObjects) {
|
||||
$defaultValue = Get-DefaultRightValue $obj.Name $sfno $sfab
|
||||
$kept = @()
|
||||
foreach ($right in $obj.Rights) {
|
||||
if ($right.Value -eq $defaultValue) { $droppedByDefault += "$($obj.Name).$($right.Name)"; continue }
|
||||
# Право с ограничением отличается от умолчания самим ограничением — его платформа хранит,
|
||||
# и выбросить его значило бы молча потерять написанное условие.
|
||||
if ($right.Value -eq $defaultValue -and -not $right.Condition) { $droppedByDefault += "$($obj.Name).$($right.Name)"; continue }
|
||||
$kept += ,$right
|
||||
}
|
||||
$obj.Rights = $kept
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-compile v1.44 — Compile 1C role from JSON
|
||||
# role-compile v1.45 — Compile 1C role from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import json
|
||||
@@ -1086,8 +1086,21 @@ def sort_rights_canonical(object_name, rights):
|
||||
return sorted_rights
|
||||
|
||||
|
||||
# У стандартных реквизитов и стандартных табличных частей uuid в выгрузке нет: они системные.
|
||||
# Отсутствие uuid для них — норма, а не потерянный объект.
|
||||
def is_standard_kind(object_name):
|
||||
parts = object_name.split('.')
|
||||
if len(parts) < 3:
|
||||
return False
|
||||
return parts[-2].startswith("Standard")
|
||||
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — спуском по дереву.
|
||||
# Искать регуляркой по всему файлу нельзя: реквизит шапки и реквизит табличной части часто
|
||||
# называются одинаково, и поиск нашёл бы первый попавшийся. Дочерние подсистемы лежат
|
||||
# отдельными файлами, поэтому для них спуск идёт по каталогам.
|
||||
# У стандартных реквизитов uuid в выгрузке нет вовсе — для них возвращаем None молча.
|
||||
def get_rights_object_uuid(object_name, config_root):
|
||||
"""uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — из его узла."""
|
||||
parts = object_name.split('.')
|
||||
if parts[0] == 'Configuration':
|
||||
cfg_path = os.path.join(config_root, 'Configuration.xml')
|
||||
@@ -1099,19 +1112,36 @@ def get_rights_object_uuid(object_name, config_root):
|
||||
directory = TYPE_DIRS.get(parts[0])
|
||||
if not directory or len(parts) < 2:
|
||||
return None
|
||||
# Подсистемы вложены каталогами: Subsystems/Родитель/Subsystems/Ребёнок.xml
|
||||
owner_path = os.path.join(config_root, directory, parts[1] + '.xml')
|
||||
i = 2
|
||||
while len(parts) > i + 1 and parts[i] == 'Subsystem':
|
||||
owner_path = os.path.join(os.path.splitext(owner_path)[0], 'Subsystems', parts[i + 1] + '.xml')
|
||||
i += 2
|
||||
if not os.path.isfile(owner_path):
|
||||
return None
|
||||
with open(owner_path, 'r', encoding='utf-8-sig') as f:
|
||||
text = f.read()
|
||||
if len(parts) == 2:
|
||||
m = re.search(r'<%s uuid="([0-9a-fA-F-]+)"' % re.escape(parts[0]), text)
|
||||
return m.group(1) if m else None
|
||||
# Вложенный: вид — предпоследний сегмент, имя — последний.
|
||||
rx = r'<%s uuid="([0-9a-fA-F-]+)"[^>]*>\s*<Properties>\s*<Name>%s</Name>' % (
|
||||
re.escape(parts[-2]), re.escape(parts[-1]))
|
||||
m = re.search(rx, text)
|
||||
return m.group(1) if m else None
|
||||
try:
|
||||
tree = etree.parse(owner_path)
|
||||
except Exception:
|
||||
return None
|
||||
md = '{http://v8.1c.ru/8.3/MDClasses}'
|
||||
node = tree.getroot()[0] if len(tree.getroot()) else None
|
||||
if node is None:
|
||||
return None
|
||||
# Оставшиеся пары «вид, имя» ищем строго внутри текущего узла.
|
||||
while i + 1 < len(parts):
|
||||
kind, name = parts[i], parts[i + 1]
|
||||
child = None
|
||||
for candidate in node.findall(f'{md}ChildObjects/{md}{kind}'):
|
||||
props = candidate.find(f'{md}Properties/{md}Name')
|
||||
if props is not None and (props.text or '') == name:
|
||||
child = candidate
|
||||
break
|
||||
if child is None:
|
||||
return None
|
||||
node = child
|
||||
i += 2
|
||||
return node.get('uuid')
|
||||
|
||||
|
||||
def sort_objects_by_uuid(objects, config_root):
|
||||
@@ -1122,9 +1152,10 @@ def sort_objects_by_uuid(objects, config_root):
|
||||
if uuid_value:
|
||||
known.append((uuid_value, o))
|
||||
else:
|
||||
print(f"[role-compile] {o['Name']}: объект не найден в выгрузке, uuid неизвестен — "
|
||||
f"узел записан в конец (платформа переставит его при первой выгрузке)",
|
||||
file=sys.stderr)
|
||||
if not is_standard_kind(o['Name']):
|
||||
print(f"[role-compile] {o['Name']}: объект не найден в выгрузке, uuid неизвестен — "
|
||||
f"узел записан в конец (платформа переставит его при первой выгрузке)",
|
||||
file=sys.stderr)
|
||||
unknown.append(o)
|
||||
known.sort(key=lambda pair: pair[0])
|
||||
return [o for _, o in known] + unknown
|
||||
@@ -1780,7 +1811,9 @@ def main():
|
||||
default_value = get_default_right_value(obj['Name'], sfno, sfab)
|
||||
kept = []
|
||||
for right in obj['Rights']:
|
||||
if right['Value'] == default_value:
|
||||
# Право с ограничением отличается от умолчания самим ограничением — его платформа хранит,
|
||||
# и выбросить его значило бы молча потерять написанное условие.
|
||||
if right['Value'] == default_value and not right['Condition']:
|
||||
dropped_by_default.append(f"{obj['Name']}.{right['Name']}")
|
||||
continue
|
||||
kept.append(right)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# role-edit v1.6 — Edit existing 1C role rights in place
|
||||
# role-edit v1.7 — Edit existing 1C role rights in place
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
[CmdletBinding(PositionalBinding=$false)]
|
||||
param(
|
||||
@@ -733,7 +733,20 @@ function Sort-RightsCanonical {
|
||||
return $sorted
|
||||
}
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — из его узла.
|
||||
# У стандартных реквизитов и стандартных табличных частей uuid в выгрузке нет: они системные.
|
||||
# Отсутствие uuid для них — норма, а не потерянный объект.
|
||||
function Test-StandardKind {
|
||||
param([string]$objName)
|
||||
$parts = $objName -split '\\.'
|
||||
if ($parts.Count -lt 3) { return $false }
|
||||
return $parts[$parts.Count-2].StartsWith("Standard")
|
||||
}
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — спуском по дереву.
|
||||
# Искать регуляркой по всему файлу нельзя: реквизит шапки и реквизит табличной части часто
|
||||
# называются одинаково, и поиск нашёл бы первый попавшийся. Дочерние подсистемы лежат
|
||||
# отдельными файлами, поэтому для них спуск идёт по каталогам.
|
||||
# У стандартных реквизитов uuid в выгрузке нет вовсе — для них возвращаем $null молча.
|
||||
function Get-RightsObjectUuid {
|
||||
param([string]$objName, [string]$configRoot)
|
||||
$parts = $objName -split '\.'
|
||||
@@ -746,18 +759,33 @@ function Get-RightsObjectUuid {
|
||||
}
|
||||
$dir = $script:typeDirs[$parts[0]]
|
||||
if (-not $dir -or $parts.Count -lt 2) { return $null }
|
||||
# Подсистемы вложены каталогами: Subsystems/Родитель/Subsystems/Ребёнок.xml
|
||||
$ownerPath = Join-Path (Join-Path $configRoot $dir) "$($parts[1]).xml"
|
||||
if (-not (Test-Path $ownerPath)) { return $null }
|
||||
$text = [System.IO.File]::ReadAllText($ownerPath)
|
||||
if ($parts.Count -eq 2) {
|
||||
if ($text -match "<$($parts[0]) uuid=`"([0-9a-fA-F-]+)`"") { return $Matches[1] }
|
||||
return $null
|
||||
$i = 2
|
||||
while ($parts.Count -gt $i + 1 -and $parts[$i] -eq 'Subsystem') {
|
||||
$ownerDir = [System.IO.Path]::Combine($configRoot, $dir, ($parts[1..($i-1)] -join [System.IO.Path]::DirectorySeparatorChar + 'Subsystems' + [System.IO.Path]::DirectorySeparatorChar))
|
||||
$ownerPath = Join-Path (Join-Path ([System.IO.Path]::GetDirectoryName($ownerPath)) ([System.IO.Path]::GetFileNameWithoutExtension($ownerPath))) (Join-Path "Subsystems" "$($parts[$i+1]).xml")
|
||||
$i += 2
|
||||
}
|
||||
# Вложенный: вид — предпоследний сегмент, имя — последний.
|
||||
$kind = [regex]::Escape($parts[$parts.Count-2])
|
||||
$name = [regex]::Escape($parts[$parts.Count-1])
|
||||
$rx = "<$kind uuid=`"([0-9a-fA-F-]+)`"[^>]*>\s*<Properties>\s*<Name>$name</Name>"
|
||||
if ($text -match $rx) { return $Matches[1] }
|
||||
if (-not (Test-Path $ownerPath)) { return $null }
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
try { $doc.Load($ownerPath) } catch { return $null }
|
||||
$nsm = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$nsm.AddNamespace("md", "http://v8.1c.ru/8.3/MDClasses")
|
||||
$node = $doc.DocumentElement.FirstChild
|
||||
while ($node -and $node.NodeType -ne 'Element') { $node = $node.NextSibling }
|
||||
if (-not $node) { return $null }
|
||||
# Оставшиеся пары «вид, имя» ищем строго внутри текущего узла.
|
||||
while ($i + 1 -lt $parts.Count) {
|
||||
$kind = $parts[$i]
|
||||
$name = $parts[$i+1]
|
||||
$child = $node.SelectSingleNode("md:ChildObjects/md:$kind[md:Properties/md:Name='$name']", $nsm)
|
||||
if (-not $child) { return $null }
|
||||
$node = $child
|
||||
$i += 2
|
||||
}
|
||||
if ($node.HasAttribute("uuid")) { return $node.GetAttribute("uuid") }
|
||||
return $null
|
||||
}
|
||||
|
||||
@@ -1696,7 +1724,7 @@ function Insert-ObjectNode($newNode, [string]$objName) {
|
||||
$otherUuid = Get-RightsObjectUuid -objName (Get-ObjectNodeName $node) -configRoot $script:configRoot
|
||||
if ($otherUuid -and [string]::CompareOrdinal($otherUuid, $uuid) -gt 0) { $refNode = $node; break }
|
||||
}
|
||||
} else {
|
||||
} elseif (-not (Test-StandardKind $objName)) {
|
||||
Add-Note "[WARN] ${objName}: объект не найден в выгрузке, uuid неизвестен — узел записан перед шаблонами (платформа переставит его при первой выгрузке)"
|
||||
}
|
||||
if (-not $refNode) {
|
||||
@@ -1949,7 +1977,7 @@ function Apply-DenyRights($spec) {
|
||||
}
|
||||
if ($denied.Count -eq 0) {
|
||||
if ($created) { Remove-NodeWithWhitespace $objNode }
|
||||
$reason = if ($script:droppedByDefault -match [regex]::Escape($spec.Name)) { "запрет совпадает с умолчанием роли и платформой не хранится" } else { "права уже запрещены" }
|
||||
$reason = if (@($script:droppedByDefault | Where-Object { $_.StartsWith("$($spec.Name).") }).Count -gt 0) { "запрет совпадает с умолчанием роли и платформой не хранится" } else { "права уже запрещены" }
|
||||
Add-Note " $($spec.Name): $reason, изменений нет"
|
||||
return
|
||||
}
|
||||
@@ -2214,6 +2242,9 @@ function Apply-ModifyProperty($spec) {
|
||||
return
|
||||
}
|
||||
$node.InnerText = $spec.Value
|
||||
# Умолчания решают, какие записи вообще пишутся, — следующие операции должны видеть новое значение.
|
||||
if ($spec.Name -eq 'setForNewObjects') { $script:roleSfno = $spec.Value }
|
||||
if ($spec.Name -eq 'setForAttributesByDefault') { $script:roleSfab = $spec.Value }
|
||||
$script:modifyCount++
|
||||
$script:rightsDirty = $true
|
||||
Add-Note " $($spec.Name) = $($spec.Value)"
|
||||
@@ -2231,9 +2262,12 @@ function Edit-RoleMetadata([string]$field, [string]$text) {
|
||||
Add-ValidationError "Файл метаданных роли не найден: $($script:roleXmlPath)"
|
||||
return
|
||||
}
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
$doc.Load($script:roleXmlPath)
|
||||
$doc = $script:metaDoc
|
||||
if (-not $doc) {
|
||||
$doc = New-Object System.Xml.XmlDocument
|
||||
$doc.PreserveWhitespace = $true
|
||||
$doc.Load($script:roleXmlPath)
|
||||
}
|
||||
$nsm = New-Object System.Xml.XmlNamespaceManager($doc.NameTable)
|
||||
$nsm.AddNamespace("md", $script:mdObjectNs)
|
||||
$nsm.AddNamespace("v8", $script:v8Ns)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-edit v1.6 — Edit existing 1C role rights in place
|
||||
# role-edit v1.7 — Edit existing 1C role rights in place
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import json
|
||||
@@ -1041,8 +1041,21 @@ def sort_rights_canonical(object_name, rights):
|
||||
return sorted_rights
|
||||
|
||||
|
||||
# У стандартных реквизитов и стандартных табличных частей uuid в выгрузке нет: они системные.
|
||||
# Отсутствие uuid для них — норма, а не потерянный объект.
|
||||
def is_standard_kind(object_name):
|
||||
parts = object_name.split('.')
|
||||
if len(parts) < 3:
|
||||
return False
|
||||
return parts[-2].startswith("Standard")
|
||||
|
||||
|
||||
# uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — спуском по дереву.
|
||||
# Искать регуляркой по всему файлу нельзя: реквизит шапки и реквизит табличной части часто
|
||||
# называются одинаково, и поиск нашёл бы первый попавшийся. Дочерние подсистемы лежат
|
||||
# отдельными файлами, поэтому для них спуск идёт по каталогам.
|
||||
# У стандартных реквизитов uuid в выгрузке нет вовсе — для них возвращаем None молча.
|
||||
def get_rights_object_uuid(object_name, config_root):
|
||||
"""uuid объекта прав: у верхнего уровня — из файла объекта, у вложенного — из его узла."""
|
||||
parts = object_name.split('.')
|
||||
if parts[0] == 'Configuration':
|
||||
cfg_path = os.path.join(config_root, 'Configuration.xml')
|
||||
@@ -1054,19 +1067,36 @@ def get_rights_object_uuid(object_name, config_root):
|
||||
directory = TYPE_DIRS.get(parts[0])
|
||||
if not directory or len(parts) < 2:
|
||||
return None
|
||||
# Подсистемы вложены каталогами: Subsystems/Родитель/Subsystems/Ребёнок.xml
|
||||
owner_path = os.path.join(config_root, directory, parts[1] + '.xml')
|
||||
i = 2
|
||||
while len(parts) > i + 1 and parts[i] == 'Subsystem':
|
||||
owner_path = os.path.join(os.path.splitext(owner_path)[0], 'Subsystems', parts[i + 1] + '.xml')
|
||||
i += 2
|
||||
if not os.path.isfile(owner_path):
|
||||
return None
|
||||
with open(owner_path, 'r', encoding='utf-8-sig') as f:
|
||||
text = f.read()
|
||||
if len(parts) == 2:
|
||||
m = re.search(r'<%s uuid="([0-9a-fA-F-]+)"' % re.escape(parts[0]), text)
|
||||
return m.group(1) if m else None
|
||||
# Вложенный: вид — предпоследний сегмент, имя — последний.
|
||||
rx = r'<%s uuid="([0-9a-fA-F-]+)"[^>]*>\s*<Properties>\s*<Name>%s</Name>' % (
|
||||
re.escape(parts[-2]), re.escape(parts[-1]))
|
||||
m = re.search(rx, text)
|
||||
return m.group(1) if m else None
|
||||
try:
|
||||
tree = etree.parse(owner_path)
|
||||
except Exception:
|
||||
return None
|
||||
md = '{http://v8.1c.ru/8.3/MDClasses}'
|
||||
node = tree.getroot()[0] if len(tree.getroot()) else None
|
||||
if node is None:
|
||||
return None
|
||||
# Оставшиеся пары «вид, имя» ищем строго внутри текущего узла.
|
||||
while i + 1 < len(parts):
|
||||
kind, name = parts[i], parts[i + 1]
|
||||
child = None
|
||||
for candidate in node.findall(f'{md}ChildObjects/{md}{kind}'):
|
||||
props = candidate.find(f'{md}Properties/{md}Name')
|
||||
if props is not None and (props.text or '') == name:
|
||||
child = candidate
|
||||
break
|
||||
if child is None:
|
||||
return None
|
||||
node = child
|
||||
i += 2
|
||||
return node.get('uuid')
|
||||
|
||||
|
||||
def sort_objects_by_uuid(objects, config_root):
|
||||
@@ -1731,7 +1761,7 @@ class Editor:
|
||||
if other and other > uuid_value:
|
||||
ref = node
|
||||
break
|
||||
else:
|
||||
elif not is_standard_kind(obj_name):
|
||||
self.note(f"[WARN] {obj_name}: объект не найден в выгрузке, uuid неизвестен — "
|
||||
f"узел записан перед шаблонами (платформа переставит его при первой выгрузке)")
|
||||
if ref is None:
|
||||
@@ -2095,6 +2125,11 @@ class Editor:
|
||||
self.note(f" {spec['Name']}: уже {spec['Value']}, изменений нет")
|
||||
return
|
||||
node.text = spec["Value"]
|
||||
# Умолчания решают, какие записи вообще пишутся, — следующие операции должны видеть новое значение.
|
||||
if spec["Name"] == "setForNewObjects":
|
||||
self.role_sfno = spec["Value"]
|
||||
if spec["Name"] == "setForAttributesByDefault":
|
||||
self.role_sfab = spec["Value"]
|
||||
self.modify_count += 1
|
||||
self.rights_dirty = True
|
||||
self.note(f" {spec['Name']} = {spec['Value']}")
|
||||
|
||||
Reference in New Issue
Block a user