feat(form-decompile,form-compile): вычисляемые поля + valueType поля DataSet динсписка (кластер C)

Две части пробела DataSet динамического списка:

1. Field valueType (63 формы/54). Поле набора <Field DataSetFieldField> может нести
   <dcssch:valueType> (тип значения; кастомные/вычисляемые поля). Декомпилятор ловил
   field/dataPath/title/nested, теперь и valueType (переиспользует Decompile-Type);
   компилятор эмитит после title через существующий emit_dl_value_type. 0 потерь на выборке.

2. CalculatedField (6 форм, редкое). Новый ключ settings.calculatedFields — зеркало skd:
   shorthand "Имя [Заголовок]: тип = Выражение #noField #noFilter #noGroup #noOrder"
   (порт Parse-CalcShorthand) или объект. Форм-специфика: dcssch:-теги, presentationExpression,
   orderExpression* (структура {expression,orderType,autoOrder} в namespace dcscommon с
   локальным xmlns), useRestriction{field,condition,group,order}. Эмиттер форм-специфичный
   (skd использует dcscom:-префикс и не имеет pres/orderExpression). Позиция в DataSet —
   после Field*, до Parameter*. Декомпилятор Build-CalcField (объектная форма для точного
   round-trip). Выборка calc-форм: calc-теги ушли из диффов (3/6 match, остаток — отдельный
   field appearance/presentationExpression).

Кейс dynamic-list-form (+grouping +calculatedFields: shorthand с флагами + объект с
presentationExpression/orderExpression/valueType) сертифицирован загрузкой в 1С (порядок
детей CalculatedField подтверждён платформой). Регресс 43/43 (ps1+py).

ps1==py байт-в-байт (сверено на кейсе). Фикс по пути: ps1 Parse-CalcShorthand — `\b` в
generator-heredoc превратился в backspace 0x08 → #-флаги не парсились (py был верен);
поймано прямой сверкой вывода ps1 vs py.

C-остаток (в BACKLOG): свойства обычного <Field> — presentationExpression + appearance
(формат/цвет поля) — отдельный подкластер.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-12 19:38:14 +03:00
co-authored by Claude Opus 4.8
parent 43d36119cb
commit 73b07a6fbc
6 changed files with 292 additions and 4 deletions
@@ -1,4 +1,4 @@
# form-decompile v0.112 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v0.113 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1246,6 +1246,44 @@ function Decompile-Type {
return ($parts -join ' | ')
}
# Вычисляемое поле DataSet динамического списка (<CalculatedField>) → объектная модель.
# Зеркало эмиссии form-compile (Emit-CalcFields). Грамматика — как skd calculatedFields,
# + форм-extras presentationExpression/orderExpression (в namespace dcscommon).
function Build-CalcField {
param($cfNode)
$o = [ordered]@{}
$o['dataPath'] = Get-Child $cfNode 'dataPath'
$o['expression'] = Get-Child $cfNode 'expression'
$tn = $cfNode.SelectSingleNode("dcssch:title", $ns)
if ($tn) { $t = Get-LangText $tn; if ($null -ne $t) { $o['title'] = $t } }
$vt = $cfNode.SelectSingleNode("dcssch:valueType", $ns)
if ($vt) { $v = Decompile-Type $vt; if ($v) { $o['valueType'] = $v } }
$ur = $cfNode.SelectSingleNode("dcssch:useRestriction", $ns)
if ($ur) {
$r = [ordered]@{}
foreach ($k in 'field','condition','group','order') { if ((Get-Child $ur $k) -eq 'true') { $r[$k] = $true } }
if ($r.Count -gt 0) { $o['useRestriction'] = $r }
}
$pe = Get-Child $cfNode 'presentationExpression'
if ($null -ne $pe -and $pe -ne '') { $o['presentationExpression'] = $pe }
$oeNodes = @($cfNode.SelectNodes("dcssch:orderExpression", $ns))
if ($oeNodes.Count -gt 0) {
$oes = New-Object System.Collections.ArrayList
foreach ($oen in $oeNodes) {
$eo = [ordered]@{}
$exprN = $oen.SelectSingleNode("*[local-name()='expression']")
$otN = $oen.SelectSingleNode("*[local-name()='orderType']")
$aoN = $oen.SelectSingleNode("*[local-name()='autoOrder']")
$eo['expression'] = if ($exprN) { $exprN.InnerText } else { '' }
if ($otN -and $otN.InnerText -ne 'Asc') { $eo['orderType'] = $otN.InnerText }
if ($aoN -and $aoN.InnerText -eq 'true') { $eo['autoOrder'] = $true }
[void]$oes.Add($eo)
}
$o['orderExpression'] = @($oes)
}
return $o
}
# Schema-параметры динамического списка (<Parameter> под <Settings>) — зеркало эмиссии
# form-compile (Emit-DLParameters). Инверсия контекстных дефолтов: useRestriction=true и
# title==Title-FromName опускаем (компилятор восстановит). Сущность = DataCompositionSchemaParameter.
@@ -2573,10 +2611,20 @@ if ($attrsNode) {
if ($fn.GetAttribute("type", $NS_XSI) -match 'NestedDataSet$') { $fo['nested'] = $true }
$ftn = $fn.SelectSingleNode("dcssch:title", $ns)
if ($ftn) { $t = Get-LangText $ftn; if ($null -ne $t) { $fo['title'] = $t } }
# valueType поля набора (тип значения; вычисляемые/кастомные поля). Грамматика типа.
$fvt = $fn.SelectSingleNode("dcssch:valueType", $ns)
if ($fvt) { $fvtVal = Decompile-Type $fvt; if ($fvtVal) { $fo['valueType'] = $fvtVal } }
[void]$fields.Add($fo)
}
$so['fields'] = @($fields)
}
# Вычисляемые поля DataSet (<CalculatedField>) — после Field*, до Parameter*.
$calcNodes = @($setNode.SelectNodes("lf:CalculatedField", $ns))
if ($calcNodes.Count -gt 0) {
$cfs = New-Object System.Collections.ArrayList
foreach ($cn in $calcNodes) { [void]$cfs.Add((Build-CalcField $cn)) }
$so['calculatedFields'] = @($cfs)
}
# Schema-параметры дин-списка (прямые <Parameter> под Settings, не в ListSettings)
$paramNodes = @($setNode.SelectNodes("lf:Parameter", $ns))
if ($paramNodes.Count -gt 0) {