mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-20 01:20:59 +03:00
feat(form-compile): значение v8:Type «Неопределено» — локальный xmlns на теге (фильтр + параметр дин-списка)
Значение типа v8:Type (на практике всегда <prefix>:Undefined — тип «Неопределено» из namespace http://v8.1c.ru/8.2/data/types, префикс авто d6p1/d8p1/dN…) эмитилось без объявления namespace → битый QName; а в параметре дин-списка компилятор вообще ронял v8:Type → xs:string. Корпус 8.3.24: 11 тегов (6 <dcsset:right> фильтра + 5 <dcssch:value> параметра), значение всегда prefix:Undefined, ns всегда data/types. Топ ROOT-пробел нового baseline (Attribute>value 48 LOST + 44 ADDED). Фикс: хелпер Get-ValueTypeNsAttr / _value_type_ns_attr (объявляет xmlns:<pref> для не-стандартного префикса при valueType v8:Type) в обе ветки Emit-FilterItem (скаляр + массив op `in`) + новая ветка v8:Type в Emit-DLValue / emit_dl_value. Выборка 7 форм (Взаимодействия acc/erp, ЖурналОпераций×3, ДокументЭДОБЗК, ЧекиККМ): match 0→6, TOTAL→0. Зеркало py байт-в-байт, регресс 43/43 (ps1+py). Раундтрип восстанавливает точные исходные байты платформы (её собственный формат — cert не нужен). Spec обновлён (раздел filter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
71ecec6594
commit
ed2339a4bc
@@ -1,4 +1,4 @@
|
|||||||
# form-compile v1.147 — Compile 1C managed form from JSON or object metadata
|
# form-compile v1.148 — Compile 1C managed form from JSON or object metadata
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[string]$JsonPath,
|
[string]$JsonPath,
|
||||||
@@ -1675,6 +1675,21 @@ function Parse-FilterShorthand {
|
|||||||
return $result
|
return $result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Значение типа v8:Type (напр. тип «Неопределено» = <prefix>:Undefined) ссылается на тип
|
||||||
|
# платформы из namespace http://v8.1c.ru/8.2/data/types — платформа объявляет его ЛОКАЛЬНО
|
||||||
|
# на теге значения (префикс авто-назначаемый: d6p1/d8p1/dN…). Без объявления QName битый.
|
||||||
|
# Возвращает строку-атрибут ' xmlns:<pref>="…"' (перед xsi:type), либо "".
|
||||||
|
function Get-ValueTypeNsAttr {
|
||||||
|
param([string]$valueType, [string]$value)
|
||||||
|
if ($valueType -eq 'v8:Type' -and "$value" -match '^([A-Za-z]\w*):') {
|
||||||
|
$pref = $Matches[1]
|
||||||
|
if ($pref -notin @('xs','cfg','v8','v8ui','ent','dcscor','dcsset','dcssch')) {
|
||||||
|
return " xmlns:$pref=`"http://v8.1c.ru/8.2/data/types`""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
function Emit-FilterItem {
|
function Emit-FilterItem {
|
||||||
param($item, [string]$indent)
|
param($item, [string]$indent)
|
||||||
if ($item.group) {
|
if ($item.group) {
|
||||||
@@ -1731,7 +1746,8 @@ function Emit-FilterItem {
|
|||||||
else { $vt = 'xs:string' }
|
else { $vt = 'xs:string' }
|
||||||
}
|
}
|
||||||
$vStr = if ($v -is [bool]) { "$v".ToLower() } else { Esc-Xml "$v" }
|
$vStr = if ($v -is [bool]) { "$v".ToLower() } else { Esc-Xml "$v" }
|
||||||
X "$indent`t<dcsset:right xsi:type=`"$vt`">$vStr</dcsset:right>"
|
$nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$v"
|
||||||
|
X "$indent`t<dcsset:right$nsAttr xsi:type=`"$vt`">$vStr</dcsset:right>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($null -ne $item.value -and (
|
} elseif ($null -ne $item.value -and (
|
||||||
@@ -1775,7 +1791,8 @@ function Emit-FilterItem {
|
|||||||
else { $vt = "xs:string" }
|
else { $vt = "xs:string" }
|
||||||
}
|
}
|
||||||
$vStr = if ($item.value -is [bool]) { "$($item.value)".ToLower() } else { Esc-Xml "$($item.value)" }
|
$vStr = if ($item.value -is [bool]) { "$($item.value)".ToLower() } else { Esc-Xml "$($item.value)" }
|
||||||
X "$indent`t<dcsset:right xsi:type=`"$vt`">$vStr</dcsset:right>"
|
$nsAttr = Get-ValueTypeNsAttr -valueType $vt -value "$($item.value)"
|
||||||
|
X "$indent`t<dcsset:right$nsAttr xsi:type=`"$vt`">$vStr</dcsset:right>"
|
||||||
}
|
}
|
||||||
if ($item.presentation) { Emit-USPresentation -val $item.presentation -tag "dcsset:presentation" -indent "$indent`t" }
|
if ($item.presentation) { Emit-USPresentation -val $item.presentation -tag "dcsset:presentation" -indent "$indent`t" }
|
||||||
if ($item.viewMode) { X "$indent`t<dcsset:viewMode>$(Esc-Xml "$($item.viewMode)")</dcsset:viewMode>" }
|
if ($item.viewMode) { X "$indent`t<dcsset:viewMode>$(Esc-Xml "$($item.viewMode)")</dcsset:viewMode>" }
|
||||||
@@ -5164,6 +5181,7 @@ function Emit-DLValue {
|
|||||||
$valStr = if ($val -is [bool]) { if ($val) { 'true' } else { 'false' } } else { "$val" }
|
$valStr = if ($val -is [bool]) { if ($val) { 'true' } else { 'false' } } else { "$val" }
|
||||||
if ($type -match '^(date|dateTime|time)') { X "$indent<dcssch:value xsi:type=`"xs:dateTime`">$(Esc-Xml $valStr)</dcssch:value>" }
|
if ($type -match '^(date|dateTime|time)') { X "$indent<dcssch:value xsi:type=`"xs:dateTime`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
elseif ($type -eq "boolean") { X "$indent<dcssch:value xsi:type=`"xs:boolean`">$(Esc-Xml $valStr)</dcssch:value>" }
|
elseif ($type -eq "boolean") { X "$indent<dcssch:value xsi:type=`"xs:boolean`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
|
elseif ($type -eq 'v8:Type') { $nsAttr = Get-ValueTypeNsAttr -valueType 'v8:Type' -value $valStr; X "$indent<dcssch:value$nsAttr xsi:type=`"v8:Type`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
elseif ($type -match '^decimal') { X "$indent<dcssch:value xsi:type=`"xs:decimal`">$(Esc-Xml $valStr)</dcssch:value>" }
|
elseif ($type -match '^decimal') { X "$indent<dcssch:value xsi:type=`"xs:decimal`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
elseif ($type -match '^string') { X "$indent<dcssch:value xsi:type=`"xs:string`">$(Esc-Xml $valStr)</dcssch:value>" }
|
elseif ($type -match '^string') { X "$indent<dcssch:value xsi:type=`"xs:string`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent<dcssch:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml $valStr)</dcssch:value>" }
|
elseif ($type -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|BusinessProcessRef|TaskRef|ExchangePlanRef)\.') { X "$indent<dcssch:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml $valStr)</dcssch:value>" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# form-compile v1.147 — Compile 1C managed form from JSON or object metadata
|
# form-compile v1.148 — Compile 1C managed form from JSON or object metadata
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
import argparse
|
import argparse
|
||||||
import copy
|
import copy
|
||||||
@@ -1425,6 +1425,17 @@ def _value_type_for(v, explicit=None):
|
|||||||
return 'xs:string'
|
return 'xs:string'
|
||||||
|
|
||||||
|
|
||||||
|
# Значение типа v8:Type (напр. тип «Неопределено» = <prefix>:Undefined) ссылается на тип
|
||||||
|
# платформы из namespace http://v8.1c.ru/8.2/data/types — платформа объявляет его ЛОКАЛЬНО
|
||||||
|
# на теге значения (префикс авто: d6p1/d8p1/dN…). Без объявления QName битый.
|
||||||
|
def _value_type_ns_attr(value_type, value):
|
||||||
|
if value_type == 'v8:Type':
|
||||||
|
m = re.match(r'^([A-Za-z]\w*):', str(value))
|
||||||
|
if m and m.group(1) not in ('xs', 'cfg', 'v8', 'v8ui', 'ent', 'dcscor', 'dcsset', 'dcssch'):
|
||||||
|
return f' xmlns:{m.group(1)}="http://v8.1c.ru/8.2/data/types"'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def emit_filter_item(lines, item, indent):
|
def emit_filter_item(lines, item, indent):
|
||||||
if item.get('group'):
|
if item.get('group'):
|
||||||
g = str(item['group'])
|
g = str(item['group'])
|
||||||
@@ -1480,7 +1491,8 @@ def emit_filter_item(lines, item, indent):
|
|||||||
for v in val:
|
for v in val:
|
||||||
vt = _value_type_for(v, item.get('valueType'))
|
vt = _value_type_for(v, item.get('valueType'))
|
||||||
v_str = str(v).lower() if isinstance(v, bool) else esc_xml(str(v))
|
v_str = str(v).lower() if isinstance(v, bool) else esc_xml(str(v))
|
||||||
lines.append(f'{indent}\t<dcsset:right xsi:type="{vt}">{v_str}</dcsset:right>')
|
ns_attr = _value_type_ns_attr(vt, v)
|
||||||
|
lines.append(f'{indent}\t<dcsset:right{ns_attr} xsi:type="{vt}">{v_str}</dcsset:right>')
|
||||||
elif val is not None and (
|
elif val is not None and (
|
||||||
re.search(r'Standard(Beginning|End)Date$', str(item.get('valueType') or '')) or
|
re.search(r'Standard(Beginning|End)Date$', str(item.get('valueType') or '')) or
|
||||||
(not item.get('valueType') and isinstance(val, str) and re.match(r'^\d{4}-\d{2}-\d{2}T', val))):
|
(not item.get('valueType') and isinstance(val, str) and re.match(r'^\d{4}-\d{2}-\d{2}T', val))):
|
||||||
@@ -1507,7 +1519,8 @@ def emit_filter_item(lines, item, indent):
|
|||||||
elif val is not None:
|
elif val is not None:
|
||||||
vt = _value_type_for(val, item.get('valueType'))
|
vt = _value_type_for(val, item.get('valueType'))
|
||||||
v_str = str(val).lower() if isinstance(val, bool) else esc_xml(str(val))
|
v_str = str(val).lower() if isinstance(val, bool) else esc_xml(str(val))
|
||||||
lines.append(f'{indent}\t<dcsset:right xsi:type="{vt}">{v_str}</dcsset:right>')
|
ns_attr = _value_type_ns_attr(vt, val)
|
||||||
|
lines.append(f'{indent}\t<dcsset:right{ns_attr} xsi:type="{vt}">{v_str}</dcsset:right>')
|
||||||
if item.get('presentation'):
|
if item.get('presentation'):
|
||||||
emit_us_presentation(lines, f'{indent}\t', 'dcsset:presentation', item['presentation'])
|
emit_us_presentation(lines, f'{indent}\t', 'dcsset:presentation', item['presentation'])
|
||||||
if item.get('viewMode'):
|
if item.get('viewMode'):
|
||||||
@@ -4893,6 +4906,9 @@ def emit_dl_value(lines, type_str, val, indent, value_list_allowed=False):
|
|||||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:dateTime">{esc_xml(val_str)}</dcssch:value>')
|
lines.append(f'{indent}<dcssch:value xsi:type="xs:dateTime">{esc_xml(val_str)}</dcssch:value>')
|
||||||
elif t == 'boolean':
|
elif t == 'boolean':
|
||||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:boolean">{esc_xml(val_str)}</dcssch:value>')
|
lines.append(f'{indent}<dcssch:value xsi:type="xs:boolean">{esc_xml(val_str)}</dcssch:value>')
|
||||||
|
elif t == 'v8:Type':
|
||||||
|
ns_attr = _value_type_ns_attr('v8:Type', val_str)
|
||||||
|
lines.append(f'{indent}<dcssch:value{ns_attr} xsi:type="v8:Type">{esc_xml(val_str)}</dcssch:value>')
|
||||||
elif re.match(r'^decimal', t):
|
elif re.match(r'^decimal', t):
|
||||||
lines.append(f'{indent}<dcssch:value xsi:type="xs:decimal">{esc_xml(val_str)}</dcssch:value>')
|
lines.append(f'{indent}<dcssch:value xsi:type="xs:decimal">{esc_xml(val_str)}</dcssch:value>')
|
||||||
elif re.match(r'^string', t):
|
elif re.match(r'^string', t):
|
||||||
|
|||||||
@@ -1064,6 +1064,7 @@ Shorthand: `"Имя [Заголовок]: тип = Выражение #noField #
|
|||||||
- **объект** `{ variant, date? }` + `valueType` — полная форма.
|
- **объект** `{ variant, date? }` + `valueType` — полная форма.
|
||||||
- **escape** для плоской `xs:dateTime`: явный `valueType: "xs:dateTime"`.
|
- **escape** для плоской `xs:dateTime`: явный `valueType: "xs:dateTime"`.
|
||||||
Эмитится структурно (`<v8:variant>`+`<v8:date>`). Декомпилятор: Custom+date → голая дата; именованный → строка+valueType.
|
Эмитится структурно (`<v8:variant>`+`<v8:date>`). Декомпилятор: Custom+date → голая дата; именованный → строка+valueType.
|
||||||
|
- **Тип-значение `valueType: "v8:Type"`** (раундтрип; сравнение поля с *типом*, на практике — «Неопределено»): `value` несёт QName типа из namespace платформы (`<prefix>:Undefined`, префикс авто). Компилятор объявляет `xmlns:<prefix>="http://v8.1c.ru/8.2/data/types"` локально на теге значения (иначе QName битый). Применимо к `<dcsset:right>` фильтра (скаляр и массив, op `in`) и к `<dcssch:value>` параметра дин-списка (`type: "v8:Type"`).
|
||||||
- **conditionalAppearance** — объект `{ selection?, filter?, appearance?, presentation?, viewMode?, userSettingID?, use? }`. `appearance` — словарь «параметр: значение» платформы (`ЦветТекста`, `ЦветФона`, `Шрифт` и т.п.).
|
- **conditionalAppearance** — объект `{ selection?, filter?, appearance?, presentation?, viewMode?, userSettingID?, use? }`. `appearance` — словарь «параметр: значение» платформы (`ЦветТекста`, `ЦветФона`, `Шрифт` и т.п.).
|
||||||
- Значение текстовых параметров (`Текст`/`Заголовок`/`Формат`) ведётся **по форме значения**: голая строка → плоский `xs:string` (нелокализованный литерал; `""` → самозакрывающийся тег); объект `{ru,en}` → локализуемый `LocalStringType`; объект `{field:"путь"}` → ссылка на поле компоновки (`dcscor:Field`). (В отличие от `title`/`tooltip`, где голая строка = `LocalStringType` — здесь это намеренное scoped-различие: платформа хранит обе формы, и их надо различать.)
|
- Значение текстовых параметров (`Текст`/`Заголовок`/`Формат`) ведётся **по форме значения**: голая строка → плоский `xs:string` (нелокализованный литерал; `""` → самозакрывающийся тег); объект `{ru,en}` → локализуемый `LocalStringType`; объект `{field:"путь"}` → ссылка на поле компоновки (`dcscor:Field`). (В отличие от `title`/`tooltip`, где голая строка = `LocalStringType` — здесь это намеренное scoped-различие: платформа хранит обе формы, и их надо различать.)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user