mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-17 00:05:17 +03:00
feat(meta-compile,meta-decompile): Characteristics — привязка ПВХ (v1.25/v0.16)
Блок «Дополнительные реквизиты и сведения»/контактная инфо. DSL `characteristics`:
массив {types:{from,key,filterField,filterValue}, values:{from,object,type,value}}
— имена зеркалят XML без xr:, -1-поля неявны. Синонимы XML-имён
(characteristicTypes/keyField/…) приняты.
Прощающий ввод (по мотивам dataPath): поля — голое→StandardAttribute.<EN>
(ссылочные Ref/Parent/Owner, RU→EN) / Attribute.<имя>, частичное Dimension.X/
Resource.X/StandardAttribute.X→+from (регистры ДопСведения), полный путь как
есть. from — рус.корни + короткая 3-сегм.→вставка TabularSection. filterValue —
голый предопределённый→+каталог из types.from.
Декомпилятор пишет короткую форму (dogfood: каждый из 212 объектов роундтрип-
тестирует резолвер). Асимметрия для безопасности: голая форма только для
Ref/Parent/Owner, прочие StandardAttribute.X — частичной формой.
remaining Characteristics=0 на ВСЕХ 212 объектах корпуса, регресс 44/44 ps1+py,
ps1↔py identical. spec §7.1.4, кейс catalog-characteristics.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.24 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.25 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -1338,6 +1338,99 @@ function Emit-ChoiceParameterLinks {
|
||||
X "$indent</ChoiceParameterLinks>"
|
||||
}
|
||||
|
||||
# --- Characteristics (привязка ПВХ «Дополнительные реквизиты и сведения») ---
|
||||
|
||||
# from: рус. корень (Справочник→Catalog) + член (ТабличнаяЧасть→TabularSection); короткая 3-сегментная
|
||||
# "<Тип>.X.Y" → вставить TabularSection (from — всегда таблица, не реквизит). Полный путь → как есть.
|
||||
function Normalize-CharFrom {
|
||||
param([string]$from)
|
||||
if (-not $from) { return $from }
|
||||
$parts = @("$from" -split '\.')
|
||||
if ($script:objectTypeSynonyms.ContainsKey($parts[0])) { $parts[0] = $script:objectTypeSynonyms[$parts[0]] }
|
||||
for ($i = 1; $i -lt $parts.Count; $i++) {
|
||||
switch -Regex ($parts[$i]) {
|
||||
'^ТабличнаяЧасть$' { $parts[$i] = 'TabularSection' }
|
||||
'^Измерение$' { $parts[$i] = 'Dimension' }
|
||||
'^Ресурс$' { $parts[$i] = 'Resource' }
|
||||
'^Реквизит$' { $parts[$i] = 'Attribute' }
|
||||
}
|
||||
}
|
||||
if ($parts.Count -eq 3 -and $parts[0] -in @('Catalog','Document','ChartOfCharacteristicTypes','ChartOfCalculationTypes','ChartOfAccounts','ExchangePlan','BusinessProcess','Task')) {
|
||||
$parts = @($parts[0], $parts[1], 'TabularSection', $parts[2])
|
||||
}
|
||||
return ($parts -join '.')
|
||||
}
|
||||
|
||||
# Стандартный реквизит ссылочного типа в полях Characteristics: Ref/Parent/Owner (по имени EN/RU).
|
||||
# Прочие стандартные реквизиты редки в полях — их задают частичной формой StandardAttribute.X.
|
||||
function Resolve-CharStdEn {
|
||||
param([string]$name)
|
||||
$n = "$name".ToLower()
|
||||
if ($n -eq 'ref' -or $n -eq 'ссылка') { return 'Ref' }
|
||||
if ($n -eq 'parent' -or $n -eq 'родитель') { return 'Parent' }
|
||||
if ($n -eq 'owner' -or $n -eq 'владелец') { return 'Owner' }
|
||||
return $null
|
||||
}
|
||||
|
||||
# Поле: голое→StandardAttribute.<EN>/Attribute.<имя>; частичное Member.X→<from>.Member.X; полный путь→verbatim.
|
||||
function Expand-CharField {
|
||||
param([string]$field, [string]$from)
|
||||
$s = "$field"
|
||||
if (-not $s) { return $s }
|
||||
if ($s -match '^(StandardAttribute|Attribute|Dimension|Resource)\.') { return "$from.$s" }
|
||||
if (-not $s.Contains('.')) {
|
||||
$en = Resolve-CharStdEn $s
|
||||
if ($en) { return "$from.StandardAttribute.$en" }
|
||||
return "$from.Attribute.$s"
|
||||
}
|
||||
return $s
|
||||
}
|
||||
|
||||
# filterValue: голый предопределённый → префикс каталога (2 сегмента) из typesFrom; полный путь → verbatim.
|
||||
function Expand-CharFilterValue {
|
||||
param([string]$fv, [string]$typesFrom)
|
||||
$s = "$fv"
|
||||
if (-not $s -or $s.Contains('.')) { return $s }
|
||||
$tp = @("$typesFrom" -split '\.')
|
||||
if ($tp.Count -ge 2) { return "$($tp[0]).$($tp[1]).$s" }
|
||||
return $s
|
||||
}
|
||||
|
||||
function Emit-Characteristics {
|
||||
param([string]$indent, $chars)
|
||||
if (-not $chars -or @($chars).Count -eq 0) { X "$indent<Characteristics/>"; return }
|
||||
X "$indent<Characteristics>"
|
||||
foreach ($ch in @($chars)) {
|
||||
$types = Get-ChElProp $ch @('types','characteristicTypes','типы')
|
||||
$values = Get-ChElProp $ch @('values','characteristicValues','значения')
|
||||
$tFrom = Normalize-CharFrom "$(Get-ChElProp $types @('from','source','источник'))"
|
||||
$vFrom = Normalize-CharFrom "$(Get-ChElProp $values @('from','source','источник'))"
|
||||
$key = Expand-CharField "$(Get-ChElProp $types @('key','keyField'))" $tFrom
|
||||
$tff = Expand-CharField "$(Get-ChElProp $types @('filterField','typesFilterField'))" $tFrom
|
||||
$tfv = Expand-CharFilterValue "$(Get-ChElProp $types @('filterValue','typesFilterValue'))" $tFrom
|
||||
$obj = Expand-CharField "$(Get-ChElProp $values @('object','objectField'))" $vFrom
|
||||
$typ = Expand-CharField "$(Get-ChElProp $values @('type','typeField'))" $vFrom
|
||||
$val = Expand-CharField "$(Get-ChElProp $values @('value','valueField'))" $vFrom
|
||||
X "$indent`t<xr:Characteristic>"
|
||||
X "$indent`t`t<xr:CharacteristicTypes from=`"$(Esc-Xml $tFrom)`">"
|
||||
X "$indent`t`t`t<xr:KeyField>$(Esc-Xml $key)</xr:KeyField>"
|
||||
X "$indent`t`t`t<xr:TypesFilterField>$(Esc-Xml $tff)</xr:TypesFilterField>"
|
||||
X "$indent`t`t`t<xr:TypesFilterValue xsi:type=`"xr:DesignTimeRef`">$(Esc-Xml $tfv)</xr:TypesFilterValue>"
|
||||
X "$indent`t`t`t<xr:DataPathField>-1</xr:DataPathField>"
|
||||
X "$indent`t`t`t<xr:MultipleValuesUseField>-1</xr:MultipleValuesUseField>"
|
||||
X "$indent`t`t</xr:CharacteristicTypes>"
|
||||
X "$indent`t`t<xr:CharacteristicValues from=`"$(Esc-Xml $vFrom)`">"
|
||||
X "$indent`t`t`t<xr:ObjectField>$(Esc-Xml $obj)</xr:ObjectField>"
|
||||
X "$indent`t`t`t<xr:TypeField>$(Esc-Xml $typ)</xr:TypeField>"
|
||||
X "$indent`t`t`t<xr:ValueField>$(Esc-Xml $val)</xr:ValueField>"
|
||||
X "$indent`t`t`t<xr:MultipleValuesKeyField>-1</xr:MultipleValuesKeyField>"
|
||||
X "$indent`t`t`t<xr:MultipleValuesOrderField>-1</xr:MultipleValuesOrderField>"
|
||||
X "$indent`t`t</xr:CharacteristicValues>"
|
||||
X "$indent`t</xr:Characteristic>"
|
||||
}
|
||||
X "$indent</Characteristics>"
|
||||
}
|
||||
|
||||
function Emit-Attribute {
|
||||
param([string]$indent, $parsed, [string]$context)
|
||||
# $context: "catalog", "document", "object", "processor", "tabular", "processor-tabular", "register"
|
||||
@@ -1758,7 +1851,7 @@ function Emit-CatalogProperties {
|
||||
X "$i<DefaultPresentation>$defaultPresentation</DefaultPresentation>"
|
||||
|
||||
Emit-StandardAttributes $i "Catalog"
|
||||
X "$i<Characteristics/>"
|
||||
Emit-Characteristics $i $def.characteristics
|
||||
X "$i<PredefinedDataUpdate>$(Get-EnumProp 'PredefinedDataUpdate' 'predefinedDataUpdate' 'Auto')</PredefinedDataUpdate>"
|
||||
X "$i<EditType>$(Get-EnumProp 'EditType' 'editType' 'InDialog')</EditType>"
|
||||
$quickChoice = if ($def.quickChoice -eq $true) { "true" } else { "false" }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# meta-compile v1.24 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.25 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -1396,6 +1396,91 @@ def emit_choice_parameter_links(indent, cpl):
|
||||
X(f'{indent}\t</xr:Link>')
|
||||
X(f'{indent}</ChoiceParameterLinks>')
|
||||
|
||||
# --- Characteristics (привязка ПВХ «Дополнительные реквизиты и сведения») ---
|
||||
|
||||
CHAR_FROM_TS_TYPES = {'Catalog', 'Document', 'ChartOfCharacteristicTypes', 'ChartOfCalculationTypes',
|
||||
'ChartOfAccounts', 'ExchangePlan', 'BusinessProcess', 'Task'}
|
||||
CHAR_MEMBER_RU = {'ТабличнаяЧасть': 'TabularSection', 'Измерение': 'Dimension', 'Ресурс': 'Resource', 'Реквизит': 'Attribute'}
|
||||
|
||||
def normalize_char_from(from_):
|
||||
if not from_:
|
||||
return from_
|
||||
parts = str(from_).split('.')
|
||||
if parts[0] in object_type_synonyms:
|
||||
parts[0] = object_type_synonyms[parts[0]]
|
||||
for i in range(1, len(parts)):
|
||||
if parts[i] in CHAR_MEMBER_RU:
|
||||
parts[i] = CHAR_MEMBER_RU[parts[i]]
|
||||
if len(parts) == 3 and parts[0] in CHAR_FROM_TS_TYPES:
|
||||
parts = [parts[0], parts[1], 'TabularSection', parts[2]]
|
||||
return '.'.join(parts)
|
||||
|
||||
def resolve_char_std_en(name):
|
||||
n = str(name).lower()
|
||||
if n in ('ref', 'ссылка'):
|
||||
return 'Ref'
|
||||
if n in ('parent', 'родитель'):
|
||||
return 'Parent'
|
||||
if n in ('owner', 'владелец'):
|
||||
return 'Owner'
|
||||
return None
|
||||
|
||||
def expand_char_field(field, from_):
|
||||
s = str(field or '')
|
||||
if not s:
|
||||
return s
|
||||
if re.match(r'^(StandardAttribute|Attribute|Dimension|Resource)\.', s):
|
||||
return f'{from_}.{s}'
|
||||
if '.' not in s:
|
||||
en = resolve_char_std_en(s)
|
||||
if en:
|
||||
return f'{from_}.StandardAttribute.{en}'
|
||||
return f'{from_}.Attribute.{s}'
|
||||
return s
|
||||
|
||||
def expand_char_filter_value(fv, types_from):
|
||||
s = str(fv or '')
|
||||
if not s or '.' in s:
|
||||
return s
|
||||
tp = str(types_from).split('.')
|
||||
if len(tp) >= 2:
|
||||
return f'{tp[0]}.{tp[1]}.{s}'
|
||||
return s
|
||||
|
||||
def emit_characteristics(indent, chars):
|
||||
if not chars:
|
||||
X(f'{indent}<Characteristics/>')
|
||||
return
|
||||
X(f'{indent}<Characteristics>')
|
||||
for ch in chars:
|
||||
types = ch_el_prop(ch, ['types', 'characteristicTypes', 'типы'])
|
||||
values = ch_el_prop(ch, ['values', 'characteristicValues', 'значения'])
|
||||
t_from = normalize_char_from(ch_el_prop(types, ['from', 'source', 'источник']) or '')
|
||||
v_from = normalize_char_from(ch_el_prop(values, ['from', 'source', 'источник']) or '')
|
||||
key = expand_char_field(ch_el_prop(types, ['key', 'keyField']), t_from)
|
||||
tff = expand_char_field(ch_el_prop(types, ['filterField', 'typesFilterField']), t_from)
|
||||
tfv = expand_char_filter_value(ch_el_prop(types, ['filterValue', 'typesFilterValue']), t_from)
|
||||
obj = expand_char_field(ch_el_prop(values, ['object', 'objectField']), v_from)
|
||||
typ = expand_char_field(ch_el_prop(values, ['type', 'typeField']), v_from)
|
||||
val = expand_char_field(ch_el_prop(values, ['value', 'valueField']), v_from)
|
||||
X(f'{indent}\t<xr:Characteristic>')
|
||||
X(f'{indent}\t\t<xr:CharacteristicTypes from="{esc_xml(t_from)}">')
|
||||
X(f'{indent}\t\t\t<xr:KeyField>{esc_xml(key)}</xr:KeyField>')
|
||||
X(f'{indent}\t\t\t<xr:TypesFilterField>{esc_xml(tff)}</xr:TypesFilterField>')
|
||||
X(f'{indent}\t\t\t<xr:TypesFilterValue xsi:type="xr:DesignTimeRef">{esc_xml(tfv)}</xr:TypesFilterValue>')
|
||||
X(f'{indent}\t\t\t<xr:DataPathField>-1</xr:DataPathField>')
|
||||
X(f'{indent}\t\t\t<xr:MultipleValuesUseField>-1</xr:MultipleValuesUseField>')
|
||||
X(f'{indent}\t\t</xr:CharacteristicTypes>')
|
||||
X(f'{indent}\t\t<xr:CharacteristicValues from="{esc_xml(v_from)}">')
|
||||
X(f'{indent}\t\t\t<xr:ObjectField>{esc_xml(obj)}</xr:ObjectField>')
|
||||
X(f'{indent}\t\t\t<xr:TypeField>{esc_xml(typ)}</xr:TypeField>')
|
||||
X(f'{indent}\t\t\t<xr:ValueField>{esc_xml(val)}</xr:ValueField>')
|
||||
X(f'{indent}\t\t\t<xr:MultipleValuesKeyField>-1</xr:MultipleValuesKeyField>')
|
||||
X(f'{indent}\t\t\t<xr:MultipleValuesOrderField>-1</xr:MultipleValuesOrderField>')
|
||||
X(f'{indent}\t\t</xr:CharacteristicValues>')
|
||||
X(f'{indent}\t</xr:Characteristic>')
|
||||
X(f'{indent}</Characteristics>')
|
||||
|
||||
def emit_attribute(indent, parsed, context):
|
||||
attr_name = parsed['name']
|
||||
ctx_reserved = RESERVED_BY_CONTEXT.get(context)
|
||||
@@ -1744,7 +1829,7 @@ def emit_catalog_properties(indent):
|
||||
default_presentation = get_enum_prop('DefaultPresentation', 'defaultPresentation', 'AsDescription')
|
||||
X(f'{i}<DefaultPresentation>{default_presentation}</DefaultPresentation>')
|
||||
emit_standard_attributes(i, 'Catalog')
|
||||
X(f'{i}<Characteristics/>')
|
||||
emit_characteristics(i, defn.get('characteristics'))
|
||||
X(f'{i}<PredefinedDataUpdate>{get_enum_prop("PredefinedDataUpdate", "predefinedDataUpdate", "Auto")}</PredefinedDataUpdate>')
|
||||
X(f'{i}<EditType>{get_enum_prop("EditType", "editType", "InDialog")}</EditType>')
|
||||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# meta-decompile v0.15 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# meta-decompile v0.16 — XML объекта метаданных 1С → JSON-черновик формата meta-compile
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
#
|
||||
# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только
|
||||
@@ -411,6 +411,52 @@ foreach ($pp in @(
|
||||
if ($null -ne $pv) { $dsl[$pp[1]] = $pv }
|
||||
}
|
||||
|
||||
# --- Characteristics (привязка ПВХ). Короткая форма: поля bare/partial, filterValue без каталога, from полный.
|
||||
function Shorten-CharField { param([string]$full, [string]$from)
|
||||
if ($full.StartsWith("$from.")) {
|
||||
$rest = $full.Substring($from.Length + 1)
|
||||
if ($rest -match '^StandardAttribute\.(Ref|Parent|Owner)$') { return $Matches[1] } # ссылочные станд. → голое
|
||||
if ($rest -match '^Attribute\.(.+)$') { return $Matches[1] } # кастом → голое
|
||||
return $rest # прочие StandardAttribute.X / Dimension.X / Resource.X → частичное (безопасно)
|
||||
}
|
||||
return $full
|
||||
}
|
||||
function Shorten-CharFilterValue { param([string]$full, [string]$typesFrom)
|
||||
$tp = @("$typesFrom" -split '\.')
|
||||
if ($tp.Count -ge 2) {
|
||||
$pref = "$($tp[0]).$($tp[1])."
|
||||
if ($full.StartsWith($pref)) { $tail = $full.Substring($pref.Length); if (-not $tail.Contains('.')) { return $tail } }
|
||||
}
|
||||
return $full
|
||||
}
|
||||
$charsNode = $props.SelectSingleNode('md:Characteristics', $nsm)
|
||||
if ($charsNode) {
|
||||
$chList = @($charsNode.SelectNodes('xr:Characteristic', $nsm))
|
||||
if ($chList.Count -gt 0) {
|
||||
$chArr = [System.Collections.ArrayList]@()
|
||||
foreach ($ch in $chList) {
|
||||
$ct = $ch.SelectSingleNode('xr:CharacteristicTypes', $nsm)
|
||||
$cv = $ch.SelectSingleNode('xr:CharacteristicValues', $nsm)
|
||||
$tFrom = $ct.GetAttribute('from'); $vFrom = $cv.GetAttribute('from')
|
||||
$gt = { param($n, $node) $x = $node.SelectSingleNode("xr:$n", $nsm); if ($x) { $x.InnerText } else { "" } }
|
||||
$types = [ordered]@{
|
||||
from = $tFrom
|
||||
key = Shorten-CharField (& $gt 'KeyField' $ct) $tFrom
|
||||
filterField = Shorten-CharField (& $gt 'TypesFilterField' $ct) $tFrom
|
||||
filterValue = Shorten-CharFilterValue (& $gt 'TypesFilterValue' $ct) $tFrom
|
||||
}
|
||||
$values = [ordered]@{
|
||||
from = $vFrom
|
||||
object = Shorten-CharField (& $gt 'ObjectField' $cv) $vFrom
|
||||
type = Shorten-CharField (& $gt 'TypeField' $cv) $vFrom
|
||||
value = Shorten-CharField (& $gt 'ValueField' $cv) $vFrom
|
||||
}
|
||||
[void]$chArr.Add([ordered]@{ types = $types; values = $values })
|
||||
}
|
||||
$dsl['characteristics'] = $chArr
|
||||
}
|
||||
}
|
||||
|
||||
# --- StandardAttributes: блок есть ⟺ кастомизация ≥1 стандартного реквизита.
|
||||
# Захватываем ОТКЛОНЕНИЯ от профиля материализованного блока (профиль компилятор восстановит сам).
|
||||
# Профиль Catalog: Owner{FC=ShowError,FFV=true}, Parent{FFV=true}, Description{FC=ShowError}. ---
|
||||
|
||||
Reference in New Issue
Block a user