From 3d0c8f233eadddfaf49d86c7a86711faf275437d Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 6 Jul 2026 18:50:20 +0300 Subject: [PATCH] =?UTF-8?q?feat(meta-compile,meta-decompile):=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D1=82=D0=B8?= =?UTF-8?q?=D0=BF=D0=B0=20InformationRegister=20(=D0=A0=D0=B5=D0=B3=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=8B=20=D1=81=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9)=20(v1.45/v0.37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Седьмой тип, доминанта семейства регистров (2600 объектов). Пилот InformationRegister; Accumulation/Accounting/Calculation — отдельными заходами позже. Итог 150-выборки: 148 match, 4 = known empty-DTR noise (класс A, как Document), 1 = MAX_PATH-артефакт харнеса. 1С-cert ✓. - Рерайт Emit-InformationRegisterProperties на общие хелперы (был легаси-хардкод: UseStandardCommands=true, Comment/презентации/формы пусто, DataHistory хардкод): comment/useStandardCommands/editType/Emit-FormRef/ презентации(RecordPresentation ML)/DataHistory-триплет. - Class-2 дефолты: DataLockControlMode Automatic→Managed (88%); MainFilterOnPeriod расцеплён от periodicity (авто-вывод неверен для ~231 объекта) — теперь явное свойство. - Ресурсы/измерения через богатый Emit-Attribute (context register-info, elemTag Resource/Dimension) вместо легаси Emit-Resource/Emit-Dimension (игнорили comment/tooltip/fullTextSearch/fillValue/choiceParameters). Dimension-специфика Master/MainFilter/DenyIncompleteValues (+захват Attr-ToDsl + проброс Parse-AttributeShorthand). Флаг shorthand master → +FillFromFillingValue=true (конвенция; расцепление обязательно — 203 master+false key-формой). - Команды регистра: register-branch ChildObjects не эмитил Command → +парсинг+Emit-Command. - Class-1 декомпилятор: пустой object-синоним → synonym:"" (аналог EP-фикса; починил и Catalog 52→53). - StandardAttributes: состав всегда 4 (Active/LineNumber/Period/Recorder), present 2465/omitted 135 (5%, не выводимо) — always-emit + opt-out standardAttributes:"" (декомпилятор эмитит при отсутствии блока). Регресс 49/49 ps1+py, ps1==py identical (modulo random GUID). spec §7.5, кейс information-register. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../meta-compile/scripts/meta-compile.ps1 | 103 ++++++++++++------ .../meta-compile/scripts/meta-compile.py | 100 +++++++++++------ .../meta-decompile/scripts/meta-decompile.ps1 | 46 +++++++- docs/meta-dsl-spec.md | 24 +++- .../meta-compile/information-register.json | 1 + .../InformationRegisters/КурсыВалют.xml | 6 +- 6 files changed, 201 insertions(+), 79 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 0f7eb6ea..1f3382c9 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.44 — Compile 1C metadata object from JSON +# meta-compile v1.45 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -839,6 +839,10 @@ function Parse-AttributeShorthand { linkByType = $val.linkByType choiceParameterLinks = $val.choiceParameterLinks choiceParameters = $val.choiceParameters + master = if ($val.master -eq $true) { $true } else { $false } + mainFilter = if ($val.mainFilter -eq $true) { $true } else { $false } + denyIncompleteValues = if ($val.denyIncompleteValues -eq $true) { $true } else { $false } + useInTotals = if ($null -ne $val.useInTotals) { ($val.useInTotals -eq $true) } else { $true } } } @@ -1136,6 +1140,7 @@ function Emit-StandardAttributes { $conditional = $script:stdAttrConditionalTypes -contains $objectType $sa = $def.standardAttributes if ($conditional -and $null -eq $sa) { return } # условный тип без кастомизации → блока нет + if ($sa -is [string] -and $sa -eq '') { return } # opt-out `standardAttributes:""` (дом-конвенция суппресса, ~5% регистров опускают all-default блок — правило не выводимо) $profile = $script:stdAttrProfile[$objectType]; if (-not $profile) { $profile = @{} } # Доп. (опциональные) стандартные реквизиты вне фикс-списка типа — напр. ExchangeDate у части ПланОбмена # (легаси, присутствие не выводится из свойств). Эмитим по факту наличия ключа в DSL, ПЕРЕД фикс-списком (их позиция). @@ -1603,7 +1608,9 @@ function Emit-Attribute { # FillFromFillingValue — not for tabular/processor/chart/register-other # (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these) if ($context -notin @("tabular", "processor", "chart", "register-other")) { - $ffv = if ($parsed.fillFromFillingValue -eq $true) { "true" } else { "false" } + # Флаг-shorthand `master` у ведущего измерения регистра конвенционально ставит и FillFromFillingValue=true + # (эргономика авторинга; декомпилятор пишет key-форму master:true + явный fillFromFillingValue → роундтрип цел). + $ffv = if ($parsed.fillFromFillingValue -eq $true -or ($elemTag -eq "Dimension" -and $parsed.flags -contains "master")) { "true" } else { "false" } X "$indent`t`t$ffv" } @@ -1630,6 +1637,16 @@ function Emit-Attribute { $chi = if ($parsed.choiceHistoryOnInput) { $parsed.choiceHistoryOnInput } else { "Auto" } X "$indent`t`t$chi" + # Измерение регистра сведений: Master/MainFilter/DenyIncompleteValues (между ChoiceHistoryOnInput и Indexing). + if ($elemTag -eq "Dimension" -and $context -eq "register-info") { + $master = if ($parsed.master -eq $true -or $parsed.flags -contains "master") { "true" } else { "false" } + $mainFilter = if ($parsed.mainFilter -eq $true -or $parsed.flags -contains "mainfilter") { "true" } else { "false" } + $denyIncomplete = if ($parsed.denyIncompleteValues -eq $true -or $parsed.flags -contains "denyincomplete") { "true" } else { "false" } + X "$indent`t`t$master" + X "$indent`t`t$mainFilter" + X "$indent`t`t$denyIncomplete" + } + # Use — only for catalog top-level attributes if ($context -eq "catalog") { $use = if ($parsed.use) { $parsed.use } else { "ForItem" } @@ -2231,48 +2248,49 @@ function Emit-InformationRegisterProperties { X "$i$(Esc-Xml $objName)" Emit-MLText $i "Synonym" $synonym - X "$i" - X "$itrue" - X "$iInDialog" - X "$i" - X "$i" - X "$i" - X "$i" + if ($def.comment) { X "$i$(Esc-XmlText "$($def.comment)")" } else { X "$i" } + $useStdCmd = if (Get-BoolProp "useStandardCommands" $true) { "true" } else { "false" } + X "$i$useStdCmd" + X "$i$(Get-EnumProp 'EditType' 'editType' 'InDialog')" + Emit-FormRef $i "DefaultRecordForm" $def.defaultRecordForm + Emit-FormRef $i "DefaultListForm" $def.defaultListForm + Emit-FormRef $i "AuxiliaryRecordForm" $def.auxiliaryRecordForm + Emit-FormRef $i "AuxiliaryListForm" $def.auxiliaryListForm Emit-StandardAttributes $i "InformationRegister" $periodicity = Get-EnumProp "InformationRegisterPeriodicity" "periodicity" "Nonperiodical" $writeMode = Get-EnumProp "WriteMode" "writeMode" "Independent" - # MainFilterOnPeriod: auto based on periodicity unless explicitly set - $mainFilterOnPeriod = "false" - if ($null -ne $def.mainFilterOnPeriod) { - $mainFilterOnPeriod = if ($def.mainFilterOnPeriod -eq $true) { "true" } else { "false" } - } elseif ($periodicity -ne "Nonperiodical") { - $mainFilterOnPeriod = "true" - } + # MainFilterOnPeriod: захватывается независимо (авто-вывод из periodicity неверен — см. корпус). + $mainFilterOnPeriod = if (Get-BoolProp "mainFilterOnPeriod" $false) { "true" } else { "false" } X "$i$periodicity" X "$i$writeMode" X "$i$mainFilterOnPeriod" - X "$ifalse" + $inclHelp = if (Get-BoolProp "includeHelpInContents" $false) { "true" } else { "false" } + X "$i$inclHelp" - $dataLockControlMode = Get-EnumProp "DataLockControlMode" "dataLockControlMode" "Automatic" + $dataLockControlMode = Get-EnumProp "DataLockControlMode" "dataLockControlMode" "Managed" X "$i$dataLockControlMode" $fullTextSearch = Get-EnumProp "FullTextSearch" "fullTextSearch" "Use" X "$i$fullTextSearch" - X "$ifalse" - X "$ifalse" - X "$i" - X "$i" - X "$i" - X "$i" - X "$i" - X "$iDontUse" - X "$ifalse" - X "$ifalse" + $enTotFirst = if (Get-BoolProp "enableTotalsSliceFirst" $false) { "true" } else { "false" } + $enTotLast = if (Get-BoolProp "enableTotalsSliceLast" $false) { "true" } else { "false" } + X "$i$enTotFirst" + X "$i$enTotLast" + Emit-MLText $i "RecordPresentation" $def.recordPresentation + Emit-MLText $i "ExtendedRecordPresentation" $def.extendedRecordPresentation + Emit-MLText $i "ListPresentation" $def.listPresentation + Emit-MLText $i "ExtendedListPresentation" $def.extendedListPresentation + Emit-MLText $i "Explanation" $def.explanation + 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-AccumulationRegisterProperties { @@ -3652,22 +3670,37 @@ if ($objType -in @("InformationRegister","AccumulationRegister","AccountingRegis $regAttrs += Parse-AttributeShorthand $a } } + $regCommands = @() + if ($def.commands) { + if ($def.commands -is [array] -or $def.commands.GetType().Name -eq 'Object[]') { + foreach ($c in $def.commands) { $regCommands += @{ name = "$($c.name)"; def = $c } } + } else { + $def.commands.PSObject.Properties | ForEach-Object { $regCommands += @{ name = $_.Name; def = $_.Value } } + } + } - if ($dims.Count -gt 0 -or $resources.Count -gt 0 -or $regAttrs.Count -gt 0) { + if ($dims.Count -gt 0 -or $resources.Count -gt 0 -or $regAttrs.Count -gt 0 -or $regCommands.Count -gt 0) { $hasChildren = $true X "`t`t" - foreach ($r in $resources) { - Emit-Resource "`t`t`t" $r $objType - } - foreach ($d in $dims) { - Emit-Dimension "`t`t`t" $d $objType - } # InformationRegister.Attribute supports FillFromFillingValue/FillValue/DataHistory; # AccumulationRegister/AccountingRegister/CalculationRegister.Attribute do NOT. $regCtx = if ($objType -eq "InformationRegister") { "register-info" } else { "register-other" } + # InformationRegister: ресурсы/измерения — через богатый Emit-Attribute (общий слой object-свойств). + # Прочие регистры — легаси Emit-Resource/Emit-Dimension (пока не портированы). + foreach ($r in $resources) { + if ($objType -eq "InformationRegister") { Emit-Attribute "`t`t`t" $r "register-info" "Resource" } + else { Emit-Resource "`t`t`t" $r $objType } + } + foreach ($d in $dims) { + if ($objType -eq "InformationRegister") { Emit-Attribute "`t`t`t" $d "register-info" "Dimension" } + else { Emit-Dimension "`t`t`t" $d $objType } + } foreach ($a in $regAttrs) { Emit-Attribute "`t`t`t" $a $regCtx } + foreach ($cmd in $regCommands) { + Emit-Command "`t`t`t" $cmd.name $cmd.def + } X "`t`t" } else { X "`t`t" diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index fdc65cb6..a7643989 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.44 — Compile 1C metadata object from JSON +# meta-compile v1.45 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -866,6 +866,10 @@ def parse_attribute_shorthand(val): 'linkByType': val.get('linkByType'), 'choiceParameterLinks': val.get('choiceParameterLinks'), 'choiceParameters': val.get('choiceParameters'), + 'master': val.get('master') is True, + 'mainFilter': val.get('mainFilter') is True, + 'denyIncompleteValues': val.get('denyIncompleteValues') is True, + 'useInTotals': (val.get('useInTotals') is True) if val.get('useInTotals') is not None else True, } def parse_enum_value_shorthand(val): @@ -1157,6 +1161,8 @@ def emit_standard_attributes(indent, object_type): sa = defn.get('standardAttributes') if conditional and sa is None: return + if isinstance(sa, str) and sa == '': + return # opt-out `standardAttributes:""` (дом-конвенция суппресса, ~5% регистров опускают all-default блок) profile = std_attr_profile.get(object_type, {}) # Доп. (опциональные) стандартные реквизиты вне фикс-списка — напр. ExchangeDate у части ПланОбмена # (легаси, присутствие не выводится). Эмитим по факту ключа в DSL, ПЕРЕД фикс-списком (их позиция). @@ -1676,7 +1682,7 @@ def emit_attribute(indent, parsed, context, elem_tag='Attribute'): # FillFromFillingValue / FillValue — not for tabular/processor/chart/register-other # (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these) if context not in ('tabular', 'processor', 'chart', 'register-other'): - ffv = 'true' if parsed.get('fillFromFillingValue') is True else 'false' + ffv = 'true' if (parsed.get('fillFromFillingValue') is True or (elem_tag == 'Dimension' and 'master' in parsed.get('flags', []))) else 'false' X(f'{indent}\t\t{ffv}') if context not in ('tabular', 'processor', 'chart', 'register-other'): emit_fill_value(f'{indent}\t\t', type_str, parsed.get('fillValue'), parsed.get('hasFillValue')) @@ -1695,6 +1701,14 @@ def emit_attribute(indent, parsed, context, elem_tag='Attribute'): emit_link_by_type(f'{indent}\t\t', parsed.get('linkByType')) chi = parsed.get('choiceHistoryOnInput') or 'Auto' X(f'{indent}\t\t{chi}') + # Измерение регистра сведений: Master/MainFilter/DenyIncompleteValues (между ChoiceHistoryOnInput и Indexing). + if elem_tag == 'Dimension' and context == 'register-info': + master = 'true' if (parsed.get('master') is True or 'master' in parsed.get('flags', [])) else 'false' + main_filter = 'true' if (parsed.get('mainFilter') is True or 'mainfilter' in parsed.get('flags', [])) else 'false' + deny_incomplete = 'true' if (parsed.get('denyIncompleteValues') is True or 'denyincomplete' in parsed.get('flags', [])) else 'false' + X(f'{indent}\t\t{master}') + X(f'{indent}\t\t{main_filter}') + X(f'{indent}\t\t{deny_incomplete}') if context == 'catalog': X(f'{indent}\t\t{parsed.get("use") or "ForItem"}') if context not in ('processor', 'processor-tabular'): @@ -2221,39 +2235,44 @@ def emit_information_register_properties(indent): i = indent X(f'{i}{esc_xml(obj_name)}') emit_mltext(i, 'Synonym', synonym) - X(f'{i}') - X(f'{i}true') - X(f'{i}InDialog') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') + if defn.get('comment'): + X(f'{i}{esc_xml_text(str(defn["comment"]))}') + else: + X(f'{i}') + use_std_cmd = 'true' if get_bool_prop('useStandardCommands', True) else 'false' + X(f'{i}{use_std_cmd}') + X(f'{i}{get_enum_prop("EditType", "editType", "InDialog")}') + emit_form_ref(i, 'DefaultRecordForm', defn.get('defaultRecordForm')) + emit_form_ref(i, 'DefaultListForm', defn.get('defaultListForm')) + emit_form_ref(i, 'AuxiliaryRecordForm', defn.get('auxiliaryRecordForm')) + emit_form_ref(i, 'AuxiliaryListForm', defn.get('auxiliaryListForm')) emit_standard_attributes(i, 'InformationRegister') periodicity = get_enum_prop('InformationRegisterPeriodicity', 'periodicity', 'Nonperiodical') write_mode = get_enum_prop('WriteMode', 'writeMode', 'Independent') - main_filter_on_period = 'false' - if defn.get('mainFilterOnPeriod') is not None: - main_filter_on_period = 'true' if defn['mainFilterOnPeriod'] is True else 'false' - elif periodicity != 'Nonperiodical': - main_filter_on_period = 'true' + main_filter_on_period = 'true' if get_bool_prop('mainFilterOnPeriod', False) else 'false' X(f'{i}{periodicity}') X(f'{i}{write_mode}') X(f'{i}{main_filter_on_period}') - X(f'{i}false') - data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic') + incl_help = 'true' if get_bool_prop('includeHelpInContents', False) else 'false' + X(f'{i}{incl_help}') + data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Managed') 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}false') - X(f'{i}false') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}') - X(f'{i}DontUse') - X(f'{i}false') - X(f'{i}false') + en_tot_first = 'true' if get_bool_prop('enableTotalsSliceFirst', False) else 'false' + en_tot_last = 'true' if get_bool_prop('enableTotalsSliceLast', False) else 'false' + X(f'{i}{en_tot_first}') + X(f'{i}{en_tot_last}') + emit_mltext(i, 'RecordPresentation', defn.get('recordPresentation')) + emit_mltext(i, 'ExtendedRecordPresentation', defn.get('extendedRecordPresentation')) + emit_mltext(i, 'ListPresentation', defn.get('listPresentation')) + emit_mltext(i, 'ExtendedListPresentation', defn.get('extendedListPresentation')) + emit_mltext(i, 'Explanation', defn.get('explanation')) + X(f'{i}{get_enum_prop("DataHistory", "dataHistory", "DontUse")}') + upd_dh = 'true' if get_bool_prop('updateDataHistoryImmediatelyAfterWrite', False) else 'false' + X(f'{i}{upd_dh}') + exec_dh = 'true' if get_bool_prop('executeAfterWriteDataHistoryVersionProcessing', False) else 'false' + X(f'{i}{exec_dh}') def emit_accumulation_register_properties(indent): i = indent @@ -3457,18 +3476,37 @@ if obj_type in ('InformationRegister', 'AccumulationRegister', 'AccountingRegist if defn.get('attributes'): for a in defn['attributes']: reg_attrs.append(parse_attribute_shorthand(a)) - if dims or resources or reg_attrs: + reg_commands = [] + if defn.get('commands'): + cd = defn['commands'] + if isinstance(cd, list): + for c in cd: + reg_commands.append({'name': str(c.get('name', '')), 'def': c}) + else: + for k, v in cd.items(): + reg_commands.append({'name': k, 'def': v}) + if dims or resources or reg_attrs or reg_commands: has_children = True X('\t\t') - for r in resources: - emit_resource('\t\t\t', r, obj_type) - for d in dims: - emit_dimension('\t\t\t', d, obj_type) # InformationRegister.Attribute supports FillFromFillingValue/FillValue/DataHistory; # AccumulationRegister/AccountingRegister/CalculationRegister.Attribute do NOT. reg_ctx = 'register-info' if obj_type == 'InformationRegister' else 'register-other' + # InformationRegister: ресурсы/измерения — через богатый emit_attribute (общий слой object-свойств). + # Прочие регистры — легаси emit_resource/emit_dimension (пока не портированы). + for r in resources: + if obj_type == 'InformationRegister': + emit_attribute('\t\t\t', r, 'register-info', 'Resource') + else: + emit_resource('\t\t\t', r, obj_type) + for d in dims: + if obj_type == 'InformationRegister': + emit_attribute('\t\t\t', d, 'register-info', 'Dimension') + else: + emit_dimension('\t\t\t', d, obj_type) for a in reg_attrs: emit_attribute('\t\t\t', a, reg_ctx) + for cmd in reg_commands: + emit_command('\t\t\t', cmd['name'], cmd['def']) X('\t\t') else: X('\t\t') diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 6c10a445..b1a340fa 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 @@ -1,4 +1,4 @@ -# meta-decompile v0.36 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.37 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document. Инверс meta-compile (omit-on-default: ключ эмитим только @@ -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 -notin @('Catalog', 'ExchangePlan', 'ChartOfCharacteristicTypes', 'ChartOfAccounts', 'ChartOfCalculationTypes', 'Document')) { - [Console]::Error.WriteLine("meta-decompile: тип '$objType' пока не поддержан (Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document)"); exit 3 +if ($objType -notin @('Catalog', 'ExchangePlan', 'ChartOfCharacteristicTypes', 'ChartOfAccounts', 'ChartOfCalculationTypes', 'Document', 'InformationRegister')) { + [Console]::Error.WriteLine("meta-decompile: тип '$objType' пока не поддержан (Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document, InformationRegister)"); exit 3 } $props = $objNode.SelectSingleNode('md:Properties', $nsm) @@ -293,6 +293,10 @@ function Attr-ToDsl { $v = & $en 'MarkNegatives'; if ($v -eq 'true') { $extra['markNegatives'] = $true } $v = & $en 'ChoiceFoldersAndItems'; if ($v -and $v -ne 'Items') { $extra['choiceFoldersAndItems'] = $v } $v = & $en 'ChoiceForm'; if ($v) { $extra['choiceForm'] = $v } + # Регистро-специфика измерения (теги присутствуют только у Dimension → безвредно для прочих). + $v = & $en 'Master'; if ($v -eq 'true') { $extra['master'] = $true } + $v = & $en 'MainFilter'; if ($v -eq 'true') { $extra['mainFilter'] = $true } + $v = & $en 'DenyIncompleteValues'; if ($v -eq 'true') { $extra['denyIncompleteValues'] = $true } # MinValue/MaxValue — граница диапазона (omit при nil). Тип сохраняем: xs:string→строка, xs:decimal→число. foreach ($mm in @('MinValue','MaxValue')) { $mn = $ap.SelectSingleNode("md:$mm", $nsm) @@ -376,9 +380,12 @@ function Attr-ToDsl { $dsl = [ordered]@{ type = $objType; name = $objName } # Синоним объекта: строка ru-only ИЛИ {ru,en} (мультиязычно). Кастом → эмитим. -$synVal = Get-MLValue ($props.SelectSingleNode('md:Synonym', $nsm)) +# Пустой (узел есть, значения нет) ≠ авто-синоним из имени → явный synonym:"" (иначе компилятор до-генерит из имени). +$synNodeObj = $props.SelectSingleNode('md:Synonym', $nsm) +$synVal = Get-MLValue $synNodeObj if ($synVal -is [string]) { if ($synVal -ne (Split-CamelWords $objName)) { $dsl['synonym'] = $synVal } } elseif ($null -ne $synVal) { $dsl['synonym'] = $synVal } +elseif ($synNodeObj) { $dsl['synonym'] = '' } $cmt = P 'Comment'; if ($cmt) { $dsl['comment'] = $cmt } # Свойства Catalog (omit-on-default). Порядок ключей — как удобно DSL. @@ -498,6 +505,17 @@ if ($objType -eq 'Document') { Add-BoolProp 'updateDataHistoryImmediatelyAfterWrite' 'UpdateDataHistoryImmediatelyAfterWrite' $false Add-BoolProp 'executeAfterWriteDataHistoryVersionProcessing' 'ExecuteAfterWriteDataHistoryVersionProcessing' $false } +# InformationRegister-специфичные свойства: периодичность, режим записи, срезы, DataHistory-триплет. +if ($objType -eq 'InformationRegister') { + Add-EnumProp 'periodicity' 'InformationRegisterPeriodicity' 'Nonperiodical' + Add-EnumProp 'writeMode' 'WriteMode' 'Independent' + Add-BoolProp 'mainFilterOnPeriod' 'MainFilterOnPeriod' $false + Add-BoolProp 'enableTotalsSliceFirst' 'EnableTotalsSliceFirst' $false + Add-BoolProp 'enableTotalsSliceLast' 'EnableTotalsSliceLast' $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 резолвера). @@ -545,10 +563,13 @@ Add-FormRef 'auxiliaryFolderForm' 'AuxiliaryFolderForm' Add-FormRef 'auxiliaryListForm' 'AuxiliaryListForm' Add-FormRef 'auxiliaryChoiceForm' 'AuxiliaryChoiceForm' Add-FormRef 'auxiliaryFolderChoiceForm' 'AuxiliaryFolderChoiceForm' +Add-FormRef 'defaultRecordForm' 'DefaultRecordForm' +Add-FormRef 'auxiliaryRecordForm' 'AuxiliaryRecordForm' # Презентации (ML, компилятор пишет пусто → omit-on-empty). foreach ($pp in @( @('ObjectPresentation','objectPresentation'), @('ExtendedObjectPresentation','extendedObjectPresentation'), + @('RecordPresentation','recordPresentation'), @('ExtendedRecordPresentation','extendedRecordPresentation'), @('ListPresentation','listPresentation'), @('ExtendedListPresentation','extendedListPresentation'), @('Explanation','explanation'))) { $pv = Get-MLValue ($props.SelectSingleNode("md:$($pp[0])", $nsm)) @@ -683,6 +704,10 @@ if ($saNode) { } # Условный тип (Catalog): пустой $saMap = триггер блока. Не-условный (ExchangePlan): блок и так эмитится → пустой не пишем. if ($saMap.Count -gt 0 -or ($stdConditionalTypes -contains $objType)) { $dsl['standardAttributes'] = $saMap } +} elseif ($objType -eq 'InformationRegister') { + # Регистр опускает all-default блок стандартных реквизитов (~5%, правило не выводимо) — компилятор эмитит его + # по дефолту, поэтому отсутствие фиксируем opt-out `standardAttributes:""` (дом-конвенция суппресса). + $dsl['standardAttributes'] = '' } # --- ChildObjects: Attributes + TabularSections --- @@ -708,6 +733,19 @@ if ($childObjs) { foreach ($a in $extDimFlagNodes) { [void]$arr.Add((Attr-ToDsl $a)) } $dsl['extDimensionAccountingFlags'] = $arr } + # Регистры: измерения и ресурсы — структурно как реквизит (Attr-ToDsl захватывает общий слой + регистро-специфику). + $dimNodes = @($childObjs.SelectNodes('md:Dimension', $nsm)) + if ($dimNodes.Count -gt 0) { + $arr = [System.Collections.ArrayList]@() + foreach ($a in $dimNodes) { [void]$arr.Add((Attr-ToDsl $a)) } + $dsl['dimensions'] = $arr + } + $resNodes = @($childObjs.SelectNodes('md:Resource', $nsm)) + if ($resNodes.Count -gt 0) { + $arr = [System.Collections.ArrayList]@() + foreach ($a in $resNodes) { [void]$arr.Add((Attr-ToDsl $a)) } + $dsl['resources'] = $arr + } $tsNodes = @($childObjs.SelectNodes('md:TabularSection', $nsm)) if ($tsNodes.Count -gt 0) { $tsMap = [ordered]@{} diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index 19938f50..a50cb7b5 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -809,16 +809,28 @@ Split-CamelCase имени. | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| -| `writeMode` | `Independent` | WriteMode | +| `writeMode` | `Independent` | WriteMode (Independent/RecorderSubordinate) | | `periodicity` | `Nonperiodical` | InformationRegisterPeriodicity | -| `mainFilterOnPeriod` | авто* | MainFilterOnPeriod | -| `dataLockControlMode` | `Automatic` | DataLockControlMode | +| `mainFilterOnPeriod` | `false` | MainFilterOnPeriod (не выводится из `periodicity` — задаётся явно) | +| `dataLockControlMode` | `Managed` | DataLockControlMode | | `fullTextSearch` | `Use` | FullTextSearch | -| `dimensions` | `[]` | → Dimension в ChildObjects | -| `resources` | `[]` | → Resource в ChildObjects | +| `editType` | `InDialog` | EditType | +| `useStandardCommands` | `true` | UseStandardCommands | +| `enableTotalsSliceFirst` / `enableTotalsSliceLast` | `false` | EnableTotalsSlice* (срез первых/последних) | +| `comment` | пусто | Comment | +| `recordPresentation` / `extendedRecordPresentation` / `listPresentation` / `extendedListPresentation` / `explanation` | пусто | презентации (ML) | +| `defaultRecordForm` / `defaultListForm` / `auxiliaryRecordForm` / `auxiliaryListForm` | пусто | *RecordForm/*ListForm (ссылка на форму) | +| `dataHistory` (+ триплет) | `DontUse` | DataHistory / UpdateDataHistoryImmediatelyAfterWrite / ExecuteAfterWriteDataHistoryVersionProcessing | +| `standardAttributes` | (блок всегда) | `""` — opt-out: подавить all-default блок стандартных реквизитов (~5% регистров его опускают, правило не выводимо) | +| `dimensions` | `[]` | → Dimension в ChildObjects (богатый object-слой реквизита + `master`/`mainFilter`/`denyIncompleteValues`) | +| `resources` | `[]` | → Resource в ChildObjects (богатый object-слой реквизита) | | `attributes` | `[]` | → Attribute в ChildObjects | +| `commands` | `[]` | → Command в ChildObjects (см. §7.1.3) | -\* `mainFilterOnPeriod` = `true` если `periodicity` != `Nonperiodical`, иначе `false`. +Измерения/ресурсы РС поддерживают полный object-слой реквизита (synonym/tooltip/comment/type/fillValue/ +choiceParameters/indexing/fullTextSearch/dataHistory/…, см. §3–4). Признаки измерения — флаги shorthand +(`master`/`mainFilter`/`denyIncomplete`) ЛИБО object-ключи (`master`/`mainFilter`/`denyIncompleteValues`: bool). +Флаг `master` дополнительно ставит `FillFromFillingValue=true` (конвенция ведущего измерения). ### 7.6 AccumulationRegister diff --git a/tests/skills/cases/meta-compile/information-register.json b/tests/skills/cases/meta-compile/information-register.json index 374fd1ae..95c733e6 100644 --- a/tests/skills/cases/meta-compile/information-register.json +++ b/tests/skills/cases/meta-compile/information-register.json @@ -4,6 +4,7 @@ "type": "InformationRegister", "name": "КурсыВалют", "periodicity": "Day", + "mainFilterOnPeriod": true, "dimensions": ["Валюта: CatalogRef.Валюты | master, mainFilter, denyIncomplete"], "resources": ["Курс: Number(15,4)", "Кратность: Number(10,0)"] }, diff --git a/tests/skills/cases/meta-compile/snapshots/information-register/InformationRegisters/КурсыВалют.xml b/tests/skills/cases/meta-compile/snapshots/information-register/InformationRegisters/КурсыВалют.xml index a2c1e24d..09900326 100644 --- a/tests/skills/cases/meta-compile/snapshots/information-register/InformationRegisters/КурсыВалют.xml +++ b/tests/skills/cases/meta-compile/snapshots/information-register/InformationRegisters/КурсыВалют.xml @@ -156,7 +156,7 @@ Independent true false - Automatic + Managed Use false false @@ -199,7 +199,7 @@ false - + 0 DontCheck Items @@ -243,7 +243,7 @@ false - + 0 DontCheck Items