fix(meta-compile,meta-edit): sync type handling and validation between scripts

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 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-03-08 18:39:18 +03:00
parent 843916642c
commit 72a4015a8d
4 changed files with 94 additions and 2 deletions
@@ -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 "$indent<v8:Type>xs:decimal</v8:Type>"
X "$indent<v8:NumberQualifiers>"
X "$indent`t<v8:Digits>10</v8:Digits>"
X "$indent`t<v8:FractionDigits>0</v8:FractionDigits>"
X "$indent`t<v8:AllowedSign>Any</v8:AllowedSign>"
X "$indent</v8:NumberQualifiers>"
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 "$indent<v8:Type>xs:base64Binary</v8:Type>"
return
}
# Reference types — use local xmlns declaration for 1C compatibility
if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.(.+)$') {
X "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$typeStr</v8:Type>"
@@ -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}\t<v8:AllowedLength>Variable</v8:AllowedLength>')
X(f'{indent}</v8:StringQualifiers>')
return
# Number without params -> Number(10,0)
if type_str == 'Number':
X(f'{indent}<v8:Type>xs:decimal</v8:Type>')
X(f'{indent}<v8:NumberQualifiers>')
X(f'{indent}\t<v8:Digits>10</v8:Digits>')
X(f'{indent}\t<v8:FractionDigits>0</v8:FractionDigits>')
X(f'{indent}\t<v8:AllowedSign>Any</v8:AllowedSign>')
X(f'{indent}</v8:NumberQualifiers>')
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}<v8:TypeSet>cfg:DefinedType.{dt_name}</v8:TypeSet>')
return
# ValueStorage
if type_str == 'ValueStorage':
X(f'{indent}<v8:Type>xs:base64Binary</v8:Type>')
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:
+25 -1
View File
@@ -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("$indent<v8:Type>xs:string</v8:Type>") | Out-Null
$sb.AppendLine("$indent<v8:StringQualifiers>") | Out-Null
$sb.AppendLine("$indent`t<v8:Length>$len</v8:Length>") | 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
+29 -1
View File
@@ -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}<v8:Type>xs:string</v8:Type>")
lines.append(f"{indent}<v8:StringQualifiers>")
lines.append(f"{indent}\t<v8:Length>{length}</v8: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 = []