mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-19 01:30:23 +03:00
fix(cfe-borrow): свойства и виды, от которых зависят стандартные поля
Сплошной прогон по типам объектов (11 минимальных объектных форм УТ, каждая заимствована и загружена в UT_DEMO) дал три падения из одиннадцати. Все три — один класс: в оболочке не хватает того, от чего зависит существование стандартного поля, и платформа отвергает загрузку «Неверный путь к данным». 1. Справочник с владельцем: «Объект.Owner» не разрешается без <Owners>. Свойство — список <xr:Item>, а не скаляр, поэтому переносится фрагментом, как __TypeXml у DefinedType. Одного переноса мало: ссылка должна вести на объект, который в расширении есть, иначе платформа падает с access violation вместо сообщения. Добавлен общий проход, заимствующий владельцев (и владельцев владельцев) — Конфигуратор поступает так же, эталон Issue66Example7_1. 2. Регистр сведений: «Запись.Period» не разрешается без InformationRegisterPeriodicity. Вместе с ним переносится WriteMode, от которого зависит «Запись.Recorder» — Конфигуратор несёт оба, 6 эталонов из 6. 3. Задача: «Объект.Исполнитель» — это реквизит адресации, отдельный вид дочернего объекта. AddressingAttribute добавлен к видам, которые заимствуются поимённо, рядом с Dimension и Resource. Правка сделана в Read-SourceObject и Build-BorrowedObjectXml — там, где оболочка рождается: механизм $extraProps работает только в потоке -BorrowMainAttribute и обычное заимствование оболочки не покрывает. Проверено: те же 11 форм грузятся 11 из 11; ПВХ с путём «Объект.ValueType» грузился и раньше — Конфигуратор Type и CharacteristicExtValues тоже не переносит (эталон Issue66Example8), правило «свойство, включающее поле» подтверждается с обеих сторон. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
112166dc0c
commit
83bb5b6fd3
@@ -1,4 +1,4 @@
|
||||
# cfe-borrow v1.30 — Borrow objects from configuration into extension (CFE)
|
||||
# cfe-borrow v1.31 — Borrow objects from configuration into extension (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$ExtensionPath,
|
||||
@@ -30,7 +30,7 @@ $script:droppedLinks = @()
|
||||
$script:mainAttrId = "1000001"
|
||||
|
||||
# Виды дочерних объектов, которые заимствуются в оболочку поимённо (табличные части — отдельно)
|
||||
$script:childObjectKinds = @('Attribute','Dimension','Resource')
|
||||
$script:childObjectKinds = @('Attribute','Dimension','Resource','AddressingAttribute')
|
||||
|
||||
# Прямые дети <Form>, которые в заимствованную форму не переносятся.
|
||||
# Структурные секции: AutoCommandBar и ChildItems забираются отдельно, остальные выбрасываются целиком.
|
||||
@@ -462,6 +462,16 @@ $typesWithChildObjects = @(
|
||||
# CommonModule properties to copy from source
|
||||
$commonModuleProps = @("Global","ClientManagedApplication","Server","ExternalConnection","ClientOrdinaryApplication","ServerCall")
|
||||
|
||||
# Свойства объекта, от которых зависит существование стандартного поля: без них платформа
|
||||
# отвергает загрузку — «Неверный путь к данным». Конфигуратор переносит ровно их (эталоны
|
||||
# Issue66Example7_1 и Issue66Example2). Проверено сплошным прогоном по типам: у регистра сведений
|
||||
# без InformationRegisterPeriodicity не разрешается «Запись.Period».
|
||||
$script:typeGateProps = @{
|
||||
"InformationRegister" = @("InformationRegisterPeriodicity","WriteMode")
|
||||
}
|
||||
# Владельцы справочника — список <xr:Item>, а не скаляр: переносится фрагментом, как __TypeXml
|
||||
$script:typesWithOwners = @("Catalog","ChartOfCharacteristicTypes")
|
||||
|
||||
# Standard system fields to skip when collecting DataPath references
|
||||
$script:standardFields = @("Code","Description","Ref","Parent","DeletionMark","Predefined","IsFolder","LineNumber","RowsCount","PredefinedDataName")
|
||||
|
||||
@@ -672,6 +682,19 @@ function Read-SourceObject {
|
||||
$srcProps["__TypeXml"] = [regex]::Replace($typeNode.OuterXml, '\s+xmlns(?::\w+)?="[^"]*"', '')
|
||||
}
|
||||
}
|
||||
# Владельцы: стандартное поле «Owner» появляется у справочника, только если задан Owners
|
||||
if ($script:typesWithOwners -ccontains $typeName) {
|
||||
$ownersNode = $propsNode.SelectSingleNode("md:Owners", $srcNs)
|
||||
if ($ownersNode -and $ownersNode.HasChildNodes) {
|
||||
$srcProps["__OwnersXml"] = [regex]::Replace($ownersNode.OuterXml, '\s+xmlns(?::\w+)?="[^"]*"', '')
|
||||
}
|
||||
}
|
||||
# Скалярные свойства, включающие стандартные поля своего типа
|
||||
foreach ($gp in @($script:typeGateProps[$typeName])) {
|
||||
if (-not $gp) { continue }
|
||||
$gpNode = $propsNode.SelectSingleNode("md:${gp}", $srcNs)
|
||||
if ($gpNode) { $srcProps[$gp] = $gpNode.InnerText.Trim() }
|
||||
}
|
||||
}
|
||||
|
||||
# Whether the platform emits <ChildObjects> for this type — the source object is the ground truth
|
||||
@@ -2036,6 +2059,16 @@ function Build-BorrowedObjectXml {
|
||||
$sb.AppendLine("`t`t`t$($sourceProps['__TypeXml'])") | Out-Null
|
||||
}
|
||||
|
||||
# Свойства, от которых зависят стандартные поля (см. $script:typeGateProps / $script:typesWithOwners)
|
||||
foreach ($gp in @($script:typeGateProps[$typeName])) {
|
||||
if ($gp -and $sourceProps.ContainsKey($gp)) {
|
||||
$sb.AppendLine("`t`t`t<${gp}>$($sourceProps[$gp])</${gp}>") | Out-Null
|
||||
}
|
||||
}
|
||||
if ($sourceProps.ContainsKey("__OwnersXml")) {
|
||||
$sb.AppendLine("`t`t`t$($sourceProps['__OwnersXml'])") | Out-Null
|
||||
}
|
||||
|
||||
$sb.AppendLine("`t`t</Properties>") | Out-Null
|
||||
|
||||
# ChildObjects (for types that need it)
|
||||
@@ -2203,6 +2236,47 @@ foreach ($item in $items) {
|
||||
}
|
||||
}
|
||||
|
||||
# --- 14b. Владельцы заимствованных справочников ---
|
||||
# Ссылка в <Owners> должна вести на объект, который в расширении есть: иначе платформа падает при
|
||||
# загрузке (проверено — access violation, не сообщение об ошибке). Конфигуратор владельца
|
||||
# заимствует (эталон Issue66Example7_1: вместе со справочником перенесён и его ПВХ-владелец).
|
||||
# Проход общий и повторяется, пока находятся новые: у владельца может быть свой владелец.
|
||||
$ownerPass = 0
|
||||
while ($true) {
|
||||
$ownerPass++
|
||||
if ($ownerPass -gt 10) { break }
|
||||
$newOwners = @()
|
||||
foreach ($shell in (Get-ChildItem -Path $extDir -Filter "*.xml" -Recurse -File)) {
|
||||
$shellText = [System.IO.File]::ReadAllText($shell.FullName)
|
||||
if ($shellText -notmatch '<Owners>') { continue }
|
||||
foreach ($om in [regex]::Matches($shellText, '<xr:Item[^>]*>(\w+)\.(\w+)</xr:Item>')) {
|
||||
$oType = $om.Groups[1].Value; $oName = $om.Groups[2].Value
|
||||
if (-not $childTypeDirMap.ContainsKey($oType)) { continue }
|
||||
if (Test-ObjectBorrowed $oType $oName) { continue }
|
||||
if ($newOwners | Where-Object { $_.T -eq $oType -and $_.N -eq $oName }) { continue }
|
||||
$newOwners += @{ T = $oType; N = $oName }
|
||||
}
|
||||
}
|
||||
if ($newOwners.Count -eq 0) { break }
|
||||
foreach ($ow in $newOwners) {
|
||||
$owSrcFile = Join-Path (Join-Path $cfgDir $childTypeDirMap[$ow.T]) "$($ow.N).xml"
|
||||
if (-not (Test-Path $owSrcFile)) {
|
||||
Warn " Владелец $($ow.T).$($ow.N) не найден в источнике — ссылка останется висячей"
|
||||
continue
|
||||
}
|
||||
$owSrc = Read-SourceObject $ow.T $ow.N
|
||||
$owXml = Build-BorrowedObjectXml $ow.T $ow.N $owSrc.Uuid $owSrc.Properties
|
||||
$owDir = Join-Path $extDir $childTypeDirMap[$ow.T]
|
||||
if (-not (Test-Path $owDir)) { New-Item -ItemType Directory -Path $owDir -Force | Out-Null }
|
||||
$owFile = Join-Path $owDir "$($ow.N).xml"
|
||||
$owEnc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($owFile, $owXml, $owEnc)
|
||||
Add-ToChildObjects $ow.T $ow.N
|
||||
$script:borrowedFiles += $owFile
|
||||
Info " Auto-borrowed owner: $($ow.T).$($ow.N)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 15. Save modified Configuration.xml ---
|
||||
$settings = New-Object System.Xml.XmlWriterSettings
|
||||
$settings.Encoding = New-Object System.Text.UTF8Encoding($true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# cfe-borrow v1.30 — Borrow objects from configuration into extension (CFE)
|
||||
# cfe-borrow v1.31 — Borrow objects from configuration into extension (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -50,7 +50,7 @@ FORM_BINDING_PICTURE_TAGS = ["MultipleValuePictureDataPath"]
|
||||
MAIN_ATTR_ID = "1000001"
|
||||
|
||||
# Виды дочерних объектов, которые заимствуются в оболочку поимённо (табличные части — отдельно)
|
||||
CHILD_OBJECT_KINDS = ("Attribute", "Dimension", "Resource")
|
||||
CHILD_OBJECT_KINDS = ("Attribute", "Dimension", "Resource", "AddressingAttribute")
|
||||
|
||||
# Прямые дети <Form>, которые в заимствованную форму не переносятся.
|
||||
# Структурные секции: AutoCommandBar и ChildItems забираются отдельно, остальные выбрасываются целиком.
|
||||
@@ -455,6 +455,16 @@ TYPES_WITH_CHILD_OBJECTS = [
|
||||
|
||||
COMMON_MODULE_PROPS = ["Global", "ClientManagedApplication", "Server", "ExternalConnection", "ClientOrdinaryApplication", "ServerCall"]
|
||||
|
||||
# Свойства объекта, от которых зависит существование стандартного поля: без них платформа
|
||||
# отвергает загрузку — «Неверный путь к данным». Конфигуратор переносит ровно их (эталоны
|
||||
# Issue66Example7_1 и Issue66Example2). Проверено сплошным прогоном по типам: у регистра
|
||||
# сведений без InformationRegisterPeriodicity не разрешается «Запись.Period».
|
||||
TYPE_GATE_PROPS = {
|
||||
"InformationRegister": ["InformationRegisterPeriodicity", "WriteMode"],
|
||||
}
|
||||
# Владельцы справочника — список <xr:Item>, а не скаляр: переносится фрагментом, как __TypeXml
|
||||
TYPES_WITH_OWNERS = ("Catalog", "ChartOfCharacteristicTypes")
|
||||
|
||||
# Standard system fields to skip when collecting DataPath references
|
||||
STANDARD_FIELDS = [
|
||||
"Code", "Description", "Ref", "Parent", "DeletionMark",
|
||||
@@ -808,6 +818,17 @@ def main():
|
||||
if type_node is not None:
|
||||
type_xml = etree.tostring(type_node, encoding="unicode")
|
||||
src_props["__TypeXml"] = re.sub(r'\s+xmlns(?::\w+)?="[^"]*"', '', type_xml)
|
||||
# Владельцы: стандартное поле «Owner» появляется у справочника, только если задан Owners
|
||||
if type_name in TYPES_WITH_OWNERS:
|
||||
owners_node = props_node.find(f"{{{MD_NS}}}Owners")
|
||||
if owners_node is not None and len(owners_node):
|
||||
owners_xml = etree.tostring(owners_node, encoding="unicode")
|
||||
src_props["__OwnersXml"] = re.sub(r'\s+xmlns(?::\w+)?="[^"]*"', '', owners_xml)
|
||||
# Скалярные свойства, включающие стандартные поля своего типа
|
||||
for gp in TYPE_GATE_PROPS.get(type_name, []):
|
||||
gp_node = props_node.find(f"{{{MD_NS}}}{gp}")
|
||||
if gp_node is not None:
|
||||
src_props[gp] = (gp_node.text or "").strip()
|
||||
|
||||
# Whether the platform emits <ChildObjects> for this type — the source object is the ground truth
|
||||
src_props["__HasChildObjects"] = src_el.find(f"{{{MD_NS}}}ChildObjects") is not None
|
||||
@@ -886,6 +907,13 @@ def main():
|
||||
if type_name == "DefinedType" and "__TypeXml" in source_props:
|
||||
lines.append(f"\t\t\t{source_props['__TypeXml']}")
|
||||
|
||||
# Свойства, от которых зависят стандартные поля (см. TYPE_GATE_PROPS / TYPES_WITH_OWNERS)
|
||||
for gp in TYPE_GATE_PROPS.get(type_name, []):
|
||||
if gp in source_props:
|
||||
lines.append(f"\t\t\t<{gp}>{source_props[gp]}</{gp}>")
|
||||
if "__OwnersXml" in source_props:
|
||||
lines.append(f"\t\t\t{source_props['__OwnersXml']}")
|
||||
|
||||
lines.append("\t\t</Properties>")
|
||||
|
||||
if source_props.get("__HasChildObjects") or type_name in TYPES_WITH_CHILD_OBJECTS:
|
||||
@@ -2068,6 +2096,47 @@ def main():
|
||||
borrowed_files.append(target_file)
|
||||
borrowed_count += 1
|
||||
|
||||
# --- Владельцы заимствованных справочников ---
|
||||
# Ссылка в <Owners> должна вести на объект, который в расширении есть: иначе платформа падает
|
||||
# при загрузке (проверено — access violation, не сообщение об ошибке). Конфигуратор владельца
|
||||
# заимствует (эталон Issue66Example7_1: вместе со справочником перенесён и его ПВХ-владелец).
|
||||
# Проход общий и повторяется, пока находятся новые: у владельца может быть свой владелец.
|
||||
for _owner_pass in range(10):
|
||||
new_owners = []
|
||||
for root_dir, _dirs, files in os.walk(ext_dir):
|
||||
for fn in files:
|
||||
if not fn.endswith(".xml"):
|
||||
continue
|
||||
with open(os.path.join(root_dir, fn), "r", encoding="utf-8-sig") as fh:
|
||||
shell_text = fh.read()
|
||||
if "<Owners>" not in shell_text:
|
||||
continue
|
||||
for om in re.finditer(r'<xr:Item[^>]*>(\w+)\.(\w+)</xr:Item>', shell_text):
|
||||
o_type, o_name = om.group(1), om.group(2)
|
||||
if o_type not in CHILD_TYPE_DIR_MAP:
|
||||
continue
|
||||
if test_object_borrowed(o_type, o_name):
|
||||
continue
|
||||
if (o_type, o_name) in new_owners:
|
||||
continue
|
||||
new_owners.append((o_type, o_name))
|
||||
if not new_owners:
|
||||
break
|
||||
for o_type, o_name in new_owners:
|
||||
ow_src_file = os.path.join(cfg_dir, CHILD_TYPE_DIR_MAP[o_type], f"{o_name}.xml")
|
||||
if not os.path.isfile(ow_src_file):
|
||||
warn(f" Владелец {o_type}.{o_name} не найден в источнике — ссылка останется висячей")
|
||||
continue
|
||||
ow_src = read_source_object(o_type, o_name)
|
||||
ow_xml = build_borrowed_object_xml(o_type, o_name, ow_src["Uuid"], ow_src["Properties"])
|
||||
ow_dir = os.path.join(ext_dir, CHILD_TYPE_DIR_MAP[o_type])
|
||||
os.makedirs(ow_dir, exist_ok=True)
|
||||
ow_file = os.path.join(ow_dir, f"{o_name}.xml")
|
||||
write_utf8_bom(ow_file, ow_xml)
|
||||
add_to_child_objects(o_type, o_name)
|
||||
borrowed_files.append(ow_file)
|
||||
info(f" Auto-borrowed owner: {o_type}.{o_name}")
|
||||
|
||||
# --- Save modified Configuration.xml ---
|
||||
save_xml_bom(tree, ext_resolved)
|
||||
info(f"Saved: {ext_resolved}")
|
||||
|
||||
Reference in New Issue
Block a user