mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-20 17:40:59 +03:00
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:
co-authored by
Claude Opus 4.8
parent
081d3a8a2f
commit
43d36119cb
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-compile v1.138 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.139 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import copy
|
||||
@@ -1689,6 +1689,64 @@ def emit_appearance_value(lines, key, val, indent):
|
||||
lines.append(f'{indent}</dcscor:item>')
|
||||
|
||||
|
||||
# === Группировка строк динамического списка (DCS-структура ListSettings) ===
|
||||
# Линейная цепочка <dcsset:item StructureItemGroup> (каждый уровень = одно поле в groupItems;
|
||||
# вложенность — через дочерний <dcsset:item>). Плоская модель уровней (список всегда линеен).
|
||||
def get_list_grouping_value(s):
|
||||
for k in ('grouping', 'structure', 'группировка'):
|
||||
if s.get(k):
|
||||
return s[k]
|
||||
return None
|
||||
|
||||
|
||||
def parse_list_grouping(grouping):
|
||||
# Шорткат "A > B > C" → массив имён; массив строк/объектов → как есть.
|
||||
if not grouping:
|
||||
return []
|
||||
if isinstance(grouping, str):
|
||||
return [p.strip() for p in re.split(r'\s*>\s*', grouping) if p.strip()]
|
||||
return list(grouping)
|
||||
|
||||
|
||||
def emit_group_item_field(lines, level, indent):
|
||||
if isinstance(level, str):
|
||||
field, gt, pat = level, 'Items', 'None'
|
||||
pab = pae = '0001-01-01T00:00:00'
|
||||
else:
|
||||
field = str(level.get('field', ''))
|
||||
gt = str(level.get('groupType') or 'Items')
|
||||
pat = str(level.get('periodAdditionType') or 'None')
|
||||
pab = str(level.get('periodAdditionBegin') or '0001-01-01T00:00:00')
|
||||
pae = str(level.get('periodAdditionEnd') or '0001-01-01T00:00:00')
|
||||
lines.append(f'{indent}<dcsset:item xsi:type="dcsset:GroupItemField">')
|
||||
lines.append(f'{indent}\t<dcsset:field>{esc_xml(field)}</dcsset:field>')
|
||||
lines.append(f'{indent}\t<dcsset:groupType>{esc_xml(gt)}</dcsset:groupType>')
|
||||
lines.append(f'{indent}\t<dcsset:periodAdditionType>{esc_xml(pat)}</dcsset:periodAdditionType>')
|
||||
# Авто-детект: ISO-дата → xs:dateTime, иначе путь → dcscor:Field.
|
||||
pab_t = 'xs:dateTime' if re.match(r'^\d{4}-\d{2}-\d{2}T', pab) else 'dcscor:Field'
|
||||
pae_t = 'xs:dateTime' if re.match(r'^\d{4}-\d{2}-\d{2}T', pae) else 'dcscor:Field'
|
||||
lines.append(f'{indent}\t<dcsset:periodAdditionBegin xsi:type="{pab_t}">{esc_xml(pab)}</dcsset:periodAdditionBegin>')
|
||||
lines.append(f'{indent}\t<dcsset:periodAdditionEnd xsi:type="{pae_t}">{esc_xml(pae)}</dcsset:periodAdditionEnd>')
|
||||
lines.append(f'{indent}</dcsset:item>')
|
||||
|
||||
|
||||
def emit_list_grouping_levels(lines, levels, i, indent):
|
||||
lines.append(f'{indent}<dcsset:item xsi:type="dcsset:StructureItemGroup">')
|
||||
lines.append(f'{indent}\t<dcsset:groupItems>')
|
||||
emit_group_item_field(lines, levels[i], f'{indent}\t\t')
|
||||
lines.append(f'{indent}\t</dcsset:groupItems>')
|
||||
if i < len(levels) - 1:
|
||||
emit_list_grouping_levels(lines, levels, i + 1, f'{indent}\t')
|
||||
lines.append(f'{indent}</dcsset:item>')
|
||||
|
||||
|
||||
def emit_list_grouping(lines, grouping, indent):
|
||||
levels = parse_list_grouping(grouping)
|
||||
if not levels:
|
||||
return
|
||||
emit_list_grouping_levels(lines, levels, 0, indent)
|
||||
|
||||
|
||||
def emit_conditional_appearance(lines, items, indent, block_view_mode=None, block_user_setting_id=None, wrap_tag='dcsset:conditionalAppearance'):
|
||||
has_items = bool(items) and len(items) > 0
|
||||
has_block_meta = (block_view_mode is not None) or (block_user_setting_id is not None)
|
||||
@@ -5262,6 +5320,8 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
|
||||
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
|
||||
elif tag == 'itemsUserSettingID':
|
||||
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
|
||||
elif tag == 'structure':
|
||||
emit_list_grouping(lines, get_list_grouping_value(s), lsi)
|
||||
else:
|
||||
# Полный каноничный скелет (умолчание, ~93% форм) — без изменений.
|
||||
emit_filter(lines, s.get('filter'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_FILTER_ID)
|
||||
@@ -5270,6 +5330,8 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
|
||||
emit_data_parameters(lines, s.get('dataParameters'), lsi)
|
||||
emit_order(lines, s.get('order'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_ORDER_ID)
|
||||
emit_conditional_appearance(lines, s.get('conditionalAppearance'), lsi, block_view_mode='Normal', block_user_setting_id=CANON_CA_ID)
|
||||
# Группировка строк списка (авторинг без round-trip дескриптора) — после CA, до itemsViewMode
|
||||
emit_list_grouping(lines, get_list_grouping_value(s), lsi)
|
||||
lines.append(f'{lsi}<dcsset:itemsViewMode>Normal</dcsset:itemsViewMode>')
|
||||
lines.append(f'{lsi}<dcsset:itemsUserSettingID>{CANON_ITEMS_ID}</dcsset:itemsUserSettingID>')
|
||||
if len(lines) - 1 == ls_open_idx:
|
||||
|
||||
Reference in New Issue
Block a user