From 4413a06c498e3e3ffb04180f9a16bae68243bd26 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Thu, 21 May 2026 17:59:19 +0300 Subject: [PATCH] =?UTF-8?q?feat(skd):=20orderExpression=20=E2=80=94=20?= =?UTF-8?q?=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=8F=20=D0=BF=D0=BE=20=D0=B2=D1=8B=D1=80?= =?UTF-8?q?=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8E=20(round-trip)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - skd-compile (ps1+py): object-form ключ orderExpression{expression,orderType,autoOrder} → - skd-decompile: читает → object form поля, без SilentDrop warning - SKILL.md skd-compile: одна строка в "Дополнительные ключи объектной формы" - docs/skd-dsl-spec.md: пример в объектной форме поля - Новый тест field-order-expression (round-trip bit-perfect) - Versions: compile v1.28→v1.29, decompile v0.10→v0.11 На сэмпле 30 ERP-отчётов: SilentDrop:orderExpression 11 → 0. --- .claude/skills/skd-compile/SKILL.md | 1 + .../skd-compile/scripts/skd-compile.ps1 | 19 ++++++- .../skills/skd-compile/scripts/skd-compile.py | 18 ++++++- .../skd-decompile/scripts/skd-decompile.ps1 | 24 ++++++--- docs/skd-dsl-spec.md | 3 +- .../skd-decompile/field-order-expression.json | 21 ++++++++ .../field-order-expression/Template.xml | 52 +++++++++++++++++++ .../field-order-expression/decompiled.json | 19 +++++++ 8 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 tests/skills/cases/skd-decompile/field-order-expression.json create mode 100644 tests/skills/cases/skd-decompile/snapshots/field-order-expression/Template.xml create mode 100644 tests/skills/cases/skd-decompile/snapshots/field-order-expression/decompiled.json diff --git a/.claude/skills/skd-compile/SKILL.md b/.claude/skills/skd-compile/SKILL.md index 2952747a..9c5791a8 100644 --- a/.claude/skills/skd-compile/SKILL.md +++ b/.claude/skills/skd-compile/SKILL.md @@ -110,6 +110,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/skd-compile.ps1" -V Дополнительные ключи объектной формы: - `"presentationExpression": "<выражение>"` — что показывать вместо значения поля. Исходное значение остаётся «под капотом» для перехода/расшифровки. - `"appearance": { "<параметр>": "<значение>" }` — оформление колонки по умолчанию (применяется во всех вариантах настроек). Ключи — параметры платформы (`ГоризонтальноеПоложение`, `МинимальнаяШирина`, `Формат`, `Текст` и т.п.). +- `"orderExpression": { "expression": "<выражение>", "orderType": "Asc"/"Desc", "autoOrder": true/false }` — сортировка поля по выражению (например `ЕстьNULL(Поле.Порядок, 10000)`). ```json { "field": "Сумма", "title": "Сумма продажи", "type": "decimal(15,2)", diff --git a/.claude/skills/skd-compile/scripts/skd-compile.ps1 b/.claude/skills/skd-compile/scripts/skd-compile.ps1 index 088211e2..72af1e44 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.28 — Compile 1C DCS from JSON +# skd-compile v1.29 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -767,6 +767,10 @@ function Emit-Field { if ($fieldDef.attrRestrict) { $f["attrRestrict"] = @($fieldDef.attrRestrict) } + # orderExpression — {expression, orderType, autoOrder} + if ($fieldDef.orderExpression) { + $f["orderExpression"] = $fieldDef.orderExpression + } } X "$indent" @@ -829,6 +833,19 @@ function Emit-Field { X "$indent`t" } + # OrderExpression — после role, до valueType + if ($f["orderExpression"]) { + $oe = $f["orderExpression"] + $expr = if ($oe.expression) { "$($oe.expression)" } else { '' } + $oType = if ($oe.orderType) { "$($oe.orderType)" } else { 'Asc' } + $autoOrder = if ($null -ne $oe.autoOrder) { $(if ($oe.autoOrder) { 'true' } else { 'false' }) } else { 'false' } + X "$indent`t" + X "$indent`t`t$(Esc-Xml $expr)" + X "$indent`t`t$oType" + X "$indent`t`t$autoOrder" + X "$indent`t" + } + # ValueType if ($f.type) { X "$indent`t" diff --git a/.claude/skills/skd-compile/scripts/skd-compile.py b/.claude/skills/skd-compile/scripts/skd-compile.py index 744ce9bc..0551f9f7 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.28 — Compile 1C DCS from JSON +# skd-compile v1.29 — Compile 1C DCS from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -584,6 +584,9 @@ def emit_field(lines, field_def, indent): # attrRestrict if field_def.get('attrRestrict'): f['attrRestrict'] = list(field_def['attrRestrict']) + # orderExpression — {expression, orderType, autoOrder} + if field_def.get('orderExpression'): + f['orderExpression'] = field_def['orderExpression'] lines.append(f'{indent}') lines.append(f'{indent}\t{esc_xml(f["dataPath"])}') @@ -633,6 +636,19 @@ def emit_field(lines, field_def, indent): lines.append(f'{indent}\t\t{esc_xml(str(v))}') lines.append(f'{indent}\t') + # OrderExpression — после role, до valueType + if f.get('orderExpression'): + oe = f['orderExpression'] + expr = str(oe.get('expression', '')) + o_type = str(oe.get('orderType', 'Asc')) + auto = oe.get('autoOrder', False) + auto_str = 'true' if auto else 'false' + lines.append(f'{indent}\t') + lines.append(f'{indent}\t\t{esc_xml(expr)}') + lines.append(f'{indent}\t\t{o_type}') + lines.append(f'{indent}\t\t{auto_str}') + lines.append(f'{indent}\t') + # ValueType if f.get('type'): lines.append(f'{indent}\t') diff --git a/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 b/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 index 5768ad83..dab619ba 100644 --- a/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 +++ b/.claude/skills/skd-decompile/scripts/skd-decompile.ps1 @@ -1,4 +1,4 @@ -# skd-decompile v0.10 — Decompile 1C DCS Template.xml to JSON DSL (draft) +# skd-decompile v0.11 — Decompile 1C DCS Template.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -345,12 +345,19 @@ function Build-Field { param($fieldNode, [string]$loc) # Silent-drop detection (non-blocking warnings only) Check-InputParameters -parentNode $fieldNode -loc $loc - $orderExpr = $fieldNode.SelectSingleNode("r:orderExpression", $ns) - if ($orderExpr) { - $expr = Get-Text $orderExpr "dcscom:expression" - if ($expr) { - $null = Add-Warning -kind 'SilentDrop:orderExpression' -loc "$loc/orderExpression" -detail "Поле имеет orderExpression='$expr' — не воспроизводится в DSL" - } + # orderExpression теперь поддерживается в DSL — читается ниже в needsObject + $orderExprNode = $fieldNode.SelectSingleNode("r:orderExpression", $ns) + $orderExpression = $null + if ($orderExprNode) { + $oeExpr = Get-Text $orderExprNode "dcscom:expression" + $oeType = Get-Text $orderExprNode "dcscom:orderType" + $oeAuto = Get-Text $orderExprNode "dcscom:autoOrder" + $orderExpression = [ordered]@{} + if ($oeExpr) { $orderExpression['expression'] = $oeExpr } + if ($oeType) { $orderExpression['orderType'] = $oeType } + # autoOrder=false — это дефолт; emit'им только если true (или явно записан false) + if ($oeAuto -eq 'true') { $orderExpression['autoOrder'] = $true } + elseif ($oeAuto -eq 'false') { $orderExpression['autoOrder'] = $false } } $dataPath = Get-Text $fieldNode "r:dataPath" $fieldName = Get-Text $fieldNode "r:field" @@ -369,7 +376,7 @@ function Build-Field { # Можно ли роль положить в shorthand-строку? $roleInString = $roleRendered -and $roleRendered.isString - $needsObject = $title -or $appearance -or $presExpr -or ($typeShort -is [array]) -or ($roleRendered -and -not $roleInString) + $needsObject = $title -or $appearance -or $presExpr -or ($typeShort -is [array]) -or ($roleRendered -and -not $roleInString) -or $orderExpression if (-not $needsObject) { # shorthand: "Name: type @role K=V #restrict" @@ -397,6 +404,7 @@ function Build-Field { if ($title) { $obj['title'] = $title } if ($typeShort) { $obj['type'] = $typeShort } if ($roleRendered) { $obj['role'] = $roleRendered.value } + if ($orderExpression) { $obj['orderExpression'] = $orderExpression } if ($restrictTokens) { $obj['restrict'] = ($restrictTokens | ForEach-Object { $_ -replace '^#','' }) } if ($presExpr) { $obj['presentationExpression'] = $presExpr } if ($appearance) { $obj['appearance'] = $appearance } diff --git a/docs/skd-dsl-spec.md b/docs/skd-dsl-spec.md index 9dd96e03..4867c177 100644 --- a/docs/skd-dsl-spec.md +++ b/docs/skd-dsl-spec.md @@ -141,7 +141,8 @@ "restrict": ["noFilter", "noGroup"], "attrRestrict": ["noFilter"], "appearance": { "Формат": "ЧДЦ=2" }, - "presentationExpression": "Формат(Сумма, \"ЧДЦ=2\")" + "presentationExpression": "Формат(Сумма, \"ЧДЦ=2\")", + "orderExpression": { "expression": "ЕстьNULL(Поле.Порядок, 10000)", "orderType": "Asc", "autoOrder": false } } ``` diff --git a/tests/skills/cases/skd-decompile/field-order-expression.json b/tests/skills/cases/skd-decompile/field-order-expression.json new file mode 100644 index 00000000..fa4bf6c8 --- /dev/null +++ b/tests/skills/cases/skd-decompile/field-order-expression.json @@ -0,0 +1,21 @@ +{ + "name": "Поле: orderExpression (сортировка по выражению)", + "preRun": [ + { + "script": "skd-compile/scripts/skd-compile", + "input": { + "dataSets": [{ + "name": "Тест", + "query": "ВЫБРАТЬ * ИЗ Справочник.ВидыРасчета", + "fields": [ + { "field": "ВидРасчета", "type": "CatalogRef.ВидыРасчета", "orderExpression": { "expression": "ЕстьNULL(ВидРасчета.Порядок, 10000)", "orderType": "Asc", "autoOrder": false } } + ] + }] + }, + "args": { "-DefinitionFile": "{inputFile}", "-OutputPath": "Template.xml" }, + "cwd": "{workDir}" + } + ], + "params": { "templatePath": "Template.xml" }, + "outputPath": "decompiled.json" +} diff --git a/tests/skills/cases/skd-decompile/snapshots/field-order-expression/Template.xml b/tests/skills/cases/skd-decompile/snapshots/field-order-expression/Template.xml new file mode 100644 index 00000000..ca22d46a --- /dev/null +++ b/tests/skills/cases/skd-decompile/snapshots/field-order-expression/Template.xml @@ -0,0 +1,52 @@ + + + + ИсточникДанных1 + Local + + + Тест + + ВидРасчета + ВидРасчета + + ЕстьNULL(ВидРасчета.Порядок, 10000) + Asc + false + + + d5p1:CatalogRef.ВидыРасчета + + + ИсточникДанных1 + ВЫБРАТЬ * ИЗ Справочник.ВидыРасчета + + + Основной + + + ru + Основной + + + + + + + + + + + + + + + + diff --git a/tests/skills/cases/skd-decompile/snapshots/field-order-expression/decompiled.json b/tests/skills/cases/skd-decompile/snapshots/field-order-expression/decompiled.json new file mode 100644 index 00000000..6e998416 --- /dev/null +++ b/tests/skills/cases/skd-decompile/snapshots/field-order-expression/decompiled.json @@ -0,0 +1,19 @@ +{ + "dataSets": [ + { + "name": "Тест", + "query": "ВЫБРАТЬ * ИЗ Справочник.ВидыРасчета", + "fields": [ + { + "field": "ВидРасчета", + "type": "CatalogRef.ВидыРасчета", + "orderExpression": { + "expression": "ЕстьNULL(ВидРасчета.Порядок, 10000)", + "orderType": "Asc", + "autoOrder": false + } + } + ] + } + ] +} \ No newline at end of file