mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-14 18:04:58 +03:00
feat(form-decompile,form-compile): хвост report-форм 2.17 (TOTAL 23→0)
Добивка длинного хвоста, раскрытого при выводе спец-полей из ring-3.
Generic-скаляры (обе стороны, +py): ItemHeight (radio), DropListWidth (input).
Форменное VariantAppearance → KNOWN_FORM_PROPS. Targeted: PasswordMode на
LabelField (факт. значение, ≠ input if-true), ChoiceButtonPicture (input, через
Emit/Get-PictureRef), TransparentPixel (под-элемент <xr:TransparentPixel x y> в
<Picture> PictureDecoration → ключ transparentPixel:{x,y}, 1162 в корпусе).
Компилятор-баг ChoiceParameters без значения: платформа эмитит
<app:value xsi:nil="true"/> (13 в корпусе), компилятор додумывал пустую
FormChoiceListDesTimeValue. Теперь по наличию ключа value (hashtable shorthand
vs PSCustomObject — для PS; dict — для py).
Выборка 2.17: TOTAL 23→0, match 170→181, diff 13→2 (остаток — только GroupList,
документированный не-покрываемый: декомпилятор намеренно опускает во избежание
тихой порчи ссылки). Регресс 40/40 (ps1+py). Кейс input-fields расширен
(value-less choiceParameter → nil) и сертифицирован загрузкой в 1С.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# form-compile v1.96 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.97 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$JsonPath,
|
||||
@@ -2616,6 +2616,8 @@ function Emit-Element {
|
||||
"pointerType"=1;"drawingSelectionShowMode"=1;"warningOnEditRepresentation"=1;"markingAppearance"=1
|
||||
# report-form контекст (generic-скаляры элементов)
|
||||
"horizontalSpacing"=1;"representationInContextMenu"=1;"settingsNamedItemDetailedRepresentation"=1
|
||||
# хвост: высота элемента списка / ширина выпадающего списка / картинка кнопки выбора / прозрачный пиксель
|
||||
"itemHeight"=1;"dropListWidth"=1;"choiceButtonPicture"=1;"transparentPixel"=1
|
||||
# columnGroup-specific
|
||||
"showInHeader"=1
|
||||
# radio-specific
|
||||
@@ -2920,6 +2922,9 @@ $script:genericScalars = @(
|
||||
@{ Tag='HorizontalSpacing'; Key='horizontalSpacing'; Kind='value' }
|
||||
@{ Tag='RepresentationInContextMenu'; Key='representationInContextMenu'; Kind='value' }
|
||||
@{ Tag='SettingsNamedItemDetailedRepresentation'; Key='settingsNamedItemDetailedRepresentation'; Kind='bool' }
|
||||
# Хвост: высота элемента списка (radio) / ширина выпадающего списка (input)
|
||||
@{ Tag='ItemHeight'; Key='itemHeight'; Kind='value' }
|
||||
@{ Tag='DropListWidth'; Key='dropListWidth'; Kind='value' }
|
||||
)
|
||||
|
||||
function Emit-GenericScalars {
|
||||
@@ -3259,6 +3264,7 @@ function Emit-Input {
|
||||
}
|
||||
}
|
||||
if ($el.choiceButtonRepresentation) { X "$inner<ChoiceButtonRepresentation>$($el.choiceButtonRepresentation)</ChoiceButtonRepresentation>" }
|
||||
Emit-PictureRef -val $el.choiceButtonPicture -picTag 'ChoiceButtonPicture' -indent $inner
|
||||
Emit-Layout -el $el -indent $inner -multiLineDefault ([bool]($el.multiLine -eq $true))
|
||||
|
||||
if ($el.inputHint) {
|
||||
@@ -3625,11 +3631,23 @@ function Emit-ChoiceParameters {
|
||||
foreach ($item in @($cp)) {
|
||||
if ($item -is [string]) { $item = ConvertFrom-ChoiceParamShorthand $item }
|
||||
$name = Get-ElProp $item @('name','имя')
|
||||
# Наличие ключа value (≠ значения): hashtable (shorthand) vs PSCustomObject (JSON-объект)
|
||||
if ($item -is [System.Collections.IDictionary]) {
|
||||
$hasVal = $item.Contains('value') -or $item.Contains('значение')
|
||||
} else {
|
||||
$hasVal = ($null -ne $item.PSObject.Properties['value']) -or ($null -ne $item.PSObject.Properties['значение'])
|
||||
}
|
||||
$val = Get-ElProp $item @('value','значение')
|
||||
X "$indent`t<app:item name=`"$(Esc-Xml "$name")`">"
|
||||
X "$indent`t`t<app:value xsi:type=`"FormChoiceListDesTimeValue`">"
|
||||
Emit-ChoiceParamValue -value $val -indent "$indent`t`t`t"
|
||||
X "$indent`t`t</app:value>"
|
||||
# Параметр выбора без значения → <app:value xsi:nil="true"/> (платформа, 13 в корпусе);
|
||||
# со значением (в т.ч. пустой строкой) → FormChoiceListDesTimeValue.
|
||||
if (-not $hasVal) {
|
||||
X "$indent`t`t<app:value xsi:nil=`"true`"/>"
|
||||
} else {
|
||||
X "$indent`t`t<app:value xsi:type=`"FormChoiceListDesTimeValue`">"
|
||||
Emit-ChoiceParamValue -value $val -indent "$indent`t`t`t"
|
||||
X "$indent`t`t</app:value>"
|
||||
}
|
||||
X "$indent`t</app:item>"
|
||||
}
|
||||
X "$indent</ChoiceParameters>"
|
||||
@@ -3777,6 +3795,8 @@ function Emit-LabelField {
|
||||
|
||||
if ($el.titleLocation) { X "$inner<TitleLocation>$(Map-TitleLoc "$($el.titleLocation)")</TitleLocation>" }
|
||||
if ($el.editMode) { X "$inner<EditMode>$($el.editMode)</EditMode>" }
|
||||
# PasswordMode на LabelField — платформа эмитит явный false (редко); факт. значение
|
||||
if ($null -ne $el.passwordMode) { X "$inner<PasswordMode>$(if ($el.passwordMode){'true'}else{'false'})</PasswordMode>" }
|
||||
Emit-ColumnPics -el $el -indent $inner
|
||||
# ВНИМАНИЕ: у LabelField платформенный тег именно <Hiperlink> (опечатка 1С), не <Hyperlink>.
|
||||
if ($el.hyperlink -eq $true) { X "$inner<Hiperlink>true</Hiperlink>" }
|
||||
@@ -4125,6 +4145,7 @@ function Emit-PictureDecoration {
|
||||
if ($srcStr -match '^abs:(.*)$') { X "$inner`t<xr:Abs>$(Esc-Xml $matches[1])</xr:Abs>" }
|
||||
else { X "$inner`t<xr:Ref>$(Esc-Xml $srcStr)</xr:Ref>" }
|
||||
X "$inner`t<xr:LoadTransparent>$lt</xr:LoadTransparent>"
|
||||
if ($el.transparentPixel) { X "$inner`t<xr:TransparentPixel x=`"$($el.transparentPixel.x)`" y=`"$($el.transparentPixel.y)`"/>" }
|
||||
X "$inner</Picture>"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# form-compile v1.96 — Compile 1C managed form from JSON or object metadata
|
||||
# form-compile v1.97 — Compile 1C managed form from JSON or object metadata
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import copy
|
||||
@@ -1837,6 +1837,8 @@ KNOWN_KEYS = {
|
||||
"pointerType", "drawingSelectionShowMode", "warningOnEditRepresentation", "markingAppearance",
|
||||
# report-form контекст (generic-скаляры элементов)
|
||||
"horizontalSpacing", "representationInContextMenu", "settingsNamedItemDetailedRepresentation",
|
||||
# хвост: высота элемента списка / ширина выпадающего списка / картинка кнопки выбора / прозрачный пиксель
|
||||
"itemHeight", "dropListWidth", "choiceButtonPicture", "transparentPixel",
|
||||
}
|
||||
|
||||
# picture/picField — НИЗКИЙ приоритет: 'picture' это и тип (PictureDecoration), и свойство-иконка
|
||||
@@ -2175,12 +2177,18 @@ def emit_choice_parameters(lines, el, indent):
|
||||
if isinstance(item, str):
|
||||
item = from_choice_param_shorthand(item)
|
||||
name = get_el_prop(item, ('name', 'имя'))
|
||||
has_val = isinstance(item, dict) and ('value' in item or 'значение' in item)
|
||||
val = get_el_prop(item, ('value', 'значение'))
|
||||
name_s = '' if name is None else str(name)
|
||||
lines.append(f'{indent}\t<app:item name="{esc_xml(name_s)}">')
|
||||
lines.append(f'{indent}\t\t<app:value xsi:type="FormChoiceListDesTimeValue">')
|
||||
emit_choice_param_value(lines, val, f'{indent}\t\t\t')
|
||||
lines.append(f'{indent}\t\t</app:value>')
|
||||
# Параметр выбора без значения → <app:value xsi:nil="true"/> (платформа, 13 в корпусе);
|
||||
# со значением (в т.ч. пустой строкой) → FormChoiceListDesTimeValue.
|
||||
if not has_val:
|
||||
lines.append(f'{indent}\t\t<app:value xsi:nil="true"/>')
|
||||
else:
|
||||
lines.append(f'{indent}\t\t<app:value xsi:type="FormChoiceListDesTimeValue">')
|
||||
emit_choice_param_value(lines, val, f'{indent}\t\t\t')
|
||||
lines.append(f'{indent}\t\t</app:value>')
|
||||
lines.append(f'{indent}\t</app:item>')
|
||||
lines.append(f'{indent}</ChoiceParameters>')
|
||||
|
||||
@@ -2741,6 +2749,9 @@ GENERIC_SCALARS = [
|
||||
('HorizontalSpacing', 'horizontalSpacing', 'value'),
|
||||
('RepresentationInContextMenu', 'representationInContextMenu', 'value'),
|
||||
('SettingsNamedItemDetailedRepresentation', 'settingsNamedItemDetailedRepresentation', 'bool'),
|
||||
# Хвост: высота элемента списка (radio) / ширина выпадающего списка (input)
|
||||
('ItemHeight', 'itemHeight', 'value'),
|
||||
('DropListWidth', 'dropListWidth', 'value'),
|
||||
]
|
||||
|
||||
|
||||
@@ -3313,6 +3324,7 @@ def emit_input(lines, el, name, eid, indent):
|
||||
lines.append(f'{inner}<{tag} xsi:type="{mvt}">{esc_xml(str(el[key]))}</{tag}>')
|
||||
if el.get('choiceButtonRepresentation'):
|
||||
lines.append(f'{inner}<ChoiceButtonRepresentation>{el["choiceButtonRepresentation"]}</ChoiceButtonRepresentation>')
|
||||
emit_picture_ref(lines, el.get('choiceButtonPicture'), 'ChoiceButtonPicture', inner)
|
||||
emit_layout(lines, el, inner, multi_line_default=(el.get('multiLine') is True))
|
||||
|
||||
if el.get('inputHint'):
|
||||
@@ -3474,6 +3486,9 @@ def emit_label_field(lines, el, name, eid, indent):
|
||||
lines.append(f'{inner}<TitleLocation>{map_title_loc(el["titleLocation"])}</TitleLocation>')
|
||||
if el.get('editMode'):
|
||||
lines.append(f'{inner}<EditMode>{el["editMode"]}</EditMode>')
|
||||
# PasswordMode на LabelField — платформа эмитит явный false (редко); факт. значение
|
||||
if el.get('passwordMode') is not None:
|
||||
lines.append(f'{inner}<PasswordMode>{"true" if el["passwordMode"] else "false"}</PasswordMode>')
|
||||
emit_column_pics(lines, el, inner)
|
||||
# ВНИМАНИЕ: у LabelField платформенный тег <Hiperlink> (опечатка 1С), не <Hyperlink>.
|
||||
if el.get('hyperlink') is True:
|
||||
@@ -3819,6 +3834,9 @@ def emit_picture_decoration(lines, el, name, eid, indent):
|
||||
else:
|
||||
lines.append(f'{inner}\t<xr:Ref>{esc_xml(src_str)}</xr:Ref>')
|
||||
lines.append(f'{inner}\t<xr:LoadTransparent>{lt}</xr:LoadTransparent>')
|
||||
tpx = el.get('transparentPixel')
|
||||
if tpx:
|
||||
lines.append(f'{inner}\t<xr:TransparentPixel x="{tpx.get("x")}" y="{tpx.get("y")}"/>')
|
||||
lines.append(f'{inner}</Picture>')
|
||||
|
||||
if el.get('hyperlink') is True:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# form-decompile v0.72 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# form-decompile v0.73 — Decompile 1C managed Form.xml to JSON DSL (draft)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
|
||||
param(
|
||||
@@ -1192,6 +1192,9 @@ $GENERIC_SCALARS = @(
|
||||
@{ Tag='HorizontalSpacing'; Key='horizontalSpacing'; Kind='value' }
|
||||
@{ Tag='RepresentationInContextMenu'; Key='representationInContextMenu'; Kind='value' }
|
||||
@{ Tag='SettingsNamedItemDetailedRepresentation'; Key='settingsNamedItemDetailedRepresentation'; Kind='bool' }
|
||||
# Хвост: высота элемента списка (radio) / ширина выпадающего списка (input)
|
||||
@{ Tag='ItemHeight'; Key='itemHeight'; Kind='value' }
|
||||
@{ Tag='DropListWidth'; Key='dropListWidth'; Kind='value' }
|
||||
)
|
||||
|
||||
# Захват generic-скаляров. Специфичная обработка (если ключ уже задан) — побеждает.
|
||||
@@ -1479,6 +1482,7 @@ function Decompile-Element {
|
||||
}
|
||||
}
|
||||
$cbr = Get-Child $node 'ChoiceButtonRepresentation'; if ($cbr) { $obj['choiceButtonRepresentation'] = $cbr }
|
||||
$cbp = Get-PictureRef $node 'ChoiceButtonPicture'; if ($null -ne $cbp) { $obj['choiceButtonPicture'] = $cbp }
|
||||
if ((Get-Child $node 'TextEdit') -eq 'false') { $obj['textEdit'] = $false }
|
||||
$cl = Decompile-ChoiceList $node; if ($cl) { $obj['choiceList'] = $cl }
|
||||
Add-FormatProps $obj $node
|
||||
@@ -1524,6 +1528,8 @@ function Decompile-Element {
|
||||
$em = Get-Child $node 'EditMode'; if ($em) { $obj['editMode'] = $em }
|
||||
# LabelField: тег <Hiperlink> (опечатка платформы), не <Hyperlink>
|
||||
if ((Get-Child $node 'Hiperlink') -eq 'true') { $obj['hyperlink'] = $true }
|
||||
# PasswordMode на LabelField — платформа эмитит явный false (редко); захват факт. значения
|
||||
$pm = Get-Child $node 'PasswordMode'; if ($null -ne $pm) { $obj['passwordMode'] = ($pm -eq 'true') }
|
||||
Add-FormatProps $obj $node
|
||||
}
|
||||
'PictureDecoration' {
|
||||
@@ -1536,6 +1542,9 @@ function Decompile-Element {
|
||||
$abs = $node.SelectSingleNode("lf:Picture/xr:Abs", $ns)
|
||||
if ($ref) { $obj['src'] = $ref.InnerText } elseif ($abs) { $obj['src'] = "abs:$($abs.InnerText)" } # встроенная картинка → префикс abs:
|
||||
$lt = $node.SelectSingleNode("lf:Picture/xr:LoadTransparent", $ns); if ($lt -and $lt.InnerText -eq 'true') { $obj['loadTransparent'] = $true }
|
||||
# Прозрачный пиксель картинки (<xr:TransparentPixel x y/>) — координаты фона прозрачности
|
||||
$tpx = $node.SelectSingleNode("lf:Picture/xr:TransparentPixel", $ns)
|
||||
if ($tpx) { $obj['transparentPixel'] = [ordered]@{ x = [int]$tpx.GetAttribute('x'); y = [int]$tpx.GetAttribute('y') } }
|
||||
if ((Get-Child $node 'Hyperlink') -eq 'true') { $obj['hyperlink'] = $true }
|
||||
}
|
||||
'PictureField' {
|
||||
@@ -1762,7 +1771,7 @@ $titleNode = $root.SelectSingleNode("lf:Title", $ns)
|
||||
if ($titleNode) { $t = Get-LangText $titleNode; if ($null -ne $t) { $dsl['title'] = $t } }
|
||||
|
||||
# properties (прямые скаляры под <Form>, PascalCase → camelCase)
|
||||
$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing')
|
||||
$KNOWN_FORM_PROPS = @('AutoTitle','ReportResult','DetailsData','ReportFormType','AutoShowState','ReportResultViewMode','ViewModeApplicationOnSetReportResult','WindowOpeningMode','CommandBarLocation','SaveDataInSettings','AutoSaveDataInSettings','AutoTime','UsePostingMode','RepostOnWrite','AutoURL','AutoFillCheck','Customizable','EnterKeyBehavior','VerticalScroll','Width','Height','Group','UseForFoldersAndItems','SaveWindowSettings','ScalingMode','VerticalSpacing','VariantAppearance')
|
||||
$props = [ordered]@{}
|
||||
foreach ($pn in $KNOWN_FORM_PROPS) {
|
||||
$v = Get-Child $root $pn
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
| `autoShowState` | `<AutoShowState>` | `Auto`, `DontShow`, `ShowOnComposition` |
|
||||
| `reportResultViewMode` | `<ReportResultViewMode>` | `Auto` |
|
||||
| `viewModeApplicationOnSetReportResult` | `<ViewModeApplicationOnSetReportResult>` | `Auto` |
|
||||
| `variantAppearance` | `<VariantAppearance>` | Имя реквизита оформления варианта (форма отчёта) |
|
||||
|
||||
Нераспознанные ключи преобразуются с автоматическим PascalCase (первая буква в верхний регистр).
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
{ "name": "Отбор.Активный", "value": true },
|
||||
{ "name": "Отбор.Вид", "value": "Основной" },
|
||||
{ "name": "Отбор.Дата", "value": "2020-01-01T00:00:00" },
|
||||
{ "name": "Отбор.Список", "value": ["Один", "Два"] }
|
||||
{ "name": "Отбор.Список", "value": ["Один", "Два"] },
|
||||
{ "name": "Отбор.БезЗначения" }
|
||||
],
|
||||
"choiceParameterLinks": [
|
||||
{ "name": "Отбор.Организация", "dataPath": "ОбычноеПоле" },
|
||||
|
||||
+3
@@ -261,6 +261,9 @@
|
||||
</Value>
|
||||
</app:value>
|
||||
</app:item>
|
||||
<app:item name="Отбор.БезЗначения">
|
||||
<app:value xsi:nil="true"/>
|
||||
</app:item>
|
||||
</ChoiceParameters>
|
||||
<ContextMenu name="ПолеСвязиКонтекстноеМеню" id="26"/>
|
||||
<ExtendedTooltip name="ПолеСвязиРасширеннаяПодсказка" id="27"/>
|
||||
|
||||
Reference in New Issue
Block a user