mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-31 00:37:47 +03:00
feat(meta-validate): проверка существования ссылочных типов (Фаза 1.2, v1.8)
Check 16: все <v8:Type> вида CatalogRef.X / DocumentRef / EnumRef / DefinedType / ChartOf*Ref / BusinessProcessRef / ExchangePlanRef / TaskRef проверяются на наличие объекта в конфигурации (Dir/X.xml или папка Dir/X). WARN-уровень, не error: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаются по маркеру ConfigurationExtensionPurpose — их типы ссылаются на объекты базовой конфигурации, отсутствующие в выгрузке расширения. Дедуп по ref-ключу; маппинг тип→каталог согласован с TYPE_TO_DIR (verify-snapshots). Тесты: valid-reftype-resolves (self-ref, -Detailed, OK) + warn-reftype-missing (WARN, exit 0). meta-validate 18/18 ps1+py. Анти-дрейф OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f8cd7dd681
commit
b9ed48ccf2
@@ -1,4 +1,4 @@
|
|||||||
# meta-validate v1.7 — Validate 1C metadata object structure
|
# meta-validate v1.8 — Validate 1C metadata object structure
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
@@ -1391,6 +1391,59 @@ if ($childObjNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Check 16: Reference type existence — типы вида CatalogRef.X должны разрешаться в объекты конфигурации ---
|
||||||
|
# WARN-уровень: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаем —
|
||||||
|
# их типы ссылаются на объекты базовой конфигурации, которых нет в выгрузке расширения.
|
||||||
|
|
||||||
|
if ($script:configDir) {
|
||||||
|
$isExtension = $false
|
||||||
|
$cfgXmlPath = Join-Path $script:configDir "Configuration.xml"
|
||||||
|
if (Test-Path $cfgXmlPath) {
|
||||||
|
$cfgContent = [System.IO.File]::ReadAllText($cfgXmlPath, [System.Text.Encoding]::UTF8)
|
||||||
|
if ($cfgContent.Contains("ConfigurationExtensionPurpose")) { $isExtension = $true }
|
||||||
|
}
|
||||||
|
if (-not $isExtension) {
|
||||||
|
$refDirMap = @{
|
||||||
|
"CatalogRef"="Catalogs"; "DocumentRef"="Documents"; "EnumRef"="Enums"
|
||||||
|
"ChartOfAccountsRef"="ChartsOfAccounts"; "ChartOfCharacteristicTypesRef"="ChartsOfCharacteristicTypes"
|
||||||
|
"ChartOfCalculationTypesRef"="ChartsOfCalculationTypes"; "BusinessProcessRef"="BusinessProcesses"
|
||||||
|
"ExchangePlanRef"="ExchangePlans"; "TaskRef"="Tasks"; "DefinedType"="DefinedTypes"
|
||||||
|
}
|
||||||
|
$typeNodes = $xmlDoc.SelectNodes("//v8:Type", $ns)
|
||||||
|
$checkedRefs = @{} # refKey -> $true если найден; для OK-условия
|
||||||
|
$missingRefs = @{} # refKey -> refDir
|
||||||
|
foreach ($tn in $typeNodes) {
|
||||||
|
$tv = $tn.InnerText.Trim()
|
||||||
|
if (-not $tv) { continue }
|
||||||
|
$colonIdx = $tv.IndexOf(':')
|
||||||
|
if ($colonIdx -ge 0) { $tv = $tv.Substring($colonIdx + 1) }
|
||||||
|
$dotIdx = $tv.IndexOf('.')
|
||||||
|
if ($dotIdx -lt 0) { continue }
|
||||||
|
$refCat = $tv.Substring(0, $dotIdx)
|
||||||
|
$refName = $tv.Substring($dotIdx + 1)
|
||||||
|
$refDir = $refDirMap[$refCat]
|
||||||
|
if (-not $refDir -or -not $refName) { continue }
|
||||||
|
$refKey = "$refCat.$refName"
|
||||||
|
if ($checkedRefs.ContainsKey($refKey)) { continue }
|
||||||
|
$refFolder = Join-Path $script:configDir (Join-Path $refDir $refName)
|
||||||
|
$refFile = Join-Path $script:configDir (Join-Path $refDir "$refName.xml")
|
||||||
|
if ((Test-Path $refFolder) -or (Test-Path $refFile)) {
|
||||||
|
$checkedRefs[$refKey] = $true
|
||||||
|
} else {
|
||||||
|
$checkedRefs[$refKey] = $false
|
||||||
|
$missingRefs[$refKey] = $refDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($missingRefs.Count -gt 0) {
|
||||||
|
foreach ($mk in ($missingRefs.Keys | Sort-Object)) {
|
||||||
|
Report-Warn "16. Ссылочный тип '$mk' не найден в конфигурации ($($missingRefs[$mk])/) — при загрузке будет ошибка неизвестного типа"
|
||||||
|
}
|
||||||
|
} elseif ($checkedRefs.Count -gt 0) {
|
||||||
|
Report-OK "16. Reference types: $($checkedRefs.Count) resolved"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# --- Final output ---
|
# --- Final output ---
|
||||||
|
|
||||||
& $finalize
|
& $finalize
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# meta-validate v1.7 — Validate 1C metadata object structure (Python port)
|
# meta-validate v1.8 — Validate 1C metadata object structure (Python port)
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -1302,6 +1302,61 @@ if child_obj_node is not None:
|
|||||||
if check15_ok and cmd_count > 0:
|
if check15_ok and cmd_count > 0:
|
||||||
report_ok(f"15. Commands: {cmd_count} command(s), groups valid")
|
report_ok(f"15. Commands: {cmd_count} command(s), groups valid")
|
||||||
|
|
||||||
|
# ── Check 16: Reference type existence — типы вида CatalogRef.X должны разрешаться в объекты конфигурации ──
|
||||||
|
# WARN-уровень: ложное срабатывание на частичных выгрузках хуже пропуска. Расширения (CFE) пропускаем —
|
||||||
|
# их типы ссылаются на объекты базовой конфигурации, которых нет в выгрузке расширения.
|
||||||
|
|
||||||
|
if config_dir:
|
||||||
|
is_extension = False
|
||||||
|
cfg_xml_path = os.path.join(config_dir, "Configuration.xml")
|
||||||
|
if os.path.exists(cfg_xml_path):
|
||||||
|
try:
|
||||||
|
with open(cfg_xml_path, "r", encoding="utf-8") as f:
|
||||||
|
cfg_content = f.read()
|
||||||
|
if "ConfigurationExtensionPurpose" in cfg_content:
|
||||||
|
is_extension = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if not is_extension:
|
||||||
|
ref_dir_map = {
|
||||||
|
"CatalogRef": "Catalogs", "DocumentRef": "Documents", "EnumRef": "Enums",
|
||||||
|
"ChartOfAccountsRef": "ChartsOfAccounts", "ChartOfCharacteristicTypesRef": "ChartsOfCharacteristicTypes",
|
||||||
|
"ChartOfCalculationTypesRef": "ChartsOfCalculationTypes", "BusinessProcessRef": "BusinessProcesses",
|
||||||
|
"ExchangePlanRef": "ExchangePlans", "TaskRef": "Tasks", "DefinedType": "DefinedTypes",
|
||||||
|
}
|
||||||
|
checked_refs = {} # ref_key -> найден ли; для условия OK
|
||||||
|
missing_refs = {} # ref_key -> ref_dir
|
||||||
|
for tn in find_all(root, ".//v8:Type"):
|
||||||
|
tv = inner_text(tn).strip()
|
||||||
|
if not tv:
|
||||||
|
continue
|
||||||
|
colon_idx = tv.find(":")
|
||||||
|
if colon_idx >= 0:
|
||||||
|
tv = tv[colon_idx + 1:]
|
||||||
|
dot_idx = tv.find(".")
|
||||||
|
if dot_idx < 0:
|
||||||
|
continue
|
||||||
|
ref_cat = tv[:dot_idx]
|
||||||
|
ref_name = tv[dot_idx + 1:]
|
||||||
|
ref_dir = ref_dir_map.get(ref_cat)
|
||||||
|
if not ref_dir or not ref_name:
|
||||||
|
continue
|
||||||
|
ref_key = f"{ref_cat}.{ref_name}"
|
||||||
|
if ref_key in checked_refs:
|
||||||
|
continue
|
||||||
|
ref_folder = os.path.join(config_dir, ref_dir, ref_name)
|
||||||
|
ref_file = os.path.join(config_dir, ref_dir, ref_name + ".xml")
|
||||||
|
if os.path.exists(ref_folder) or os.path.exists(ref_file):
|
||||||
|
checked_refs[ref_key] = True
|
||||||
|
else:
|
||||||
|
checked_refs[ref_key] = False
|
||||||
|
missing_refs[ref_key] = ref_dir
|
||||||
|
if missing_refs:
|
||||||
|
for mk in sorted(missing_refs):
|
||||||
|
report_warn(f"16. Ссылочный тип '{mk}' не найден в конфигурации ({missing_refs[mk]}/) — при загрузке будет ошибка неизвестного типа")
|
||||||
|
elif checked_refs:
|
||||||
|
report_ok(f"16. Reference types: {len(checked_refs)} resolved")
|
||||||
|
|
||||||
# ── Final output ──────────────────────────────────────────────
|
# ── Final output ──────────────────────────────────────────────
|
||||||
|
|
||||||
finalize()
|
finalize()
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "Ссылочный тип реквизита разрешается в объект конфигурации (Check 16)",
|
||||||
|
"setup": "empty-config",
|
||||||
|
"preRun": [
|
||||||
|
{
|
||||||
|
"script": "meta-compile/scripts/meta-compile",
|
||||||
|
"input": { "type": "Catalog", "name": "ТестСсылка", "attributes": ["Связанный: CatalogRef.ТестСсылка"] },
|
||||||
|
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": { "objectPath": "Catalogs/ТестСсылка" },
|
||||||
|
"args_extra": ["-Detailed"],
|
||||||
|
"expect": {
|
||||||
|
"stdoutContains": "16. Reference types:",
|
||||||
|
"stdoutNotContains": "16. Ссылочный тип"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "Валидатор предупреждает о несуществующем ссылочном типе реквизита (Check 16)",
|
||||||
|
"setup": "empty-config",
|
||||||
|
"preRun": [
|
||||||
|
{
|
||||||
|
"script": "meta-compile/scripts/meta-compile",
|
||||||
|
"input": { "type": "Catalog", "name": "ТестСсылкаБитая", "attributes": ["Контрагент: CatalogRef.НетТакогоСправочника"] },
|
||||||
|
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": { "objectPath": "Catalogs/ТестСсылкаБитая" },
|
||||||
|
"expect": {
|
||||||
|
"stdoutContains": "16. Ссылочный тип 'CatalogRef.НетТакогоСправочника'"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user