feat(form-decompile,form-compile): Save — сохранение значения реквизита в польз. настройках

Ключ save на реквизите формы (<Save><Field>…):
- true → <Field>имя</Field> (голое имя, 93% случаев)
- строка/массив строк → под-поля с авто-префиксом "имя." (путь с точкой,
  UUID 1/0:guid, или совпадающее с именем — берутся как есть)
- нет ключа или false → не эмитим

Гипотеза подтверждена на корпусе: Field=имя в 383/410 (93%); gating формовыми
SaveDataInSettings/AutoSaveDataInSettings НЕ требуется (51/160 форм с Save без них).
Период-кейс (голое имя Период + Период.EndDate/StartDate/Variant) round-trip
бит-в-бит. Переиспользует механику нормализации useAlways. ≠ savedData (отдельное,
уже было). Декомпилятор: один Field=имя → save:true, иначе массив со снятым
префиксом. Зеркало py (байт-в-байт), кейс attributes-types сертифицирован в 1С.
Регресс 39/39 ps1+py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-08 17:59:55 +03:00
parent 09c3dcd988
commit 5a67c56e92
6 changed files with 88 additions and 7 deletions
@@ -1,4 +1,4 @@
# form-compile v1.75 — Compile 1C managed form from JSON or object metadata
# form-compile v1.76 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -4400,6 +4400,27 @@ function Emit-Attributes {
if ($attr.savedData -eq $true -or $mainSaved) {
X "$inner<SavedData>true</SavedData>"
}
# Save: сохранение значения реквизита в пользовательских настройках. true → <Field>имя</Field>;
# строка/массив → под-поля с авто-префиксом "имя." (путь с точкой / UUID / =имя — как есть).
# Нет ключа или false → не эмитим.
if ($null -ne $attr.PSObject.Properties['save'] -and $null -ne $attr.save) {
$saveFields = New-Object System.Collections.ArrayList
if ($attr.save -is [bool]) {
if ($attr.save) { [void]$saveFields.Add($attrName) }
} else {
foreach ($e in @($attr.save)) {
$fld = "$e"
if ([string]::IsNullOrEmpty($fld)) { continue }
if ($fld -ne $attrName -and $fld -notmatch '\.' -and $fld -notmatch '^\d+/\d+:') { $fld = "$attrName.$fld" }
if (-not $saveFields.Contains($fld)) { [void]$saveFields.Add($fld) }
}
}
if ($saveFields.Count -gt 0) {
X "$inner<Save>"
foreach ($f in $saveFields) { X "$inner`t<Field>$(Esc-Xml $f)</Field>" }
X "$inner</Save>"
}
}
if ($attr.fillChecking) {
X "$inner<FillChecking>$($attr.fillChecking)</FillChecking>"
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.75 — Compile 1C managed form from JSON or object metadata
# form-compile v1.76 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -4124,6 +4124,29 @@ def emit_attributes(lines, attrs, indent):
main_saved = bool(re.match(r'^(CatalogObject|DocumentObject|ChartOfAccountsObject|ChartOfCalculationTypesObject|ChartOfCharacteristicTypesObject|ExchangePlanObject|BusinessProcessObject|TaskObject)\.', t)) or ('RecordManager.' in t)
if attr.get('savedData') is True or main_saved:
lines.append(f'{inner}<SavedData>true</SavedData>')
# Save: сохранение значения реквизита в пользовательских настройках. true → <Field>имя</Field>;
# строка/массив → под-поля с авто-префиксом "имя." (путь с точкой / UUID / =имя — как есть).
# Нет ключа или false → не эмитим.
if 'save' in attr and attr['save'] is not None:
save_fields = []
sv = attr['save']
if isinstance(sv, bool):
if sv:
save_fields.append(attr_name)
else:
for e in (sv if isinstance(sv, (list, tuple)) else [sv]):
fld = str(e)
if not fld:
continue
if fld != attr_name and '.' not in fld and not re.match(r'^\d+/\d+:', fld):
fld = f'{attr_name}.{fld}'
if fld not in save_fields:
save_fields.append(fld)
if save_fields:
lines.append(f'{inner}<Save>')
for f in save_fields:
lines.append(f'{inner}\t<Field>{esc_xml(f)}</Field>')
lines.append(f'{inner}</Save>')
if attr.get('fillChecking'):
lines.append(f'{inner}<FillChecking>{attr["fillChecking"]}</FillChecking>')
@@ -1,4 +1,4 @@
# form-decompile v0.52 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v0.53 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1689,6 +1689,21 @@ if ($attrsNode) {
$ao['title'] = ''
}
if ((Get-Child $a 'SavedData') -eq 'true') { $ao['savedData'] = $true }
# Save: сохранение значения реквизита в пользовательских настройках. Один Field=имя → save:true;
# иначе снимаем префикс "имя." (голое имя/UUID/прочее — как есть) → строка (1) или массив.
$saveNode = $a.SelectSingleNode("lf:Save", $ns)
if ($saveNode) {
$nm = "$($ao['name'])"
$flds = @($saveNode.SelectNodes("lf:Field", $ns) | ForEach-Object { $_.InnerText })
if ($flds.Count -eq 1 -and $flds[0] -eq $nm) {
$ao['save'] = $true
} elseif ($flds.Count -gt 0) {
$stripped = @($flds | ForEach-Object {
if ($_ -match "^$([regex]::Escape($nm))\.(.+)$") { $matches[1] } else { $_ }
})
if ($stripped.Count -eq 1) { $ao['save'] = $stripped[0] } else { $ao['save'] = $stripped }
}
}
$fc = Get-Child $a 'FillChecking'; if ($fc) { $ao['fillChecking'] = $fc }
$afo = Decompile-FunctionalOptions $a; if ($afo) { $ao['functionalOptions'] = $afo }
$colsNode = $a.SelectSingleNode("lf:Columns", $ns)