mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-14 18:04:58 +03:00
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:
@@ -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')) {
|
||||
|
||||
@@ -766,7 +766,7 @@ Pages поддерживает `pagesRepresentation`: `None`, `TabsOnTop`, `Tabs
|
||||
| `view` | bool/object | Просмотр по ролям (`<View>`). См. §4.1c |
|
||||
| `edit` | bool/object | Редактирование по ролям (`<Edit>`). См. §4.1c |
|
||||
| `functionalOptions` | array | Функциональные опции (`<FunctionalOptions><Item>FunctionalOption.X</Item>…`). Массив имён; forgiving: `"X"`/`"FunctionalOption.X"`. Также у колонок (`columns[*]`) и команд (§7) |
|
||||
| `useAlways` | array | Поля, всегда читаемые (`<UseAlways><Field>Имя.Поле</Field>…`). Массив коротких имён полей (forgiving: с/без префикса `Имя.`). **Две формы**: этот массив на реквизите ИЛИ `useAlways: true` на колонке (`columns[*]`) — компилятор сливает. Для дин-списка — только массив (колонки не эмитятся, но формируют `<UseAlways>`) |
|
||||
| `useAlways` | array | Поля, всегда читаемые (`<UseAlways><Field>Имя.Поле</Field>…`). Массив коротких имён полей (forgiving: с/без префикса `Имя.`). **Маркер `~`** (query-поля дин-списка): `~Остановлен` → `<Field>~Список.Остановлен</Field>` (префикс ставится ПОСЛЕ `~`; полная форма `~Список.Остановлен` тоже принимается verbatim). **Две формы**: этот массив на реквизите ИЛИ `useAlways: true` на колонке (`columns[*]`) — компилятор сливает. Для дин-списка — только массив (колонки не эмитятся, но формируют `<UseAlways>`) |
|
||||
| `valueType` | string | Тип значений у реквизита типа `ValueList` (`<Settings xsi:type="v8:TypeDescription">`). Грамматика — как у `type`, включая составной `A \| B`. **Три состояния**: нет ключа → нет `<Settings>`; `""` → пустой `<Settings…/>` (список без ограничения типа); тип → с типом. Forgiving-синонимы: `typeDescription` (≈1С «ОписаниеТипов» / XML), `описаниеТипов`, `типЗначений`. Пример: `"valueType": "CatalogRef.Контрагенты"` |
|
||||
| `savedData` | bool | Сохраняемые данные (`<SavedData>`) |
|
||||
| `save` | bool/string/array | Сохранение значения в пользовательских настройках (`<Save><Field>…`). `true` → `<Field>имя</Field>`; строка/массив строк → под-поля с авто-префиксом `имя.` (путь с точкой / UUID `1/0:…` / совпадающее с именем — берётся как есть). Нет ключа или `false` → не эмитится. Пример периода: `["Период","EndDate","StartDate","Variant"]` |
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"input": {
|
||||
"title": "Товары",
|
||||
"attributes": [
|
||||
{ "name": "Список", "type": "DynamicList", "settings": {
|
||||
{ "name": "Список", "type": "DynamicList", "useAlways": ["~Артикул", "Список.Code", "Description"], "settings": {
|
||||
"mainTable": "Catalog.Товары", "dynamicDataRead": true,
|
||||
"order": [ "Description", "Code desc" ],
|
||||
"filter": [ "Артикул = _ @off @user" ],
|
||||
|
||||
+5
@@ -88,6 +88,11 @@
|
||||
<v8:Type>cfg:DynamicList</v8:Type>
|
||||
</Type>
|
||||
<MainAttribute>true</MainAttribute>
|
||||
<UseAlways>
|
||||
<Field>~Список.Артикул</Field>
|
||||
<Field>Список.Code</Field>
|
||||
<Field>Список.Description</Field>
|
||||
</UseAlways>
|
||||
<Settings xsi:type="DynamicList">
|
||||
<ManualQuery>false</ManualQuery>
|
||||
<DynamicDataRead>true</DynamicDataRead>
|
||||
|
||||
Reference in New Issue
Block a user