feat(meta-validate): проверка ссылок на поля таблицы внешнего источника

Свойства таблицы ссылаются на её же поля шестичастным путём
(ExternalDataSource.И.Table.Т.Field.П). Опечатка в имени поля даёт «Неизвестный объект
метаданных» при загрузке, а глазами в таком пути её не видно. Проверка 21 разбирает форму
пути, требует, чтобы таблица в ссылке была той же самой, и чтобы поле существовало —
в сообщении перечисляются имеющиеся поля.

Отдельно: таблица без ключевых полей даёт предупреждение, а не ошибку. Загрузку XML такой
таблицы платформа принимает молча (измерено на 8.3.24.1691), Конфигуратор интерактивно
требует ключ, а рабочие конфигурации без ключей существуют — значит это повод предупредить,
а не отвергнуть.

Закрыты последние пункты плана: раздел про внешние источники в docs/meta-dsl-spec.md
(формой и связями с общими конвенциями, без дублирования таблицы свойств — она живёт
в reference навыка) и кейсы meta-validate, включая негативный.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
This commit is contained in:
Nick Shirokov
2026-09-06 15:35:56 +03:00
co-authored by Claude Opus 5
parent 24cfe6a0a6
commit 89ce1af9fa
18 changed files with 1576 additions and 2 deletions
@@ -1,4 +1,4 @@
# meta-validate v1.26 — Validate 1C metadata object structure
# meta-validate v1.27 — Validate 1C metadata object structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
[CmdletBinding(PositionalBinding=$false)]
param(
@@ -1836,6 +1836,60 @@ if (-not $isAdopted) {
}
}
# --- Check 21: таблица внешнего источника — ссылки на поля и наличие ключа ---
# Свойства таблицы ссылаются на её же поля полным путём. Опечатка в имени поля даёт
# «Неизвестный объект метаданных» при загрузке, а найти её глазами в шестичастном пути трудно.
if ($mdType -eq "Table") {
$fieldNames = @{}
if ($childObjNode) {
foreach ($f in $childObjNode.SelectNodes("md:Field/md:Properties/md:Name", $ns)) {
$fieldNames[$f.InnerText] = $true
}
}
$edsRefsChecked = 0
$edsRefsBad = $false
foreach ($spec in @(
@("KeyFields", "xr:Field"), @("InputByString", "xr:Field"), @("DataLockFields", "xr:Field"),
@("PresentationField", $null), @("ParentField", $null), @("DataVersionField", $null))) {
$tag = $spec[0]
$refs = @()
if ($spec[1]) {
foreach ($n in $propsNode.SelectNodes("md:$tag/$($spec[1])", $ns)) { $refs += $n.InnerText }
} else {
$n = $propsNode.SelectSingleNode("md:$tag", $ns)
if ($n -and $n.InnerText) { $refs += $n.InnerText }
}
foreach ($ref in $refs) {
$edsRefsChecked++
$parts = $ref -split '\.'
# Ожидается ExternalDataSource.<Источник>.Table.<Таблица>.Field.<Поле>
if ($parts.Count -ne 6 -or $parts[0] -ne "ExternalDataSource" -or $parts[2] -ne "Table" -or $parts[4] -ne "Field") {
Report-Error "21. $tag '$ref' — ожидается ExternalDataSource.<Источник>.Table.<Таблица>.Field.<Поле>"
$edsRefsBad = $true
continue
}
if ($parts[3] -ne $objName) {
Report-Error "21. $tag '$ref' — ссылка на поле ЧУЖОЙ таблицы (эта: $objName)"
$edsRefsBad = $true
continue
}
if (-not $fieldNames.ContainsKey($parts[5])) {
$known = if ($fieldNames.Count -gt 0) { ($fieldNames.Keys | Sort-Object) -join ", " } else { "полей нет" }
Report-Error "21. $tag '$ref' — поля '$($parts[5])' нет в таблице (есть: $known)"
$edsRefsBad = $true
}
}
}
if ($edsRefsChecked -gt 0 -and -not $edsRefsBad) { Report-OK "21. Field refs: $edsRefsChecked resolved" }
# Ключ: загрузка XML таблицу без ключа принимает (проверено на платформе), а Конфигуратор
# интерактивно требует. Отсюда предупреждение, а не ошибка: рабочие конфигурации без ключа есть.
$keyNodes = @($propsNode.SelectNodes("md:KeyFields/xr:Field", $ns))
if ($keyNodes.Count -eq 0) {
Report-Warn "21. KeyFields пуст — платформа такую таблицу загрузит, но форма записи и набор записей будут недоступны"
}
}
# --- Final output ---
& $finalize
@@ -1,4 +1,4 @@
# meta-validate v1.26 — Validate 1C metadata object structure (Python port)
# meta-validate v1.27 — Validate 1C metadata object structure (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
@@ -1778,6 +1778,48 @@ if not is_adopted:
if form_refs_checked > 0 and not form_ref_bad:
report_ok(f"20. Form refs: {form_refs_checked} resolved")
# ── Check 21: таблица внешнего источника — ссылки на поля и наличие ключа ──
# Свойства таблицы ссылаются на её же поля полным путём. Опечатка в имени поля даёт
# «Неизвестный объект метаданных» при загрузке, а найти её глазами в шестичастном пути трудно.
if md_type == "Table":
field_names = set()
if child_obj_node is not None:
for f in find_all(child_obj_node, "md:Field/md:Properties/md:Name"):
field_names.add(inner_text(f))
eds_refs_checked = 0
eds_refs_bad = False
for tag, is_list in (("KeyFields", True), ("InputByString", True), ("DataLockFields", True),
("PresentationField", False), ("ParentField", False), ("DataVersionField", False)):
if is_list:
refs = [inner_text(n) for n in find_all(props_node, f"md:{tag}/xr:Field")]
else:
n = find(props_node, f"md:{tag}")
refs = [inner_text(n)] if n is not None and inner_text(n) else []
for ref in refs:
eds_refs_checked += 1
parts = ref.split(".")
# Ожидается ExternalDataSource.<Источник>.Table.<Таблица>.Field.<Поле>
if len(parts) != 6 or parts[0] != "ExternalDataSource" or parts[2] != "Table" or parts[4] != "Field":
report_error(f"21. {tag} '{ref}' — ожидается ExternalDataSource.<Источник>.Table.<Таблица>.Field.<Поле>")
eds_refs_bad = True
continue
if parts[3] != obj_name:
report_error(f"21. {tag} '{ref}' — ссылка на поле ЧУЖОЙ таблицы (эта: {obj_name})")
eds_refs_bad = True
continue
if parts[5] not in field_names:
known = ", ".join(sorted(field_names)) if field_names else "полей нет"
report_error(f"21. {tag} '{ref}' — поля '{parts[5]}' нет в таблице (есть: {known})")
eds_refs_bad = True
if eds_refs_checked > 0 and not eds_refs_bad:
report_ok(f"21. Field refs: {eds_refs_checked} resolved")
# Ключ: загрузка XML таблицу без ключа принимает (проверено на платформе), а Конфигуратор
# интерактивно требует. Отсюда предупреждение, а не ошибка: рабочие конфигурации без ключа есть.
if not find_all(props_node, "md:KeyFields/xr:Field"):
report_warn("21. KeyFields пуст — платформа такую таблицу загрузит, но форма записи и набор записей будут недоступны")
# ── Final output ──────────────────────────────────────────────
finalize()