fix(form-compile,form-decompile): UseAlways маркер "~" (query-поля дин-списка) — двойной префикс

Компилятор-баг: поле "~Список.Остановлен" (декомпилятор хранил verbatim) не
матчило проверку префикса ^Список\. → добавлялся ещё префикс →
"Список.~Список.Остановлен" (8+ форм выборки: ЗаявкаСотрудника*, Банки,
ОбеспечениеПроизводственныхПроцессов…). "~" — легитимный маркер query-полей
динамического списка (2234/17266 = 13% корпуса).

- Компилятор: префикс ИмяРеквизита. ставится ПОСЛЕ "~" (~Остановлен →
  ~Список.Остановлен); полная форма ~Список.X — verbatim (forgiving ввод).
- Декомпилятор: компактит ~Список.X → ~X (единообразно с короткими именами;
  компилятор разворачивает обратно).

Зеркало py. Кейс dynamic-list-form расширен (~Артикул + Список.Code +
Description), сертифицирован загрузкой в 1С. Регресс 39/39 в обоих рантаймах.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-08 22:44:36 +03:00
parent 7882c1cc2b
commit 4916f5bf7c
6 changed files with 38 additions and 8 deletions
@@ -1,4 +1,4 @@
# form-compile v1.86 — Compile 1C managed form from JSON or object metadata
# form-compile v1.87 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -4583,7 +4583,16 @@ function Emit-Attributes {
if ($attr.useAlways) {
foreach ($e in @($attr.useAlways)) {
$fld = "$e"
if ($fld -notmatch "^$([regex]::Escape($attrName))\.") { $fld = "$attrName.$fld" }
# Префикс "ИмяРеквизита." добавляем к коротким именам. Поля дин-списка с маркером "~"
# (query-поля, ~13% корпуса) — префикс ставится ПОСЛЕ "~": ~Остановлен → ~Список.Остановлен.
# Полная форма (~Список.Остановлен / Список.Остановлен) — verbatim (forgiving ввод).
if ($fld.StartsWith('~')) {
$bare = $fld.Substring(1)
if ($bare -notmatch "^$([regex]::Escape($attrName))\.") { $bare = "$attrName.$bare" }
$fld = "~$bare"
} elseif ($fld -notmatch "^$([regex]::Escape($attrName))\.") {
$fld = "$attrName.$fld"
}
if (-not $uaFields.Contains($fld)) { [void]$uaFields.Add($fld) }
}
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.86 — Compile 1C managed form from JSON or object metadata
# form-compile v1.87 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -4294,7 +4294,15 @@ def emit_attributes(lines, attrs, indent):
ua_fields = []
for e in (attr.get('useAlways') or []):
fld = str(e)
if not re.match(r'^' + re.escape(attr_name) + r'\.', fld):
# Префикс "ИмяРеквизита." добавляем к коротким именам. Поля дин-списка с маркером "~"
# (query-поля, ~13% корпуса) — префикс ставится ПОСЛЕ "~": ~Остановлен → ~Список.Остановлен.
# Полная форма (~Список.Остановлен / Список.Остановлен) — verbatim (forgiving ввод).
if fld.startswith('~'):
bare = fld[1:]
if not re.match(r'^' + re.escape(attr_name) + r'\.', bare):
bare = f'{attr_name}.{bare}'
fld = f'~{bare}'
elif not re.match(r'^' + re.escape(attr_name) + r'\.', fld):
fld = f'{attr_name}.{fld}'
if fld not in ua_fields:
ua_fields.append(fld)
@@ -1,4 +1,4 @@
# form-decompile v0.62 — Decompile 1C managed Form.xml to JSON DSL (draft)
# form-decompile v0.63 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1814,7 +1814,15 @@ if ($attrsNode) {
$shorts = New-Object System.Collections.ArrayList
foreach ($fn in @($uaNode.SelectNodes("lf:Field", $ns))) {
$t = $fn.InnerText.Trim()
if ($t.StartsWith($prefix)) { $t = $t.Substring($prefix.Length) }
# Снимаем префикс "ИмяРеквизита.". Маркер "~" (query-поле дин-списка) сохраняем,
# префикс снимаем ПОСЛЕ него: ~Список.Остановлен → ~Остановлен (компилятор развернёт обратно).
if ($t.StartsWith('~')) {
$rest = $t.Substring(1)
if ($rest.StartsWith($prefix)) { $rest = $rest.Substring($prefix.Length) }
$t = "~$rest"
} elseif ($t.StartsWith($prefix)) {
$t = $t.Substring($prefix.Length)
}
[void]$shorts.Add($t)
}
if ($ao.Contains('columns')) {