fix(form-compile): внятный отказ на группу additionalColumns без ключа columns

PS-порт падал с «Не удается индексировать в массив NULL»: @($null).Count в
PowerShell равен единице, поэтому ветка самозакрывающегося тега была
недостижима, и код шёл эмитить несуществующую колонку. PY на том же входе
молча писал пустую группу — портируемого поведения не было вовсе.

Разведены два случая, которые до сих пор путались:
- `"columns": []` — явно пустая группа, законная форма (платформа так
  пишет таблицу без доп. колонок). Работает как работала, self-closing;
- ключа `columns` нет вовсе — недосказанность автора: «доп. колонки есть»,
  а какие, не сказано. Теперь отказ с указанием, что делать.

Валидатор такое по-прежнему пропускает: в выгрузке платформы пустая группа
встречается, запрет на авторинг не равен запрету на существование.

Заодно кейс cfe-borrow/form-main-attr-columns вернулся к форме эталона
Конфигуратора — таблица без колонок описана явно пустым списком, как в
выгрузке, а не обходным манёвром вокруг падения.
This commit is contained in:
Nick Shirokov
2026-08-12 17:01:10 +03:00
parent a6cb656a2d
commit c9b64f3a0d
6 changed files with 157 additions and 64 deletions
@@ -1,4 +1,4 @@
# form-compile v1.191 — Compile 1C managed form from JSON or object metadata (+write_xml_file/write_utf8_bom: общий эталон записи)
# form-compile v1.192 — Compile 1C managed form from JSON or object metadata (гвард на группу additionalColumns без ключа columns)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -5926,9 +5926,17 @@ function Emit-Attributes {
}
if ($hasAddCols) {
foreach ($ac in @($attr.additionalColumns)) {
# Пустой список колонок задаётся ЯВНО (`"columns": []`) — это законная форма,
# платформа так пишет таблицу, у которой доп. колонок нет. А вот отсутствие ключа
# — недосказанность автора: «доп. колонки есть», а какие, не указано. Раньше на
# этом PS падал с «Не удается индексировать в массив NULL» (@($null).Count = 1).
if ($null -eq $ac.PSObject.Properties['columns'] -or $null -eq $ac.columns) {
Write-Error "additionalColumns group for table '$($ac.table)': key 'columns' is missing — list the columns, or pass an empty array for a table without extra columns"
exit 1
}
$acCols = @($ac.columns)
if ($acCols.Count -eq 0) {
# Пустая группа доп.колонок (table-ref без колонок) → self-closing (как платформа)
# Явно пустая группа → self-closing (как платформа)
X "$inner`t<AdditionalColumns table=`"$($ac.table)`"/>"
continue
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.191 — Compile 1C managed form from JSON or object metadata (+write_xml_file/write_utf8_bom: общий эталон записи)
# form-compile v1.192 — Compile 1C managed form from JSON or object metadata (гвард на группу additionalColumns без ключа columns)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -5773,9 +5773,18 @@ def emit_attributes(lines, attrs, indent, conditional_appearance=None):
emit_attr_column(lines, col, f'{inner}\t')
if has_add_cols:
for ac in attr['additionalColumns']:
ac_cols = ac.get('columns') or []
# Пустой список колонок задаётся ЯВНО (`"columns": []`) — это законная форма,
# платформа так пишет таблицу, у которой доп. колонок нет. А вот отсутствие ключа
# — недосказанность автора: «доп. колонки есть», а какие, не указано. PS-порт на
# этом падал с «Не удается индексировать в массив NULL» (@($null).Count = 1).
if ac.get('columns') is None:
print(f"additionalColumns group for table '{ac['table']}': key 'columns' is missing "
"— list the columns, or pass an empty array for a table without extra columns",
file=sys.stderr)
sys.exit(1)
ac_cols = ac['columns']
if not ac_cols:
# Пустая группа доп.колонок (table-ref без колонок) → self-closing (как платформа)
# Явно пустая группа → self-closing (как платформа)
lines.append(f'{inner}\t<AdditionalColumns table="{ac["table"]}"/>')
continue
lines.append(f'{inner}\t<AdditionalColumns table="{ac["table"]}">')
@@ -4,59 +4,134 @@
{
"script": "meta-compile/scripts/meta-compile",
"input": {
"type": "Catalog", "name": "Номенклатура",
"attributes": [ { "name": "Артикул", "type": "String", "length": 25 } ]
"type": "Catalog",
"name": "Номенклатура",
"attributes": [
{
"name": "Артикул",
"type": "String",
"length": 25
}
]
},
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
"args": {
"-JsonPath": "{inputFile}",
"-OutputDir": "{workDir}"
}
},
{
"script": "meta-compile/scripts/meta-compile",
"input": {
"type": "Document", "name": "ЗаказКлиента",
"attributes": [ { "name": "Комментарий", "type": "String", "length": 100 } ],
"type": "Document",
"name": "ЗаказКлиента",
"attributes": [
{
"name": "Комментарий",
"type": "String",
"length": 100
}
],
"tabularSections": {
"Товары": [
{ "name": "Номенклатура", "type": "CatalogRef.Номенклатура" },
{ "name": "Количество", "type": "Number(10,3)" }
{
"name": "Номенклатура",
"type": "CatalogRef.Номенклатура"
},
{
"name": "Количество",
"type": "Number(10,3)"
}
],
"Оплата": [ { "name": "Сумма", "type": "Number(15,2)" } ]
"Оплата": [
{
"name": "Сумма",
"type": "Number(15,2)"
}
]
}
},
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
"args": {
"-JsonPath": "{inputFile}",
"-OutputDir": "{workDir}"
}
},
{
"script": "form-add/scripts/form-add",
"args": { "-ObjectPath": "{workDir}/Documents/ЗаказКлиента.xml", "-FormName": "ФормаДокумента" }
"args": {
"-ObjectPath": "{workDir}/Documents/ЗаказКлиента.xml",
"-FormName": "ФормаДокумента"
}
},
{
"script": "form-compile/scripts/form-compile",
"input": {
"title": "Заказ клиента",
"elements": [
{ "input": "Комментарий", "path": "Объект.Комментарий" },
{ "table": "Товары", "path": "Объект.Товары", "columns": [
{ "input": "Номенклатура", "path": "Объект.Товары.Номенклатура" },
{ "label": "Артикул", "path": "Объект.Товары.Артикул" }
] }
{
"input": "Комментарий",
"path": "Объект.Комментарий"
},
{
"table": "Товары",
"path": "Объект.Товары",
"columns": [
{
"input": "Номенклатура",
"path": "Объект.Товары.Номенклатура"
},
{
"label": "Артикул",
"path": "Объект.Товары.Артикул"
}
]
}
],
"attributes": [
{
"name": "Объект", "type": "DocumentObject.ЗаказКлиента", "main": true,
"useAlways": ["Объект.Комментарий"],
"name": "Объект",
"type": "DocumentObject.ЗаказКлиента",
"main": true,
"useAlways": [
"Объект.Комментарий"
],
"additionalColumns": [
{ "table": "Объект.Оплата", "columns": [ { "name": "СуммаПрописью", "type": "String(200)" } ] },
{ "table": "Объект.Товары", "columns": [ { "name": "Артикул", "type": "String(25)" } ] }
{
"table": "Объект.Оплата",
"columns": []
},
{
"table": "Объект.Товары",
"columns": [
{
"name": "Артикул",
"type": "String(25)"
}
]
}
]
}
]
},
"args": { "-JsonPath": "{inputFile}", "-OutputPath": "{workDir}/Documents/ЗаказКлиента/Forms/ФормаДокумента/Ext/Form.xml" }
"args": {
"-JsonPath": "{inputFile}",
"-OutputPath": "{workDir}/Documents/ЗаказКлиента/Forms/ФормаДокумента/Ext/Form.xml"
}
},
{
"script": "cfe-init/scripts/cfe-init",
"args": { "-Name": "Тест", "-OutputDir": "{workDir}/ext", "-ConfigPath": "{workDir}" }
"args": {
"-Name": "Тест",
"-OutputDir": "{workDir}/ext",
"-ConfigPath": "{workDir}"
}
}
],
"params": { "extensionPath": "ext", "object": "Document.ЗаказКлиента.Form.ФормаДокумента" },
"args_extra": ["-BorrowMainAttribute", "Form"]
"params": {
"extensionPath": "ext",
"object": "Document.ЗаказКлиента.Form.ФормаДокумента"
},
"args_extra": [
"-BorrowMainAttribute",
"Form"
]
}
@@ -73,19 +73,9 @@
<Field>Объект.Комментарий</Field>
</UseAlways>
<Columns>
<AdditionalColumns table="Объект.Оплата">
<Column name="СуммаПрописью" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>200</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
</Column>
</AdditionalColumns>
<AdditionalColumns table="Объект.Оплата"/>
<AdditionalColumns table="Объект.Товары">
<Column name="Артикул" id="25">
<Column name="Артикул" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
@@ -71,19 +71,9 @@
<Field>Объект.Комментарий</Field>
</UseAlways>
<Columns>
<AdditionalColumns table="Объект.Оплата">
<Column name="СуммаПрописью" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>200</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
</Column>
</AdditionalColumns>
<AdditionalColumns table="Объект.Оплата"/>
<AdditionalColumns table="Объект.Товары">
<Column name="Артикул" id="25">
<Column name="Артикул" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
@@ -168,19 +158,9 @@
<Field>Объект.Комментарий</Field>
</UseAlways>
<Columns>
<AdditionalColumns table="Объект.Оплата">
<Column name="СуммаПрописью" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>200</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
</Column>
</AdditionalColumns>
<AdditionalColumns table="Объект.Оплата"/>
<AdditionalColumns table="Объект.Товары">
<Column name="Артикул" id="25">
<Column name="Артикул" id="24">
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
@@ -0,0 +1,31 @@
{
"name": "Группа доп. колонок без ключа columns → [ERROR] exit 1",
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": {
"type": "DataProcessor",
"name": "ДопКолонкиБезКлюча",
"tabularSections": [
{ "name": "Прочее", "attributes": [ { "name": "Значение", "type": "String", "length": 50 } ] }
]
},
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
},
{
"script": "form-add/scripts/form-add",
"args": { "-ObjectPath": "{workDir}/DataProcessors/ДопКолонкиБезКлюча.xml", "-FormName": "Форма" }
}
],
"params": { "outputPath": "DataProcessors/ДопКолонкиБезКлюча/Forms/Форма/Ext/Form.xml" },
"expectError": "key 'columns' is missing",
"input": {
"title": "Доп. колонки без ключа",
"elements": [ { "label": "Подпись", "title": "Группа без columns" } ],
"attributes": [
{ "name": "Объект", "type": "DataProcessorObject.ДопКолонкиБезКлюча", "main": true,
"additionalColumns": [ { "table": "Объект.Прочее" } ]
}
]
}
}