diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 0dcba72e..23cfd241 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.33 — Compile 1C metadata object from JSON +# meta-compile v1.34 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -1037,6 +1037,11 @@ $script:stdAttrProfile = @{ "Parent" = @{ FillFromFillingValue = "true" } "Description" = @{ FillChecking = "ShowError" } } + # ExchangePlan: блок материализуется всегда (не условный), Наименование/Код → FillChecking=ShowError (корпус 40/38 из 41). + "ExchangePlan" = @{ + "Description" = @{ FillChecking = "ShowError" } + "Code" = @{ FillChecking = "ShowError" } + } } # $ov — hashtable переопределений (профиль + DSL) для полей: FillChecking, FillFromFillingValue, @@ -1097,7 +1102,7 @@ function Emit-StandardAttribute { # Прочие типы (не в множестве) → блок эмитится всегда (текущее поведение, пока их правило не выведено). # - stdAttrProfile[тип]: профиль материализованного блока (пусто = schema-дефолт), поверх — DSL-override. # Миграция типа = добавить его в stdAttrConditionalTypes + stdAttrProfile и переснять снэпшоты; КОД НЕ ТРОГАЕМ. -$script:stdAttrConditionalTypes = @('Catalog') +$script:stdAttrConditionalTypes = @('Catalog', 'ExchangePlan') function Emit-StandardAttributes { param([string]$indent, [string]$objectType) $attrs = $script:standardAttributesByType[$objectType] @@ -1106,11 +1111,15 @@ function Emit-StandardAttributes { $sa = $def.standardAttributes if ($conditional -and $null -eq $sa) { return } # условный тип без кастомизации → блока нет $profile = $script:stdAttrProfile[$objectType]; if (-not $profile) { $profile = @{} } + # Доп. (опциональные) стандартные реквизиты вне фикс-списка типа — напр. ExchangeDate у части ПланОбмена + # (легаси, присутствие не выводится из свойств). Эмитим по факту наличия ключа в DSL, ПЕРЕД фикс-списком (их позиция). + $extra = @() + if ($sa) { foreach ($k in $sa.PSObject.Properties.Name) { if ($attrs -notcontains $k) { $extra += $k } } } X "$indent" - foreach ($a in $attrs) { + foreach ($a in ($extra + $attrs)) { $ov = @{} if ($profile.ContainsKey($a)) { foreach ($k in $profile[$a].Keys) { $ov[$k] = $profile[$a][$k] } } - if ($conditional -and $sa) { + if ($sa) { # DSL-override применяем всегда при наличии ключа (для не-условных типов тоже, напр. ExchangePlan) $d = $sa.$a if ($d) { if ($null -ne $d.synonym) { $ov['Synonym'] = $d.synonym } # строка ИЛИ {ru,en} @@ -2476,62 +2485,70 @@ function Emit-ExchangePlanProperties { X "$i$(Esc-Xml $objName)" Emit-MLText $i "Synonym" $synonym - X "$i" - X "$itrue" + if ($def.comment) { X "$i$(Esc-XmlText "$($def.comment)")" } else { X "$i" } + $useStdCmd = if (Get-BoolProp "useStandardCommands" $true) { "true" } else { "false" } + X "$i$useStdCmd" $codeLength = if ($null -ne $def.codeLength) { "$($def.codeLength)" } else { "9" } - $descriptionLength = if ($null -ne $def.descriptionLength) { "$($def.descriptionLength)" } else { "100" } + $descriptionLength = if ($null -ne $def.descriptionLength) { "$($def.descriptionLength)" } else { "150" } $codeAllowedLength = Get-EnumProp "CodeAllowedLength" "codeAllowedLength" "Variable" X "$i$codeLength" X "$i$codeAllowedLength" X "$i$descriptionLength" - X "$iAsDescription" - X "$iInDialog" + X "$i$(Get-EnumProp 'DefaultPresentation' 'defaultPresentation' 'AsDescription')" + X "$i$(Get-EnumProp 'EditType' 'editType' 'InDialog')" + $quickChoice = if ($def.quickChoice -eq $true) { "true" } else { "false" } + X "$i$quickChoice" + X "$i$(Get-EnumProp 'ChoiceMode' 'choiceMode' 'BothWays')" + + # InputByString: override `inputByString` ЛИБО дефолт [Descr при D>0]+[Code при C>0] (prefix ExchangePlan). + if (Test-DefKey 'inputByString') { + $ibFields = @($def.inputByString | ForEach-Object { Expand-DataPath "$_" }) + } else { + $ibFields = @() + if ([int]$descriptionLength -gt 0) { $ibFields += "ExchangePlan.$objName.StandardAttribute.Description" } + if ([int]$codeLength -gt 0) { $ibFields += "ExchangePlan.$objName.StandardAttribute.Code" } + } + Emit-FieldBlock $i "InputByString" $ibFields + X "$i$(Get-EnumProp 'SearchStringModeOnInputByString' 'searchStringModeOnInputByString' 'Begin')" + X "$iDontUse" + X "$iDirectly" + Emit-FormRef $i "DefaultObjectForm" $def.defaultObjectForm + Emit-FormRef $i "DefaultListForm" $def.defaultListForm + Emit-FormRef $i "DefaultChoiceForm" $def.defaultChoiceForm + Emit-FormRef $i "AuxiliaryObjectForm" $def.auxiliaryObjectForm + Emit-FormRef $i "AuxiliaryListForm" $def.auxiliaryListForm + Emit-FormRef $i "AuxiliaryChoiceForm" $def.auxiliaryChoiceForm Emit-StandardAttributes $i "ExchangePlan" + Emit-Characteristics $i $def.characteristics + Emit-BasedOn $i $def.basedOn $distributed = if ($def.distributedInfoBase -eq $true) { "true" } else { "false" } $includeExt = if ($def.includeConfigurationExtensions -eq $true) { "true" } else { "false" } X "$i$distributed" X "$i$includeExt" - X "$i" - $quickChoice = if ($def.quickChoice -eq $true) { "true" } else { "false" } - X "$i$quickChoice" - X "$iBothWays" - X "$i" - X "$i`tExchangePlan.$objName.StandardAttribute.Description" - X "$i`tExchangePlan.$objName.StandardAttribute.Code" - X "$i" - X "$iBegin" - X "$iDontUse" - X "$iDirectly" - X "$i" - X "$i" - X "$i" - X "$i" - X "$i" - X "$i" - X "$ifalse" - X "$i" - - $dataLockControlMode = Get-EnumProp "DataLockControlMode" "dataLockControlMode" "Automatic" - X "$i$dataLockControlMode" - - $fullTextSearch = Get-EnumProp "FullTextSearch" "fullTextSearch" "Use" - X "$i$fullTextSearch" + X "$i$(Get-EnumProp 'CreateOnInput' 'createOnInput' 'DontUse')" + X "$i$(Get-EnumProp 'ChoiceHistoryOnInput' 'choiceHistoryOnInput' 'Auto')" + $inclHelp = if (Get-BoolProp "includeHelpInContents" $false) { "true" } else { "false" } + X "$i$inclHelp" + $dlFields = if (Test-DefKey 'dataLockFields') { @($def.dataLockFields | ForEach-Object { Expand-DataPath "$_" }) } else { @() } + Emit-FieldBlock $i "DataLockFields" $dlFields + X "$i$(Get-EnumProp 'DataLockControlMode' 'dataLockControlMode' 'Managed')" + X "$i$(Get-EnumProp 'FullTextSearch' 'fullTextSearch' 'Use')" Emit-MLText $i "ObjectPresentation" $def.objectPresentation Emit-MLText $i "ExtendedObjectPresentation" $def.extendedObjectPresentation Emit-MLText $i "ListPresentation" $def.listPresentation Emit-MLText $i "ExtendedListPresentation" $def.extendedListPresentation Emit-MLText $i "Explanation" $def.explanation - X "$iDontUse" - X "$iAuto" - X "$iDontUse" - X "$ifalse" - X "$ifalse" + X "$i$(Get-EnumProp 'DataHistory' 'dataHistory' 'DontUse')" + $updDH = if (Get-BoolProp "updateDataHistoryImmediatelyAfterWrite" $false) { "true" } else { "false" } + X "$i$updDH" + $execDH = if (Get-BoolProp "executeAfterWriteDataHistoryVersionProcessing" $false) { "true" } else { "false" } + X "$i$execDH" } function Emit-ChartOfCharacteristicTypesProperties { diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index d99a1b84..99a759da 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-compile v1.33 — Compile 1C metadata object from JSON +# meta-compile v1.34 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -1050,7 +1050,12 @@ std_attr_profile = { 'Owner': {'FillChecking': 'ShowError', 'FillFromFillingValue': 'true'}, 'Parent': {'FillFromFillingValue': 'true'}, 'Description': {'FillChecking': 'ShowError'}, - } + }, + # ExchangePlan: блок при кастомизации; Наименование/Код → FillChecking=ShowError (корпус 40/38 из 41). + 'ExchangePlan': { + 'Description': {'FillChecking': 'ShowError'}, + 'Code': {'FillChecking': 'ShowError'}, + }, } # ov — dict переопределений (профиль + DSL): FillChecking, FillFromFillingValue, Synonym, @@ -1116,7 +1121,7 @@ def emit_standard_attribute(indent, attr_name, ov=None): # Единый эмиттер блока StandardAttributes — поведение правят ДАННЫЕ, не форк кода (см. коммент в .ps1). # std_attr_conditional_types: типы, где блок только при кастомизации (DSL-ключ standardAttributes). # Прочие типы → блок всегда (текущее поведение). Миграция типа = +строчка в оба справочника + снэпшоты. -std_attr_conditional_types = {'Catalog'} +std_attr_conditional_types = {'Catalog', 'ExchangePlan'} def emit_standard_attributes(indent, object_type): attrs = standard_attributes_by_type.get(object_type) if not attrs: @@ -1126,10 +1131,13 @@ def emit_standard_attributes(indent, object_type): if conditional and sa is None: return profile = std_attr_profile.get(object_type, {}) + # Доп. (опциональные) стандартные реквизиты вне фикс-списка — напр. ExchangeDate у части ПланОбмена + # (легаси, присутствие не выводится). Эмитим по факту ключа в DSL, ПЕРЕД фикс-списком (их позиция). + extra = [k for k in sa if k not in attrs] if isinstance(sa, dict) else [] X(f'{indent}') - for a in attrs: + for a in extra + list(attrs): ov = dict(profile.get(a, {})) - if conditional and isinstance(sa, dict): + if isinstance(sa, dict): d = sa.get(a) if d: if d.get('synonym') is not None: @@ -2407,54 +2415,62 @@ def emit_exchange_plan_properties(indent): i = indent X(f'{i}{esc_xml(obj_name)}') emit_mltext(i, 'Synonym', synonym) - X(f'{i}') - X(f'{i}true') + if defn.get('comment'): + X(f'{i}{esc_xml_text(str(defn["comment"]))}') + else: + X(f'{i}') + X(f'{i}{"true" if get_bool_prop("useStandardCommands", True) else "false"}') code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9' - description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '100' + description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '150' code_allowed_length = get_enum_prop('CodeAllowedLength', 'codeAllowedLength', 'Variable') X(f'{i}{code_length}') X(f'{i}{code_allowed_length}') X(f'{i}{description_length}') - X(f'{i}AsDescription') - X(f'{i}InDialog') + X(f'{i}{get_enum_prop("DefaultPresentation", "defaultPresentation", "AsDescription")}') + X(f'{i}{get_enum_prop("EditType", "editType", "InDialog")}') + X(f'{i}{"true" if defn.get("quickChoice") is True else "false"}') + X(f'{i}{get_enum_prop("ChoiceMode", "choiceMode", "BothWays")}') + # InputByString: override `inputByString` ЛИБО дефолт [Descr при D>0]+[Code при C>0] (prefix ExchangePlan). + if 'inputByString' in defn: + ib_fields = [expand_data_path(str(x)) for x in (defn.get('inputByString') or [])] + else: + ib_fields = [] + if int(description_length) > 0: + ib_fields.append(f'ExchangePlan.{obj_name}.StandardAttribute.Description') + if int(code_length) > 0: + ib_fields.append(f'ExchangePlan.{obj_name}.StandardAttribute.Code') + emit_field_block(i, 'InputByString', ib_fields) + X(f'{i}{get_enum_prop("SearchStringModeOnInputByString", "searchStringModeOnInputByString", "Begin")}') + X(f'{i}DontUse') + X(f'{i}Directly') + emit_form_ref(i, 'DefaultObjectForm', defn.get('defaultObjectForm')) + emit_form_ref(i, 'DefaultListForm', defn.get('defaultListForm')) + emit_form_ref(i, 'DefaultChoiceForm', defn.get('defaultChoiceForm')) + emit_form_ref(i, 'AuxiliaryObjectForm', defn.get('auxiliaryObjectForm')) + emit_form_ref(i, 'AuxiliaryListForm', defn.get('auxiliaryListForm')) + emit_form_ref(i, 'AuxiliaryChoiceForm', defn.get('auxiliaryChoiceForm')) emit_standard_attributes(i, 'ExchangePlan') + emit_characteristics(i, defn.get('characteristics')) + emit_based_on(i, defn.get('basedOn')) distributed = 'true' if defn.get('distributedInfoBase') is True else 'false' include_ext = 'true' if defn.get('includeConfigurationExtensions') is True else 'false' X(f'{i}{distributed}') X(f'{i}{include_ext}') - X(f'{i}') - quick_choice = 'true' if defn.get('quickChoice') is True else 'false' - X(f'{i}{quick_choice}') - X(f'{i}BothWays') - X(f'{i}') - X(f'{i}\tExchangePlan.{obj_name}.StandardAttribute.Description') - X(f'{i}\tExchangePlan.{obj_name}.StandardAttribute.Code') - X(f'{i}') - X(f'{i}Begin') - X(f'{i}DontUse') - X(f'{i}Directly') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}false') - X(f'{i}') - data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic') - X(f'{i}{data_lock_control_mode}') - full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use') - X(f'{i}{full_text_search}') + X(f'{i}{get_enum_prop("CreateOnInput", "createOnInput", "DontUse")}') + X(f'{i}{get_enum_prop("ChoiceHistoryOnInput", "choiceHistoryOnInput", "Auto")}') + X(f'{i}{"true" if get_bool_prop("includeHelpInContents", False) else "false"}') + dl_fields = [expand_data_path(str(x)) for x in defn.get('dataLockFields', [])] if 'dataLockFields' in defn else [] + emit_field_block(i, 'DataLockFields', dl_fields) + X(f'{i}{get_enum_prop("DataLockControlMode", "dataLockControlMode", "Managed")}') + X(f'{i}{get_enum_prop("FullTextSearch", "fullTextSearch", "Use")}') emit_mltext(i, 'ObjectPresentation', defn.get('objectPresentation')) emit_mltext(i, 'ExtendedObjectPresentation', defn.get('extendedObjectPresentation')) emit_mltext(i, 'ListPresentation', defn.get('listPresentation')) emit_mltext(i, 'ExtendedListPresentation', defn.get('extendedListPresentation')) emit_mltext(i, 'Explanation', defn.get('explanation')) - X(f'{i}DontUse') - X(f'{i}Auto') - X(f'{i}DontUse') - X(f'{i}false') - X(f'{i}false') + X(f'{i}{get_enum_prop("DataHistory", "dataHistory", "DontUse")}') + X(f'{i}{"true" if get_bool_prop("updateDataHistoryImmediatelyAfterWrite", False) else "false"}') + X(f'{i}{"true" if get_bool_prop("executeAfterWriteDataHistoryVersionProcessing", False) else "false"}') def emit_chart_of_characteristic_types_properties(indent): i = indent diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 5a19814d..9298b182 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 @@ -1,7 +1,7 @@ -# meta-decompile v0.22 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.23 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # -# Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только +# Поддержаны: Catalog, ExchangePlan. Инверс meta-compile (omit-on-default: ключ эмитим только # когда значение в XML отличается от умолчания компилятора). Неподдерживаемый тип / не-MetaDataObject # root → exit 3 (ring3, как form-decompile). param( @@ -91,8 +91,8 @@ foreach ($c in $rootEl.ChildNodes) { if ($c.NodeType -eq 'Element') { $objNode = if (-not $objNode) { [Console]::Error.WriteLine("meta-decompile: пустой MetaDataObject"); exit 3 } $objType = $objNode.LocalName -if ($objType -ne 'Catalog') { - [Console]::Error.WriteLine("meta-decompile: тип '$objType' пока не поддержан (пилот — только Catalog)"); exit 3 +if ($objType -notin @('Catalog', 'ExchangePlan')) { + [Console]::Error.WriteLine("meta-decompile: тип '$objType' пока не поддержан (Catalog, ExchangePlan)"); exit 3 } $props = $objNode.SelectSingleNode('md:Properties', $nsm) @@ -261,8 +261,12 @@ function Attr-ToDsl { $ml = $ap.SelectSingleNode('md:MultiLine', $nsm); if ($ml -and $ml.InnerText -eq 'true') { $flags += 'multiline' } # Синоним/подсказка (строка ru-only ИЛИ {ru,en}). - $synVal = Get-MLValue ($ap.SelectSingleNode('md:Synonym', $nsm)) + $synNode = $ap.SelectSingleNode('md:Synonym', $nsm) + $synVal = Get-MLValue $synNode $synCustom = $false + # Пустой (узел есть, значения нет) ≠ авто-синоним из имени → явный пустой (synonym:""). + # У Catalog-реквизитов не встречается (0/4018), у части ExchangePlan — да. + $synEmpty = ($synNode -and $null -eq $synVal) if ($synVal -is [string]) { if ($synVal -ne (Split-CamelWords $nm)) { $synCustom = $true } } elseif ($null -ne $synVal) { $synCustom = $true } # {ru,en} = всегда кастом $ttVal = Get-MLValue ($ap.SelectSingleNode('md:ToolTip', $nsm)) @@ -343,10 +347,11 @@ function Attr-ToDsl { $cplArr = Parse-ChoiceParameterLinks $ap 'md:ChoiceParameterLinks'; if ($null -ne $cplArr) { $extra['choiceParameterLinks'] = $cplArr } $cpArr = Parse-ChoiceParameters $ap 'md:ChoiceParameters'; if ($null -ne $cpArr) { $extra['choiceParameters'] = $cpArr } - if ($synCustom -or ($null -ne $ttVal) -or $extra.Count -gt 0) { + if ($synCustom -or $synEmpty -or ($null -ne $ttVal) -or $extra.Count -gt 0) { $o = [ordered]@{ name = $nm } if ($ts) { $o['type'] = $ts } if ($synCustom) { $o['synonym'] = $synVal } + elseif ($synEmpty) { $o['synonym'] = '' } if ($null -ne $ttVal) { $o['tooltip'] = $ttVal } foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] } if ($flags.Count -gt 0) { $o['flags'] = [System.Collections.ArrayList]@($flags) } @@ -358,7 +363,7 @@ function Attr-ToDsl { } # === Сборка DSL === -$dsl = [ordered]@{ type = 'Catalog'; name = $objName } +$dsl = [ordered]@{ type = $objType; name = $objName } # Синоним объекта: строка ru-only ИЛИ {ru,en} (мультиязычно). Кастом → эмитим. $synVal = Get-MLValue ($props.SelectSingleNode('md:Synonym', $nsm)) @@ -383,8 +388,12 @@ if ($ownersNode) { if ($items.Count -gt 0) { $dsl['owners'] = [System.Collections.ArrayList]@($items | ForEach-Object { if ($_ -match '^Catalog\.') { ($_ -split '\.', 2)[1] } else { $_ } }) } } Add-EnumProp 'subordinationUse' 'SubordinationUse' 'ToItems' +# Тип-зависимые дефолты (компилятор задаёт их по типу — декомпилятор обязан зеркалить, иначе omit ≠ значению). +$descrLenDef = if ($objType -eq 'ExchangePlan') { 150 } else { 25 } +$createInpDef = if ($objType -eq 'ExchangePlan') { 'DontUse' } else { 'Use' } +$dataLockDef = if ($objType -eq 'ExchangePlan') { 'Managed' } else { 'Automatic' } Add-IntProp 'codeLength' 'CodeLength' 9 -Add-IntProp 'descriptionLength' 'DescriptionLength' 25 +Add-IntProp 'descriptionLength' 'DescriptionLength' $descrLenDef Add-EnumProp 'codeType' 'CodeType' 'String' Add-EnumProp 'codeAllowedLength' 'CodeAllowedLength' 'Variable' Add-BoolProp 'autonumbering' 'Autonumbering' $true @@ -393,15 +402,23 @@ Add-EnumProp 'codeSeries' 'CodeSeries' 'WholeCatalog' Add-EnumProp 'defaultPresentation' 'DefaultPresentation' 'AsDescription' Add-BoolProp 'quickChoice' 'QuickChoice' $false Add-EnumProp 'choiceMode' 'ChoiceMode' 'BothWays' -Add-EnumProp 'dataLockControlMode' 'DataLockControlMode' 'Automatic' +Add-EnumProp 'dataLockControlMode' 'DataLockControlMode' $dataLockDef Add-EnumProp 'fullTextSearch' 'FullTextSearch' 'Use' Add-BoolProp 'useStandardCommands' 'UseStandardCommands' $true -Add-EnumProp 'createOnInput' 'CreateOnInput' 'Use' +Add-EnumProp 'createOnInput' 'CreateOnInput' $createInpDef Add-EnumProp 'editType' 'EditType' 'InDialog' Add-BoolProp 'includeHelpInContents' 'IncludeHelpInContents' $false Add-EnumProp 'choiceHistoryOnInput' 'ChoiceHistoryOnInput' 'Auto' Add-EnumProp 'predefinedDataUpdate' 'PredefinedDataUpdate' 'Auto' Add-EnumProp 'searchStringModeOnInputByString' 'SearchStringModeOnInputByString' 'Begin' +# ExchangePlan-специфичные свойства (у Catalog этих тегов нет → блок не трогает его). +if ($objType -eq 'ExchangePlan') { + Add-BoolProp 'distributedInfoBase' 'DistributedInfoBase' $false + Add-BoolProp 'includeConfigurationExtensions' 'IncludeConfigurationExtensions' $false + Add-EnumProp 'dataHistory' 'DataHistory' 'DontUse' + Add-BoolProp 'updateDataHistoryImmediatelyAfterWrite' 'UpdateDataHistoryImmediatelyAfterWrite' $false + Add-BoolProp 'executeAfterWriteDataHistoryVersionProcessing' 'ExecuteAfterWriteDataHistoryVersionProcessing' $false +} # Короткая форма поля: ..StandardAttribute.X / .Attribute.X → StandardAttribute.X / Attribute.X # (Expand-DataPath компилятора разворачивает частичную форму обратно — dogfood резолвера). @@ -504,14 +521,32 @@ if ($charsNode) { } } -# --- 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' } +# --- StandardAttributes: захватываем ОТКЛОНЕНИЯ от профиля материализованного блока (профиль компилятор +# восстановит сам). Профиль зеркалит stdAttrProfile компилятора — по типу объекта. +# Catalog: Owner{FC=ShowError,FFV=true}, Parent{FFV=true}, Description{FC=ShowError}. +# ExchangePlan: Description{FC=ShowError}, Code{FC=ShowError} (блок всегда материализован). --- +$stdProfileByType = @{ + 'Catalog' = @{ + 'Owner' = @{ fillChecking = 'ShowError'; fillFromFillingValue = $true } + 'Parent' = @{ fillFromFillingValue = $true } + 'Description' = @{ fillChecking = 'ShowError' } + } + 'ExchangePlan' = @{ + 'Description' = @{ fillChecking = 'ShowError' } + 'Code' = @{ fillChecking = 'ShowError' } + } } +$catStdProfile = if ($stdProfileByType.ContainsKey($objType)) { $stdProfileByType[$objType] } else { @{} } +# Фикс-список стандартных реквизитов типа (зеркало standardAttributesByType компилятора) — чтобы отличать +# доп./опциональные (напр. ExchangeDate у ПланОбмена), которые эмитим по факту присутствия даже all-default. +$stdFixedByType = @{ + 'Catalog' = @('PredefinedDataName','Predefined','Ref','DeletionMark','IsFolder','Owner','Parent','Description','Code') + 'ExchangePlan' = @('Ref','DeletionMark','Code','Description','ThisNode','SentNo','ReceivedNo') +} +$stdFixed = if ($stdFixedByType.ContainsKey($objType)) { $stdFixedByType[$objType] } else { @() } +# Условные типы: блок эмитим-как-триггер даже пустым (материализуется при отклонении ≥1 реквизита от schema-default; +# у ExchangePlan это почти всегда — Description/Code=ShowError; редкий all-default EP блок опускает). +$stdConditionalTypes = @('Catalog', 'ExchangePlan') $saNode = $props.SelectSingleNode('md:StandardAttributes', $nsm) if ($saNode) { $saMap = [ordered]@{} @@ -543,9 +578,11 @@ if ($saNode) { $saCf = $sa.SelectSingleNode('xr:ChoiceForm', $nsm); if ($saCf -and $saCf.InnerText) { $ov['choiceForm'] = $saCf.InnerText } $saCpl = Parse-ChoiceParameterLinks $sa 'xr:ChoiceParameterLinks'; if ($null -ne $saCpl) { $ov['choiceParameterLinks'] = $saCpl } $saCp = Parse-ChoiceParameters $sa 'xr:ChoiceParameters'; if ($null -ne $saCp) { $ov['choiceParameters'] = $saCp } - if ($ov.Count -gt 0) { $saMap[$an] = $ov } + # Доп./опциональный реквизит (не в фикс-списке) — эмитим по присутствию даже без отклонений. + if ($ov.Count -gt 0 -or ($stdFixed -notcontains $an)) { $saMap[$an] = $ov } } - $dsl['standardAttributes'] = $saMap # даже пустой = блок есть (чистый профиль) + # Условный тип (Catalog): пустой $saMap = триггер блока. Не-условный (ExchangePlan): блок и так эмитится → пустой не пишем. + if ($saMap.Count -gt 0 -or ($stdConditionalTypes -contains $objType)) { $dsl['standardAttributes'] = $saMap } } # --- ChildObjects: Attributes + TabularSections --- diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index 70621e80..5a29ad8c 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -594,6 +594,31 @@ omit-on-empty. Поля пишутся частичной формой (`Standar | `attributes` | `[]` | → Attribute в ChildObjects | | `tabularSections` | `{}` | → TabularSection в ChildObjects | +### 7.2a ExchangePlan (План обмена) + +Близок к Catalog (без иерархии/владельцев/кодогенерации), плюс два своих флага. Общий с Catalog слой: `synonym`, +`comment`, `useStandardCommands`, `codeLength`/`codeAllowedLength`/`descriptionLength`, `defaultPresentation`, `editType`, +`quickChoice`, `choiceMode`, `inputByString` (§7.1.5, дефолт [Наименование]+[Код] по длинам), формы (`defaultObjectForm`/ +`defaultListForm`/`defaultChoiceForm`/`auxiliary*`), `standardAttributes` (§7.1.1), `characteristics` (§7.1.4), `basedOn`, +`dataLockFields`, презентации, `choiceHistoryOnInput`, `includeHelpInContents`. + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `distributedInfoBase` | `false` | DistributedInfoBase (РИБ — распределённая ИБ) | +| `includeConfigurationExtensions` | `false` | IncludeConfigurationExtensions | +| `createOnInput` | `DontUse` | CreateOnInput *(отличие от Catalog: там `Use`)* | +| `dataLockControlMode` | `Managed` | DataLockControlMode *(отличие от Catalog: там `Automatic`)* | +| `dataHistory` | `DontUse` | DataHistory | +| `updateDataHistoryImmediatelyAfterWrite` | `false` | UpdateDataHistoryImmediatelyAfterWrite | +| `executeAfterWriteDataHistoryVersionProcessing` | `false` | ExecuteAfterWriteDataHistoryVersionProcessing | +| `descriptionLength` | `150` | DescriptionLength *(отличие от Catalog: там `25`)* | +| `attributes` / `tabularSections` | `[]` / `{}` | → ChildObjects | + +Стандартные реквизиты EP: Ref, DeletionMark, Code, Description, ThisNode, SentNo, ReceivedNo (профиль материализованного +блока: Наименование/Код → FillChecking=ShowError). Блок **условный** (как Catalog): материализуется при кастомизации — +ключ `standardAttributes`. Опциональный легаси-реквизит `ExchangeDate` (часть планов) поддержан как «доп.» член +`standardAttributes` (эмитится по факту наличия ключа, вне фикс-списка). + ### 7.3 Enum | Поле JSON | Умолчание | XML элемент | diff --git a/tests/skills/cases/meta-compile/exchange-plan.json b/tests/skills/cases/meta-compile/exchange-plan.json index c6c27ef2..8783ecce 100644 --- a/tests/skills/cases/meta-compile/exchange-plan.json +++ b/tests/skills/cases/meta-compile/exchange-plan.json @@ -3,9 +3,11 @@ "input": { "type": "ExchangePlan", "name": "ОбменСФилиалами", + "comment": "обмен данными с филиалами", "codeLength": 9, "descriptionLength": 100, "distributedInfoBase": true, + "standardAttributes": { "Description": { "synonym": "Имя узла" }, "Code": { "fillChecking": "ShowError" } }, "attributes": ["ПутьКФайлу: String(500)"], "tabularSections": [ { diff --git a/tests/skills/cases/meta-compile/snapshots/exchange-plan/ExchangePlans/ОбменСФилиалами.xml b/tests/skills/cases/meta-compile/snapshots/exchange-plan/ExchangePlans/ОбменСФилиалами.xml index 142f6198..6939dd84 100644 --- a/tests/skills/cases/meta-compile/snapshots/exchange-plan/ExchangePlans/ОбменСФилиалами.xml +++ b/tests/skills/cases/meta-compile/snapshots/exchange-plan/ExchangePlans/ОбменСФилиалами.xml @@ -32,13 +32,28 @@ Обмен сфилиалами - + обмен данными с филиалами true 9 Variable 100 AsDescription InDialog + false + BothWays + + ExchangePlan.ОбменСФилиалами.StandardAttribute.Description + ExchangePlan.ОбменСФилиалами.StandardAttribute.Code + + Begin + DontUse + Directly + + + + + + @@ -94,7 +109,7 @@ - DontCheck + ShowError false false Auto @@ -120,7 +135,7 @@ - DontCheck + ShowError false false Auto @@ -136,7 +151,12 @@ Use false - + + + ru + Имя узла + + Use @@ -223,35 +243,21 @@ + + true false - - false - BothWays - - ExchangePlan.ОбменСФилиалами.StandardAttribute.Description - ExchangePlan.ОбменСФилиалами.StandardAttribute.Code - - Begin - DontUse - Directly - - - - - - + DontUse + Auto false - Automatic + Managed Use - DontUse - Auto DontUse false false