mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-20 01:20:59 +03:00
feat(meta-compile,meta-decompile): условный профильный StandardAttributes для Catalog (v1.16/v0.3)
Раундтрип-находка (класс-2 + новый DSL-блок). Правило платформы, выведенное из корпуса
(1596/1640 каталогов) и подтверждённое синтетикой: блок <StandardAttributes> материализуется
ТОЛЬКО при кастомизации ≥1 стандартного реквизита; 0 all-default блоков, 44 голых без блока.
При материализации платформа заполняет характеристический профиль (Owner{FC=ShowError,FFV=true},
Parent{FFV=true}, Description{FC=ShowError}), не зависящий от иерархии/владельца.
Было: meta-compile писал блок ВСЕГДА единым all-default шаблоном (неверные пер-атрибутные
дефолты) → ADDED-блок у 44 голых + потеря профиля/кастомизаций у 1596.
Стало:
- meta-compile (дуал-порт): блок эмитится только при наличии DSL-ключа `standardAttributes`
(map реквизит→{synonym,fillChecking,fillFromFillingValue,fullTextSearch,dataHistory}); база =
профиль типа (stdAttrProfile), поверх — override. Emit-StandardAttribute параметризован (ov).
Эмиттер общий (Emit-StandardAttributesProfiled $type) — подключён только Catalog; прочие 13
типов пока на старом безусловном пути (мигрируем при их пилоте).
- meta-decompile: захват `standardAttributes` как отклонений от профиля (блока нет → ключ опущен).
- spec meta-dsl-spec.md v2.2: раздел 7.1.1 (правило, профиль, формат).
- тест-кейс catalog-standard-attributes (профиль + override synonym + Code.fillChecking).
Валидация: byte-match блока на синтетике; полный прогон −27k строк (474339→447285), обвал
cascade ADDED 11538→1242; регресс 34/34 (ps+py); 1С-cert снэпшотов зелёный.
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
85e13e95f2
commit
df5c1ee17d
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.15 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.16 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -817,13 +817,33 @@ $script:standardAttributesByType = @{
|
||||
"DocumentJournal" = @("Type","Ref","Date","Posted","DeletionMark","Number")
|
||||
}
|
||||
|
||||
# Профиль материализованного блока StandardAttributes (значения, которые платформа заполняет
|
||||
# автоматически при материализации блока, независимо от структуры каталога). Выведено из корпуса
|
||||
# (acc+erp: Owner.FFV=true 1592/1596, Owner.FC=ShowError 1589, Parent.FFV=true 1593, Description.FC=ShowError 1467)
|
||||
# и подтверждено синтетикой. Пока только Catalog (у прочих типов свои профили — добавим при их пилоте).
|
||||
$script:stdAttrProfile = @{
|
||||
"Catalog" = @{
|
||||
"Owner" = @{ FillChecking = "ShowError"; FillFromFillingValue = "true" }
|
||||
"Parent" = @{ FillFromFillingValue = "true" }
|
||||
"Description" = @{ FillChecking = "ShowError" }
|
||||
}
|
||||
}
|
||||
|
||||
# $ov — hashtable переопределений (профиль + DSL) для полей: FillChecking, FillFromFillingValue,
|
||||
# Synonym, FullTextSearch, DataHistory. Прочие поля — фиксированный schema-дефолт.
|
||||
function Emit-StandardAttribute {
|
||||
param([string]$indent, [string]$attrName)
|
||||
param([string]$indent, [string]$attrName, $ov = $null)
|
||||
function OvOr { param($k, $d) if ($ov -and $ov.ContainsKey($k)) { return $ov[$k] } else { return $d } }
|
||||
$fc = OvOr 'FillChecking' 'DontCheck'
|
||||
$ffv = OvOr 'FillFromFillingValue' 'false'
|
||||
$dh = OvOr 'DataHistory' 'Use'
|
||||
$fts = OvOr 'FullTextSearch' 'Use'
|
||||
$syn = OvOr 'Synonym' ''
|
||||
X "$indent<xr:StandardAttribute name=`"$attrName`">"
|
||||
X "$indent`t<xr:LinkByType/>"
|
||||
X "$indent`t<xr:FillChecking>DontCheck</xr:FillChecking>"
|
||||
X "$indent`t<xr:FillChecking>$fc</xr:FillChecking>"
|
||||
X "$indent`t<xr:MultiLine>false</xr:MultiLine>"
|
||||
X "$indent`t<xr:FillFromFillingValue>false</xr:FillFromFillingValue>"
|
||||
X "$indent`t<xr:FillFromFillingValue>$ffv</xr:FillFromFillingValue>"
|
||||
X "$indent`t<xr:CreateOnInput>Auto</xr:CreateOnInput>"
|
||||
X "$indent`t<xr:MaxValue xsi:nil=`"true`"/>"
|
||||
X "$indent`t<xr:ToolTip/>"
|
||||
@@ -834,12 +854,21 @@ function Emit-StandardAttribute {
|
||||
X "$indent`t<xr:ChoiceHistoryOnInput>Auto</xr:ChoiceHistoryOnInput>"
|
||||
X "$indent`t<xr:EditFormat/>"
|
||||
X "$indent`t<xr:PasswordMode>false</xr:PasswordMode>"
|
||||
X "$indent`t<xr:DataHistory>Use</xr:DataHistory>"
|
||||
X "$indent`t<xr:DataHistory>$dh</xr:DataHistory>"
|
||||
X "$indent`t<xr:MarkNegatives>false</xr:MarkNegatives>"
|
||||
X "$indent`t<xr:MinValue xsi:nil=`"true`"/>"
|
||||
X "$indent`t<xr:Synonym/>"
|
||||
if ($syn) {
|
||||
X "$indent`t<xr:Synonym>"
|
||||
X "$indent`t`t<v8:item>"
|
||||
X "$indent`t`t`t<v8:lang>ru</v8:lang>"
|
||||
X "$indent`t`t`t<v8:content>$(Esc-Xml $syn)</v8:content>"
|
||||
X "$indent`t`t</v8:item>"
|
||||
X "$indent`t</xr:Synonym>"
|
||||
} else {
|
||||
X "$indent`t<xr:Synonym/>"
|
||||
}
|
||||
X "$indent`t<xr:Comment/>"
|
||||
X "$indent`t<xr:FullTextSearch>Use</xr:FullTextSearch>"
|
||||
X "$indent`t<xr:FullTextSearch>$fts</xr:FullTextSearch>"
|
||||
X "$indent`t<xr:ChoiceParameterLinks/>"
|
||||
X "$indent`t<xr:FillValue xsi:nil=`"true`"/>"
|
||||
X "$indent`t<xr:Mask/>"
|
||||
@@ -858,6 +887,34 @@ function Emit-StandardAttributes {
|
||||
X "$indent</StandardAttributes>"
|
||||
}
|
||||
|
||||
# Профильный+условный эмиттер блока StandardAttributes (общий, ключёван типом).
|
||||
# Блок материализуется платформой ТОЛЬКО при кастомизации ≥1 стандартного реквизита → в DSL это
|
||||
# наличие ключа `standardAttributes` (map имяРеквизита → {synonym, fillChecking, fillFromFillingValue,
|
||||
# fullTextSearch, dataHistory}). Наличие ключа = блок включён; база = профиль типа (stdAttrProfile),
|
||||
# поверх — DSL-override. Ключа нет → блок опускаем. Пока подключён только Catalog; при пилоте прочих
|
||||
# типов: добавить их профиль в stdAttrProfile + переключить их вызов сюда (+ переснять снэпшоты).
|
||||
function Emit-StandardAttributesProfiled {
|
||||
param([string]$indent, [string]$objectType)
|
||||
if ($null -eq $def.standardAttributes) { return }
|
||||
$profile = $script:stdAttrProfile[$objectType]; if (-not $profile) { $profile = @{} }
|
||||
$attrs = $script:standardAttributesByType[$objectType]
|
||||
X "$indent<StandardAttributes>"
|
||||
foreach ($a in $attrs) {
|
||||
$ov = @{}
|
||||
if ($profile.ContainsKey($a)) { foreach ($k in $profile[$a].Keys) { $ov[$k] = $profile[$a][$k] } }
|
||||
$d = $def.standardAttributes.$a
|
||||
if ($d) {
|
||||
if ($null -ne $d.synonym) { $ov['Synonym'] = "$($d.synonym)" }
|
||||
if ($d.fillChecking) { $ov['FillChecking'] = "$($d.fillChecking)" }
|
||||
if ($null -ne $d.fillFromFillingValue) { $ov['FillFromFillingValue'] = if ($d.fillFromFillingValue) { 'true' } else { 'false' } }
|
||||
if ($d.fullTextSearch) { $ov['FullTextSearch'] = "$($d.fullTextSearch)" }
|
||||
if ($d.dataHistory) { $ov['DataHistory'] = "$($d.dataHistory)" }
|
||||
}
|
||||
Emit-StandardAttribute "$indent`t" $a $ov
|
||||
}
|
||||
X "$indent</StandardAttributes>"
|
||||
}
|
||||
|
||||
# TabularSection standard attributes (just LineNumber)
|
||||
function Emit-TabularStandardAttributes {
|
||||
param([string]$indent)
|
||||
@@ -1247,7 +1304,7 @@ function Emit-CatalogProperties {
|
||||
$defaultPresentation = Get-EnumProp "DefaultPresentation" "defaultPresentation" "AsDescription"
|
||||
X "$i<DefaultPresentation>$defaultPresentation</DefaultPresentation>"
|
||||
|
||||
Emit-StandardAttributes $i "Catalog"
|
||||
Emit-StandardAttributesProfiled $i "Catalog"
|
||||
X "$i<Characteristics/>"
|
||||
X "$i<PredefinedDataUpdate>Auto</PredefinedDataUpdate>"
|
||||
X "$i<EditType>InDialog</EditType>"
|
||||
|
||||
@@ -813,12 +813,29 @@ standard_attributes_by_type = {
|
||||
'DocumentJournal': ['Type', 'Ref', 'Date', 'Posted', 'DeletionMark', 'Number'],
|
||||
}
|
||||
|
||||
def emit_standard_attribute(indent, attr_name):
|
||||
# Профиль материализованного блока StandardAttributes (см. коммент в .ps1). Пока только Catalog.
|
||||
std_attr_profile = {
|
||||
'Catalog': {
|
||||
'Owner': {'FillChecking': 'ShowError', 'FillFromFillingValue': 'true'},
|
||||
'Parent': {'FillFromFillingValue': 'true'},
|
||||
'Description': {'FillChecking': 'ShowError'},
|
||||
}
|
||||
}
|
||||
|
||||
# ov — dict переопределений (профиль + DSL): FillChecking, FillFromFillingValue, Synonym,
|
||||
# FullTextSearch, DataHistory. Прочие поля — фиксированный schema-дефолт.
|
||||
def emit_standard_attribute(indent, attr_name, ov=None):
|
||||
ov = ov or {}
|
||||
fc = ov.get('FillChecking', 'DontCheck')
|
||||
ffv = ov.get('FillFromFillingValue', 'false')
|
||||
dh = ov.get('DataHistory', 'Use')
|
||||
fts = ov.get('FullTextSearch', 'Use')
|
||||
syn = ov.get('Synonym', '')
|
||||
X(f'{indent}<xr:StandardAttribute name="{attr_name}">')
|
||||
X(f'{indent}\t<xr:LinkByType/>')
|
||||
X(f'{indent}\t<xr:FillChecking>DontCheck</xr:FillChecking>')
|
||||
X(f'{indent}\t<xr:FillChecking>{fc}</xr:FillChecking>')
|
||||
X(f'{indent}\t<xr:MultiLine>false</xr:MultiLine>')
|
||||
X(f'{indent}\t<xr:FillFromFillingValue>false</xr:FillFromFillingValue>')
|
||||
X(f'{indent}\t<xr:FillFromFillingValue>{ffv}</xr:FillFromFillingValue>')
|
||||
X(f'{indent}\t<xr:CreateOnInput>Auto</xr:CreateOnInput>')
|
||||
X(f'{indent}\t<xr:MaxValue xsi:nil="true"/>')
|
||||
X(f'{indent}\t<xr:ToolTip/>')
|
||||
@@ -829,12 +846,20 @@ def emit_standard_attribute(indent, attr_name):
|
||||
X(f'{indent}\t<xr:ChoiceHistoryOnInput>Auto</xr:ChoiceHistoryOnInput>')
|
||||
X(f'{indent}\t<xr:EditFormat/>')
|
||||
X(f'{indent}\t<xr:PasswordMode>false</xr:PasswordMode>')
|
||||
X(f'{indent}\t<xr:DataHistory>Use</xr:DataHistory>')
|
||||
X(f'{indent}\t<xr:DataHistory>{dh}</xr:DataHistory>')
|
||||
X(f'{indent}\t<xr:MarkNegatives>false</xr:MarkNegatives>')
|
||||
X(f'{indent}\t<xr:MinValue xsi:nil="true"/>')
|
||||
X(f'{indent}\t<xr:Synonym/>')
|
||||
if syn:
|
||||
X(f'{indent}\t<xr:Synonym>')
|
||||
X(f'{indent}\t\t<v8:item>')
|
||||
X(f'{indent}\t\t\t<v8:lang>ru</v8:lang>')
|
||||
X(f'{indent}\t\t\t<v8:content>{esc_xml(syn)}</v8:content>')
|
||||
X(f'{indent}\t\t</v8:item>')
|
||||
X(f'{indent}\t</xr:Synonym>')
|
||||
else:
|
||||
X(f'{indent}\t<xr:Synonym/>')
|
||||
X(f'{indent}\t<xr:Comment/>')
|
||||
X(f'{indent}\t<xr:FullTextSearch>Use</xr:FullTextSearch>')
|
||||
X(f'{indent}\t<xr:FullTextSearch>{fts}</xr:FullTextSearch>')
|
||||
X(f'{indent}\t<xr:ChoiceParameterLinks/>')
|
||||
X(f'{indent}\t<xr:FillValue xsi:nil="true"/>')
|
||||
X(f'{indent}\t<xr:Mask/>')
|
||||
@@ -850,6 +875,31 @@ def emit_standard_attributes(indent, object_type):
|
||||
emit_standard_attribute(f'{indent}\t', a)
|
||||
X(f'{indent}</StandardAttributes>')
|
||||
|
||||
# Профильный+условный эмиттер блока StandardAttributes (общий, ключёван типом; см. коммент в .ps1).
|
||||
def emit_standard_attributes_profiled(indent, object_type):
|
||||
sa = defn.get('standardAttributes')
|
||||
if sa is None:
|
||||
return
|
||||
profile = std_attr_profile.get(object_type, {})
|
||||
attrs = standard_attributes_by_type[object_type]
|
||||
X(f'{indent}<StandardAttributes>')
|
||||
for a in attrs:
|
||||
ov = dict(profile.get(a, {}))
|
||||
d = sa.get(a) if isinstance(sa, dict) else None
|
||||
if d:
|
||||
if d.get('synonym') is not None:
|
||||
ov['Synonym'] = str(d['synonym'])
|
||||
if d.get('fillChecking'):
|
||||
ov['FillChecking'] = str(d['fillChecking'])
|
||||
if d.get('fillFromFillingValue') is not None:
|
||||
ov['FillFromFillingValue'] = 'true' if d['fillFromFillingValue'] else 'false'
|
||||
if d.get('fullTextSearch'):
|
||||
ov['FullTextSearch'] = str(d['fullTextSearch'])
|
||||
if d.get('dataHistory'):
|
||||
ov['DataHistory'] = str(d['dataHistory'])
|
||||
emit_standard_attribute(f'{indent}\t', a, ov)
|
||||
X(f'{indent}</StandardAttributes>')
|
||||
|
||||
def emit_tabular_standard_attributes(indent):
|
||||
X(f'{indent}<StandardAttributes>')
|
||||
emit_standard_attribute(f'{indent}\t', 'LineNumber')
|
||||
@@ -1183,7 +1233,7 @@ def emit_catalog_properties(indent):
|
||||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||||
default_presentation = get_enum_prop('DefaultPresentation', 'defaultPresentation', 'AsDescription')
|
||||
X(f'{i}<DefaultPresentation>{default_presentation}</DefaultPresentation>')
|
||||
emit_standard_attributes(i, 'Catalog')
|
||||
emit_standard_attributes_profiled(i, 'Catalog')
|
||||
X(f'{i}<Characteristics/>')
|
||||
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
|
||||
X(f'{i}<EditType>InDialog</EditType>')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# meta-decompile v0.2 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# meta-decompile v0.3 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
#
|
||||
# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только
|
||||
@@ -226,6 +226,40 @@ Add-EnumProp 'choiceMode' 'ChoiceMode' 'BothWays'
|
||||
Add-EnumProp 'dataLockControlMode' 'DataLockControlMode' 'Automatic'
|
||||
Add-EnumProp 'fullTextSearch' 'FullTextSearch' 'Use'
|
||||
|
||||
# --- StandardAttributes: блок есть ⟺ кастомизация ≥1 стандартного реквизита.
|
||||
# Захватываем ОТКЛОНЕНИЯ от профиля материализованного блока (профиль компилятор восстановит сам).
|
||||
# Профиль Catalog: Owner{FC=ShowError,FFV=true}, Parent{FFV=true}, Description{FC=ShowError}. ---
|
||||
$catStdProfile = @{
|
||||
'Owner' = @{ fillChecking = 'ShowError'; fillFromFillingValue = $true }
|
||||
'Parent' = @{ fillFromFillingValue = $true }
|
||||
'Description' = @{ fillChecking = 'ShowError' }
|
||||
}
|
||||
$saNode = $props.SelectSingleNode('md:StandardAttributes', $nsm)
|
||||
if ($saNode) {
|
||||
$saMap = [ordered]@{}
|
||||
foreach ($sa in @($saNode.SelectNodes('xr:StandardAttribute', $nsm))) {
|
||||
$an = $sa.GetAttribute('name')
|
||||
$prof = if ($catStdProfile.ContainsKey($an)) { $catStdProfile[$an] } else { @{} }
|
||||
$ov = [ordered]@{}
|
||||
# FillChecking (профиль или DontCheck)
|
||||
$fcN = $sa.SelectSingleNode('xr:FillChecking', $nsm); $fc = if ($fcN) { $fcN.InnerText } else { 'DontCheck' }
|
||||
$profFc = if ($prof.ContainsKey('fillChecking')) { $prof['fillChecking'] } else { 'DontCheck' }
|
||||
if ($fc -ne $profFc) { $ov['fillChecking'] = $fc }
|
||||
# FillFromFillingValue (профиль или false)
|
||||
$ffvN = $sa.SelectSingleNode('xr:FillFromFillingValue', $nsm); $ffv = ($ffvN -and $ffvN.InnerText -eq 'true')
|
||||
$profFfv = ($prof['fillFromFillingValue'] -eq $true)
|
||||
if ($ffv -ne $profFfv) { $ov['fillFromFillingValue'] = $ffv }
|
||||
# Synonym (профиль пуст)
|
||||
$syn = Get-MLru ($sa.SelectSingleNode('xr:Synonym', $nsm))
|
||||
if ($syn) { $ov['synonym'] = $syn }
|
||||
# FullTextSearch / DataHistory (профиль = Use)
|
||||
$ftsN = $sa.SelectSingleNode('xr:FullTextSearch', $nsm); if ($ftsN -and $ftsN.InnerText -ne 'Use') { $ov['fullTextSearch'] = $ftsN.InnerText }
|
||||
$dhN = $sa.SelectSingleNode('xr:DataHistory', $nsm); if ($dhN -and $dhN.InnerText -ne 'Use') { $ov['dataHistory'] = $dhN.InnerText }
|
||||
if ($ov.Count -gt 0) { $saMap[$an] = $ov }
|
||||
}
|
||||
$dsl['standardAttributes'] = $saMap # даже пустой = блок есть (чистый профиль)
|
||||
}
|
||||
|
||||
# --- ChildObjects: Attributes + TabularSections ---
|
||||
$childObjs = $objNode.SelectSingleNode('md:ChildObjects', $nsm)
|
||||
if ($childObjs) {
|
||||
|
||||
Reference in New Issue
Block a user