fix(form-decompile,form-compile): DataSet field TypeLink/Folder/пустой dataPath

Поле набора динсписка (settings.fields[]) — три подвида, терявшиеся при раундтрипе
(форма ИнвентаризацияНМА/ФормаПодбораДокументовЗатрат: 18 diff-строк → 0):

1. inputParameters[].typeLink {field, linkItem} — связь по типу (dcscor:TypeLink,
   субконто с типом-от-счёта). Декомпилятор склеивал InnerText в строку
   ("СчётДт"+"1"="СчётДт1") → компилятор писал xs:string. Структурный захват + эмит.
2. folder: true — поле-папка (DataSetFieldFolder, группировка СубконтоДт над
   СубконтоДт1/2/3; без <field>). Ловился только NestedDataSet; компилятор хардкодил
   DataSetFieldField + всегда <field>.
3. пустой dataPath: "" — поле с <dcssch:dataPath/> + <field> (≠ дефолт dataPath==field).
   Декомпилятор дропал → компилятор реконструировал dataPath=field. Has-Child вместо
   $dp -and; явный dataPath (вкл. "") побеждает fallback (self-closing при "").

Зеркало py (ps1==py байт-в-байт), регресс 43/43 (ps+py), широкий прогон list-top:
match 25→26, TOTAL 445→427, 0 регрессий. Декомпилятор v0.147 / компилятор v1.171.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-13 18:28:20 +03:00
parent 06331a9b80
commit 2a8d594f66
4 changed files with 56 additions and 14 deletions
@@ -1,4 +1,4 @@
# form-compile v1.170 — Compile 1C managed form from JSON or object metadata
# form-compile v1.171 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -5327,6 +5327,13 @@ function Emit-DLInputParameters {
}
X "$indent`t`t</dcscor:value>"
}
} elseif (Has-DLProp $item 'typeLink') {
# Связь по типу (dcscor:TypeLink) — field + linkItem (структурное значение параметра).
$tl = $item.typeLink
X "$indent`t`t<dcscor:value xsi:type=`"dcscor:TypeLink`">"
$tlf = Get-Prop $tl 'field'; if ($null -ne $tlf) { X "$indent`t`t`t<dcscor:field>$(Esc-Xml "$tlf")</dcscor:field>" }
$tli = Get-Prop $tl 'linkItem'; if ($null -ne $tli) { X "$indent`t`t`t<dcscor:linkItem>$(Esc-Xml "$tli")</dcscor:linkItem>" }
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>" }
@@ -5762,11 +5769,16 @@ function Emit-Attributes {
foreach ($fld in $st.fields) {
# Тип поля набора: DataSetFieldField (дефолт) vs DataSetFieldNestedDataSet
# (поле-вложенный набор = реквизит табличной части; маркер nested).
$ftype = if ($fld.nested) { "DataSetFieldNestedDataSet" } else { "DataSetFieldField" }
# folder = папка-группировка полей (DataSetFieldFolder, без <field>); nested = вложенный набор.
$isFolder = [bool](Get-Prop $fld 'folder')
$ftype = if ($fld.nested) { "DataSetFieldNestedDataSet" } elseif ($isFolder) { "DataSetFieldFolder" } else { "DataSetFieldField" }
X "$si<Field xsi:type=`"dcssch:$ftype`">"
$dp = if ($fld.dataPath) { $fld.dataPath } else { $fld.field }
X "$si`t<dcssch:dataPath>$(Esc-Xml "$dp")</dcssch:dataPath>"
X "$si`t<dcssch:field>$(Esc-Xml "$($fld.field)")</dcssch:field>"
# dataPath: явный (включая пустой "" → self-closing <dcssch:dataPath/>) побеждает; иначе fallback на field.
if ($null -ne (Get-Prop $fld 'dataPath')) { $dp = "$($fld.dataPath)" }
elseif ($isFolder) { $dp = "" }
else { $dp = "$($fld.field)" }
if ($dp -eq "") { X "$si`t<dcssch:dataPath/>" } else { X "$si`t<dcssch:dataPath>$(Esc-Xml "$dp")</dcssch:dataPath>" }
if (-not $isFolder) { X "$si`t<dcssch:field>$(Esc-Xml "$($fld.field)")</dcssch:field>" }
if ($fld.title) {
X "$si`t<dcssch:title xsi:type=`"v8:LocalStringType`">"
Emit-MLItems -val $fld.title -indent "$si`t`t"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.170 — Compile 1C managed form from JSON or object metadata
# form-compile v1.171 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -5074,6 +5074,15 @@ def emit_dl_input_parameters(lines, ip, indent):
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 'typeLink' in item:
# Связь по типу (dcscor:TypeLink) — field + linkItem (структурное значение параметра).
tl = item.get('typeLink') or {}
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:TypeLink">')
if tl.get('field') is not None:
lines.append(f'{indent}\t\t\t<dcscor:field>{esc_xml(str(tl.get("field")))}</dcscor:field>')
if tl.get('linkItem') is not None:
lines.append(f'{indent}\t\t\t<dcscor:linkItem>{esc_xml(str(tl.get("linkItem")))}</dcscor:linkItem>')
lines.append(f'{indent}\t\t</dcscor:value>')
elif 'value' in item:
val = item.get('value')
if isinstance(val, bool):
@@ -5526,11 +5535,23 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
for fld in s['fields']:
# Тип поля набора: DataSetFieldField (дефолт) vs DataSetFieldNestedDataSet
# (поле-вложенный набор = реквизит табличной части; маркер nested).
ftype = 'DataSetFieldNestedDataSet' if fld.get('nested') else 'DataSetFieldField'
# folder = папка-группировка полей (DataSetFieldFolder, без <field>); nested = вложенный набор.
is_folder = bool(fld.get('folder'))
ftype = 'DataSetFieldNestedDataSet' if fld.get('nested') else ('DataSetFieldFolder' if is_folder else 'DataSetFieldField')
lines.append(f'{si}<Field xsi:type="dcssch:{ftype}">')
dp = fld.get('dataPath') or fld.get('field')
lines.append(f'{si}\t<dcssch:dataPath>{esc_xml(str(dp))}</dcssch:dataPath>')
lines.append(f'{si}\t<dcssch:field>{esc_xml(str(fld.get("field", "")))}</dcssch:field>')
# dataPath: явный (включая "" → self-closing) побеждает; иначе fallback на field.
if fld.get('dataPath') is not None:
dp = str(fld.get('dataPath'))
elif is_folder:
dp = ''
else:
dp = str(fld.get('field', ''))
if dp == '':
lines.append(f'{si}\t<dcssch:dataPath/>')
else:
lines.append(f'{si}\t<dcssch:dataPath>{esc_xml(dp)}</dcssch:dataPath>')
if not is_folder:
lines.append(f'{si}\t<dcssch:field>{esc_xml(str(fld.get("field", "")))}</dcssch:field>')
if fld.get('title'):
lines.append(f'{si}\t<dcssch:title xsi:type="v8:LocalStringType">')
emit_ml_items(lines, f'{si}\t\t', fld['title'])
@@ -1,4 +1,4 @@
# form-decompile v0.146 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v0.147 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1423,6 +1423,13 @@ function Build-DLInputParameters {
[void]$cpls.Add($cpo)
}
$io['choiceParameterLinks'] = @($cpls)
} elseif ($vt -match 'TypeLink$') {
# Связь по типу (dcscor:TypeLink): field + linkItem — структурное значение,
# НЕ склеивать InnerText в строку ("СчетДт"+"1"="СчетДт1").
$tlo = [ordered]@{}
$tlf = Get-Child $valN 'field'; if ($null -ne $tlf) { $tlo['field'] = $tlf }
$tli = Get-Child $valN 'linkItem'; if ($null -ne $tli) { $tlo['linkItem'] = if ($tli -match '^-?\d+$') { [int]$tli } else { $tli } }
$io['typeLink'] = $tlo
} else {
if ($valN.GetAttribute("nil", $NS_XSI) -ne 'true') { $io['value'] = Convert-TypedValue -raw $valN.InnerText -xsiType $vt }
}
@@ -2798,10 +2805,12 @@ if ($attrsNode) {
$fld = Get-Child $fn 'field'
$dp = Get-Child $fn 'dataPath'
if ($fld) { $fo['field'] = $fld }
if ($dp -and $dp -ne $fld) { $fo['dataPath'] = $dp }
if ((Has-Child $fn 'dataPath') -and $dp -ne $fld) { $fo['dataPath'] = $dp }
# Тип поля набора: DataSetFieldField (дефолт, компилятор хардкодит) vs
# DataSetFieldNestedDataSet (поле-вложенный набор = реквизит табличной части). Маркер nested.
if ($fn.GetAttribute("type", $NS_XSI) -match 'NestedDataSet$') { $fo['nested'] = $true }
$fTypeAttr = $fn.GetAttribute("type", $NS_XSI)
if ($fTypeAttr -match 'NestedDataSet$') { $fo['nested'] = $true }
elseif ($fTypeAttr -match 'Folder$') { $fo['folder'] = $true }
$ftn = $fn.SelectSingleNode("dcssch:title", $ns)
if ($ftn) { $t = Get-LangText $ftn; if ($null -ne $t) { $fo['title'] = $t } }
# valueType поля набора (тип значения; вычисляемые/кастомные поля). Грамматика типа.
+1 -1
View File
@@ -950,7 +950,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
| `query` | string | Текст запроса (`ManualQuery=true`). Поддерживает `@file.sql` (путь относительно JSON) |
| `dynamicDataRead` | bool | Динамическое считывание. **Умолчание `true`** — указывать только для отключения (`false`) |
| `autoFillAvailableFields` | bool | Автозаполнение доступных полей (`<AutoFillAvailableFields>`). **Умолчание `true`** — указывать только для отключения (`false`; тогда поля берутся из явного запроса, не авто). Эмитится первым в `<Settings>` |
| `fields` | array | Явные поля набора (редко): `{ field, dataPath?, title?, useRestriction?, attributeUseRestriction?, valueType?, presentationExpression?, appearance?, inputParameters?, nested? }` — для переопределения свойств поля. `useRestriction`/`attributeUseRestriction` — ограничения использования: объект `{ field?, condition?, group?, order? }` (bool) или флаг-строка `"#noField #noFilter #noGroup #noOrder"`. `valueType` — тип значения (грамматика типа). `presentationExpression` — выражение представления поля (строка). `appearance` — оформление/формат поля: объект `{ Параметр: Значение }` (та же грамматика, что `appearance` условного оформления, напр. `{ "Формат": "ДЛ=9", "ЦветТекста": "web:Gray" }`). `inputParameters` — связь по параметрам выбора (как у параметра дин-списка). `nested: true` помечает поле-вложенный набор (`DataSetFieldNestedDataSet` = реквизит табличной части объекта; дефолт — `DataSetFieldField`). Обычно поля выводятся из запроса автоматически |
| `fields` | array | Явные поля набора (редко): `{ field, dataPath?, title?, useRestriction?, attributeUseRestriction?, valueType?, presentationExpression?, appearance?, inputParameters?, nested?, folder? }` — для переопределения свойств поля. `useRestriction`/`attributeUseRestriction` — ограничения использования: объект `{ field?, condition?, group?, order? }` (bool) или флаг-строка `"#noField #noFilter #noGroup #noOrder"`. `valueType` — тип значения (грамматика типа). `presentationExpression` — выражение представления поля (строка). `appearance` — оформление/формат поля: объект `{ Параметр: Значение }` (та же грамматика, что `appearance` условного оформления, напр. `{ "Формат": "ДЛ=9", "ЦветТекста": "web:Gray" }`). `inputParameters` — связь по параметрам выбора (как у параметра дин-списка); элемент `{ parameter, value? \| choiceParameters? \| choiceParameterLinks? \| typeLink? }`. `typeLink: { field, linkItem }` — связь по типу (`dcscor:TypeLink`, напр. субконто, тип которого определяется счётом): `field` = поле-источник типа, `linkItem` = индекс. `nested: true` помечает поле-вложенный набор (`DataSetFieldNestedDataSet` = реквизит табличной части объекта). `folder: true` помечает поле-папку (`DataSetFieldFolder` = группировка вложенных полей, напр. `СубконтоДт` над `СубконтоДт1/2/3`; без `<field>`). Дефолт — `DataSetFieldField`. Пустой `dataPath: ""` → self-closing `<dcssch:dataPath/>` (поле без пути, только `field`). Обычно поля выводятся из запроса автоматически |
| `calculatedFields` | array | Вычисляемые поля набора (см. ниже) |
| `parameters` | array | Параметры схемы запроса (`DataCompositionSchemaParameter`) — см. ниже |
| `order` | array | Сортировка списка (см. ниже) |