mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-06 19:50:20 +03:00
feat(form-decompile,form-compile): schema-параметры динамического списка (кластер DynamicList Settings>Parameters)
Захват/эмиссия <Parameter> (DataCompositionSchemaParameter) внутри <Settings xsi:type="DynamicList"> — та же сущность, что параметры СКД, но обёртка <Parameter> + дети dcssch:. Ранее теряли при декомпиляции и не умели эмитить (компилятор обрывал на Field*→MainTable). Корпус acc+erp 8.3.24: 123 формы, 406 параметров. DSL: ключ settings.parameters переиспользует грамматику параметров СКД ОДИН-В-ОДИН (shorthand "Имя [Заголовок]: Тип = Значение @valueList @hidden" + объектная форма; ключи value/type/ availableValues/inputParameters/use/denyIncompleteValues/expression/availableAsField). Новых сущностей не вводим — модель переносит знание skd-параметров напрямую. Контекстные дефолты дин-списка (паттерн «умный дефолт у всегда-эмитируемого тега», для модели невидимы — просто опускает ключ): - useRestriction: эмитим ВСЕГДА, дефолт true (в СКД дефолт false); false → объект useRestriction:false; - title: авто из имени через Title-FromName (camelCase-split с сохранением аббревиатур); явный заголовок — только при отклонении от авто-вывода; - value: пустое всегда xsi:nil, даже при известном типе (в отличие от типизированного пустого в СКД). Канон. порядок детей по корпусу: name, title, valueType, value, useRestriction, expression, availableValue*, valueListAllowed, availableAsField, inputParameters, denyIncompleteValues, use. Декомпилятор PS-only (зеркало в py отложено, как и весь form-decompile); компилятор зеркалён в py. Валидация: round-trip бит-в-бит на ФормаОстатков (субсет) + ПроверкаПользовательскихНастроек (полная грамматика: availableValues/inputParameters/denyIncompleteValues/use). Регресс 35/35 ps+py. Тест-кейс dynamic-list-parameters (+снэпшот). spec обновлён. Версии: form-compile v1.64, form-decompile v0.46.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# form-compile v1.63 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.64 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$JsonPath,
|
||||
@@ -3684,6 +3684,257 @@ function Emit-AttrColumn {
|
||||
X "$indent</Column>"
|
||||
}
|
||||
|
||||
# --- Schema-параметры динамического списка (DataCompositionSchemaParameter) ---
|
||||
# Та же сущность, что параметры СКД (см. skd-compile), но в форме: обёртка <Parameter>
|
||||
# + дети с префиксом dcssch:. DSL переиспользует грамматику параметров СКД (shorthand +
|
||||
# объект). Контекстные дефолты дин-списка (паттерн «умный дефолт у всегда-эмитируемого тега»):
|
||||
# useRestriction — эмитим ВСЕГДА, дефолт TRUE (в СКД дефолт false);
|
||||
# title — авто из имени (Title-FromName), если ключ не задан.
|
||||
# Канон. порядок детей (по корпусу acc/erp 8.3.24): name, title, valueType, value,
|
||||
# useRestriction, expression, availableValue*, valueListAllowed, availableAsField,
|
||||
# inputParameters, denyIncompleteValues, use.
|
||||
|
||||
# Многоязычный текст в DCS-контексте — с xsi:type="v8:LocalStringType" (form-compile
|
||||
# Emit-MLText его НЕ ставит, т.к. формовые <Title> голые; в dcssch:* — обязателен).
|
||||
function Emit-DLMLText {
|
||||
param([string]$tag, $text, [string]$indent)
|
||||
X "$indent<$tag xsi:type=`"v8:LocalStringType`">"
|
||||
Emit-MLItems -val $text -indent "$indent`t"
|
||||
X "$indent</$tag>"
|
||||
}
|
||||
|
||||
function Has-DLProp {
|
||||
param($obj, [string]$name)
|
||||
if ($null -eq $obj) { return $false }
|
||||
if ($obj -is [System.Collections.IDictionary]) { return $obj.Contains($name) }
|
||||
if ($obj.PSObject -and $obj.PSObject.Properties[$name]) { return $true }
|
||||
return $false
|
||||
}
|
||||
|
||||
function Split-DLValueListCsv {
|
||||
param([string]$s)
|
||||
$result = @()
|
||||
if ($null -eq $s) { return ,$result }
|
||||
$items = @(); $buf = New-Object System.Text.StringBuilder; $inQuote = $null
|
||||
for ($i = 0; $i -lt $s.Length; $i++) {
|
||||
$ch = $s[$i]
|
||||
if ($inQuote) { [void]$buf.Append($ch); if ($ch -eq $inQuote) { $inQuote = $null } }
|
||||
elseif ($ch -eq "'" -or $ch -eq '"') { $inQuote = $ch; [void]$buf.Append($ch) }
|
||||
elseif ($ch -eq ',') { $items += $buf.ToString(); [void]$buf.Clear() }
|
||||
else { [void]$buf.Append($ch) }
|
||||
}
|
||||
if ($buf.Length -gt 0) { $items += $buf.ToString() }
|
||||
foreach ($raw in $items) {
|
||||
$t = $raw.Trim()
|
||||
if ($t.Length -ge 2 -and (($t[0] -eq "'" -and $t[-1] -eq "'") -or ($t[0] -eq '"' -and $t[-1] -eq '"'))) { $t = $t.Substring(1, $t.Length - 2) }
|
||||
if ($t -ne "") { $result += $t }
|
||||
}
|
||||
return ,$result
|
||||
}
|
||||
|
||||
# Shorthand: "Имя [Заголовок]: Тип = Значение @valueList @hidden"
|
||||
function Parse-DLParamShorthand {
|
||||
param([string]$s)
|
||||
$result = @{ name = ""; type = ""; value = $null; title = $null }
|
||||
if ($s -match '@valueList') { $result.valueListAllowed = $true; $s = $s -replace '\s*@valueList', '' }
|
||||
if ($s -match '@hidden') { $result.hidden = $true; $s = $s -replace '\s*@hidden', '' }
|
||||
if ($s -match '\[([^\]]*)\]') { $result.title = $Matches[1].Trim(); $s = ($s -replace '\s*\[[^\]]*\]\s*', ' ').Trim() }
|
||||
if ($s -match '^([^:]+):\s*(\S+)(\s*=\s*(.*))?$') {
|
||||
$result.name = $Matches[1].Trim()
|
||||
$result.type = Resolve-TypeStr ($Matches[2].Trim())
|
||||
if ($Matches[4]) {
|
||||
$rhs = $Matches[4].Trim()
|
||||
$items = Split-DLValueListCsv $rhs
|
||||
if ($items.Count -ge 2) { $result.value = $items; $result.valueListAllowed = $true }
|
||||
elseif ($items.Count -eq 1) { $result.value = $items[0] }
|
||||
else { $result.value = $rhs }
|
||||
}
|
||||
} else { $result.name = $s.Trim() }
|
||||
return $result
|
||||
}
|
||||
|
||||
function Test-DLEmptyValue {
|
||||
param($v)
|
||||
if ($null -eq $v) { return $true }
|
||||
$s = "$v".Trim()
|
||||
if ($s -eq "" -or $s -eq "_" -or $s.ToLowerInvariant() -eq "null") { return $true }
|
||||
return $false
|
||||
}
|
||||
|
||||
# Эмиссия <dcssch:value> по типу/значению (xs:* или dcscor:DesignTimeValue для ссылок).
|
||||
function Emit-DLValue {
|
||||
param([string]$type, $val, [string]$indent, [bool]$valueListAllowed = $false)
|
||||
if (Test-DLEmptyValue $val) {
|
||||
# Дин-список: пустое значение платформа ВСЕГДА пишет как xsi:nil, даже при известном
|
||||
# типе (в отличие от типизированного пустого в параметрах отчёта СКД).
|
||||
if ($valueListAllowed) { return }
|
||||
X "$indent<dcssch:value xsi:nil=`"true`"/>"
|
||||
return
|
||||
}
|
||||
$valStr = if ($val -is [bool]) { if ($val) { 'true' } else { 'false' } } else { "$val" }
|
||||
if ($type -match '^(date|dateTime|time)') { X "$indent<dcssch:value xsi:type=`"xs:dateTime`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($type -eq "boolean") { X "$indent<dcssch:value xsi:type=`"xs:boolean`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($type -match '^decimal') { X "$indent<dcssch:value xsi:type=`"xs:decimal`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($type -match '^string') { X "$indent<dcssch:value xsi:type=`"xs:string`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent<dcssch:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
else {
|
||||
if ($valStr -match '^\d{4}-\d{2}-\d{2}T') { X "$indent<dcssch:value xsi:type=`"xs:dateTime`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($valStr -eq "true" -or $valStr -eq "false") { X "$indent<dcssch:value xsi:type=`"xs:boolean`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
elseif ($valStr -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or $valStr -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') { X "$indent<dcssch:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
else { X "$indent<dcssch:value xsi:type=`"xs:string`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||
}
|
||||
}
|
||||
|
||||
# <dcssch:valueType> — обёртка + тело через Emit-SingleType (ref-типы → cfg:, как в форме).
|
||||
function Emit-DLValueType {
|
||||
param($typeStr, [string]$indent)
|
||||
if (-not $typeStr) { return }
|
||||
X "$indent<dcssch:valueType>"
|
||||
$parts = "$typeStr" -split '\s*[|+]\s*'
|
||||
foreach ($part in $parts) { Emit-SingleType -typeStr $part.Trim() -indent "$indent`t" }
|
||||
X "$indent</dcssch:valueType>"
|
||||
}
|
||||
|
||||
function Emit-DLAvailableValue {
|
||||
param($av, [string]$type, [string]$indent)
|
||||
X "$indent<dcssch:availableValue>"
|
||||
$avVal = if (Has-DLProp $av 'value') { $av.value } else { $null }
|
||||
Emit-DLValue -type $type -val $avVal -indent "$indent`t" -valueListAllowed $false
|
||||
$pres = if ($av.presentation) { $av.presentation } elseif ($av.title) { $av.title } else { $null }
|
||||
if ($pres) { Emit-DLMLText -tag "dcssch:presentation" -text $pres -indent "$indent`t" }
|
||||
X "$indent</dcssch:availableValue>"
|
||||
}
|
||||
|
||||
# <dcssch:inputParameters> — ChoiceParameters / ChoiceParameterLinks / простое значение (порт из skd).
|
||||
function Emit-DLInputParameters {
|
||||
param($ip, [string]$indent)
|
||||
if ($null -eq $ip) { return }
|
||||
$items = @($ip)
|
||||
if ($items.Count -eq 0) { return }
|
||||
X "$indent<dcssch:inputParameters>"
|
||||
foreach ($item in $items) {
|
||||
X "$indent`t<dcscor:item>"
|
||||
if ((Has-DLProp $item 'use') -and $null -ne $item.use -and -not $item.use) { X "$indent`t`t<dcscor:use>false</dcscor:use>" }
|
||||
X "$indent`t`t<dcscor:parameter>$(Esc-Xml "$($item.parameter)")</dcscor:parameter>"
|
||||
if (Has-DLProp $item 'choiceParameters') {
|
||||
$cpItems = if ($null -ne $item.choiceParameters) { @($item.choiceParameters) } else { @() }
|
||||
if ($cpItems.Count -eq 0) { X "$indent`t`t<dcscor:value xsi:type=`"dcscor:ChoiceParameters`"/>" }
|
||||
else {
|
||||
X "$indent`t`t<dcscor:value xsi:type=`"dcscor:ChoiceParameters`">"
|
||||
foreach ($cpItem in $cpItems) {
|
||||
X "$indent`t`t`t<dcscor:item>"
|
||||
X "$indent`t`t`t`t<dcscor:choiceParameter>$(Esc-Xml "$($cpItem.name)")</dcscor:choiceParameter>"
|
||||
foreach ($v in @($cpItem.values)) {
|
||||
if ($v -is [bool]) { X "$indent`t`t`t`t<dcscor:value xsi:type=`"xs:boolean`">$(if ($v) { 'true' } else { 'false' })</dcscor:value>" }
|
||||
elseif ($v -is [int] -or $v -is [long] -or $v -is [double] -or $v -is [decimal]) { X "$indent`t`t`t`t<dcscor:value xsi:type=`"xs:decimal`">$v</dcscor:value>" }
|
||||
else { X "$indent`t`t`t`t<dcscor:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml "$v")</dcscor:value>" }
|
||||
}
|
||||
X "$indent`t`t`t</dcscor:item>"
|
||||
}
|
||||
X "$indent`t`t</dcscor:value>"
|
||||
}
|
||||
} elseif (Has-DLProp $item 'choiceParameterLinks') {
|
||||
$cplItems = if ($null -ne $item.choiceParameterLinks) { @($item.choiceParameterLinks) } else { @() }
|
||||
if ($cplItems.Count -eq 0) { X "$indent`t`t<dcscor:value xsi:type=`"dcscor:ChoiceParameterLinks`"/>" }
|
||||
else {
|
||||
X "$indent`t`t<dcscor:value xsi:type=`"dcscor:ChoiceParameterLinks`">"
|
||||
foreach ($cplItem in $cplItems) {
|
||||
X "$indent`t`t`t<dcscor:item>"
|
||||
X "$indent`t`t`t`t<dcscor:choiceParameter>$(Esc-Xml "$($cplItem.name)")</dcscor:choiceParameter>"
|
||||
X "$indent`t`t`t`t<dcscor:value>$(Esc-Xml "$($cplItem.value)")</dcscor:value>"
|
||||
$mode = if ($cplItem.mode) { "$($cplItem.mode)" } else { 'Auto' }
|
||||
X "$indent`t`t`t`t<dcscor:mode xmlns:d8p1=`"http://v8.1c.ru/8.1/data/enterprise`" xsi:type=`"d8p1:LinkedValueChangeMode`">$mode</dcscor:mode>"
|
||||
X "$indent`t`t`t</dcscor:item>"
|
||||
}
|
||||
X "$indent`t`t</dcscor:value>"
|
||||
}
|
||||
} elseif (Has-DLProp $item 'value') {
|
||||
$val = $item.value
|
||||
if ($val -is [bool]) { X "$indent`t`t<dcscor:value xsi:type=`"xs:boolean`">$(if ($val) { 'true' } else { 'false' })</dcscor:value>" }
|
||||
elseif ($val -is [int] -or $val -is [long] -or $val -is [double] -or $val -is [decimal]) { X "$indent`t`t<dcscor:value xsi:type=`"xs:decimal`">$val</dcscor:value>" }
|
||||
elseif ($val -is [hashtable] -or $val -is [System.Collections.IDictionary] -or $val -is [PSCustomObject]) { Emit-DLMLText -tag "dcscor:value" -text $val -indent "$indent`t`t" }
|
||||
else { X "$indent`t`t<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$val")</dcscor:value>" }
|
||||
}
|
||||
X "$indent`t</dcscor:item>"
|
||||
}
|
||||
X "$indent</dcssch:inputParameters>"
|
||||
}
|
||||
|
||||
function Emit-DLParameter {
|
||||
param($p, $parsed, [string]$indent)
|
||||
X "$indent<Parameter>"
|
||||
$ci = "$indent`t"
|
||||
X "$ci<dcssch:name>$(Esc-Xml $parsed.name)</dcssch:name>"
|
||||
# Title: явный override (shorthand [..] / объект title/presentation) или авто из имени.
|
||||
$title = $null
|
||||
if ($parsed.title) { $title = $parsed.title }
|
||||
elseif ($p -isnot [string] -and (Has-DLProp $p 'title') -and $p.title) { $title = $p.title }
|
||||
elseif ($p -isnot [string] -and (Has-DLProp $p 'presentation') -and $p.presentation) { $title = $p.presentation }
|
||||
if ($null -eq $title -or ($title -is [string] -and $title -eq '')) { $title = Title-FromName -name $parsed.name }
|
||||
Emit-DLMLText -tag "dcssch:title" -text $title -indent $ci
|
||||
# valueType
|
||||
if ($parsed.type) { Emit-DLValueType -typeStr $parsed.type -indent $ci }
|
||||
# value (дефолт nil; при valueListAllowed пустое — опускаем)
|
||||
$vla = [bool]$parsed.valueListAllowed
|
||||
$valIsArray = ($parsed.value -is [array]) -or ($parsed.value -is [System.Collections.IList] -and $parsed.value -isnot [string])
|
||||
if ($valIsArray) {
|
||||
foreach ($v in @($parsed.value)) { Emit-DLValue -type $parsed.type -val $v -indent $ci -valueListAllowed $false }
|
||||
} else {
|
||||
Emit-DLValue -type $parsed.type -val $parsed.value -indent $ci -valueListAllowed $vla
|
||||
}
|
||||
# useRestriction — ВСЕГДА; дефолт true; false только при явном useRestriction:false.
|
||||
$ur = $true
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'useRestriction')) { $ur = [bool]$p.useRestriction }
|
||||
X "$ci<dcssch:useRestriction>$(if ($ur) { 'true' } else { 'false' })</dcssch:useRestriction>"
|
||||
# expression
|
||||
$expr = $null
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'expression') -and $p.expression) { $expr = "$($p.expression)" }
|
||||
if ($expr) { X "$ci<dcssch:expression>$(Esc-Xml $expr)</dcssch:expression>" }
|
||||
# availableValues
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'availableValues') -and $p.availableValues) {
|
||||
foreach ($av in @($p.availableValues)) { Emit-DLAvailableValue -av $av -type $parsed.type -indent $ci }
|
||||
}
|
||||
# valueListAllowed
|
||||
if ($vla) { X "$ci<dcssch:valueListAllowed>true</dcssch:valueListAllowed>" }
|
||||
# availableAsField=false (hidden или явный)
|
||||
$aaf = $null
|
||||
if ($parsed.hidden -eq $true) { $aaf = $false }
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'availableAsField')) { $aaf = [bool]$p.availableAsField }
|
||||
if ($aaf -eq $false) { X "$ci<dcssch:availableAsField>false</dcssch:availableAsField>" }
|
||||
# inputParameters
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'inputParameters') -and $p.inputParameters) { Emit-DLInputParameters -ip $p.inputParameters -indent $ci }
|
||||
# denyIncompleteValues
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'denyIncompleteValues') -and $p.denyIncompleteValues -eq $true) { X "$ci<dcssch:denyIncompleteValues>true</dcssch:denyIncompleteValues>" }
|
||||
# use
|
||||
$useVal = $null
|
||||
if ($p -isnot [string] -and (Has-DLProp $p 'use') -and $p.use) { $useVal = "$($p.use)" }
|
||||
if ($useVal) { X "$ci<dcssch:use>$(Esc-Xml $useVal)</dcssch:use>" }
|
||||
X "$indent</Parameter>"
|
||||
}
|
||||
|
||||
function Emit-DLParameters {
|
||||
param($params, [string]$indent)
|
||||
if (-not $params) { return }
|
||||
foreach ($p in @($params)) {
|
||||
if ($p -is [string]) {
|
||||
$parsed = Parse-DLParamShorthand $p
|
||||
} else {
|
||||
$resolvedType = ""
|
||||
if ((Has-DLProp $p 'type') -and $p.type) {
|
||||
if ($p.type -is [array] -or ($p.type -is [System.Collections.IList] -and $p.type -isnot [string])) {
|
||||
$resolvedType = (@($p.type | ForEach-Object { Resolve-TypeStr "$_" })) -join ' | '
|
||||
} else { $resolvedType = Resolve-TypeStr "$($p.type)" }
|
||||
} elseif ((Has-DLProp $p 'valueType') -and $p.valueType) {
|
||||
$resolvedType = Resolve-TypeStr "$($p.valueType)"
|
||||
}
|
||||
$parsed = @{ name = "$($p.name)"; type = $resolvedType; value = $(if (Has-DLProp $p 'value') { $p.value } else { $null }); title = $null }
|
||||
if ((Has-DLProp $p 'valueListAllowed') -and $p.valueListAllowed -eq $true) { $parsed.valueListAllowed = $true }
|
||||
if ((Has-DLProp $p 'hidden') -and $p.hidden -eq $true) { $parsed.hidden = $true }
|
||||
}
|
||||
Emit-DLParameter -p $p -parsed $parsed -indent $indent
|
||||
}
|
||||
}
|
||||
|
||||
function Emit-Attributes {
|
||||
param($attrs, [string]$indent)
|
||||
|
||||
@@ -3807,6 +4058,8 @@ function Emit-Attributes {
|
||||
X "$si</Field>"
|
||||
}
|
||||
}
|
||||
# Schema-параметры дин-списка (DataCompositionSchemaParameter) — после Field*, до MainTable.
|
||||
Emit-DLParameters -params $st.parameters -indent $si
|
||||
if ($st.mainTable) { X "$si<MainTable>$(Normalize-MetaTypeRef "$($st.mainTable)")</MainTable>" }
|
||||
# ListSettings: filter/order/conditionalAppearance (skd-грамматика) + каноничные блок-GUID.
|
||||
# Нет items → контейнеры всё равно эмитятся (blockMeta) = каноничный пустой скелет платформы.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-compile v1.63 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.64 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import copy
|
||||
@@ -3374,6 +3374,286 @@ def emit_attr_column(lines, col, indent):
|
||||
lines.append(f'{indent}</Column>')
|
||||
|
||||
|
||||
# --- Schema-параметры динамического списка (DataCompositionSchemaParameter) ---
|
||||
# Зеркало form-compile.ps1 (Emit-DLParameters). Та же сущность, что параметры СКД, но в
|
||||
# форме: обёртка <Parameter> + дети dcssch:. DSL переиспользует грамматику параметров СКД.
|
||||
# Контекстные дефолты: useRestriction эмитим ВСЕГДА, дефолт true (в СКД false); title — авто
|
||||
# из имени; пустое value — всегда xsi:nil (даже при известном типе). Канон. порядок детей
|
||||
# (по корпусу): name, title, valueType, value, useRestriction, expression, availableValue*,
|
||||
# valueListAllowed, availableAsField, inputParameters, denyIncompleteValues, use.
|
||||
|
||||
def emit_dl_mltext(lines, indent, tag, text):
|
||||
# ML-текст с xsi:type="v8:LocalStringType" (в dcssch:* обязателен; emit_mltext его не ставит).
|
||||
lines.append(f'{indent}<{tag} xsi:type="v8:LocalStringType">')
|
||||
emit_ml_items(lines, f'{indent}\t', text)
|
||||
lines.append(f'{indent}</{tag}>')
|
||||
|
||||
|
||||
def split_dl_valuelist_csv(s):
|
||||
result = []
|
||||
if s is None:
|
||||
return result
|
||||
items = []
|
||||
buf = []
|
||||
in_quote = None
|
||||
for ch in s:
|
||||
if in_quote:
|
||||
buf.append(ch)
|
||||
if ch == in_quote:
|
||||
in_quote = None
|
||||
elif ch in ("'", '"'):
|
||||
in_quote = ch
|
||||
buf.append(ch)
|
||||
elif ch == ',':
|
||||
items.append(''.join(buf)); buf = []
|
||||
else:
|
||||
buf.append(ch)
|
||||
if buf:
|
||||
items.append(''.join(buf))
|
||||
for raw in items:
|
||||
t = raw.strip()
|
||||
if len(t) >= 2 and ((t[0] == "'" and t[-1] == "'") or (t[0] == '"' and t[-1] == '"')):
|
||||
t = t[1:-1]
|
||||
if t != '':
|
||||
result.append(t)
|
||||
return result
|
||||
|
||||
|
||||
def parse_dl_param_shorthand(s):
|
||||
result = {'name': '', 'type': '', 'value': None, 'title': None}
|
||||
if '@valueList' in s:
|
||||
result['valueListAllowed'] = True
|
||||
s = re.sub(r'\s*@valueList', '', s)
|
||||
if '@hidden' in s:
|
||||
result['hidden'] = True
|
||||
s = re.sub(r'\s*@hidden', '', s)
|
||||
m = re.search(r'\[([^\]]*)\]', s)
|
||||
if m:
|
||||
result['title'] = m.group(1).strip()
|
||||
s = re.sub(r'\s*\[[^\]]*\]\s*', ' ', s).strip()
|
||||
m = re.match(r'^([^:]+):\s*(\S+)(\s*=\s*(.*))?$', s)
|
||||
if m:
|
||||
result['name'] = m.group(1).strip()
|
||||
result['type'] = resolve_type_str(m.group(2).strip())
|
||||
if m.group(4):
|
||||
rhs = m.group(4).strip()
|
||||
items = split_dl_valuelist_csv(rhs)
|
||||
if len(items) >= 2:
|
||||
result['value'] = items
|
||||
result['valueListAllowed'] = True
|
||||
elif len(items) == 1:
|
||||
result['value'] = items[0]
|
||||
else:
|
||||
result['value'] = rhs
|
||||
else:
|
||||
result['name'] = s.strip()
|
||||
return result
|
||||
|
||||
|
||||
def is_dl_empty_value(v):
|
||||
if v is None:
|
||||
return True
|
||||
sv = str(v).strip()
|
||||
return sv == '' or sv == '_' or sv.lower() == 'null'
|
||||
|
||||
|
||||
def emit_dl_value(lines, type_str, val, indent, value_list_allowed=False):
|
||||
if is_dl_empty_value(val):
|
||||
# Дин-список: пустое значение платформа ВСЕГДА пишет как xsi:nil (даже при известном типе).
|
||||
if value_list_allowed:
|
||||
return
|
||||
lines.append(f'{indent}<dcssch:value xsi:nil="true"/>')
|
||||
return
|
||||
if isinstance(val, bool):
|
||||
val_str = 'true' if val else 'false'
|
||||
else:
|
||||
val_str = str(val)
|
||||
t = type_str or ''
|
||||
if re.match(r'^(date|dateTime|time)', t):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:dateTime">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif t == 'boolean':
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:boolean">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif re.match(r'^decimal', t):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:decimal">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif re.match(r'^string', t):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:string">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.', t):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="dcscor:DesignTimeValue">{esc_xml(val_str)}</dcssch:value>')
|
||||
else:
|
||||
if re.match(r'^\d{4}-\d{2}-\d{2}T', val_str):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:dateTime">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif val_str in ('true', 'false'):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:boolean">{esc_xml(val_str)}</dcssch:value>')
|
||||
elif re.match(r'^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.', val_str) or re.match(r'^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.', val_str):
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="dcscor:DesignTimeValue">{esc_xml(val_str)}</dcssch:value>')
|
||||
else:
|
||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:string">{esc_xml(val_str)}</dcssch:value>')
|
||||
|
||||
|
||||
def emit_dl_value_type(lines, type_str, indent):
|
||||
if not type_str:
|
||||
return
|
||||
lines.append(f'{indent}<dcssch:valueType>')
|
||||
for part in re.split(r'\s*[|+]\s*', str(type_str)):
|
||||
emit_single_type(lines, part.strip(), f'{indent}\t')
|
||||
lines.append(f'{indent}</dcssch:valueType>')
|
||||
|
||||
|
||||
def emit_dl_available_value(lines, av, type_str, indent):
|
||||
lines.append(f'{indent}<dcssch:availableValue>')
|
||||
av_val = av.get('value') if isinstance(av, dict) else None
|
||||
emit_dl_value(lines, type_str, av_val, f'{indent}\t', False)
|
||||
pres = (av.get('presentation') or av.get('title')) if isinstance(av, dict) else None
|
||||
if pres:
|
||||
emit_dl_mltext(lines, f'{indent}\t', 'dcssch:presentation', pres)
|
||||
lines.append(f'{indent}</dcssch:availableValue>')
|
||||
|
||||
|
||||
def emit_dl_input_parameters(lines, ip, indent):
|
||||
if ip is None:
|
||||
return
|
||||
items = ip if isinstance(ip, list) else [ip]
|
||||
if len(items) == 0:
|
||||
return
|
||||
lines.append(f'{indent}<dcssch:inputParameters>')
|
||||
for item in items:
|
||||
lines.append(f'{indent}\t<dcscor:item>')
|
||||
if 'use' in item and item.get('use') is not None and not item.get('use'):
|
||||
lines.append(f'{indent}\t\t<dcscor:use>false</dcscor:use>')
|
||||
lines.append(f'{indent}\t\t<dcscor:parameter>{esc_xml(str(item.get("parameter", "")))}</dcscor:parameter>')
|
||||
if 'choiceParameters' in item:
|
||||
cp_items = item.get('choiceParameters') or []
|
||||
if len(cp_items) == 0:
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:ChoiceParameters"/>')
|
||||
else:
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:ChoiceParameters">')
|
||||
for cp in cp_items:
|
||||
lines.append(f'{indent}\t\t\t<dcscor:item>')
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:choiceParameter>{esc_xml(str(cp.get("name", "")))}</dcscor:choiceParameter>')
|
||||
for v in (cp.get('values') or []):
|
||||
if isinstance(v, bool):
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:value xsi:type="xs:boolean">{"true" if v else "false"}</dcscor:value>')
|
||||
elif isinstance(v, (int, float)):
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:value xsi:type="xs:decimal">{v}</dcscor:value>')
|
||||
else:
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:value xsi:type="dcscor:DesignTimeValue">{esc_xml(str(v))}</dcscor:value>')
|
||||
lines.append(f'{indent}\t\t\t</dcscor:item>')
|
||||
lines.append(f'{indent}\t\t</dcscor:value>')
|
||||
elif 'choiceParameterLinks' in item:
|
||||
cpl_items = item.get('choiceParameterLinks') or []
|
||||
if len(cpl_items) == 0:
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:ChoiceParameterLinks"/>')
|
||||
else:
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:ChoiceParameterLinks">')
|
||||
for cpl in cpl_items:
|
||||
lines.append(f'{indent}\t\t\t<dcscor:item>')
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:choiceParameter>{esc_xml(str(cpl.get("name", "")))}</dcscor:choiceParameter>')
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:value>{esc_xml(str(cpl.get("value", "")))}</dcscor:value>')
|
||||
mode = str(cpl.get('mode') or 'Auto')
|
||||
lines.append(f'{indent}\t\t\t\t<dcscor:mode xmlns:d8p1="http://v8.1c.ru/8.1/data/enterprise" xsi:type="d8p1:LinkedValueChangeMode">{mode}</dcscor:mode>')
|
||||
lines.append(f'{indent}\t\t\t</dcscor:item>')
|
||||
lines.append(f'{indent}\t\t</dcscor:value>')
|
||||
elif 'value' in item:
|
||||
val = item.get('value')
|
||||
if isinstance(val, bool):
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:boolean">{"true" if val else "false"}</dcscor:value>')
|
||||
elif isinstance(val, (int, float)):
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:decimal">{val}</dcscor:value>')
|
||||
elif isinstance(val, dict):
|
||||
emit_dl_mltext(lines, f'{indent}\t\t', 'dcscor:value', val)
|
||||
else:
|
||||
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:string">{esc_xml(str(val))}</dcscor:value>')
|
||||
lines.append(f'{indent}\t</dcscor:item>')
|
||||
lines.append(f'{indent}</dcssch:inputParameters>')
|
||||
|
||||
|
||||
def emit_dl_parameter(lines, p, parsed, indent):
|
||||
is_obj = not isinstance(p, str)
|
||||
lines.append(f'{indent}<Parameter>')
|
||||
ci = f'{indent}\t'
|
||||
lines.append(f'{ci}<dcssch:name>{esc_xml(parsed["name"])}</dcssch:name>')
|
||||
# Title: явный override (shorthand [..] / объект title/presentation) или авто из имени.
|
||||
title = None
|
||||
if parsed.get('title'):
|
||||
title = parsed['title']
|
||||
elif is_obj and p.get('title'):
|
||||
title = p['title']
|
||||
elif is_obj and p.get('presentation'):
|
||||
title = p['presentation']
|
||||
if title is None or (isinstance(title, str) and title == ''):
|
||||
title = title_from_name(parsed['name'])
|
||||
emit_dl_mltext(lines, ci, 'dcssch:title', title)
|
||||
# valueType
|
||||
if parsed.get('type'):
|
||||
emit_dl_value_type(lines, parsed['type'], ci)
|
||||
# value (дефолт nil; при valueListAllowed пустое — опускаем)
|
||||
vla = bool(parsed.get('valueListAllowed'))
|
||||
pv = parsed.get('value')
|
||||
if isinstance(pv, list):
|
||||
for v in pv:
|
||||
emit_dl_value(lines, parsed.get('type', ''), v, ci, False)
|
||||
else:
|
||||
emit_dl_value(lines, parsed.get('type', ''), pv, ci, vla)
|
||||
# useRestriction — ВСЕГДА; дефолт true; false только при явном useRestriction:false.
|
||||
ur = True
|
||||
if is_obj and 'useRestriction' in p:
|
||||
ur = bool(p['useRestriction'])
|
||||
lines.append(f'{ci}<dcssch:useRestriction>{"true" if ur else "false"}</dcssch:useRestriction>')
|
||||
# expression
|
||||
expr = str(p['expression']) if (is_obj and p.get('expression')) else None
|
||||
if expr:
|
||||
lines.append(f'{ci}<dcssch:expression>{esc_xml(expr)}</dcssch:expression>')
|
||||
# availableValues
|
||||
if is_obj and p.get('availableValues'):
|
||||
for av in p['availableValues']:
|
||||
emit_dl_available_value(lines, av, parsed.get('type', ''), ci)
|
||||
# valueListAllowed
|
||||
if vla:
|
||||
lines.append(f'{ci}<dcssch:valueListAllowed>true</dcssch:valueListAllowed>')
|
||||
# availableAsField=false (hidden или явный)
|
||||
aaf = None
|
||||
if parsed.get('hidden') is True:
|
||||
aaf = False
|
||||
if is_obj and 'availableAsField' in p:
|
||||
aaf = bool(p['availableAsField'])
|
||||
if aaf is False:
|
||||
lines.append(f'{ci}<dcssch:availableAsField>false</dcssch:availableAsField>')
|
||||
# inputParameters
|
||||
if is_obj and p.get('inputParameters'):
|
||||
emit_dl_input_parameters(lines, p['inputParameters'], ci)
|
||||
# denyIncompleteValues
|
||||
if is_obj and p.get('denyIncompleteValues') is True:
|
||||
lines.append(f'{ci}<dcssch:denyIncompleteValues>true</dcssch:denyIncompleteValues>')
|
||||
# use
|
||||
if is_obj and p.get('use'):
|
||||
lines.append(f'{ci}<dcssch:use>{esc_xml(str(p["use"]))}</dcssch:use>')
|
||||
lines.append(f'{indent}</Parameter>')
|
||||
|
||||
|
||||
def emit_dl_parameters(lines, params, indent):
|
||||
if not params:
|
||||
return
|
||||
for p in params:
|
||||
if isinstance(p, str):
|
||||
parsed = parse_dl_param_shorthand(p)
|
||||
else:
|
||||
resolved_type = ''
|
||||
if p.get('type'):
|
||||
if isinstance(p['type'], list):
|
||||
resolved_type = ' | '.join(resolve_type_str(str(x)) for x in p['type'])
|
||||
else:
|
||||
resolved_type = resolve_type_str(str(p['type']))
|
||||
elif p.get('valueType'):
|
||||
resolved_type = resolve_type_str(str(p['valueType']))
|
||||
parsed = {'name': str(p.get('name', '')), 'type': resolved_type,
|
||||
'value': p.get('value') if 'value' in p else None, 'title': None}
|
||||
if p.get('valueListAllowed') is True:
|
||||
parsed['valueListAllowed'] = True
|
||||
if p.get('hidden') is True:
|
||||
parsed['hidden'] = True
|
||||
emit_dl_parameter(lines, p, parsed, indent)
|
||||
|
||||
|
||||
def emit_attributes(lines, attrs, indent):
|
||||
if not attrs or len(attrs) == 0:
|
||||
return
|
||||
@@ -3482,6 +3762,8 @@ def emit_attributes(lines, attrs, indent):
|
||||
emit_ml_items(lines, f'{si}\t\t', fld['title'])
|
||||
lines.append(f'{si}\t</dcssch:title>')
|
||||
lines.append(f'{si}</Field>')
|
||||
# Schema-параметры дин-списка (DataCompositionSchemaParameter) — после Field*, до MainTable.
|
||||
emit_dl_parameters(lines, s.get('parameters'), si)
|
||||
if s.get('mainTable'):
|
||||
lines.append(f'{si}<MainTable>{normalize_meta_type_ref(str(s["mainTable"]))}</MainTable>')
|
||||
# ListSettings: filter/order/conditionalAppearance (skd-грамматика) + каноничные блок-GUID.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# form-decompile v0.45 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# form-decompile v0.46 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
|
||||
param(
|
||||
@@ -949,6 +949,111 @@ function Decompile-Type {
|
||||
return ($parts -join ' | ')
|
||||
}
|
||||
|
||||
# Schema-параметры динамического списка (<Parameter> под <Settings>) — зеркало эмиссии
|
||||
# form-compile (Emit-DLParameters). Инверсия контекстных дефолтов: useRestriction=true и
|
||||
# title==Title-FromName опускаем (компилятор восстановит). Сущность = DataCompositionSchemaParameter.
|
||||
function Build-DLInputParameters {
|
||||
param($ipNode)
|
||||
$items = New-Object System.Collections.ArrayList
|
||||
foreach ($it in @($ipNode.SelectNodes("dcscor:item", $ns))) {
|
||||
$io = [ordered]@{}
|
||||
$io['parameter'] = Get-Child $it 'parameter'
|
||||
$useN = $it.SelectSingleNode("dcscor:use", $ns)
|
||||
if ($useN -and $useN.InnerText -eq 'false') { $io['use'] = $false }
|
||||
$valN = $it.SelectSingleNode("dcscor:value", $ns)
|
||||
if ($valN) {
|
||||
$vt = $valN.GetAttribute("type", $NS_XSI)
|
||||
if ($vt -match 'ChoiceParameters$') {
|
||||
$cps = New-Object System.Collections.ArrayList
|
||||
foreach ($cpi in @($valN.SelectNodes("dcscor:item", $ns))) {
|
||||
$cpo = [ordered]@{}
|
||||
$cpo['name'] = Get-Child $cpi 'choiceParameter'
|
||||
$vals = New-Object System.Collections.ArrayList
|
||||
foreach ($cv in @($cpi.SelectNodes("dcscor:value", $ns))) {
|
||||
[void]$vals.Add((Convert-TypedValue -raw $cv.InnerText -xsiType ($cv.GetAttribute("type", $NS_XSI))))
|
||||
}
|
||||
$cpo['values'] = @($vals)
|
||||
[void]$cps.Add($cpo)
|
||||
}
|
||||
$io['choiceParameters'] = @($cps)
|
||||
} elseif ($vt -match 'ChoiceParameterLinks$') {
|
||||
$cpls = New-Object System.Collections.ArrayList
|
||||
foreach ($cpi in @($valN.SelectNodes("dcscor:item", $ns))) {
|
||||
$cpo = [ordered]@{}
|
||||
$cpo['name'] = Get-Child $cpi 'choiceParameter'
|
||||
$cpo['value'] = Get-Child $cpi 'value'
|
||||
$md = Get-Child $cpi 'mode'; if ($md -and $md -ne 'Auto') { $cpo['mode'] = $md }
|
||||
[void]$cpls.Add($cpo)
|
||||
}
|
||||
$io['choiceParameterLinks'] = @($cpls)
|
||||
} else {
|
||||
if ($valN.GetAttribute("nil", $NS_XSI) -ne 'true') { $io['value'] = Convert-TypedValue -raw $valN.InnerText -xsiType $vt }
|
||||
}
|
||||
}
|
||||
[void]$items.Add($io)
|
||||
}
|
||||
return @($items)
|
||||
}
|
||||
|
||||
function Build-DLParameter {
|
||||
param($pNode)
|
||||
$name = Get-Child $pNode 'name'
|
||||
$o = [ordered]@{}
|
||||
$o['name'] = $name
|
||||
# title — опускаем, если совпадает с авто-выводом из имени (ru-only)
|
||||
$titleNode = $pNode.SelectSingleNode("dcssch:title", $ns)
|
||||
if ($titleNode) {
|
||||
$t = Get-LangText $titleNode
|
||||
if ($null -ne $t) {
|
||||
$auto = Title-FromName -name $name
|
||||
if (-not (($t -is [string]) -and ($t -eq $auto))) { $o['title'] = $t }
|
||||
}
|
||||
}
|
||||
# valueType
|
||||
$vtNode = $pNode.SelectSingleNode("dcssch:valueType", $ns)
|
||||
$typeVal = $null
|
||||
if ($vtNode) { $typeVal = Decompile-Type $vtNode; if ($typeVal) { $o['type'] = $typeVal } }
|
||||
# value — опускаем nil (дефолт)
|
||||
$vNode = $pNode.SelectSingleNode("dcssch:value", $ns)
|
||||
if ($vNode -and ($vNode.GetAttribute("nil", $NS_XSI) -ne 'true')) {
|
||||
$o['value'] = Convert-TypedValue -raw $vNode.InnerText -xsiType ($vNode.GetAttribute("type", $NS_XSI))
|
||||
}
|
||||
# useRestriction — опускаем true (дефолт), фиксируем false
|
||||
if ((Get-Child $pNode 'useRestriction') -eq 'false') { $o['useRestriction'] = $false }
|
||||
# expression
|
||||
$expr = Get-Child $pNode 'expression'; if ($null -ne $expr -and $expr -ne '') { $o['expression'] = $expr }
|
||||
# availableValues
|
||||
$avNodes = @($pNode.SelectNodes("dcssch:availableValue", $ns))
|
||||
if ($avNodes.Count -gt 0) {
|
||||
$avs = New-Object System.Collections.ArrayList
|
||||
foreach ($avn in $avNodes) {
|
||||
$avo = [ordered]@{}
|
||||
$avv = $avn.SelectSingleNode("dcssch:value", $ns)
|
||||
if ($avv -and ($avv.GetAttribute("nil", $NS_XSI) -ne 'true')) { $avo['value'] = Convert-TypedValue -raw $avv.InnerText -xsiType ($avv.GetAttribute("type", $NS_XSI)) }
|
||||
else { $avo['value'] = $null }
|
||||
$avp = $avn.SelectSingleNode("dcssch:presentation", $ns)
|
||||
if ($avp) { $pres = Get-LangText $avp; if ($null -ne $pres) { $avo['presentation'] = $pres } }
|
||||
[void]$avs.Add($avo)
|
||||
}
|
||||
$o['availableValues'] = @($avs)
|
||||
}
|
||||
# valueListAllowed / availableAsField
|
||||
if ((Get-Child $pNode 'valueListAllowed') -eq 'true') { $o['valueListAllowed'] = $true }
|
||||
if ((Get-Child $pNode 'availableAsField') -eq 'false') { $o['availableAsField'] = $false }
|
||||
# inputParameters
|
||||
$ipNode = $pNode.SelectSingleNode("dcssch:inputParameters", $ns)
|
||||
if ($ipNode) { $ip = Build-DLInputParameters $ipNode; if (@($ip).Count -gt 0) { $o['inputParameters'] = $ip } }
|
||||
# denyIncompleteValues / use
|
||||
if ((Get-Child $pNode 'denyIncompleteValues') -eq 'true') { $o['denyIncompleteValues'] = $true }
|
||||
$use = Get-Child $pNode 'use'; if ($null -ne $use -and $use -ne '') { $o['use'] = $use }
|
||||
|
||||
# Компактизация: {name} → строка "name"; {name, type} → "name: type"; иначе объект.
|
||||
$keys = @($o.Keys)
|
||||
if ($keys.Count -eq 1) { return $name }
|
||||
if ($keys.Count -eq 2 -and $o.Contains('type') -and ($typeVal -is [string])) { return ("{0}: {1}" -f $name, $typeVal) }
|
||||
return $o
|
||||
}
|
||||
|
||||
# --- 4. Element dispatch ---
|
||||
$ELEMENT_KEY = @{
|
||||
'UsualGroup'='group'; 'ColumnGroup'='columnGroup'; 'ButtonGroup'='buttonGroup'; 'InputField'='input'; 'CheckBoxField'='check';
|
||||
@@ -1468,6 +1573,13 @@ if ($attrsNode) {
|
||||
}
|
||||
$so['fields'] = @($fields)
|
||||
}
|
||||
# Schema-параметры дин-списка (прямые <Parameter> под Settings, не в ListSettings)
|
||||
$paramNodes = @($setNode.SelectNodes("lf:Parameter", $ns))
|
||||
if ($paramNodes.Count -gt 0) {
|
||||
$dlPars = New-Object System.Collections.ArrayList
|
||||
foreach ($pn in $paramNodes) { [void]$dlPars.Add((Build-DLParameter $pn)) }
|
||||
$so['parameters'] = @($dlPars)
|
||||
}
|
||||
# ListSettings: пустой скелет (только viewMode+GUID) опускаем — компилятор
|
||||
# регенерит каноничный скелет. Захватываем только контейнеры с реальными
|
||||
# dcsset:item (filter/order/conditionalAppearance) в формат компилятора.
|
||||
|
||||
Reference in New Issue
Block a user