From 60519723912106814617cfb1223a3db4a07114a0 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 7 Mar 2026 21:33:56 +0300 Subject: [PATCH 01/19] fix(meta-compile,meta-validate): fix 7 e2e bugs, add hints and reserved name checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group 1: ExchangePlan — remove invalid CodeType/CheckUnique/Autonumbering; Flowchart.xml — add version="2.17"; Content.xml — fix namespace + version. Group 2: Add missing GeneratedType categories — DefinedType, ChartOfAccounts ExtDimensionTypes/Row, CalculationRegister RecalculationsManager/Recalcs. Group 3: BusinessProcess — emit property from DSL. Group 4: Cross-reference [HINT] output after compilation for AccountingRegister, CalculationRegister, BusinessProcess, ChartOfAccounts (oriented to /meta-edit DSL). Group 5: Reserved attribute name warnings in meta-compile and meta-validate (Check 7b); cross-reference validation in Check 10 with [HINT] output. All changes synced to both PS1 and PY versions. Co-Authored-By: Claude Opus 4.6 --- .../meta-compile/scripts/meta-compile.ps1 | 71 +++++++++++++--- .../meta-compile/scripts/meta-compile.py | 70 ++++++++++++++-- .../meta-validate/scripts/meta-validate.ps1 | 80 +++++++++++++++++++ .../meta-validate/scripts/meta-validate.py | 72 +++++++++++++++++ docs/meta-dsl-spec.md | 3 +- 5 files changed, 274 insertions(+), 22 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 6995acc3..c67e614a 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -436,13 +436,16 @@ $script:generatedTypes = @{ @{ prefix = "CalculationRegisterList"; category = "List" } @{ prefix = "CalculationRegisterRecordSet"; category = "RecordSet" } @{ prefix = "CalculationRegisterRecordKey"; category = "RecordKey" } + @{ prefix = "RecalculationsManager"; category = "Recalcs" } ) "ChartOfAccounts" = @( - @{ prefix = "ChartOfAccountsObject"; category = "Object" } - @{ prefix = "ChartOfAccountsRef"; category = "Ref" } - @{ prefix = "ChartOfAccountsSelection"; category = "Selection" } - @{ prefix = "ChartOfAccountsList"; category = "List" } - @{ prefix = "ChartOfAccountsManager"; category = "Manager" } + @{ prefix = "ChartOfAccountsObject"; category = "Object" } + @{ prefix = "ChartOfAccountsRef"; category = "Ref" } + @{ prefix = "ChartOfAccountsSelection"; category = "Selection" } + @{ prefix = "ChartOfAccountsList"; category = "List" } + @{ prefix = "ChartOfAccountsManager"; category = "Manager" } + @{ prefix = "ChartOfAccountsExtDimensionTypes"; category = "ExtDimensionTypes" } + @{ prefix = "ChartOfAccountsExtDimensionTypesRow"; category = "ExtDimensionTypesRow" } ) "ChartOfCharacteristicTypes" = @( @{ prefix = "ChartOfCharacteristicTypesObject"; category = "Object" } @@ -487,6 +490,9 @@ $script:generatedTypes = @{ @{ prefix = "ExchangePlanList"; category = "List" } @{ prefix = "ExchangePlanManager"; category = "Manager" } ) + "DefinedType" = @( + @{ prefix = "DefinedType"; category = "DefinedType" } + ) "DocumentJournal" = @( @{ prefix = "DocumentJournalSelection"; category = "Selection" } @{ prefix = "DocumentJournalList"; category = "List" } @@ -592,9 +598,26 @@ function Emit-TabularStandardAttributes { # --- 8. Attribute emitter --- +$script:reservedAttrNames = @{ + "Ref"="Ссылка"; "DeletionMark"="ПометкаУдаления"; "Code"="Код"; "Description"="Наименование" + "Date"="Дата"; "Number"="Номер"; "Posted"="Проведен"; "Parent"="Родитель"; "Owner"="Владелец" + "IsFolder"="ЭтоГруппа"; "Predefined"="Предопределенный"; "PredefinedDataName"="ИмяПредопределенныхДанных" + "Recorder"="Регистратор"; "Period"="Период"; "LineNumber"="НомерСтроки"; "Active"="Активность" + "Order"="Порядок"; "Type"="Тип"; "OffBalance"="Забалансовый" + "Started"="Стартован"; "Completed"="Завершен"; "HeadTask"="ВедущаяЗадача" + "Executed"="Выполнена"; "RoutePoint"="ТочкаМаршрута"; "BusinessProcess"="БизнесПроцесс" + "ThisNode"="ЭтотУзел"; "SentNo"="НомерОтправленного"; "ReceivedNo"="НомерПринятого" + "CalculationType"="ВидРасчета"; "RegistrationPeriod"="ПериодРегистрации"; "ReversingEntry"="СторноЗапись" + "Account"="Счет"; "ValueType"="ТипЗначения"; "ActionPeriodIsBasic"="ПериодДействияБазовый" +} + function Emit-Attribute { param([string]$indent, $parsed, [string]$context) # $context: "catalog", "document", "object", "processor", "tabular", "processor-tabular", "register" + $attrName = $parsed.name + if ($script:reservedAttrNames.ContainsKey($attrName) -or $script:reservedAttrNames.ContainsValue($attrName)) { + Write-Warning "Attribute '$attrName' conflicts with a standard attribute name. This may cause errors when loading into 1C." + } $uuid = New-Guid-String X "$indent" X "$indent`t" @@ -1440,19 +1463,13 @@ function Emit-ExchangePlanProperties { $codeLength = if ($null -ne $def.codeLength) { "$($def.codeLength)" } else { "9" } $descriptionLength = if ($null -ne $def.descriptionLength) { "$($def.descriptionLength)" } else { "100" } - $codeType = if ($def.codeType) { "$($def.codeType)" } else { "String" } $codeAllowedLength = if ($def.codeAllowedLength) { "$($def.codeAllowedLength)" } else { "Variable" } - $autonumbering = if ($def.autonumbering -eq $false) { "false" } else { "true" } - $checkUnique = if ($def.checkUnique -eq $true) { "true" } else { "false" } X "$i$codeLength" - X "$i$codeType" X "$i$codeAllowedLength" X "$i$descriptionLength" X "$iAsDescription" X "$iInDialog" - X "$i$checkUnique" - X "$i$autonumbering" Emit-StandardAttributes $i "ExchangePlan" @@ -1941,6 +1958,13 @@ function Emit-BusinessProcessProperties { Emit-StandardAttributes $i "BusinessProcess" X "$i" + $task = if ($def.task) { "$($def.task)" } else { "" } + if ($task) { + X "$i$task" + } else { + X "$i" + } + X "$i" X "$i" X "$i`tBusinessProcess.$objName.StandardAttribute.Number" @@ -2708,7 +2732,7 @@ if ($objType -in $typesWithModule) { if ($objType -eq "ExchangePlan") { $contentPath = Join-Path $extDir "Content.xml" if (-not (Test-Path $contentPath)) { - $contentXml = "`r`n`r`n" + $contentXml = "`r`n`r`n" [System.IO.File]::WriteAllText($contentPath, $contentXml, $enc) $modulesCreated += $contentPath } @@ -2716,7 +2740,7 @@ if ($objType -eq "ExchangePlan") { if ($objType -eq "BusinessProcess") { $flowchartPath = Join-Path $extDir "Flowchart.xml" if (-not (Test-Path $flowchartPath)) { - $flowchartXml = "`r`n`r`n" + $flowchartXml = "`r`n`r`n" [System.IO.File]::WriteAllText($flowchartPath, $flowchartXml, $enc) $modulesCreated += $flowchartPath } @@ -2842,3 +2866,24 @@ switch ($regResult) { "no-childobj" { Write-Warning "Configuration.xml found but not found" } "no-config" { Write-Host " Configuration.xml: not found at $configXmlPath (register manually)" } } + +# Cross-reference hints +if ($objType -eq "AccountingRegister" -and -not $def.chartOfAccounts) { + Write-Host "[HINT] AccountingRegister requires ChartOfAccounts reference:" + Write-Host " /meta-edit -Operation modify-property -Value `"ChartOfAccounts=ChartOfAccounts.XXX`"" +} +if ($objType -eq "CalculationRegister" -and -not $def.chartOfCalculationTypes) { + Write-Host "[HINT] CalculationRegister requires ChartOfCalculationTypes reference:" + Write-Host " /meta-edit -Operation modify-property -Value `"ChartOfCalculationTypes=ChartOfCalculationTypes.XXX`"" +} +if ($objType -eq "BusinessProcess" -and -not $def.task) { + Write-Host "[HINT] BusinessProcess requires Task reference:" + Write-Host " /meta-edit -Operation modify-property -Value `"Task=Task.XXX`"" +} +if ($objType -eq "ChartOfAccounts") { + $maxExtDim = if ($null -ne $def.maxExtDimensionCount) { [int]$def.maxExtDimensionCount } else { 0 } + if ($maxExtDim -gt 0 -and -not $def.extDimensionTypes) { + Write-Host "[HINT] ChartOfAccounts with MaxExtDimensionCount>0 requires ExtDimensionTypes:" + Write-Host " /meta-edit -Operation modify-property -Value `"ExtDimensionTypes=ChartOfCharacteristicTypes.XXX`"" + } +} diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 045b09af..b1ea7bcf 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -406,6 +406,7 @@ generated_types = { {'prefix': 'CalculationRegisterList', 'category': 'List'}, {'prefix': 'CalculationRegisterRecordSet', 'category': 'RecordSet'}, {'prefix': 'CalculationRegisterRecordKey', 'category': 'RecordKey'}, + {'prefix': 'RecalculationsManager', 'category': 'Recalcs'}, ], 'ChartOfAccounts': [ {'prefix': 'ChartOfAccountsObject', 'category': 'Object'}, @@ -413,6 +414,8 @@ generated_types = { {'prefix': 'ChartOfAccountsSelection', 'category': 'Selection'}, {'prefix': 'ChartOfAccountsList', 'category': 'List'}, {'prefix': 'ChartOfAccountsManager', 'category': 'Manager'}, + {'prefix': 'ChartOfAccountsExtDimensionTypes', 'category': 'ExtDimensionTypes'}, + {'prefix': 'ChartOfAccountsExtDimensionTypesRow', 'category': 'ExtDimensionTypesRow'}, ], 'ChartOfCharacteristicTypes': [ {'prefix': 'ChartOfCharacteristicTypesObject', 'category': 'Object'}, @@ -457,6 +460,9 @@ generated_types = { {'prefix': 'ExchangePlanList', 'category': 'List'}, {'prefix': 'ExchangePlanManager', 'category': 'Manager'}, ], + 'DefinedType': [ + {'prefix': 'DefinedType', 'category': 'DefinedType'}, + ], 'DocumentJournal': [ {'prefix': 'DocumentJournalSelection', 'category': 'Selection'}, {'prefix': 'DocumentJournalList', 'category': 'List'}, @@ -554,7 +560,40 @@ def emit_tabular_standard_attributes(indent): # 8. Attribute emitter # --------------------------------------------------------------------------- +RESERVED_ATTR_NAMES = { + 'Ref', 'DeletionMark', 'Code', 'Description', 'Date', 'Number', 'Posted', + 'Parent', 'Owner', 'IsFolder', 'Predefined', 'PredefinedDataName', + 'Recorder', 'Period', 'LineNumber', 'Active', 'Order', 'Type', 'OffBalance', + 'Started', 'Completed', 'HeadTask', 'Executed', 'RoutePoint', 'BusinessProcess', + 'ThisNode', 'SentNo', 'ReceivedNo', 'CalculationType', 'RegistrationPeriod', + 'ReversingEntry', 'Account', 'ValueType', 'ActionPeriodIsBasic', +} +RESERVED_ATTR_NAMES_RU = { + '\u0421\u0441\u044b\u043b\u043a\u0430', '\u041f\u043e\u043c\u0435\u0442\u043a\u0430\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u044f', + '\u041a\u043e\u0434', '\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435', + '\u0414\u0430\u0442\u0430', '\u041d\u043e\u043c\u0435\u0440', '\u041f\u0440\u043e\u0432\u0435\u0434\u0435\u043d', + '\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c', '\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446', + '\u042d\u0442\u043e\u0413\u0440\u0443\u043f\u043f\u0430', '\u041f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439', + '\u0418\u043c\u044f\u041f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445\u0414\u0430\u043d\u043d\u044b\u0445', + '\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440', '\u041f\u0435\u0440\u0438\u043e\u0434', + '\u041d\u043e\u043c\u0435\u0440\u0421\u0442\u0440\u043e\u043a\u0438', '\u0410\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c', + '\u041f\u043e\u0440\u044f\u0434\u043e\u043a', '\u0422\u0438\u043f', '\u0417\u0430\u0431\u0430\u043b\u0430\u043d\u0441\u043e\u0432\u044b\u0439', + '\u0421\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u043d', '\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d', + '\u0412\u0435\u0434\u0443\u0449\u0430\u044f\u0417\u0430\u0434\u0430\u0447\u0430', + '\u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430', '\u0422\u043e\u0447\u043a\u0430\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u0430', + '\u0411\u0438\u0437\u043d\u0435\u0441\u041f\u0440\u043e\u0446\u0435\u0441\u0441', + '\u042d\u0442\u043e\u0442\u0423\u0437\u0435\u043b', '\u041d\u043e\u043c\u0435\u0440\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e', + '\u041d\u043e\u043c\u0435\u0440\u041f\u0440\u0438\u043d\u044f\u0442\u043e\u0433\u043e', + '\u0412\u0438\u0434\u0420\u0430\u0441\u0447\u0435\u0442\u0430', '\u041f\u0435\u0440\u0438\u043e\u0434\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438', + '\u0421\u0442\u043e\u0440\u043d\u043e\u0417\u0430\u043f\u0438\u0441\u044c', + '\u0421\u0447\u0435\u0442', '\u0422\u0438\u043f\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f', + '\u041f\u0435\u0440\u0438\u043e\u0434\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0411\u0430\u0437\u043e\u0432\u044b\u0439', +} + def emit_attribute(indent, parsed, context): + attr_name = parsed['name'] + if attr_name in RESERVED_ATTR_NAMES or attr_name in RESERVED_ATTR_NAMES_RU: + print(f"WARNING: Attribute '{attr_name}' conflicts with a standard attribute name. This may cause errors when loading into 1C.", file=sys.stderr) uid = new_uuid() X(f'{indent}') X(f'{indent}\t') @@ -1250,18 +1289,12 @@ def emit_exchange_plan_properties(indent): X(f'{i}true') code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9' description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '100' - code_type = str(defn['codeType']) if defn.get('codeType') else 'String' code_allowed_length = str(defn['codeAllowedLength']) if defn.get('codeAllowedLength') else 'Variable' - autonumbering = 'false' if defn.get('autonumbering') is False else 'true' - check_unique = 'true' if defn.get('checkUnique') is True else 'false' X(f'{i}{code_length}') - X(f'{i}{code_type}') X(f'{i}{code_allowed_length}') X(f'{i}{description_length}') X(f'{i}AsDescription') X(f'{i}InDialog') - X(f'{i}{check_unique}') - X(f'{i}{autonumbering}') emit_standard_attributes(i, 'ExchangePlan') distributed = 'true' if defn.get('distributedInfoBase') is True else 'false' include_ext = 'true' if defn.get('includeConfigurationExtensions') is True else 'false' @@ -1664,6 +1697,11 @@ def emit_business_process_properties(indent): X(f'{i}{autonumbering}') emit_standard_attributes(i, 'BusinessProcess') X(f'{i}') + task_ref = str(defn['task']) if defn.get('task') else '' + if task_ref: + X(f'{i}{task_ref}') + else: + X(f'{i}') X(f'{i}') X(f'{i}') X(f'{i}\tBusinessProcess.{obj_name}.StandardAttribute.Number') @@ -2335,14 +2373,14 @@ if obj_type in types_with_module: if obj_type == 'ExchangePlan': content_path = os.path.join(ext_dir, 'Content.xml') if not os.path.isfile(content_path): - content_xml = '\r\n\r\n' + content_xml = '\r\n\r\n' write_utf8_bom(content_path, content_xml) modules_created.append(content_path) if obj_type == 'BusinessProcess': flowchart_path = os.path.join(ext_dir, 'Flowchart.xml') if not os.path.isfile(flowchart_path): - flowchart_xml = '\r\n\r\n' + flowchart_xml = '\r\n\r\n' write_utf8_bom(flowchart_path, flowchart_xml) modules_created.append(flowchart_path) @@ -2464,3 +2502,19 @@ elif reg_result == 'no-childobj': print('WARNING: Configuration.xml found but not found', file=sys.stderr) elif reg_result == 'no-config': print(f' Configuration.xml: not found at {config_xml_path} (register manually)') + +# Cross-reference hints +if obj_type == 'AccountingRegister' and not defn.get('chartOfAccounts'): + print('[HINT] AccountingRegister requires ChartOfAccounts reference:') + print(' /meta-edit -Operation modify-property -Value "ChartOfAccounts=ChartOfAccounts.XXX"') +if obj_type == 'CalculationRegister' and not defn.get('chartOfCalculationTypes'): + print('[HINT] CalculationRegister requires ChartOfCalculationTypes reference:') + print(' /meta-edit -Operation modify-property -Value "ChartOfCalculationTypes=ChartOfCalculationTypes.XXX"') +if obj_type == 'BusinessProcess' and not defn.get('task'): + print('[HINT] BusinessProcess requires Task reference:') + print(' /meta-edit -Operation modify-property -Value "Task=Task.XXX"') +if obj_type == 'ChartOfAccounts': + max_ext_dim = int(defn['maxExtDimensionCount']) if defn.get('maxExtDimensionCount') is not None else 0 + if max_ext_dim > 0 and not defn.get('extDimensionTypes'): + print('[HINT] ChartOfAccounts with MaxExtDimensionCount>0 requires ExtDimensionTypes:') + print(' /meta-edit -Operation modify-property -Value "ExtDimensionTypes=ChartOfCharacteristicTypes.XXX"') diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index aba022bf..e2f2f46a 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -649,6 +649,41 @@ if ($childObjNode) { if ($script:stopped) { & $finalize; exit 1 } +# --- Check 7b: Reserved attribute names --- + +$reservedAttrNames = @( + "Ref","DeletionMark","Code","Description","Date","Number","Posted","Parent","Owner", + "IsFolder","Predefined","PredefinedDataName","Recorder","Period","LineNumber","Active", + "Order","Type","OffBalance","Started","Completed","HeadTask","Executed","RoutePoint", + "BusinessProcess","ThisNode","SentNo","ReceivedNo","CalculationType","RegistrationPeriod", + "ReversingEntry","Account","ValueType","ActionPeriodIsBasic" +) + +if ($childObjNode) { + $check7bOk = $true + $attrNodes = $childObjNode.SelectNodes("md:Attribute", $ns) + foreach ($attrNode in $attrNodes) { + $attrProps = $attrNode.SelectSingleNode("md:Properties", $ns) + if ($attrProps) { + $attrNameNode = $attrProps.SelectSingleNode("md:Name", $ns) + if ($attrNameNode -and $attrNameNode.InnerText) { + $an = $attrNameNode.InnerText + if ($reservedAttrNames -contains $an) { + Report-Warn "7b. Attribute '$an' conflicts with a standard attribute name" + $check7bOk = $false + } + } + } + } + if ($check7bOk) { + Report-OK "7b. Reserved attribute names: no conflicts" + } +} else { + Report-OK "7b. Reserved attribute names: N/A" +} + +if ($script:stopped) { & $finalize; exit 1 } + # --- Check 8: Name uniqueness --- function Check-Uniqueness { @@ -887,6 +922,51 @@ if ($propsNode) { $check10Issues++ } } + + # AccountingRegister: ChartOfAccounts must not be empty + if ($mdType -eq "AccountingRegister") { + $coa = $propsNode.SelectSingleNode("md:ChartOfAccounts", $ns) + if (-not $coa -or -not $coa.InnerText.Trim()) { + Report-Error "10. AccountingRegister: empty ChartOfAccounts" + $check10Ok = $false + $check10Issues++ + Write-Host "[HINT] /meta-edit -Operation modify-property -Value `"ChartOfAccounts=ChartOfAccounts.XXX`"" + } + } + + # CalculationRegister: ChartOfCalculationTypes must not be empty + if ($mdType -eq "CalculationRegister") { + $coct = $propsNode.SelectSingleNode("md:ChartOfCalculationTypes", $ns) + if (-not $coct -or -not $coct.InnerText.Trim()) { + Report-Error "10. CalculationRegister: empty ChartOfCalculationTypes" + $check10Ok = $false + $check10Issues++ + Write-Host "[HINT] /meta-edit -Operation modify-property -Value `"ChartOfCalculationTypes=ChartOfCalculationTypes.XXX`"" + } + } + + # BusinessProcess: Task should not be empty + if ($mdType -eq "BusinessProcess") { + $taskProp = $propsNode.SelectSingleNode("md:Task", $ns) + if (-not $taskProp -or -not $taskProp.InnerText.Trim()) { + Report-Warn "10. BusinessProcess: empty Task reference" + $check10Issues++ + Write-Host "[HINT] /meta-edit -Operation modify-property -Value `"Task=Task.XXX`"" + } + } + + # ChartOfAccounts: ExtDimensionTypes should be set if MaxExtDimensionCount > 0 + if ($mdType -eq "ChartOfAccounts") { + $maxExtDim = $propsNode.SelectSingleNode("md:MaxExtDimensionCount", $ns) + if ($maxExtDim -and [int]$maxExtDim.InnerText -gt 0) { + $edt = $propsNode.SelectSingleNode("md:ExtDimensionTypes", $ns) + if (-not $edt -or -not $edt.InnerText.Trim()) { + Report-Warn "10. ChartOfAccounts: MaxExtDimensionCount>0 but ExtDimensionTypes is empty" + $check10Issues++ + Write-Host "[HINT] /meta-edit -Operation modify-property -Value `"ExtDimensionTypes=ChartOfCharacteristicTypes.XXX`"" + } + } + } } if ($check10Ok -and $check10Issues -eq 0) { diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index 14004de4..f7cea0c6 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -626,6 +626,37 @@ if stopped: finalize() sys.exit(1) +# ── Check 7b: Reserved attribute names ─────────────────────── + +RESERVED_ATTR_NAMES = { + 'Ref', 'DeletionMark', 'Code', 'Description', 'Date', 'Number', 'Posted', + 'Parent', 'Owner', 'IsFolder', 'Predefined', 'PredefinedDataName', + 'Recorder', 'Period', 'LineNumber', 'Active', 'Order', 'Type', 'OffBalance', + 'Started', 'Completed', 'HeadTask', 'Executed', 'RoutePoint', 'BusinessProcess', + 'ThisNode', 'SentNo', 'ReceivedNo', 'CalculationType', 'RegistrationPeriod', + 'ReversingEntry', 'Account', 'ValueType', 'ActionPeriodIsBasic', +} + +if child_obj_node is not None: + check7b_ok = True + for attr_node in find_all(child_obj_node, 'md:Attribute'): + attr_props = find(attr_node, 'md:Properties') + if attr_props is not None: + attr_name_node = find(attr_props, 'md:Name') + if attr_name_node is not None and inner_text(attr_name_node): + an = inner_text(attr_name_node) + if an in RESERVED_ATTR_NAMES: + report_warn(f"7b. Attribute '{an}' conflicts with a standard attribute name") + check7b_ok = False + if check7b_ok: + report_ok("7b. Reserved attribute names: no conflicts") +else: + report_ok("7b. Reserved attribute names: N/A") + +if stopped: + finalize() + sys.exit(1) + # ── Check 8: Name uniqueness ───────────────────────────────── @@ -841,6 +872,47 @@ if props_node is not None: check10_ok = False check10_issues += 1 + # AccountingRegister: ChartOfAccounts must not be empty + if md_type == 'AccountingRegister': + coa = find(props_node, 'md:ChartOfAccounts') + if coa is None or not text_of(coa): + report_error('10. AccountingRegister: empty ChartOfAccounts') + check10_ok = False + check10_issues += 1 + print('[HINT] /meta-edit -Operation modify-property -Value "ChartOfAccounts=ChartOfAccounts.XXX"') + + # CalculationRegister: ChartOfCalculationTypes must not be empty + if md_type == 'CalculationRegister': + coct = find(props_node, 'md:ChartOfCalculationTypes') + if coct is None or not text_of(coct): + report_error('10. CalculationRegister: empty ChartOfCalculationTypes') + check10_ok = False + check10_issues += 1 + print('[HINT] /meta-edit -Operation modify-property -Value "ChartOfCalculationTypes=ChartOfCalculationTypes.XXX"') + + # BusinessProcess: Task should not be empty + if md_type == 'BusinessProcess': + task_prop = find(props_node, 'md:Task') + if task_prop is None or not text_of(task_prop): + report_warn('10. BusinessProcess: empty Task reference') + check10_issues += 1 + print('[HINT] /meta-edit -Operation modify-property -Value "Task=Task.XXX"') + + # ChartOfAccounts: ExtDimensionTypes should be set if MaxExtDimensionCount > 0 + if md_type == 'ChartOfAccounts': + max_ext_dim = find(props_node, 'md:MaxExtDimensionCount') + if max_ext_dim is not None: + try: + med_val = int(inner_text(max_ext_dim) or '0') + except ValueError: + med_val = 0 + if med_val > 0: + edt = find(props_node, 'md:ExtDimensionTypes') + if edt is None or not text_of(edt): + report_warn('10. ChartOfAccounts: MaxExtDimensionCount>0 but ExtDimensionTypes is empty') + check10_issues += 1 + print('[HINT] /meta-edit -Operation modify-property -Value "ExtDimensionTypes=ChartOfCharacteristicTypes.XXX"') + if check10_ok and check10_issues == 0: report_ok("10. Cross-property consistency") diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index a5792a15..bbd2a35e 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -580,6 +580,7 @@ DSL для `columns` (§12). | `autonumbering` | `true` | Autonumbering | | `dataLockControlMode` | `Automatic` | DataLockControlMode | | `fullTextSearch` | `Use` | FullTextSearch | +| `task` | `""` | Task (ссылка на Task.XXX) | | `attributes` | `[]` | → Attribute в ChildObjects | | `tabularSections` | `{}` | → TabularSection в ChildObjects | @@ -587,7 +588,7 @@ DSL для `columns` (§12). Дополнительно: `Ext/Flowchart.xml` (заглушка карты маршрута). ```json -{ "type": "BusinessProcess", "name": "Задание", "attributes": ["Описание: String(200)"] } +{ "type": "BusinessProcess", "name": "Задание", "task": "Task.ЗадачаИсполнителя", "attributes": ["Описание: String(200)"] } ``` ### 7.21 Task From e717d4a57fc989cdbbd36e2d2ca1fb6750f9e346 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 7 Mar 2026 21:43:21 +0300 Subject: [PATCH 02/19] fix(meta-compile): remove invalid properties from ChartOf* types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ChartOfCharacteristicTypes: remove CodeType (not a valid property). ChartOfAccounts: remove Autonumbering, Hierarchical (not valid). ChartOfCalculationTypes: remove CheckUnique, Autonumbering (not valid), fix DependenceOnCalculationTypes default from NotUsed to DontUse. Verified against real ACC 8.3.24 dumps. E2E test passed: cf-init → meta-compile ×18 → LoadConfigFromFiles → UpdateDBCfg. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/scripts/meta-compile.ps1 | 12 +----------- .claude/skills/meta-compile/scripts/meta-compile.py | 11 +---------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index c67e614a..e4e16bfb 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1526,13 +1526,11 @@ function Emit-ChartOfCharacteristicTypesProperties { $codeLength = if ($null -ne $def.codeLength) { "$($def.codeLength)" } else { "9" } $descriptionLength = if ($null -ne $def.descriptionLength) { "$($def.descriptionLength)" } else { "25" } - $codeType = if ($def.codeType) { "$($def.codeType)" } else { "String" } $codeAllowedLength = if ($def.codeAllowedLength) { "$($def.codeAllowedLength)" } else { "Variable" } $autonumbering = if ($def.autonumbering -eq $false) { "false" } else { "true" } $checkUnique = if ($def.checkUnique -eq $true) { "true" } else { "false" } X "$i$codeLength" - X "$i$codeType" X "$i$codeAllowedLength" X "$i$descriptionLength" X "$i$checkUnique" @@ -1702,14 +1700,10 @@ function Emit-ChartOfAccountsProperties { X "$i$descriptionLength" X "$i$codeSeries" X "$ifalse" - X "$itrue" X "$iAsDescription" X "$i$autoOrder" X "$i$orderLength" - $hierarchical = if ($def.hierarchical -eq $true) { "true" } else { "false" } - X "$i$hierarchical" - X "$iInDialog" Emit-StandardAttributes $i "ChartOfAccounts" @@ -1813,18 +1807,14 @@ function Emit-ChartOfCalculationTypesProperties { $descriptionLength = if ($null -ne $def.descriptionLength) { "$($def.descriptionLength)" } else { "25" } $codeType = if ($def.codeType) { "$($def.codeType)" } else { "String" } $codeAllowedLength = if ($def.codeAllowedLength) { "$($def.codeAllowedLength)" } else { "Variable" } - $autonumbering = if ($def.autonumbering -eq $false) { "false" } else { "true" } - $checkUnique = if ($def.checkUnique -eq $true) { "true" } else { "false" } X "$i$codeLength" X "$i$codeType" X "$i$codeAllowedLength" X "$i$descriptionLength" X "$iAsDescription" - X "$i$checkUnique" - X "$i$autonumbering" - $dependence = if ($def.dependenceOnCalculationTypes) { "$($def.dependenceOnCalculationTypes)" } else { "NotUsed" } + $dependence = if ($def.dependenceOnCalculationTypes) { "$($def.dependenceOnCalculationTypes)" } else { "DontUse" } X "$i$dependence" # BaseCalculationTypes diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index b1ea7bcf..7529563e 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1341,12 +1341,10 @@ def emit_chart_of_characteristic_types_properties(indent): X(f'{i}true') code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9' description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '25' - code_type = str(defn['codeType']) if defn.get('codeType') else 'String' code_allowed_length = str(defn['codeAllowedLength']) if defn.get('codeAllowedLength') else 'Variable' autonumbering = 'false' if defn.get('autonumbering') is False else 'true' check_unique = 'true' if defn.get('checkUnique') is True else 'false' X(f'{i}{code_length}') - X(f'{i}{code_type}') X(f'{i}{code_allowed_length}') X(f'{i}{description_length}') X(f'{i}{check_unique}') @@ -1490,12 +1488,9 @@ def emit_chart_of_accounts_properties(indent): X(f'{i}{description_length}') X(f'{i}{code_series}') X(f'{i}false') - X(f'{i}true') X(f'{i}AsDescription') X(f'{i}{auto_order}') X(f'{i}{order_length}') - hierarchical = 'true' if defn.get('hierarchical') is True else 'false' - X(f'{i}{hierarchical}') X(f'{i}InDialog') emit_standard_attributes(i, 'ChartOfAccounts') X(f'{i}') @@ -1578,16 +1573,12 @@ def emit_chart_of_calculation_types_properties(indent): description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '25' code_type = str(defn['codeType']) if defn.get('codeType') else 'String' code_allowed_length = str(defn['codeAllowedLength']) if defn.get('codeAllowedLength') else 'Variable' - autonumbering = 'false' if defn.get('autonumbering') is False else 'true' - check_unique = 'true' if defn.get('checkUnique') is True else 'false' X(f'{i}{code_length}') X(f'{i}{code_type}') X(f'{i}{code_allowed_length}') X(f'{i}{description_length}') X(f'{i}AsDescription') - X(f'{i}{check_unique}') - X(f'{i}{autonumbering}') - dependence = str(defn['dependenceOnCalculationTypes']) if defn.get('dependenceOnCalculationTypes') else 'NotUsed' + dependence = str(defn['dependenceOnCalculationTypes']) if defn.get('dependenceOnCalculationTypes') else 'DontUse' X(f'{i}{dependence}') base_types = list(defn.get('baseCalculationTypes', [])) if base_types: From c2b90a97ef4a1d204276b64639009405a3320f03 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 7 Mar 2026 21:49:57 +0300 Subject: [PATCH 03/19] feat(meta-validate): add forbidden property and DocumentJournal checks Check 12: detect properties that are invalid for a given metadata type and would cause LoadConfigFromFiles to fail: - ChartOfCharacteristicTypes: CodeType - ChartOfAccounts: Autonumbering, Hierarchical - ChartOfCalculationTypes: CheckUnique, Autonumbering - ExchangePlan: CodeType, CheckUnique, Autonumbering Check 10 extensions: - DocumentJournal: warn on empty RegisteredDocuments - Add DependenceOnCalculationTypes to enum validation (Check 4) Co-Authored-By: Claude Opus 4.6 --- .../meta-validate/scripts/meta-validate.ps1 | 44 +++++++++++++++++++ .../meta-validate/scripts/meta-validate.py | 40 +++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index e2f2f46a..ed26a2a6 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -216,6 +216,15 @@ $validPropertyValues = @{ "FillChecking" = @("DontCheck","ShowError","ShowWarning") "Indexing" = @("DontIndex","Index","IndexWithAdditionalOrder") "DataHistory" = @("Use","DontUse") + "DependenceOnCalculationTypes" = @("DontUse","RequireCalculationTypes") +} + +# Properties forbidden per type (would cause LoadConfigFromFiles error) +$forbiddenProperties = @{ + "ChartOfCharacteristicTypes" = @("CodeType") + "ChartOfAccounts" = @("Autonumbering","Hierarchical") + "ChartOfCalculationTypes" = @("CheckUnique","Autonumbering") + "ExchangePlan" = @("CodeType","CheckUnique","Autonumbering") } # --- 1. Parse XML --- @@ -955,6 +964,20 @@ if ($propsNode) { } } + # DocumentJournal: RegisteredDocuments should not be empty + if ($mdType -eq "DocumentJournal") { + $regDocs = $propsNode.SelectSingleNode("md:RegisteredDocuments", $ns) + $hasRegDocs = $false + if ($regDocs) { + $items = $regDocs.SelectNodes("v8:Type", $ns) + if ($items.Count -gt 0) { $hasRegDocs = $true } + } + if (-not $hasRegDocs) { + Report-Warn "10. DocumentJournal: no RegisteredDocuments specified" + $check10Issues++ + } + } + # ChartOfAccounts: ExtDimensionTypes should be set if MaxExtDimensionCount > 0 if ($mdType -eq "ChartOfAccounts") { $maxExtDim = $propsNode.SelectSingleNode("md:MaxExtDimensionCount", $ns) @@ -1071,6 +1094,27 @@ if ($mdType -eq "HTTPService" -and $childObjNode) { Report-OK "11. HTTPService/WebService: N/A" } +if ($script:stopped) { & $finalize; exit 1 } + +# --- Check 12: Forbidden properties per type --- + +if ($propsNode -and $forbiddenProperties.ContainsKey($mdType)) { + $forbidden = $forbiddenProperties[$mdType] + $check12Ok = $true + foreach ($fp in $forbidden) { + $fpNode = $propsNode.SelectSingleNode("md:$fp", $ns) + if ($fpNode) { + Report-Error "12. Forbidden property '$fp' present in $mdType (will fail on LoadConfigFromFiles)" + $check12Ok = $false + } + } + if ($check12Ok) { + Report-OK "12. Forbidden properties: none found" + } +} else { + Report-OK "12. Forbidden properties: N/A" +} + # --- Final output --- & $finalize diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index f7cea0c6..7bf07bf1 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -216,6 +216,15 @@ valid_property_values = { "FillChecking": ["DontCheck", "ShowError", "ShowWarning"], "Indexing": ["DontIndex", "Index", "IndexWithAdditionalOrder"], "DataHistory": ["Use", "DontUse"], + "DependenceOnCalculationTypes": ["DontUse", "RequireCalculationTypes"], +} + +# Properties forbidden per type (would cause LoadConfigFromFiles error) +forbidden_properties = { + "ChartOfCharacteristicTypes": ["CodeType"], + "ChartOfAccounts": ["Autonumbering", "Hierarchical"], + "ChartOfCalculationTypes": ["CheckUnique", "Autonumbering"], + "ExchangePlan": ["CodeType", "CheckUnique", "Autonumbering"], } # ── Namespaces ─────────────────────────────────────────────── @@ -898,6 +907,18 @@ if props_node is not None: check10_issues += 1 print('[HINT] /meta-edit -Operation modify-property -Value "Task=Task.XXX"') + # DocumentJournal: RegisteredDocuments should not be empty + if md_type == 'DocumentJournal': + reg_docs = find(props_node, 'md:RegisteredDocuments') + has_reg_docs = False + if reg_docs is not None: + items = find_all(reg_docs, 'v8:Type') + if len(items) > 0: + has_reg_docs = True + if not has_reg_docs: + report_warn('10. DocumentJournal: no RegisteredDocuments specified') + check10_issues += 1 + # ChartOfAccounts: ExtDimensionTypes should be set if MaxExtDimensionCount > 0 if md_type == 'ChartOfAccounts': max_ext_dim = find(props_node, 'md:MaxExtDimensionCount') @@ -1001,6 +1022,25 @@ elif md_type == "WebService" and child_obj_node is not None: else: report_ok("11. HTTPService/WebService: N/A") +if stopped: + finalize() + sys.exit(1) + +# ── Check 12: Forbidden properties per type ────────────────── + +if props_node is not None and md_type in forbidden_properties: + forbidden = forbidden_properties[md_type] + check12_ok = True + for fp in forbidden: + fp_node = find(props_node, f"md:{fp}") + if fp_node is not None: + report_error(f"12. Forbidden property '{fp}' present in {md_type} (will fail on LoadConfigFromFiles)") + check12_ok = False + if check12_ok: + report_ok("12. Forbidden properties: none found") +else: + report_ok("12. Forbidden properties: N/A") + # ── Final output ────────────────────────────────────────────── finalize() From ca328e3e8f94225445c9074a4f2c092d08769685 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 7 Mar 2026 22:00:37 +0300 Subject: [PATCH 04/19] feat(meta-validate): add cross-object register-registrar check Detect configDir by walking up from object path to find Configuration.xml. For AccumulationRegister, AccountingRegister, CalculationRegister, and InformationRegister (RecorderSubordinate) scan Documents/*.xml to verify at least one document references the register in RegisterRecords. Warn if no registrar found. Co-Authored-By: Claude Opus 4.6 --- .../meta-validate/scripts/meta-validate.ps1 | 45 +++++++++++++++++++ .../meta-validate/scripts/meta-validate.py | 41 +++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index ed26a2a6..96bce120 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -54,6 +54,19 @@ if (-not (Test-Path $ObjectPath)) { $resolvedPath = (Resolve-Path $ObjectPath).Path +# --- Detect config directory (for cross-object checks) --- + +$script:configDir = $null +$probe = Split-Path $resolvedPath +for ($depth = 0; $depth -lt 4; $depth++) { + if (-not $probe) { break } + if (Test-Path (Join-Path $probe "Configuration.xml")) { + $script:configDir = $probe + break + } + $probe = Split-Path $probe +} + # --- Output infrastructure --- $script:errors = 0 @@ -990,6 +1003,38 @@ if ($propsNode) { } } } + + # Register: must have at least one registrar document + $registerTypes = @("AccumulationRegister","AccountingRegister","CalculationRegister","InformationRegister") + if ($registerTypes -contains $mdType -and $script:configDir -and $objName -ne "(unknown)") { + $needsRegistrar = $true + # InformationRegister with WriteMode=Independent does not need a registrar + if ($mdType -eq "InformationRegister") { + $writeMode = $propsNode.SelectSingleNode("md:WriteMode", $ns) + if (-not $writeMode -or $writeMode.InnerText -ne "RecorderSubordinate") { + $needsRegistrar = $false + } + } + if ($needsRegistrar) { + $regRef = "$mdType.$objName" + $docsDir = Join-Path $script:configDir "Documents" + $hasRegistrar = $false + if (Test-Path $docsDir) { + $docXmls = Get-ChildItem $docsDir -Filter "*.xml" -File -ErrorAction SilentlyContinue + foreach ($docXml in $docXmls) { + $content = [System.IO.File]::ReadAllText($docXml.FullName, [System.Text.Encoding]::UTF8) + if ($content.Contains($regRef)) { + $hasRegistrar = $true + break + } + } + } + if (-not $hasRegistrar) { + Report-Warn "10. $mdType`: no registrar document found (none references '$regRef' in RegisterRecords)" + $check10Issues++ + } + } + } } if ($check10Ok -and $check10Issues -eq 0) { diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index 7bf07bf1..a98a3429 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -59,6 +59,18 @@ if not os.path.exists(object_path): resolved_path = os.path.abspath(object_path) +# ── detect config directory (for cross-object checks) ──────── + +config_dir = None +probe = os.path.dirname(resolved_path) +for _ in range(4): + if not probe: + break + if os.path.exists(os.path.join(probe, "Configuration.xml")): + config_dir = probe + break + probe = os.path.dirname(probe) + # ── output infrastructure ──────────────────────────────────── errors = 0 @@ -934,6 +946,35 @@ if props_node is not None: check10_issues += 1 print('[HINT] /meta-edit -Operation modify-property -Value "ExtDimensionTypes=ChartOfCharacteristicTypes.XXX"') + # Register: must have at least one registrar document + register_types = ('AccumulationRegister', 'AccountingRegister', 'CalculationRegister', 'InformationRegister') + if md_type in register_types and config_dir and obj_name != '(unknown)': + needs_registrar = True + # InformationRegister with WriteMode=Independent does not need a registrar + if md_type == 'InformationRegister': + write_mode = find(props_node, 'md:WriteMode') + if write_mode is None or inner_text(write_mode) != 'RecorderSubordinate': + needs_registrar = False + if needs_registrar: + reg_ref = f'{md_type}.{obj_name}' + docs_dir = os.path.join(config_dir, 'Documents') + has_registrar = False + if os.path.isdir(docs_dir): + for fname in os.listdir(docs_dir): + if not fname.endswith('.xml'): + continue + fpath = os.path.join(docs_dir, fname) + if not os.path.isfile(fpath): + continue + with open(fpath, 'r', encoding='utf-8-sig') as f: + content = f.read() + if reg_ref in content: + has_registrar = True + break + if not has_registrar: + report_warn(f"10. {md_type}: no registrar document found (none references '{reg_ref}' in RegisterRecords)") + check10_issues += 1 + if check10_ok and check10_issues == 0: report_ok("10. Cross-property consistency") From 87dc18b12022899e7da6a21af7fec1fb2b84ca4a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sat, 7 Mar 2026 22:08:46 +0300 Subject: [PATCH 05/19] feat(meta-validate): add method reference and DocumentJournal column checks Check 13: validates EventSubscription.Handler and ScheduledJob.MethodName reference format (CommonModuleName.ProcedureName), verifies CommonModule exists and optionally checks BSL for exported procedure. Check 14: validates DocumentJournal Column References are non-empty (empty References causes LoadConfigFromFiles to fail). Co-Authored-By: Claude Opus 4.6 --- .../meta-validate/scripts/meta-validate.ps1 | 94 +++++++++++++++++++ .../meta-validate/scripts/meta-validate.py | 88 +++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index 96bce120..a161fb9a 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -1160,6 +1160,100 @@ if ($propsNode -and $forbiddenProperties.ContainsKey($mdType)) { Report-OK "12. Forbidden properties: N/A" } +if ($script:stopped) { & $finalize; exit 1 } + +# --- Check 13: Method reference validation (EventSubscription.Handler, ScheduledJob.MethodName) --- + +if ($propsNode -and $mdType -in @("EventSubscription","ScheduledJob") -and $script:configDir) { + $check13Ok = $true + $methodRef = $null + $propLabel = $null + + if ($mdType -eq "EventSubscription") { + $hNode = $propsNode.SelectSingleNode("md:Handler", $ns) + if ($hNode) { $methodRef = $hNode.InnerText.Trim() } + $propLabel = "Handler" + } elseif ($mdType -eq "ScheduledJob") { + $mNode = $propsNode.SelectSingleNode("md:MethodName", $ns) + if ($mNode) { $methodRef = $mNode.InnerText.Trim() } + $propLabel = "MethodName" + } + + if ($methodRef) { + $parts = $methodRef.Split('.') + if ($parts.Count -ne 2) { + Report-Error "13. ${mdType}.${propLabel} = '$methodRef': expected format 'CommonModuleName.ProcedureName'" + $check13Ok = $false + } else { + $cmName = $parts[0] + $procName = $parts[1] + $cmXml = Join-Path (Join-Path $script:configDir "CommonModules") "$cmName.xml" + if (-not (Test-Path $cmXml)) { + Report-Error "13. ${mdType}.${propLabel}: CommonModule '$cmName' not found (expected $cmXml)" + $check13Ok = $false + } else { + # Check BSL file for exported procedure + $bslPath = Join-Path (Join-Path (Join-Path $script:configDir "CommonModules") $cmName) "Ext/Module.bsl" + if (Test-Path $bslPath) { + $bslContent = [System.IO.File]::ReadAllText($bslPath, [System.Text.Encoding]::UTF8) + # Match: Procedure/Function ProcName(...) Export or Процедура/Функция ProcName(...) Экспорт + $exportPattern = "(?mi)^[\s]*(Procedure|Function|Процедура|Функция)\s+$([regex]::Escape($procName))\s*\(.*\)\s+(Export|Экспорт)" + if (-not [regex]::IsMatch($bslContent, $exportPattern)) { + Report-Warn "13. ${mdType}.${propLabel}: procedure '$procName' not found as exported in CommonModule '$cmName'" + $check13Ok = $false + } + } else { + Report-Warn "13. ${mdType}.${propLabel}: BSL file not found ($bslPath), cannot verify procedure" + } + } + } + } + + if ($check13Ok) { + Report-OK "13. Method reference: $propLabel = '$methodRef'" + } +} else { + Report-OK "13. Method reference: N/A" +} + +if ($script:stopped) { & $finalize; exit 1 } + +# --- Check 14: DocumentJournal Column content --- + +if ($mdType -eq "DocumentJournal" -and $childObjNode) { + $columns = $childObjNode.SelectNodes("md:Column", $ns) + $check14Ok = $true + $colCount = 0 + $emptyRefCount = 0 + + foreach ($col in $columns) { + $colCount++ + $colProps = $col.SelectSingleNode("md:Properties", $ns) + $colNameNode = if ($colProps) { $colProps.SelectSingleNode("md:Name", $ns) } else { $null } + $colName = if ($colNameNode) { $colNameNode.InnerText } else { "(unnamed)" } + + $refs = if ($colProps) { $colProps.SelectSingleNode("md:References", $ns) } else { $null } + $hasItems = $false + if ($refs) { + $items = $refs.SelectNodes("xr:Item", $ns) + if ($items.Count -gt 0) { $hasItems = $true } + } + if (-not $hasItems) { + Report-Error "14. DocumentJournal Column '$colName': empty References (will fail on LoadConfigFromFiles)" + $check14Ok = $false + $emptyRefCount++ + } + } + + if ($check14Ok -and $colCount -gt 0) { + Report-OK "14. DocumentJournal Columns: $colCount column(s), all have References" + } elseif ($colCount -eq 0) { + Report-OK "14. DocumentJournal Columns: none" + } +} else { + Report-OK "14. DocumentJournal Columns: N/A" +} + # --- Final output --- & $finalize diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index a98a3429..e0d59234 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -1082,6 +1082,94 @@ if props_node is not None and md_type in forbidden_properties: else: report_ok("12. Forbidden properties: N/A") +if stopped: + finalize() + sys.exit(1) + +# ── Check 13: Method reference validation ───────────────────── + +if props_node is not None and md_type in ("EventSubscription", "ScheduledJob") and config_dir: + check13_ok = True + method_ref = None + prop_label = None + + if md_type == "EventSubscription": + h_node = find(props_node, "md:Handler") + if h_node is not None: + method_ref = text_of(h_node) + prop_label = "Handler" + elif md_type == "ScheduledJob": + m_node = find(props_node, "md:MethodName") + if m_node is not None: + method_ref = text_of(m_node) + prop_label = "MethodName" + + if method_ref: + parts = method_ref.split(".") + if len(parts) != 2: + report_error(f"13. {md_type}.{prop_label} = '{method_ref}': expected format 'CommonModuleName.ProcedureName'") + check13_ok = False + else: + cm_name = parts[0] + proc_name = parts[1] + cm_xml = os.path.join(config_dir, "CommonModules", f"{cm_name}.xml") + if not os.path.exists(cm_xml): + report_error(f"13. {md_type}.{prop_label}: CommonModule '{cm_name}' not found (expected {cm_xml})") + check13_ok = False + else: + # Check BSL file for exported procedure + bsl_path = os.path.join(config_dir, "CommonModules", cm_name, "Ext", "Module.bsl") + if os.path.exists(bsl_path): + with open(bsl_path, "r", encoding="utf-8-sig") as f: + bsl_content = f.read() + export_pattern = rf"(?mi)^\s*(Procedure|Function|Процедура|Функция)\s+{re.escape(proc_name)}\s*\(.*\)\s+(Export|Экспорт)" + if not re.search(export_pattern, bsl_content): + report_warn(f"13. {md_type}.{prop_label}: procedure '{proc_name}' not found as exported in CommonModule '{cm_name}'") + check13_ok = False + else: + report_warn(f"13. {md_type}.{prop_label}: BSL file not found ({bsl_path}), cannot verify procedure") + + if check13_ok: + report_ok(f"13. Method reference: {prop_label} = '{method_ref}'") +else: + report_ok("13. Method reference: N/A") + +if stopped: + finalize() + sys.exit(1) + +# ── Check 14: DocumentJournal Column content ────────────────── + +if md_type == "DocumentJournal" and child_obj_node is not None: + columns = find_all(child_obj_node, "md:Column") + check14_ok = True + col_count = 0 + empty_ref_count = 0 + + for col in columns: + col_count += 1 + col_props = find(col, "md:Properties") + col_name_node = find(col_props, "md:Name") if col_props is not None else None + col_name = inner_text(col_name_node) if col_name_node is not None else "(unnamed)" + + refs = find(col_props, "md:References") if col_props is not None else None + has_items = False + if refs is not None: + items = find_all(refs, "xr:Item") + if len(items) > 0: + has_items = True + if not has_items: + report_error(f"14. DocumentJournal Column '{col_name}': empty References (will fail on LoadConfigFromFiles)") + check14_ok = False + empty_ref_count += 1 + + if check14_ok and col_count > 0: + report_ok(f"14. DocumentJournal Columns: {col_count} column(s), all have References") + elif col_count == 0: + report_ok("14. DocumentJournal Columns: none") +else: + report_ok("14. DocumentJournal Columns: N/A") + # ── Final output ────────────────────────────────────────────── finalize() From dbb39b7a3e12b26d453ebb478e6ceb559b64a35a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 11:05:29 +0300 Subject: [PATCH 06/19] fix(meta-compile): fix String/Number length loss, RegisterRecords format, CommonModule prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Parse-AttributeShorthand: combine type + length/precision from separate JSON fields (String→String(N), Number→Number(D,F)) — fixes SDBL error "Строка без спецификации длины недопустима" - Constant valueType: same length combination fix - DefinedType: accept both valueType (singular) and valueTypes (plural) - ChartOfCharacteristicTypes: default String length 0→100 - Document RegisterRecords: - ScheduledJob.MethodName, EventSubscription.Handler: auto-prepend CommonModule. prefix - meta-validate check 13: support 3-part method format (CommonModule.Module.Proc) E2E verified: 18 types (21 objects) pass cf-init → meta-compile → LoadConfigFromFiles → UpdateDBCfg Co-Authored-By: Claude Opus 4.6 --- .../meta-compile/scripts/meta-compile.ps1 | 40 ++++++++++++++++--- .../meta-compile/scripts/meta-compile.py | 33 +++++++++++++-- .../meta-validate/scripts/meta-validate.ps1 | 16 ++++++-- .../meta-validate/scripts/meta-validate.py | 15 +++++-- 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index e4e16bfb..dd310970 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -345,9 +345,20 @@ function Parse-AttributeShorthand { # Object form $name = "$($val.name)" + # Build type string combining type + length/precision from separate JSON fields + $typeStr = if ($val.type) { "$($val.type)" } else { "" } + if ($typeStr -and -not $typeStr.Contains('(')) { + if ($typeStr -eq "String" -and $val.length) { + $typeStr = "String($($val.length))" + } elseif ($typeStr -eq "Number" -and $val.length) { + $prec = if ($val.precision) { $val.precision } else { 0 } + $nn = if ($val.nonneg -or $val.nonnegative) { ",nonneg" } else { "" } + $typeStr = "Number($($val.length),$prec$nn)" + } + } return @{ name = $name - type = if ($val.type) { "$($val.type)" } else { "" } + type = $typeStr synonym = if ($val.synonym) { "$($val.synonym)" } else { Split-CamelCase $name } comment = if ($val.comment) { "$($val.comment)" } else { "" } flags = @(if ($val.flags) { $val.flags } else { @() }) @@ -1078,7 +1089,7 @@ function Emit-DocumentProperties { if ($regRecords.Count -gt 0) { X "$i" foreach ($rr in $regRecords) { - X "$i`t$rr" + X "$i`t$rr" } X "$i" } else { @@ -1139,8 +1150,17 @@ function Emit-ConstantProperties { Emit-MLText $i "Synonym" $synonym X "$i" - # Type + # Type — combine valueType + length/precision from separate JSON fields $valueType = if ($def.valueType) { "$($def.valueType)" } else { "String" } + if ($valueType -and -not $valueType.Contains('(')) { + if ($valueType -eq "String" -and $def.length) { + $valueType = "String($($def.length))" + } elseif ($valueType -eq "Number" -and $def.length) { + $p = if ($def.precision) { $def.precision } else { 0 } + $nn = if ($def.nonneg -or $def.nonnegative) { ",nonneg" } else { "" } + $valueType = "Number($($def.length),$p$nn)" + } + } Emit-ValueType $i $valueType X "$itrue" @@ -1265,10 +1285,12 @@ function Emit-DefinedTypeProperties { Emit-MLText $i "Synonym" $synonym X "$i" - # Type — composite type with multiple v8:Type entries + # Type — composite type with multiple v8:Type entries (accept both valueType and valueTypes) $valueTypes = @() if ($def.valueTypes) { $valueTypes = @($def.valueTypes) + } elseif ($def.valueType) { + $valueTypes = @($def.valueType) } if ($valueTypes.Count -gt 0) { X "$i" @@ -1345,6 +1367,10 @@ function Emit-ScheduledJobProperties { X "$i" $methodName = if ($def.methodName) { "$($def.methodName)" } else { "" } + # Ensure CommonModule. prefix + if ($methodName -and -not $methodName.StartsWith("CommonModule.")) { + $methodName = "CommonModule.$methodName" + } X "$i$(Esc-Xml $methodName)" $description = if ($def.description) { "$($def.description)" } else { $synonym } @@ -1391,6 +1417,10 @@ function Emit-EventSubscriptionProperties { X "$i$event" $handler = if ($def.handler) { "$($def.handler)" } else { "" } + # Ensure CommonModule. prefix + if ($handler -and -not $handler.StartsWith("CommonModule.")) { + $handler = "CommonModule.$handler" + } X "$i$(Esc-Xml $handler)" } @@ -1556,7 +1586,7 @@ function Emit-ChartOfCharacteristicTypesProperties { X "$i`txs:boolean" X "$i`txs:string" X "$i`t" - X "$i`t`t0" + X "$i`t`t100" X "$i`t`tVariable" X "$i`t" X "$i`txs:decimal" diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 7529563e..f422955f 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -320,9 +320,18 @@ def parse_attribute_shorthand(val): return parsed # Object form name = str(val.get('name', '')) + # Build type string combining type + length/precision from separate JSON fields + type_str = str(val['type']) if val.get('type') else '' + if type_str and '(' not in type_str: + if type_str == 'String' and val.get('length'): + type_str = f"String({val['length']})" + elif type_str == 'Number' and val.get('length'): + prec = val.get('precision', 0) + nn = ',nonneg' if val.get('nonneg') or val.get('nonnegative') else '' + type_str = f"Number({val['length']},{prec}{nn})" return { 'name': name, - 'type': str(val['type']) if val.get('type') else '', + 'type': type_str, 'synonym': str(val['synonym']) if val.get('synonym') else split_camel_case(name), 'comment': str(val['comment']) if val.get('comment') else '', 'flags': list(val.get('flags', [])), @@ -968,7 +977,7 @@ def emit_document_properties(indent): if reg_records: X(f'{i}') for rr in reg_records: - X(f'{i}\t{rr}') + X(f'{i}\t{rr}') X(f'{i}') else: X(f'{i}') @@ -1014,7 +1023,15 @@ def emit_constant_properties(indent): X(f'{i}{esc_xml(obj_name)}') emit_mltext(i, 'Synonym', synonym) X(f'{i}') + # Type — combine valueType + length/precision from separate JSON fields value_type = str(defn['valueType']) if defn.get('valueType') else 'String' + if value_type and '(' not in value_type: + if value_type == 'String' and defn.get('length'): + value_type = f"String({defn['length']})" + elif value_type == 'Number' and defn.get('length'): + prec = defn.get('precision', 0) + nn = ',nonneg' if defn.get('nonneg') or defn.get('nonnegative') else '' + value_type = f"Number({defn['length']},{prec}{nn})" emit_value_type(i, value_type) X(f'{i}true') X(f'{i}') @@ -1111,7 +1128,11 @@ def emit_defined_type_properties(indent): X(f'{i}{esc_xml(obj_name)}') emit_mltext(i, 'Synonym', synonym) X(f'{i}') + # Accept both valueType and valueTypes value_types = list(defn.get('valueTypes', [])) + if not value_types and defn.get('valueType'): + vt_raw = defn['valueType'] + value_types = list(vt_raw) if isinstance(vt_raw, list) else [vt_raw] if value_types: X(f'{i}') for vt in value_types: @@ -1182,6 +1203,9 @@ def emit_scheduled_job_properties(indent): emit_mltext(i, 'Synonym', synonym) X(f'{i}') method_name = str(defn['methodName']) if defn.get('methodName') else '' + # Ensure CommonModule. prefix + if method_name and not method_name.startswith('CommonModule.'): + method_name = f'CommonModule.{method_name}' X(f'{i}{esc_xml(method_name)}') description = str(defn['description']) if defn.get('description') else synonym X(f'{i}{esc_xml(description)}') @@ -1213,6 +1237,9 @@ def emit_event_subscription_properties(indent): event = str(defn['event']) if defn.get('event') else 'BeforeWrite' X(f'{i}{event}') handler = str(defn['handler']) if defn.get('handler') else '' + # Ensure CommonModule. prefix + if handler and not handler.startswith('CommonModule.'): + handler = f'CommonModule.{handler}' X(f'{i}{esc_xml(handler)}') # --- 13b. Report, DataProcessor --- @@ -1366,7 +1393,7 @@ def emit_chart_of_characteristic_types_properties(indent): X(f'{i}\txs:boolean') X(f'{i}\txs:string') X(f'{i}\t') - X(f'{i}\t\t0') + X(f'{i}\t\t100') X(f'{i}\t\tVariable') X(f'{i}\t') X(f'{i}\txs:decimal') diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index a161fb9a..05257e55 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -1181,12 +1181,20 @@ if ($propsNode -and $mdType -in @("EventSubscription","ScheduledJob") -and $scri if ($methodRef) { $parts = $methodRef.Split('.') - if ($parts.Count -ne 2) { - Report-Error "13. ${mdType}.${propLabel} = '$methodRef': expected format 'CommonModuleName.ProcedureName'" - $check13Ok = $false - } else { + # Format: CommonModule.ModuleName.ProcedureName (3 parts) or ModuleName.ProcedureName (2 parts, legacy) + if ($parts.Count -eq 3 -and $parts[0] -eq "CommonModule") { + $cmName = $parts[1] + $procName = $parts[2] + } elseif ($parts.Count -eq 2) { $cmName = $parts[0] $procName = $parts[1] + } else { + Report-Error "13. ${mdType}.${propLabel} = '$methodRef': expected format 'CommonModule.ModuleName.ProcedureName'" + $check13Ok = $false + $cmName = $null + $procName = $null + } + if ($cmName) { $cmXml = Join-Path (Join-Path $script:configDir "CommonModules") "$cmName.xml" if (-not (Test-Path $cmXml)) { Report-Error "13. ${mdType}.${propLabel}: CommonModule '$cmName' not found (expected $cmXml)" diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index e0d59234..adf42731 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -1106,12 +1106,19 @@ if props_node is not None and md_type in ("EventSubscription", "ScheduledJob") a if method_ref: parts = method_ref.split(".") - if len(parts) != 2: - report_error(f"13. {md_type}.{prop_label} = '{method_ref}': expected format 'CommonModuleName.ProcedureName'") - check13_ok = False - else: + # Format: CommonModule.ModuleName.ProcedureName (3 parts) or ModuleName.ProcedureName (2 parts, legacy) + if len(parts) == 3 and parts[0] == "CommonModule": + cm_name = parts[1] + proc_name = parts[2] + elif len(parts) == 2: cm_name = parts[0] proc_name = parts[1] + else: + report_error(f"13. {md_type}.{prop_label} = '{method_ref}': expected format 'CommonModule.ModuleName.ProcedureName'") + check13_ok = False + cm_name = None + proc_name = None + if cm_name: cm_xml = os.path.join(config_dir, "CommonModules", f"{cm_name}.xml") if not os.path.exists(cm_xml): report_error(f"13. {md_type}.{prop_label}: CommonModule '{cm_name}' not found (expected {cm_xml})") From 49108f72dc05765867e971a01e1405c1687acb2d Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 11:18:52 +0300 Subject: [PATCH 07/19] =?UTF-8?q?docs(meta-dsl-spec):=20update=20to=20v2.1?= =?UTF-8?q?=20=E2=80=94=20reflect=20e2e-verified=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §4.2: document separate type+length/precision fields in object form - §7.4 Constant: add length/precision fields - §7.7 DefinedType: document valueType (singular) alias - §7.9 ScheduledJob: document CommonModule. auto-prefix for methodName - §7.10 EventSubscription: document CommonModule. auto-prefix for handler - §7.13 ExchangePlan: remove codeType/autonumbering/checkUnique (not valid) - §7.14 ChartOfCharacteristicTypes: default String length 0→100 - §7.20 BusinessProcess: add task to example Co-Authored-By: Claude Opus 4.6 --- docs/meta-dsl-spec.md | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index bbd2a35e..3f986e8d 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -1,6 +1,6 @@ # Meta DSL — спецификация JSON-формата для объектов метаданных 1С -Версия: 2.0 +Версия: 2.1 ## Обзор @@ -130,6 +130,17 @@ JSON DSL для описания объектов метаданных конф } ``` +Тип можно задать единой строкой (`"type": "String(100)"`) или раздельными полями: + +```json +{ "name": "Имя", "type": "String", "length": 100 } +{ "name": "Сумма", "type": "Number", "length": 15, "precision": 2 } +{ "name": "Остаток", "type": "Number", "length": 15, "precision": 2, "nonneg": true } +``` + +Раздельная форма эквивалентна `String(100)`, `Number(15,2)`, `Number(15,2,nonneg)`. +Если `type` уже содержит скобки — `length`/`precision` игнорируются. + ### 4.3 Флаги | Флаг | Действие | Применимость | @@ -243,8 +254,13 @@ JSON DSL для описания объектов метаданных конф | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| | `valueType` | `String` | Type | +| `length` | — | Длина строки (если valueType=String) | +| `precision` | — | Точность числа (если valueType=Number) | | `dataLockControlMode` | `Automatic` | DataLockControlMode | +`valueType` + `length`/`precision` работают аналогично раздельной форме типа (§4.2): +`"valueType": "String", "length": 100` → `String(100)`. + ### 7.5 InformationRegister | Поле JSON | Умолчание | XML элемент | @@ -277,11 +293,13 @@ JSON DSL для описания объектов метаданных конф | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| | `valueTypes` | `[]` | Type (составной — массив `v8:Type`) | +| `valueType` | — | Алиас для `valueTypes` (принимает строку или массив) | -Без ChildObjects и модулей. +Без ChildObjects и модулей. Принимается как `valueTypes` (мн.ч.), так и `valueType` (ед.ч.). ```json { "type": "DefinedType", "name": "ДенежныеСредства", "valueTypes": ["CatalogRef.БанковскиеСчета", "CatalogRef.Кассы"] } +{ "type": "DefinedType", "name": "ФлагАктивности", "valueType": "Boolean" } ``` ### 7.8 CommonModule @@ -313,7 +331,7 @@ JSON DSL для описания объектов метаданных конф | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| -| `methodName` | `""` | MethodName | +| `methodName` | `""` | MethodName (авто-префикс `CommonModule.`) | | `description` | = synonym | Description | | `key` | `""` | Key | | `use` | `false` | Use | @@ -323,6 +341,8 @@ JSON DSL для описания объектов метаданных конф Без ChildObjects и модулей. +Формат `methodName`: `"МодульСервер.Процедура"` — при компиляции авто-дополняется до `CommonModule.МодульСервер.Процедура`. Если уже содержит `CommonModule.` — оставляется как есть. + ```json { "type": "ScheduledJob", "name": "ОбменДанными", "methodName": "ОбменДаннымиСервер.Выполнить", "use": true } ``` @@ -333,12 +353,14 @@ JSON DSL для описания объектов метаданных конф |-----------|----------|-------------| | `source` | `[]` | Source (массив `v8:Type`, формат `cfg:XxxObject.Name`) | | `event` | `BeforeWrite` | Event | -| `handler` | `""` | Handler | +| `handler` | `""` | Handler (авто-префикс `CommonModule.`) | Без ChildObjects и модулей. Значения `event`: `BeforeWrite`, `OnWrite`, `BeforeDelete`, `OnReadAtServer`, `FillCheckProcessing` и др. +Формат `handler`: `"МодульСервер.Процедура"` — при компиляции авто-дополняется до `CommonModule.МодульСервер.Процедура`. Если уже содержит `CommonModule.` — оставляется как есть. + ```json { "type": "EventSubscription", "name": "ПередЗаписьюКонтрагента", "source": ["CatalogObject.Контрагенты"], "event": "BeforeWrite", "handler": "ОбщегоНазначенияСервер.ПередЗаписьюКонтрагента" } ``` @@ -382,11 +404,8 @@ JSON DSL для описания объектов метаданных конф | Поле JSON | Умолчание | XML элемент | |-----------|----------|-------------| | `codeLength` | `9` | CodeLength | -| `codeType` | `String` | CodeType | | `codeAllowedLength` | `Variable` | CodeAllowedLength | | `descriptionLength` | `100` | DescriptionLength | -| `autonumbering` | `true` | Autonumbering | -| `checkUnique` | `false` | CheckUnique | | `distributedInfoBase` | `false` | DistributedInfoBase | | `includeConfigurationExtensions` | `false` | IncludeConfigurationExtensions | | `dataLockControlMode` | `Automatic` | DataLockControlMode | @@ -419,7 +438,7 @@ JSON DSL для описания объектов метаданных конф | `attributes` | `[]` | → Attribute в ChildObjects | | `tabularSections` | `{}` | → TabularSection в ChildObjects | -\* Если `valueTypes` не указан, по умолчанию: Boolean, String, Number(15,2), DateTime. +\* Если `valueTypes` не указан, по умолчанию: Boolean, String(100), Number(15,2), DateTime. Модули: `Ext/ObjectModule.bsl` (пустой). @@ -905,7 +924,7 @@ DSL для `columns` (§12). ### Бизнес-процесс ```json -{ "type": "BusinessProcess", "name": "Задание", "attributes": ["Описание: String(200)"] } +{ "type": "BusinessProcess", "name": "Задание", "task": "Task.ЗадачаИсполнителя", "attributes": ["Описание: String(200)"] } ``` ### Задача From 97fc6dbd7f2c13346b78e436fb28be358129a2d3 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 12:13:46 +0300 Subject: [PATCH 08/19] refactor(meta-compile): extract Build-TypeStr helper for type+length combination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify inline type string building logic (type + length/precision → "String(100)", "Number(10,2)") into a shared Build-TypeStr/build_type_str function. Used by both Parse-AttributeShorthand and Emit-ConstantProperties. Fix: check valueType before type to avoid treating metadata type name (e.g. "Constant") as data type. Co-Authored-By: Claude Opus 4.6 --- .../meta-compile/scripts/meta-compile.ps1 | 42 +++++++++---------- .../meta-compile/scripts/meta-compile.py | 33 +++++++-------- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index dd310970..c0359019 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -311,6 +311,21 @@ function Emit-FillValue { # --- 5. Attribute shorthand parser --- +function Build-TypeStr { + param($obj) + $t = if ($obj.valueType) { "$($obj.valueType)" } elseif ($obj.type) { "$($obj.type)" } else { "" } + if ($t -and -not $t.Contains('(')) { + if ($t -eq "String" -and $obj.length) { + $t = "String($($obj.length))" + } elseif ($t -eq "Number" -and $obj.length) { + $p = if ($obj.precision) { $obj.precision } else { 0 } + $nn = if ($obj.nonneg -or $obj.nonnegative) { ",nonneg" } else { "" } + $t = "Number($($obj.length),$p$nn)" + } + } + return $t +} + function Parse-AttributeShorthand { param($val) @@ -345,20 +360,9 @@ function Parse-AttributeShorthand { # Object form $name = "$($val.name)" - # Build type string combining type + length/precision from separate JSON fields - $typeStr = if ($val.type) { "$($val.type)" } else { "" } - if ($typeStr -and -not $typeStr.Contains('(')) { - if ($typeStr -eq "String" -and $val.length) { - $typeStr = "String($($val.length))" - } elseif ($typeStr -eq "Number" -and $val.length) { - $prec = if ($val.precision) { $val.precision } else { 0 } - $nn = if ($val.nonneg -or $val.nonnegative) { ",nonneg" } else { "" } - $typeStr = "Number($($val.length),$prec$nn)" - } - } return @{ name = $name - type = $typeStr + type = Build-TypeStr $val synonym = if ($val.synonym) { "$($val.synonym)" } else { Split-CamelCase $name } comment = if ($val.comment) { "$($val.comment)" } else { "" } flags = @(if ($val.flags) { $val.flags } else { @() }) @@ -1150,17 +1154,9 @@ function Emit-ConstantProperties { Emit-MLText $i "Synonym" $synonym X "$i" - # Type — combine valueType + length/precision from separate JSON fields - $valueType = if ($def.valueType) { "$($def.valueType)" } else { "String" } - if ($valueType -and -not $valueType.Contains('(')) { - if ($valueType -eq "String" -and $def.length) { - $valueType = "String($($def.length))" - } elseif ($valueType -eq "Number" -and $def.length) { - $p = if ($def.precision) { $def.precision } else { 0 } - $nn = if ($def.nonneg -or $def.nonnegative) { ",nonneg" } else { "" } - $valueType = "Number($($def.length),$p$nn)" - } - } + # Type + $valueType = Build-TypeStr $def + if (-not $valueType) { $valueType = "String" } Emit-ValueType $i $valueType X "$itrue" diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index f422955f..6d629b0b 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -296,6 +296,17 @@ def emit_fill_value(indent, type_str): # 5. Attribute shorthand parser # --------------------------------------------------------------------------- +def build_type_str(obj): + t = str(obj.get('valueType') or obj.get('type') or '') + if t and '(' not in t: + if t == 'String' and obj.get('length'): + t = f"String({obj['length']})" + elif t == 'Number' and obj.get('length'): + prec = obj.get('precision', 0) + nn = ',nonneg' if obj.get('nonneg') or obj.get('nonnegative') else '' + t = f"Number({obj['length']},{prec}{nn})" + return t + def parse_attribute_shorthand(val): if isinstance(val, str): parsed = { @@ -320,18 +331,9 @@ def parse_attribute_shorthand(val): return parsed # Object form name = str(val.get('name', '')) - # Build type string combining type + length/precision from separate JSON fields - type_str = str(val['type']) if val.get('type') else '' - if type_str and '(' not in type_str: - if type_str == 'String' and val.get('length'): - type_str = f"String({val['length']})" - elif type_str == 'Number' and val.get('length'): - prec = val.get('precision', 0) - nn = ',nonneg' if val.get('nonneg') or val.get('nonnegative') else '' - type_str = f"Number({val['length']},{prec}{nn})" return { 'name': name, - 'type': type_str, + 'type': build_type_str(val), 'synonym': str(val['synonym']) if val.get('synonym') else split_camel_case(name), 'comment': str(val['comment']) if val.get('comment') else '', 'flags': list(val.get('flags', [])), @@ -1023,15 +1025,8 @@ def emit_constant_properties(indent): X(f'{i}{esc_xml(obj_name)}') emit_mltext(i, 'Synonym', synonym) X(f'{i}') - # Type — combine valueType + length/precision from separate JSON fields - value_type = str(defn['valueType']) if defn.get('valueType') else 'String' - if value_type and '(' not in value_type: - if value_type == 'String' and defn.get('length'): - value_type = f"String({defn['length']})" - elif value_type == 'Number' and defn.get('length'): - prec = defn.get('precision', 0) - nn = ',nonneg' if defn.get('nonneg') or defn.get('nonnegative') else '' - value_type = f"Number({defn['length']},{prec}{nn})" + # Type + value_type = build_type_str(defn) or 'String' emit_value_type(i, value_type) X(f'{i}true') X(f'{i}') From 19667caccb188b6b3e49e1648b3cc0387bb74e76 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 12:38:21 +0300 Subject: [PATCH 09/19] =?UTF-8?q?feat(meta-validate):=20add=20batch=20mode?= =?UTF-8?q?=20=E2=80=94=20pipe-separated=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accept multiple object paths via pipe separator (|) in -ObjectPath. Each object is validated separately with individual results, followed by a summary line: "Batch: N objects, X passed, Y failed". Single-path mode unchanged. Version bumped to v1.1. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-validate/SKILL.md | 12 ++++++--- .../meta-validate/scripts/meta-validate.ps1 | 27 ++++++++++++++++++- .../meta-validate/scripts/meta-validate.py | 27 +++++++++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/.claude/skills/meta-validate/SKILL.md b/.claude/skills/meta-validate/SKILL.md index edeb31c2..0cbb215b 100644 --- a/.claude/skills/meta-validate/SKILL.md +++ b/.claude/skills/meta-validate/SKILL.md @@ -1,7 +1,7 @@ --- name: meta-validate description: Валидация объекта метаданных 1С. Используй после создания или модификации объекта конфигурации для проверки корректности -argument-hint: [-MaxErrors 30] +argument-hint: [-MaxErrors 30] — pipe-separated paths for batch allowed-tools: - Bash - Read @@ -16,18 +16,21 @@ allowed-tools: ``` /meta-validate +/meta-validate path1.xml|path2.xml|path3.xml — batch mode ``` ## Параметры | Параметр | Обязательный | По умолчанию | Описание | |------------|:------------:|--------------|-------------------------------------------------| -| ObjectPath | да | — | Путь к XML-файлу или каталогу объекта | -| MaxErrors | нет | 30 | Остановиться после N ошибок | +| ObjectPath | да | — | Путь к XML-файлу или каталогу объекта. Несколько путей через `\|` для batch | +| MaxErrors | нет | 30 | Остановиться после N ошибок (per object) | | OutFile | нет | — | Записать результат в файл (UTF-8 BOM) | `ObjectPath` авторезолв: если указана директория — ищет `/.xml`. +**Batch mode**: при нескольких путях через `|` каждый объект валидируется отдельно, в конце выводится сводка `=== Batch: N objects, X passed, Y failed ===`. + ## Команда ```powershell @@ -94,6 +97,9 @@ Errors: 1, Warnings: 1 # С записью в файл ... -ObjectPath Catalogs/Номенклатура.xml -OutFile result.txt + +# Batch: несколько объектов через | +... -ObjectPath "Catalogs/Банки.xml|Documents/Заказ.xml|Enums/ВидДоговора.xml" ``` ## Верификация diff --git a/.claude/skills/meta-validate/scripts/meta-validate.ps1 b/.claude/skills/meta-validate/scripts/meta-validate.ps1 index 05257e55..80b76b65 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.ps1 +++ b/.claude/skills/meta-validate/scripts/meta-validate.ps1 @@ -1,4 +1,4 @@ -# meta-validate v1.0 — Validate 1C metadata object structure +# meta-validate v1.1 — Validate 1C metadata object structure # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -12,6 +12,31 @@ param( $ErrorActionPreference = "Stop" [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +# --- Batch mode: pipe-separated paths (comma reserved by PowerShell) --- + +$pathList = @($ObjectPath -split '\|' | ForEach-Object { $_.Trim() } | Where-Object { $_ }) +if ($pathList.Count -gt 1) { + $batchOk = 0 + $batchFail = 0 + foreach ($singlePath in $pathList) { + $callArgs = @{ ObjectPath = $singlePath; MaxErrors = $MaxErrors } + if ($OutFile) { + $baseName = [System.IO.Path]::GetFileNameWithoutExtension($OutFile) + $ext = [System.IO.Path]::GetExtension($OutFile) + $dir = Split-Path $OutFile + if (-not $dir) { $dir = "." } + $objLeaf = [System.IO.Path]::GetFileNameWithoutExtension($singlePath) + $callArgs.OutFile = Join-Path $dir "$baseName`_$objLeaf$ext" + } + & $PSCommandPath @callArgs + if ($LASTEXITCODE -eq 0) { $batchOk++ } else { $batchFail++ } + } + Write-Host "" + Write-Host "=== Batch: $($pathList.Count) objects, $batchOk passed, $batchFail failed ===" + if ($batchFail -gt 0) { exit 1 } + exit 0 +} + # --- Resolve path --- if (-not [System.IO.Path]::IsPathRooted($ObjectPath)) { diff --git a/.claude/skills/meta-validate/scripts/meta-validate.py b/.claude/skills/meta-validate/scripts/meta-validate.py index adf42731..6f6fb2ef 100644 --- a/.claude/skills/meta-validate/scripts/meta-validate.py +++ b/.claude/skills/meta-validate/scripts/meta-validate.py @@ -1,8 +1,9 @@ -# meta-validate v1.0 — Validate 1C metadata object structure (Python port) +# meta-validate v1.1 — Validate 1C metadata object structure (Python port) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os import re +import subprocess import sys from lxml import etree @@ -18,10 +19,32 @@ parser.add_argument("-MaxErrors", type=int, default=30) parser.add_argument("-OutFile", default="") args = parser.parse_args() -object_path = args.ObjectPath max_errors = args.MaxErrors out_file = args.OutFile +# ── batch mode: pipe-separated paths ───────────────────────── + +path_list = [p.strip() for p in args.ObjectPath.split('|') if p.strip()] +if len(path_list) > 1: + batch_ok = 0 + batch_fail = 0 + for single_path in path_list: + cmd = [sys.executable, __file__, "-ObjectPath", single_path, "-MaxErrors", str(max_errors)] + if out_file: + base, ext = os.path.splitext(out_file) + obj_leaf = os.path.splitext(os.path.basename(single_path))[0] + cmd += ["-OutFile", f"{base}_{obj_leaf}{ext}"] + rc = subprocess.call(cmd) + if rc == 0: + batch_ok += 1 + else: + batch_fail += 1 + print() + print(f"=== Batch: {len(path_list)} objects, {batch_ok} passed, {batch_fail} failed ===") + sys.exit(1 if batch_fail > 0 else 0) + +object_path = path_list[0] + # ── resolve path ───────────────────────────────────────────── if not os.path.isabs(object_path): From 589091510bf13db10993b0851a4eb580199085a3 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 13:21:32 +0300 Subject: [PATCH 10/19] =?UTF-8?q?feat(meta-compile):=20add=20batch=20mode?= =?UTF-8?q?=20=E2=80=94=20JSON=20array=20in=20single=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support passing a JSON array of object definitions in a single file. Each element is compiled independently via subprocess isolation (Start-Process in PS1, subprocess.call in PY). Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 14 ++++++++++- .../meta-compile/scripts/meta-compile.ps1 | 24 +++++++++++++++++- .../meta-compile/scripts/meta-compile.py | 25 ++++++++++++++++++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index 9af0db6b..3594e7f5 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -17,7 +17,7 @@ allowed-tools: | Параметр | Описание | |----------|----------| -| `JsonPath` | Путь к JSON-определению объекта | +| `JsonPath` | Путь к JSON-определению объекта (один объект `{...}` или массив `[{...}, ...]`) | | `OutputDir` | Корневая директория выгрузки конфигурации (где `Catalogs/`, `Documents/` и т.д.) | ```powershell @@ -169,6 +169,18 @@ Constant (Константа), DefinedType (ОпределяемыйТип), Com { "type": "BusinessProcess", "name": "Задание", "attributes": ["Описание: String(200)"] } ``` +### Batch — массив объектов в одном файле + +```json +[ + { "type": "Enum", "name": "Статусы", "values": ["Новый", "Закрыт"] }, + { "type": "Catalog", "name": "Валюты" }, + { "type": "Constant", "name": "ОсновнаяВалюта", "valueType": "CatalogRef.Валюты" } +] +``` + +Каждый элемент массива компилируется отдельно. Итоговый вывод: `=== Batch: 3 objects, 3 compiled, 0 failed ===`. + ## Что генерируется - `{OutputDir}/{TypePlural}/{Name}.xml` — метаданные объекта diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index c0359019..5ab0b515 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.0 — Compile 1C metadata object from JSON +# meta-compile v1.1 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -21,6 +21,28 @@ if (-not (Test-Path $JsonPath)) { $json = Get-Content -Raw -Encoding UTF8 $JsonPath $def = $json | ConvertFrom-Json +# --- Batch mode: JSON array of objects --- +if ($def -is [array] -or ($null -ne $def -and $def.GetType().BaseType.Name -eq 'Array')) { + $batchOk = 0 + $batchFail = 0 + $idx = 0 + foreach ($item in $def) { + $idx++ + $tmpJson = Join-Path ([System.IO.Path]::GetTempPath()) "meta-compile-batch-$idx.json" + try { + $item | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 $tmpJson + $proc = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -File `"$PSCommandPath`" -JsonPath `"$tmpJson`" -OutputDir `"$OutputDir`"" -NoNewWindow -Wait -PassThru + if ($proc.ExitCode -eq 0) { $batchOk++ } else { $batchFail++ } + } finally { + Remove-Item $tmpJson -Force -ErrorAction SilentlyContinue + } + } + Write-Host "" + Write-Host "=== Batch: $idx objects, $batchOk compiled, $batchFail failed ===" + if ($batchFail -gt 0) { exit 1 } + exit 0 +} + # Normalize field synonyms: accept "objectType" as alias for "type" if (-not $def.type -and $def.objectType) { $def | Add-Member -NotePropertyName "type" -NotePropertyValue $def.objectType diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 6d629b0b..ad87f460 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 -# meta-compile v1.0 — Compile 1C metadata object from JSON +# meta-compile v1.1 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json import os import re +import subprocess import sys +import tempfile import uuid import xml.etree.ElementTree as ET @@ -81,6 +83,27 @@ with open(json_path, 'r', encoding='utf-8-sig') as f: defn = json.loads(json_text) +# --- Batch mode: JSON array of objects --- +if isinstance(defn, list): + batch_ok = 0 + batch_fail = 0 + for idx, item in enumerate(defn, 1): + tmp_fd, tmp_path = tempfile.mkstemp(suffix='.json', prefix=f'meta-compile-batch-{idx}-') + try: + with os.fdopen(tmp_fd, 'w', encoding='utf-8') as f: + json.dump(item, f, ensure_ascii=False, indent=2) + rc = subprocess.call([sys.executable, __file__, '-JsonPath', tmp_path, '-OutputDir', output_dir]) + if rc == 0: + batch_ok += 1 + else: + batch_fail += 1 + finally: + if os.path.exists(tmp_path): + os.unlink(tmp_path) + print() + print(f"=== Batch: {len(defn)} objects, {batch_ok} compiled, {batch_fail} failed ===") + sys.exit(1 if batch_fail > 0 else 0) + # Normalize field synonyms: accept "objectType" as alias for "type" if not defn.get('type') and defn.get('objectType'): defn['type'] = defn['objectType'] From 5a1974be4e3fa626c890bce2772d1afb3a976c08 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 13:47:38 +0300 Subject: [PATCH 11/19] fix(meta-compile): parse shorthand syntax for Task AddressingAttributes AddressingAttribute was taking the entire shorthand string (e.g. "Name: String(100)") as the attribute name instead of parsing it through Parse-AttributeShorthand. This caused String type without length qualifiers (NTEXT in SQL), making Index creation impossible. Co-Authored-By: Claude Opus 4.6 --- .../skills/meta-compile/scripts/meta-compile.ps1 | 12 +++++------- .claude/skills/meta-compile/scripts/meta-compile.py | 13 +++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 5ab0b515..0efa10fe 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -2392,13 +2392,11 @@ function Emit-AddressingAttribute { $addressingDimension = "" $indexing = "Index" - if ($addrDef -is [string]) { - $name = "$addrDef" - $attrSynonym = Split-CamelCase $name - } else { - $name = "$($addrDef.name)" - $attrSynonym = if ($addrDef.synonym) { "$($addrDef.synonym)" } else { Split-CamelCase $name } - if ($addrDef.type) { $typeStr = "$($addrDef.type)" } + $parsed = Parse-AttributeShorthand $addrDef + $name = $parsed.name + $attrSynonym = $parsed.synonym + $typeStr = $parsed.type + if ($addrDef -isnot [string]) { if ($addrDef.addressingDimension) { $addressingDimension = "$($addrDef.addressingDimension)" } if ($addrDef.indexing) { $indexing = "$($addrDef.indexing)" } } diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index ad87f460..f2528cc1 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -2068,14 +2068,11 @@ def emit_addressing_attribute(indent, addr_def): type_str = '' addressing_dimension = '' indexing = 'Index' - if isinstance(addr_def, str): - name = addr_def - attr_synonym = split_camel_case(name) - else: - name = str(addr_def.get('name', '')) - attr_synonym = str(addr_def['synonym']) if addr_def.get('synonym') else split_camel_case(name) - if addr_def.get('type'): - type_str = str(addr_def['type']) + parsed = parse_attribute_shorthand(addr_def) + name = parsed['name'] + attr_synonym = parsed['synonym'] + type_str = parsed['type'] + if not isinstance(addr_def, str): if addr_def.get('addressingDimension'): addressing_dimension = str(addr_def['addressingDimension']) if addr_def.get('indexing'): From cf1cd1a711048608605958212ba052e7cfc90d17 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 16:12:49 +0300 Subject: [PATCH 12/19] docs(meta-compile): add progressive disclosure reference files Replace monolithic docs/meta-dsl-spec.md link with 4 domain-specific reference files inside the skill directory. Agent reads only the relevant file when compiling a specific type, reducing context usage. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 6 +- .../meta-compile/reference/types-basic.md | 109 +++++++++++ .../meta-compile/reference/types-process.md | 136 ++++++++++++++ .../meta-compile/reference/types-registers.md | 174 ++++++++++++++++++ .../meta-compile/reference/types-web.md | 103 +++++++++++ 5 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/meta-compile/reference/types-basic.md create mode 100644 .claude/skills/meta-compile/reference/types-process.md create mode 100644 .claude/skills/meta-compile/reference/types-registers.md create mode 100644 .claude/skills/meta-compile/reference/types-web.md diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index 3594e7f5..11d0e420 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -42,7 +42,11 @@ Constant (Константа), DefinedType (ОпределяемыйТип), Com ## JSON DSL — краткий справочник -Полная спецификация: `docs/meta-dsl-spec.md`. +Детали по типам — в справочных файлах (читай нужный при компиляции конкретного типа): +- `reference/types-basic.md` — Catalog, Document, Enum, Constant, DefinedType, Report, DataProcessor +- `reference/types-registers.md` — InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes +- `reference/types-process.md` — BusinessProcess, Task, ExchangePlan, CommonModule, ScheduledJob, EventSubscription, DocumentJournal +- `reference/types-web.md` — HTTPService, WebService ### Корневая структура diff --git a/.claude/skills/meta-compile/reference/types-basic.md b/.claude/skills/meta-compile/reference/types-basic.md new file mode 100644 index 00000000..2763635a --- /dev/null +++ b/.claude/skills/meta-compile/reference/types-basic.md @@ -0,0 +1,109 @@ +# Базовые типы: Catalog, Document, Enum, Constant, DefinedType, Report, DataProcessor + +## Catalog + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `hierarchical` | `false` | Hierarchical | +| `hierarchyType` | `HierarchyFoldersAndItems` | HierarchyType | +| `codeLength` | `9` | CodeLength | +| `codeType` | `String` | CodeType | +| `codeAllowedLength` | `Variable` | CodeAllowedLength | +| `descriptionLength` | `25` | DescriptionLength | +| `autonumbering` | `true` | Autonumbering | +| `checkUnique` | `false` | CheckUnique | +| `defaultPresentation` | `AsDescription` | DefaultPresentation | +| `owners` | `[]` | Owners | +| `attributes` | `[]` | → Attribute в ChildObjects | +| `tabularSections` | `{}` | → TabularSection в ChildObjects | + +```json +{ "type": "Catalog", "name": "Организации", "attributes": ["ИНН: String(12)", "КПП: String(9)"] } +``` + +## Document + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `numberType` | `String` | NumberType | +| `numberLength` | `11` | NumberLength | +| `numberAllowedLength` | `Variable` | NumberAllowedLength | +| `numberPeriodicity` | `Year` | NumberPeriodicity | +| `checkUnique` | `true` | CheckUnique | +| `autonumbering` | `true` | Autonumbering | +| `posting` | `Allow` | Posting | +| `realTimePosting` | `Deny` | RealTimePosting | +| `registerRecordsDeletion` | `AutoDelete` | RegisterRecordsDeletion | +| `registerRecordsWritingOnPost` | `WriteModified` | RegisterRecordsWritingOnPost | +| `postInPrivilegedMode` | `true` | PostInPrivilegedMode | +| `unpostInPrivilegedMode` | `true` | UnpostInPrivilegedMode | +| `registerRecords` | `[]` | RegisterRecords | +| `attributes` | `[]` | → Attribute в ChildObjects | +| `tabularSections` | `{}` | → TabularSection в ChildObjects | + +RegisterRecords — массив строк: `"AccumulationRegister.Продажи"`, `"InformationRegister.Цены"`. + +```json +{ + "type": "Document", "name": "ПриходнаяНакладная", + "registerRecords": ["AccumulationRegister.ОстаткиТоваров"], + "attributes": ["Организация: CatalogRef.Организации", "Контрагент: CatalogRef.Контрагенты"], + "tabularSections": { "Товары": ["Номенклатура: CatalogRef.Номенклатура", "Количество: Number(15,3)"] } +} +``` + +## Enum + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `values` | `[]` | → EnumValue в ChildObjects | + +```json +{ "type": "Enum", "name": "Статусы", "values": ["Новый", "ВРаботе", "Закрыт"] } +``` + +## Constant + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `valueType` | `String` | Type | + +`valueType` принимает shorthand типа: `"String(100)"`, `"Number(15,2)"`, `"Boolean"`, `"CatalogRef.Валюты"`. + +```json +{ "type": "Constant", "name": "ОсновнаяВалюта", "valueType": "CatalogRef.Валюты" } +``` + +## DefinedType + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `valueTypes` | `[]` | Type (составной тип) | +| `valueType` | — | Алиас для `valueTypes` (строка или массив) | + +```json +{ "type": "DefinedType", "name": "ДенежныеСредства", "valueTypes": ["CatalogRef.БанковскиеСчета", "CatalogRef.Кассы"] } +{ "type": "DefinedType", "name": "ФлагАктивности", "valueType": "Boolean" } +``` + +## Report + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `attributes` | `[]` | → Attribute в ChildObjects | +| `tabularSections` | `{}` | → TabularSection в ChildObjects | + +```json +{ "type": "Report", "name": "ОстаткиТоваров" } +``` + +## DataProcessor + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `attributes` | `[]` | → Attribute в ChildObjects | +| `tabularSections` | `{}` | → TabularSection в ChildObjects | + +```json +{ "type": "DataProcessor", "name": "ЗагрузкаДанных", "attributes": ["ПутьКФайлу: String(500)"] } +``` diff --git a/.claude/skills/meta-compile/reference/types-process.md b/.claude/skills/meta-compile/reference/types-process.md new file mode 100644 index 00000000..4ef4e9ae --- /dev/null +++ b/.claude/skills/meta-compile/reference/types-process.md @@ -0,0 +1,136 @@ +# Процессы и сервисные: BusinessProcess, Task, ExchangePlan, CommonModule, ScheduledJob, EventSubscription, DocumentJournal + +## BusinessProcess + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `task` | `""` | Task (ссылка `Task.XXX`) | +| `numberType` | `String` | NumberType | +| `numberLength` | `11` | NumberLength | +| `checkUnique` | `true` | CheckUnique | +| `autonumbering` | `true` | Autonumbering | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | + +Модули: `Ext/ObjectModule.bsl`, `Ext/Flowchart.xml`. + +```json +{ "type": "BusinessProcess", "name": "Задание", "task": "Task.ЗадачаИсполнителя", "attributes": ["Описание: String(200)"] } +``` + +## Task + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `numberType` | `String` | NumberType | +| `numberLength` | `14` | NumberLength | +| `checkUnique` | `true` | CheckUnique | +| `autonumbering` | `true` | Autonumbering | +| `descriptionLength` | `150` | DescriptionLength | +| `addressing` | `""` | Addressing (ссылка на РС адресации) | +| `mainAddressingAttribute` | `""` | MainAddressingAttribute | +| `currentPerformer` | `""` | CurrentPerformer | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | +| `addressingAttributes` | `[]` | → AddressingAttribute (shorthand или объект) | + +AddressingAttribute — shorthand `"Имя: Тип"` или объект `{ "name", "type", "addressingDimension" }`. + +```json +{ + "type": "Task", "name": "ЗадачаИсполнителя", + "addressingAttributes": ["Исполнитель: CatalogRef.Пользователи"] +} +``` + +## ExchangePlan + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `codeLength` | `9` | CodeLength | +| `descriptionLength` | `100` | DescriptionLength | +| `distributedInfoBase` | `false` | DistributedInfoBase | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | + +Модули: `Ext/ObjectModule.bsl`, `Ext/Content.xml`. + +```json +{ "type": "ExchangePlan", "name": "ОбменССайтом", "attributes": ["АдресСервера: String(200)"] } +``` + +## CommonModule + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `context` | — | Шорткат (см. ниже) | +| `global` | `false` | Global | +| `server` | `false` | Server | +| `serverCall` | `false` | ServerCall | +| `clientManagedApplication` | `false` | ClientManagedApplication | +| `externalConnection` | `false` | ExternalConnection | +| `privileged` | `false` | Privileged | +| `returnValuesReuse` | `DontUse` | ReturnValuesReuse | + +Шорткаты `context`: `"server"` → Server+ServerCall, `"client"` → ClientManagedApplication, `"serverClient"` → Server+ClientManagedApplication. + +```json +{ "type": "CommonModule", "name": "ОбщиеФункции", "context": "serverClient" } +``` + +## ScheduledJob + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `methodName` | `""` | MethodName | +| `description` | = synonym | Description | +| `use` | `false` | Use | +| `predefined` | `false` | Predefined | +| `restartCountOnFailure` | `3` | RestartCountOnFailure | +| `restartIntervalOnFailure` | `10` | RestartIntervalOnFailure | + +Формат `methodName`: `"МодульСервер.Процедура"` — авто-дополняется до `CommonModule.МодульСервер.Процедура`. + +```json +{ "type": "ScheduledJob", "name": "ОбменДанными", "methodName": "ОбменДаннымиСервер.Выполнить" } +``` + +## EventSubscription + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `source` | `[]` | Source (массив, формат `XxxObject.Name`) | +| `event` | `BeforeWrite` | Event | +| `handler` | `""` | Handler | + +Формат `handler`: `"МодульСервер.Процедура"` — авто-дополняется до `CommonModule.МодульСервер.Процедура`. + +Значения `event`: `BeforeWrite`, `OnWrite`, `BeforeDelete`, `OnReadAtServer`, `FillCheckProcessing`. + +Формат `source`: `"CatalogObject.Xxx"`, `"DocumentObject.Xxx"`. + +```json +{ "type": "EventSubscription", "name": "ПередЗаписью", "source": ["CatalogObject.Контрагенты"], "event": "BeforeWrite", "handler": "ОбщиеФункции.ПередЗаписью" } +``` + +## DocumentJournal + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `registeredDocuments` | `[]` | RegisteredDocuments (массив `"Document.Xxx"`) | +| `columns` | `[]` | → Column | + +Колонки — строка `"Имя"` или объект `{ "name", "synonym", "indexing": "Index"/"DontIndex", "references": ["Document.Xxx.Attribute.Yyy"] }`. + +```json +{ + "type": "DocumentJournal", "name": "Взаимодействия", + "registeredDocuments": ["Document.Встреча", "Document.Звонок"], + "columns": [{ "name": "Организация", "indexing": "Index", "references": ["Document.Встреча.Attribute.Организация"] }] +} +``` + +## Зависимости + +- **ScheduledJob/EventSubscription** — процедура-обработчик должна существовать в модуле (экспортная) +- **BusinessProcess** → `Task` (задача должна существовать) diff --git a/.claude/skills/meta-compile/reference/types-registers.md b/.claude/skills/meta-compile/reference/types-registers.md new file mode 100644 index 00000000..4c87c46e --- /dev/null +++ b/.claude/skills/meta-compile/reference/types-registers.md @@ -0,0 +1,174 @@ +# Регистры и планы: InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes + +## Измерения и ресурсы (общее) + +Синтаксис аналогичен реквизитам (shorthand `"Имя: Тип | флаги"`). + +Флаги измерений: `master`, `mainFilter`, `denyIncomplete`, `useInTotals` (AccumulationRegister only, default `true`). + +```json +"dimensions": ["Организация: CatalogRef.Организации | master, mainFilter, denyIncomplete"], +"resources": ["Сумма: Number(15,2)"] +``` + +--- + +## InformationRegister + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `writeMode` | `Independent` | WriteMode | +| `periodicity` | `Nonperiodical` | InformationRegisterPeriodicity | +| `mainFilterOnPeriod` | авто* | MainFilterOnPeriod | +| `dimensions` | `[]` | → Dimension | +| `resources` | `[]` | → Resource | +| `attributes` | `[]` | → Attribute | + +\* `mainFilterOnPeriod` = `true` если `periodicity` != `Nonperiodical`. + +```json +{ + "type": "InformationRegister", "name": "КурсыВалют", "periodicity": "Day", + "dimensions": ["Валюта: CatalogRef.Валюты | master, mainFilter, denyIncomplete"], + "resources": ["Курс: Number(15,4)", "Кратность: Number(10,0)"] +} +``` + +## AccumulationRegister + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `registerType` | `Balance` | RegisterType (`Balance` / `Turnovers`) | +| `enableTotalsSplitting` | `true` | EnableTotalsSplitting | +| `dimensions` | `[]` | → Dimension | +| `resources` | `[]` | → Resource | +| `attributes` | `[]` | → Attribute | + +```json +{ + "type": "AccumulationRegister", "name": "ОстаткиТоваров", "registerType": "Balance", + "dimensions": ["Номенклатура: CatalogRef.Номенклатура"], + "resources": ["Количество: Number(15,3)"] +} +``` + +## AccountingRegister + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `chartOfAccounts` | `""` | ChartOfAccounts (**обязательная** ссылка на план счетов) | +| `correspondence` | `false` | Correspondence | +| `periodAdjustmentLength` | `0` | PeriodAdjustmentLength | +| `dimensions` | `[]` | → Dimension | +| `resources` | `[]` | → Resource | +| `attributes` | `[]` | → Attribute | + +```json +{ + "type": "AccountingRegister", "name": "Хозрасчетный", + "chartOfAccounts": "ChartOfAccounts.Хозрасчетный", + "dimensions": ["Организация: CatalogRef.Организации"], + "resources": ["Сумма: Number(15,2)"] +} +``` + +## CalculationRegister + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `chartOfCalculationTypes` | `""` | ChartOfCalculationTypes (**обязательная** ссылка на ПВР) | +| `periodicity` | `Month` | Periodicity | +| `actionPeriod` | `false` | ActionPeriod | +| `basePeriod` | `false` | BasePeriod | +| `schedule` | `""` | Schedule (ссылка на РС графиков) | +| `dimensions` | `[]` | → Dimension | +| `resources` | `[]` | → Resource | +| `attributes` | `[]` | → Attribute | + +```json +{ + "type": "CalculationRegister", "name": "Начисления", + "chartOfCalculationTypes": "ChartOfCalculationTypes.Начисления", + "periodicity": "Month", + "dimensions": ["Сотрудник: CatalogRef.Сотрудники"], + "resources": ["Сумма: Number(15,2)"] +} +``` + +--- + +## ChartOfCharacteristicTypes + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `codeLength` | `9` | CodeLength | +| `descriptionLength` | `25` | DescriptionLength | +| `autonumbering` | `true` | Autonumbering | +| `checkUnique` | `false` | CheckUnique | +| `characteristicExtValues` | `""` | CharacteristicExtValues | +| `valueTypes` | авто* | Type (составной тип значений характеристик) | +| `hierarchical` | `false` | Hierarchical | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | + +\* По умолчанию: Boolean, String(100), Number(15,2), DateTime. + +```json +{ + "type": "ChartOfCharacteristicTypes", "name": "ВидыСубконто", + "valueTypes": ["CatalogRef.Номенклатура", "CatalogRef.Контрагенты", "Boolean", "String", "Number(15,2)"] +} +``` + +## ChartOfAccounts + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `extDimensionTypes` | `""` | ExtDimensionTypes (ссылка на ПВХ) | +| `maxExtDimensionCount` | `3` | MaxExtDimensionCount | +| `codeMask` | `""` | CodeMask | +| `codeLength` | `8` | CodeLength | +| `descriptionLength` | `120` | DescriptionLength | +| `codeSeries` | `WholeChartOfAccounts` | CodeSeries | +| `autoOrderByCode` | `true` | AutoOrderByCode | +| `orderLength` | `5` | OrderLength | +| `hierarchical` | `false` | Hierarchical | +| `accountingFlags` | `[]` | → AccountingFlag (Boolean-тип, массив имён) | +| `extDimensionAccountingFlags` | `[]` | → ExtDimensionAccountingFlag (Boolean-тип, массив имён) | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | + +```json +{ + "type": "ChartOfAccounts", "name": "Хозрасчетный", + "extDimensionTypes": "ChartOfCharacteristicTypes.ВидыСубконто", "maxExtDimensionCount": 3, + "codeLength": 8, "codeMask": "@@@.@@.@", + "accountingFlags": ["Валютный", "Количественный"], + "extDimensionAccountingFlags": ["Суммовой", "Валютный"] +} +``` + +## ChartOfCalculationTypes + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `codeLength` | `9` | CodeLength | +| `descriptionLength` | `25` | DescriptionLength | +| `autonumbering` | `true` | Autonumbering | +| `checkUnique` | `false` | CheckUnique | +| `dependenceOnCalculationTypes` | `NotUsed` | DependenceOnCalculationTypes | +| `actionPeriodUse` | `false` | ActionPeriodUse | +| `attributes` | `[]` | → Attribute | +| `tabularSections` | `{}` | → TabularSection | + +`dependenceOnCalculationTypes`: `NotUsed`, `ExclusionAndDependence`, `ExclusionOnly`. + +```json +{ "type": "ChartOfCalculationTypes", "name": "Начисления", "dependenceOnCalculationTypes": "ExclusionAndDependence" } +``` + +## Зависимости + +- **AccountingRegister** требует `ChartOfAccounts` (и документ-регистратор) +- **CalculationRegister** требует `ChartOfCalculationTypes` (и документ-регистратор) +- **ChartOfAccounts** ссылается на `ChartOfCharacteristicTypes` через `extDimensionTypes` diff --git a/.claude/skills/meta-compile/reference/types-web.md b/.claude/skills/meta-compile/reference/types-web.md new file mode 100644 index 00000000..81c65890 --- /dev/null +++ b/.claude/skills/meta-compile/reference/types-web.md @@ -0,0 +1,103 @@ +# Веб-сервисы: HTTPService, WebService + +## HTTPService + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `rootURL` | `= name.toLower()` | RootURL | +| `reuseSessions` | `DontUse` | ReuseSessions | +| `sessionMaxAge` | `20` | SessionMaxAge | +| `urlTemplates` | `{}` | → URLTemplate | + +Модули: `Ext/Module.bsl`. + +### urlTemplates — вложенная структура + +`urlTemplates` — объект `{ "TemplateName": templateDef, ... }`. + +Каждый `templateDef`: +- Строка — URL-шаблон: `"/v1/users"` (без методов) +- Объект: + +| Поле | Умолчание | Описание | +|------|----------|----------| +| `template` | `"/templatename"` | URL-путь (с параметрами `{id}`) | +| `methods` | `{}` | Методы: `{ "MethodName": "HTTPMethod" }` | + +Допустимые HTTPMethod: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`, `CONNECT`, `TRACE`, `MERGE`. + +Обработчик метода генерируется автоматически: `{TemplateName}{MethodName}` — должен быть реализован в `Ext/Module.bsl`. + +```json +{ + "type": "HTTPService", "name": "API", "rootURL": "api", + "urlTemplates": { + "Users": { + "template": "/v1/users/{id}", + "methods": { "Get": "GET", "Create": "POST", "Update": "PUT", "Delete": "DELETE" } + }, + "Health": "/health" + } +} +``` + +## WebService + +| Поле JSON | Умолчание | XML элемент | +|-----------|----------|-------------| +| `namespace` | `""` | Namespace (URI пространства имён WSDL) | +| `xdtoPackages` | `""` | XDTOPackages | +| `reuseSessions` | `DontUse` | ReuseSessions | +| `sessionMaxAge` | `20` | SessionMaxAge | +| `operations` | `{}` | → Operation | + +Модули: `Ext/Module.bsl`. + +### operations — вложенная структура + +`operations` — объект `{ "OperationName": operationDef, ... }`. + +Каждый `operationDef`: +- Строка — тип возврата: `"xs:boolean"` (параметров нет, обработчик = имя операции) +- Объект: + +| Поле | Умолчание | Описание | +|------|----------|----------| +| `returnType` | `xs:string` | XDTO-тип возврата | +| `nillable` | `false` | Может ли вернуть null | +| `transactioned` | `false` | Выполнять в транзакции | +| `handler` | `= operationName` | Имя процедуры в модуле | +| `parameters` | `{}` | Параметры операции | + +### parameters — параметры операции + +`parameters` — объект `{ "ParamName": paramDef, ... }`. + +Каждый `paramDef`: +- Строка — XDTO-тип: `"xs:string"` (direction = In, nillable = true) +- Объект: + +| Поле | Умолчание | Описание | +|------|----------|----------| +| `type` | `xs:string` | XDTO-тип параметра | +| `nillable` | `true` | Может ли быть null | +| `direction` | `In` | Направление: `In`, `Out`, `InOut` | + +Стандартные XDTO-типы: `xs:string`, `xs:boolean`, `xs:int`, `xs:long`, `xs:decimal`, `xs:dateTime`, `xs:base64Binary`. + +```json +{ + "type": "WebService", "name": "DataExchange", + "namespace": "http://www.1c.ru/DataExchange", + "operations": { + "TestConnection": { + "returnType": "xs:boolean", + "handler": "ПроверкаПодключения", + "parameters": { + "ErrorMessage": { "type": "xs:string", "direction": "Out" } + } + }, + "GetVersion": "xs:string" + } +} +``` From c0255c71d05c9c60c7b399d3de88df9593de1f33 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 16:45:01 +0300 Subject: [PATCH 13/19] docs(meta-compile): slim down SKILL.md with pattern-based examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 12 type-specific examples with 5 DSL pattern examples (minimal, attributes, tabularSections, register, batch). Strengthen reference file instruction to "прочитай перед компиляцией". 203 → 121 lines. Tested on Opus and Sonnet — both pass e2e. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 179 ++++++++------------------- 1 file changed, 49 insertions(+), 130 deletions(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index 11d0e420..e6d60e5d 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -13,105 +13,82 @@ allowed-tools: Принимает JSON-определение объекта метаданных → генерирует XML + модули в структуре выгрузки конфигурации + регистрирует в Configuration.xml. -## Параметры и команда - -| Параметр | Описание | -|----------|----------| -| `JsonPath` | Путь к JSON-определению объекта (один объект `{...}` или массив `[{...}, ...]`) | -| `OutputDir` | Корневая директория выгрузки конфигурации (где `Catalogs/`, `Documents/` и т.д.) | +## Команда ```powershell powershell.exe -NoProfile -File .claude/skills/meta-compile/scripts/meta-compile.ps1 -JsonPath "" -OutputDir "" ``` -`OutputDir` — директория, содержащая подпапки типов (`Catalogs/`, `Documents/`, ...) и `Configuration.xml`. +| Параметр | Описание | +|----------|----------| +| `JsonPath` | Путь к JSON-файлу (один объект `{...}` или массив `[{...}, ...]`) | +| `OutputDir` | Корень выгрузки конфигурации (где `Configuration.xml`, `Catalogs/`, `Documents/` и т.д.) | -## Поддерживаемые типы (23) +## JSON DSL -### Ссылочные -Catalog (Справочник), Document (Документ), Enum (Перечисление), ExchangePlan (ПланОбмена), ChartOfAccounts (ПланСчетов), ChartOfCharacteristicTypes (ПВХ), ChartOfCalculationTypes (ПВР), BusinessProcess (БизнесПроцесс), Task (Задача) +### Общая структура -### Регистры -InformationRegister (РегистрСведений), AccumulationRegister (РегистрНакопления), AccountingRegister (РегистрБухгалтерии), CalculationRegister (РегистрРасчёта) +```json +{ "type": "Catalog", "name": "Номенклатура", "synonym": "авто", ...свойства типа... } +``` -### Отчёты/Обработки -Report (Отчёт), DataProcessor (Обработка) +`type` и `name` — обязательные. `synonym` генерируется из `name` автоматически. -### Сервисные -Constant (Константа), DefinedType (ОпределяемыйТип), CommonModule (ОбщийМодуль), ScheduledJob (РегламентноеЗадание), EventSubscription (ПодпискаНаСобытие), DocumentJournal (ЖурналДокументов), HTTPService (HTTPСервис), WebService (ВебСервис) +### Shorthand реквизитов -## JSON DSL — краткий справочник +Используется в `attributes`, `dimensions`, `resources`, `tabularSections`: + +``` +"ИмяРеквизита" → String без квалификаторов +"ИмяРеквизита: Тип" → с типом +"ИмяРеквизита: Тип | req, index" → с флагами +``` + +Типы: `String(100)`, `Number(15,2)`, `Boolean`, `Date`, `DateTime`, `CatalogRef.Xxx`, `DocumentRef.Xxx`, `EnumRef.Xxx`, `DefinedType.Xxx` и др. ссылочные. + +Составной тип: `"Значение: String + Number(15,2) + CatalogRef.Контрагенты"`. + +Флаги: `req`, `index`, `indexAdditional`, `nonneg`, `master`, `mainFilter`, `denyIncomplete`, `useInTotals`. + +### Свойства по типам — справочники + +**Перед компиляцией прочитай справочник нужного типа** — там таблицы всех свойств, умолчания и допустимые значения enum-полей: -Детали по типам — в справочных файлах (читай нужный при компиляции конкретного типа): - `reference/types-basic.md` — Catalog, Document, Enum, Constant, DefinedType, Report, DataProcessor - `reference/types-registers.md` — InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes - `reference/types-process.md` — BusinessProcess, Task, ExchangePlan, CommonModule, ScheduledJob, EventSubscription, DocumentJournal - `reference/types-web.md` — HTTPService, WebService -### Корневая структура +## Примеры паттернов DSL -```json -{ - "type": "Catalog", - "name": "Номенклатура", - "synonym": "авто из name", - ...type-specific..., - "attributes": [...], - "tabularSections": {...} -} -``` - -### Реквизиты — shorthand - -``` -"ИмяРеквизита" — String без квалификаторов -"ИмяРеквизита: Тип" — с типом -"ИмяРеквизита: Тип | req, index" — с флагами -``` - -Типы: `String(100)`, `Number(15,2)`, `Boolean`, `Date`, `DateTime`, `CatalogRef.Xxx`, `DocumentRef.Xxx`, `EnumRef.Xxx`, `ChartOfAccountsRef.Xxx`, `ChartOfCharacteristicTypesRef.Xxx`, `ChartOfCalculationTypesRef.Xxx`, `ExchangePlanRef.Xxx`, `BusinessProcessRef.Xxx`, `TaskRef.Xxx`, `DefinedType.Xxx`. - -Русские синонимы типов: `Строка`, `Число`, `Булево`, `Дата`, `СправочникСсылка.Xxx`, `ДокументСсылка.Xxx`, `ПланСчетовСсылка.Xxx`. - -Составной тип (несколько допустимых типов через `+`): `"Значение: Строка + Число(15,2) + Дата + CatalogRef.Контрагенты"`. - -Флаги: `req`, `index`, `indexAdditional`, `nonneg`, `master`, `mainFilter`, `denyIncomplete`, `useInTotals`. - -## Примеры - -### Справочник +### Минимальный объект ```json { "type": "Catalog", "name": "Валюты" } ``` -### Перечисление +### С реквизитами ```json -{ "type": "Enum", "name": "Статусы", "values": ["Новый", "Закрыт"] } +{ + "type": "Catalog", "name": "Организации", + "descriptionLength": 100, + "attributes": ["ИНН: String(12)", "КПП: String(9)", "Директор: CatalogRef.ФизическиеЛица"] +} ``` -### Константа +### С табличной частью ```json -{ "type": "Constant", "name": "ОсновнаяВалюта", "valueType": "CatalogRef.Валюты" } +{ + "type": "Document", "name": "ПриходнаяНакладная", + "registerRecords": ["AccumulationRegister.ОстаткиТоваров"], + "attributes": ["Организация: CatalogRef.Организации", "Контрагент: CatalogRef.Контрагенты"], + "tabularSections": { "Товары": ["Номенклатура: CatalogRef.Номенклатура", "Количество: Number(15,3)", "Цена: Number(15,2)"] } +} ``` -### Определяемый тип - -```json -{ "type": "DefinedType", "name": "ДенежныеСредства", "valueTypes": ["CatalogRef.БанковскиеСчета", "CatalogRef.Кассы"] } -``` - -### Общий модуль - -```json -{ "type": "CommonModule", "name": "ОбменДаннымиСервер", "context": "server", "returnValuesReuse": "DuringRequest" } -``` - -Шорткаты context: `"server"` → Server+ServerCall, `"client"` → ClientManagedApplication, `"serverClient"` → Server+ClientManagedApplication. - -### Регистр сведений +### Регистровый паттерн (измерения + ресурсы) ```json { @@ -121,59 +98,7 @@ Constant (Константа), DefinedType (ОпределяемыйТип), Com } ``` -### План обмена - -```json -{ "type": "ExchangePlan", "name": "ОбменССайтом", "attributes": ["АдресСервера: String(200)"] } -``` - -### Журнал документов - -```json -{ - "type": "DocumentJournal", "name": "Взаимодействия", - "registeredDocuments": ["Document.Встреча", "Document.ТелефонныйЗвонок"], - "columns": [{ "name": "Организация", "indexing": "Index", "references": ["Document.Встреча.Attribute.Организация"] }] -} -``` - -### HTTP-сервис - -```json -{ - "type": "HTTPService", "name": "API", "rootURL": "api", - "urlTemplates": { "Users": { "template": "/v1/users", "methods": { "Get": "GET", "Create": "POST" } } } -} -``` - -### Веб-сервис - -```json -{ - "type": "WebService", "name": "DataExchange", "namespace": "http://www.1c.ru/DataExchange", - "operations": { "TestConnection": { "returnType": "xs:boolean", "handler": "ПроверкаПодключения", "parameters": { "ErrorMessage": { "type": "xs:string", "direction": "Out" } } } } -} -``` - -### План счетов - -```json -{ - "type": "ChartOfAccounts", "name": "Хозрасчетный", - "extDimensionTypes": "ChartOfCharacteristicTypes.ВидыСубконто", "maxExtDimensionCount": 3, - "codeMask": "@@@.@@.@", "codeLength": 8, - "accountingFlags": ["Валютный", "Количественный"], - "extDimensionAccountingFlags": ["Суммовой", "Валютный"] -} -``` - -### Бизнес-процесс - -```json -{ "type": "BusinessProcess", "name": "Задание", "attributes": ["Описание: String(200)"] } -``` - -### Batch — массив объектов в одном файле +### Batch — несколько объектов в одном файле ```json [ @@ -183,20 +108,14 @@ Constant (Константа), DefinedType (ОпределяемыйТип), Com ] ``` -Каждый элемент массива компилируется отдельно. Итоговый вывод: `=== Batch: 3 objects, 3 compiled, 0 failed ===`. - ## Что генерируется -- `{OutputDir}/{TypePlural}/{Name}.xml` — метаданные объекта -- `{OutputDir}/{TypePlural}/{Name}/Ext/ObjectModule.bsl` — модуль объекта (Catalog, Document, Report, DataProcessor, ExchangePlan, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes, BusinessProcess, Task) -- `{OutputDir}/{TypePlural}/{Name}/Ext/RecordSetModule.bsl` — модуль набора записей (4 типа регистров) -- `{OutputDir}/{TypePlural}/{Name}/Ext/Module.bsl` — модуль (CommonModule, HTTPService, WebService) -- `{OutputDir}/{TypePlural}/{Name}/Ext/Content.xml` — состав плана обмена (ExchangePlan) -- `{OutputDir}/{TypePlural}/{Name}/Ext/Flowchart.xml` — карта маршрута (BusinessProcess) +- `{TypePlural}/{Name}.xml` — метаданные объекта +- `{TypePlural}/{Name}/Ext/*.bsl` — модули (ObjectModule, RecordSetModule, Module — зависит от типа) - `Configuration.xml` — автоматическая регистрация в `` ## Верификация ``` -/meta-info //.xml — проверка структуры +/meta-validate //.xml ``` From 730daf108993cde5122be6c0c156b19c038e348a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 17:18:29 +0300 Subject: [PATCH 14/19] fix(meta-compile): default bare String to String(10), fix SKILL.md examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bare `String` type (without length qualifier) now defaults to String(10) instead of String(0) — matching 1C Designer behavior. String(0) means unlimited length (NTEXT in SQL), which is rarely intended. Also fixes SKILL.md: removes misleading `"synonym": "авто"` from JSON example, clarifies synonym auto-generation from CamelCase name. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 6 +++--- .claude/skills/meta-compile/scripts/meta-compile.ps1 | 2 +- .claude/skills/meta-compile/scripts/meta-compile.py | 2 +- docs/meta-dsl-spec.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index e6d60e5d..e0a6bcc5 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -29,17 +29,17 @@ powershell.exe -NoProfile -File .claude/skills/meta-compile/scripts/meta-compile ### Общая структура ```json -{ "type": "Catalog", "name": "Номенклатура", "synonym": "авто", ...свойства типа... } +{ "type": "Catalog", "name": "Номенклатура", ...свойства типа... } ``` -`type` и `name` — обязательные. `synonym` генерируется из `name` автоматически. +`type` и `name` — обязательные. `synonym` генерируется из `name` автоматически (CamelCase → слова через пробел). Можно задать явно: `"synonym": "Мой синоним"`. ### Shorthand реквизитов Используется в `attributes`, `dimensions`, `resources`, `tabularSections`: ``` -"ИмяРеквизита" → String без квалификаторов +"ИмяРеквизита" → String(10) по умолчанию "ИмяРеквизита: Тип" → с типом "ИмяРеквизита: Тип | req, index" → с флагами ``` diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 0efa10fe..ed8cc6f4 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -239,7 +239,7 @@ function Emit-TypeContent { # String or String(N) if ($typeStr -match '^String(\((\d+)\))?$') { - $len = if ($Matches[2]) { $Matches[2] } else { "0" } + $len = if ($Matches[2]) { $Matches[2] } else { "10" } X "$indentxs:string" X "$indent" X "$indent`t$len" diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index f2528cc1..f1e9db8b 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -244,7 +244,7 @@ def emit_type_content(indent, type_str): # String or String(N) m = re.match(r'^String(\((\d+)\))?$', type_str) if m: - length = m.group(2) if m.group(2) else '0' + length = m.group(2) if m.group(2) else '10' X(f'{indent}xs:string') X(f'{indent}') X(f'{indent}\t{length}') diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index 3f986e8d..dee4aba0 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -112,7 +112,7 @@ JSON DSL для описания объектов метаданных конф ### 4.1 Строковая форма ``` -"ИмяРеквизита" → String (без квалификаторов) +"ИмяРеквизита" → String(10) по умолчанию "ИмяРеквизита: Тип" → с типом "ИмяРеквизита: Тип | req, index" → с флагами ``` From 4cdd3377ae3669655f6328c51dcd97806366f67f Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 17:43:04 +0300 Subject: [PATCH 15/19] docs(meta-compile): soften reference guidance, prevent XML hunting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace imperative "read reference before compiling" with conditional "if you need properties not shown in examples" - Add explicit instruction not to search for XML in config dumps - Rename "справочник" → "reference-файл" to avoid confusion with Catalog metadata type Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index e0a6bcc5..fa3c8479 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -50,15 +50,17 @@ powershell.exe -NoProfile -File .claude/skills/meta-compile/scripts/meta-compile Флаги: `req`, `index`, `indexAdditional`, `nonneg`, `master`, `mainFilter`, `denyIncomplete`, `useInTotals`. -### Свойства по типам — справочники +### Свойства по типам -**Перед компиляцией прочитай справочник нужного типа** — там таблицы всех свойств, умолчания и допустимые значения enum-полей: +Примеров и shorthand-синтаксиса выше достаточно для типовых задач. Если нужны свойства типа, не показанные в примерах, и их допустимые значения — см. reference-файл: - `reference/types-basic.md` — Catalog, Document, Enum, Constant, DefinedType, Report, DataProcessor - `reference/types-registers.md` — InformationRegister, AccumulationRegister, AccountingRegister, CalculationRegister, ChartOfAccounts, ChartOfCharacteristicTypes, ChartOfCalculationTypes - `reference/types-process.md` — BusinessProcess, Task, ExchangePlan, CommonModule, ScheduledJob, EventSubscription, DocumentJournal - `reference/types-web.md` — HTTPService, WebService +Эта инструкция и reference-файлы — полная документация для генерации. Не ищи примеры XML в выгрузках конфигураций. + ## Примеры паттернов DSL ### Минимальный объект From 84f3662c0251dabf6f6fa6fff43f38e13712d95b Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 17:53:57 +0300 Subject: [PATCH 16/19] docs(meta-compile): add workflow steps, remove redundant verification section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit 4-step workflow (write JSON → run script → /meta-edit → /meta-validate) to guide the model through the optimal path instead of free exploration. Remove standalone "Верификация" section as it duplicates workflow step 4. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index fa3c8479..b224debc 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -13,6 +13,13 @@ allowed-tools: Принимает JSON-определение объекта метаданных → генерирует XML + модули в структуре выгрузки конфигурации + регистрирует в Configuration.xml. +## Порядок работы + +1. Составь JSON по синтаксису и примерам ниже → запиши во временный файл +2. Запусти скрипт meta-compile +3. Если нужно изменить созданный объект — `/meta-edit` +4. Если нужно проверить — `/meta-validate` + ## Команда ```powershell @@ -116,8 +123,3 @@ powershell.exe -NoProfile -File .claude/skills/meta-compile/scripts/meta-compile - `{TypePlural}/{Name}/Ext/*.bsl` — модули (ObjectModule, RecordSetModule, Module — зависит от типа) - `Configuration.xml` — автоматическая регистрация в `` -## Верификация - -``` -/meta-validate //.xml -``` From 843916642cc3a7fa1f312a84eb8c31d54f02d875 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 18:09:53 +0300 Subject: [PATCH 17/19] =?UTF-8?q?docs(meta-compile):=20restructure=20trigg?= =?UTF-8?q?er=20=E2=80=94=20types=20in=20trigger,=20shorter=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move type enumeration from description to trigger clause for better skill discovery matching. Remove implementation details from description. Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/meta-compile/SKILL.md b/.claude/skills/meta-compile/SKILL.md index b224debc..b3693d24 100644 --- a/.claude/skills/meta-compile/SKILL.md +++ b/.claude/skills/meta-compile/SKILL.md @@ -1,6 +1,6 @@ --- name: meta-compile -description: Создать исходники объекта метаданных 1С (справочник, документ, регистр, перечисление, константа, общий модуль, обработка, HTTP-сервис и др.) в выгрузке конфигурации. Используй когда пользователь просит добавить или создать объект конфигурации +description: Создать объект метаданных 1С. Используй когда пользователь просит создать или добавить справочник, документ, регистр, перечисление, константу, общий модуль, обработку, отчёт и др. argument-hint: allowed-tools: - Bash From 72a4015a8d59de06a4a0765ca0155343c533c90a Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 18:39:18 +0300 Subject: [PATCH 18/19] fix(meta-compile,meta-edit): sync type handling and validation between scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit meta-compile: bare Number→Number(10,0), ValueStorage→xs:base64Binary, lowercase ref synonyms (catalogref, documentref, enumref). meta-edit: bare String default 0→10, reserved attribute name warnings. Co-Authored-By: Claude Opus 4.6 --- .../meta-compile/scripts/meta-compile.ps1 | 21 +++++++++++++ .../meta-compile/scripts/meta-compile.py | 19 ++++++++++++ .../skills/meta-edit/scripts/meta-edit.ps1 | 26 +++++++++++++++- .claude/skills/meta-edit/scripts/meta-edit.py | 30 ++++++++++++++++++- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index ed8cc6f4..37ccf14a 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -185,6 +185,10 @@ $script:typeSynonyms["бизнеспроцессссылка"] = "Bus $script:typeSynonyms["задачассылка"] = "TaskRef" $script:typeSynonyms["определяемыйтип"] = "DefinedType" $script:typeSynonyms["definedtype"] = "DefinedType" +# English lowercase ref synonyms +$script:typeSynonyms["catalogref"] = "CatalogRef" +$script:typeSynonyms["documentref"] = "DocumentRef" +$script:typeSynonyms["enumref"] = "EnumRef" function Resolve-TypeStr { param([string]$typeStr) @@ -248,6 +252,17 @@ function Emit-TypeContent { return } + # Number without params → Number(10,0) + if ($typeStr -eq "Number") { + X "$indentxs:decimal" + X "$indent" + X "$indent`t10" + X "$indent`t0" + X "$indent`tAny" + X "$indent" + return + } + # Number(D,F) or Number(D,F,nonneg) if ($typeStr -match '^Number\((\d+),(\d+)(,nonneg)?\)$') { $digits = $Matches[1] @@ -285,6 +300,12 @@ function Emit-TypeContent { return } + # ValueStorage + if ($typeStr -eq "ValueStorage") { + X "$indentxs:base64Binary" + return + } + # Reference types — use local xmlns declaration for 1C compatibility if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.(.+)$') { X "$indentd5p1:$typeStr" diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index f1e9db8b..9a7b5042 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -198,6 +198,10 @@ type_synonyms = { 'задачассылка': 'TaskRef', 'определяемыйтип': 'DefinedType', 'definedtype': 'DefinedType', + # English lowercase ref synonyms + 'catalogref': 'CatalogRef', + 'documentref': 'DocumentRef', + 'enumref': 'EnumRef', } def resolve_type_str(type_str): @@ -251,6 +255,16 @@ def emit_type_content(indent, type_str): X(f'{indent}\tVariable') X(f'{indent}') return + # Number without params -> Number(10,0) + if type_str == 'Number': + X(f'{indent}xs:decimal') + X(f'{indent}') + X(f'{indent}\t10') + X(f'{indent}\t0') + X(f'{indent}\tAny') + X(f'{indent}') + return + # Number(D,F) or Number(D,F,nonneg) m = re.match(r'^Number\((\d+),(\d+)(,nonneg)?\)$', type_str) if m: @@ -283,6 +297,11 @@ def emit_type_content(indent, type_str): dt_name = m.group(1) X(f'{indent}cfg:DefinedType.{dt_name}') return + # ValueStorage + if type_str == 'ValueStorage': + X(f'{indent}xs:base64Binary') + return + # Reference types — use local xmlns declaration for 1C compatibility m = re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.(.+)$', type_str) if m: diff --git a/.claude/skills/meta-edit/scripts/meta-edit.ps1 b/.claude/skills/meta-edit/scripts/meta-edit.ps1 index 89ca13e0..cafc9eaf 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.ps1 +++ b/.claude/skills/meta-edit/scripts/meta-edit.ps1 @@ -297,7 +297,7 @@ function Build-TypeContentXml { # String or String(N) if ($typeStr -match '^String(\((\d+)\))?$') { - $len = if ($Matches[2]) { $Matches[2] } else { "0" } + $len = if ($Matches[2]) { $Matches[2] } else { "10" } $sb.AppendLine("$indentxs:string") | Out-Null $sb.AppendLine("$indent") | Out-Null $sb.AppendLine("$indent`t$len") | Out-Null @@ -689,10 +689,34 @@ function Get-AttributeContext { } } +$script:reservedAttrNames = @{ + "Ref"="Ссылка"; "DeletionMark"="ПометкаУдаления"; "Code"="Код"; "Description"="Наименование" + "Date"="Дата"; "Number"="Номер"; "Posted"="Проведен"; "Parent"="Родитель"; "Owner"="Владелец" + "IsFolder"="ЭтоГруппа"; "Predefined"="Предопределенный"; "PredefinedDataName"="ИмяПредопределенныхДанных" + "Recorder"="Регистратор"; "Period"="Период"; "LineNumber"="НомерСтроки"; "Active"="Активность" + "Order"="Порядок"; "Type"="Тип"; "OffBalance"="Забалансовый" + "Started"="Стартован"; "Completed"="Завершен"; "HeadTask"="ВедущаяЗадача" + "Executed"="Выполнена"; "RoutePoint"="ТочкаМаршрута"; "BusinessProcess"="БизнесПроцесс" + "ThisNode"="ЭтотУзел"; "SentNo"="НомерОтправленного"; "ReceivedNo"="НомерПринятого" + "CalculationType"="ВидРасчета"; "RegistrationPeriod"="ПериодРегистрации"; "ReversingEntry"="СторноЗапись" + "Account"="Счет"; "ValueType"="ТипЗначения"; "ActionPeriodIsBasic"="ПериодДействияБазовый" +} + function Build-AttributeFragment { param($parsed, [string]$context, [string]$indent) if (-not $context) { $context = Get-AttributeContext } + + # Check reserved attribute names + $attrName = $parsed.name + if ($script:reservedAttrNames.ContainsKey($attrName)) { + Write-Warning "Attribute '$attrName' conflicts with a standard attribute name. This may cause errors when loading into 1C." + } + $ruValues = $script:reservedAttrNames.Values + if ($ruValues -contains $attrName) { + Write-Warning "Attribute '$attrName' conflicts with a standard attribute name (Russian). This may cause errors when loading into 1C." + } + $uuid = New-Guid-String $sb = New-Object System.Text.StringBuilder diff --git a/.claude/skills/meta-edit/scripts/meta-edit.py b/.claude/skills/meta-edit/scripts/meta-edit.py index e593f6c7..f2f4fdca 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.py +++ b/.claude/skills/meta-edit/scripts/meta-edit.py @@ -214,7 +214,7 @@ def build_type_content_xml(indent, type_str): # String or String(N) m = re.match(r"^String(\((\d+)\))?$", type_str) if m: - length = m.group(2) if m.group(2) else "0" + length = m.group(2) if m.group(2) else "10" lines.append(f"{indent}xs:string") lines.append(f"{indent}") lines.append(f"{indent}\t{length}") @@ -600,10 +600,38 @@ def get_attribute_context(): return "object" +RESERVED_ATTR_NAMES = { + 'Ref', 'DeletionMark', 'Code', 'Description', 'Date', 'Number', 'Posted', + 'Parent', 'Owner', 'IsFolder', 'Predefined', 'PredefinedDataName', + 'Recorder', 'Period', 'LineNumber', 'Active', 'Order', 'Type', 'OffBalance', + 'Started', 'Completed', 'HeadTask', 'Executed', 'RoutePoint', 'BusinessProcess', + 'ThisNode', 'SentNo', 'ReceivedNo', 'CalculationType', 'RegistrationPeriod', + 'ReversingEntry', 'Account', 'ValueType', 'ActionPeriodIsBasic', +} +RESERVED_ATTR_NAMES_RU = { + 'Ссылка', 'ПометкаУдаления', 'Код', 'Наименование', + 'Дата', 'Номер', 'Проведен', 'Родитель', 'Владелец', + 'ЭтоГруппа', 'Предопределенный', 'ИмяПредопределенныхДанных', + 'Регистратор', 'Период', 'НомерСтроки', 'Активность', + 'Порядок', 'Тип', 'Забалансовый', + 'Стартован', 'Завершен', 'ВедущаяЗадача', + 'Выполнена', 'ТочкаМаршрута', 'БизнесПроцесс', + 'ЭтотУзел', 'НомерОтправленного', 'НомерПринятого', + 'ВидРасчета', 'ПериодРегистрации', 'СторноЗапись', + 'Счет', 'ТипЗначения', 'ПериодДействияБазовый', +} + + def build_attribute_fragment(parsed, context, indent): """Build XML fragment string for an Attribute element.""" if not context: context = get_attribute_context() + + # Check reserved attribute names + attr_name = parsed['name'] + if attr_name in RESERVED_ATTR_NAMES or attr_name in RESERVED_ATTR_NAMES_RU: + print(f"WARNING: Attribute '{attr_name}' conflicts with a standard attribute name. This may cause errors when loading into 1C.", file=sys.stderr) + uid = new_uuid() lines = [] From 42cc7acdbe05274c367b7b4a554d161a3f473b01 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 8 Mar 2026 18:45:05 +0300 Subject: [PATCH 19/19] chore: bump script versions (meta-compile v1.2, meta-edit v1.4) Co-Authored-By: Claude Opus 4.6 --- .claude/skills/meta-compile/scripts/meta-compile.ps1 | 2 +- .claude/skills/meta-compile/scripts/meta-compile.py | 2 +- .claude/skills/meta-edit/scripts/meta-edit.ps1 | 2 +- .claude/skills/meta-edit/scripts/meta-edit.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 37ccf14a..706e6d84 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.1 — Compile 1C metadata object from JSON +# meta-compile v1.2 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 9a7b5042..da19ce9d 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-compile v1.1 — Compile 1C metadata object from JSON +# meta-compile v1.2 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse diff --git a/.claude/skills/meta-edit/scripts/meta-edit.ps1 b/.claude/skills/meta-edit/scripts/meta-edit.ps1 index cafc9eaf..bab26e14 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.ps1 +++ b/.claude/skills/meta-edit/scripts/meta-edit.ps1 @@ -1,4 +1,4 @@ -# meta-edit v1.3 — Edit existing 1C metadata object XML (inline mode + complex properties + TS attribute ops + modify-ts) +# meta-edit v1.4 — Edit existing 1C metadata object XML (inline mode + complex properties + TS attribute ops + modify-ts) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, diff --git a/.claude/skills/meta-edit/scripts/meta-edit.py b/.claude/skills/meta-edit/scripts/meta-edit.py index f2f4fdca..0894323f 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.py +++ b/.claude/skills/meta-edit/scripts/meta-edit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-edit v1.0 — Edit existing 1C metadata object XML (inline mode + complex properties + TS attribute ops + modify-ts) +# meta-edit v1.4 — Edit existing 1C metadata object XML (inline mode + complex properties + TS attribute ops + modify-ts) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse