feat(meta-compile): создание внешних источников данных

Вид ExternalDataSource навыки meta-* не поддерживали вовсе: в корпусе выгрузок его нет
ни одного, формат был неизвестен. Разведан на стенде (PostgreSQL в Docker + база 8.3.24)
и по реальной выгрузке рабочего проекта.

meta-compile собирает источник целиком одним JSON: сам источник, его таблицы с полями
и функции. Единственный вид, у которого объект складывается более чем из одного XML —
файл источника плюс по файлу на таблицу в <Источник>/Tables/.

DSL без новых конвенций: tables — dict имя → массив полей ЛИБО объект (как tabularSections),
поле — обычный реквизит плюс три своих ключа (nameInDataSource/readOnly/allowNull),
ссылки на поля короткими именами (как inputByString у справочника), functions — dict
имя → строка ИЛИ объект (как urlTemplates). Параметры функций не объекты метаданных:
они живут в самом выражении как &1, &2.

Инструкция под каскадом: в SKILL.md строка индекса, вся специфика — в
reference/external-data-source.md.

meta-validate принимает ExternalDataSource и Table, знает их наборы GeneratedType,
состав ChildObjects и шестичастную ссылку на форму таблицы. Проверено на выгрузке
платформы и на чужой рабочей выгрузке из другого проекта — обе проходят чисто.

Раундтрип на платформе 8.3.24.1691: наш эмит → загрузка → выгрузка даёт файлы,
идентичные поданным (с точностью до uuid). Два поведения платформы пришлось воспроизвести
явно, оба измерены: ввод по строке выводится из поля представления, а заданное значение
незаполненного родителя платформа при загрузке XML сбрасывает в пустую строку — задать
его можно только интерактивно.

Спека: раздел «Внешние источники данных» в 1c-config-objects-spec.md, формат ссылок,
наборы GeneratedType, строка в индексе спек.

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-05 20:53:37 +03:00
co-authored by Claude Opus 5
parent c1f07f96ad
commit 6dde36a690
20 changed files with 2071 additions and 39 deletions
@@ -1,4 +1,4 @@
# meta-validate v1.25 — Validate 1C metadata object structure (Python port)
# meta-validate v1.26 — Validate 1C metadata object structure (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
@@ -226,6 +226,8 @@ valid_types = (
"Report", "DataProcessor",
"CommonModule", "ScheduledJob", "EventSubscription",
"HTTPService", "WebService", "DefinedType",
# Внешний источник данных и его таблица (корень файла таблицы — <Table>).
"ExternalDataSource", "Table",
)
# Валидные типы метаданных без глубоких правил валидации — раньше падали как "Unrecognized"
@@ -259,6 +261,9 @@ generated_type_categories = {
"DataProcessor": ["Object", "Manager"],
"DefinedType": ["DefinedType"],
"ExternalDataSource": ["Manager", "TablesManager", "CubesManager"],
# Таблица внешнего источника: имя элемента трёхчастное (Префикс.Источник.Таблица),
# но проверка «имя оканчивается на .ИмяОбъекта» на нём работает как есть.
"Table": ["Manager", "Object", "Ref", "List", "Record", "RecordSet", "RecordKey", "RecordManager"],
}
# Types that have NO InternalInfo / GeneratedType
@@ -320,6 +325,9 @@ child_object_rules = {
"DocumentJournal": ["Column", "Form", "Template", "Command"],
"HTTPService": ["URLTemplate"],
"WebService": ["Operation"],
# Внешний источник: таблицы перечислены именами, функции лежат полными узлами.
"ExternalDataSource": ["Table", "Function", "Cube"],
"Table": ["Field", "Form", "Template", "Command"],
"Constant": ["Form"],
"DefinedType": [],
"CommonModule": [],
@@ -344,7 +352,8 @@ valid_property_values = {
"RealTimePosting": ["Allow", "Deny"],
"RegisterRecordsDeletion": ["AutoDelete", "AutoDeleteOnUnpost", "AutoDeleteOff"],
"RegisterRecordsWritingOnPost": ["WriteModified", "WriteSelected", "WriteAll"],
"DataLockControlMode": ["Automatic", "Managed"],
# AutomaticAndManaged — только у внешнего источника данных и его таблиц.
"DataLockControlMode": ["Automatic", "Managed", "AutomaticAndManaged"],
"FullTextSearch": ["Use", "DontUse"],
"DefaultPresentation": ["AsDescription", "AsCode"],
"HierarchyType": ["HierarchyFoldersAndItems", "HierarchyOfItems"],
@@ -1707,6 +1716,13 @@ if not is_adopted:
form_ref_bad = True
continue
# Ссылка на форму таблицы внешнего источника — шестичастная:
# ExternalDataSource.<Источник>.Table.<Таблица>.Form.<Форма>. Сводим её к четырём частям
# (вид = Table, объект = имя таблицы), дальше проверка общая.
if (len(parts) == 6 and parts[0] == "ExternalDataSource"
and parts[2] == "Table" and parts[4] == "Form"):
parts = ["Table", parts[3], "Form", parts[5]]
if len(parts) != 4 or parts[2] != "Form":
report_warn(f"20. {tag} '{ref}' — неожиданный вид ссылки на форму "
f"(ожидается 'CommonForm.Имя' или '<Вид>.<Объект>.Form.<Форма>')")