mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-20 17:40:59 +03:00
feat(form-compile): add --from-object support for 5 new object types
Add InformationRegister (Record/List), AccumulationRegister (List), ChartOfCharacteristicTypes (Item/Folder/List/Choice via Catalog delegation), ExchangePlan (Item/List/Choice via Catalog delegation), ChartOfAccounts (Item/Folder/List/Choice with AccountingFlags + ExtDimensionTypes). Generalize extractAttrs → extractFields with tag parameter. Add preset defaults and erp-standard.json keys for all new types. Bump version to v1.6 in both PS1 and PY. Also: form-add now supports AccumulationRegister (PS1+PY). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
97a2ef91d5
commit
b8ab791456
@@ -73,7 +73,7 @@ if (-not $metaDataObject) {
|
|||||||
$supportedTypes = @(
|
$supportedTypes = @(
|
||||||
"Document", "Catalog", "DataProcessor", "Report",
|
"Document", "Catalog", "DataProcessor", "Report",
|
||||||
"ExternalDataProcessor", "ExternalReport",
|
"ExternalDataProcessor", "ExternalReport",
|
||||||
"InformationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes",
|
"InformationRegister", "AccumulationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes",
|
||||||
"ExchangePlan", "BusinessProcess", "Task"
|
"ExchangePlan", "BusinessProcess", "Task"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -280,6 +280,7 @@ if ($Purpose -eq "List" -or $Purpose -eq "Choice") {
|
|||||||
"BusinessProcess" = "BusinessProcessObject"
|
"BusinessProcess" = "BusinessProcessObject"
|
||||||
"Task" = "TaskObject"
|
"Task" = "TaskObject"
|
||||||
"InformationRegister" = "InformationRegisterRecordManager"
|
"InformationRegister" = "InformationRegisterRecordManager"
|
||||||
|
"AccumulationRegister" = "AccumulationRegisterRecordSet"
|
||||||
}
|
}
|
||||||
|
|
||||||
$mainAttrType = "$($attrTypeMap[$objectType]).$objectName"
|
$mainAttrType = "$($attrTypeMap[$objectType]).$objectName"
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ def main():
|
|||||||
supported_types = [
|
supported_types = [
|
||||||
"Document", "Catalog", "DataProcessor", "Report",
|
"Document", "Catalog", "DataProcessor", "Report",
|
||||||
"ExternalDataProcessor", "ExternalReport",
|
"ExternalDataProcessor", "ExternalReport",
|
||||||
"InformationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes",
|
"InformationRegister", "AccumulationRegister", "ChartOfAccounts", "ChartOfCharacteristicTypes",
|
||||||
"ExchangePlan", "BusinessProcess", "Task",
|
"ExchangePlan", "BusinessProcess", "Task",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -310,6 +310,7 @@ def main():
|
|||||||
"BusinessProcess": "BusinessProcessObject",
|
"BusinessProcess": "BusinessProcessObject",
|
||||||
"Task": "TaskObject",
|
"Task": "TaskObject",
|
||||||
"InformationRegister": "InformationRegisterRecordManager",
|
"InformationRegister": "InformationRegisterRecordManager",
|
||||||
|
"AccumulationRegister": "AccumulationRegisterRecordSet",
|
||||||
}
|
}
|
||||||
|
|
||||||
main_attr_type = f"{attr_type_map[object_type]}.{object_name}"
|
main_attr_type = f"{attr_type_map[object_type]}.{object_name}"
|
||||||
|
|||||||
@@ -40,5 +40,29 @@
|
|||||||
"tabularSections": {
|
"tabularSections": {
|
||||||
"exclude": ["ДополнительныеРеквизиты", "Представления"]
|
"exclude": ["ДополнительныеРеквизиты", "Представления"]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"informationRegister.record": {
|
||||||
|
"properties": {
|
||||||
|
"windowOpeningMode": "LockOwnerWindow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"informationRegister.list": {},
|
||||||
|
|
||||||
|
"accumulationRegister.list": {},
|
||||||
|
|
||||||
|
"chartOfCharacteristicTypes.item": {
|
||||||
|
"basedOn": "catalog.item"
|
||||||
|
},
|
||||||
|
|
||||||
|
"exchangePlan.item": {
|
||||||
|
"basedOn": "catalog.item"
|
||||||
|
},
|
||||||
|
|
||||||
|
"chartOfAccounts.item": {
|
||||||
|
"parent": {
|
||||||
|
"title": "Подчинен счету"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# form-compile v1.5 — Compile 1C managed form from JSON or object metadata
|
# form-compile v1.6 — Compile 1C managed form from JSON or object metadata
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
param(
|
param(
|
||||||
[string]$JsonPath,
|
[string]$JsonPath,
|
||||||
@@ -65,30 +65,30 @@ function Parse-ObjectMeta([string]$ObjectPath) {
|
|||||||
return ($t -match 'Ref\.' -or $t -match 'ссылка\.')
|
return ($t -match 'Ref\.' -or $t -match 'ссылка\.')
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper: extract attribute list from ChildObjects
|
# Helper: extract field list from ChildObjects by tag name (Attribute, Dimension, Resource, AccountingFlag, ExtDimensionAccountingFlag)
|
||||||
$extractAttrs = {
|
$extractFields = {
|
||||||
param($parentNode)
|
param($parentNode, [string]$tagName)
|
||||||
$result = @()
|
$result = @()
|
||||||
if (-not $parentNode) { return $result }
|
if (-not $parentNode) { return $result }
|
||||||
foreach ($attrNode in $parentNode.SelectNodes("md:Attribute", $ns)) {
|
foreach ($fieldNode in $parentNode.SelectNodes("md:$tagName", $ns)) {
|
||||||
$ap = $attrNode.SelectSingleNode("md:Properties", $ns)
|
$fp = $fieldNode.SelectSingleNode("md:Properties", $ns)
|
||||||
$aName = $ap.SelectSingleNode("md:Name", $ns).InnerText
|
$fName = $fp.SelectSingleNode("md:Name", $ns).InnerText
|
||||||
$aSynNode = $ap.SelectSingleNode("md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
$fSynNode = $fp.SelectSingleNode("md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
||||||
$aSyn = if ($aSynNode) { $aSynNode.InnerText } else { $aName }
|
$fSyn = if ($fSynNode) { $fSynNode.InnerText } else { $fName }
|
||||||
$aTypeNode = $ap.SelectSingleNode("md:Type", $ns)
|
$fTypeNode = $fp.SelectSingleNode("md:Type", $ns)
|
||||||
$aType = & $extractType $aTypeNode
|
$fType = & $extractType $fTypeNode
|
||||||
$result += @{
|
$result += @{
|
||||||
Name = $aName
|
Name = $fName
|
||||||
Synonym = $aSyn
|
Synonym = $fSyn
|
||||||
Type = $aType
|
Type = $fType
|
||||||
IsRef = (& $isRefType $aType)
|
IsRef = (& $isRefType $fType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $result
|
return $result
|
||||||
}
|
}
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
$attributes = @(& $extractAttrs $childObjs)
|
$attributes = @(& $extractFields $childObjs "Attribute")
|
||||||
|
|
||||||
# Tabular sections
|
# Tabular sections
|
||||||
$tabularSections = @()
|
$tabularSections = @()
|
||||||
@@ -99,7 +99,7 @@ function Parse-ObjectMeta([string]$ObjectPath) {
|
|||||||
$tsSynNode = $tsp.SelectSingleNode("md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
$tsSynNode = $tsp.SelectSingleNode("md:Synonym/v8:item[v8:lang='ru']/v8:content", $ns)
|
||||||
$tsSyn = if ($tsSynNode) { $tsSynNode.InnerText } else { $tsName }
|
$tsSyn = if ($tsSynNode) { $tsSynNode.InnerText } else { $tsName }
|
||||||
$tsCo = $tsNode.SelectSingleNode("md:ChildObjects", $ns)
|
$tsCo = $tsNode.SelectSingleNode("md:ChildObjects", $ns)
|
||||||
$tsCols = @(& $extractAttrs $tsCo)
|
$tsCols = @(& $extractFields $tsCo "Attribute")
|
||||||
$tabularSections += @{
|
$tabularSections += @{
|
||||||
Name = $tsName
|
Name = $tsName
|
||||||
Synonym = $tsSyn
|
Synonym = $tsSyn
|
||||||
@@ -138,6 +138,59 @@ function Parse-ObjectMeta([string]$ObjectPath) {
|
|||||||
}
|
}
|
||||||
$meta.Owners = $owners
|
$meta.Owners = $owners
|
||||||
}
|
}
|
||||||
|
"InformationRegister" {
|
||||||
|
$meta.Dimensions = @(& $extractFields $childObjs "Dimension")
|
||||||
|
$meta.Resources = @(& $extractFields $childObjs "Resource")
|
||||||
|
$prdNode = $propsNode.SelectSingleNode("md:InformationRegisterPeriodicity", $ns)
|
||||||
|
$meta.Periodicity = if ($prdNode) { $prdNode.InnerText } else { "Nonperiodical" }
|
||||||
|
$wmNode = $propsNode.SelectSingleNode("md:WriteMode", $ns)
|
||||||
|
$meta.WriteMode = if ($wmNode) { $wmNode.InnerText } else { "Independent" }
|
||||||
|
}
|
||||||
|
"AccumulationRegister" {
|
||||||
|
$meta.Dimensions = @(& $extractFields $childObjs "Dimension")
|
||||||
|
$meta.Resources = @(& $extractFields $childObjs "Resource")
|
||||||
|
$rtNode = $propsNode.SelectSingleNode("md:RegisterType", $ns)
|
||||||
|
$meta.RegisterType = if ($rtNode) { $rtNode.InnerText } else { "Balances" }
|
||||||
|
}
|
||||||
|
"ChartOfCharacteristicTypes" {
|
||||||
|
$clNode = $propsNode.SelectSingleNode("md:CodeLength", $ns)
|
||||||
|
$meta.CodeLength = if ($clNode) { [int]$clNode.InnerText } else { 0 }
|
||||||
|
$dlNode = $propsNode.SelectSingleNode("md:DescriptionLength", $ns)
|
||||||
|
$meta.DescriptionLength = if ($dlNode) { [int]$dlNode.InnerText } else { 0 }
|
||||||
|
$hiNode = $propsNode.SelectSingleNode("md:Hierarchical", $ns)
|
||||||
|
$meta.Hierarchical = ($hiNode -and $hiNode.InnerText -eq "true")
|
||||||
|
$htNode = $propsNode.SelectSingleNode("md:HierarchyType", $ns)
|
||||||
|
$meta.HierarchyType = if ($htNode) { $htNode.InnerText } else { "HierarchyFoldersAndItems" }
|
||||||
|
$owners = @()
|
||||||
|
foreach ($ow in $propsNode.SelectNodes("md:Owners/xr:Item", $ns)) {
|
||||||
|
$owners += $ow.InnerText
|
||||||
|
}
|
||||||
|
$meta.Owners = $owners
|
||||||
|
$meta.HasValueType = $true
|
||||||
|
}
|
||||||
|
"ExchangePlan" {
|
||||||
|
$clNode = $propsNode.SelectSingleNode("md:CodeLength", $ns)
|
||||||
|
$meta.CodeLength = if ($clNode) { [int]$clNode.InnerText } else { 0 }
|
||||||
|
$dlNode = $propsNode.SelectSingleNode("md:DescriptionLength", $ns)
|
||||||
|
$meta.DescriptionLength = if ($dlNode) { [int]$dlNode.InnerText } else { 0 }
|
||||||
|
$meta.Hierarchical = $false
|
||||||
|
$meta.HierarchyType = $null
|
||||||
|
$meta.Owners = @()
|
||||||
|
}
|
||||||
|
"ChartOfAccounts" {
|
||||||
|
$clNode = $propsNode.SelectSingleNode("md:CodeLength", $ns)
|
||||||
|
$meta.CodeLength = if ($clNode) { [int]$clNode.InnerText } else { 0 }
|
||||||
|
$dlNode = $propsNode.SelectSingleNode("md:DescriptionLength", $ns)
|
||||||
|
$meta.DescriptionLength = if ($dlNode) { [int]$dlNode.InnerText } else { 0 }
|
||||||
|
$meta.Hierarchical = $true
|
||||||
|
$htNode = $propsNode.SelectSingleNode("md:HierarchyType", $ns)
|
||||||
|
$meta.HierarchyType = if ($htNode) { $htNode.InnerText } else { "HierarchyFoldersAndItems" }
|
||||||
|
$meta.Owners = @()
|
||||||
|
$maxEdNode = $propsNode.SelectSingleNode("md:MaxExtDimensionCount", $ns)
|
||||||
|
$meta.MaxExtDimensionCount = if ($maxEdNode) { [int]$maxEdNode.InnerText } else { 0 }
|
||||||
|
$meta.AccountingFlags = @(& $extractFields $childObjs "AccountingFlag")
|
||||||
|
$meta.ExtDimensionAccountingFlags = @(& $extractFields $childObjs "ExtDimensionAccountingFlag")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $meta
|
return $meta
|
||||||
@@ -189,6 +242,41 @@ function Load-Preset([string]$PresetName, [string]$ScriptDir) {
|
|||||||
basedOn = "catalog.list"; choiceMode = $true
|
basedOn = "catalog.list"; choiceMode = $true
|
||||||
properties = @{ windowOpeningMode = "LockOwnerWindow" }
|
properties = @{ windowOpeningMode = "LockOwnerWindow" }
|
||||||
}
|
}
|
||||||
|
# ─── Register defaults ───
|
||||||
|
"informationRegister.record" = @{
|
||||||
|
fieldDefaults = @{ ref = @{ choiceButton = $true }; boolean = @{ element = "check" } }
|
||||||
|
properties = @{ windowOpeningMode = "LockOwnerWindow" }
|
||||||
|
}
|
||||||
|
"informationRegister.list" = @{
|
||||||
|
columns = "all"; columnType = "labelField"
|
||||||
|
tableCommandBar = "none"; commandBar = "auto"
|
||||||
|
properties = @{}
|
||||||
|
}
|
||||||
|
"accumulationRegister.list" = @{
|
||||||
|
columns = "all"; columnType = "labelField"
|
||||||
|
tableCommandBar = "none"; commandBar = "auto"
|
||||||
|
properties = @{}
|
||||||
|
}
|
||||||
|
# ─── Catalog-like type defaults ───
|
||||||
|
"chartOfCharacteristicTypes.item" = @{ basedOn = "catalog.item" }
|
||||||
|
"chartOfCharacteristicTypes.folder" = @{ basedOn = "catalog.folder" }
|
||||||
|
"chartOfCharacteristicTypes.list" = @{ basedOn = "catalog.list" }
|
||||||
|
"chartOfCharacteristicTypes.choice" = @{ basedOn = "catalog.choice" }
|
||||||
|
"exchangePlan.item" = @{ basedOn = "catalog.item" }
|
||||||
|
"exchangePlan.list" = @{ basedOn = "catalog.list" }
|
||||||
|
"exchangePlan.choice" = @{ basedOn = "catalog.choice" }
|
||||||
|
# ─── ChartOfAccounts defaults ───
|
||||||
|
"chartOfAccounts.item" = @{
|
||||||
|
parent = @{ title = "Подчинен счету" }
|
||||||
|
fieldDefaults = @{ ref = @{ choiceButton = $true }; boolean = @{ element = "check" } }
|
||||||
|
properties = @{}
|
||||||
|
}
|
||||||
|
"chartOfAccounts.folder" = @{
|
||||||
|
parent = @{ title = "Подчинен счету" }
|
||||||
|
properties = @{ windowOpeningMode = "LockOwnerWindow" }
|
||||||
|
}
|
||||||
|
"chartOfAccounts.list" = @{ basedOn = "catalog.list" }
|
||||||
|
"chartOfAccounts.choice" = @{ basedOn = "catalog.choice" }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Deep merge helper
|
# Deep merge helper
|
||||||
@@ -826,6 +914,413 @@ function Generate-DocumentItemDSL($meta, [hashtable]$p, [hashtable]$fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── InformationRegister ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Generate-InformationRegisterDSL {
|
||||||
|
param($meta, [hashtable]$presetData, [string]$purpose)
|
||||||
|
$pKey = "informationRegister.$($purpose.ToLower())"
|
||||||
|
$p = if ($presetData.ContainsKey($pKey)) { $presetData[$pKey] } else { @{} }
|
||||||
|
$fd = if ($p.fieldDefaults) { $p.fieldDefaults } else { @{ ref = @{ choiceButton = $true }; boolean = @{ element = "check" } } }
|
||||||
|
switch ($purpose) {
|
||||||
|
"Record" { return Generate-InformationRegisterRecordDSL $meta $p $fd }
|
||||||
|
"List" { return Generate-InformationRegisterListDSL $meta $p }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-InformationRegisterRecordDSL($meta, [hashtable]$p, [hashtable]$fd) {
|
||||||
|
$elements = [ordered]@{}
|
||||||
|
$isPeriodic = $meta.Periodicity -and $meta.Periodicity -ne "Nonperiodical"
|
||||||
|
|
||||||
|
# Period first (if periodic)
|
||||||
|
if ($isPeriodic) {
|
||||||
|
$elements["Период"] = @{ element = "input"; path = "Запись.Period" }
|
||||||
|
}
|
||||||
|
# Dimensions
|
||||||
|
foreach ($dim in $meta.Dimensions) {
|
||||||
|
if (-not (Test-DisplayableType $dim.Type)) { continue }
|
||||||
|
$elements[$dim.Name] = New-FieldElement $dim "Запись" $fd
|
||||||
|
}
|
||||||
|
# Resources
|
||||||
|
foreach ($res in $meta.Resources) {
|
||||||
|
if (-not (Test-DisplayableType $res.Type)) { continue }
|
||||||
|
$elements[$res.Name] = New-FieldElement $res "Запись" $fd
|
||||||
|
}
|
||||||
|
# Attributes
|
||||||
|
foreach ($attr in $meta.Attributes) {
|
||||||
|
if (-not (Test-DisplayableType $attr.Type)) { continue }
|
||||||
|
$elements[$attr.Name] = New-FieldElement $attr "Запись" $fd
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [ordered]@{ windowOpeningMode = "LockOwnerWindow" }
|
||||||
|
if ($p.properties) { foreach ($k in $p.properties.Keys) { $props[$k] = $p.properties[$k] } }
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
title = $meta.Synonym
|
||||||
|
properties = $props
|
||||||
|
elements = $elements
|
||||||
|
attributes = @(
|
||||||
|
@{ name = "Запись"; type = "InformationRegisterRecordManager.$($meta.Name)"; main = $true; savedData = $true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-InformationRegisterListDSL($meta, [hashtable]$p) {
|
||||||
|
$isPeriodic = $meta.Periodicity -and $meta.Periodicity -ne "Nonperiodical"
|
||||||
|
$isRecorderSubordinate = $meta.WriteMode -eq "RecorderSubordinate"
|
||||||
|
|
||||||
|
$columns = [ordered]@{}
|
||||||
|
# Period
|
||||||
|
if ($isPeriodic) {
|
||||||
|
$columns["Период"] = @{ element = "labelField"; path = "Список.Period" }
|
||||||
|
}
|
||||||
|
# Recorder/LineNumber for subordinate registers
|
||||||
|
if ($isRecorderSubordinate) {
|
||||||
|
$columns["Регистратор"] = @{ element = "labelField"; path = "Список.Recorder" }
|
||||||
|
$columns["НомерСтроки"] = @{ element = "labelField"; path = "Список.LineNumber" }
|
||||||
|
}
|
||||||
|
# Dimensions
|
||||||
|
foreach ($dim in $meta.Dimensions) {
|
||||||
|
if (-not (Test-DisplayableType $dim.Type)) { continue }
|
||||||
|
$columns[$dim.Name] = @{ element = "labelField"; path = "Список.$($dim.Name)" }
|
||||||
|
}
|
||||||
|
# Resources
|
||||||
|
foreach ($res in $meta.Resources) {
|
||||||
|
if (-not (Test-DisplayableType $res.Type)) { continue }
|
||||||
|
$el = "labelField"
|
||||||
|
if ($res.Type -match '^xs:boolean$|^Boolean$') { $el = "checkBox" }
|
||||||
|
$columns[$res.Name] = @{ element = $el; path = "Список.$($res.Name)" }
|
||||||
|
}
|
||||||
|
# Attributes
|
||||||
|
foreach ($attr in $meta.Attributes) {
|
||||||
|
if (-not (Test-DisplayableType $attr.Type)) { continue }
|
||||||
|
$el = "labelField"
|
||||||
|
if ($attr.Type -match '^xs:boolean$|^Boolean$') { $el = "checkBox" }
|
||||||
|
$columns[$attr.Name] = @{ element = $el; path = "Список.$($attr.Name)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableEl = [ordered]@{
|
||||||
|
element = "table"
|
||||||
|
path = "Список"
|
||||||
|
commandBarLocation = "none"
|
||||||
|
autoCommandBar = @{ autofill = $false }
|
||||||
|
columns = $columns
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [ordered]@{}
|
||||||
|
if ($p.properties) { foreach ($k in $p.properties.Keys) { $props[$k] = $p.properties[$k] } }
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
title = $meta.Synonym
|
||||||
|
properties = $props
|
||||||
|
elements = [ordered]@{
|
||||||
|
"Список" = $tableEl
|
||||||
|
}
|
||||||
|
attributes = @(
|
||||||
|
@{ name = "Список"; type = "DynamicList"; main = $true; settings = @{ mainTable = "InformationRegister.$($meta.Name)"; dynamicDataRead = $true } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── AccumulationRegister ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Generate-AccumulationRegisterDSL {
|
||||||
|
param($meta, [hashtable]$presetData, [string]$purpose)
|
||||||
|
$pKey = "accumulationRegister.$($purpose.ToLower())"
|
||||||
|
$p = if ($presetData.ContainsKey($pKey)) { $presetData[$pKey] } else { @{} }
|
||||||
|
switch ($purpose) {
|
||||||
|
"List" { return Generate-AccumulationRegisterListDSL $meta $p }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-AccumulationRegisterListDSL($meta, [hashtable]$p) {
|
||||||
|
$columns = [ordered]@{}
|
||||||
|
# AccumulationRegisters always have Period, Recorder, LineNumber
|
||||||
|
$columns["Период"] = @{ element = "labelField"; path = "Список.Period" }
|
||||||
|
$columns["Регистратор"] = @{ element = "labelField"; path = "Список.Recorder" }
|
||||||
|
$columns["НомерСтроки"] = @{ element = "labelField"; path = "Список.LineNumber" }
|
||||||
|
# Dimensions
|
||||||
|
foreach ($dim in $meta.Dimensions) {
|
||||||
|
if (-not (Test-DisplayableType $dim.Type)) { continue }
|
||||||
|
$columns[$dim.Name] = @{ element = "labelField"; path = "Список.$($dim.Name)" }
|
||||||
|
}
|
||||||
|
# Resources
|
||||||
|
foreach ($res in $meta.Resources) {
|
||||||
|
if (-not (Test-DisplayableType $res.Type)) { continue }
|
||||||
|
$el = "labelField"
|
||||||
|
if ($res.Type -match '^xs:boolean$|^Boolean$') { $el = "checkBox" }
|
||||||
|
$columns[$res.Name] = @{ element = $el; path = "Список.$($res.Name)" }
|
||||||
|
}
|
||||||
|
# Attributes
|
||||||
|
foreach ($attr in $meta.Attributes) {
|
||||||
|
if (-not (Test-DisplayableType $attr.Type)) { continue }
|
||||||
|
$el = "labelField"
|
||||||
|
if ($attr.Type -match '^xs:boolean$|^Boolean$') { $el = "checkBox" }
|
||||||
|
$columns[$attr.Name] = @{ element = $el; path = "Список.$($attr.Name)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
$tableEl = [ordered]@{
|
||||||
|
element = "table"
|
||||||
|
path = "Список"
|
||||||
|
commandBarLocation = "none"
|
||||||
|
autoCommandBar = @{ autofill = $false }
|
||||||
|
columns = $columns
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [ordered]@{}
|
||||||
|
if ($p.properties) { foreach ($k in $p.properties.Keys) { $props[$k] = $p.properties[$k] } }
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
title = $meta.Synonym
|
||||||
|
properties = $props
|
||||||
|
elements = [ordered]@{
|
||||||
|
"Список" = $tableEl
|
||||||
|
}
|
||||||
|
attributes = @(
|
||||||
|
@{ name = "Список"; type = "DynamicList"; main = $true; settings = @{ mainTable = "AccumulationRegister.$($meta.Name)"; dynamicDataRead = $true } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── ChartOfCharacteristicTypes (delegates to Catalog) ────────────────────
|
||||||
|
|
||||||
|
function Generate-ChartOfCharacteristicTypesDSL {
|
||||||
|
param($meta, [hashtable]$presetData, [string]$purpose)
|
||||||
|
# Delegate to Catalog generators — meta already has CodeLength, DescriptionLength, etc.
|
||||||
|
$dsl = Generate-CatalogDSL -meta $meta -presetData $presetData -purpose $purpose
|
||||||
|
|
||||||
|
# Post-patch: replace Catalog types with ChartOfCharacteristicTypes types
|
||||||
|
$catObjType = "CatalogObject.$($meta.Name)"
|
||||||
|
$ccoctObjType = "ChartOfCharacteristicTypesObject.$($meta.Name)"
|
||||||
|
$catListType = "Catalog.$($meta.Name)"
|
||||||
|
$ccoctListType = "ChartOfCharacteristicTypes.$($meta.Name)"
|
||||||
|
|
||||||
|
foreach ($a in $dsl.attributes) {
|
||||||
|
if ($a.type -eq $catObjType) { $a.type = $ccoctObjType }
|
||||||
|
if ($a.type -eq "DynamicList" -and $a.settings -and $a.settings.mainTable -eq $catListType) {
|
||||||
|
$a.settings.mainTable = $ccoctListType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# For Item forms: inject ValueType field after Code/Description
|
||||||
|
if ($purpose -eq "Item" -and $dsl.elements) {
|
||||||
|
$newElements = [ordered]@{}
|
||||||
|
$inserted = $false
|
||||||
|
foreach ($k in $dsl.elements.Keys) {
|
||||||
|
$newElements[$k] = $dsl.elements[$k]
|
||||||
|
# Insert after Description or after ГруппаКодНаименование
|
||||||
|
if (-not $inserted -and ($k -eq "Наименование" -or $k -eq "ГруппаКодНаименование")) {
|
||||||
|
$newElements["ТипЗначения"] = @{ element = "input"; path = "Объект.ValueType" }
|
||||||
|
$inserted = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $inserted) {
|
||||||
|
$newElements["ТипЗначения"] = @{ element = "input"; path = "Объект.ValueType" }
|
||||||
|
}
|
||||||
|
$dsl.elements = $newElements
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dsl
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── ExchangePlan (delegates to Catalog) ──────────────────────────────────
|
||||||
|
|
||||||
|
function Generate-ExchangePlanDSL {
|
||||||
|
param($meta, [hashtable]$presetData, [string]$purpose)
|
||||||
|
# ExchangePlans are not hierarchical and have no Folder form
|
||||||
|
$dsl = Generate-CatalogDSL -meta $meta -presetData $presetData -purpose $purpose
|
||||||
|
|
||||||
|
# Post-patch: replace Catalog types with ExchangePlan types
|
||||||
|
$catObjType = "CatalogObject.$($meta.Name)"
|
||||||
|
$epObjType = "ExchangePlanObject.$($meta.Name)"
|
||||||
|
$catListType = "Catalog.$($meta.Name)"
|
||||||
|
$epListType = "ExchangePlan.$($meta.Name)"
|
||||||
|
|
||||||
|
foreach ($a in $dsl.attributes) {
|
||||||
|
if ($a.type -eq $catObjType) { $a.type = $epObjType }
|
||||||
|
if ($a.type -eq "DynamicList" -and $a.settings -and $a.settings.mainTable -eq $catListType) {
|
||||||
|
$a.settings.mainTable = $epListType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# For Item forms: inject SentNo, ReceivedNo after Code/Description
|
||||||
|
if ($purpose -eq "Item" -and $dsl.elements) {
|
||||||
|
$newElements = [ordered]@{}
|
||||||
|
$inserted = $false
|
||||||
|
foreach ($k in $dsl.elements.Keys) {
|
||||||
|
$newElements[$k] = $dsl.elements[$k]
|
||||||
|
if (-not $inserted -and ($k -eq "Наименование" -or $k -eq "ГруппаКодНаименование")) {
|
||||||
|
$newElements["НомерОтправленного"] = @{ element = "input"; path = "Объект.SentNo"; readOnly = $true }
|
||||||
|
$newElements["НомерПринятого"] = @{ element = "input"; path = "Объект.ReceivedNo"; readOnly = $true }
|
||||||
|
$inserted = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $inserted) {
|
||||||
|
$newElements["НомерОтправленного"] = @{ element = "input"; path = "Объект.SentNo"; readOnly = $true }
|
||||||
|
$newElements["НомерПринятого"] = @{ element = "input"; path = "Объект.ReceivedNo"; readOnly = $true }
|
||||||
|
}
|
||||||
|
$dsl.elements = $newElements
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dsl
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── ChartOfAccounts ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Generate-ChartOfAccountsDSL {
|
||||||
|
param($meta, [hashtable]$presetData, [string]$purpose)
|
||||||
|
$pKey = "chartOfAccounts.$($purpose.ToLower())"
|
||||||
|
$p = if ($presetData.ContainsKey($pKey)) { $presetData[$pKey] } else { @{} }
|
||||||
|
$fd = if ($p.fieldDefaults) { $p.fieldDefaults } else { @{ ref = @{ choiceButton = $true }; boolean = @{ element = "check" } } }
|
||||||
|
switch ($purpose) {
|
||||||
|
"Item" { return Generate-ChartOfAccountsItemDSL $meta $p $fd $presetData }
|
||||||
|
"Folder" { return Generate-ChartOfAccountsFolderDSL $meta $p }
|
||||||
|
"List" { return Generate-ChartOfAccountsListDSL $meta $presetData }
|
||||||
|
"Choice" { return Generate-ChartOfAccountsChoiceDSL $meta $presetData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-ChartOfAccountsItemDSL($meta, [hashtable]$p, [hashtable]$fd, [hashtable]$presetData) {
|
||||||
|
$elements = [ordered]@{}
|
||||||
|
|
||||||
|
# Header: Code + Parent
|
||||||
|
$headerLeft = [ordered]@{}
|
||||||
|
if ($meta.CodeLength -gt 0) {
|
||||||
|
$headerLeft["Код"] = @{ element = "input"; path = "Объект.Code" }
|
||||||
|
}
|
||||||
|
$headerRight = [ordered]@{}
|
||||||
|
if ($meta.Hierarchical) {
|
||||||
|
$parentTitle = if ($p.parent -and $p.parent.title) { $p.parent.title } else { "Подчинен счету" }
|
||||||
|
$headerRight["Родитель"] = @{ element = "input"; path = "Объект.Parent"; title = $parentTitle }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($headerRight.Count -gt 0) {
|
||||||
|
$elements["ГруппаШапка"] = [ordered]@{
|
||||||
|
element = "group"; groupType = "horizontal"; showTitle = $false; representation = "none"
|
||||||
|
elements = [ordered]@{
|
||||||
|
"ГруппаШапкаЛево" = [ordered]@{ element = "group"; groupType = "vertical"; showTitle = $false; elements = $headerLeft }
|
||||||
|
"ГруппаШапкаПраво" = [ordered]@{ element = "group"; groupType = "vertical"; showTitle = $false; elements = $headerRight }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($headerLeft.Count -gt 0) {
|
||||||
|
foreach ($k in $headerLeft.Keys) { $elements[$k] = $headerLeft[$k] }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Description
|
||||||
|
if ($meta.DescriptionLength -gt 0) {
|
||||||
|
$elements["Наименование"] = @{ element = "input"; path = "Объект.Description" }
|
||||||
|
}
|
||||||
|
|
||||||
|
# OffBalance
|
||||||
|
$elements["Забалансовый"] = @{ element = "check"; path = "Объект.OffBalance" }
|
||||||
|
|
||||||
|
# AccountingFlags as checkboxes
|
||||||
|
if ($meta.AccountingFlags -and $meta.AccountingFlags.Count -gt 0) {
|
||||||
|
$flagElements = [ordered]@{}
|
||||||
|
foreach ($flag in $meta.AccountingFlags) {
|
||||||
|
$flagElements[$flag.Name] = @{ element = "check"; path = "Объект.$($flag.Name)" }
|
||||||
|
}
|
||||||
|
$elements["ГруппаПризнакиУчета"] = [ordered]@{
|
||||||
|
element = "group"; groupType = "vertical"; title = "Признаки учета"
|
||||||
|
elements = $flagElements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ExtDimensionTypes table
|
||||||
|
if ($meta.MaxExtDimensionCount -gt 0) {
|
||||||
|
$edCols = [ordered]@{}
|
||||||
|
$edCols["ВидСубконто"] = @{ element = "input"; path = "Объект.ExtDimensionTypes.ExtDimensionType" }
|
||||||
|
$edCols["ТолькоОбороты"] = @{ element = "check"; path = "Объект.ExtDimensionTypes.TurnoversOnly" }
|
||||||
|
if ($meta.ExtDimensionAccountingFlags) {
|
||||||
|
foreach ($edFlag in $meta.ExtDimensionAccountingFlags) {
|
||||||
|
$edCols[$edFlag.Name] = @{ element = "check"; path = "Объект.ExtDimensionTypes.$($edFlag.Name)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$elements["ВидыСубконто"] = [ordered]@{
|
||||||
|
element = "table"
|
||||||
|
path = "Объект.ExtDimensionTypes"
|
||||||
|
columns = $edCols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom attributes
|
||||||
|
foreach ($attr in $meta.Attributes) {
|
||||||
|
if (-not (Test-DisplayableType $attr.Type)) { continue }
|
||||||
|
$elements[$attr.Name] = New-FieldElement $attr "Объект" $fd
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tabular sections
|
||||||
|
$tsExclude = @("ДополнительныеРеквизиты","Представления")
|
||||||
|
foreach ($ts in $meta.TabularSections) {
|
||||||
|
if ($tsExclude -contains $ts.Name) { continue }
|
||||||
|
$tsCols = [ordered]@{}
|
||||||
|
foreach ($col in $ts.Columns) {
|
||||||
|
if (-not (Test-DisplayableType $col.Type)) { continue }
|
||||||
|
$tsCols["$($ts.Name)$($col.Name)"] = New-FieldElement $col "Объект.$($ts.Name)" $fd
|
||||||
|
}
|
||||||
|
$elements[$ts.Name] = [ordered]@{ element = "table"; path = "Объект.$($ts.Name)"; columns = $tsCols }
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [ordered]@{}
|
||||||
|
if ($p.properties) { foreach ($k in $p.properties.Keys) { $props[$k] = $p.properties[$k] } }
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
title = $meta.Synonym
|
||||||
|
properties = $props
|
||||||
|
elements = $elements
|
||||||
|
attributes = @(
|
||||||
|
@{ name = "Объект"; type = "ChartOfAccountsObject.$($meta.Name)"; main = $true; savedData = $true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-ChartOfAccountsFolderDSL($meta, [hashtable]$p) {
|
||||||
|
$elements = [ordered]@{}
|
||||||
|
if ($meta.CodeLength -gt 0) {
|
||||||
|
$elements["Код"] = @{ element = "input"; path = "Объект.Code" }
|
||||||
|
}
|
||||||
|
if ($meta.DescriptionLength -gt 0) {
|
||||||
|
$elements["Наименование"] = @{ element = "input"; path = "Объект.Description" }
|
||||||
|
}
|
||||||
|
if ($meta.Hierarchical) {
|
||||||
|
$parentTitle = if ($p.parent -and $p.parent.title) { $p.parent.title } else { "Подчинен счету" }
|
||||||
|
$elements["Родитель"] = @{ element = "input"; path = "Объект.Parent"; title = $parentTitle }
|
||||||
|
}
|
||||||
|
|
||||||
|
$props = [ordered]@{ windowOpeningMode = "LockOwnerWindow" }
|
||||||
|
if ($p.properties) { foreach ($k in $p.properties.Keys) { $props[$k] = $p.properties[$k] } }
|
||||||
|
|
||||||
|
return [ordered]@{
|
||||||
|
title = $meta.Synonym
|
||||||
|
useForFoldersAndItems = "Folders"
|
||||||
|
properties = $props
|
||||||
|
elements = $elements
|
||||||
|
attributes = @(
|
||||||
|
@{ name = "Объект"; type = "ChartOfAccountsObject.$($meta.Name)"; main = $true; savedData = $true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-ChartOfAccountsListDSL($meta, [hashtable]$presetData) {
|
||||||
|
# Delegate to Catalog List and patch types
|
||||||
|
$dsl = Generate-CatalogDSL -meta $meta -presetData $presetData -purpose "List"
|
||||||
|
foreach ($a in $dsl.attributes) {
|
||||||
|
if ($a.type -eq "DynamicList" -and $a.settings -and $a.settings.mainTable -eq "Catalog.$($meta.Name)") {
|
||||||
|
$a.settings.mainTable = "ChartOfAccounts.$($meta.Name)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $dsl
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generate-ChartOfAccountsChoiceDSL($meta, [hashtable]$presetData) {
|
||||||
|
$dsl = Generate-CatalogDSL -meta $meta -presetData $presetData -purpose "Choice"
|
||||||
|
foreach ($a in $dsl.attributes) {
|
||||||
|
if ($a.type -eq "DynamicList" -and $a.settings -and $a.settings.mainTable -eq "Catalog.$($meta.Name)") {
|
||||||
|
$a.settings.mainTable = "ChartOfAccounts.$($meta.Name)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $dsl
|
||||||
|
}
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
# END OF FROM-OBJECT MODE FUNCTIONS
|
# END OF FROM-OBJECT MODE FUNCTIONS
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -859,6 +1354,9 @@ $script:formNameToPurpose = @{
|
|||||||
"ФормаСписка" = "List"
|
"ФормаСписка" = "List"
|
||||||
"ФормаВыбора" = "Choice"
|
"ФормаВыбора" = "Choice"
|
||||||
"ФормаГруппы" = "Folder"
|
"ФормаГруппы" = "Folder"
|
||||||
|
"ФормаЗаписи" = "Record"
|
||||||
|
"ФормаСчета" = "Item"
|
||||||
|
"ФормаУзла" = "Item"
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($FromObject -and $JsonPath) {
|
if ($FromObject -and $JsonPath) {
|
||||||
@@ -942,12 +1440,17 @@ if ($FromObject) {
|
|||||||
$presetData = Load-Preset -PresetName $Preset -ScriptDir $PSScriptRoot
|
$presetData = Load-Preset -PresetName $Preset -ScriptDir $PSScriptRoot
|
||||||
|
|
||||||
$supportedPurposes = switch ($meta.Type) {
|
$supportedPurposes = switch ($meta.Type) {
|
||||||
"Document" { @("Item","List","Choice") }
|
"Document" { @("Item","List","Choice") }
|
||||||
"Catalog" { @("Item","Folder","List","Choice") }
|
"Catalog" { @("Item","Folder","List","Choice") }
|
||||||
default { @() }
|
"InformationRegister" { @("Record","List") }
|
||||||
|
"AccumulationRegister" { @("List") }
|
||||||
|
"ChartOfCharacteristicTypes" { @("Item","Folder","List","Choice") }
|
||||||
|
"ExchangePlan" { @("Item","List","Choice") }
|
||||||
|
"ChartOfAccounts" { @("Item","Folder","List","Choice") }
|
||||||
|
default { @() }
|
||||||
}
|
}
|
||||||
if ($supportedPurposes.Count -eq 0) {
|
if ($supportedPurposes.Count -eq 0) {
|
||||||
Write-Error "Object type '$($meta.Type)' is not yet supported by --from-object. Supported: Document, Catalog."
|
Write-Error "Object type '$($meta.Type)' is not yet supported by --from-object. Supported: Document, Catalog, InformationRegister, AccumulationRegister, ChartOfCharacteristicTypes, ExchangePlan, ChartOfAccounts."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if ($supportedPurposes -notcontains $effectivePurpose) {
|
if ($supportedPurposes -notcontains $effectivePurpose) {
|
||||||
@@ -957,8 +1460,13 @@ if ($FromObject) {
|
|||||||
|
|
||||||
# Generate DSL
|
# Generate DSL
|
||||||
$dsl = switch ($meta.Type) {
|
$dsl = switch ($meta.Type) {
|
||||||
"Document" { Generate-DocumentDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
"Document" { Generate-DocumentDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
"Catalog" { Generate-CatalogDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
"Catalog" { Generate-CatalogDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
|
"InformationRegister" { Generate-InformationRegisterDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
|
"AccumulationRegister" { Generate-AccumulationRegisterDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
|
"ChartOfCharacteristicTypes" { Generate-ChartOfCharacteristicTypesDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
|
"ExchangePlan" { Generate-ExchangePlanDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
|
"ChartOfAccounts" { Generate-ChartOfAccountsDSL -meta $meta -presetData $presetData -purpose $effectivePurpose }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Emit DSL if requested
|
# Emit DSL if requested
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# form-compile v1.5 — Compile 1C managed form from JSON or object metadata
|
# form-compile v1.6 — Compile 1C managed form from JSON or object metadata
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
import argparse
|
import argparse
|
||||||
import copy
|
import copy
|
||||||
@@ -84,28 +84,28 @@ def parse_object_meta(object_path):
|
|||||||
def is_ref_type(t):
|
def is_ref_type(t):
|
||||||
return bool(re.search(r'Ref\.', t) or re.search(r'\u0441\u0441\u044b\u043b\u043a\u0430\.', t))
|
return bool(re.search(r'Ref\.', t) or re.search(r'\u0441\u0441\u044b\u043b\u043a\u0430\.', t))
|
||||||
|
|
||||||
def extract_attrs(parent_node):
|
def extract_fields(parent_node, tag_name='Attribute'):
|
||||||
"""Extract attribute list from ChildObjects."""
|
"""Extract field list from ChildObjects by tag name (Attribute, Dimension, Resource, AccountingFlag, ExtDimensionAccountingFlag)."""
|
||||||
result = []
|
result = []
|
||||||
if parent_node is None:
|
if parent_node is None:
|
||||||
return result
|
return result
|
||||||
for attr_node in _et_findall(parent_node, 'md:Attribute'):
|
for field_node in _et_findall(parent_node, f'md:{tag_name}'):
|
||||||
ap = _et_find(attr_node, 'md:Properties')
|
fp = _et_find(field_node, 'md:Properties')
|
||||||
a_name = _et_text(ap, 'md:Name')
|
f_name = _et_text(fp, 'md:Name')
|
||||||
a_syn_node = _et_find(ap, "md:Synonym/v8:item[v8:lang='ru']/v8:content")
|
f_syn_node = _et_find(fp, "md:Synonym/v8:item[v8:lang='ru']/v8:content")
|
||||||
a_syn = a_syn_node.text if a_syn_node is not None and a_syn_node.text else a_name
|
f_syn = f_syn_node.text if f_syn_node is not None and f_syn_node.text else f_name
|
||||||
a_type_node = _et_find(ap, 'md:Type')
|
f_type_node = _et_find(fp, 'md:Type')
|
||||||
a_type = extract_type(a_type_node)
|
f_type = extract_type(f_type_node)
|
||||||
result.append({
|
result.append({
|
||||||
'Name': a_name,
|
'Name': f_name,
|
||||||
'Synonym': a_syn,
|
'Synonym': f_syn,
|
||||||
'Type': a_type,
|
'Type': f_type,
|
||||||
'IsRef': is_ref_type(a_type),
|
'IsRef': is_ref_type(f_type),
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Attributes
|
# Attributes
|
||||||
attributes = extract_attrs(child_objs)
|
attributes = extract_fields(child_objs, 'Attribute')
|
||||||
|
|
||||||
# Tabular sections
|
# Tabular sections
|
||||||
tabular_sections = []
|
tabular_sections = []
|
||||||
@@ -116,7 +116,7 @@ def parse_object_meta(object_path):
|
|||||||
ts_syn_node = _et_find(tsp, "md:Synonym/v8:item[v8:lang='ru']/v8:content")
|
ts_syn_node = _et_find(tsp, "md:Synonym/v8:item[v8:lang='ru']/v8:content")
|
||||||
ts_syn = ts_syn_node.text if ts_syn_node is not None and ts_syn_node.text else ts_name
|
ts_syn = ts_syn_node.text if ts_syn_node is not None and ts_syn_node.text else ts_name
|
||||||
ts_co = _et_find(ts_node, 'md:ChildObjects')
|
ts_co = _et_find(ts_node, 'md:ChildObjects')
|
||||||
ts_cols = extract_attrs(ts_co)
|
ts_cols = extract_fields(ts_co, 'Attribute')
|
||||||
tabular_sections.append({
|
tabular_sections.append({
|
||||||
'Name': ts_name,
|
'Name': ts_name,
|
||||||
'Synonym': ts_syn,
|
'Synonym': ts_syn,
|
||||||
@@ -149,6 +149,54 @@ def parse_object_meta(object_path):
|
|||||||
if ow.text:
|
if ow.text:
|
||||||
owners.append(ow.text)
|
owners.append(ow.text)
|
||||||
meta['Owners'] = owners
|
meta['Owners'] = owners
|
||||||
|
elif obj_type == 'InformationRegister':
|
||||||
|
meta['Dimensions'] = extract_fields(child_objs, 'Dimension')
|
||||||
|
meta['Resources'] = extract_fields(child_objs, 'Resource')
|
||||||
|
prd_node = _et_find(props_node, 'md:InformationRegisterPeriodicity')
|
||||||
|
meta['Periodicity'] = prd_node.text if prd_node is not None and prd_node.text else 'Nonperiodical'
|
||||||
|
wm_node = _et_find(props_node, 'md:WriteMode')
|
||||||
|
meta['WriteMode'] = wm_node.text if wm_node is not None and wm_node.text else 'Independent'
|
||||||
|
elif obj_type == 'AccumulationRegister':
|
||||||
|
meta['Dimensions'] = extract_fields(child_objs, 'Dimension')
|
||||||
|
meta['Resources'] = extract_fields(child_objs, 'Resource')
|
||||||
|
rt_node = _et_find(props_node, 'md:RegisterType')
|
||||||
|
meta['RegisterType'] = rt_node.text if rt_node is not None and rt_node.text else 'Balances'
|
||||||
|
elif obj_type == 'ChartOfCharacteristicTypes':
|
||||||
|
cl_node = _et_find(props_node, 'md:CodeLength')
|
||||||
|
meta['CodeLength'] = int(cl_node.text) if cl_node is not None and cl_node.text else 0
|
||||||
|
dl_node = _et_find(props_node, 'md:DescriptionLength')
|
||||||
|
meta['DescriptionLength'] = int(dl_node.text) if dl_node is not None and dl_node.text else 0
|
||||||
|
hi_node = _et_find(props_node, 'md:Hierarchical')
|
||||||
|
meta['Hierarchical'] = (hi_node is not None and hi_node.text == 'true')
|
||||||
|
ht_node = _et_find(props_node, 'md:HierarchyType')
|
||||||
|
meta['HierarchyType'] = ht_node.text if ht_node is not None and ht_node.text else 'HierarchyFoldersAndItems'
|
||||||
|
owners = []
|
||||||
|
for ow in _et_findall(props_node, 'md:Owners/xr:Item'):
|
||||||
|
if ow.text:
|
||||||
|
owners.append(ow.text)
|
||||||
|
meta['Owners'] = owners
|
||||||
|
meta['HasValueType'] = True
|
||||||
|
elif obj_type == 'ExchangePlan':
|
||||||
|
cl_node = _et_find(props_node, 'md:CodeLength')
|
||||||
|
meta['CodeLength'] = int(cl_node.text) if cl_node is not None and cl_node.text else 0
|
||||||
|
dl_node = _et_find(props_node, 'md:DescriptionLength')
|
||||||
|
meta['DescriptionLength'] = int(dl_node.text) if dl_node is not None and dl_node.text else 0
|
||||||
|
meta['Hierarchical'] = False
|
||||||
|
meta['HierarchyType'] = None
|
||||||
|
meta['Owners'] = []
|
||||||
|
elif obj_type == 'ChartOfAccounts':
|
||||||
|
cl_node = _et_find(props_node, 'md:CodeLength')
|
||||||
|
meta['CodeLength'] = int(cl_node.text) if cl_node is not None and cl_node.text else 0
|
||||||
|
dl_node = _et_find(props_node, 'md:DescriptionLength')
|
||||||
|
meta['DescriptionLength'] = int(dl_node.text) if dl_node is not None and dl_node.text else 0
|
||||||
|
meta['Hierarchical'] = True
|
||||||
|
ht_node = _et_find(props_node, 'md:HierarchyType')
|
||||||
|
meta['HierarchyType'] = ht_node.text if ht_node is not None and ht_node.text else 'HierarchyFoldersAndItems'
|
||||||
|
meta['Owners'] = []
|
||||||
|
max_ed_node = _et_find(props_node, 'md:MaxExtDimensionCount')
|
||||||
|
meta['MaxExtDimensionCount'] = int(max_ed_node.text) if max_ed_node is not None and max_ed_node.text else 0
|
||||||
|
meta['AccountingFlags'] = extract_fields(child_objs, 'AccountingFlag')
|
||||||
|
meta['ExtDimensionAccountingFlags'] = extract_fields(child_objs, 'ExtDimensionAccountingFlag')
|
||||||
|
|
||||||
return meta
|
return meta
|
||||||
|
|
||||||
@@ -216,6 +264,41 @@ def load_preset(preset_name, script_dir, out_path_resolved):
|
|||||||
'basedOn': 'catalog.list', 'choiceMode': True,
|
'basedOn': 'catalog.list', 'choiceMode': True,
|
||||||
'properties': {'windowOpeningMode': 'LockOwnerWindow'},
|
'properties': {'windowOpeningMode': 'LockOwnerWindow'},
|
||||||
},
|
},
|
||||||
|
# --- Register defaults ---
|
||||||
|
'informationRegister.record': {
|
||||||
|
'fieldDefaults': {'ref': {'choiceButton': True}, 'boolean': {'element': 'check'}},
|
||||||
|
'properties': {'windowOpeningMode': 'LockOwnerWindow'},
|
||||||
|
},
|
||||||
|
'informationRegister.list': {
|
||||||
|
'columns': 'all', 'columnType': 'labelField',
|
||||||
|
'tableCommandBar': 'none', 'commandBar': 'auto',
|
||||||
|
'properties': {},
|
||||||
|
},
|
||||||
|
'accumulationRegister.list': {
|
||||||
|
'columns': 'all', 'columnType': 'labelField',
|
||||||
|
'tableCommandBar': 'none', 'commandBar': 'auto',
|
||||||
|
'properties': {},
|
||||||
|
},
|
||||||
|
# --- Catalog-like type defaults ---
|
||||||
|
'chartOfCharacteristicTypes.item': {'basedOn': 'catalog.item'},
|
||||||
|
'chartOfCharacteristicTypes.folder': {'basedOn': 'catalog.folder'},
|
||||||
|
'chartOfCharacteristicTypes.list': {'basedOn': 'catalog.list'},
|
||||||
|
'chartOfCharacteristicTypes.choice': {'basedOn': 'catalog.choice'},
|
||||||
|
'exchangePlan.item': {'basedOn': 'catalog.item'},
|
||||||
|
'exchangePlan.list': {'basedOn': 'catalog.list'},
|
||||||
|
'exchangePlan.choice': {'basedOn': 'catalog.choice'},
|
||||||
|
# --- ChartOfAccounts defaults ---
|
||||||
|
'chartOfAccounts.item': {
|
||||||
|
'parent': {'title': '\u041f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u0441\u0447\u0435\u0442\u0443'},
|
||||||
|
'fieldDefaults': {'ref': {'choiceButton': True}, 'boolean': {'element': 'check'}},
|
||||||
|
'properties': {},
|
||||||
|
},
|
||||||
|
'chartOfAccounts.folder': {
|
||||||
|
'parent': {'title': '\u041f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u0441\u0447\u0435\u0442\u0443'},
|
||||||
|
'properties': {'windowOpeningMode': 'LockOwnerWindow'},
|
||||||
|
},
|
||||||
|
'chartOfAccounts.list': {'basedOn': 'catalog.list'},
|
||||||
|
'chartOfAccounts.choice': {'basedOn': 'catalog.choice'},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Try built-in preset
|
# Try built-in preset
|
||||||
@@ -768,6 +851,403 @@ def generate_document_item_dsl(meta, p, fd):
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# --- InformationRegister DSL generators ---
|
||||||
|
|
||||||
|
def generate_information_register_dsl(meta, preset_data, purpose):
|
||||||
|
p_key = f"informationRegister.{purpose.lower()}"
|
||||||
|
p = preset_data.get(p_key, {})
|
||||||
|
fd = p.get('fieldDefaults') or {'ref': {'choiceButton': True}, 'boolean': {'element': 'check'}}
|
||||||
|
dispatch = {
|
||||||
|
'Record': lambda: generate_information_register_record_dsl(meta, p, fd),
|
||||||
|
'List': lambda: generate_information_register_list_dsl(meta, p),
|
||||||
|
}
|
||||||
|
return dispatch[purpose]()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_information_register_record_dsl(meta, p, fd):
|
||||||
|
elements = OrderedDict()
|
||||||
|
is_periodic = meta.get('Periodicity') and meta['Periodicity'] != 'Nonperiodical'
|
||||||
|
|
||||||
|
# Period first (if periodic)
|
||||||
|
if is_periodic:
|
||||||
|
elements['\u041f\u0435\u0440\u0438\u043e\u0434'] = {'element': 'input', 'path': '\u0417\u0430\u043f\u0438\u0441\u044c.Period'}
|
||||||
|
# Dimensions
|
||||||
|
for dim in meta.get('Dimensions', []):
|
||||||
|
if not is_displayable_type(dim['Type']):
|
||||||
|
continue
|
||||||
|
elements[dim['Name']] = new_field_element(dim['Name'], f"\u0417\u0430\u043f\u0438\u0441\u044c.{dim['Name']}", dim['Type'], fd)
|
||||||
|
# Resources
|
||||||
|
for res in meta.get('Resources', []):
|
||||||
|
if not is_displayable_type(res['Type']):
|
||||||
|
continue
|
||||||
|
elements[res['Name']] = new_field_element(res['Name'], f"\u0417\u0430\u043f\u0438\u0441\u044c.{res['Name']}", res['Type'], fd)
|
||||||
|
# Attributes
|
||||||
|
for attr in meta['Attributes']:
|
||||||
|
if not is_displayable_type(attr['Type']):
|
||||||
|
continue
|
||||||
|
elements[attr['Name']] = new_field_element(attr['Name'], f"\u0417\u0430\u043f\u0438\u0441\u044c.{attr['Name']}", attr['Type'], fd)
|
||||||
|
|
||||||
|
props = OrderedDict([('windowOpeningMode', 'LockOwnerWindow')])
|
||||||
|
if p.get('properties'):
|
||||||
|
for k in p['properties']:
|
||||||
|
props[k] = p['properties'][k]
|
||||||
|
|
||||||
|
return OrderedDict([
|
||||||
|
('title', meta['Synonym']),
|
||||||
|
('properties', props),
|
||||||
|
('elements', elements),
|
||||||
|
('attributes', [
|
||||||
|
{'name': '\u0417\u0430\u043f\u0438\u0441\u044c', 'type': f"InformationRegisterRecordManager.{meta['Name']}", 'main': True, 'savedData': True}
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_information_register_list_dsl(meta, p):
|
||||||
|
is_periodic = meta.get('Periodicity') and meta['Periodicity'] != 'Nonperiodical'
|
||||||
|
is_recorder_subordinate = meta.get('WriteMode') == 'RecorderSubordinate'
|
||||||
|
|
||||||
|
columns = OrderedDict()
|
||||||
|
# Period
|
||||||
|
if is_periodic:
|
||||||
|
columns['\u041f\u0435\u0440\u0438\u043e\u0434'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.Period'}
|
||||||
|
# Recorder/LineNumber for subordinate registers
|
||||||
|
if is_recorder_subordinate:
|
||||||
|
columns['\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.Recorder'}
|
||||||
|
columns['\u041d\u043e\u043c\u0435\u0440\u0421\u0442\u0440\u043e\u043a\u0438'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.LineNumber'}
|
||||||
|
# Dimensions
|
||||||
|
for dim in meta.get('Dimensions', []):
|
||||||
|
if not is_displayable_type(dim['Type']):
|
||||||
|
continue
|
||||||
|
columns[dim['Name']] = {'element': 'labelField', 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{dim['Name']}"}
|
||||||
|
# Resources
|
||||||
|
for res in meta.get('Resources', []):
|
||||||
|
if not is_displayable_type(res['Type']):
|
||||||
|
continue
|
||||||
|
el = 'labelField'
|
||||||
|
if re.match(r'^xs:boolean$|^Boolean$', res['Type']):
|
||||||
|
el = 'checkBox'
|
||||||
|
columns[res['Name']] = {'element': el, 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{res['Name']}"}
|
||||||
|
# Attributes
|
||||||
|
for attr in meta['Attributes']:
|
||||||
|
if not is_displayable_type(attr['Type']):
|
||||||
|
continue
|
||||||
|
el = 'labelField'
|
||||||
|
if re.match(r'^xs:boolean$|^Boolean$', attr['Type']):
|
||||||
|
el = 'checkBox'
|
||||||
|
columns[attr['Name']] = {'element': el, 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{attr['Name']}"}
|
||||||
|
|
||||||
|
table_el = OrderedDict([
|
||||||
|
('element', 'table'),
|
||||||
|
('path', '\u0421\u043f\u0438\u0441\u043e\u043a'),
|
||||||
|
('commandBarLocation', 'none'),
|
||||||
|
('autoCommandBar', {'autofill': False}),
|
||||||
|
('columns', columns),
|
||||||
|
])
|
||||||
|
|
||||||
|
props = OrderedDict()
|
||||||
|
if p.get('properties'):
|
||||||
|
for k in p['properties']:
|
||||||
|
props[k] = p['properties'][k]
|
||||||
|
|
||||||
|
return OrderedDict([
|
||||||
|
('title', meta['Synonym']),
|
||||||
|
('properties', props),
|
||||||
|
('elements', OrderedDict([('\u0421\u043f\u0438\u0441\u043e\u043a', table_el)])),
|
||||||
|
('attributes', [
|
||||||
|
{'name': '\u0421\u043f\u0438\u0441\u043e\u043a', 'type': 'DynamicList', 'main': True, 'settings': {'mainTable': f"InformationRegister.{meta['Name']}", 'dynamicDataRead': True}}
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# --- AccumulationRegister DSL generators ---
|
||||||
|
|
||||||
|
def generate_accumulation_register_dsl(meta, preset_data, purpose):
|
||||||
|
p_key = f"accumulationRegister.{purpose.lower()}"
|
||||||
|
p = preset_data.get(p_key, {})
|
||||||
|
dispatch = {
|
||||||
|
'List': lambda: generate_accumulation_register_list_dsl(meta, p),
|
||||||
|
}
|
||||||
|
return dispatch[purpose]()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_accumulation_register_list_dsl(meta, p):
|
||||||
|
columns = OrderedDict()
|
||||||
|
# AccumulationRegisters always have Period, Recorder, LineNumber
|
||||||
|
columns['\u041f\u0435\u0440\u0438\u043e\u0434'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.Period'}
|
||||||
|
columns['\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.Recorder'}
|
||||||
|
columns['\u041d\u043e\u043c\u0435\u0440\u0421\u0442\u0440\u043e\u043a\u0438'] = {'element': 'labelField', 'path': '\u0421\u043f\u0438\u0441\u043e\u043a.LineNumber'}
|
||||||
|
# Dimensions
|
||||||
|
for dim in meta.get('Dimensions', []):
|
||||||
|
if not is_displayable_type(dim['Type']):
|
||||||
|
continue
|
||||||
|
columns[dim['Name']] = {'element': 'labelField', 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{dim['Name']}"}
|
||||||
|
# Resources
|
||||||
|
for res in meta.get('Resources', []):
|
||||||
|
if not is_displayable_type(res['Type']):
|
||||||
|
continue
|
||||||
|
el = 'labelField'
|
||||||
|
if re.match(r'^xs:boolean$|^Boolean$', res['Type']):
|
||||||
|
el = 'checkBox'
|
||||||
|
columns[res['Name']] = {'element': el, 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{res['Name']}"}
|
||||||
|
# Attributes
|
||||||
|
for attr in meta['Attributes']:
|
||||||
|
if not is_displayable_type(attr['Type']):
|
||||||
|
continue
|
||||||
|
el = 'labelField'
|
||||||
|
if re.match(r'^xs:boolean$|^Boolean$', attr['Type']):
|
||||||
|
el = 'checkBox'
|
||||||
|
columns[attr['Name']] = {'element': el, 'path': f"\u0421\u043f\u0438\u0441\u043e\u043a.{attr['Name']}"}
|
||||||
|
|
||||||
|
table_el = OrderedDict([
|
||||||
|
('element', 'table'),
|
||||||
|
('path', '\u0421\u043f\u0438\u0441\u043e\u043a'),
|
||||||
|
('commandBarLocation', 'none'),
|
||||||
|
('autoCommandBar', {'autofill': False}),
|
||||||
|
('columns', columns),
|
||||||
|
])
|
||||||
|
|
||||||
|
props = OrderedDict()
|
||||||
|
if p.get('properties'):
|
||||||
|
for k in p['properties']:
|
||||||
|
props[k] = p['properties'][k]
|
||||||
|
|
||||||
|
return OrderedDict([
|
||||||
|
('title', meta['Synonym']),
|
||||||
|
('properties', props),
|
||||||
|
('elements', OrderedDict([('\u0421\u043f\u0438\u0441\u043e\u043a', table_el)])),
|
||||||
|
('attributes', [
|
||||||
|
{'name': '\u0421\u043f\u0438\u0441\u043e\u043a', 'type': 'DynamicList', 'main': True, 'settings': {'mainTable': f"AccumulationRegister.{meta['Name']}", 'dynamicDataRead': True}}
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# --- ChartOfCharacteristicTypes (delegates to Catalog) ---
|
||||||
|
|
||||||
|
def generate_chart_of_characteristic_types_dsl(meta, preset_data, purpose):
|
||||||
|
# Delegate to Catalog generators -- meta already has CodeLength, DescriptionLength, etc.
|
||||||
|
dsl = generate_catalog_dsl(meta, preset_data, purpose)
|
||||||
|
|
||||||
|
# Post-patch: replace Catalog types with ChartOfCharacteristicTypes types
|
||||||
|
cat_obj_type = f"CatalogObject.{meta['Name']}"
|
||||||
|
ccoct_obj_type = f"ChartOfCharacteristicTypesObject.{meta['Name']}"
|
||||||
|
cat_list_type = f"Catalog.{meta['Name']}"
|
||||||
|
ccoct_list_type = f"ChartOfCharacteristicTypes.{meta['Name']}"
|
||||||
|
|
||||||
|
for a in dsl['attributes']:
|
||||||
|
if a.get('type') == cat_obj_type:
|
||||||
|
a['type'] = ccoct_obj_type
|
||||||
|
if a.get('type') == 'DynamicList' and a.get('settings') and a['settings'].get('mainTable') == cat_list_type:
|
||||||
|
a['settings']['mainTable'] = ccoct_list_type
|
||||||
|
|
||||||
|
# For Item forms: inject ValueType field after Description/ГруппаКодНаименование
|
||||||
|
if purpose == 'Item' and dsl.get('elements'):
|
||||||
|
vt_el = OrderedDict([('input', '\u0422\u0438\u043f\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f'), ('path', '\u041e\u0431\u044a\u0435\u043a\u0442.ValueType')])
|
||||||
|
els = dsl['elements']
|
||||||
|
if isinstance(els, list):
|
||||||
|
inserted = False
|
||||||
|
new_els = []
|
||||||
|
for el in els:
|
||||||
|
new_els.append(el)
|
||||||
|
if not inserted and isinstance(el, dict):
|
||||||
|
name = el.get('input') or el.get('group') or ''
|
||||||
|
if name in ('\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435', '\u0413\u0440\u0443\u043f\u043f\u0430\u041a\u043e\u0434\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435'):
|
||||||
|
new_els.append(vt_el)
|
||||||
|
inserted = True
|
||||||
|
if not inserted:
|
||||||
|
new_els.append(vt_el)
|
||||||
|
dsl['elements'] = new_els
|
||||||
|
|
||||||
|
return dsl
|
||||||
|
|
||||||
|
|
||||||
|
# --- ExchangePlan (delegates to Catalog) ---
|
||||||
|
|
||||||
|
def generate_exchange_plan_dsl(meta, preset_data, purpose):
|
||||||
|
# ExchangePlans are not hierarchical and have no Folder form
|
||||||
|
dsl = generate_catalog_dsl(meta, preset_data, purpose)
|
||||||
|
|
||||||
|
# Post-patch: replace Catalog types with ExchangePlan types
|
||||||
|
cat_obj_type = f"CatalogObject.{meta['Name']}"
|
||||||
|
ep_obj_type = f"ExchangePlanObject.{meta['Name']}"
|
||||||
|
cat_list_type = f"Catalog.{meta['Name']}"
|
||||||
|
ep_list_type = f"ExchangePlan.{meta['Name']}"
|
||||||
|
|
||||||
|
for a in dsl['attributes']:
|
||||||
|
if a.get('type') == cat_obj_type:
|
||||||
|
a['type'] = ep_obj_type
|
||||||
|
if a.get('type') == 'DynamicList' and a.get('settings') and a['settings'].get('mainTable') == cat_list_type:
|
||||||
|
a['settings']['mainTable'] = ep_list_type
|
||||||
|
|
||||||
|
# For Item forms: inject SentNo, ReceivedNo after Code/Description
|
||||||
|
if purpose == 'Item' and dsl.get('elements'):
|
||||||
|
sent_el = OrderedDict([('input', '\u041d\u043e\u043c\u0435\u0440\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e'), ('path', '\u041e\u0431\u044a\u0435\u043a\u0442.SentNo'), ('readOnly', True)])
|
||||||
|
recv_el = OrderedDict([('input', '\u041d\u043e\u043c\u0435\u0440\u041f\u0440\u0438\u043d\u044f\u0442\u043e\u0433\u043e'), ('path', '\u041e\u0431\u044a\u0435\u043a\u0442.ReceivedNo'), ('readOnly', True)])
|
||||||
|
els = dsl['elements']
|
||||||
|
if isinstance(els, list):
|
||||||
|
inserted = False
|
||||||
|
new_els = []
|
||||||
|
for el in els:
|
||||||
|
new_els.append(el)
|
||||||
|
if not inserted and isinstance(el, dict):
|
||||||
|
name = el.get('input') or el.get('group') or ''
|
||||||
|
if name in ('\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435', '\u0413\u0440\u0443\u043f\u043f\u0430\u041a\u043e\u0434\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435'):
|
||||||
|
new_els.append(sent_el)
|
||||||
|
new_els.append(recv_el)
|
||||||
|
inserted = True
|
||||||
|
if not inserted:
|
||||||
|
new_els.append(sent_el)
|
||||||
|
new_els.append(recv_el)
|
||||||
|
dsl['elements'] = new_els
|
||||||
|
|
||||||
|
return dsl
|
||||||
|
|
||||||
|
|
||||||
|
# --- ChartOfAccounts DSL generators ---
|
||||||
|
|
||||||
|
def generate_chart_of_accounts_dsl(meta, preset_data, purpose):
|
||||||
|
p_key = f"chartOfAccounts.{purpose.lower()}"
|
||||||
|
p = preset_data.get(p_key, {})
|
||||||
|
fd = p.get('fieldDefaults') or {'ref': {'choiceButton': True}, 'boolean': {'element': 'check'}}
|
||||||
|
dispatch = {
|
||||||
|
'Item': lambda: generate_chart_of_accounts_item_dsl(meta, p, fd, preset_data),
|
||||||
|
'Folder': lambda: generate_chart_of_accounts_folder_dsl(meta, p),
|
||||||
|
'List': lambda: generate_chart_of_accounts_list_dsl(meta, preset_data),
|
||||||
|
'Choice': lambda: generate_chart_of_accounts_choice_dsl(meta, preset_data),
|
||||||
|
}
|
||||||
|
return dispatch[purpose]()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_chart_of_accounts_item_dsl(meta, p, fd, preset_data):
|
||||||
|
elements = OrderedDict()
|
||||||
|
|
||||||
|
# Header: Code + Parent
|
||||||
|
header_left = OrderedDict()
|
||||||
|
if meta.get('CodeLength', 0) > 0:
|
||||||
|
header_left['\u041a\u043e\u0434'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Code'}
|
||||||
|
header_right = OrderedDict()
|
||||||
|
if meta.get('Hierarchical'):
|
||||||
|
parent_title = (p.get('parent') or {}).get('title', '\u041f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u0441\u0447\u0435\u0442\u0443')
|
||||||
|
header_right['\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Parent', 'title': parent_title}
|
||||||
|
|
||||||
|
if len(header_right) > 0:
|
||||||
|
elements['\u0413\u0440\u0443\u043f\u043f\u0430\u0428\u0430\u043f\u043a\u0430'] = OrderedDict([
|
||||||
|
('element', 'group'), ('groupType', 'horizontal'), ('showTitle', False), ('representation', 'none'),
|
||||||
|
('elements', OrderedDict([
|
||||||
|
('\u0413\u0440\u0443\u043f\u043f\u0430\u0428\u0430\u043f\u043a\u0430\u041b\u0435\u0432\u043e', OrderedDict([('element', 'group'), ('groupType', 'vertical'), ('showTitle', False), ('elements', header_left)])),
|
||||||
|
('\u0413\u0440\u0443\u043f\u043f\u0430\u0428\u0430\u043f\u043a\u0430\u041f\u0440\u0430\u0432\u043e', OrderedDict([('element', 'group'), ('groupType', 'vertical'), ('showTitle', False), ('elements', header_right)])),
|
||||||
|
])),
|
||||||
|
])
|
||||||
|
elif len(header_left) > 0:
|
||||||
|
for k, v in header_left.items():
|
||||||
|
elements[k] = v
|
||||||
|
|
||||||
|
# Description
|
||||||
|
if meta.get('DescriptionLength', 0) > 0:
|
||||||
|
elements['\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Description'}
|
||||||
|
|
||||||
|
# OffBalance
|
||||||
|
elements['\u0417\u0430\u0431\u0430\u043b\u0430\u043d\u0441\u043e\u0432\u044b\u0439'] = {'element': 'check', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.OffBalance'}
|
||||||
|
|
||||||
|
# AccountingFlags as checkboxes
|
||||||
|
if meta.get('AccountingFlags') and len(meta['AccountingFlags']) > 0:
|
||||||
|
flag_elements = OrderedDict()
|
||||||
|
for flag in meta['AccountingFlags']:
|
||||||
|
flag_elements[flag['Name']] = {'element': 'check', 'path': f"\u041e\u0431\u044a\u0435\u043a\u0442.{flag['Name']}"}
|
||||||
|
elements['\u0413\u0440\u0443\u043f\u043f\u0430\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438\u0423\u0447\u0435\u0442\u0430'] = OrderedDict([
|
||||||
|
('element', 'group'), ('groupType', 'vertical'), ('title', '\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0443\u0447\u0435\u0442\u0430'),
|
||||||
|
('elements', flag_elements),
|
||||||
|
])
|
||||||
|
|
||||||
|
# ExtDimensionTypes table
|
||||||
|
if meta.get('MaxExtDimensionCount', 0) > 0:
|
||||||
|
ed_cols = OrderedDict()
|
||||||
|
ed_cols['\u0412\u0438\u0434\u0421\u0443\u0431\u043a\u043e\u043d\u0442\u043e'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.ExtDimensionTypes.ExtDimensionType'}
|
||||||
|
ed_cols['\u0422\u043e\u043b\u044c\u043a\u043e\u041e\u0431\u043e\u0440\u043e\u0442\u044b'] = {'element': 'check', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.ExtDimensionTypes.TurnoversOnly'}
|
||||||
|
if meta.get('ExtDimensionAccountingFlags'):
|
||||||
|
for ed_flag in meta['ExtDimensionAccountingFlags']:
|
||||||
|
ed_cols[ed_flag['Name']] = {'element': 'check', 'path': f"\u041e\u0431\u044a\u0435\u043a\u0442.ExtDimensionTypes.{ed_flag['Name']}"}
|
||||||
|
elements['\u0412\u0438\u0434\u044b\u0421\u0443\u0431\u043a\u043e\u043d\u0442\u043e'] = OrderedDict([
|
||||||
|
('element', 'table'),
|
||||||
|
('path', '\u041e\u0431\u044a\u0435\u043a\u0442.ExtDimensionTypes'),
|
||||||
|
('columns', ed_cols),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Custom attributes
|
||||||
|
for attr in meta['Attributes']:
|
||||||
|
if not is_displayable_type(attr['Type']):
|
||||||
|
continue
|
||||||
|
elements[attr['Name']] = new_field_element(attr['Name'], f"\u041e\u0431\u044a\u0435\u043a\u0442.{attr['Name']}", attr['Type'], fd)
|
||||||
|
|
||||||
|
# Tabular sections
|
||||||
|
ts_exclude = ['\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435\u0420\u0435\u043a\u0432\u0438\u0437\u0438\u0442\u044b', '\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f']
|
||||||
|
for ts in meta['TabularSections']:
|
||||||
|
if ts['Name'] in ts_exclude:
|
||||||
|
continue
|
||||||
|
ts_cols = OrderedDict()
|
||||||
|
for col in ts['Columns']:
|
||||||
|
if not is_displayable_type(col['Type']):
|
||||||
|
continue
|
||||||
|
ts_cols[f"{ts['Name']}{col['Name']}"] = new_field_element(col['Name'], f"\u041e\u0431\u044a\u0435\u043a\u0442.{ts['Name']}.{col['Name']}", col['Type'], fd)
|
||||||
|
elements[ts['Name']] = OrderedDict([('element', 'table'), ('path', f"\u041e\u0431\u044a\u0435\u043a\u0442.{ts['Name']}"), ('columns', ts_cols)])
|
||||||
|
|
||||||
|
props = OrderedDict()
|
||||||
|
if p.get('properties'):
|
||||||
|
for k in p['properties']:
|
||||||
|
props[k] = p['properties'][k]
|
||||||
|
|
||||||
|
return OrderedDict([
|
||||||
|
('title', meta['Synonym']),
|
||||||
|
('properties', props),
|
||||||
|
('elements', elements),
|
||||||
|
('attributes', [
|
||||||
|
{'name': '\u041e\u0431\u044a\u0435\u043a\u0442', 'type': f"ChartOfAccountsObject.{meta['Name']}", 'main': True, 'savedData': True}
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_chart_of_accounts_folder_dsl(meta, p):
|
||||||
|
elements = OrderedDict()
|
||||||
|
if meta.get('CodeLength', 0) > 0:
|
||||||
|
elements['\u041a\u043e\u0434'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Code'}
|
||||||
|
if meta.get('DescriptionLength', 0) > 0:
|
||||||
|
elements['\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Description'}
|
||||||
|
if meta.get('Hierarchical'):
|
||||||
|
parent_title = (p.get('parent') or {}).get('title', '\u041f\u043e\u0434\u0447\u0438\u043d\u0435\u043d \u0441\u0447\u0435\u0442\u0443')
|
||||||
|
elements['\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c'] = {'element': 'input', 'path': '\u041e\u0431\u044a\u0435\u043a\u0442.Parent', 'title': parent_title}
|
||||||
|
|
||||||
|
props = OrderedDict([('windowOpeningMode', 'LockOwnerWindow')])
|
||||||
|
if p.get('properties'):
|
||||||
|
for k in p['properties']:
|
||||||
|
props[k] = p['properties'][k]
|
||||||
|
|
||||||
|
return OrderedDict([
|
||||||
|
('title', meta['Synonym']),
|
||||||
|
('useForFoldersAndItems', 'Folders'),
|
||||||
|
('properties', props),
|
||||||
|
('elements', elements),
|
||||||
|
('attributes', [
|
||||||
|
{'name': '\u041e\u0431\u044a\u0435\u043a\u0442', 'type': f"ChartOfAccountsObject.{meta['Name']}", 'main': True, 'savedData': True}
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_chart_of_accounts_list_dsl(meta, preset_data):
|
||||||
|
# Delegate to Catalog List and patch types
|
||||||
|
dsl = generate_catalog_dsl(meta, preset_data, 'List')
|
||||||
|
for a in dsl['attributes']:
|
||||||
|
if a.get('type') == 'DynamicList' and a.get('settings') and a['settings'].get('mainTable') == f"Catalog.{meta['Name']}":
|
||||||
|
a['settings']['mainTable'] = f"ChartOfAccounts.{meta['Name']}"
|
||||||
|
return dsl
|
||||||
|
|
||||||
|
|
||||||
|
def generate_chart_of_accounts_choice_dsl(meta, preset_data):
|
||||||
|
dsl = generate_catalog_dsl(meta, preset_data, 'Choice')
|
||||||
|
for a in dsl['attributes']:
|
||||||
|
if a.get('type') == 'DynamicList' and a.get('settings') and a['settings'].get('mainTable') == f"Catalog.{meta['Name']}":
|
||||||
|
a['settings']['mainTable'] = f"ChartOfAccounts.{meta['Name']}"
|
||||||
|
return dsl
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
# END OF FROM-OBJECT MODE FUNCTIONS
|
# END OF FROM-OBJECT MODE FUNCTIONS
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
@@ -1817,6 +2297,95 @@ def detect_format_version(d):
|
|||||||
return "2.17"
|
return "2.17"
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_elements(defn):
|
||||||
|
"""Convert dict-style elements from --from-object generators to list-style expected by compiler.
|
||||||
|
Generator format: elements = {"ИмяЭлемента": {"element": "input", "path": "..."}, ...}
|
||||||
|
Compiler format: elements = [{"input": "ИмяЭлемента", "path": "..."}, ...]
|
||||||
|
Also handles nested 'elements' in groups and 'columns' in tables recursively.
|
||||||
|
"""
|
||||||
|
def convert_elements(els):
|
||||||
|
if isinstance(els, list):
|
||||||
|
# Already list format — but may have nested dicts inside groups
|
||||||
|
result = []
|
||||||
|
for el in els:
|
||||||
|
if isinstance(el, dict):
|
||||||
|
el = dict(el) # copy
|
||||||
|
if 'elements' in el and isinstance(el['elements'], dict):
|
||||||
|
el['elements'] = convert_elements(el['elements'])
|
||||||
|
if 'columns' in el and isinstance(el['columns'], dict):
|
||||||
|
el['columns'] = convert_columns(el['columns'])
|
||||||
|
result.append(el)
|
||||||
|
return result
|
||||||
|
if isinstance(els, dict):
|
||||||
|
result = []
|
||||||
|
for name, props in els.items():
|
||||||
|
if not isinstance(props, dict):
|
||||||
|
continue
|
||||||
|
new_el = {}
|
||||||
|
el_type = props.get('element', 'input')
|
||||||
|
# Map element type to the key name used in JSON DSL
|
||||||
|
type_map = {
|
||||||
|
'input': 'input', 'check': 'check', 'labelField': 'labelField',
|
||||||
|
'table': 'table', 'group': 'group', 'pages': 'pages',
|
||||||
|
'page': 'page', 'label': 'label', 'button': 'button',
|
||||||
|
'checkBox': 'check', 'radioButton': 'radioButton',
|
||||||
|
'pictureField': 'pictureField',
|
||||||
|
}
|
||||||
|
mapped_type = type_map.get(el_type, el_type)
|
||||||
|
new_el[mapped_type] = name
|
||||||
|
for k, v in props.items():
|
||||||
|
if k == 'element':
|
||||||
|
continue
|
||||||
|
if k == 'elements' and isinstance(v, dict):
|
||||||
|
new_el['elements'] = convert_elements(v)
|
||||||
|
elif k == 'columns' and isinstance(v, dict):
|
||||||
|
new_el['columns'] = convert_columns(v)
|
||||||
|
elif k == 'groupType':
|
||||||
|
# groupType → group property in DSL
|
||||||
|
new_el['group'] = v
|
||||||
|
elif k == 'showTitle':
|
||||||
|
new_el['showTitle'] = v
|
||||||
|
elif k == 'representation':
|
||||||
|
new_el['representation'] = v
|
||||||
|
elif k == 'autoCommandBar':
|
||||||
|
new_el['autoCommandBar'] = v
|
||||||
|
elif k == 'commandBarLocation':
|
||||||
|
new_el['commandBarLocation'] = v
|
||||||
|
else:
|
||||||
|
new_el[k] = v
|
||||||
|
result.append(new_el)
|
||||||
|
return result
|
||||||
|
return els
|
||||||
|
|
||||||
|
def convert_columns(cols):
|
||||||
|
if isinstance(cols, list):
|
||||||
|
return cols
|
||||||
|
if isinstance(cols, dict):
|
||||||
|
result = []
|
||||||
|
for name, props in cols.items():
|
||||||
|
if not isinstance(props, dict):
|
||||||
|
continue
|
||||||
|
new_col = {}
|
||||||
|
el_type = props.get('element', 'input')
|
||||||
|
type_map = {
|
||||||
|
'input': 'input', 'check': 'check', 'labelField': 'labelField',
|
||||||
|
'checkBox': 'check',
|
||||||
|
}
|
||||||
|
mapped_type = type_map.get(el_type, el_type)
|
||||||
|
new_col[mapped_type] = name
|
||||||
|
for k, v in props.items():
|
||||||
|
if k == 'element':
|
||||||
|
continue
|
||||||
|
new_col[k] = v
|
||||||
|
result.append(new_col)
|
||||||
|
return result
|
||||||
|
return cols
|
||||||
|
|
||||||
|
if 'elements' in defn:
|
||||||
|
defn['elements'] = convert_elements(defn['elements'])
|
||||||
|
return defn
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
sys.stdout.reconfigure(encoding="utf-8")
|
sys.stdout.reconfigure(encoding="utf-8")
|
||||||
sys.stderr.reconfigure(encoding="utf-8")
|
sys.stderr.reconfigure(encoding="utf-8")
|
||||||
@@ -1839,6 +2408,9 @@ def main():
|
|||||||
'\u0424\u043e\u0440\u043c\u0430\u0421\u043f\u0438\u0441\u043a\u0430': 'List', # ФормаСписка
|
'\u0424\u043e\u0440\u043c\u0430\u0421\u043f\u0438\u0441\u043a\u0430': 'List', # ФормаСписка
|
||||||
'\u0424\u043e\u0440\u043c\u0430\u0412\u044b\u0431\u043e\u0440\u0430': 'Choice', # ФормаВыбора
|
'\u0424\u043e\u0440\u043c\u0430\u0412\u044b\u0431\u043e\u0440\u0430': 'Choice', # ФормаВыбора
|
||||||
'\u0424\u043e\u0440\u043c\u0430\u0413\u0440\u0443\u043f\u043f\u044b': 'Folder', # ФормаГруппы
|
'\u0424\u043e\u0440\u043c\u0430\u0413\u0440\u0443\u043f\u043f\u044b': 'Folder', # ФормаГруппы
|
||||||
|
'\u0424\u043e\u0440\u043c\u0430\u0417\u0430\u043f\u0438\u0441\u0438': 'Record', # ФормаЗаписи
|
||||||
|
'\u0424\u043e\u0440\u043c\u0430\u0421\u0447\u0435\u0442\u0430': 'Item', # ФормаСчета
|
||||||
|
'\u0424\u043e\u0440\u043c\u0430\u0423\u0437\u043b\u0430': 'Item', # ФормаУзла
|
||||||
}
|
}
|
||||||
|
|
||||||
# Mutual exclusion validation
|
# Mutual exclusion validation
|
||||||
@@ -1916,18 +2488,32 @@ def main():
|
|||||||
|
|
||||||
preset_data = load_preset(args.Preset, os.path.dirname(os.path.abspath(__file__)), out_path_resolved)
|
preset_data = load_preset(args.Preset, os.path.dirname(os.path.abspath(__file__)), out_path_resolved)
|
||||||
|
|
||||||
supported = {'Document': ['Item', 'List', 'Choice'], 'Catalog': ['Item', 'Folder', 'List', 'Choice']}
|
supported = {
|
||||||
|
'Document': ['Item', 'List', 'Choice'],
|
||||||
|
'Catalog': ['Item', 'Folder', 'List', 'Choice'],
|
||||||
|
'InformationRegister': ['Record', 'List'],
|
||||||
|
'AccumulationRegister': ['List'],
|
||||||
|
'ChartOfCharacteristicTypes': ['Item', 'Folder', 'List', 'Choice'],
|
||||||
|
'ExchangePlan': ['Item', 'List', 'Choice'],
|
||||||
|
'ChartOfAccounts': ['Item', 'Folder', 'List', 'Choice'],
|
||||||
|
}
|
||||||
if meta['Type'] not in supported:
|
if meta['Type'] not in supported:
|
||||||
print(f"Object type '{meta['Type']}' not supported. Supported: Document, Catalog.", file=sys.stderr)
|
print(f"Object type '{meta['Type']}' not supported. Supported: Document, Catalog, InformationRegister, AccumulationRegister, ChartOfCharacteristicTypes, ExchangePlan, ChartOfAccounts.", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if purpose not in supported[meta['Type']]:
|
if purpose not in supported[meta['Type']]:
|
||||||
print(f"Purpose '{purpose}' not valid for {meta['Type']}. Valid: {', '.join(supported[meta['Type']])}", file=sys.stderr)
|
print(f"Purpose '{purpose}' not valid for {meta['Type']}. Valid: {', '.join(supported[meta['Type']])}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if meta['Type'] == 'Document':
|
dsl_dispatch = {
|
||||||
dsl = generate_document_dsl(meta, preset_data, purpose)
|
'Document': generate_document_dsl,
|
||||||
else:
|
'Catalog': generate_catalog_dsl,
|
||||||
dsl = generate_catalog_dsl(meta, preset_data, purpose)
|
'InformationRegister': generate_information_register_dsl,
|
||||||
|
'AccumulationRegister': generate_accumulation_register_dsl,
|
||||||
|
'ChartOfCharacteristicTypes': generate_chart_of_characteristic_types_dsl,
|
||||||
|
'ExchangePlan': generate_exchange_plan_dsl,
|
||||||
|
'ChartOfAccounts': generate_chart_of_accounts_dsl,
|
||||||
|
}
|
||||||
|
dsl = dsl_dispatch[meta['Type']](meta, preset_data, purpose)
|
||||||
|
|
||||||
if args.EmitDsl:
|
if args.EmitDsl:
|
||||||
dsl_path = args.EmitDsl if os.path.isabs(args.EmitDsl) else os.path.join(os.getcwd(), args.EmitDsl)
|
dsl_path = args.EmitDsl if os.path.isabs(args.EmitDsl) else os.path.join(os.getcwd(), args.EmitDsl)
|
||||||
@@ -1937,6 +2523,8 @@ def main():
|
|||||||
print(f"[from-object] DSL saved: {dsl_path}")
|
print(f"[from-object] DSL saved: {dsl_path}")
|
||||||
|
|
||||||
defn = json.loads(json.dumps(dsl)) # normalize OrderedDict to regular dict
|
defn = json.loads(json.dumps(dsl)) # normalize OrderedDict to regular dict
|
||||||
|
# Convert dict-style elements (from generators) to list-style (expected by compiler)
|
||||||
|
defn = _normalize_elements(defn)
|
||||||
else:
|
else:
|
||||||
# --- 1. Load and validate JSON ---
|
# --- 1. Load and validate JSON ---
|
||||||
json_path = args.JsonPath
|
json_path = args.JsonPath
|
||||||
|
|||||||
Reference in New Issue
Block a user