feat(mxl-compile,mxl-decompile,mxl-validate): ячейки-поля ввода

Тип значения ячейки и элемент управления переживают цикл компиляции и
декомпиляции. Макет стенда с полями ввода собирается из своего же JSON
байт в байт.

DSL: два ключа ячейки — valueType (грамматика типа семьи: примитивы с
квалификаторами, ссылочные типы, категории целиком, составной через " + ")
и control (input/checkbox, синонимы, GUID для неизвестных элементов, none
для формата вовсе без тега). containsValue отдельным ключом не выражается:
он выводится из наличия типа. Текст и шаблон в такой ячейке запрещены,
параметр и расшифровка допустимы.

Умолчания голых типов взяты платформенные (строка без длины безлимитна,
число без параметров без ограничения разрядности), поэтому эмиттер типа
свой, а не копия meta-compile; resolve_type_str скопирован из эталона
семьи и объявлен в реестре дрейфа. Канон типа в ключе дедупликации
палитры развёрнут: иначе разные написания одного типа дали бы две
одинаковые записи формата и сдвинули бы все ссылки ячеек.

mxl-validate: проверка согласованности таких форматов — тип без признака
значения, ячейка с текстом и значением одновременно, посторонний тег
внутри типа, формат значения у строки или колонки, неизвестный GUID
элемента управления.

Попутно сведены два расхождения портов mxl-validate: py молчал там, где
ps1 писал строку об отсутствии шрифтов, и шапка подробного вывода
печаталась в разной форме.

