From 1bc5e8f07a69d9fb65b261982b1d54d66c42341e Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Mon, 6 Apr 2026 14:17:33 +0300 Subject: [PATCH] feat(skd-compile): hidden, valueListAllowed, drilldown, groupHeaderTemplate, dataPath fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix: calculatedField dataPath fallback from "field" key (#5) - fix: groupHeaderTemplate vs groupTemplate, groupName support (#7) - feat: @valueList / valueListAllowed for parameters (#4) - feat: @hidden / hidden params + "dataParameters": "auto" (#1) - feat: drilldown in template params — DetailsAreaTemplateParameter + appearance (#2+#3) - fix(template-add): improved error message with path hint (#6) - docs: SKILL.md updated with new keys and examples - version bump: skd-compile v1.4, template-add v1.2 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/skills/skd-compile/SKILL.md | 30 ++++- .../skd-compile/scripts/skd-compile.ps1 | 118 ++++++++++++++++-- .../skills/skd-compile/scripts/skd-compile.py | 117 +++++++++++++++-- .../template-add/scripts/add-template.ps1 | 4 +- .../template-add/scripts/add-template.py | 6 +- 5 files changed, 248 insertions(+), 27 deletions(-) diff --git a/.claude/skills/skd-compile/SKILL.md b/.claude/skills/skd-compile/SKILL.md index afa3beaa..2d535adb 100644 --- a/.claude/skills/skd-compile/SKILL.md +++ b/.claude/skills/skd-compile/SKILL.md @@ -97,7 +97,14 @@ powershell.exe -NoProfile -File .claude/skills/skd-compile/scripts/skd-compile.p ] ``` -`@autoDates` — автоматически генерирует параметры `ДатаНачала` и `ДатаОкончания` с выражениями `&Период.ДатаНачала` / `&Период.ДатаОкончания` и `availableAsField=false`. Заменяет 5 строк на 1. +Флаги shorthand: +- `@autoDates` — автоматически генерирует параметры `ДатаНачала` и `ДатаОкончания` с выражениями `&Период.ДатаНачала` / `&Период.ДатаОкончания` и `availableAsField=false` +- `@valueList` — `true` — разрешает передавать список значений +- `@hidden` — скрытый параметр: `availableAsField=false` + исключается из `"dataParameters": "auto"` + +Объектная форма: `hidden: true`, `valueListAllowed: true`, `availableAsField: false`. + +В варианте настроек `"dataParameters": "auto"` автоматически генерирует записи для всех не-hidden параметров с `userSettingID`. ### Фильтры — shorthand @@ -235,15 +242,32 @@ powershell.exe -NoProfile -File .claude/skills/skd-compile/scripts/skd-compile.p Raw XML (`"template": "<...>"`) остаётся как fallback. Детект: если есть `rows` — DSL, иначе — raw. +### Расшифровка (drilldown) в параметрах шаблона + +Ключ `drilldown` в параметре шаблона автоматически генерирует `DetailsAreaTemplateParameter` и привязку `Расшифровка` в appearance ячеек: + +```json +"parameters": [ + { "name": "Сырье", "expression": "ПоступлениеСырья", "drilldown": "ПоступлениеСырья" } +] +``` + +Генерирует: `ExpressionAreaTemplateParameter` (обычный) + `DetailsAreaTemplateParameter` с именем `Расшифровка_ПоступлениеСырья`, `fieldExpression` по полю `ИмяРесурса`, `mainAction=DrillDown`. Ячейки `{Сырье}` автоматически получают appearance `Расшифровка = Расшифровка_ПоступлениеСырья`. + ### Привязки макетов к группировкам ```json "groupTemplates": [ - { "groupField": "Счет", "templateType": "GroupHeader", "template": "Макет1" }, - { "groupField": "Счет", "templateType": "Header", "template": "Макет2" } + { "groupName": "ДанныеОтчета", "templateType": "GroupHeader", "template": "Макет1" }, + { "groupField": "Счет", "templateType": "Header", "template": "Макет2" }, + { "groupField": "Счет", "templateType": "OverallHeader", "template": "Макет3" } ] ``` +`groupField` — привязка к полю группировки, `groupName` — к именованной группировке в структуре варианта. + +`templateType`: `Header` (строки данных) → ``, `OverallHeader` (итоги) → ``, `GroupHeader` (шапка) → ``. + ## Примеры ### Минимальный diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1 index 528c5916..b89ec461 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.ps1 +++ b/.claude/skills/skd-compile/scripts/skd-compile.ps1 @@ -1,4 +1,4 @@ -# skd-compile v1.3 — Compile 1C DCS from JSON +# skd-compile v1.4 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -322,6 +322,18 @@ function Parse-ParamShorthand { $s = $s -replace '\s*@autoDates', '' } + # Extract @valueList flag + if ($s -match '@valueList') { + $result.valueListAllowed = $true + $s = $s -replace '\s*@valueList', '' + } + + # Extract @hidden flag + if ($s -match '@hidden') { + $result.hidden = $true + $s = $s -replace '\s*@hidden', '' + } + # Split "Name: Type = Value" if ($s -match '^([^:]+):\s*(\S+)(\s*=\s*(.+))?$') { $result.name = $Matches[1].Trim() @@ -746,8 +758,9 @@ function Emit-CalcFields { if ($cf -is [string]) { $parsed = Parse-CalcShorthand $cf } else { + $dp = if ($cf.dataPath) { "$($cf.dataPath)" } else { "$($cf.field)" } $parsed = @{ - dataPath = "$($cf.dataPath)" + dataPath = $dp expression = "$($cf.expression)" } } @@ -854,11 +867,19 @@ function Emit-SingleParam { X "`t`t$(Esc-Xml $parsed.expression)" } + # Hidden implies availableAsField=false + if ($parsed.hidden -eq $true) { $parsed.availableAsField = $false } + # AvailableAsField if ($parsed.availableAsField -eq $false) { X "`t`tfalse" } + # ValueListAllowed + if ($parsed.valueListAllowed -eq $true) { + X "`t`ttrue" + } + # Use if ($p -isnot [string] -and $p.use) { X "`t`t$(Esc-Xml "$($p.use)")" @@ -867,6 +888,8 @@ function Emit-SingleParam { X "`t" } +$script:allParams = @() + function Emit-Parameters { if (-not $def.parameters) { return } foreach ($p in $def.parameters) { @@ -881,11 +904,16 @@ function Emit-Parameters { } if ($p.expression) { $parsed.expression = "$($p.expression)" } if ($p.availableAsField -eq $false) { $parsed.availableAsField = $false } + if ($p.valueListAllowed -eq $true) { $parsed.valueListAllowed = $true } + if ($p.hidden -eq $true) { $parsed.hidden = $true } if ($p.autoDates -eq $true) { $parsed.autoDates = $true } } Emit-SingleParam -p $p -parsed $parsed + # Track parameter for auto dataParameters + $script:allParams += @{ name = $parsed.name; hidden = [bool]$parsed.hidden; type = "$($parsed.type)"; value = $parsed.value } + # @autoDates: auto-generate ДатаНачала and ДатаОкончания if ($parsed.autoDates) { $paramName = $parsed.name @@ -1005,7 +1033,7 @@ function Emit-ColorValue { } function Emit-CellAppearance { - param($style, [double]$width = 0, [bool]$vMerge = $false, [double]$minHeight = 0) + param($style, [double]$width = 0, [bool]$vMerge = $false, [double]$minHeight = 0, $extraItems = @()) $ind = "`t`t`t`t`t" X "`t`t`t`t" # Background color @@ -1098,6 +1126,8 @@ function Emit-CellAppearance { X "$ind`ttrue" X "$ind" } + # Extra appearance items (e.g. drilldown Расшифровка) + foreach ($ei in $extraItems) { X $ei } X "`t`t`t`t" } @@ -1128,6 +1158,14 @@ function Emit-AreaTemplateDSL { } if (-not $vMerge.ContainsKey(0)) { $vMerge[0] = @{} } + # Build drilldown map: param_name -> drilldown_value + $drilldownMap = @{} + if ($t.parameters) { + foreach ($tp in $t.parameters) { + if ($tp.drilldown) { $drilldownMap["$($tp.name)"] = "$($tp.drilldown)" } + } + } + X "`t" @@ -1222,11 +1296,20 @@ function Emit-Templates { function Emit-GroupTemplates { if (-not $def.groupTemplates) { return } foreach ($gt in $def.groupTemplates) { - X "`t" - X "`t`t$(Esc-Xml "$($gt.groupField)")" - X "`t`t$(Esc-Xml "$($gt.templateType)")" + $ttype = if ($gt.templateType) { "$($gt.templateType)" } else { "Header" } + $isHeader = ($ttype -eq 'GroupHeader') + $tag = if ($isHeader) { 'groupHeaderTemplate' } else { 'groupTemplate' } + $xmlTType = if ($isHeader) { 'Header' } else { $ttype } + + X "`t<$tag>" + if ($gt.groupName) { + X "`t`t$(Esc-Xml "$($gt.groupName)")" + } elseif ($gt.groupField) { + X "`t`t$(Esc-Xml "$($gt.groupField)")" + } + X "`t`t$(Esc-Xml $xmlTType)" X "`t`t" - X "`t" + X "`t" } } @@ -1868,7 +1951,22 @@ function Emit-SettingsVariants { } # DataParameters - if ($s.dataParameters) { + if ($s.dataParameters -eq 'auto') { + # Auto-generate dataParameters for all non-hidden params + $autoDP = @() + foreach ($ap in $script:allParams) { + if (-not $ap.hidden) { + $dpItem = New-Object PSObject + $dpItem | Add-Member -NotePropertyName "parameter" -NotePropertyValue $ap.name + $dpItem | Add-Member -NotePropertyName "use" -NotePropertyValue $false + $dpItem | Add-Member -NotePropertyName "userSettingID" -NotePropertyValue "auto" + $autoDP += $dpItem + } + } + if ($autoDP.Count -gt 0) { + Emit-DataParameters -items $autoDP -indent "`t`t`t" + } + } elseif ($s.dataParameters) { Emit-DataParameters -items $s.dataParameters -indent "`t`t`t" } diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py index 33198a0d..35a86414 100644 --- a/.claude/skills/skd-compile/scripts/skd-compile.py +++ b/.claude/skills/skd-compile/scripts/skd-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# skd-compile v1.3 — Compile 1C DCS from JSON +# skd-compile v1.4 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -233,6 +233,16 @@ def parse_param_shorthand(s): result['autoDates'] = True s = re.sub(r'\s*@autoDates', '', s) + # Extract @valueList flag + if '@valueList' in s: + result['valueListAllowed'] = True + s = re.sub(r'\s*@valueList', '', s) + + # Extract @hidden flag + if '@hidden' in s: + result['hidden'] = True + s = re.sub(r'\s*@hidden', '', s) + # Split "Name: Type = Value" m = re.match(r'^([^:]+):\s*(\S+)(\s*=\s*(.+))?$', s) if m: @@ -598,7 +608,7 @@ def emit_calc_fields(lines, defn): is_obj = False else: parsed = { - 'dataPath': str(cf.get('dataPath', '')), + 'dataPath': str(cf.get('dataPath') or cf.get('field', '')), 'expression': str(cf.get('expression', '')), } is_obj = True @@ -724,10 +734,18 @@ def emit_single_param(lines, p, parsed): if parsed.get('expression'): lines.append(f'\t\t{esc_xml(parsed["expression"])}') + # Hidden implies availableAsField=false + if parsed.get('hidden'): + parsed['availableAsField'] = False + # AvailableAsField if parsed.get('availableAsField') is False: lines.append('\t\tfalse') + # ValueListAllowed + if parsed.get('valueListAllowed'): + lines.append('\t\ttrue') + # Use if p is not None and not isinstance(p, str) and p.get('use'): lines.append(f'\t\t{esc_xml(str(p["use"]))}') @@ -735,7 +753,12 @@ def emit_single_param(lines, p, parsed): lines.append('\t') +_all_params = [] + + def emit_parameters(lines, defn): + global _all_params + _all_params = [] if not defn.get('parameters'): return for p in defn['parameters']: @@ -752,11 +775,23 @@ def emit_parameters(lines, defn): parsed['expression'] = str(p['expression']) if p.get('availableAsField') is False: parsed['availableAsField'] = False + if p.get('valueListAllowed') is True: + parsed['valueListAllowed'] = True + if p.get('hidden') is True: + parsed['hidden'] = True if p.get('autoDates') is True: parsed['autoDates'] = True emit_single_param(lines, p, parsed) + # Track parameter for auto dataParameters + _all_params.append({ + 'name': parsed['name'], + 'hidden': bool(parsed.get('hidden')), + 'type': parsed.get('type', ''), + 'value': parsed.get('value'), + }) + # @autoDates: auto-generate ДатаНачала and ДатаОкончания if parsed.get('autoDates'): param_name = parsed['name'] @@ -827,7 +862,7 @@ def _emit_color_value(lines, color, indent): lines.append(f'{indent}{esc_xml(color)}') -def _emit_cell_appearance(lines, style, width=0, v_merge=False, min_height=0): +def _emit_cell_appearance(lines, style, width=0, v_merge=False, min_height=0, extra_items=None): ind = '\t\t\t\t\t' lines.append('\t\t\t\t') # Background color @@ -909,6 +944,10 @@ def _emit_cell_appearance(lines, style, width=0, v_merge=False, min_height=0): lines.append(f'{ind}\t\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c\u041f\u043e\u0412\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438') lines.append(f'{ind}\ttrue') lines.append(f'{ind}') + # Extra appearance items (e.g. drilldown) + if extra_items: + for ei in extra_items: + lines.append(ei) lines.append('\t\t\t\t') @@ -935,6 +974,13 @@ def _emit_area_template_dsl(lines, t): if 0 not in v_merge: v_merge[0] = {} + # Build drilldown map: param_name -> drilldown_value + drilldown_map = {} + if t.get('parameters'): + for tp in t['parameters']: + if tp.get('drilldown'): + drilldown_map[str(tp['name'])] = str(tp['drilldown']) + lines.append('\t') @@ -1016,11 +1093,19 @@ def emit_group_templates(lines, defn): if not defn.get('groupTemplates'): return for gt in defn['groupTemplates']: - lines.append('\t') - lines.append(f'\t\t{esc_xml(str(gt["groupField"]))}') - lines.append(f'\t\t{esc_xml(str(gt["templateType"]))}') + ttype = str(gt.get('templateType', '')) or 'Header' + is_header = (ttype == 'GroupHeader') + tag = 'groupHeaderTemplate' if is_header else 'groupTemplate' + xml_ttype = 'Header' if is_header else ttype + + lines.append(f'\t<{tag}>') + if gt.get('groupName'): + lines.append(f'\t\t{esc_xml(str(gt["groupName"]))}') + elif gt.get('groupField'): + lines.append(f'\t\t{esc_xml(str(gt["groupField"]))}') + lines.append(f'\t\t{esc_xml(xml_ttype)}') lines.append(f'\t\t') - lines.append('\t') + lines.append(f'\t') # === Settings Variants === @@ -1540,7 +1625,19 @@ def emit_settings_variants(lines, defn): emit_output_parameters(lines, s['outputParameters'], '\t\t\t') # DataParameters - if s.get('dataParameters'): + if s.get('dataParameters') == 'auto': + # Auto-generate dataParameters for all non-hidden params + auto_dp = [] + for ap in _all_params: + if not ap['hidden']: + auto_dp.append({ + 'parameter': ap['name'], + 'use': False, + 'userSettingID': 'auto', + }) + if auto_dp: + emit_data_parameters(lines, auto_dp, '\t\t\t') + elif s.get('dataParameters'): emit_data_parameters(lines, s['dataParameters'], '\t\t\t') # Structure (supports string shorthand) diff --git a/.claude/skills/template-add/scripts/add-template.ps1 b/.claude/skills/template-add/scripts/add-template.ps1 index 614403e1..c6253e5b 100644 --- a/.claude/skills/template-add/scripts/add-template.ps1 +++ b/.claude/skills/template-add/scripts/add-template.ps1 @@ -1,4 +1,4 @@ -# template-add v1.1 — Add template to 1C object +# template-add v1.2 — Add template to 1C object # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -37,7 +37,7 @@ $tmpl = $typeMap[$TemplateType] $rootXmlPath = Join-Path $SrcDir "$ObjectName.xml" if (-not (Test-Path $rootXmlPath)) { - Write-Error "Корневой файл обработки не найден: $rootXmlPath" + Write-Error "Корневой файл объекта не найден: $rootXmlPath`nОжидается: //.xml`nПодсказка: SrcDir должен указывать на папку типа объектов (например Reports), а не на корень конфигурации" exit 1 } diff --git a/.claude/skills/template-add/scripts/add-template.py b/.claude/skills/template-add/scripts/add-template.py index 096981cf..8a2f61d2 100644 --- a/.claude/skills/template-add/scripts/add-template.py +++ b/.claude/skills/template-add/scripts/add-template.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# add-template v1.0 — Add template to 1C object +# add-template v1.2 — Add template to 1C object # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -63,7 +63,9 @@ def main(): root_xml_path = os.path.join(src_dir, f"{object_name}.xml") if not os.path.exists(root_xml_path): - print(f"Корневой файл обработки не найден: {root_xml_path}", file=sys.stderr) + print(f"Корневой файл объекта не найден: {root_xml_path}", file=sys.stderr) + print(f"Ожидается: //.xml", file=sys.stderr) + print(f"Подсказка: SrcDir должен указывать на папку типа объектов (например Reports), а не на корень конфигурации", file=sys.stderr) sys.exit(1) processor_dir = os.path.join(src_dir, object_name)