From b93fd0807fb3fb8497bd9ce76df6be74e1d326bc Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Fri, 3 Jul 2026 17:46:31 +0300 Subject: [PATCH] =?UTF-8?q?feat(meta-compile,meta-decompile):=20=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D0=B2=D0=B8=D0=B7=D0=B8=D1=82=D0=B0=20fillValue=20(v1.27/v0.12?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пара FillFromFillingValue+FillValue — единый блок «заполнения» (у top-level реквизитов всегда, у ТЧ нет). Класс-2 фикс: boolean дефолт пустого FillValue false→nil (модальная форма платформы, 3734 мисматча). Дефолт по типу: String→typed-empty, Number→0, всё прочее→nil. DSL-ключ fillValue (интерпретация по типу реквизита): bool/число/строка/дата- литерал + null→nil-override + DTR-путь (полный рус/англ, GUID.GUID, короткая запись по типу: EmptyRef / имя-значения перечисления / предопределённое). Ref-резолвер портирован из form-compile (+ПустаяСсылка/Истина/Ложь, гард составного типа). Декомпилятор: захват FillValue с omit-при-дефолте. remaining=0 на выборке категории Attribute>FillValue (baseline-impact 7222), регресс 39/39 ps1+py. spec v2.7 §4.2, кейс catalog-attr-fillvalue. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../meta-compile/scripts/meta-compile.ps1 | 172 ++++- .../meta-compile/scripts/meta-compile.py | 167 ++++- .../meta-decompile/scripts/meta-decompile.ps1 | 32 +- docs/meta-dsl-spec.md | 30 +- .../meta-compile/catalog-attr-fillvalue.json | 27 + .../Catalogs/ТестЗаполнения.xml | 677 ++++++++++++++++++ .../ТестЗаполнения/Ext/ObjectModule.bsl | 0 .../catalog-attr-fillvalue/Configuration.xml | 252 +++++++ .../Ext/ClientApplicationInterface.xml | 18 + .../Languages/Русский.xml | 16 + .../Catalogs/Контрагенты.xml | 2 +- 11 files changed, 1351 insertions(+), 42 deletions(-) create mode 100644 tests/skills/cases/meta-compile/catalog-attr-fillvalue.json create mode 100644 tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения.xml create mode 100644 tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения/Ext/ObjectModule.bsl create mode 100644 tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Configuration.xml create mode 100644 tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Ext/ClientApplicationInterface.xml create mode 100644 tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Languages/Русский.xml diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 4a93ed31..09a719d4 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.16 — Compile 1C metadata object from JSON +# meta-compile v1.17 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -563,33 +563,153 @@ function Emit-ValueType { X "$indent" } +# --- FillValue (значение заполнения реквизита) --- +# Пара FillFromFillingValue+FillValue — единый блок «заполнения» (недоступен у реквизитов ТЧ). +# Форма пустого FillValue зависит от типа реквизита (то же значение по умолчанию, что и «пустое» +# значение типа): String→typed-empty, Number→0, всё остальное (Boolean/Date/Ref/составной/TypeSet)→nil. +# Реальное значение задаётся ключом `fillValue` (интерпретация по типу реквизита; см. §4.2 spec). + +# Категория типа реквизита для выбора формы FillValue. +function Get-FillTypeCategory { + param([string]$typeStr) + if (-not $typeStr) { return 'String' } # реквизит без типа → неквалифиц. строка + if ($typeStr -match '\+') { return 'Other' } # составной тип → nil-дефолт + $t = Resolve-TypeStr $typeStr + if ($t -match '^Boolean$') { return 'Boolean' } + if ($t -match '^String(\(|$)') { return 'String' } + if ($t -match '^Number(\(|$)') { return 'Number' } + if ($t -match '^(Date|DateTime)$') { return 'Date' } + return 'Other' # ссылки, TypeSet, ValueStorage, … → nil-дефолт +} + +# Прощающий ввод для ссылочных путей DTR: рус/англ корни, ПустаяСсылка/EmptyRef, ЗначениеПеречисления/EnumValue. +$script:fillRefRoots = @{ + 'перечисление'='Enum'; 'справочник'='Catalog'; 'документ'='Document'; + 'плансчетов'='ChartOfAccounts'; 'планвидовхарактеристик'='ChartOfCharacteristicTypes'; + 'планвидоврасчета'='ChartOfCalculationTypes'; 'планвидоврасчёта'='ChartOfCalculationTypes'; + 'планобмена'='ExchangePlan'; 'бизнеспроцесс'='BusinessProcess'; 'задача'='Task'; + 'enum'='Enum'; 'catalog'='Catalog'; 'document'='Document'; 'chartofaccounts'='ChartOfAccounts'; + 'chartofcharacteristictypes'='ChartOfCharacteristicTypes'; 'chartofcalculationtypes'='ChartOfCalculationTypes'; + 'exchangeplan'='ExchangePlan'; 'businessprocess'='BusinessProcess'; 'task'='Task' +} +$script:fillEmptyRefWords = @('emptyref','пустаяссылка') +$script:fillEnumValWords = @('enumvalue','значениеперечисления') +$script:fillBoolTrue = @('true','истина','да') +$script:fillBoolFalse = @('false','ложь','нет') +# XxxRef (тип реквизита) → корень DTR-пути (для разворота короткой записи значения). +$script:fillRefKindRoot = @{ + 'catalogref'='Catalog'; 'documentref'='Document'; 'enumref'='Enum'; + 'chartofaccountsref'='ChartOfAccounts'; 'chartofcharacteristictypesref'='ChartOfCharacteristicTypes'; + 'chartofcalculationtypesref'='ChartOfCalculationTypes'; 'exchangeplanref'='ExchangePlan'; + 'businessprocessref'='BusinessProcess'; 'taskref'='Task' +} + +# Короткая запись значения ссылочного реквизита (без точки): имя разворачиваем по типу реквизита. +# "EmptyRef"/"ПустаяСсылка" → .<Тип>.EmptyRef; для Enum — EnumValue; прочие — предопределённое. +# $null, если развернуть нельзя (тип не одиночный ссылочный). +function Expand-FillShortRef { + param([string]$s, [string]$typeStr) + if (-not $typeStr) { return $null } + if ($typeStr -match '\+') { return $null } # составной тип — короткая форма неоднозначна + $t = Resolve-TypeStr $typeStr + if ($t -notmatch '^(\w+Ref)\.(.+)$') { return $null } + $root = $script:fillRefKindRoot[$Matches[1].ToLower()] + if (-not $root) { return $null } + $typeName = $Matches[2] + if ($script:fillEmptyRefWords -contains $s.ToLower()) { return "$root.$typeName.EmptyRef" } + if ($root -eq 'Enum') { return "Enum.$typeName.EnumValue.$s" } + return "$root.$typeName.$s" +} + +# Строка → нормализованный DTR-путь ("Catalog.X.EmptyRef" / "Enum.X.EnumValue.Y" / GUID.GUID) ЛИБО $null (не ссылка). +function Normalize-FillRef { + param([string]$s) + if ([string]::IsNullOrEmpty($s)) { return $null } + # Raw-ссылка по паре GUID (метаданные.значение) — всегда ссылка. + if ($s -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\.[0-9a-fA-F-]+$') { return $s } + $parts = $s -split '\.' + if ($parts.Count -lt 2) { return $null } + $root = $script:fillRefRoots[$parts[0].ToLower()] + if (-not $root) { return $null } + $typeName = $parts[1] + if ($root -eq 'Enum') { + if ($parts.Count -eq 2) { return $null } # "Enum.X" — не значение + if ($parts.Count -eq 3) { + if ($script:fillEmptyRefWords -contains $parts[2].ToLower()) { return "Enum.$typeName.EmptyRef" } + return "Enum.$typeName.EnumValue.$($parts[2])" + } + $member = $parts[2] + if ($script:fillEnumValWords -contains $member.ToLower()) { $rest = $parts[3..($parts.Count-1)] -join '.' } + else { $rest = $parts[2..($parts.Count-1)] -join '.' } + return "Enum.$typeName.EnumValue.$rest" + } + # Прочие корни: переводим корень, ПустаяСсылка→EmptyRef в хвосте. + $tail = @($parts[1..($parts.Count-1)]) + for ($i = 0; $i -lt $tail.Count; $i++) { + if ($script:fillEmptyRefWords -contains $tail[$i].ToLower()) { $tail[$i] = 'EmptyRef' } + } + return "$root." + ($tail -join '.') +} + +# Строковый spec → @{ XsiType; Text }. Интерпретация по типу реквизита ($typeStr). +function Resolve-FillValueSpec { + param([string]$s, [string]$typeStr) + $cat = Get-FillTypeCategory $typeStr + if ($s -eq '') { return @{ XsiType='xs:string'; Text='' } } + # String-реквизит: значение заполнения — всегда строковый литерал (без ref/date-детекции). + if ($cat -eq 'String') { return @{ XsiType='xs:string'; Text=$s } } + # Булевы слова (для Boolean-реквизита ИЛИ явное истина/ложь). + if ($cat -eq 'Boolean' -or ($script:fillBoolTrue -contains $s.ToLower()) -or ($script:fillBoolFalse -contains $s.ToLower())) { + if ($script:fillBoolTrue -contains $s.ToLower()) { return @{ XsiType='xs:boolean'; Text='true' } } + if ($script:fillBoolFalse -contains $s.ToLower()) { return @{ XsiType='xs:boolean'; Text='false' } } + } + if ($cat -eq 'Number') { return @{ XsiType='xs:decimal'; Text=$s } } + # Дата: явный Date-реквизит ИЛИ ISO-паттерн. "2020-01-01" → добавить время. + if ($cat -eq 'Date' -or $s -match '^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$') { + if ($s -match '^\d{4}-\d{2}-\d{2}$') { $s = "${s}T00:00:00" } + return @{ XsiType='xs:dateTime'; Text=$s } + } + # Полный ссылочный путь DTR (с точкой: "Catalog.X.EmptyRef", "Enum.X.EnumValue.Y", GUID.GUID). + $ref = Normalize-FillRef $s + if ($ref) { return @{ XsiType='xr:DesignTimeRef'; Text=$ref } } + # Короткая запись значения ссылочного реквизита (одно имя — разворачиваем по типу). + $short = Expand-FillShortRef $s $typeStr + if ($short) { return @{ XsiType='xr:DesignTimeRef'; Text=$short } } + # Фолбэк — строковый литерал. + return @{ XsiType='xs:string'; Text=$s } +} + +# Формат числа-значения без привязки к культуре (точка-разделитель). +function Format-FillNum { + param($n) + if ($n -is [double] -or $n -is [decimal]) { return $n.ToString([System.Globalization.CultureInfo]::InvariantCulture) } + return "$n" +} + +# $spec — значение ключа `fillValue` ($null при явном nil-override), $hasSpec — присутствует ли ключ. function Emit-FillValue { - param([string]$indent, [string]$typeStr) - if (-not $typeStr) { - X "$indent" - return + param([string]$indent, [string]$typeStr, $spec, $hasSpec) + $cat = Get-FillTypeCategory $typeStr + + if ($hasSpec -ne $true) { + # Значение не задано — форма по умолчанию для типа. + switch ($cat) { + 'String' { X "$indent"; return } + 'Number' { X "$indent0"; return } + default { X "$indent"; return } + } } - $typeStr = Resolve-TypeStr $typeStr - - if ($typeStr -eq "Boolean") { - X "$indentfalse" - return + if ($null -eq $spec) { X "$indent"; return } # явный nil-override + if ($spec -is [bool]) { + X "$indent$(if ($spec) { 'true' } else { 'false' })"; return } - if ($typeStr -match '^String') { - X "$indent" - return + if ($spec -is [int] -or $spec -is [long] -or $spec -is [double] -or $spec -is [decimal]) { + X "$indent$(Format-FillNum $spec)"; return } - if ($typeStr -match '^Number') { - X "$indent0" - return - } - if ($typeStr -match '^(Date|DateTime)$') { - X "$indent" - return - } - # References and others - X "$indent" + $r = Resolve-FillValueSpec "$spec" $typeStr + if ($r.Text -eq '' -and $r.XsiType -eq 'xs:string') { X "$indent"; return } + X "$indent$(Esc-XmlText $r.Text)" } # --- 5. Attribute shorthand parser --- @@ -620,6 +740,8 @@ function Parse-AttributeShorthand { synonym = "" comment = "" flags = @() + hasFillValue = $false + fillValue = $null } # Split by | for flags @@ -669,6 +791,8 @@ function Parse-AttributeShorthand { format = $val.format editFormat = $val.editFormat mask = if ($val.mask) { "$($val.mask)" } else { "" } + hasFillValue = ($val.PSObject -and $val.PSObject.Properties -and ($val.PSObject.Properties.Name -contains 'fillValue')) + fillValue = $val.fillValue } } @@ -1020,7 +1144,7 @@ function Emit-Attribute { # FillValue — same restriction if ($context -notin @("tabular", "processor", "chart", "register-other")) { - Emit-FillValue "$indent`t`t" $typeStr + Emit-FillValue "$indent`t`t" $typeStr $parsed.fillValue $parsed.hasFillValue } # FillChecking diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 42385adc..6fb93516 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.15 — Compile 1C metadata object from JSON +# meta-compile v1.17 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -587,24 +587,157 @@ def emit_value_type(indent, type_str): emit_type_content(f'{indent}\t', type_str) X(f'{indent}') -def emit_fill_value(indent, type_str): +# --- FillValue (значение заполнения реквизита) --- +# Пара FillFromFillingValue+FillValue — единый блок «заполнения» (недоступен у реквизитов ТЧ). +# Форма пустого FillValue зависит от типа реквизита (то же значение по умолчанию, что и «пустое» +# значение типа): String→typed-empty, Number→0, всё остальное (Boolean/Date/Ref/составной/TypeSet)→nil. +# Реальное значение задаётся ключом `fillValue` (интерпретация по типу реквизита; см. §4.2 spec). + +def get_fill_type_category(type_str): if not type_str: - X(f'{indent}') + return 'String' # реквизит без типа → неквалифиц. строка + if '+' in type_str: + return 'Other' # составной тип → nil-дефолт + t = resolve_type_str(type_str) + if re.match(r'^Boolean$', t): + return 'Boolean' + if re.match(r'^String(\(|$)', t): + return 'String' + if re.match(r'^Number(\(|$)', t): + return 'Number' + if re.match(r'^(Date|DateTime)$', t): + return 'Date' + return 'Other' # ссылки, TypeSet, ValueStorage, … → nil-дефолт + +# Прощающий ввод для ссылочных путей DTR: рус/англ корни, ПустаяСсылка/EmptyRef, ЗначениеПеречисления/EnumValue. +fill_ref_roots = { + 'перечисление': 'Enum', 'справочник': 'Catalog', 'документ': 'Document', + 'плансчетов': 'ChartOfAccounts', 'планвидовхарактеристик': 'ChartOfCharacteristicTypes', + 'планвидоврасчета': 'ChartOfCalculationTypes', 'планвидоврасчёта': 'ChartOfCalculationTypes', + 'планобмена': 'ExchangePlan', 'бизнеспроцесс': 'BusinessProcess', 'задача': 'Task', + 'enum': 'Enum', 'catalog': 'Catalog', 'document': 'Document', 'chartofaccounts': 'ChartOfAccounts', + 'chartofcharacteristictypes': 'ChartOfCharacteristicTypes', 'chartofcalculationtypes': 'ChartOfCalculationTypes', + 'exchangeplan': 'ExchangePlan', 'businessprocess': 'BusinessProcess', 'task': 'Task', +} +fill_empty_ref_words = ('emptyref', 'пустаяссылка') +fill_enum_val_words = ('enumvalue', 'значениеперечисления') +fill_bool_true = ('true', 'истина', 'да') +fill_bool_false = ('false', 'ложь', 'нет') +# XxxRef (тип реквизита) → корень DTR-пути (для разворота короткой записи значения). +fill_ref_kind_root = { + 'catalogref': 'Catalog', 'documentref': 'Document', 'enumref': 'Enum', + 'chartofaccountsref': 'ChartOfAccounts', 'chartofcharacteristictypesref': 'ChartOfCharacteristicTypes', + 'chartofcalculationtypesref': 'ChartOfCalculationTypes', 'exchangeplanref': 'ExchangePlan', + 'businessprocessref': 'BusinessProcess', 'taskref': 'Task', +} + +def normalize_fill_ref(s): + """Строка → нормализованный DTR-путь ЛИБО None (не ссылка).""" + if not s: + return None + if re.match(r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\.[0-9a-fA-F-]+$', s): + return s + parts = s.split('.') + if len(parts) < 2: + return None + root = fill_ref_roots.get(parts[0].lower()) + if not root: + return None + type_name = parts[1] + if root == 'Enum': + if len(parts) == 2: + return None + if len(parts) == 3: + if parts[2].lower() in fill_empty_ref_words: + return f'Enum.{type_name}.EmptyRef' + return f'Enum.{type_name}.EnumValue.{parts[2]}' + if parts[2].lower() in fill_enum_val_words: + rest = '.'.join(parts[3:]) + else: + rest = '.'.join(parts[2:]) + return f'Enum.{type_name}.EnumValue.{rest}' + tail = list(parts[1:]) + for i in range(len(tail)): + if tail[i].lower() in fill_empty_ref_words: + tail[i] = 'EmptyRef' + return f'{root}.' + '.'.join(tail) + +def expand_fill_short_ref(s, type_str): + """Короткая запись значения ссылочного реквизита (без точки) → полный DTR-путь по типу, либо None.""" + if not type_str: + return None + if '+' in type_str: # составной тип — короткая форма неоднозначна + return None + t = resolve_type_str(type_str) + m = re.match(r'^(\w+Ref)\.(.+)$', t) + if not m: + return None + root = fill_ref_kind_root.get(m.group(1).lower()) + if not root: + return None + type_name = m.group(2) + if s.lower() in fill_empty_ref_words: + return f'{root}.{type_name}.EmptyRef' + if root == 'Enum': + return f'Enum.{type_name}.EnumValue.{s}' + return f'{root}.{type_name}.{s}' + +def resolve_fill_value_spec(s, type_str): + """Строковый spec → (xsi_type, text). Интерпретация по типу реквизита.""" + cat = get_fill_type_category(type_str) + if s == '': + return ('xs:string', '') + if cat == 'String': + return ('xs:string', s) + if cat == 'Boolean' or s.lower() in fill_bool_true or s.lower() in fill_bool_false: + if s.lower() in fill_bool_true: + return ('xs:boolean', 'true') + if s.lower() in fill_bool_false: + return ('xs:boolean', 'false') + if cat == 'Number': + return ('xs:decimal', s) + if cat == 'Date' or re.match(r'^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$', s): + if re.match(r'^\d{4}-\d{2}-\d{2}$', s): + s = f'{s}T00:00:00' + return ('xs:dateTime', s) + ref = normalize_fill_ref(s) + if ref: + return ('xr:DesignTimeRef', ref) + short = expand_fill_short_ref(s, type_str) + if short: + return ('xr:DesignTimeRef', short) + return ('xs:string', s) + +def format_fill_num(n): + if isinstance(n, bool): + return 'true' if n else 'false' + return str(n) + +def emit_fill_value(indent, type_str, spec, has_spec): + """spec — значение ключа fillValue (None при явном nil-override), has_spec — присутствует ли ключ.""" + cat = get_fill_type_category(type_str) + if not has_spec: + if cat == 'String': + X(f'{indent}') + elif cat == 'Number': + X(f'{indent}0') + else: + X(f'{indent}') return - type_str = resolve_type_str(type_str) - if type_str == 'Boolean': - X(f'{indent}false') + if spec is None: + X(f'{indent}') # явный nil-override return - if re.match(r'^String', type_str): + if isinstance(spec, bool): + X(f'{indent}{"true" if spec else "false"}') + return + if isinstance(spec, (int, float)): + X(f'{indent}{format_fill_num(spec)}') + return + xsi_type, text = resolve_fill_value_spec(str(spec), type_str) + if text == '' and xsi_type == 'xs:string': X(f'{indent}') return - if re.match(r'^Number', type_str): - X(f'{indent}0') - return - if re.match(r'^(Date|DateTime)$', type_str): - X(f'{indent}') - return - X(f'{indent}') + X(f'{indent}{esc_xml_text(text)}') # --------------------------------------------------------------------------- # 5. Attribute shorthand parser @@ -631,6 +764,8 @@ def parse_attribute_shorthand(val): 'flags': [], 'fillChecking': '', 'indexing': '', + 'hasFillValue': False, + 'fillValue': None, } parts = val.split('|', 1) main_part = parts[0].strip() @@ -677,6 +812,8 @@ def parse_attribute_shorthand(val): 'format': val.get('format'), 'editFormat': val.get('editFormat'), 'mask': str(val['mask']) if val.get('mask') else '', + 'hasFillValue': ('fillValue' in val), + 'fillValue': val.get('fillValue'), } def parse_enum_value_shorthand(val): @@ -1023,7 +1160,7 @@ def emit_attribute(indent, parsed, context): ffv = 'true' if parsed.get('fillFromFillingValue') is True 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) + emit_fill_value(f'{indent}\t\t', type_str, parsed.get('fillValue'), parsed.get('hasFillValue')) fill_checking = 'DontCheck' if 'req' in parsed.get('flags', []): fill_checking = 'ShowError' diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 0d4d6bb2..bcb86135 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.11 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.12 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Пилот: только Catalog. Инверс meta-compile (omit-on-default: ключ эмитим только @@ -221,6 +221,36 @@ function Attr-ToDsl { $fmtV = Get-MLValue ($ap.SelectSingleNode('md:Format', $nsm)); if ($null -ne $fmtV) { $extra['format'] = $fmtV } $efmtV = Get-MLValue ($ap.SelectSingleNode('md:EditFormat', $nsm)); if ($null -ne $efmtV) { $extra['editFormat'] = $efmtV } + # FillValue (значение заполнения). Форма по умолчанию зависит от типа реквизита: String→typed-empty, + # Number→zero, всё остальное→nil. Эмитим `fillValue` только при отклонении от дефолта (§4.2 spec). + # nil у String/Number → JSON null (nil-override); реальное значение → строка/bool/число/DTR-путь verbatim. + $fvNode = $ap.SelectSingleNode('md:FillValue', $nsm) + if ($fvNode) { + $fcat = 'Other' + if ($ts -match '\+') { $fcat = 'Other' } + elseif ($ts -match '^Boolean') { $fcat = 'Boolean' } + elseif ($ts -match '^String') { $fcat = 'String' } + elseif ($ts -match '^Number') { $fcat = 'Number' } + elseif ($ts -match '^(Date|DateTime)') { $fcat = 'Date' } + $nilAttr = $fvNode.GetAttribute('nil', 'http://www.w3.org/2001/XMLSchema-instance') + $xsiT = $fvNode.GetAttribute('type', 'http://www.w3.org/2001/XMLSchema-instance') + $fvText = $fvNode.InnerText + if ($nilAttr -eq 'true') { + if ($fcat -eq 'String' -or $fcat -eq 'Number') { $extra['fillValue'] = $null } # nil-override + # иначе nil — это дефолт → пропускаем + } elseif ($xsiT -match 'boolean$') { + $extra['fillValue'] = ($fvText -eq 'true') + } elseif ($xsiT -match 'decimal$') { + if (-not ($fcat -eq 'Number' -and ($fvText -eq '0' -or $fvText -eq ''))) { $extra['fillValue'] = $fvText } + } elseif ($xsiT -match 'string$') { + if (-not ($fcat -eq 'String' -and $fvText -eq '')) { $extra['fillValue'] = $fvText } + } elseif ($xsiT -match 'dateTime$') { + $extra['fillValue'] = $fvText + } elseif ($xsiT -match 'DesignTimeRef$') { + $extra['fillValue'] = $fvText + } + } + if ($synCustom -or ($null -ne $ttVal) -or $extra.Count -gt 0) { $o = [ordered]@{ name = $nm } if ($ts) { $o['type'] = $ts } diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index f58158f1..861fdcc7 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -1,6 +1,6 @@ # Meta DSL — спецификация JSON-формата для объектов метаданных 1С -Версия: 2.6 +Версия: 2.7 ## Обзор @@ -146,6 +146,7 @@ JSON DSL для описания объектов метаданных конф | `fillChecking` | FillChecking | DontCheck | DontCheck/ShowError/ShowWarning. Синоним `fillCheck` (из формы; `true`→ShowError). Флаг `req`→ShowError | | `fullTextSearch` | FullTextSearch | Use | Use/DontUse | | `fillFromFillingValue` | FillFromFillingValue | false | bool | +| `fillValue` | FillValue | по типу (см. ниже) | значение заполнения — bool/число/строка/дата/DTR-путь; `null` → nil | | `createOnInput` | CreateOnInput | Auto | Auto/Use/DontUse | | `quickChoice` | QuickChoice | Auto | Auto/Use/DontUse. Прощаем bool (форм-стиль): `true`→Use, `false`→DontUse | | `dataHistory` | DataHistory | Use | Use/DontUse | @@ -157,6 +158,33 @@ JSON DSL для описания объектов метаданных конф | `indexing` | Indexing | DontIndex | флаги `index`/`indexAdditional` | | `multiLine` | MultiLine | false | bool (флаг `multiline`) | +**`fillValue` — значение заполнения реквизита** (``). Пара с `fillFromFillingValue` — единый блок +«заполнения» (недоступен у реквизитов ТЧ; там оба свойства не эмитятся). Форма **пустого** значения зависит от +типа реквизита (то же, что «пустое» значение типа), компилятор выводит её сам — ключ **не задают**: + +| Тип реквизита | Пустое значение (без ключа) | +|---------------|-----------------------------| +| String | `` | +| Number | `0` | +| Boolean, Date, ссылочный, составной, TypeSet | `` | + +Ключ `fillValue` задают только для **реального** значения; интерпретация — по типу реквизита: + +- **Boolean** — `true`/`false` (прощаем `"Истина"`/`"Ложь"`, `"да"`/`"нет"`). +- **Number** — число или строка-число (`1.5`, `"21"`). +- **String** — строковый литерал (без ссылочной/датовой детекции). +- **Date** — ISO-строка `"0001-01-01T00:00:00"` (или `"2020-01-01"` → добавит время). +- **Ссылочный / составной / TypeSet** — DTR-путь: + - полный: `"Catalog.Валюты.EmptyRef"`, `"Enum.Периодичность.EnumValue.Месяц"`, `"Catalog.СтраныМира.Россия"`; + - русские метатипы: `"Справочник.Валюты.ПустаяСсылка"`, `"Перечисление.Периодичность.ЗначениеПеречисления.Год"`; + - GUID-ссылка `"."` (несётся verbatim); + - **короткая запись** по типу реквизита (одно имя, без точки): `"EmptyRef"`/`"ПустаяСсылка"` → пустая ссылка типа; + для `EnumRef` — имя значения (`"Месяц"` → `Enum.<Тип>.EnumValue.Месяц`); для прочих — имя предопределённого + (`"Россия"` → `Catalog.<Тип>.Россия`). Для составного типа короткая запись запрещена (нужен полный путь). +- **`null`** — явный `` (для String/Number, где нужно nil вместо пустого значения типа). + +> `EmptyRef` («пустая ссылка типа X») ≠ `null` («значение не задано»): платформа хранит их различно, оба сохраняются. + Тип можно задать единой строкой (`"type": "String(100)"`) или раздельными полями: ```json diff --git a/tests/skills/cases/meta-compile/catalog-attr-fillvalue.json b/tests/skills/cases/meta-compile/catalog-attr-fillvalue.json new file mode 100644 index 00000000..8234e999 --- /dev/null +++ b/tests/skills/cases/meta-compile/catalog-attr-fillvalue.json @@ -0,0 +1,27 @@ +{ + "name": "Значение заполнения реквизита (fillValue)", + "input": { + "type": "Catalog", + "name": "ТестЗаполнения", + "attributes": [ + "СтрокаДефолт: String(50)", + { "name": "БулевоДефолт", "type": "Boolean" }, + { "name": "БулевоИстина", "type": "Boolean", "fillValue": true }, + { "name": "БулевоЛожь", "type": "Boolean", "fillValue": "Ложь" }, + { "name": "СтрокаNil", "type": "String(10)", "fillValue": null }, + { "name": "СтрокаЗначение", "type": "String(20)", "fillValue": "по умолчанию" }, + { "name": "ЧислоЗначение", "type": "Number(15,2)", "fillValue": "1.5" }, + { "name": "ЧислоNil", "type": "Number(10,0)", "fillValue": null }, + { "name": "ДатаЗначение", "type": "Date", "fillValue": "0001-01-01T00:00:00" }, + { "name": "СсылкаПустаяКратко", "type": "CatalogRef.Валюты", "fillValue": "EmptyRef" }, + { "name": "СсылкаПустаяРус", "type": "CatalogRef.Валюты", "fillValue": "Справочник.Валюты.ПустаяСсылка" }, + { "name": "ПеречислениеКратко", "type": "EnumRef.Периодичность", "fillValue": "Месяц" }, + { "name": "ПеречислениеПолноРус", "type": "EnumRef.Периодичность", "fillValue": "Перечисление.Периодичность.ЗначениеПеречисления.Год" }, + { "name": "ПредопределённоеКратко", "type": "CatalogRef.СтраныМира", "fillValue": "Россия" } + ] + }, + "validatePath": "Catalogs/ТестЗаполнения", + "expect": { + "files": ["Catalogs/ТестЗаполнения.xml"] + } +} diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения.xml new file mode 100644 index 00000000..0f0a9171 --- /dev/null +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения.xml @@ -0,0 +1,677 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + + ТестЗаполнения + + + ru + Тест заполнения + + + + false + HierarchyFoldersAndItems + false + 2 + true + true + + ToItems + 9 + 25 + String + Variable + WholeCatalog + false + true + AsDescription + + Auto + InDialog + false + BothWays + + Catalog.ТестЗаполнения.StandardAttribute.Description + Catalog.ТестЗаполнения.StandardAttribute.Code + + Begin + DontUse + Directly + + + + + + + + + + + false + + + Automatic + Use + + + + + + DontUse + Auto + DontUse + false + false + + + + + СтрокаДефолт + + + ru + Строка дефолт + + + + + xs:string + + 50 + Variable + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + БулевоДефолт + + + ru + Булево дефолт + + + + + xs:boolean + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + БулевоИстина + + + ru + Булево истина + + + + + xs:boolean + + false + + + + false + + false + false + + + false + true + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + БулевоЛожь + + + ru + Булево ложь + + + + + xs:boolean + + false + + + + false + + false + false + + + false + false + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + СтрокаNil + + + ru + Строкаnil + + + + + xs:string + + 10 + Variable + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + СтрокаЗначение + + + ru + Строка значение + + + + + xs:string + + 20 + Variable + + + false + + + + false + + false + false + + + false + по умолчанию + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ЧислоЗначение + + + ru + Число значение + + + + + xs:decimal + + 15 + 2 + Any + + + false + + + + false + + false + false + + + false + 1.5 + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ЧислоNil + + + ru + Числоnil + + + + + xs:decimal + + 10 + 0 + Any + + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ДатаЗначение + + + ru + Дата значение + + + + + xs:dateTime + + Date + + + false + + + + false + + false + false + + + false + 0001-01-01T00:00:00 + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + СсылкаПустаяКратко + + + ru + Ссылка пустая кратко + + + + + d5p1:CatalogRef.Валюты + + false + + + + false + + false + false + + + false + Catalog.Валюты.EmptyRef + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + СсылкаПустаяРус + + + ru + Ссылка пустая рус + + + + + d5p1:CatalogRef.Валюты + + false + + + + false + + false + false + + + false + Catalog.Валюты.EmptyRef + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ПеречислениеКратко + + + ru + Перечисление кратко + + + + + d5p1:EnumRef.Периодичность + + false + + + + false + + false + false + + + false + Enum.Периодичность.EnumValue.Месяц + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ПеречислениеПолноРус + + + ru + Перечисление полно рус + + + + + d5p1:EnumRef.Периодичность + + false + + + + false + + false + false + + + false + Enum.Периодичность.EnumValue.Год + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + ПредопределённоеКратко + + + ru + Предопределённое кратко + + + + + d5p1:CatalogRef.СтраныМира + + false + + + + false + + false + false + + + false + Catalog.СтраныМира.Россия + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения/Ext/ObjectModule.bsl b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Catalogs/ТестЗаполнения/Ext/ObjectModule.bsl new file mode 100644 index 00000000..e69de29b diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Configuration.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Configuration.xml new file mode 100644 index 00000000..77d4e497 --- /dev/null +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Configuration.xml @@ -0,0 +1,252 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + UUID-012 + UUID-013 + + + UUID-014 + UUID-015 + + + + TestConfig + + + ru + TestConfig + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + ТестЗаполнения + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Ext/ClientApplicationInterface.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Ext/ClientApplicationInterface.xml new file mode 100644 index 00000000..3c1161b2 --- /dev/null +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + UUID-002 + + + + + UUID-004 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Languages/Русский.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Languages/Русский.xml new file mode 100644 index 00000000..37c60d78 --- /dev/null +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-fillvalue/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml b/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml index 10ef8346..afcb4722 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml @@ -198,7 +198,7 @@ false - false + DontCheck Items