feat(meta-compile): отклонять имена реквизитов, совпадающие со стандартными (v1.20)

Раньше совпадение имени собственного реквизита со стандартным (Код,
Наименование, Владелец, Родитель, Ссылка, Предопределённый, …) давало лишь
предупреждение — теперь жёсткий отказ (платформа такое имя не примет).

Проверка типозависима (reservedByContext): у справочника один набор
стандартных имён, у документа — другой. Напр. «Номер» — легальный реквизит
справочника, отклоняется только у документа. Прочие контексты сохраняют
мягкое предупреждение по плоскому списку; реквизиты ТЧ не проверяются.

Кейс catalog-object-props: «Код»→«КодНастройки» (было ложное имя). Новый
негативный кейс catalog-attr-reserved-name. spec §4. Регресс 41/41 ps1+py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-03 19:07:55 +03:00
parent 1565ae23ab
commit b4d48ca656
6 changed files with 70 additions and 7 deletions
@@ -1,4 +1,4 @@
# meta-compile v1.19 — Compile 1C metadata object from JSON
# meta-compile v1.20 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1129,11 +1129,27 @@ $script:reservedAttrNames = @{
"Account"="Счет"; "ValueType"="ТипЗначения"; "ActionPeriodIsBasic"="ПериодДействияБазовый"
}
# Стандартные реквизиты по типу объекта (ключи из reservedAttrNames). Имя реквизита, совпадающее
# с ними (англ. ИЛИ рус.), платформа не позволит — жёсткий отказ. Контексты вне карты → мягкое предупреждение.
$script:reservedByContext = @{
"catalog" = @("Ref","DeletionMark","Predefined","PredefinedDataName","Code","Description","Owner","Parent","IsFolder")
"document" = @("Ref","DeletionMark","Date","Number","Posted")
}
function Emit-Attribute {
param([string]$indent, $parsed, [string]$context)
# $context: "catalog", "document", "object", "processor", "tabular", "processor-tabular", "register"
$attrName = $parsed.name
if ($context -notin @("tabular", "processor-tabular") -and
$ctxReserved = $script:reservedByContext[$context]
if ($ctxReserved) {
foreach ($en in $ctxReserved) {
$ru = $script:reservedAttrNames[$en]
if (($attrName -ieq $en) -or ($ru -and $attrName -ieq $ru)) {
Write-Error "Имя реквизита '$attrName' зарезервировано стандартным реквизитом ($en/$ru) объекта '$context'. Выберите другое имя."
exit 1
}
}
} elseif ($context -notin @("tabular", "processor-tabular") -and
($script:reservedAttrNames.ContainsKey($attrName) -or $script:reservedAttrNames.ContainsValue($attrName))) {
Write-Warning "Attribute '$attrName' conflicts with a standard attribute name. This may cause errors when loading into 1C."
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.19 — Compile 1C metadata object from JSON
# meta-compile v1.20 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1156,9 +1156,37 @@ RESERVED_ATTR_NAMES_RU = {
'\u041f\u0435\u0440\u0438\u043e\u0434\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0411\u0430\u0437\u043e\u0432\u044b\u0439',
}
# Стандартные реквизиты по типу объекта (EN + RU). Совпадение имени реквизита с ними платформа
# не позволит — жёсткий отказ. Контексты вне карты → мягкое предупреждение по плоскому списку.
RESERVED_BY_CONTEXT = {
'catalog': {
'ref', 'ссылка', # Ссылка
'deletionmark', 'пометкаудаления', # ПометкаУдаления
'predefined', 'предопределенный', # Предопределенный
'predefineddataname', 'имяпредопределенныхданных', # ИмяПредопределенныхДанных
'code', 'код', # Код
'description', 'наименование', # Наименование
'owner', 'владелец', # Владелец
'parent', 'родитель', # Родитель
'isfolder', 'этогруппа', # ЭтоГруппа
},
'document': {
'ref', 'ссылка', # Ссылка
'deletionmark', 'пометкаудаления', # ПометкаУдаления
'date', 'дата', # Дата
'number', 'номер', # Номер
'posted', 'проведен', # Проведен
},
}
def emit_attribute(indent, parsed, context):
attr_name = parsed['name']
if context not in ('tabular', 'processor-tabular') and (attr_name in RESERVED_ATTR_NAMES or attr_name in RESERVED_ATTR_NAMES_RU):
ctx_reserved = RESERVED_BY_CONTEXT.get(context)
if ctx_reserved is not None:
if attr_name.lower() in ctx_reserved:
print(f"meta-compile: имя реквизита '{attr_name}' зарезервировано стандартным реквизитом объекта '{context}'. Выберите другое имя.", file=sys.stderr)
sys.exit(1)
elif context not in ('tabular', 'processor-tabular') and (attr_name in RESERVED_ATTR_NAMES or attr_name in RESERVED_ATTR_NAMES_RU):
print(f"WARNING: Attribute '{attr_name}' conflicts with a standard attribute name. This may cause errors when loading into 1C.", file=sys.stderr)
uid = new_uuid()
X(f'{indent}<Attribute uuid="{uid}">')
+10
View File
@@ -194,6 +194,16 @@ JSON DSL для описания объектов метаданных конф
Раздельная форма эквивалентна `String(100)`, `Number(15,2)`, `Number(15,2,nonneg)`.
Если `type` уже содержит скобки — `length`/`precision` игнорируются.
**Зарезервированные имена.** Имя собственного реквизита не должно совпадать со стандартным реквизитом объекта
(англ. или рус., регистронезависимо) — платформа такое не примет. Компилятор отклоняет с ошибкой:
- **Catalog**: `Ref`/`Ссылка`, `DeletionMark`/`ПометкаУдаления`, `Predefined`/`Предопределенный`,
`PredefinedDataName`/`ИмяПредопределенныхДанных`, `Code`/`Код`, `Description`/`Наименование`, `Owner`/`Владелец`,
`Parent`/`Родитель`, `IsFolder`/`ЭтоГруппа`.
- **Document**: `Ref`/`Ссылка`, `DeletionMark`/`ПометкаУдаления`, `Date`/`Дата`, `Number`/`Номер`, `Posted`/`Проведен`.
Проверка **типозависима**: напр. `Номер` — легальное имя реквизита справочника (стандартный он только у документа).
Реквизиты табличных частей проверке не подлежат (там зарезервирован лишь `НомерСтроки`).
### 4.3 Флаги
| Флаг | Действие | Применимость |
@@ -0,0 +1,9 @@
{
"name": "Ошибка: имя реквизита совпадает со стандартным",
"input": {
"type": "Catalog",
"name": "Тест",
"attributes": ["Код: String(9)"]
},
"expectError": "зарезервировано стандартным реквизитом"
}
@@ -16,7 +16,7 @@
"defaultListForm": "Справочник.НастройкиОбмена.Форма.ФормаСписка",
"defaultChoiceForm": "Справочник.НастройкиОбмена.ФормаВыбора",
"attributes": [
"Код: String(9)"
"КодНастройки: String(9)"
]
},
"validatePath": "Catalogs/НастройкиОбмена",
@@ -89,11 +89,11 @@
<ChildObjects>
<Attribute uuid="UUID-012">
<Properties>
<Name>Код</Name>
<Name>КодНастройки</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Код</v8:content>
<v8:content>Код настройки</v8:content>
</v8:item>
</Synonym>
<Comment/>