Версии сведены: mxl-compile 1.41 в обоих портах (было 1.40/1.39).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-14 22:21:59 +03:00
co-authored by Claude Opus 5
parent 83bb5b6fd3
commit 7f743b18a9
37 changed files with 2471 additions and 26 deletions
+2 -1
View File
@@ -59,7 +59,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
styles: { name: { font, horizontalAlignment, verticalAlignment, textPlacement,
backColor, textColor, border, borderColor, format, hidden } },
areas: [{ name, columnSet, rows: [{ height, hidden, rowStyle, cells: [
{ col, span, rowspan, style, param, detail, text, template }
{ col, span, rowspan, style, param, detail, text, template, valueType, control }
]}]}],
namedAreas: [{ name, rows, cols }],
columnSets: { name: { columns, columnWidths, columnStyles } }
@@ -80,6 +80,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/mxl-compile.ps1" -J
- `col` — 1-based позиция колонки
- `rowspan` — объединение строк вниз (rowStyle учитывает занятые ячейки)
- Содержимое ячейки задаётся одним из ключей: `param` — параметр заполнения, `text` — статический текст, `template` — текст со вставками `[Параметр]`
- `valueType` делает ячейку полем ввода (`"Number(15,3,nonneg)"`, `"String(10)"`, `"CatalogRef.Валюты"`, составной через ` + `); текста в такой ячейке быть не может, а `control: "checkbox"` меняет поле ввода на флажок
Двухуровневая шапка массивами:
```json
@@ -182,6 +182,8 @@
| `detail` | нет | — | Параметр расшифровки (только с `param`) |
| `text` | нет | — | Статический текст. Строка или объект `{ ru, en }` — см. ниже |
| `template` | нет | — | Шаблонный текст с `[Параметр]`. Строка или объект, как `text` |
| `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) |
| `control` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` |
### Содержимое ячейки
@@ -212,6 +214,44 @@
Пустая строка — это текст: ячейка с `"text": ""` даёт пустую надпись, а не ячейку без текста.
## Ячейка-поле ввода
Ячейка может не показывать текст, а принимать значение от пользователя — так делают макеты для
ввода данных. Достаточно задать тип значения:
```json
{ "col": 1, "valueType": "Number(15,3,nonneg)" }
{ "col": 2, "valueType": "String(10)" }
{ "col": 3, "valueType": "Boolean", "control": "checkbox" }
{ "col": 4, "valueType": "CatalogRef.Валюты" }
{ "col": 5, "valueType": "Boolean + String + Date + CatalogRef.Валюты" }
{ "col": 6, "valueType": "AnyRef" }
```
Грамматика типа:
| Запись | Значение |
|---|---|
| `Boolean` | булево |
| `String`, `String(10)`, `String(10,fixed)` | строка; без длины — неограниченная, `fixed` — фиксированной длины |
| `Number`, `Number(15,3)`, `Number(15,3,nonneg)` | число; без параметров — без ограничения разрядности, `nonneg` — неотрицательное |
| `Date`, `DateTime`, `Time` | дата, дата со временем, время |
| `CatalogRef.Валюты`, `DocumentRef.Реализация`, `EnumRef.Статусы`, `DefinedType.Сумма` | ссылочный тип |
| `CatalogRef`, `DocumentRef`, `AnyRef` | категория целиком: любая ссылка этого вида |
| `A + B` | составной тип; повтор одного примитива недопустим |
Русские имена принимаются наравне с английскими: `Число(15,3)`, `Строка(10)`,
`СправочникСсылка.Валюты`.
`text` и `template` в такой ячейке недопустимы — платформа не показывает текст там, где вводится
значение. `param` и `detail` допустимы: они описывают заполнение и расшифровку, а не содержимое.
Пустой тип `"valueType": ""` — «ячейка содержит значение», но тип не ограничен.
`control` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, включая
`Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — форма
раундтрипа, для ручного авторинга не нужна.
## `rowStyle` — оформление строки
Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.
@@ -226,7 +266,8 @@ DSL описывает не все конструкции табличного
round-trip** (`/mxl-decompile``/mxl-compile`): в JSON оно не попадает, в сгенерированный
XML не возвращается.
- ячейки-поля ввода (`containsValue` / `valueType` / `controlType`);
- начальное значение ячейки-поля ввода и сериализованные настройки её элемента управления
(тип значения при этом переживает цикл);
- объединения, не привязанные к ячейке (по всей высоте или ширине документа);
- рисунки и картинки, в том числе штрихкоды, и примечания к ячейкам;
- группировки строк и колонок;
@@ -74,4 +74,4 @@
- `fillType` — выводится из того, каким ключом задано содержимое ячейки
(`text` / `param` / `template`);
- `containsValue`, `valueType`, `controlType` — свойства конкретной ячейки, а не общего
оформления; сейчас не поддерживаются.
оформления: задаются ключами ячейки `valueType` и `control`.
@@ -1,4 +1,4 @@
# mxl-compile v1.40 — Compile 1C spreadsheet from JSON
# mxl-compile v1.41 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -621,6 +621,270 @@ function Resolve-Style {
return $props
}
# --- 5.9. Ячейка-поле ввода: тип значения и элемент управления ---
# containsValue/valueType/controlType платформа держит в записи палитры формата, но принадлежат
# они конкретной ЯЧЕЙКЕ: на корпусе ERP 370 197 ссылок на такие записи — все из <f>, ни одной
# из строки, колонки или defaultFormatIndex. Поэтому в DSL это ключи ячейки, а не стиля.
# Прощающий ввод типа — общая семья Resolve-TypeStr (эталон meta-compile). Словарь синонимов
# у каждого навыка свой: здесь только то, что бывает значением ячейки макета.
$script:typeSynonyms = @{}
$script:typeSynonyms["число"] = "Number"
$script:typeSynonyms["строка"] = "String"
$script:typeSynonyms["булево"] = "Boolean"
$script:typeSynonyms["дата"] = "Date"
$script:typeSynonyms["датавремя"] = "DateTime"
$script:typeSynonyms["время"] = "Time"
$script:typeSynonyms["number"] = "Number"
$script:typeSynonyms["string"] = "String"
$script:typeSynonyms["boolean"] = "Boolean"
$script:typeSynonyms["bool"] = "Boolean"
$script:typeSynonyms["date"] = "Date"
$script:typeSynonyms["datetime"] = "DateTime"
$script:typeSynonyms["time"] = "Time"
$script:typeSynonyms["справочникссылка"] = "CatalogRef"
$script:typeSynonyms["документссылка"] = "DocumentRef"
$script:typeSynonyms["перечислениессылка"] = "EnumRef"
$script:typeSynonyms["плансчетовссылка"] = "ChartOfAccountsRef"
$script:typeSynonyms["планвидовхарактеристикссылка"] = "ChartOfCharacteristicTypesRef"
$script:typeSynonyms["планвидоврасчётассылка"] = "ChartOfCalculationTypesRef"
$script:typeSynonyms["планвидоврасчетассылка"] = "ChartOfCalculationTypesRef"
$script:typeSynonyms["планобменассылка"] = "ExchangePlanRef"
$script:typeSynonyms["бизнеспроцессссылка"] = "BusinessProcessRef"
$script:typeSynonyms["задачассылка"] = "TaskRef"
$script:typeSynonyms["любаяссылка"] = "AnyRef"
$script:typeSynonyms["catalogref"] = "CatalogRef"
$script:typeSynonyms["documentref"] = "DocumentRef"
$script:typeSynonyms["enumref"] = "EnumRef"
$script:typeSynonyms["anyref"] = "AnyRef"
$script:typeSynonyms["определяемыйтип"] = "DefinedType"
$script:typeSynonyms["definedtype"] = "DefinedType"
# Голые метатипы-категории («любой объект категории») — множество, а не тип: <v8:TypeSet>.
$script:valueTypeSets = @('CatalogRef', 'DocumentRef', 'EnumRef', 'ChartOfAccountsRef',
'ChartOfCharacteristicTypesRef', 'ChartOfCalculationTypesRef', 'ExchangePlanRef',
'BusinessProcessRef', 'TaskRef', 'AnyRef', 'AnyIBRef')
# Категории ссылочных типов с именем объекта — <v8:Type>.
$script:valueRefKinds = @('CatalogRef', 'DocumentRef', 'EnumRef', 'ChartOfAccountsRef',
'ChartOfCharacteristicTypesRef', 'ChartOfCalculationTypesRef', 'ExchangePlanRef',
'BusinessProcessRef', 'BusinessProcessRoutePointRef', 'TaskRef')
# Элемент управления. В выгрузке — GUID, имён у платформы в XML нет вовсе, поэтому канон DSL
# придуман: input и checkbox. Умолчание платформы — поле ввода для ВСЕХ типов, включая Булево
# (корпус: 63 629 форматов против 7 у флажка), поэтому input не пишем в XML как «по умолчанию»,
# а эмитим всегда — так делает платформа.
$script:valueControlGuids = @{
'input' = '381ed624-9217-4e63-85db-c4c3cb87daae'
'checkbox' = '35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26'
}
$script:valueControlSynonyms = @{
# Ключи нормализованы: без пробелов, в нижнем регистре («Поле ввода» → «полеввода»).
'полеввода' = 'input'; 'inputfield' = 'input'
'полефлажка' = 'checkbox'; 'флажок' = 'checkbox'; 'checkboxfield' = 'checkbox'
}
function Resolve-TypeStr {
param([string]$typeStr)
if (-not $typeStr) { return $typeStr }
# Прощающий ввод: ведущий префикс приходит копипастой из выгрузки. Без срезания он ломает
# поиск в словаре — русское имя типа остаётся непереведённым, и платформа отвечает
# «Неизвестное имя типа». cfg: снимаем всегда — он однозначно означает текущую конфигурацию.
# Сгенерированный dNpM: (в корпусе на этом URI встречаются d4p1, d5p1, d6p1 — имя префикса
# платформа выдаёт по порядку объявления) снимаем ТОЛЬКО у ссылочных типов, с точкой:
# сам по себе префикс многозначен — в формах d5p1:Chart, d5p1:TextDocument,
# d5p1:GeographicalSchema адресуют чужие пространства имён, и там он часть значения.
if ($typeStr.StartsWith('cfg:')) {
$typeStr = $typeStr.Substring(4)
} elseif ($typeStr.Contains('.') -and $typeStr -match '^d\d+p\d+:') {
$typeStr = $typeStr.Substring($typeStr.IndexOf(':') + 1)
}
# Параметризованные типы: Number(15,2), Строка(100)
if ($typeStr -match '^([^(]+)\((.+)\)$') {
$baseName = $Matches[1].Trim()
$params = $Matches[2]
$resolved = $script:typeSynonyms[$baseName.ToLower()]
if ($resolved) { return "$resolved($params)" }
return $typeStr
}
# Ссылочные типы: СправочникСсылка.Организации → CatalogRef.Организации
if ($typeStr.Contains('.')) {
$dotIdx = $typeStr.IndexOf('.')
$prefix = $typeStr.Substring(0, $dotIdx)
$suffix = $typeStr.Substring($dotIdx) # includes the dot
$resolved = $script:typeSynonyms[$prefix.ToLower()]
if ($resolved) { return "$resolved$suffix" }
return $typeStr
}
# Простое имя
$resolved = $script:typeSynonyms[$typeStr.ToLower()]
if ($resolved) { return $resolved }
return $typeStr
}
# Канон типа: развёрнутая запись со всеми умолчаниями. Она же ложится в ключ дедупликации
# палитры, поэтому «Число(15,3)» и «Number(15,3)» обязаны дать ОДНУ запись формата — иначе
# палитра распухнет на дубли и все ссылки <f> уедут.
# Умолчания здесь ПЛАТФОРМЕННЫЕ, а не как у реквизита метаданных: голая строка — безлимитная
# (Length 0), голое число — 0,0 (проверено на макетах, собранных Конфигуратором).
function Get-CanonValueType {
param([string]$typeStr, [string]$where)
$parts = @()
$seen = @{}
foreach ($raw in ($typeStr -split '[+|]')) {
$p = Resolve-TypeStr $raw.Trim()
if (-not $p) { continue }
$canon = $null
$kind = $null
if ($p -ceq 'Boolean') {
$canon = 'Boolean'; $kind = 'Boolean'
} elseif ($p -match '^String(\((\d+)(\s*,\s*(fixed|variable))?\))?$') {
$len = if ($Matches[2]) { $Matches[2] } else { '0' }
$al = if ($Matches[4] -and $Matches[4].ToLowerInvariant() -eq 'fixed') { 'fixed' } else { 'variable' }
$canon = "String($len,$al)"; $kind = 'String'
} elseif ($p -match '^Number(\((\d+)(\s*,\s*(\d+))?(\s*,\s*(nonneg|any))?\))?$') {
$digits = if ($Matches[2]) { $Matches[2] } else { '0' }
$fraction = if ($Matches[4]) { $Matches[4] } else { '0' }
$sign = if ($Matches[6] -and $Matches[6].ToLowerInvariant() -eq 'nonneg') { 'nonneg' } else { 'any' }
$canon = "Number($digits,$fraction,$sign)"; $kind = 'Number'
} elseif ($p -ceq 'Date' -or $p -ceq 'DateTime' -or $p -ceq 'Time') {
$canon = $p; $kind = 'Date'
} elseif ($p -match '^(DefinedType|Characteristic)\.(.+)$') {
$canon = $p; $kind = "set:$p"
} elseif ($p.Contains('.')) {
$kindName = $p.Substring(0, $p.IndexOf('.'))
if ($script:valueRefKinds -ccontains $kindName) {
$canon = $p; $kind = "ref:$p"
} else {
[Console]::Error.WriteLine("Unknown value type `"$($raw.Trim())`" ($where). Reference kinds: $($script:valueRefKinds -join ', ')")
exit 1
}
} elseif ($p.Contains(':')) {
# Чужое пространство имён — пишем как есть: калечить его мы не вправе.
$canon = $p; $kind = "raw:$p"
} elseif ($script:valueTypeSets -ccontains $p) {
$canon = $p; $kind = "set:$p"
} else {
[Console]::Error.WriteLine("Unknown value type `"$($raw.Trim())`" ($where). Expected: Boolean, String(N), Number(D,F), Date, <Kind>Ref.<Name> or a bare metatype")
exit 1
}
# Повтор одного вида в составном типе платформа выразить не может: блок квалификаторов
# внутри <valueType> один на вид.
if ($seen.ContainsKey($kind)) {
[Console]::Error.WriteLine("Duplicate type `"$canon`" in composite value type ($where)")
exit 1
}
$seen[$kind] = $true
$parts += $canon
}
if ($parts.Count -eq 0) {
[Console]::Error.WriteLine("Empty value type ($where)")
exit 1
}
return ($parts -join ' + ')
}
# Содержимое <valueType> по КАНОНУ — умолчаний здесь нет, они уже развёрнуты. Порядок снят
# с платформы: сначала ВСЕ <v8:Type>/<v8:TypeSet> в порядке источника, затем блоки
# квалификаторов Number → String → Date. Ссылочные типы несут ЛОКАЛЬНОЕ объявление xmlns
# на каждом узле — в макете префикс current-config всегда d4p1.
function Emit-ValueTypeContent {
param([string]$indent, [string]$canonType)
$cfgNs = 'http://v8.1c.ru/8.1/data/enterprise/current-config'
$typeLines = @()
$quals = @{}
foreach ($p in ($canonType -split ' \+ ')) {
if ($p -ceq 'Boolean') {
$typeLines += "$indent<v8:Type>xs:boolean</v8:Type>"
} elseif ($p -match '^String\((\d+),(fixed|variable)\)$') {
$len = $Matches[1]
$al = if ($Matches[2] -ceq 'fixed') { 'Fixed' } else { 'Variable' }
$typeLines += "$indent<v8:Type>xs:string</v8:Type>"
$quals['String'] = @(
"$indent<v8:StringQualifiers>",
"$indent`t<v8:Length>$len</v8:Length>",
"$indent`t<v8:AllowedLength>$al</v8:AllowedLength>",
"$indent</v8:StringQualifiers>")
} elseif ($p -match '^Number\((\d+),(\d+),(nonneg|any)\)$') {
$digits = $Matches[1]
$fraction = $Matches[2]
$sign = if ($Matches[3] -ceq 'nonneg') { 'Nonnegative' } else { 'Any' }
$typeLines += "$indent<v8:Type>xs:decimal</v8:Type>"
$quals['Number'] = @(
"$indent<v8:NumberQualifiers>",
"$indent`t<v8:Digits>$digits</v8:Digits>",
"$indent`t<v8:FractionDigits>$fraction</v8:FractionDigits>",
"$indent`t<v8:AllowedSign>$sign</v8:AllowedSign>",
"$indent</v8:NumberQualifiers>")
} elseif ($p -ceq 'Date' -or $p -ceq 'DateTime' -or $p -ceq 'Time') {
$typeLines += "$indent<v8:Type>xs:dateTime</v8:Type>"
$quals['Date'] = @(
"$indent<v8:DateQualifiers>",
"$indent`t<v8:DateFractions>$p</v8:DateFractions>",
"$indent</v8:DateQualifiers>")
} elseif ($p -match '^(DefinedType|Characteristic)\.') {
$typeLines += "$indent<v8:TypeSet xmlns:d4p1=`"$cfgNs`">d4p1:$p</v8:TypeSet>"
} elseif ($p.Contains('.')) {
$typeLines += "$indent<v8:Type xmlns:d4p1=`"$cfgNs`">d4p1:$p</v8:Type>"
} elseif ($p.Contains(':')) {
$typeLines += "$indent<v8:Type>$p</v8:Type>"
} else {
$typeLines += "$indent<v8:TypeSet xmlns:d4p1=`"$cfgNs`">d4p1:$p</v8:TypeSet>"
}
}
foreach ($l in $typeLines) { X $l }
foreach ($q in @('Number', 'String', 'Date')) {
if ($quals.ContainsKey($q)) { foreach ($l in $quals[$q]) { X $l } }
}
}
# Свойства значения ячейки — пустой набор, если ячейка обычная. Считаются ОДНОЙ функцией:
# пре-проход регистрации палитры и генерация обязаны получить один и тот же набор, иначе
# состав палитры разойдётся и все ссылки <f> сдвинутся.
function Get-CellValueProps {
param($cell, [string]$where)
$props = @{}
$vt = $cell.valueType
$ctl = $cell.control
if ($null -eq $vt) {
if ($null -ne $ctl -and "$ctl" -ne '') {
[Console]::Error.WriteLine("Cell 'control' requires 'valueType' ($where)")
exit 1
}
return $props
}
# Ячейка, содержащая значение, текста не несёт: платформа этого не допускает, и в корпусе
# нет ни одной такой ячейки из 370 197.
if ($null -ne $cell.text -or $null -ne $cell.template) {
[Console]::Error.WriteLine("Cell with 'valueType' cannot have 'text' or 'template' ($where)")
exit 1
}
$props['containsValue'] = 'true'
# Пустая строка — третье состояние: <containsValue> есть, тип не задан (<valueType/>).
$props['valueType'] = if ("$vt" -eq '') { '' } else { Get-CanonValueType "$vt" $where }
$name = 'input'
if ($null -ne $ctl -and "$ctl" -ne '') {
$norm = ("$ctl" -replace '\s', '').ToLowerInvariant()
if ($script:valueControlSynonyms.ContainsKey($norm)) { $norm = $script:valueControlSynonyms[$norm] }
$name = $norm
}
if ($name -ceq 'none') { return $props } # формат вовсе без <controlType>
if ($script:valueControlGuids.ContainsKey($name)) {
$props['controlType'] = $script:valueControlGuids[$name]
} elseif ($name -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
# Неизвестный элемент управления сохраняем как есть — терять его нельзя.
$props['controlType'] = $name
} else {
[Console]::Error.WriteLine("Unknown 'control' value `"$ctl`" ($where). Allowed: input, checkbox, none or a GUID")
exit 1
}
return $props
}
# --- 6. Format palette builder ---
$formatRegistry = [ordered]@{} # key -> hashtable with properties
@@ -843,8 +1107,13 @@ function Get-FillType {
# Helper: register a cell format and return its index
function Register-CellFormat {
param($styleName, [string]$fillType)
param($styleName, [string]$fillType, [hashtable]$valueProps)
$resolved = Resolve-Style -styleName $styleName -fillType $fillType
# Свойства значения кладём ПОВЕРХ стиля — до проверки на пустоту: у ячейки-поля ввода
# оформления может не быть вовсе, и без слияния она получила бы <f>0</f>, потеряв значение.
if ($valueProps) {
foreach ($k in $valueProps.Keys) { $resolved[$k] = $valueProps[$k] }
}
# У ячейки без собственного оформления формата НЕТ вовсе: <f>0</f>, где ноль — не индекс
# записи, а «формата нет». В корпусе так у 170 710 ячеек против 50 635, ссылающихся на
# формат по умолчанию; <f>0</f> встречается в 71% макетов.
@@ -870,7 +1139,8 @@ function Set-CellProp {
# это ru, en, ru1, Русский.
function Test-CellObject {
param($el)
$cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template')
$cellKeys = @('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template',
'valueType', 'control')
foreach ($p in $el.PSObject.Properties) {
if ($cellKeys -contains $p.Name) { return $true }
}
@@ -1051,7 +1321,8 @@ foreach ($area in $def.areas) {
foreach ($cell in $row.cells) {
$cellStyle = if ($cell.style) { $cell.style } elseif ($cellsStyle) { $cellsStyle } else { "default" }
$ft = Get-FillType $cell
Register-CellFormat -styleName $cellStyle -fillType $ft | Out-Null
$vp = Get-CellValueProps $cell "area `"$($area.name)`""
Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp | Out-Null
}
}
}
@@ -1332,7 +1603,8 @@ foreach ($area in $def.areas) {
$rowspan = if ($cell.rowspan) { [int]$cell.rowspan } else { 1 }
$cellStyle = if ($cell.style) { $cell.style } elseif ($cellsStyle) { $cellsStyle } else { "default" }
$ft = Get-FillType $cell
$fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft
$vp = Get-CellValueProps $cell "area `"$areaName`", row $($localRow + 1)"
$fmtIdx = Register-CellFormat -styleName $cellStyle -fillType $ft -valueProps $vp
$cellInfo = @{
Col = $colStart - 1 # 0-based
@@ -1659,6 +1931,15 @@ foreach ($key in $formatRegistry.Keys) {
X "`t`t`t`t<v8:content>$(Esc-XmlText $val)</v8:content>"
X "`t`t`t</v8:item>"
X "`t`t</$tag>"
} elseif ($tag -ceq 'valueType') {
# Пустой тип — самостоятельное состояние: «содержит значение», тип не задан.
if ("$val" -eq '') {
X "`t`t<valueType/>"
} else {
X "`t`t<valueType>"
Emit-ValueTypeContent "`t`t`t" "$val"
X "`t`t</valueType>"
}
} elseif ($script:formatTagKind[$tag] -ceq 'color' -and (Get-ColorNamespace "$val")) {
# web/win-палитры в корне документа не объявлены — платформа дописывает объявление
# прямо на узел и пишет значение с этим префиксом.
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-compile v1.39 — Compile 1C spreadsheet from JSON
# mxl-compile v1.41 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import hashlib
@@ -439,6 +439,251 @@ def get_format_key(props):
return '|'.join(parts)
# --- Ячейка-поле ввода: тип значения и элемент управления ---
# containsValue/valueType/controlType платформа держит в записи палитры формата, но принадлежат
# они конкретной ЯЧЕЙКЕ: на корпусе ERP 370 197 ссылок на такие записи — все из <f>, ни одной
# из строки, колонки или defaultFormatIndex. Поэтому в DSL это ключи ячейки, а не стиля.
# Прощающий ввод типа — общая семья resolve_type_str (эталон meta-compile). Словарь синонимов
# у каждого навыка свой: здесь только то, что бывает значением ячейки макета.
TYPE_SYNONYMS = {
'число': 'Number', 'строка': 'String', 'булево': 'Boolean',
'дата': 'Date', 'датавремя': 'DateTime', 'время': 'Time',
'number': 'Number', 'string': 'String', 'boolean': 'Boolean', 'bool': 'Boolean',
'date': 'Date', 'datetime': 'DateTime', 'time': 'Time',
'справочникссылка': 'CatalogRef',
'документссылка': 'DocumentRef',
'перечислениессылка': 'EnumRef',
'плансчетовссылка': 'ChartOfAccountsRef',
'планвидовхарактеристикссылка': 'ChartOfCharacteristicTypesRef',
'планвидоврасчётассылка': 'ChartOfCalculationTypesRef',
'планвидоврасчетассылка': 'ChartOfCalculationTypesRef',
'планобменассылка': 'ExchangePlanRef',
'бизнеспроцессссылка': 'BusinessProcessRef',
'задачассылка': 'TaskRef',
'любаяссылка': 'AnyRef',
'catalogref': 'CatalogRef', 'documentref': 'DocumentRef', 'enumref': 'EnumRef',
'anyref': 'AnyRef',
'определяемыйтип': 'DefinedType', 'definedtype': 'DefinedType',
}
# Голые метатипы-категории («любой объект категории») — множество, а не тип: <v8:TypeSet>.
VALUE_TYPE_SETS = ('CatalogRef', 'DocumentRef', 'EnumRef', 'ChartOfAccountsRef',
'ChartOfCharacteristicTypesRef', 'ChartOfCalculationTypesRef', 'ExchangePlanRef',
'BusinessProcessRef', 'TaskRef', 'AnyRef', 'AnyIBRef')
# Категории ссылочных типов с именем объекта — <v8:Type>.
VALUE_REF_KINDS = ('CatalogRef', 'DocumentRef', 'EnumRef', 'ChartOfAccountsRef',
'ChartOfCharacteristicTypesRef', 'ChartOfCalculationTypesRef', 'ExchangePlanRef',
'BusinessProcessRef', 'BusinessProcessRoutePointRef', 'TaskRef')
# Элемент управления. В выгрузке — GUID, имён у платформы в XML нет вовсе, поэтому канон DSL
# придуман: input и checkbox. Умолчание платформы — поле ввода для ВСЕХ типов, включая Булево
# (корпус: 63 629 форматов против 7 у флажка), поэтому input не пишем в XML как «по умолчанию»,
# а эмитим всегда — так делает платформа.
VALUE_CONTROL_GUIDS = {
'input': '381ed624-9217-4e63-85db-c4c3cb87daae',
'checkbox': '35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26',
}
VALUE_CONTROL_SYNONYMS = {
# Ключи нормализованы: без пробелов, в нижнем регистре («Поле ввода» → «полеввода»).
'полеввода': 'input', 'inputfield': 'input',
'полефлажка': 'checkbox', 'флажок': 'checkbox', 'checkboxfield': 'checkbox',
}
CFG_NS = 'http://v8.1c.ru/8.1/data/enterprise/current-config'
def resolve_type_str(type_str):
if not type_str:
return type_str
# Прощающий ввод: ведущий префикс приходит копипастой из выгрузки. Без срезания он ломает
# поиск в словаре — русское имя типа остаётся непереведённым, и платформа отвечает
# «Неизвестное имя типа». cfg: снимаем всегда — он однозначно означает текущую конфигурацию.
# Сгенерированный dNpM: (в корпусе на этом URI встречаются d4p1, d5p1, d6p1 — имя префикса
# платформа выдаёт по порядку объявления) снимаем ТОЛЬКО у ссылочных типов, с точкой:
# сам по себе префикс многозначен — в формах d5p1:Chart, d5p1:TextDocument,
# d5p1:GeographicalSchema адресуют чужие пространства имён, и там он часть значения.
if type_str.startswith('cfg:'):
type_str = type_str[4:]
elif '.' in type_str and re.match(r'^d\d+p\d+:', type_str):
type_str = type_str[type_str.index(':') + 1:]
# Параметризованные типы: Number(15,2), Строка(100)
m = re.match(r'^([^(]+)\((.+)\)$', type_str)
if m:
base_name = m.group(1).strip()
params = m.group(2)
resolved = TYPE_SYNONYMS.get(base_name.lower())
if resolved:
return f'{resolved}({params})'
return type_str
# Ссылочные типы: СправочникСсылка.Организации → CatalogRef.Организации
if '.' in type_str:
dot_idx = type_str.index('.')
prefix = type_str[:dot_idx]
suffix = type_str[dot_idx:] # includes the dot
resolved = TYPE_SYNONYMS.get(prefix.lower())
if resolved:
return f'{resolved}{suffix}'
return type_str
# Простое имя
resolved = TYPE_SYNONYMS.get(type_str.lower())
if resolved:
return resolved
return type_str
def canon_value_type(type_str, where):
"""Канон типа: развёрнутая запись со всеми умолчаниями. Она же ложится в ключ дедупликации
палитры, поэтому «Число(15,3)» и «Number(15,3)» обязаны дать ОДНУ запись формата — иначе
палитра распухнет на дубли и все ссылки <f> уедут.
Умолчания здесь ПЛАТФОРМЕННЫЕ, а не как у реквизита метаданных: голая строка — безлимитная
(Length 0), голое число — 0,0 (проверено на макетах, собранных Конфигуратором)."""
parts = []
seen = set()
for raw in re.split(r'[+|]', type_str):
p = resolve_type_str(raw.strip())
if not p:
continue
m_str = re.match(r'^String(\((\d+)(\s*,\s*(fixed|variable))?\))?$', p)
m_num = re.match(r'^Number(\((\d+)(\s*,\s*(\d+))?(\s*,\s*(nonneg|any))?\))?$', p)
if p == 'Boolean':
canon, kind = 'Boolean', 'Boolean'
elif m_str:
length = m_str.group(2) if m_str.group(2) else '0'
al = 'fixed' if (m_str.group(4) and m_str.group(4).lower() == 'fixed') else 'variable'
canon, kind = f'String({length},{al})', 'String'
elif m_num:
digits = m_num.group(2) if m_num.group(2) else '0'
fraction = m_num.group(4) if m_num.group(4) else '0'
sign = 'nonneg' if (m_num.group(6) and m_num.group(6).lower() == 'nonneg') else 'any'
canon, kind = f'Number({digits},{fraction},{sign})', 'Number'
elif p in ('Date', 'DateTime', 'Time'):
canon, kind = p, 'Date'
elif re.match(r'^(DefinedType|Characteristic)\.(.+)$', p):
canon, kind = p, f'set:{p}'
elif '.' in p:
kind_name = p[:p.index('.')]
if kind_name in VALUE_REF_KINDS:
canon, kind = p, f'ref:{p}'
else:
print(f'Unknown value type "{raw.strip()}" ({where}).'
f' Reference kinds: {", ".join(VALUE_REF_KINDS)}', file=sys.stderr)
sys.exit(1)
elif ':' in p:
# Чужое пространство имён — пишем как есть: калечить его мы не вправе.
canon, kind = p, f'raw:{p}'
elif p in VALUE_TYPE_SETS:
canon, kind = p, f'set:{p}'
else:
print(f'Unknown value type "{raw.strip()}" ({where}). Expected: Boolean, String(N),'
f' Number(D,F), Date, <Kind>Ref.<Name> or a bare metatype', file=sys.stderr)
sys.exit(1)
# Повтор одного вида в составном типе платформа выразить не может: блок квалификаторов
# внутри <valueType> один на вид.
if kind in seen:
print(f'Duplicate type "{canon}" in composite value type ({where})', file=sys.stderr)
sys.exit(1)
seen.add(kind)
parts.append(canon)
if not parts:
print(f'Empty value type ({where})', file=sys.stderr)
sys.exit(1)
return ' + '.join(parts)
def emit_value_type_content(lines, indent, canon_type):
"""Содержимое <valueType> по КАНОНУ — умолчаний здесь нет, они уже развёрнуты. Порядок снят
с платформы: сначала ВСЕ <v8:Type>/<v8:TypeSet> в порядке источника, затем блоки
квалификаторов Number → String → Date. Ссылочные типы несут ЛОКАЛЬНОЕ объявление xmlns
на каждом узле — в макете префикс current-config всегда d4p1."""
type_lines = []
quals = {}
for p in canon_type.split(' + '):
m_str = re.match(r'^String\((\d+),(fixed|variable)\)$', p)
m_num = re.match(r'^Number\((\d+),(\d+),(nonneg|any)\)$', p)
if p == 'Boolean':
type_lines.append(f'{indent}<v8:Type>xs:boolean</v8:Type>')
elif m_str:
al = 'Fixed' if m_str.group(2) == 'fixed' else 'Variable'
type_lines.append(f'{indent}<v8:Type>xs:string</v8:Type>')
quals['String'] = [
f'{indent}<v8:StringQualifiers>',
f'{indent}\t<v8:Length>{m_str.group(1)}</v8:Length>',
f'{indent}\t<v8:AllowedLength>{al}</v8:AllowedLength>',
f'{indent}</v8:StringQualifiers>']
elif m_num:
sign = 'Nonnegative' if m_num.group(3) == 'nonneg' else 'Any'
type_lines.append(f'{indent}<v8:Type>xs:decimal</v8:Type>')
quals['Number'] = [
f'{indent}<v8:NumberQualifiers>',
f'{indent}\t<v8:Digits>{m_num.group(1)}</v8:Digits>',
f'{indent}\t<v8:FractionDigits>{m_num.group(2)}</v8:FractionDigits>',
f'{indent}\t<v8:AllowedSign>{sign}</v8:AllowedSign>',
f'{indent}</v8:NumberQualifiers>']
elif p in ('Date', 'DateTime', 'Time'):
type_lines.append(f'{indent}<v8:Type>xs:dateTime</v8:Type>')
quals['Date'] = [
f'{indent}<v8:DateQualifiers>',
f'{indent}\t<v8:DateFractions>{p}</v8:DateFractions>',
f'{indent}</v8:DateQualifiers>']
elif re.match(r'^(DefinedType|Characteristic)\.', p):
type_lines.append(f'{indent}<v8:TypeSet xmlns:d4p1="{CFG_NS}">d4p1:{p}</v8:TypeSet>')
elif '.' in p:
type_lines.append(f'{indent}<v8:Type xmlns:d4p1="{CFG_NS}">d4p1:{p}</v8:Type>')
elif ':' in p:
type_lines.append(f'{indent}<v8:Type>{p}</v8:Type>')
else:
type_lines.append(f'{indent}<v8:TypeSet xmlns:d4p1="{CFG_NS}">d4p1:{p}</v8:TypeSet>')
lines.extend(type_lines)
for q in ('Number', 'String', 'Date'):
if q in quals:
lines.extend(quals[q])
def cell_value_props(cell, where):
"""Свойства значения ячейки — пустой набор, если ячейка обычная. Считаются ОДНОЙ функцией:
пре-проход регистрации палитры и генерация обязаны получить один и тот же набор, иначе
состав палитры разойдётся и все ссылки <f> сдвинутся."""
props = {}
vt = cell.get('valueType')
ctl = cell.get('control')
if vt is None:
if ctl is not None and str(ctl) != '':
print(f"Cell 'control' requires 'valueType' ({where})", file=sys.stderr)
sys.exit(1)
return props
# Ячейка, содержащая значение, текста не несёт: платформа этого не допускает, и в корпусе
# нет ни одной такой ячейки из 370 197.
if cell.get('text') is not None or cell.get('template') is not None:
print(f"Cell with 'valueType' cannot have 'text' or 'template' ({where})", file=sys.stderr)
sys.exit(1)
props['containsValue'] = 'true'
# Пустая строка — третье состояние: <containsValue> есть, тип не задан (<valueType/>).
props['valueType'] = '' if str(vt) == '' else canon_value_type(str(vt), where)
name = 'input'
if ctl is not None and str(ctl) != '':
norm = re.sub(r'\s', '', str(ctl)).lower()
norm = VALUE_CONTROL_SYNONYMS.get(norm, norm)
name = norm
if name == 'none':
return props # формат вовсе без <controlType>
if name in VALUE_CONTROL_GUIDS:
props['controlType'] = VALUE_CONTROL_GUIDS[name]
elif re.match(r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', name):
# Неизвестный элемент управления сохраняем как есть — терять его нельзя.
props['controlType'] = name
else:
print(f'Unknown \'control\' value "{ctl}" ({where}).'
f' Allowed: input, checkbox, none or a GUID', file=sys.stderr)
sys.exit(1)
return props
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
@@ -885,8 +1130,12 @@ def main():
return ''
# Helper: register a cell format and return its index
def register_cell_format(style_name, fill_type):
def register_cell_format(style_name, fill_type, value_props=None):
resolved = resolve_style(style_name, fill_type)
# Свойства значения кладём ПОВЕРХ стиля — до проверки на пустоту: у ячейки-поля ввода
# оформления может не быть вовсе, и без слияния она получила бы <f>0</f>, потеряв значение.
if value_props:
resolved.update(value_props)
# У ячейки без собственного оформления формата НЕТ вовсе: <f>0</f>, где ноль — не
# индекс записи, а «формата нет». В корпусе так у 170 710 ячеек против 50 635,
# ссылающихся на формат по умолчанию; <f>0</f> встречается в 71% макетов.
@@ -904,7 +1153,8 @@ def main():
"""Объект описывает СВОЙСТВА ячейки или является её ЗНАЧЕНИЕМ: в первом случае среди
ключей есть ключ схемы ячейки, во втором ключи — идентификаторы языков. Пересечений
нет: в корпусе это ru, en, ru1, Русский."""
cell_keys = ('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template')
cell_keys = ('col', 'span', 'rowspan', 'style', 'param', 'detail', 'text', 'template',
'valueType', 'control')
return any(k in el for k in cell_keys)
def expand_shorthand_row(row, area_name, row_idx, open_by_col, max_cols):
@@ -1069,7 +1319,8 @@ def main():
for cell in row['cells']:
cell_style = cell.get('style') or cells_style or 'default'
ft = get_fill_type(cell)
register_cell_format(cell_style, ft)
vp = cell_value_props(cell, f'area "{area.get("name") or ""}"')
register_cell_format(cell_style, ft, vp)
# Формат по умолчанию — последняя запись палитры (см. выше).
default_format_index = register_format({'width': default_width})
@@ -1309,7 +1560,8 @@ def main():
rowspan = int(cell.get('rowspan', 1))
cell_style = cell.get('style') or cells_style or 'default'
ft = get_fill_type(cell)
fmt_idx = register_cell_format(cell_style, ft)
vp = cell_value_props(cell, f'area "{area_name}", row {local_row + 1}')
fmt_idx = register_cell_format(cell_style, ft, vp)
cell_info = {
'Col': col_start - 1, # 0-based
@@ -1603,6 +1855,14 @@ def main():
lines.append(f'\t\t\t\t<v8:content>{esc_xml_text(val)}</v8:content>')
lines.append('\t\t\t</v8:item>')
lines.append(f'\t\t</{tag}>')
elif tag == 'valueType':
# Пустой тип — самостоятельное состояние: «содержит значение», тип не задан.
if str(val) == '':
lines.append('\t\t<valueType/>')
else:
lines.append('\t\t<valueType>')
emit_value_type_content(lines, '\t\t\t', str(val))
lines.append('\t\t</valueType>')
elif kinds.get(tag) == 'color' and color_namespace(str(val)):
# web/win-палитры в корне документа не объявлены — платформа дописывает
# объявление прямо на узел и пишет значение с этим префиксом.
@@ -1,4 +1,4 @@
# mxl-decompile v1.20 — Decompile 1C spreadsheet to JSON
# mxl-decompile v1.21 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -78,6 +78,78 @@ function ConvertTo-DslColor {
return $val
}
$CFG_NS = 'http://v8.1c.ru/8.1/data/enterprise/current-config'
# Элемент управления ячейки: в выгрузке это GUID, имён у платформы в XML нет.
$valueControlNames = @{
'381ed624-9217-4e63-85db-c4c3cb87daae' = 'input'
'35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26' = 'checkbox'
}
# Обратная функция к эмиттеру типа компилятора: <valueType> → строка DSL в КРАТЧАЙШЕЙ форме
# (Boolean, String, String(10,fixed), Number(15,3,nonneg), DateTime, CatalogRef.X, AnyRef),
# из которой компилятор восстановит тот же XML. Пустой тег → пустая строка: «содержит
# значение» без указания типа — самостоятельное состояние.
# Префикс ссылочного типа разрешаем по URI, а не по имени: локальное объявление платформа
# называет сгенерированным dNpM. Чужое пространство имён оставляем как есть.
function Get-ValueQualifier {
param($quals, [string]$kind, [string]$name, [string]$default = '')
if (-not $quals.ContainsKey($kind)) { return $default }
$sub = $quals[$kind].SelectSingleNode("v8:$name", $ns)
if ($sub -and $sub.InnerText) { return $sub.InnerText.Trim() }
return $default
}
function ConvertFrom-ValueTypeNode {
param($node)
$types = @()
$quals = @{}
foreach ($child in $node.ChildNodes) {
if ($child.NodeType -ne [System.Xml.XmlNodeType]::Element) { continue }
$tag = $child.get_LocalName()
if ($tag -ceq 'Type' -or $tag -ceq 'TypeSet') {
$types += ,@($child, $child.InnerText.Trim())
} elseif ($tag -like '*Qualifiers') {
$quals[$tag.Substring(0, $tag.Length - 'Qualifiers'.Length)] = $child
}
}
$parts = @()
foreach ($t in $types) {
$child = $t[0]
$val = $t[1]
if ($val -like '*:*') {
$prefix = $val.Substring(0, $val.IndexOf(':'))
$name = $val.Substring($val.IndexOf(':') + 1)
# get_*(): XML-адаптер PowerShell перекрывает .NET-члены одноимёнными атрибутами узла.
$uri = $child.GetNamespaceOfPrefix($prefix)
if ($uri -ceq $CFG_NS) { $val = $name }
elseif ($uri -ceq 'http://www.w3.org/2001/XMLSchema') { $val = "xs:$name" }
}
if ($val -ceq 'xs:boolean') {
$parts += 'Boolean'
} elseif ($val -ceq 'xs:string') {
$length = Get-ValueQualifier $quals 'String' 'Length' '0'
$fixed = (Get-ValueQualifier $quals 'String' 'AllowedLength' 'Variable') -ceq 'Fixed'
if ($fixed) { $parts += "String($length,fixed)" }
elseif ($length -ne '0') { $parts += "String($length)" }
else { $parts += 'String' }
} elseif ($val -ceq 'xs:decimal') {
$digits = Get-ValueQualifier $quals 'Number' 'Digits' '0'
$fraction = Get-ValueQualifier $quals 'Number' 'FractionDigits' '0'
$nonneg = (Get-ValueQualifier $quals 'Number' 'AllowedSign' 'Any') -ceq 'Nonnegative'
if ($nonneg) { $parts += "Number($digits,$fraction,nonneg)" }
elseif ($digits -ne '0' -or $fraction -ne '0') { $parts += "Number($digits,$fraction)" }
else { $parts += 'Number' }
} elseif ($val -ceq 'xs:dateTime') {
$parts += (Get-ValueQualifier $quals 'Date' 'DateFractions' 'DateTime')
} else {
$parts += $val
}
}
return ($parts -join ' + ')
}
# --- 2. Extract font palette ---
# Размер шрифта бывает дробным (8.3, 11.3 — в корпусе ERP это треть макетов). [int] его
@@ -144,6 +216,13 @@ foreach ($fmtNode in $root.SelectNodes("d:format", $ns)) {
if ($item -and $item.InnerText) { $fmt.Props[$tag] = $item.InnerText }
continue
}
# Тип значения ячейки — единственный тег со СВОЕЙ структурой: разбираем его до обеих
# отсечек ниже. Пустой <valueType/> тоже значим — это «содержит значение» без
# указания типа.
if ($tag -ceq 'valueType') {
$fmt.Props[$tag] = ConvertFrom-ValueTypeNode $child
continue
}
# Вложенный элемент скаляром не является: InnerText склеил бы всё поддерево вместе
# с переводами строк. py-порт брал .text и получал пустоту — отсюда расхождение портов.
if ($child.SelectNodes('*').Count -gt 0) { continue }
@@ -952,7 +1031,11 @@ foreach ($area in $blocks) {
$gapCells = @()
foreach ($cell in $rd.Cells) {
$hasContent = $cell.Param -or $cell.HasText
# Ячейка-поле ввода — содержательная, даже когда ни текста, ни параметра в ней нет:
# значение задаёт её формат. Без этого такая строка уходила бы в пустые.
$cf = Get-Format $cell.FormatIdx
$hasValue = ($cf -and $cf.Props['containsValue'] -ceq 'true')
$hasContent = $cell.Param -or $cell.HasText -or $hasValue
$hasMerge = $mergeMap.ContainsKey("$globalRow,$($cell.Col)")
if ($hasContent -or $hasMerge) {
@@ -1034,6 +1117,24 @@ foreach ($area in $blocks) {
}
}
# Значение (ячейка-поле ввода). Свойство живёт в формате, но принадлежит ячейке:
# на корпусе на такие записи ссылаются только <f>.
if ($cellFmt -and $cellFmt.Props['containsValue'] -ceq 'true') {
$dslCell["valueType"] = if ($cellFmt.Props.Contains('valueType')) { $cellFmt.Props['valueType'] } else { "" }
if (-not $cellFmt.Props.Contains('controlType')) {
# Формат без <controlType> вовсе — так пишут машинно-сгенерированные макеты
# регламентированной отчётности; компилятор такое повторит только по
# явному указанию.
$dslCell["control"] = "none"
} else {
$ctl = "$($cellFmt.Props['controlType'])"
$name = if ($valueControlNames.ContainsKey($ctl.ToLowerInvariant())) { $valueControlNames[$ctl.ToLowerInvariant()] } else { $ctl }
# input — умолчание компилятора, писать его значит шуметь на 63 629
# форматах корпуса.
if ($name -cne 'input') { $dslCell["control"] = $name }
}
}
# Content
$fillType = if ($cellFmt) { $cellFmt.FillType } else { "" }
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-decompile v1.20 — Decompile 1C spreadsheet to JSON
# mxl-decompile v1.21 — Decompile 1C spreadsheet to JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -117,6 +117,76 @@ def color_to_dsl(node, val):
return val
CFG_NS = 'http://v8.1c.ru/8.1/data/enterprise/current-config'
# Элемент управления ячейки: в выгрузке это GUID, имён у платформы в XML нет.
VALUE_CONTROL_NAMES = {
'381ed624-9217-4e63-85db-c4c3cb87daae': 'input',
'35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26': 'checkbox',
}
def value_type_to_dsl(node):
"""Обратная функция к эмиттеру типа компилятора: <valueType> → строка DSL в КРАТЧАЙШЕЙ
форме (Boolean, String, String(10,fixed), Number(15,3,nonneg), DateTime, CatalogRef.X,
AnyRef), из которой компилятор восстановит тот же XML. Пустой тег → пустая строка:
«содержит значение» без указания типа — самостоятельное состояние.
Префикс ссылочного типа разрешаем по URI, а не по имени: локальное объявление платформа
называет сгенерированным dNpM. Чужое пространство имён оставляем как есть."""
types = []
quals = {}
for child in node:
tag = etree.QName(child).localname
if tag in ('Type', 'TypeSet'):
types.append((tag, child, (child.text or '').strip()))
elif tag.endswith('Qualifiers'):
quals[tag[:-len('Qualifiers')]] = child
def qual(kind, name, default=''):
el = quals.get(kind)
if el is None:
return default
sub = find(el, 'v8:' + name)
val = text_of(sub)
return val.strip() if val else default
parts = []
for tag, child, val in types:
if ':' in val:
prefix, name = val.split(':', 1)
uri = child.nsmap.get(prefix)
if uri == CFG_NS:
val = name
elif uri == 'http://www.w3.org/2001/XMLSchema':
val = 'xs:' + name
if val == 'xs:boolean':
parts.append('Boolean')
elif val == 'xs:string':
length = qual('String', 'Length', '0')
fixed = qual('String', 'AllowedLength', 'Variable') == 'Fixed'
if fixed:
parts.append(f'String({length},fixed)')
elif length != '0':
parts.append(f'String({length})')
else:
parts.append('String')
elif val == 'xs:decimal':
digits = qual('Number', 'Digits', '0')
fraction = qual('Number', 'FractionDigits', '0')
nonneg = qual('Number', 'AllowedSign', 'Any') == 'Nonnegative'
if nonneg:
parts.append(f'Number({digits},{fraction},nonneg)')
elif digits != '0' or fraction != '0':
parts.append(f'Number({digits},{fraction})')
else:
parts.append('Number')
elif val == 'xs:dateTime':
parts.append(qual('Date', 'DateFractions', 'DateTime'))
else:
parts.append(val)
return ' + '.join(parts)
def int_of(node, default=0):
if node is not None and node.text:
return int(node.text)
@@ -311,6 +381,12 @@ def main():
if val:
fmt["Props"][tag] = val
continue
# Тип значения ячейки — единственный тег со СВОЕЙ структурой: разбираем его до
# обеих отсечек ниже. Пустой <valueType/> тоже значим — это «содержит значение»
# без указания типа.
if tag == "valueType":
fmt["Props"][tag] = value_type_to_dsl(child)
continue
# Вложенный элемент скаляром не является: .text дал бы пустоту, а ps1-порт брал
# InnerText и получал склейку поддерева — отсюда расхождение портов.
if len(child):
@@ -952,7 +1028,11 @@ def main():
gap_cells = []
for cell in rd["Cells"]:
has_content = cell["Param"] or cell["HasText"]
# Ячейка-поле ввода — содержательная, даже когда ни текста, ни параметра в ней
# нет: значение задаёт её формат. Без этого такая строка уходила бы в пустые.
cf = get_format(cell["FormatIdx"])
has_value = bool(cf and cf["Props"].get("containsValue") == "true")
has_content = cell["Param"] or cell["HasText"] or has_value
has_merge = f"{global_row},{cell['Col']}" in merge_map
if has_content or has_merge:
@@ -1030,6 +1110,23 @@ def main():
if sn != "default" or cells_name:
dsl_cell["style"] = sn
# Значение (ячейка-поле ввода). Свойство живёт в формате, но принадлежит
# ячейке: на корпусе на такие записи ссылаются только <f>.
if cell_fmt and cell_fmt["Props"].get("containsValue") == "true":
dsl_cell["valueType"] = cell_fmt["Props"].get("valueType", "")
ctl = cell_fmt["Props"].get("controlType")
if ctl is None:
# Формат без <controlType> вовсе — так пишут машинно-сгенерированные
# макеты регламентированной отчётности; компилятор такое повторит
# только по явному указанию.
dsl_cell["control"] = "none"
else:
name = VALUE_CONTROL_NAMES.get(ctl.lower(), ctl)
# input — умолчание компилятора, писать его значит шуметь на 63 629
# форматах корпуса.
if name != "input":
dsl_cell["control"] = name
# Content
fill_type = cell_fmt["FillType"] if cell_fmt else ""
+1 -1
View File
@@ -10,7 +10,7 @@ allowed-tools:
# /mxl-validate — валидация макета табличного документа (MXL)
Проверяет Template.xml на структурные ошибки: индексы, ссылки на палитры, диапазоны именованных областей и объединений.
Проверяет Template.xml на структурные ошибки: индексы, ссылки на палитры, диапазоны именованных областей и объединений, согласованность ячеек-полей ввода.
## Параметры
@@ -1,4 +1,4 @@
# mxl-validate v1.2 — Validate 1C spreadsheet
# mxl-validate v1.3 — Validate 1C spreadsheet
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Alias('Path')]
@@ -91,7 +91,9 @@ function Report-Warn {
$templateName = [System.IO.Path]::GetFileName([System.IO.Path]::GetDirectoryName([System.IO.Path]::GetDirectoryName($TemplatePath)))
if ($Detailed) {
Write-Host "=== Validation: $templateName ==="
# Форма имени та же, что в итоговой строке и у py-порта: раньше шапка -Detailed
# печаталась без префикса «Template.», и вывод портов расходился одной строкой.
Write-Host "=== Validation: Template.$templateName ==="
Write-Host ""
}
@@ -400,6 +402,80 @@ foreach ($drawing in $root.SelectNodes("d:drawing", $nsMgr)) {
}
}
# --- Check 13: value cells (input fields) ---
# Свойства containsValue/valueType/controlType принадлежат ЯЧЕЙКЕ: на корпусе ERP
# (370 197 ссылок) на такие записи палитры ссылаются только <f>, ни строка, ни колонка,
# ни defaultFormatIndex. Ячейка со значением текста не несёт — ни одна из 370 197.
$valueFormatIdx = @{}
$controlGuids = @('381ed624-9217-4e63-85db-c4c3cb87daae', '35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26')
for ($i = 0; $i -lt $formatNodes.Count; $i++) {
$fmt = $formatNodes[$i]
$contains = $fmt.SelectSingleNode("d:containsValue", $nsMgr)
$vt = $fmt.SelectSingleNode("d:valueType", $nsMgr)
if (-not $contains -and -not $vt) { continue }
$num = $i + 1
$valueFormatIdx[$num] = $true
if (-not $contains -or $contains.InnerText.Trim() -cne 'true') {
Report-Error "Format ${num}: <valueType> without <containsValue>true</containsValue>"
} elseif (-not $vt) {
Report-Error "Format ${num}: <containsValue> without <valueType>"
}
if ($vt) {
foreach ($child in $vt.ChildNodes) {
if ($child.NodeType -ne [System.Xml.XmlNodeType]::Element) { continue }
$tag = $child.get_LocalName()
if ($tag -cne 'Type' -and $tag -cne 'TypeSet' -and $tag -notlike '*Qualifiers') {
Report-Error "Format ${num}: unexpected <$tag> inside <valueType>"
}
}
}
$ctl = $fmt.SelectSingleNode("d:controlType", $nsMgr)
if ($ctl -and ($controlGuids -notcontains $ctl.InnerText.Trim().ToLowerInvariant())) {
Report-Warn "Format ${num}: unknown controlType $($ctl.InnerText.Trim())"
}
}
if ($valueFormatIdx.Count -gt 0) {
foreach ($ri in $root.SelectNodes("d:rowsItem", $nsMgr)) {
if ($stopped) { break }
$row = $ri.SelectSingleNode("d:row", $nsMgr)
if (-not $row) { continue }
$idxNode = $ri.SelectSingleNode("d:index", $nsMgr)
$rn = if ($idxNode) { $idxNode.InnerText } else { '?' }
$rowFmt = $row.SelectSingleNode("d:formatIndex", $nsMgr)
if ($rowFmt -and $valueFormatIdx.ContainsKey([int]$rowFmt.InnerText)) {
Report-Warn "Row ${rn}: formatIndex points to a value format (cell-only property)"
}
foreach ($cGroup in $row.SelectNodes("d:c", $nsMgr)) {
$cell = $cGroup.SelectSingleNode("d:c", $nsMgr)
if (-not $cell) { continue }
$fNode = $cell.SelectSingleNode("d:f", $nsMgr)
if (-not $fNode -or -not $valueFormatIdx.ContainsKey([int]$fNode.InnerText)) { continue }
if ($cell.SelectSingleNode("d:tl", $nsMgr)) {
Report-Error "Row ${rn}: cell contains a value and text at the same time"
}
}
}
foreach ($cols in $root.SelectNodes("d:columns", $nsMgr)) {
foreach ($ci in $cols.SelectNodes("d:columnsItem", $nsMgr)) {
$col = $ci.SelectSingleNode("d:column", $nsMgr)
if (-not $col) { continue }
$fmtNode = $col.SelectSingleNode("d:formatIndex", $nsMgr)
if ($fmtNode -and $valueFormatIdx.ContainsKey([int]$fmtNode.InnerText)) {
$colIdxNode = $ci.SelectSingleNode("d:index", $nsMgr)
$colIdxText = if ($colIdxNode) { $colIdxNode.InnerText } else { '?' }
Report-Warn "Column ${colIdxText}: formatIndex points to a value format (cell-only property)"
}
}
}
$dfi = $root.SelectSingleNode("d:defaultFormatIndex", $nsMgr)
if ($dfi -and $valueFormatIdx.ContainsKey([int]$dfi.InnerText)) {
Report-Warn "defaultFormatIndex points to a value format (cell-only property)"
}
Report-OK "Value cells: $($valueFormatIdx.Count) value formats"
}
# --- Summary ---
$checks = $script:okCount + $errors + $warnings
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-validate v1.2 — Validate 1C spreadsheet document Template.xml
# mxl-validate v1.3 — Validate 1C spreadsheet document Template.xml
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates spreadsheet Template.xml: height, palette refs, column/row indices, areas, merges."""
import sys, os, argparse
@@ -223,7 +223,11 @@ def main():
r.error(f'Font index {max_font_ref} exceeds palette size ({font_count})')
elif max_font_ref > 0:
r.error(f'Font index {max_font_ref} referenced but no fonts defined')
# No font references — no check needed
else:
# Макет без единого шрифта — норма (ячейки-поля ввода, макеты без оформления).
# Строку печатаем как ps1-порт: раньше py молчал, и на таком макете число проверок
# у портов расходилось.
r.ok('No font references')
# --- Check 11: line/border indices in formats ---
if line_count > 0:
@@ -233,7 +237,8 @@ def main():
r.error(f'Line index {max_line_ref} exceeds palette size ({line_count})')
elif max_line_ref > 0:
r.error(f'Line index {max_line_ref} referenced but no lines defined')
# No line/border references — no check needed
else:
r.ok('No line/border references')
# --- Check 3, 4, 5, 6: row/cell checks ---
max_cell_format_ref = 0
@@ -393,6 +398,67 @@ def main():
draw_id = draw_id_node.text if draw_id_node is not None else '?'
r.error(f'Drawing id={draw_id}: pictureIndex={pic_idx} > picture count ({picture_count})')
# --- Check 13: value cells (input fields) ---
# Свойства containsValue/valueType/controlType принадлежат ЯЧЕЙКЕ: на корпусе ERP
# (370 197 ссылок) на такие записи палитры ссылаются только <f>, ни строка, ни колонка,
# ни defaultFormatIndex. Ячейка со значением текста не несёт — ни одна из 370 197.
value_format_idx = set()
control_guids = ('381ed624-9217-4e63-85db-c4c3cb87daae', '35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26')
for i, fmt in enumerate(format_nodes, start=1):
contains = fmt.find(f'{{{NS_D}}}containsValue')
vt = fmt.find(f'{{{NS_D}}}valueType')
if contains is None and vt is None:
continue
value_format_idx.add(i)
if contains is None or (contains.text or '').strip() != 'true':
r.error(f'Format {i}: <valueType> without <containsValue>true</containsValue>')
elif vt is None:
r.error(f'Format {i}: <containsValue> without <valueType>')
if vt is not None:
for child in vt:
tag = child.tag.split('}')[-1]
if tag not in ('Type', 'TypeSet') and not tag.endswith('Qualifiers'):
r.error(f'Format {i}: unexpected <{tag}> inside <valueType>')
ctl = fmt.find(f'{{{NS_D}}}controlType')
if ctl is not None and (ctl.text or '').strip().lower() not in control_guids:
r.warn(f'Format {i}: unknown controlType {(ctl.text or "").strip()}')
if value_format_idx:
for ri in root.findall(f'{{{NS_D}}}rowsItem'):
if r.stopped:
break
row = ri.find(f'{{{NS_D}}}row')
if row is None:
continue
idx_node = ri.find(f'{{{NS_D}}}index')
rn = idx_node.text if idx_node is not None else '?'
row_fmt = row.find(f'{{{NS_D}}}formatIndex')
if row_fmt is not None and row_fmt.text and int(row_fmt.text) in value_format_idx:
r.warn(f'Row {rn}: formatIndex points to a value format (cell-only property)')
for c_group in row.findall(f'{{{NS_D}}}c'):
cell = c_group.find(f'{{{NS_D}}}c')
if cell is None:
continue
f_node = cell.find(f'{{{NS_D}}}f')
if f_node is None or not f_node.text or int(f_node.text) not in value_format_idx:
continue
if cell.find(f'{{{NS_D}}}tl') is not None:
r.error(f'Row {rn}: cell contains a value and text at the same time')
for cols in root.findall(f'{{{NS_D}}}columns'):
for ci in cols.findall(f'{{{NS_D}}}columnsItem'):
col = ci.find(f'{{{NS_D}}}column')
if col is None:
continue
fmt_node = col.find(f'{{{NS_D}}}formatIndex')
if fmt_node is not None and fmt_node.text and int(fmt_node.text) in value_format_idx:
col_idx_node = ci.find(f'{{{NS_D}}}index')
col_idx_text = col_idx_node.text if col_idx_node is not None else '?'
r.warn(f'Column {col_idx_text}: formatIndex points to a value format (cell-only property)')
dfi = root.find(f'{{{NS_D}}}defaultFormatIndex')
if dfi is not None and dfi.text and int(dfi.text) in value_format_idx:
r.warn('defaultFormatIndex points to a value format (cell-only property)')
r.ok(f'Value cells: {len(value_format_idx)} value formats')
# --- Finalize ---
checks = r.ok_count + r.errors + r.warnings
if r.errors == 0 and r.warnings == 0 and not detailed:
+78
View File
@@ -513,6 +513,84 @@ pictureSizeMode · picHorizontalAlignment · picVerticalAlignment · textPositio
каждой ячейки (`backColor`: 13 899 ячеек повторяют против 96). Исключение — `hidden`: оно
остаётся только у строки (24 026 против 62 856).
### Ячейка-поле ввода: `containsValue` / `valueType` / `controlType`
Ячейка может содержать не текст, а редактируемое значение. Три тега формата описывают это
целиком:
```xml
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Nonnegative</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
```
Свойство принадлежит ЯЧЕЙКЕ, хотя и живёт в общей палитре: из 370 197 ссылок на такие записи
(корпус ERP, 5 809 макетов из 10 924) все до одной идут из `<f>` — ни строка, ни колонка, ни
`<defaultFormatIndex>` на них не ссылаются ни разу.
| Наблюдение | Данные корпуса |
|---|---|
| `containsValue` всегда `true` | 63 726 форматов, других значений нет |
| `valueType` присутствует всегда | 0 форматов без него; 12 — с ПУСТЫМ `<valueType/>` (тип не ограничен) |
| текста у такой ячейки не бывает | 0 из 370 197 несут `<tl>` |
| `<parameter>` и `<detailParameter>` — бывают | 3 219 и 2 278 |
| `fillType` у формата со значением недостоверен | 94 062 записи несут `Text` при полном отсутствии текста — след прежнего состояния, как `width` |
**Элемент управления** задаётся GUID; имён в XML нет вовсе. В Конфигураторе это «ЭлементУправления»
со значениями «Поле ввода» и «Поле флажка»:
| GUID | Элемент | Вхождений |
|---|---|---:|
| `381ed624-9217-4e63-85db-c4c3cb87daae` | Поле ввода | 63 629 |
| `35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26` | Поле флажка | 7 |
Умолчание — поле ввода, и оно же стоит у Булево: из 664 булевых форматов флажок выбран в семи.
У 90 форматов (22 макета регламентированной отчётности, явно машинно-сгенерированных) тега
`<controlType>` нет вовсе.
**Структура `<valueType>`** — то же описание типов, что в метаданных: сначала все `<v8:Type>` и
`<v8:TypeSet>` в порядке источника, затем блоки квалификаторов в порядке Number → String → Date.
Голая категория без имени объекта (`CatalogRef`, `AnyRef`) — это `<v8:TypeSet>`, а не `<v8:Type>`.
Ссылочные типы несут **локальное объявление пространства имён на каждом узле**:
```xml
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.Валюты</v8:Type>
```
Префикс всегда `d4p1` (15 вхождений на корпусе, отклонений нет) — независимо от того, есть ли
в макете цвета web-палитры, которым платформа даёт `d3p1`. Корень `<document>` пространство
current-config не объявляет, поэтому вынести объявление наверх нельзя.
**Продолжение конструкции — на уровне САМОЙ ячейки**, а не палитры:
```xml
<c>
<f>34</f>
<v xsi:type="xs:decimal">0</v>
<control xsi:type="xs:base64Binary">77u/MiwxLDM4MWVkNjI0…</control>
</c>
```
| Тег ячейки | Смысл | Вхождений | Макетов |
|---|---|---:|---:|
| `<v>` | сохранённое значение ячейки; `xsi:type` соответствует объявленному типу (decimal 153 566, string 61 203, dateTime 3 333, boolean 1 012) | 219 114 | 4 578 |
| `<control>` | сериализованные настройки элемента управления (base64; внутри — тот же GUID и структура вида маски/списка выбора) | 26 370 | 1 246 |
Оба тега встречаются ИСКЛЮЧИТЕЛЬНО в ячейках, чей формат несёт `containsValue` — ни одного
вхождения в обычной ячейке. То есть это вторая половина той же конструкции, а не отдельное
свойство. Обрати внимание на совпадение имён: `<control>` у ячейки и `<controlType>` в формате —
разные вещи.
**Ловушка: `width` в формате ЯЧЕЙКИ не описывает ячейку.** Ссылка ячейки на запись палитры не
обновляется при изменении ширины колонки, поэтому там остаётся ширина от прежнего состояния
документа. Проверено на контролируемом стенде: в макете, где ширины колонок поменяли на
+1 -1
View File
@@ -74,4 +74,4 @@
- `fillType` — выводится из того, каким ключом задано содержимое ячейки
(`text` / `param` / `template`);
- `containsValue`, `valueType`, `controlType` — свойства конкретной ячейки, а не общего
оформления; сейчас не поддерживаются.
оформления: задаются ключами ячейки `valueType` и `control`.
+42 -1
View File
@@ -182,6 +182,8 @@
| `detail` | нет | — | Параметр расшифровки (только с `param`) |
| `text` | нет | — | Статический текст. Строка или объект `{ ru, en }` — см. ниже |
| `template` | нет | — | Шаблонный текст с `[Параметр]`. Строка или объект, как `text` |
| `valueType` | нет | — | Тип значения: ячейка становится полем ввода (см. ниже) |
| `control` | нет | `input` | Элемент управления поля ввода: `input` или `checkbox`. Только вместе с `valueType` |
### Содержимое ячейки
@@ -212,6 +214,44 @@
Пустая строка — это текст: ячейка с `"text": ""` даёт пустую надпись, а не ячейку без текста.
## Ячейка-поле ввода
Ячейка может не показывать текст, а принимать значение от пользователя — так делают макеты для
ввода данных. Достаточно задать тип значения:
```json
{ "col": 1, "valueType": "Number(15,3,nonneg)" }
{ "col": 2, "valueType": "String(10)" }
{ "col": 3, "valueType": "Boolean", "control": "checkbox" }
{ "col": 4, "valueType": "CatalogRef.Валюты" }
{ "col": 5, "valueType": "Boolean + String + Date + CatalogRef.Валюты" }
{ "col": 6, "valueType": "AnyRef" }
```
Грамматика типа:
| Запись | Значение |
|---|---|
| `Boolean` | булево |
| `String`, `String(10)`, `String(10,fixed)` | строка; без длины — неограниченная, `fixed` — фиксированной длины |
| `Number`, `Number(15,3)`, `Number(15,3,nonneg)` | число; без параметров — без ограничения разрядности, `nonneg` — неотрицательное |
| `Date`, `DateTime`, `Time` | дата, дата со временем, время |
| `CatalogRef.Валюты`, `DocumentRef.Реализация`, `EnumRef.Статусы`, `DefinedType.Сумма` | ссылочный тип |
| `CatalogRef`, `DocumentRef`, `AnyRef` | категория целиком: любая ссылка этого вида |
| `A + B` | составной тип; повтор одного примитива недопустим |
Русские имена принимаются наравне с английскими: `Число(15,3)`, `Строка(10)`,
`СправочникСсылка.Валюты`.
`text` и `template` в такой ячейке недопустимы — платформа не показывает текст там, где вводится
значение. `param` и `detail` допустимы: они описывают заполнение и расшифровку, а не содержимое.
Пустой тип `"valueType": ""` — «ячейка содержит значение», но тип не ограничен.
`control` нужен редко: умолчание платформы — поле ввода, и оно применяется ко всем типам, включая
`Boolean`. Флажок задаётся явно. Значение `"none"` (тега элемента управления нет вовсе) — форма
раундтрипа, для ручного авторинга не нужна.
## `rowStyle` — оформление строки
Стиль применяется ко ВСЕЙ ширине строки: позиции без явных ячеек получают тот же стиль. Так в табличных строках получаются сплошные рамки. Он же становится оформлением самой строки — именно так платформа хранит строку, оформленную целиком.
@@ -226,7 +266,8 @@ DSL описывает не все конструкции табличного
round-trip** (`/mxl-decompile``/mxl-compile`): в JSON оно не попадает, в сгенерированный
XML не возвращается.
- ячейки-поля ввода (`containsValue` / `valueType` / `controlType`);
- начальное значение ячейки-поля ввода и сериализованные настройки её элемента управления
(тип значения при этом переживает цикл);
- объединения, не привязанные к ячейке (по всей высоте или ширине документа);
- рисунки и картинки, в том числе штрихкоды, и примечания к ячейкам;
- группировки строк и колонок;
@@ -0,0 +1,14 @@
{
"name": "Ошибка: неизвестный элемент управления",
"input": {
"columns": 1,
"areas": [
{
"name": "Ввод",
"rows": [{ "cells": [{ "col": 1, "valueType": "Boolean", "control": "кнопка" }] }]
}
]
},
"params": { "outputPath": "Template.xml" },
"expectError": "Unknown 'control' value \"кнопка\" (area \"Ввод\"). Allowed: input, checkbox, none or a GUID"
}
@@ -0,0 +1,14 @@
{
"name": "Ошибка: неизвестный тип значения (голое имя не становится множеством типов)",
"input": {
"columns": 1,
"areas": [
{
"name": "Ввод",
"rows": [{ "cells": [{ "col": 1, "valueType": "Boolen" }] }]
}
]
},
"params": { "outputPath": "Template.xml" },
"expectError": "Unknown value type \"Boolen\" (area \"Ввод\")"
}
@@ -0,0 +1,14 @@
{
"name": "Ошибка: control без valueType",
"input": {
"columns": 1,
"areas": [
{
"name": "Ввод",
"rows": [{ "cells": [{ "col": 1, "control": "checkbox" }] }]
}
]
},
"params": { "outputPath": "Template.xml" },
"expectError": "Cell 'control' requires 'valueType' (area \"Ввод\")"
}
@@ -0,0 +1,14 @@
{
"name": "Ошибка: составной тип с двумя строками (блок квалификаторов один)",
"input": {
"columns": 1,
"areas": [
{
"name": "Ввод",
"rows": [{ "cells": [{ "col": 1, "valueType": "String(10) + String(20)" }] }]
}
]
},
"params": { "outputPath": "Template.xml" },
"expectError": "Duplicate type \"String(20,variable)\" in composite value type (area \"Ввод\")"
}
@@ -0,0 +1,14 @@
{
"name": "Ошибка: ячейка со значением несёт текст",
"input": {
"columns": 1,
"areas": [
{
"name": "Ввод",
"rows": [{ "cells": [{ "col": 1, "valueType": "Boolean", "text": "Да" }] }]
}
]
},
"params": { "outputPath": "Template.xml" },
"expectError": "Cell with 'valueType' cannot have 'text' or 'template' (area \"Ввод\")"
}
@@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
<languageInfo>
<id>en</id>
<code>Английский</code>
<description>Английский</description>
</languageInfo>
</languageSettings>
<columns>
<size>3</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<c>
<f>4</f>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<c>
<f>3</f>
</c>
</c>
<c>
<c>
<f>6</f>
</c>
</c>
<c>
<c>
<f>7</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>3</index>
<row>
<c>
<c>
<f>8</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>4</index>
<row>
<c>
<c>
<f>9</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>5</index>
<row>
<c>
<c>
<f>10</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>6</index>
<row>
<c>
<c>
<f>11</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>12</defaultFormatIndex>
<height>7</height>
<vgRows>7</vgRows>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Nonnegative</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>0</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Fixed</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.Валюты</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.ГорячиеКлавиши</v8:Type>
<v8:Type>xs:string</v8:Type>
<v8:Type>xs:dateTime</v8:Type>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>0</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:DocumentRef</v8:TypeSet>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:AnyRef</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>72</width>
</format>
</document>
@@ -0,0 +1,25 @@
{
"name": "Раундтрип макета ПЛАТФОРМЫ байт в байт: СЗначениями (ячейки-поля ввода)",
"setup": "fixture:platform-roundtrip",
"preRun": [
{
"script": "mxl-decompile/scripts/mxl-decompile",
"args": {
"-TemplatePath": "СЗначениями.xml",
"-OutputPath": "back.json"
},
"cwd": "{workDir}"
}
],
"params": {
"outputPath": "out.xml"
},
"noSnapshot": "эталон — файл платформы, сверяется expect.filesEqual",
"expect": {
"filesEqual": {
"actual": "out.xml",
"expected": "СЗначениями.xml"
}
},
"inputFrom": "back.json"
}
@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>3</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<c>
<f>4</f>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<c>
<f>6</f>
</c>
</c>
<c>
<c>
<f>7</f>
</c>
</c>
<c>
<c>
<f>8</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>3</index>
<row>
<c>
<c>
<f>9</f>
</c>
</c>
<c>
<c>
<f>10</f>
</c>
</c>
<c>
<c>
<f>11</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>12</defaultFormatIndex>
<height>4</height>
<vgRows>4</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Ввод</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>3</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Nonnegative</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>0</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Fixed</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:dateTime</v8:Type>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:dateTime</v8:Type>
<v8:DateQualifiers>
<v8:DateFractions>Date</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:dateTime</v8:Type>
<v8:DateQualifiers>
<v8:DateFractions>Time</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
<v8:Type>xs:string</v8:Type>
<v8:Type>xs:dateTime</v8:Type>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>0</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>6</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>4</defaultFormatIndex>
<height>1</height>
<vgRows>1</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Дедупликация</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>0</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26</controlType>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>3</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<c>
<f>4</f>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
<c>
<c>
<f>6</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>7</defaultFormatIndex>
<height>2</height>
<vgRows>2</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Ввод</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>1</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.Валюты</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:DocumentRef.Реализация</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:DefinedType.СуммаДокумента</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:DocumentRef</v8:TypeSet>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:AnyRef</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.ГорячиеКлавиши</v8:Type>
<v8:Type>xs:string</v8:Type>
<v8:Type>xs:dateTime</v8:Type>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>0</v8:Digits>
<v8:FractionDigits>0</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>4</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
<c>
<c>
<f>4</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<formatIndex>5</formatIndex>
<c>
<c>
<f>5</f>
</c>
</c>
<c>
<c>
<f>6</f>
<parameter>Сумма</parameter>
<detailParameter>Расшифровка</detailParameter>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>7</defaultFormatIndex>
<height>2</height>
<vgRows>2</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Состояния</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>1</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<line width="1" gap="false">
<v8ui:style xsi:type="v8ui:SpreadsheetDocumentCellLineType">Solid</v8ui:style>
</line>
<format>
<containsValue>true</containsValue>
<valueType/>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</controlType>
</format>
<format>
<border>0</border>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>50</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<border>0</border>
</format>
<format>
<border>0</border>
<fillType>Parameter</fillType>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,35 @@
{
"name": "Ячейки-поля ввода: примитивные типы значения",
"input": {
"columns": 3,
"areas": [
{
"name": "Ввод",
"rows": [
{ "cells": [
{ "col": 1, "valueType": "Number(15,3,nonneg)" },
{ "col": 2, "valueType": "Number" },
{ "col": 3, "valueType": "String" }
]},
{ "cells": [
{ "col": 1, "valueType": "Boolean" },
{ "col": 2, "valueType": "Boolean", "control": "checkbox" }
]},
{ "cells": [
{ "col": 1, "valueType": "String(10)" },
{ "col": 2, "valueType": "String(10,fixed)" },
{ "col": 3, "valueType": "DateTime" }
]},
{ "cells": [
{ "col": 1, "valueType": "Date" },
{ "col": 2, "valueType": "Time" },
{ "col": 3, "valueType": "Boolean + String + DateTime + Number" }
]}
]
}
]
},
"params": {
"outputPath": "Template.xml"
}
}
@@ -0,0 +1,24 @@
{
"name": "Ячейки-поля ввода: разные написания одного типа дают ОДИН формат",
"input": {
"columns": 6,
"areas": [
{
"name": "Дедупликация",
"rows": [
[
{ "valueType": "Число(15,3)" },
{ "valueType": "Number(15,3)" },
{ "valueType": "Number(15,3,any)" },
{ "valueType": "Строка(10)" },
{ "valueType": "String(10,variable)" },
{ "valueType": "Булево", "control": "Поле Флажка" }
]
]
}
]
},
"params": {
"outputPath": "Template.xml"
}
}
@@ -0,0 +1,27 @@
{
"name": "Ячейки-поля ввода: ссылочные типы и категории целиком",
"input": {
"columns": 3,
"areas": [
{
"name": "Ввод",
"rows": [
{ "cells": [
{ "col": 1, "valueType": "CatalogRef.Валюты" },
{ "col": 2, "valueType": "DocumentRef.Реализация" },
{ "col": 3, "valueType": "DefinedType.СуммаДокумента" }
]},
{ "cells": [
{ "col": 1, "valueType": "DocumentRef + CatalogRef" },
{ "col": 2, "valueType": "AnyRef" },
{ "col": 3, "valueType": "Boolean + CatalogRef.ГорячиеКлавиши + String + DateTime + Number" }
]}
]
}
]
},
"params": {
"outputPath": "Template.xml"
},
"skipPlatformVerify": "ссылочные типы разрешаются только в базе с матч-конфигурацией: временная база-заглушка таких объектов не содержит"
}
@@ -0,0 +1,26 @@
{
"name": "Ячейки-поля ввода: три состояния типа и элемент управления",
"input": {
"columns": 4,
"styles": { "bordered": { "border": "Solid" } },
"areas": [
{
"name": "Состояния",
"rows": [
{ "cells": [
{ "col": 1, "valueType": "" },
{ "col": 2, "valueType": "Number(15,3)", "control": "none" },
{ "col": 3, "valueType": "Boolean", "control": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" },
{ "col": 4, "valueType": "String(50)", "style": "bordered" }
]},
{ "rowStyle": "bordered", "cells": [
{ "col": 2, "valueType": "Number(10,2)", "param": "Сумма", "detail": "Расшифровка" }
]}
]
}
]
},
"params": {
"outputPath": "Template.xml"
}
}
@@ -0,0 +1,40 @@
{
"name": "Roundtrip — ячейки-поля ввода (тип значения и элемент управления)",
"preRun": [
{
"script": "mxl-compile/scripts/mxl-compile",
"input": {
"columns": 3,
"areas": [
{
"name": "Ввод",
"rows": [
{ "cells": [
{ "col": 1, "valueType": "Number(15,3,nonneg)" },
{ "col": 2, "valueType": "String(10,fixed)" },
{ "col": 3, "valueType": "Boolean", "control": "checkbox" }
]},
{ "cells": [
{ "col": 1, "valueType": "" },
{ "col": 2, "valueType": "Number(10,2)", "control": "none" },
{ "col": 3, "valueType": "Boolean + CatalogRef.Валюты + String + DateTime" }
]},
{ "cells": [
{ "col": 1, "valueType": "AnyRef" },
{ "col": 2, "valueType": "Number(10,2)", "param": "Сумма", "detail": "Расшифровка" }
]}
]
}
]
},
"args": { "-JsonPath": "{inputFile}", "-OutputPath": "Template.xml" },
"cwd": "{workDir}"
}
],
"params": { "templatePath": "Template.xml" },
"args_extra": ["-OutputPath", "{workDir}/back.json"],
"expect": {
"files": ["back.json"],
"stdoutContains": "[OK] Decompiled:"
}
}
@@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>3</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
</c>
</c>
<c>
<c>
<f>2</f>
</c>
</c>
<c>
<c>
<f>3</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>1</index>
<row>
<c>
<c>
<f>4</f>
</c>
</c>
<c>
<c>
<f>5</f>
</c>
</c>
<c>
<c>
<f>6</f>
</c>
</c>
</row>
</rowsItem>
<rowsItem>
<index>2</index>
<row>
<c>
<c>
<f>7</f>
</c>
</c>
<c>
<c>
<f>8</f>
<parameter>Сумма</parameter>
<detailParameter>Расшифровка</detailParameter>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>9</defaultFormatIndex>
<height>3</height>
<vgRows>3</vgRows>
<namedItem xsi:type="NamedItemCells">
<name>Ввод</name>
<area>
<type>Rows</type>
<beginRow>0</beginRow>
<endRow>2</endRow>
<beginColumn>-1</beginColumn>
<endColumn>-1</endColumn>
</area>
</namedItem>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>3</v8:FractionDigits>
<v8:AllowedSign>Nonnegative</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>10</v8:Length>
<v8:AllowedLength>Fixed</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>35af3d93-d7c7-4a2e-a8eb-bac87a1a3f26</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType/>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
<v8:Type xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:CatalogRef.Валюты</v8:Type>
<v8:Type>xs:string</v8:Type>
<v8:Type>xs:dateTime</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
<v8:DateQualifiers>
<v8:DateFractions>DateTime</v8:DateFractions>
</v8:DateQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:TypeSet xmlns:d4p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d4p1:AnyRef</v8:TypeSet>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<fillType>Parameter</fillType>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>10</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>10</width>
</format>
</document>
@@ -0,0 +1,12 @@
{
"columns": 3,
"defaultWidth": 10,
"fonts": {},
"styles": {},
"areas": [
{
"name": "Ввод",
"rows": [[{ "valueType": "Number(15,3,nonneg)" }, { "valueType": "String(10,fixed)" }, { "valueType": "Boolean", "control": "checkbox" }], [{ "valueType": "" }, { "valueType": "Number(10,2)", "control": "none" }, { "valueType": "Boolean + CatalogRef.Валюты + String + DateTime" }], [{ "valueType": "AnyRef" }, { "valueType": "Number(10,2)", "param": "Сумма", "detail": "Расшифровка" }]]
}
]
}
@@ -0,0 +1,7 @@
{
"name": "Ошибка: ячейка одновременно содержит значение и текст",
"setup": "fixture:value-cell-with-text",
"params": { "templatePath": "Template.xml" },
"expectError": true,
"expect": { "stdoutContains": "cell contains a value and text at the same time" }
}
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>1</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<c>
<c>
<f>1</f>
<tl>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Текст в ячейке со значением</v8:content>
</v8:item>
</tl>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>2</defaultFormatIndex>
<height>1</height>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>381ed624-9217-4e63-85db-c4c3cb87daae</controlType>
</format>
<format>
<width>72</width>
</format>
</document>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>1</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<formatIndex>1</formatIndex>
<c>
<c>
<f>1</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>2</defaultFormatIndex>
<height>1</height>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>11111111-2222-3333-4444-555555555555</controlType>
</format>
<format>
<width>72</width>
</format>
</document>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://v8.1c.ru/8.2/data/spreadsheet" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<languageSettings>
<currentLanguage>ru</currentLanguage>
<defaultLanguage>ru</defaultLanguage>
<languageInfo>
<id>ru</id>
<code>Русский</code>
<description>Русский</description>
</languageInfo>
</languageSettings>
<columns>
<size>1</size>
</columns>
<rowsItem>
<index>0</index>
<row>
<formatIndex>1</formatIndex>
<c>
<c>
<f>1</f>
</c>
</c>
</row>
</rowsItem>
<templateMode>true</templateMode>
<defaultFormatIndex>2</defaultFormatIndex>
<height>1</height>
<format>
<containsValue>true</containsValue>
<valueType>
<v8:Type>xs:boolean</v8:Type>
</valueType>
<controlType>11111111-2222-3333-4444-555555555555</controlType>
</format>
<format>
<width>72</width>
</format>
</document>
@@ -0,0 +1,7 @@
{
"name": "Предупреждения: формат со значением у строки и неизвестный элемент управления",
"setup": "fixture:value-format-misplaced",
"params": { "templatePath": "Template.xml" },
"args_extra": ["-Detailed"],
"expect": { "stdoutContains": "unknown controlType" }
}
+1 -1
View File
@@ -170,7 +170,7 @@ const FAMILIES = [
// TYPE_SYNONYMS / $script:typeSynonyms на свой локальный словарь.
variants: [
{ id: 'base', authority: 'meta-compile',
consumers: ['form-compile', 'form-edit', 'meta-edit', 'skd-compile', 'skd-edit'] },
consumers: ['form-compile', 'form-edit', 'meta-edit', 'mxl-compile', 'skd-compile', 'skd-edit'] },
],
},