diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 16259fec..102871d7 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.104 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.105 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -3048,6 +3048,27 @@ function Emit-PlannerColor {
param([string]$tag, $o, [string]$key, [string]$ind)
X "$ind$(Esc-Xml "$(PL-Get $o $key 'auto')")"
}
+# /… — пустое → самозакрывающийся тег (как в выгрузке платформы).
+function Emit-PlannerText {
+ param([string]$tag, $v, [string]$ind)
+ if ([string]::IsNullOrEmpty("$v")) { X "$ind" }
+ else { X "$ind$(Esc-Xml "$v")" }
+}
+# Признак ссылочного значения (объект разреза/элемент-ссылка) → xsi:type="xr:DesignTimeRef";
+# иначе xs:string. Покрывает англ. (Enum.X.EnumValue.Y) и рус. (Справочник.X) метатипы.
+function Test-PlannerRef {
+ param([string]$v)
+ return ($v -match '^(Enum|Catalog|Document|ChartOfAccounts|ChartOfCalculationTypes|ChartOfCharacteristicTypes|ExchangePlan|BusinessProcess|Task)\.' -or `
+ $v -match '\.EnumValue\.' -or $v -match 'EmptyRef$' -or `
+ $v -match '^(Перечисление|Справочник|Документ|ПланСчетов|ПланВидовХарактеристик|ПланВидовРасчета|ПланОбмена|БизнесПроцесс|Задача)\.')
+}
+# — nil (нет значения) / xr:DesignTimeRef (ссылка) / xs:string (строка/прочее).
+function Emit-PlannerValue {
+ param($v, [string]$ind)
+ if ($null -eq $v -or "$v" -eq '') { X "$ind"; return }
+ $t = if (Test-PlannerRef "$v") { 'xr:DesignTimeRef' } else { 'xs:string' }
+ X "$ind$(Esc-Xml "$v")"
+}
function Emit-PlannerFont {
param($o, [string]$ind)
$f = PL-Get $o 'font' $null
@@ -3114,12 +3135,9 @@ function Emit-PlannerItem {
param($it, [string]$ind)
X "$ind"
$ii = "$ind`t"
- $val = PL-Get $it 'value' $null
- if ($null -eq $val) { X "$ii" }
- else { X "$ii$(Esc-Xml "$val")" }
- X "$ii$(Esc-Xml "$(PL-Get $it 'text' '')")"
- $tt = PL-Get $it 'tooltip' ''
- if ("$tt" -eq '') { X "$ii" } else { X "$ii$(Esc-Xml "$tt")" }
+ Emit-PlannerValue (PL-Get $it 'value' $null) $ii
+ Emit-PlannerText 'text' (PL-Get $it 'text' '') $ii
+ Emit-PlannerText 'tooltip' (PL-Get $it 'tooltip' '') $ii
X "$ii$(PL-Get $it 'begin' '0001-01-01T00:00:00')"
X "$ii$(PL-Get $it 'end' '0001-01-01T00:00:00')"
Emit-PlannerColor 'borderColor' $it 'borderColor' $ii
@@ -3137,11 +3155,45 @@ function Emit-PlannerItem {
X "$ii$(Esc-Xml "$(PL-Get $it 'editMode' 'EnableEdit')")"
X "$ind"
}
+# Элемент измерения ( внутри ) — рекурсивен: может нести вложенные
+# элементы (UI: колонка «Элементы» у элемента). Порядок: value, text, цвета, font,
+# вложенные элементы, showOnlySubordinatesAreas, textFormatted.
+function Emit-PlannerDimElement {
+ param($el, [string]$ind)
+ X "$ind"
+ $ii = "$ind`t"
+ Emit-PlannerValue (PL-Get $el 'value' $null) $ii
+ Emit-PlannerText 'text' (PL-Get $el 'text' '') $ii
+ Emit-PlannerColor 'borderColor' $el 'borderColor' $ii
+ Emit-PlannerColor 'backColor' $el 'backColor' $ii
+ Emit-PlannerColor 'textColor' $el 'textColor' $ii
+ Emit-PlannerFont $el $ii
+ foreach ($sub in @(PL-Get $el 'elements' @())) { Emit-PlannerDimElement $sub $ii }
+ X "$ii$(PL-Bool (PL-Get $el 'showOnlySubordinatesAreas' $true))"
+ X "$ii$(PL-Bool (PL-Get $el 'textFormatted' $false))"
+ X "$ind"
+}
+# Измерение планировщика () — объект разреза + его элементы.
+function Emit-PlannerDimension {
+ param($d, [string]$ind)
+ X "$ind"
+ $di = "$ind`t"
+ Emit-PlannerValue (PL-Get $d 'value' $null) $di
+ Emit-PlannerText 'text' (PL-Get $d 'text' '') $di
+ Emit-PlannerColor 'borderColor' $d 'borderColor' $di
+ Emit-PlannerColor 'backColor' $d 'backColor' $di
+ Emit-PlannerColor 'textColor' $d 'textColor' $di
+ Emit-PlannerFont $d $di
+ foreach ($el in @(PL-Get $d 'elements' @())) { Emit-PlannerDimElement $el $di }
+ X "$di$(PL-Bool (PL-Get $d 'textFormatted' $false))"
+ X "$ind"
+}
function Emit-PlannerSettings {
param($pl, [string]$ind)
X "$ind"
$si = "$ind`t"
foreach ($it in @(PL-Get $pl 'items' @())) { Emit-PlannerItem $it $si }
+ foreach ($d in @(PL-Get $pl 'dimensions' @())) { Emit-PlannerDimension $d $si }
Emit-PlannerColor 'borderColor' $pl 'borderColor' $si
Emit-PlannerColor 'backColor' $pl 'backColor' $si
Emit-PlannerColor 'textColor' $pl 'textColor' $si
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index d2089903..f830721a 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.104 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.105 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -2740,6 +2740,31 @@ def emit_planner_color(lines, tag, o, key, ind):
lines.append(f'{ind}{esc_xml(str(_pl_get(o, key, "auto")))}')
+def emit_planner_text(lines, tag, v, ind):
+ if v is None or str(v) == '':
+ lines.append(f'{ind}')
+ else:
+ lines.append(f'{ind}{esc_xml(str(v))}')
+
+
+_PLANNER_REF_RE = re.compile(
+ r'^(Enum|Catalog|Document|ChartOfAccounts|ChartOfCalculationTypes|ChartOfCharacteristicTypes|ExchangePlan|BusinessProcess|Task)\.'
+ r'|\.EnumValue\.|EmptyRef$'
+ r'|^(Перечисление|Справочник|Документ|ПланСчетов|ПланВидовХарактеристик|ПланВидовРасчета|ПланОбмена|БизнесПроцесс|Задача)\.')
+
+
+def test_planner_ref(v):
+ return bool(_PLANNER_REF_RE.search(str(v)))
+
+
+def emit_planner_value(lines, v, ind):
+ if v is None or str(v) == '':
+ lines.append(f'{ind}')
+ return
+ t = 'xr:DesignTimeRef' if test_planner_ref(v) else 'xs:string'
+ lines.append(f'{ind}{esc_xml(str(v))}')
+
+
def emit_planner_font(lines, o, ind):
f = _pl_get(o, 'font')
if f is None:
@@ -2814,17 +2839,9 @@ def emit_planner_timescale(lines, ts, ind):
def emit_planner_item(lines, it, ind):
lines.append(f'{ind}')
ii = f'{ind}\t'
- val = _pl_get(it, 'value')
- if val is None:
- lines.append(f'{ii}')
- else:
- lines.append(f'{ii}{esc_xml(str(val))}')
- lines.append(f'{ii}{esc_xml(str(_pl_get(it, "text", "")))}')
- tt = _pl_get(it, 'tooltip', '')
- if str(tt) == '':
- lines.append(f'{ii}')
- else:
- lines.append(f'{ii}{esc_xml(str(tt))}')
+ emit_planner_value(lines, _pl_get(it, 'value'), ii)
+ emit_planner_text(lines, 'text', _pl_get(it, 'text', ''), ii)
+ emit_planner_text(lines, 'tooltip', _pl_get(it, 'tooltip', ''), ii)
lines.append(f'{ii}{_pl_get(it, "begin", "0001-01-01T00:00:00")}')
lines.append(f'{ii}{_pl_get(it, "end", "0001-01-01T00:00:00")}')
emit_planner_color(lines, 'borderColor', it, 'borderColor', ii)
@@ -2845,11 +2862,44 @@ def emit_planner_item(lines, it, ind):
lines.append(f'{ind}')
+def emit_planner_dim_element(lines, el, ind):
+ lines.append(f'{ind}')
+ ii = f'{ind}\t'
+ emit_planner_value(lines, _pl_get(el, 'value'), ii)
+ emit_planner_text(lines, 'text', _pl_get(el, 'text', ''), ii)
+ emit_planner_color(lines, 'borderColor', el, 'borderColor', ii)
+ emit_planner_color(lines, 'backColor', el, 'backColor', ii)
+ emit_planner_color(lines, 'textColor', el, 'textColor', ii)
+ emit_planner_font(lines, el, ii)
+ for sub in _pl_get(el, 'elements', []):
+ emit_planner_dim_element(lines, sub, ii)
+ lines.append(f'{ii}{_pl_bool(_pl_get(el, "showOnlySubordinatesAreas", True))}')
+ lines.append(f'{ii}{_pl_bool(_pl_get(el, "textFormatted", False))}')
+ lines.append(f'{ind}')
+
+
+def emit_planner_dimension(lines, d, ind):
+ lines.append(f'{ind}')
+ di = f'{ind}\t'
+ emit_planner_value(lines, _pl_get(d, 'value'), di)
+ emit_planner_text(lines, 'text', _pl_get(d, 'text', ''), di)
+ emit_planner_color(lines, 'borderColor', d, 'borderColor', di)
+ emit_planner_color(lines, 'backColor', d, 'backColor', di)
+ emit_planner_color(lines, 'textColor', d, 'textColor', di)
+ emit_planner_font(lines, d, di)
+ for el in _pl_get(d, 'elements', []):
+ emit_planner_dim_element(lines, el, di)
+ lines.append(f'{di}{_pl_bool(_pl_get(d, "textFormatted", False))}')
+ lines.append(f'{ind}')
+
+
def emit_planner_settings(lines, pl, ind):
lines.append(f'{ind}')
si = f'{ind}\t'
for it in _pl_get(pl, 'items', []):
emit_planner_item(lines, it, si)
+ for d in _pl_get(pl, 'dimensions', []):
+ emit_planner_dimension(lines, d, si)
emit_planner_color(lines, 'borderColor', pl, 'borderColor', si)
emit_planner_color(lines, 'backColor', pl, 'backColor', si)
emit_planner_color(lines, 'textColor', pl, 'textColor', si)
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index 545309d8..9e98ee18 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.80 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.81 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1838,6 +1838,13 @@ function Decompile-Element {
# Полный захват каждого поля (раундтрип бит-в-бит); зеркало Emit-PlannerSettings.
function PLD-Bool { param($v) if ($null -eq $v) { return $null } return ($v -eq 'true') }
function PLD-Int { param($v) if ($null -eq $v) { return $null } return [int]$v }
+# → текст (тип xr:DesignTimeRef/xs:string выводится компилятором из вида значения); nil → $null.
+function Get-PlannerValue {
+ param($node)
+ if (-not $node) { return $null }
+ if ($node.GetAttribute('nil', $NS_XSI) -eq 'true') { return $null }
+ if ($node.InnerText) { return $node.InnerText } else { return $null }
+}
function Build-PlannerFont {
param($node)
if (-not $node) { return $null }
@@ -1877,6 +1884,37 @@ function Build-PlannerItem {
$o['editMode'] = (Get-Child $itn 'editMode')
return $o
}
+function Build-PlannerDimElement {
+ param($eln)
+ $o = [ordered]@{}
+ $v = Get-PlannerValue ($eln.SelectSingleNode("*[local-name()='value']")); if ($null -ne $v) { $o['value'] = $v }
+ $o['text'] = (Get-Child $eln 'text')
+ $o['borderColor'] = (Get-Child $eln 'borderColor')
+ $o['backColor'] = (Get-Child $eln 'backColor')
+ $o['textColor'] = (Get-Child $eln 'textColor')
+ $fnt = Build-PlannerFont ($eln.SelectSingleNode("*[local-name()='font']")); if ($fnt) { $o['font'] = $fnt }
+ $subs = New-Object System.Collections.ArrayList
+ foreach ($s in @($eln.SelectNodes("*[local-name()='item']"))) { [void]$subs.Add((Build-PlannerDimElement $s)) }
+ if ($subs.Count -gt 0) { $o['elements'] = @($subs) }
+ $sos = Get-Child $eln 'showOnlySubordinatesAreas'; if ($null -ne $sos) { $o['showOnlySubordinatesAreas'] = ($sos -eq 'true') }
+ $o['textFormatted'] = (PLD-Bool (Get-Child $eln 'textFormatted'))
+ return $o
+}
+function Build-PlannerDimension {
+ param($dn)
+ $o = [ordered]@{}
+ $v = Get-PlannerValue ($dn.SelectSingleNode("*[local-name()='value']")); if ($null -ne $v) { $o['value'] = $v }
+ $o['text'] = (Get-Child $dn 'text')
+ $o['borderColor'] = (Get-Child $dn 'borderColor')
+ $o['backColor'] = (Get-Child $dn 'backColor')
+ $o['textColor'] = (Get-Child $dn 'textColor')
+ $fnt = Build-PlannerFont ($dn.SelectSingleNode("*[local-name()='font']")); if ($fnt) { $o['font'] = $fnt }
+ $els = New-Object System.Collections.ArrayList
+ foreach ($e in @($dn.SelectNodes("*[local-name()='item']"))) { [void]$els.Add((Build-PlannerDimElement $e)) }
+ if ($els.Count -gt 0) { $o['elements'] = @($els) }
+ $o['textFormatted'] = (PLD-Bool (Get-Child $dn 'textFormatted'))
+ return $o
+}
function Build-PlannerLevel {
param($lvn)
$o = [ordered]@{}
@@ -1924,6 +1962,12 @@ function Build-PlannerSettings {
foreach ($itn in $itemNodes) { [void]$items.Add((Build-PlannerItem $itn)) }
$pl['items'] = @($items)
}
+ $dimNodes = @($setNode.SelectNodes("*[local-name()='dimension']"))
+ if ($dimNodes.Count -gt 0) {
+ $dims = New-Object System.Collections.ArrayList
+ foreach ($dn in $dimNodes) { [void]$dims.Add((Build-PlannerDimension $dn)) }
+ $pl['dimensions'] = @($dims)
+ }
$pl['borderColor'] = (Get-Child $setNode 'borderColor')
$pl['backColor'] = (Get-Child $setNode 'backColor')
$pl['textColor'] = (Get-Child $setNode 'textColor')
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 063e6b93..9b03086d 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -852,6 +852,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
| Ключ planner | Тип | Назначение |
|---|---|---|
| `items` | array | Элементы планировщика (``): `value`(nil по умолч.)/`text`/`tooltip`/`begin`/`end`/`borderColor`/`backColor`/`textColor`/`font`/`replacementDate`/`deleted`/`id`(авто-GUID)/`textFormatted`/`border`/`editMode` |
+| `dimensions` | array | Измерения планировщика (``, «Измерения» в конфигураторе): `value` (объект разреза — ссылка `Enum.X.EnumValue.Y`/`Справочник.X`, опускается → nil)/`text` (заголовок)/`borderColor`/`backColor`/`textColor`/`font`/`elements`/`textFormatted`. `elements` — элементы измерения (рекурсивны, могут нести вложенные `elements`): `value`/`text`/цвета/`font`/`showOnlySubordinatesAreas`(bool)/`textFormatted`. Тип `value` авто: ссылочный вид → `xsi:type="xr:DesignTimeRef"`, иначе `xs:string` |
| `borderColor`/`backColor`/`textColor`/`lineColor` | color | Цвета планировщика (умолч. `auto`) |
| `font` | font | Шрифт (умолч. `{kind:"AutoFont"}`) |
| `beginOfRepresentationPeriod`/`endOfRepresentationPeriod` | dateTime | Период представления |
@@ -866,7 +867,7 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
| `minColumnWidth`/`minRowHeight` | int | Минимальные размеры |
| `border` | border | Рамка планировщика (`{ width, style }`) |
-> **Ограничение Phase 1:** `dimensions` (измерения планировщика) и `item.dimensionValues` пока всегда пустые (захват только пустого блока). Конфиг Chart/GanttChart (`d4p1:*`) — отдельная фаза.
+> **Ограничение Phase 1:** `item.dimensionValues` (привязка элемента расписания к элементам измерений) пока всегда пустой (захват только пустого блока). Сами измерения (`dimensions`) поддержаны. Конфиг Chart/GanttChart (`d4p1:*`) — отдельная фаза.
### settings — динамический список
diff --git a/tests/skills/cases/form-compile/chart-fields.json b/tests/skills/cases/form-compile/chart-fields.json
index 63a1bbcb..a2d2e520 100644
--- a/tests/skills/cases/form-compile/chart-fields.json
+++ b/tests/skills/cases/form-compile/chart-fields.json
@@ -35,6 +35,11 @@
"items": [
{ "text": "Встреча", "begin": "2026-06-09T01:00:00", "end": "2026-06-09T04:00:00" }
],
+ "dimensions": [
+ { "text": "Измерение", "elements": [
+ { "value": "А", "text": "Элемент А", "showOnlySubordinatesAreas": true }
+ ] }
+ ],
"period": { "begin": "2026-06-09T00:00:00", "end": "2026-06-09T23:59:59" }
} }
]
diff --git a/tests/skills/cases/form-compile/snapshots/chart-fields/DataProcessors/Диаграммы/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/chart-fields/DataProcessors/Диаграммы/Forms/Форма/Ext/Form.xml
index 7769c5d6..9627dc6c 100644
--- a/tests/skills/cases/form-compile/snapshots/chart-fields/DataProcessors/Диаграммы/Forms/Форма/Ext/Form.xml
+++ b/tests/skills/cases/form-compile/snapshots/chart-fields/DataProcessors/Диаграммы/Forms/Форма/Ext/Form.xml
@@ -171,6 +171,25 @@
EnableEdit
+
+
+ Измерение
+ auto
+ auto
+ auto
+
+
+ А
+ Элемент А
+ auto
+ auto
+ auto
+
+ true
+ false
+
+ false
+
auto
auto
auto