mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-02 01:37:45 +03:00
feat(form-decompile,form-compile): динсписок DataSet/ListSettings — TypeId-verbatim + DataSetFieldNestedDataSet + ListSettings self-closing (механика кластера A)
Три механических фикса доминирующего кластера встроенных DCS-настроек динсписка
(152/690 дифф-форм rt-iter). Таргет-выборка 102 формы: TOTAL 657→280, match 0→82,
ноль регрессий.
1. TypeId-verbatim. Тип параметра/реквизита, заданный глобальным стабильным GUID
(<v8:TypeId>, не <v8:Type>) — платформа так сериализует типы, чьё имя в контексте
недоступно (определяемые/характеристики). Декомпилятор не ловил → параметр терял
valueType. Маркер 'typeid:<GUID>' в грамматике типа: Decompile-Type ловит <v8:TypeId>,
Emit-SingleType разворачивает обратно (как роль-по-GUID; GUID глобально стабилен →
безопасно). Форма ОстаткиПартийСАТУРН/ФормаОстатков → match.
2. DataSetFieldNestedDataSet. Компилятор хардкодил xsi:type="DataSetFieldField" для
всех полей набора → терял поле-вложенный набор (реквизит табличной части объекта).
Маркер fields[].nested: декомпилятор ловит ...NestedDataSet, компилятор зеркалит.
3. ListSettings self-closing. Пустой дескриптор listSettings:{} эмитился парой
<ListSettings></ListSettings>, оригинал — self-closing <ListSettings/> (70 форм
корпуса). Зеркалим self-closing при пустом эмите (отслеживание буфера).
Остаток кластера (вне A, отдельные задачи): структура группировок списка
(dcsset:item StructureItemGroup), KeyField/CalculatedField/Field-valueType DataSet.
spec: fields.nested, listSettings:{} → self-closing, тип-токен typeid:<GUID>
(раундтрип, не для авторинга). Регресс form-compile 43/43 (ps1+py).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c383cc4ffe
commit
081d3a8a2f
@@ -1,4 +1,4 @@
|
||||
# form-compile v1.137 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.138 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$JsonPath,
|
||||
@@ -2106,6 +2106,14 @@ function Emit-SingleType {
|
||||
|
||||
$typeStr = Resolve-TypeStr $typeStr
|
||||
|
||||
# TypeId — тип, заданный глобальным стабильным GUID (<v8:TypeId>, не <v8:Type>). Платформа так
|
||||
# сериализует типы, чьё имя в этом контексте недоступно (определяемые/характеристики). GUID
|
||||
# глобально стабилен → эмитим verbatim (как роль-по-GUID). Маркер декомпилятора: 'typeid:GUID'.
|
||||
if ($typeStr -match '^typeid:([0-9a-fA-F-]{36})$') {
|
||||
X "$indent<v8:TypeId>$($Matches[1])</v8:TypeId>"
|
||||
return
|
||||
}
|
||||
|
||||
# boolean
|
||||
if ($typeStr -eq "boolean") {
|
||||
X "$indent<v8:Type>xs:boolean</v8:Type>"
|
||||
@@ -5471,7 +5479,10 @@ function Emit-Attributes {
|
||||
# Явные поля набора (редко): override title/dataPath
|
||||
if ($st.fields) {
|
||||
foreach ($fld in $st.fields) {
|
||||
X "$si<Field xsi:type=`"dcssch:DataSetFieldField`">"
|
||||
# Тип поля набора: DataSetFieldField (дефолт) vs DataSetFieldNestedDataSet
|
||||
# (поле-вложенный набор = реквизит табличной части; маркер nested).
|
||||
$ftype = if ($fld.nested) { "DataSetFieldNestedDataSet" } 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>"
|
||||
@@ -5491,7 +5502,9 @@ function Emit-Attributes {
|
||||
# ListSettings: filter/order/conditionalAppearance (skd-грамматика) + каноничные блок-GUID.
|
||||
# Нет items → контейнеры всё равно эмитятся (blockMeta) = каноничный пустой скелет платформы.
|
||||
$lsi = "$si`t"
|
||||
$lsOpenLen = $script:xml.Length
|
||||
X "$si<ListSettings>"
|
||||
$lsAfterOpenLen = $script:xml.Length # для self-closing, если внутри ничего не эмитнётся
|
||||
if ($st.PSObject.Properties['listSettings'] -and $null -ne $st.listSettings) {
|
||||
# Частичная/минимальная форма скелета — эмитим ТОЛЬКО указанные части с их блок-метой.
|
||||
# meta: 'v'=viewMode, 'u'=userSettingID (контейнеры); itemsViewMode/itemsUserSettingID → present.
|
||||
@@ -5516,7 +5529,13 @@ function Emit-Attributes {
|
||||
X "$lsi<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>"
|
||||
X "$lsi<dcsset:itemsUserSettingID>$($script:CANON_ITEMS_ID)</dcsset:itemsUserSettingID>"
|
||||
}
|
||||
X "$si</ListSettings>"
|
||||
if ($script:xml.Length -eq $lsAfterOpenLen) {
|
||||
# Пустой дескриптор listSettings:{} (оригинал = <ListSettings/>) → зеркалим self-closing.
|
||||
$script:xml.Length = $lsOpenLen
|
||||
X "$si<ListSettings/>"
|
||||
} else {
|
||||
X "$si</ListSettings>"
|
||||
}
|
||||
X "$inner</Settings>"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-compile v1.137 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.138 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import copy
|
||||
@@ -3373,6 +3373,13 @@ def resolve_type_str(type_str):
|
||||
|
||||
def emit_single_type(lines, type_str, indent):
|
||||
type_str = resolve_type_str(type_str)
|
||||
# TypeId — тип, заданный глобальным стабильным GUID (<v8:TypeId>, не <v8:Type>). Платформа так
|
||||
# сериализует типы, чьё имя в этом контексте недоступно (определяемые/характеристики). GUID
|
||||
# глобально стабилен → эмитим verbatim (как роль-по-GUID). Маркер декомпилятора: 'typeid:GUID'.
|
||||
m = re.match(r'^typeid:([0-9a-fA-F-]{36})$', type_str)
|
||||
if m:
|
||||
lines.append(f'{indent}<v8:TypeId>{m.group(1)}</v8:TypeId>')
|
||||
return
|
||||
# boolean
|
||||
if type_str == 'boolean':
|
||||
lines.append(f'{indent}<v8:Type>xs:boolean</v8:Type>')
|
||||
@@ -5212,7 +5219,10 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
|
||||
# Явные поля набора (редко): override title/dataPath
|
||||
if s.get('fields'):
|
||||
for fld in s['fields']:
|
||||
lines.append(f'{si}<Field xsi:type="dcssch:DataSetFieldField">')
|
||||
# Тип поля набора: DataSetFieldField (дефолт) vs DataSetFieldNestedDataSet
|
||||
# (поле-вложенный набор = реквизит табличной части; маркер nested).
|
||||
ftype = 'DataSetFieldNestedDataSet' if fld.get('nested') 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>')
|
||||
@@ -5232,6 +5242,7 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
|
||||
# Нет items → контейнеры всё равно эмитятся (blockMeta) = каноничный пустой скелет платформы.
|
||||
lsi = f'{si}\t'
|
||||
lines.append(f'{si}<ListSettings>')
|
||||
ls_open_idx = len(lines) - 1 # для self-closing, если внутри ничего не эмитнётся
|
||||
ls_shape = s.get('listSettings')
|
||||
if ls_shape is not None:
|
||||
# Частичная/минимальная форма скелета — эмитим ТОЛЬКО указанные части с их блок-метой.
|
||||
@@ -5261,7 +5272,11 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
|
||||
emit_conditional_appearance(lines, s.get('conditionalAppearance'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_CA_ID)
|
||||
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
|
||||
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
|
||||
lines.append(f'{si}</ListSettings>')
|
||||
if len(lines) - 1 == ls_open_idx:
|
||||
# Пустой дескриптор listSettings:{} (оригинал = <ListSettings/>) → зеркалим self-closing.
|
||||
lines[ls_open_idx] = f'{si}<ListSettings/>'
|
||||
else:
|
||||
lines.append(f'{si}</ListSettings>')
|
||||
lines.append(f'{inner}</Settings>')
|
||||
|
||||
lines.append(f'{indent}\t</Attribute>')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# form-decompile v0.110 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# form-decompile v0.111 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
|
||||
param(
|
||||
@@ -1180,6 +1180,12 @@ function Decompile-Type {
|
||||
$short = $raw -replace '^(v8ui|v8|cfg):', ''
|
||||
[void]$parts.Add($short)
|
||||
}
|
||||
# TypeId — тип, заданный глобальным стабильным GUID (<v8:TypeId>, не <v8:Type>). Платформа так
|
||||
# сериализует типы, чьё имя в этом контексте недоступно (определяемые/характеристики). GUID глобально
|
||||
# стабилен → эмитим verbatim как маркер 'typeid:GUID' (компилятор разворачивает обратно; как роль-по-GUID).
|
||||
foreach ($ti in @($typeNode.SelectNodes("v8:TypeId", $ns))) {
|
||||
[void]$parts.Add("typeid:" + $ti.InnerText.Trim())
|
||||
}
|
||||
if ($parts.Count -eq 0) { return $null }
|
||||
if ($parts.Count -eq 1) { return $parts[0] }
|
||||
return ($parts -join ' | ')
|
||||
@@ -2507,6 +2513,9 @@ if ($attrsNode) {
|
||||
$dp = Get-Child $fn 'dataPath'
|
||||
if ($fld) { $fo['field'] = $fld }
|
||||
if ($dp -and $dp -ne $fld) { $fo['dataPath'] = $dp }
|
||||
# Тип поля набора: DataSetFieldField (дефолт, компилятор хардкодит) vs
|
||||
# DataSetFieldNestedDataSet (поле-вложенный набор = реквизит табличной части). Маркер nested.
|
||||
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 } }
|
||||
[void]$fields.Add($fo)
|
||||
|
||||
@@ -940,7 +940,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? }` — для переопределения заголовка. Обычно поля выводятся из запроса автоматически |
|
||||
| `fields` | array | Явные поля набора (редко): `{ field, dataPath?, title?, nested? }` — для переопределения заголовка. `nested: true` помечает поле-вложенный набор (`DataSetFieldNestedDataSet` = реквизит табличной части объекта; дефолт — `DataSetFieldField`). Обычно поля выводятся из запроса автоматически |
|
||||
| `parameters` | array | Параметры схемы запроса (`DataCompositionSchemaParameter`) — см. ниже |
|
||||
| `order` | array | Сортировка списка (см. ниже) |
|
||||
| `filter` | array | Отбор списка (грамматика как в СКД) |
|
||||
@@ -951,7 +951,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
|
||||
|
||||
Пустой блок настроек компоновщика (`ListSettings`) генерируется автоматически (каноничный полный скелет платформы — filter+order+conditionalAppearance+itemsViewMode+itemsUserSettingID, ~93% форм); указывать ничего не нужно.
|
||||
|
||||
| `listSettings` | object | **Дескриптор формы скелета `<ListSettings>`** — только для НЕ-каноничных (частичных/минимальных) форм. Ordered-карта present top-level элементов: контейнеры `filter`/`order`/`conditionalAppearance` → блок-мета (`"vu"`=viewMode+userSettingID, `"u"`=только userSettingID, `"v"`, `""`); `itemsViewMode`/`itemsUserSettingID` → `true`. Компилятор эмитит ТОЛЬКО указанные части (контент берёт из `filter`/`order`/`conditionalAppearance`). Нет ключа → полный каноничный скелет. Декомпилятор пишет дескриптор только для отклонений от канона |
|
||||
| `listSettings` | object | **Дескриптор формы скелета `<ListSettings>`** — только для НЕ-каноничных (частичных/минимальных) форм. Ordered-карта present top-level элементов: контейнеры `filter`/`order`/`conditionalAppearance` → блок-мета (`"vu"`=viewMode+userSettingID, `"u"`=только userSettingID, `"v"`, `""`); `itemsViewMode`/`itemsUserSettingID` → `true`. Компилятор эмитит ТОЛЬКО указанные части (контент берёт из `filter`/`order`/`conditionalAppearance`). Нет ключа → полный каноничный скелет. Пустой объект `{}` → self-closing `<ListSettings/>` (оригинал без скелета). Декомпилятор пишет дескриптор только для отклонений от канона |
|
||||
|
||||
#### parameters — параметры схемы дин-списка
|
||||
|
||||
@@ -979,6 +979,8 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
|
||||
|
||||
Объектные ключи (как в СКД): `name`, `title`, `type`/`valueType`, `value`, `valueListAllowed`, `useRestriction`, `availableAsField`, `expression`, `availableValues` (`[{ value, presentation }]`), `inputParameters`, `denyIncompleteValues`, `use`.
|
||||
|
||||
> **Тип-токен `typeid:<GUID>`** (раундтрип, не для ручного авторинга) — тип, заданный глобальным стабильным GUID (`<v8:TypeId>`, не `<v8:Type>`). Платформа так сериализует типы, чьё имя в данном контексте недоступно (определяемые типы / характеристики). GUID глобально стабилен → эмитится verbatim. Применим везде, где принимается `type`/`valueType` (параметры, реквизиты). Декомпилятор ставит его сам; вручную указывают только реальный существующий GUID типа конфигурации.
|
||||
|
||||
> **`value: null` при `valueListAllowed: true`** — явный маркер «эмитить `<dcssch:value xsi:nil/>`». Платформа пишет nil-значение для valueListAllowed-параметра не всегда (корпус 27 с / 47 без); по умолчанию (ключ `value` отсутствует) компилятор его НЕ эмитит. Декомпилятор ставит `value: null`, когда оригинал содержит nil-тег.
|
||||
|
||||
#### order / filter / conditionalAppearance
|
||||
|
||||
Reference in New Issue
Block a user