diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1 index b259a258..d661686f 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.135 — Compile 1C managed form from JSON or object metadata +# form-compile v1.136 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$JsonPath, @@ -1577,6 +1577,19 @@ function Emit-MLText { X "$indent" } +# и подобные DCS-подписи: платформа пишет плоскую строку как +# xsi:type="xs:string" (скаляр; корпус 26), мультиязычный текст — как xsi:type="v8:LocalStringType" +# (7). Декомпилятор различает (Get-PresText: строка ИЛИ объект {ru,en}). +function Emit-USPresentation { + param($val, [string]$tag, [string]$indent) + if ($null -eq $val) { return } + if ($val -is [string]) { + X "$indent<$tag xsi:type=`"xs:string`">$(Esc-Xml $val)" + } else { + Emit-MLText -tag $tag -text $val -indent $indent -xsiType "v8:LocalStringType" + } +} + # Детектор «настоящей» inline-разметки форматированного текста (1С: ///… # и закрывающий ). Плейсхолдеры вида <не заполнен> НЕ срабатывают (нет известного тега/). # ВАЖНО: regex должен быть идентичен в form-decompile (иначе гибрид-раундтрип поедет). @@ -1689,7 +1702,7 @@ function Emit-FilterItem { $guid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" } X "$indent`t$(Esc-Xml $guid)" } - if ($item.userSettingPresentation) { Emit-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t" } + if ($item.userSettingPresentation) { Emit-USPresentation -val $item.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" return } @@ -1770,7 +1783,7 @@ function Emit-FilterItem { $uid = if ("$($item.userSettingID)" -eq "auto") { New-Guid-String } else { "$($item.userSettingID)" } X "$indent`t$(Esc-Xml $uid)" } - if ($item.userSettingPresentation) { Emit-MLText -tag "dcsset:userSettingPresentation" -text $item.userSettingPresentation -indent "$indent`t" } + if ($item.userSettingPresentation) { Emit-USPresentation -val $item.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t" } X "$indent" } @@ -1971,7 +1984,7 @@ function Emit-ConditionalAppearance { $uid = if ("$($ca.userSettingID)" -eq "auto") { New-Guid-String } else { "$($ca.userSettingID)" } X "$indent`t`t$(Esc-Xml $uid)" } - if ($ca.userSettingPresentation) { Emit-MLText -tag "dcsset:userSettingPresentation" -text $ca.userSettingPresentation -indent "$indent`t`t" } + if ($ca.userSettingPresentation) { Emit-USPresentation -val $ca.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t`t" } if ($ca.useInDontUse -and $ca.useInDontUse.Count -gt 0) { $useInOrder = @('group','hierarchicalGroup','overall','fieldsHeader','header','parameters','filter','resourceFieldsHeader','overallHeader','overallResourceFieldsHeader') $set = @{} @@ -5175,7 +5188,7 @@ function Emit-DataParameters { } if ($dp.viewMode) { X "$indent`t`t$(Esc-Xml "$($dp.viewMode)")" } if ($dp.userSettingID) { $uid = if ("$($dp.userSettingID)" -eq "auto") { New-Guid-String } else { "$($dp.userSettingID)" }; X "$indent`t`t$(Esc-Xml $uid)" } - if ($dp.userSettingPresentation) { Emit-MLText -tag "dcsset:userSettingPresentation" -text $dp.userSettingPresentation -indent "$indent`t`t" } + if ($dp.userSettingPresentation) { Emit-USPresentation -val $dp.userSettingPresentation -tag "dcsset:userSettingPresentation" -indent "$indent`t`t" } X "$indent`t" } if ($null -ne $blockViewMode) { X "$indent`t$(Esc-Xml "$blockViewMode")" } diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py index 807a3308..4494b029 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.135 — Compile 1C managed form from JSON or object metadata +# form-compile v1.136 — Compile 1C managed form from JSON or object metadata # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import copy @@ -1310,6 +1310,16 @@ def emit_mltext(lines, indent, tag, text, xsi_type=None): lines.append(f"{indent}") +def emit_us_presentation(lines, indent, tag, val): + # : плоская строка → xsi:type="xs:string"; мультиязычный → v8:LocalStringType + if val is None: + return + if isinstance(val, str): + lines.append(f'{indent}<{tag} xsi:type="xs:string">{esc_xml(val)}') + else: + emit_mltext(lines, indent, tag, val, xsi_type='v8:LocalStringType') + + # Каноничные GUID пустых контейнеров ListSettings (умолчание платформы, ~90% форм). CANON_FILTER_ID = 'dfcece9d-5077-440b-b6b3-45a5cb4538eb' CANON_ORDER_ID = '88619765-ccb3-46c6-ac52-38e9c992ebd4' @@ -1446,7 +1456,7 @@ def emit_filter_item(lines, item, indent): guid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID']) lines.append(f'{indent}\t{esc_xml(guid)}') if item.get('userSettingPresentation'): - emit_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) + emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) lines.append(f'{indent}') return @@ -1506,7 +1516,7 @@ def emit_filter_item(lines, item, indent): uid = new_uuid() if str(item['userSettingID']) == 'auto' else str(item['userSettingID']) lines.append(f'{indent}\t{esc_xml(uid)}') if item.get('userSettingPresentation'): - emit_mltext(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) + emit_us_presentation(lines, f'{indent}\t', 'dcsset:userSettingPresentation', item['userSettingPresentation']) lines.append(f'{indent}') @@ -1721,7 +1731,7 @@ def emit_conditional_appearance(lines, items, indent, block_view_mode=None, bloc uid = new_uuid() if str(ca['userSettingID']) == 'auto' else str(ca['userSettingID']) lines.append(f'{indent}\t\t{esc_xml(uid)}') if ca.get('userSettingPresentation'): - emit_mltext(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', ca['userSettingPresentation']) + emit_us_presentation(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', ca['userSettingPresentation']) if ca.get('useInDontUse') and len(ca['useInDontUse']) > 0: use_in_order = ['group', 'hierarchicalGroup', 'overall', 'fieldsHeader', 'header', 'parameters', 'filter', 'resourceFieldsHeader', 'overallHeader', @@ -4922,7 +4932,7 @@ def emit_data_parameters(lines, items, indent, block_view_mode=None): uid = new_uuid() if str(dp['userSettingID']) == 'auto' else str(dp['userSettingID']) lines.append(f'{indent}\t\t{esc_xml(uid)}') if dp.get('userSettingPresentation'): - emit_mltext(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', dp['userSettingPresentation']) + emit_us_presentation(lines, f'{indent}\t\t', 'dcsset:userSettingPresentation', dp['userSettingPresentation']) lines.append(f'{indent}\t') if block_view_mode is not None: lines.append(f'{indent}\t{esc_xml(str(block_view_mode))}') diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1 index 0ee93982..f597d911 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.108 — Decompile 1C managed Form.xml to JSON DSL (draft) +# form-decompile v0.109 — Decompile 1C managed Form.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью. param( @@ -1259,7 +1259,7 @@ function Build-FormDataParameters { if ($use -eq 'false') { $obj['use'] = $false } if ($usidN) { $obj['userSettingID'] = 'auto' } if ($vmN) { $obj['viewMode'] = $vmN.InnerText } - if ($uspN) { $usp = Get-MLText $uspN; if ($null -ne $usp) { $obj['userSettingPresentation'] = $usp } } + if ($uspN) { $usp = Get-PresText $uspN; if ($null -ne $usp) { $obj['userSettingPresentation'] = $usp } } $entries += $obj } else { $s = $pn; if ($use -eq 'false') { $s += ' @off' } diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md index 9a512759..a84b5baa 100644 --- a/docs/form-dsl-spec.md +++ b/docs/form-dsl-spec.md @@ -992,7 +992,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и ``` - **order** — строка `"Поле"` (asc) / `"Поле desc"` (синонимы `убыв`/`desc`, `возр`/`asc`) / `"Auto"`, либо объект `{ field, direction?, use?, viewMode? }`. -- **filter** — shorthand `"Поле оператор значение @флаги"` (`@off`, `@user`, `@quickAccess`, `@normal`, `@inaccessible`; `_` = пусто) или объект `{ field, op, value?, use?, userSettingID? }` или группа `{ group: "And"|"Or"|"Not", items: [...] }`. +- **filter** — shorthand `"Поле оператор значение @флаги"` (`@off`, `@user`, `@quickAccess`, `@normal`, `@inaccessible`; `_` = пусто) или объект `{ field, op, value?, use?, userSettingID?, userSettingPresentation? }` или группа `{ group: "And"|"Or"|"Not", items: [...] }`. `userSettingPresentation` (кастомная подпись настройки) ведётся **по форме значения**: голая строка → `xsi:type="xs:string"`; объект `{ru,en}` → `v8:LocalStringType` (так же у `order`/`conditionalAppearance`/`dataParameters`). - **Операторы:** `=` `<>` `>` `>=` `<` `<=`, `in`/`notIn`, `inHierarchy`/`inListByHierarchy`, `contains`/`notContains`, `beginsWith`/`notBeginsWith`, `like`/`notLike` (подобно; `%`-шаблон в значении, напр. `"КодВалют like %/ %"`), `filled`/`notFilled`. Регистр оператора не важен; у `like`/`notLike` есть рус. синоним `подобно`/`неподобно`. - **Дата в фильтре = `StandardBeginningDate`** (так платформа хранит дату-значение почти всегда — корпус 268 vs 2 `xs:dateTime`). Формы значения (от компактной к полной): - **голая ISO-дата** `"2020-01-01T00:00:00"` (без `valueType`) → `Custom` + эта дата. Работает и в shorthand: `"ДатаЗаказа > 2020-01-01T00:00:00"`. Это дефолт даты в фильтре. diff --git a/tests/skills/cases/form-compile/dynamic-list-form.json b/tests/skills/cases/form-compile/dynamic-list-form.json index 5cac52ab..c18ad92d 100644 --- a/tests/skills/cases/form-compile/dynamic-list-form.json +++ b/tests/skills/cases/form-compile/dynamic-list-form.json @@ -21,7 +21,9 @@ "mainTable": "Catalog.Товары", "dynamicDataRead": true, "autoSaveUserSettings": false, "parameters": [ { "name": "ВыборТовара", "type": "CatalogRef.Товары", "valueListAllowed": true, "value": null } ], "order": [ "Description", "Code desc" ], - "filter": [ "Артикул = _ @off @user", "Артикул like %тест%", "Description подобно %abc%" ], + "filter": [ "Артикул = _ @off @user", "Артикул like %тест%", "Description подобно %abc%", + { "field": "Code", "op": "=", "userSettingID": "auto", "userSettingPresentation": "Код товара" }, + { "field": "Description", "op": "=", "userSettingID": "auto", "userSettingPresentation": { "ru": "Наименование", "en": "Name" } } ], "dataParameters": [ "ВыборТовара @off" ], "conditionalAppearance": [ { "filter": ["Артикул = _"], "appearance": { "ЦветТекста": "web:Red" } } ] } } diff --git a/tests/skills/cases/form-compile/snapshots/dynamic-list-form/Catalogs/Товары/Forms/ФормаСписка/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/dynamic-list-form/Catalogs/Товары/Forms/ФормаСписка/Ext/Form.xml index 9db56f5f..fd3beaeb 100644 --- a/tests/skills/cases/form-compile/snapshots/dynamic-list-form/Catalogs/Товары/Forms/ФормаСписка/Ext/Form.xml +++ b/tests/skills/cases/form-compile/snapshots/dynamic-list-form/Catalogs/Товары/Forms/ФормаСписка/Ext/Form.xml @@ -135,8 +135,29 @@ Like %abc% + + Code + Equal + UUID-003 + Код товара + + + Description + Equal + UUID-004 + + + ru + Наименование + + + en + Name + + + Normal - UUID-003 + UUID-005 @@ -154,7 +175,7 @@ Desc Normal - UUID-004 + UUID-006 @@ -173,10 +194,10 @@ Normal - UUID-005 + UUID-007 Normal - UUID-006 + UUID-008