feat(form-decompile,form-compile): группировка строк динамического списка (StructureItemGroup в ListSettings, кластер B)

Структура группировок дин-списка (`<dcsset:item StructureItemGroup>` → groupItems →
GroupItemField, вложенность через дочерний item) — переиспользована модель/реализация
из skd (Emit-GroupItems/Get-GroupFields), но плоская: группировка списка всегда
линейная цепочка одно-польных уровней над неявными деталями (без children/selection/
order/details — корпус подтверждает).

DSL — новый ключ `settings.grouping` (forgiving-синонимы `structure`/`группировка`):
- шорткат "A > B > C" (вложенные уровни, внешний→внутренний) или массив;
- элемент уровня — строка (имя поля) или объект {field, groupType?, periodAdditionType?,
  periodAdditionBegin?, periodAdditionEnd?} для нестандартного поля (ключи = теги
  исходника; periodAddition с авто-детектом ISO-дата/dcscor:Field).
Корпус 8.3.24 (29 форм/34 уровня): groupType Items 33 / Hierarchy 1, periodAddition нет.

Компилятор (ps1+py): Emit-ListGrouping + рекурсивная цепочка StructureItemGroup в
позиции после conditionalAppearance, до itemsViewMode. Оба пути — shape-дескриптор
(round-trip) и канонический (авторинг). Декомпилятор: Build-ListGrouping (линейная
цепочка; bail→$null на ветвлении/мультиполе/доп.содержимом = честный LOST, не порча);
Get-ListSettingsShape распознаёт `item`→`structure` (раньше → $null/канон-fallback,
из-за чего терялась группировка и додумывался itemsUserSettingID).

Выборка 17 форм с группировкой: match 0→10, TOTAL 75→25 (остаток — др. кластеры:
presentation xs:string, order-item use). Широкая (cat-a 102): match 82→84, TOTAL
280→256, ноль регрессий. Кейс dynamic-list-form (+grouping "Description > Code")
сертифицирован загрузкой в 1С. Регресс 43/43 (ps1+py).

Фикс по пути: PS-ловушка — одноэлементный массив разворачивался при return из
Parse-ListGrouping → строка → индексация давала char → пустой <field>. Unary comma ,@().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-12 19:09:48 +03:00
parent 081d3a8a2f
commit 43d36119cb
6 changed files with 245 additions and 6 deletions
+30
View File
@@ -946,6 +946,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
| `filter` | array | Отбор списка (грамматика как в СКД) |
| `dataParameters` | array | Значения параметров запроса в настройках (`<dcsset:dataParameters>`). **Грамматика как в СКД**: shorthand `"Имя = Значение @off @user"` или объект `{ parameter, value?, valueType?, use?, nilValue?, viewMode?, userSettingID?, userSettingPresentation? }`. В дин-списке частый паттерн — плейсхолдер отключённого параметра без значения: `"ИмяПараметра @off"` |
| `conditionalAppearance` | array | Условное оформление списка (грамматика как в СКД) |
| `grouping` | string \| array | Группировка строк списка (см. ниже). Forgiving-синонимы: `structure`, `группировка` |
`ManualQuery` выводится из наличия `query` — отдельным ключом не задаётся.
@@ -983,6 +984,35 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
> **`value: null` при `valueListAllowed: true`** — явный маркер «эмитить `<dcssch:value xsi:nil/>`». Платформа пишет nil-значение для valueListAllowed-параметра не всегда (корпус 27 с / 47 без); по умолчанию (ключ `value` отсутствует) компилятор его НЕ эмитит. Декомпилятор ставит `value: null`, когда оригинал содержит nil-тег.
#### grouping — группировка строк списка
Уровни группировки над детальными записями списка (XML: цепочка `<dcsset:item StructureItemGroup>` в `<ListSettings>`). Группировка списка — **линейная цепочка** (каждый уровень = одно поле; несколько уровней вкладываются друг в друга), поэтому DSL плоский:
```json
"grouping": "Контрагент" // один уровень
"grouping": "Контрагент > Договор > Заказ" // вложенные уровни (внешний → внутренний)
"grouping": ["Контрагент", "Договор"] // то же массивом
```
Шорткат `>` разделяет уровни. Элемент уровня — строка (имя поля) ИЛИ объект для нестандартного поля (ключи = теги исходника):
```json
"grouping": [
"Контрагент",
{ "field": "Период", "groupType": "Hierarchy" },
{ "field": "Дата", "periodAdditionType": "...", "periodAdditionBegin": "2024-01-01T00:00:00", "periodAdditionEnd": "Параметр.КонецПериода" }
]
```
| Ключ уровня | Значение |
|-------------|----------|
| `field` | Имя поля группировки |
| `groupType` | Тип группировки (умолчание `Items`; `Hierarchy` — с учётом иерархии) |
| `periodAdditionType` | Дополнение периода для группировки по дате (умолчание `None`) |
| `periodAdditionBegin` / `periodAdditionEnd` | Границы дополнения периода: ISO-дата (`xs:dateTime`) или путь к полю (`dcscor:Field`) — авто-детект |
> Грамматика уровня совпадает с элементом `groupBy`/`groupFields` структуры СКД (см. [skd-dsl-spec.md](skd-dsl-spec.md)); отличие от СКД — плоская модель (нет `children`/`selection`/`order`/детальных записей, которых у группировки списка не бывает).
#### order / filter / conditionalAppearance
Грамматика этих ключей идентична настройкам СКД — см. [skd-dsl-spec.md](skd-dsl-spec.md) (разделы filter / order / conditionalAppearance). Кратко: