diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index 757c3a43..0d23f188 100644 --- a/.claude/skills/form-compile/scripts/form-compile.ps1 +++ b/.claude/skills/form-compile/scripts/form-compile.ps1 @@ -1,4 +1,4 @@ -# form-compile v1.53 — Compile 1C managed form from JSON or object metadata +# form-compile v1.54 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -3657,10 +3657,36 @@ function Emit-Attributes { if ($attr.fillChecking) { X "$inner$($attr.fillChecking)" } + + # UseAlways: поля, всегда читаемые (дин-список/таблица). Две формы DSL сливаются: + # attr.useAlways[] (короткие имена) + columns с useAlways:true → ИмяРеквизита.Поле. + $uaFields = New-Object System.Collections.ArrayList + if ($attr.useAlways) { + foreach ($e in @($attr.useAlways)) { + $fld = "$e" + if ($fld -notmatch "^$([regex]::Escape($attrName))\.") { $fld = "$attrName.$fld" } + if (-not $uaFields.Contains($fld)) { [void]$uaFields.Add($fld) } + } + } + if ($attr.columns) { + foreach ($col in $attr.columns) { + if ($col.useAlways -eq $true) { + $fld = "$attrName.$($col.name)" + if (-not $uaFields.Contains($fld)) { [void]$uaFields.Add($fld) } + } + } + } + if ($uaFields.Count -gt 0) { + X "$inner" + foreach ($f in $uaFields) { X "$inner`t$f" } + X "$inner" + } + Emit-FunctionalOptions -fo $attr.functionalOptions -indent $inner - # Columns (for ValueTable/ValueTree) - if ($attr.columns -and $attr.columns.Count -gt 0) { + # Columns (for ValueTable/ValueTree). Для дин-списка (есть settings) колонки НЕ эмитим — + # они служат лишь для формирования UseAlways (поля выше). + if ($attr.columns -and $attr.columns.Count -gt 0 -and -not $attr.settings) { X "$inner" foreach ($col in $attr.columns) { $colId = New-Id diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 7abda8a1..7e00c709 100644 --- a/.claude/skills/form-compile/scripts/form-compile.py +++ b/.claude/skills/form-compile/scripts/form-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# form-compile v1.53 — Compile 1C managed form from JSON or object metadata +# form-compile v1.54 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -3331,10 +3331,32 @@ def emit_attributes(lines, attrs, indent): lines.append(f'{inner}true') if attr.get('fillChecking'): lines.append(f'{inner}{attr["fillChecking"]}') + + # UseAlways: поля, всегда читаемые. Две формы DSL сливаются: + # attr.useAlways[] (короткие имена) + columns с useAlways:true → ИмяРеквизита.Поле. + ua_fields = [] + for e in (attr.get('useAlways') or []): + fld = str(e) + if not re.match(r'^' + re.escape(attr_name) + r'\.', fld): + fld = f'{attr_name}.{fld}' + if fld not in ua_fields: + ua_fields.append(fld) + for col in (attr.get('columns') or []): + if col.get('useAlways') is True: + fld = f'{attr_name}.{col["name"]}' + if fld not in ua_fields: + ua_fields.append(fld) + if ua_fields: + lines.append(f'{inner}') + for f in ua_fields: + lines.append(f'{inner}\t{f}') + lines.append(f'{inner}') + emit_functional_options(lines, attr.get('functionalOptions'), inner) - # Columns (for ValueTable/ValueTree) - if attr.get('columns') and len(attr['columns']) > 0: + # Columns (for ValueTable/ValueTree). Для дин-списка (есть settings) колонки НЕ эмитим — + # они служат лишь для формирования UseAlways. + if attr.get('columns') and len(attr['columns']) > 0 and not attr.get('settings'): lines.append(f'{inner}') for col in attr['columns']: col_id = new_id() diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index e567a259..e7cafc48 100644 --- a/.claude/skills/form-decompile/scripts/form-decompile.ps1 +++ b/.claude/skills/form-decompile/scripts/form-decompile.ps1 @@ -1,4 +1,4 @@ -# form-decompile v0.35 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.36 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1329,6 +1329,29 @@ if ($attrsNode) { } if ($cols.Count -gt 0) { $ao['columns'] = @($cols) } } + # UseAlways: поля, всегда читаемые. Префикс "ИмяРеквизита." снимаем. + # ValueTable (есть columns): useAlways:true на совпавшей колонке; остальные → массив атрибута. + # Дин-список/прочие (нет columns): массив useAlways на атрибуте. + $uaNode = $a.SelectSingleNode("lf:UseAlways", $ns) + if ($uaNode) { + $prefix = "$($ao['name'])." + $shorts = New-Object System.Collections.ArrayList + foreach ($fn in @($uaNode.SelectNodes("lf:Field", $ns))) { + $t = $fn.InnerText.Trim() + if ($t.StartsWith($prefix)) { $t = $t.Substring($prefix.Length) } + [void]$shorts.Add($t) + } + if ($ao.Contains('columns')) { + $rest = New-Object System.Collections.ArrayList + foreach ($s in $shorts) { + $col = $ao['columns'] | Where-Object { $_['name'] -eq $s } | Select-Object -First 1 + if ($col) { $col['useAlways'] = $true } else { [void]$rest.Add($s) } + } + if ($rest.Count -gt 0) { $ao['useAlways'] = @($rest) } + } elseif ($shorts.Count -gt 0) { + $ao['useAlways'] = @($shorts) + } + } # Settings динамического списка $setNode = $a.SelectSingleNode("lf:Settings", $ns) if ($setNode) { diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 3bcda4cf..51328bfe 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -612,6 +612,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs | `view` | bool/object | Просмотр по ролям (``). См. §4.1c | | `edit` | bool/object | Редактирование по ролям (``). См. §4.1c | | `functionalOptions` | array | Функциональные опции (`FunctionalOption.X…`). Массив имён; forgiving: `"X"`/`"FunctionalOption.X"`. Также у колонок (`columns[*]`) и команд (§7) | +| `useAlways` | array | Поля, всегда читаемые (`Имя.Поле…`). Массив коротких имён полей (forgiving: с/без префикса `Имя.`). **Две формы**: этот массив на реквизите ИЛИ `useAlways: true` на колонке (`columns[*]`) — компилятор сливает. Для дин-списка — только массив (колонки не эмитятся, но формируют ``) | | `savedData` | bool | Сохраняемые данные | | `fillChecking` | string | `Show`, `DontShow` | | `columns` | array | Колонки для ValueTable/ValueTree | diff --git a/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml index 0971f54f..c7fd5f9e 100644 --- a/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/table/DataProcessors/Таблица/Forms/Форма/Ext/Form.xml @@ -106,6 +106,10 @@ v8:ValueTable + + Данные.Сумма + Данные.Дата + diff --git a/tests/skills/cases/form-compile/table.json b/tests/skills/cases/form-compile/table.json index d4931af8..8f007f3e 100644 --- a/tests/skills/cases/form-compile/table.json +++ b/tests/skills/cases/form-compile/table.json @@ -35,8 +35,8 @@ ], "attributes": [ { "name": "Объект", "type": "DataProcessorObject.Таблица", "main": true }, - { "name": "Данные", "type": "ValueTable", "columns": [ - { "name": "Дата", "type": "date" }, + { "name": "Данные", "type": "ValueTable", "useAlways": ["Сумма"], "columns": [ + { "name": "Дата", "type": "date", "useAlways": true }, { "name": "Сумма", "type": "decimal(15,2)" }, { "name": "Комментарий", "type": "string(200)" }, { "name": "Объект", "type": "AnyRef" },