feat(meta-validate): проверка формы MDObjectRef-ссылок

Значения xsi:type="xr:MDObjectRef" не проверялись вообще. Ошибка «тип ссылки
вместо объекта метаданных» обнаруживалась только платформой при загрузке
(«Неизвестный объект метаданных»), причём в логе, а не в коде возврата.

Проверка 17 по первому сегменту пути (переиспользован $validTypes +
$structuralOnlyTypes):
- сегмент оканчивается на Ref → Error: вида метаданных с таким именем
  не существует, ссылка гарантированно нерабочая; в тексте подсказана
  исправленная форма;
- неизвестный сегмент без Ref → Warn (список видов может быть неполон).

Ловит дефект статически, без платформы, независимо от происхождения файла.

Кейс error-mdobjectref-type-form + фикстура. Регресс 23/23 ps1+py.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-25 14:27:14 +03:00
co-authored by Claude Opus 5
parent eb1a2ed8c1
commit 387f10edf0
4 changed files with 164 additions and 2 deletions
@@ -1,4 +1,4 @@
# meta-validate v1.10 — Validate 1C metadata object structure (+корневой <Type>: скаляр без структуры = ошибка)
# meta-validate v1.11 — Validate 1C metadata object structure (+корневой <Type>: скаляр без структуры = ошибка)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1493,6 +1493,36 @@ if ($script:configDir) {
}
}
# --- Check 17: MDObjectRef form — ссылка должна указывать на ОБЪЕКТ метаданных, а не на тип ссылки ---
# Owners/BasedOn/RegisterRecords/RegisteredDocuments/References содержат путь вида "Catalog.Валюты".
# "CatalogRef.Валюты" — частая ошибка (тип ссылки вместо объекта): платформа отвечает
# «Неизвестный объект метаданных». Вида метаданных, оканчивающегося на Ref, не существует → ERROR.
# Неизвестный первый сегмент без Ref — только WARN (список видов может быть неполон).
$mdRefNodes = $xmlDoc.SelectNodes("//*[@xsi:type='xr:MDObjectRef']", $ns)
if ($mdRefNodes -and $mdRefNodes.Count -gt 0) {
$knownRoots = @($validTypes) + @($structuralOnlyTypes)
$badRefForm = @{} # значение -> $true (ссылочная форма, гарантированно нерабочая)
$unknownRoot = @{} # значение -> корень
foreach ($rn in $mdRefNodes) {
$rv = $rn.InnerText.Trim()
if (-not $rv) { continue }
$root = $rv.Split('.')[0]
if ($knownRoots -ccontains $root) { continue }
if ($root -cmatch 'Ref$') { $badRefForm[$rv] = $true } else { $unknownRoot[$rv] = $root }
}
foreach ($bk in ($badRefForm.Keys | Sort-Object)) {
$fixed = $bk -replace '^([A-Za-z]+)Ref\.', '$1.'
Report-Error "17. MDObjectRef '$bk' — ссылка на ТИП, а не на объект метаданных; нужно '$fixed' (иначе «Неизвестный объект метаданных» при загрузке)"
}
foreach ($uk in ($unknownRoot.Keys | Sort-Object)) {
Report-Warn "17. MDObjectRef '$uk' — неизвестный вид метаданных '$($unknownRoot[$uk])' (опечатка?)"
}
if ($badRefForm.Count -eq 0 -and $unknownRoot.Count -eq 0) {
Report-OK "17. MDObjectRef form: $($mdRefNodes.Count) checked"
}
}
# --- Final output ---
& $finalize
@@ -1,4 +1,4 @@
# meta-validate v1.10 — Validate 1C metadata object structure (Python port) (+корневой <Type>: скаляр без структуры = ошибка)
# meta-validate v1.11 — Validate 1C metadata object structure (Python port) (+корневой <Type>: скаляр без структуры = ошибка)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
@@ -1395,6 +1395,36 @@ if config_dir:
elif checked_refs:
report_ok(f"16. Reference types: {len(checked_refs)} resolved")
# ── Check 17: MDObjectRef form — ссылка на ОБЪЕКТ метаданных, а не на тип ссылки ──
# Owners/BasedOn/RegisterRecords/RegisteredDocuments/References содержат путь вида "Catalog.Валюты".
# "CatalogRef.Валюты" — частая ошибка (тип ссылки вместо объекта): платформа отвечает
# «Неизвестный объект метаданных». Вида метаданных, оканчивающегося на Ref, не существует → ERROR.
# Неизвестный первый сегмент без Ref — только WARN (список видов может быть неполон).
md_ref_nodes = find_all(root, "//*[@xsi:type='xr:MDObjectRef']")
if md_ref_nodes:
known_roots = tuple(valid_types) + tuple(structural_only_types)
bad_ref_form = {} # значение -> True (ссылочная форма, гарантированно нерабочая)
unknown_root = {} # значение -> корень
for rn in md_ref_nodes:
rv = inner_text(rn).strip()
if not rv:
continue
rroot = rv.split('.')[0]
if rroot in known_roots:
continue
if rroot.endswith('Ref'):
bad_ref_form[rv] = True
else:
unknown_root[rv] = rroot
for bk in sorted(bad_ref_form):
fixed = re.sub(r'^([A-Za-z]+)Ref\.', r'\1.', bk)
report_error(f"17. MDObjectRef '{bk}' — ссылка на ТИП, а не на объект метаданных; нужно '{fixed}' (иначе «Неизвестный объект метаданных» при загрузке)")
for uk in sorted(unknown_root):
report_warn(f"17. MDObjectRef '{uk}' — неизвестный вид метаданных '{unknown_root[uk]}' (опечатка?)")
if not bad_ref_form and not unknown_root:
report_ok(f"17. MDObjectRef form: {len(md_ref_nodes)} checked")
# ── Final output ──────────────────────────────────────────────
finalize()