diff --git a/.claude/skills/form-compile/scripts/form-compile.ps1 b/.claude/skills/form-compile/scripts/form-compile.ps1
index 298b7fb7..1a8d3cf7 100644
--- a/.claude/skills/form-compile/scripts/form-compile.ps1
+++ b/.claude/skills/form-compile/scripts/form-compile.ps1
@@ -1,4 +1,4 @@
-# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.115 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -2056,13 +2056,14 @@ function Emit-SingleType {
return
}
- # string or string(N)
- if ($typeStr -match '^string(\((\d+)\))?$') {
+ # string or string(N) or string(N,fixed) (AllowedLength: Variable дефолт / Fixed)
+ if ($typeStr -match '^string(\((\d+)(\s*,\s*(fixed|variable))?\))?$') {
$len = if ($Matches[2]) { $Matches[2] } else { "0" }
+ $al = if ($Matches[4] -and $Matches[4].ToLower() -eq 'fixed') { 'Fixed' } else { 'Variable' }
X "$indentxs:string"
X "$indent"
X "$indent`t$len"
- X "$indent`tVariable"
+ X "$indent`t$al"
X "$indent"
return
}
diff --git a/.claude/skills/form-compile/scripts/form-compile.py b/.claude/skills/form-compile/scripts/form-compile.py
index 97880515..429881a8 100644
--- a/.claude/skills/form-compile/scripts/form-compile.py
+++ b/.claude/skills/form-compile/scripts/form-compile.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# form-compile v1.114 — Compile 1C managed form from JSON or object metadata
+# form-compile v1.115 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -3307,14 +3307,15 @@ def emit_single_type(lines, type_str, indent):
lines.append(f'{indent}xs:boolean')
return
- # string or string(N)
- m = re.match(r'^string(\((\d+)\))?$', type_str)
+ # string or string(N) or string(N,fixed) (AllowedLength: Variable дефолт / Fixed)
+ m = re.match(r'^string(\((\d+)(\s*,\s*(fixed|variable))?\))?$', type_str, re.IGNORECASE)
if m:
length = m.group(2) if m.group(2) else '0'
+ al = 'Fixed' if (m.group(4) and m.group(4).lower() == 'fixed') else 'Variable'
lines.append(f'{indent}xs:string')
lines.append(f'{indent}')
lines.append(f'{indent}\t{length}')
- lines.append(f'{indent}\tVariable')
+ lines.append(f'{indent}\t{al}')
lines.append(f'{indent}')
return
diff --git a/.claude/skills/form-decompile/scripts/form-decompile.ps1 b/.claude/skills/form-decompile/scripts/form-decompile.ps1
index 132bfc5b..86be4675 100644
--- a/.claude/skills/form-decompile/scripts/form-decompile.ps1
+++ b/.claude/skills/form-decompile/scripts/form-decompile.ps1
@@ -1,4 +1,4 @@
-# form-decompile v0.90 — Decompile 1C managed Form.xml to JSON DSL (draft)
+# form-decompile v0.91 — Decompile 1C managed Form.xml to JSON DSL (draft)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
# ВНИМАНИЕ: раундтрип не гарантируется. Навык исключён из авто-использования моделью.
param(
@@ -1080,7 +1080,11 @@ function Decompile-Type {
switch -regex ($raw) {
'^xs:string$' {
$len = $typeNode.SelectSingleNode("v8:StringQualifiers/v8:Length", $ns)
- if ($len -and [int]$len.InnerText -gt 0) { $short = "string($($len.InnerText))" } else { $short = "string" }
+ $al = $typeNode.SelectSingleNode("v8:StringQualifiers/v8:AllowedLength", $ns)
+ $fixed = ($al -and $al.InnerText -eq 'Fixed') # Variable = дефолт (опускаем); Fixed — явно
+ if ($len -and [int]$len.InnerText -gt 0) {
+ $short = if ($fixed) { "string($($len.InnerText),fixed)" } else { "string($($len.InnerText))" }
+ } else { $short = "string" } # Length=0 → всегда Variable (корпус)
break
}
'^xs:decimal$' {
diff --git a/docs/form-dsl-spec.md b/docs/form-dsl-spec.md
index 6506a999..2bd6f8c2 100644
--- a/docs/form-dsl-spec.md
+++ b/docs/form-dsl-spec.md
@@ -1095,8 +1095,9 @@ Forgiving-синонимы типа: XML-имя (`SpreadSheetDocumentField`) и
| DSL | XML |
|-----|-----|
-| `"string"` | `xs:string` (неограниченная) |
-| `"string(100)"` | `xs:string` + Length=100 |
+| `"string"` | `xs:string` (неограниченная, AllowedLength=Variable) |
+| `"string(100)"` | `xs:string` + Length=100 (AllowedLength=Variable, дефолт) |
+| `"string(12,fixed)"` | `xs:string` + Length=12, AllowedLength=Fixed (строка фиксированной длины, напр. ИНН/КПП). Только с длиной > 0; `variable` принимается forgiving (= дефолт) |
| `"decimal(15,2)"` | `xs:decimal` + Digits=15, FractionDigits=2, AllowedSign=Any |
| `"decimal(10,0,nonneg)"` | `xs:decimal` + AllowedSign=Nonnegative |
| `"boolean"` | `xs:boolean` |
diff --git a/tests/skills/cases/form-compile/attributes-types.json b/tests/skills/cases/form-compile/attributes-types.json
index 3321cd0d..d45ed593 100644
--- a/tests/skills/cases/form-compile/attributes-types.json
+++ b/tests/skills/cases/form-compile/attributes-types.json
@@ -30,7 +30,12 @@
{ "name": "Период", "type": "СтандартныйПериод", "save": ["Период", "EndDate", "StartDate", "Variant"] },
{ "name": "СписокЗначений", "type": "ValueList", "valueType": "string(50) | decimal(10,2)" },
{ "name": "СписокЛюбой", "type": "ValueList", "valueType": "" },
- { "name": "Идентификатор", "type": "v8:UUID" }
+ { "name": "Идентификатор", "type": "v8:UUID" },
+ { "name": "Таблица", "type": "ValueTable", "title": "Таблица", "columns": [
+ { "name": "ИНН", "type": "string(12,fixed)" },
+ { "name": "Код", "type": "string(9,fixed)" },
+ { "name": "Имя", "type": "string(100)" }
+ ]}
]
}
}
diff --git a/tests/skills/cases/form-compile/snapshots/attributes-types/DataProcessors/Типы/Forms/Форма/Ext/Form.xml b/tests/skills/cases/form-compile/snapshots/attributes-types/DataProcessors/Типы/Forms/Форма/Ext/Form.xml
index bfd843be..64da1196 100644
--- a/tests/skills/cases/form-compile/snapshots/attributes-types/DataProcessors/Типы/Forms/Форма/Ext/Form.xml
+++ b/tests/skills/cases/form-compile/snapshots/attributes-types/DataProcessors/Типы/Forms/Форма/Ext/Form.xml
@@ -164,5 +164,45 @@
v8:UUID
+
+
+
+ ru
+ Таблица
+
+
+
+ v8:ValueTable
+
+
+
+
+ xs:string
+
+ 12
+ Fixed
+
+
+
+
+
+ xs:string
+
+ 9
+ Fixed
+
+
+
+
+
+ xs:string
+
+ 100
+ Variable
+
+
+
+
+