mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
fix(skd-edit): preserve <valueType>, detect line endings, drop CRLF leak
Targeted follow-ups к round-trip фиксу: * modify-field больше не теряет <valueType> при перестройке поля — Read-FieldProperties сохраняет полный OuterXml элемента (StringQualifiers, NumberQualifiers, DateQualifiers и т.п.), Build-FieldFragment отдаёт его обратно. Лишние xmlns-декларации, добавляемые сериализатором при выгрузке поддерева, стрипаются регексом. * Line-ending convention теперь определяется при load (CRLF vs LF) и единообразно применяется в финале save. Раньше CreateWhitespace и Build-*Fragment везде использовали CRLF, что приводило к смешанным переносам в LF-исходниках (и наоборот) и к non-idempotent выходу modify-parameter title (run 1 → \n\t\t<title>\r\n... → run 2 → \r\n\t\t<title>\r\n...). * PS Insert-BeforeElement переведён на LF; все -join "`r`n" → "`n"; py "\r\n".join → "\n". Конечная нормализация переносов делается в save в соответствии со script:LineEnding. * preserve-entities-modify-parameter-title.json теперь idempotent: true (после фикса CRLF leak'а двойной прогон byte-identical). На реальной схеме diff после modify-field составил 30 строк: целевая вставка title плюс полезная одноразовая коррекция ранее повреждённых " в text-content <dcsat:expression>. modify-field идемпотентен. skd-edit v1.19 -> v1.20. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# skd-edit v1.19 — Atomic 1C DCS editor
|
||||
# skd-edit v1.20 — Atomic 1C DCS editor
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -203,7 +203,15 @@ function Read-FieldProperties($fieldEl) {
|
||||
}
|
||||
}
|
||||
"valueType" {
|
||||
# Read type info — store the raw element for now, we'll use type from parsed if overridden
|
||||
# Preserve the entire <valueType> OuterXml so rebuild can re-emit qualifiers
|
||||
# (StringQualifiers, NumberQualifiers, DateQualifiers, etc.) that would
|
||||
# otherwise be lost. Also extract Type string for type-override shorthand.
|
||||
$raw = $ch.OuterXml
|
||||
# .NET OuterXml re-declares xmlns on every element where the prefix is in
|
||||
# scope (because the fragment is treated as standalone). Strip these since
|
||||
# the parent context at insertion point already provides them.
|
||||
$raw = [regex]::Replace($raw, ' xmlns(?::\w+)?="[^"]*"', '')
|
||||
$props["_rawValueType"] = $raw
|
||||
$typeEl = $null
|
||||
foreach ($gc in $ch.ChildNodes) {
|
||||
if ($gc.NodeType -eq 'Element' -and $gc.LocalName -eq 'Type') {
|
||||
@@ -727,7 +735,7 @@ function Build-ValueTypeXml {
|
||||
|
||||
if ($typeStr -eq "boolean") {
|
||||
$lines += "$indent<v8:Type>xs:boolean</v8:Type>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr -match '^string(\((\d+)\))?$') {
|
||||
@@ -737,7 +745,7 @@ function Build-ValueTypeXml {
|
||||
$lines += "$indent`t<v8:Length>$len</v8:Length>"
|
||||
$lines += "$indent`t<v8:AllowedLength>Variable</v8:AllowedLength>"
|
||||
$lines += "$indent</v8:StringQualifiers>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr -match '^decimal\((\d+),(\d+)(,nonneg)?\)$') {
|
||||
@@ -750,7 +758,7 @@ function Build-ValueTypeXml {
|
||||
$lines += "$indent`t<v8:FractionDigits>$fraction</v8:FractionDigits>"
|
||||
$lines += "$indent`t<v8:AllowedSign>$sign</v8:AllowedSign>"
|
||||
$lines += "$indent</v8:NumberQualifiers>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr -match '^(date|dateTime)$') {
|
||||
@@ -762,26 +770,26 @@ function Build-ValueTypeXml {
|
||||
$lines += "$indent<v8:DateQualifiers>"
|
||||
$lines += "$indent`t<v8:DateFractions>$fractions</v8:DateFractions>"
|
||||
$lines += "$indent</v8:DateQualifiers>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr -eq "StandardPeriod") {
|
||||
$lines += "$indent<v8:Type>v8:StandardPeriod</v8:Type>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr -match '^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.') {
|
||||
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-Xml $typeStr)</v8:Type>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
if ($typeStr.Contains('.')) {
|
||||
$lines += "$indent<v8:Type xmlns:d5p1=`"http://v8.1c.ru/8.1/data/enterprise/current-config`">d5p1:$(Esc-Xml $typeStr)</v8:Type>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
$lines += "$indent<v8:Type>$(Esc-Xml $typeStr)</v8:Type>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-MLTextXml {
|
||||
@@ -793,7 +801,7 @@ function Build-MLTextXml {
|
||||
$lines += "$indent`t`t<v8:content>$(Esc-Xml $text)</v8:content>"
|
||||
$lines += "$indent`t</v8:item>"
|
||||
$lines += "$indent</$tag>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-RoleXml {
|
||||
@@ -812,7 +820,7 @@ function Build-RoleXml {
|
||||
}
|
||||
}
|
||||
$lines += "$indent</role>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-RestrictionXml {
|
||||
@@ -834,7 +842,7 @@ function Build-RestrictionXml {
|
||||
}
|
||||
}
|
||||
$lines += "$indent</useRestriction>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-FieldFragment {
|
||||
@@ -857,14 +865,18 @@ function Build-FieldFragment {
|
||||
$roleXml = Build-RoleXml -roles $parsed.roles -indent "$i`t"
|
||||
if ($roleXml) { $lines += $roleXml }
|
||||
|
||||
if ($parsed.type) {
|
||||
if ($parsed.rawValueType) {
|
||||
# Preserve original <valueType> verbatim — keeps qualifiers (StringQualifiers,
|
||||
# NumberQualifiers, DateQualifiers, …) that aren't expressible via shorthand.
|
||||
$lines += "$i`t" + $parsed.rawValueType
|
||||
} elseif ($parsed.type) {
|
||||
$lines += "$i`t<valueType>"
|
||||
$lines += (Build-ValueTypeXml -typeStr $parsed.type -indent "$i`t`t")
|
||||
$lines += "$i`t</valueType>"
|
||||
}
|
||||
|
||||
$lines += "$i</field>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-TotalFragment {
|
||||
@@ -876,7 +888,7 @@ function Build-TotalFragment {
|
||||
$lines += "$i`t<dataPath>$(Esc-Xml $parsed.dataPath)</dataPath>"
|
||||
$lines += "$i`t<expression>$(Esc-Xml $parsed.expression)</expression>"
|
||||
$lines += "$i</totalField>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-CalcFieldFragment {
|
||||
@@ -903,7 +915,7 @@ function Build-CalcFieldFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</calculatedField>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-ParamValueXml {
|
||||
@@ -1010,7 +1022,7 @@ function Build-ParamFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</parameter>"
|
||||
$fragments += ($lines -join "`r`n")
|
||||
$fragments += ($lines -join "`n")
|
||||
|
||||
if ($parsed.autoDates) {
|
||||
$paramName = $parsed.name
|
||||
@@ -1027,7 +1039,7 @@ function Build-ParamFragment {
|
||||
$bLines += "$i`t<useRestriction>true</useRestriction>"
|
||||
$bLines += "$i`t<expression>$(Esc-Xml "&$paramName.ДатаНачала")</expression>"
|
||||
$bLines += "$i</parameter>"
|
||||
$fragments += ($bLines -join "`r`n")
|
||||
$fragments += ($bLines -join "`n")
|
||||
|
||||
$eLines = @()
|
||||
$eLines += "$i<parameter>"
|
||||
@@ -1040,7 +1052,7 @@ function Build-ParamFragment {
|
||||
$eLines += "$i`t<useRestriction>true</useRestriction>"
|
||||
$eLines += "$i`t<expression>$(Esc-Xml "&$paramName.ДатаОкончания")</expression>"
|
||||
$eLines += "$i</parameter>"
|
||||
$fragments += ($eLines -join "`r`n")
|
||||
$fragments += ($eLines -join "`n")
|
||||
}
|
||||
|
||||
return ,$fragments
|
||||
@@ -1075,7 +1087,7 @@ function Build-FilterItemFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</dcsset:item>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-SelectionItemFragment {
|
||||
@@ -1116,7 +1128,7 @@ function Build-SelectionItemFragment {
|
||||
$lines += "$i`t<dcsset:field>$(Esc-Xml $fieldName)</dcsset:field>"
|
||||
$lines += "$i</dcsset:item>"
|
||||
}
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-DataParamFragment {
|
||||
@@ -1158,7 +1170,7 @@ function Build-DataParamFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</dcscor:item>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-OrderItemFragment {
|
||||
@@ -1174,7 +1186,7 @@ function Build-OrderItemFragment {
|
||||
$lines += "$i`t<dcsset:orderType>$($parsed.direction)</dcsset:orderType>"
|
||||
$lines += "$i</dcsset:item>"
|
||||
}
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-DataSetLinkFragment {
|
||||
@@ -1191,7 +1203,7 @@ function Build-DataSetLinkFragment {
|
||||
$lines += "$i`t<parameter>$(Esc-Xml $parsed.parameter)</parameter>"
|
||||
}
|
||||
$lines += "$i</dataSetLink>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-DataSetQueryFragment {
|
||||
@@ -1204,7 +1216,7 @@ function Build-DataSetQueryFragment {
|
||||
$lines += "$i`t<dataSource>$(Esc-Xml $parsed.dataSource)</dataSource>"
|
||||
$lines += "$i`t<query>$(Esc-Xml $parsed.query)</query>"
|
||||
$lines += "$i</dataSet>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-VariantFragment {
|
||||
@@ -1230,7 +1242,7 @@ function Build-VariantFragment {
|
||||
$lines += "$i`t`t</dcsset:item>"
|
||||
$lines += "$i`t</dcsset:settings>"
|
||||
$lines += "$i</settingsVariant>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Emit-FilterComparison {
|
||||
@@ -1312,7 +1324,7 @@ function Build-ConditionalAppearanceItemFragment {
|
||||
$lines += "$i`t</dcsset:appearance>"
|
||||
|
||||
$lines += "$i</dcsset:item>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-StructureItemFragment {
|
||||
@@ -1364,7 +1376,7 @@ function Build-StructureItemFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</dcsset:item>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
function Build-OutputParamFragment {
|
||||
@@ -1392,7 +1404,7 @@ function Build-OutputParamFragment {
|
||||
}
|
||||
|
||||
$lines += "$i</dcscor:item>"
|
||||
return $lines -join "`r`n"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
# --- 5. XML helpers ---
|
||||
@@ -1437,7 +1449,9 @@ function Get-ChildIndent($container) {
|
||||
}
|
||||
|
||||
function Insert-BeforeElement($container, $newNode, $refNode, $childIndent) {
|
||||
$ws = $xmlDoc.CreateWhitespace("`r`n$childIndent")
|
||||
# LF line endings — 1С DCS files use LF consistently; CRLF causes idempotency
|
||||
# leaks when modify-* removes one whitespace and inserts a different-style one.
|
||||
$ws = $xmlDoc.CreateWhitespace("`n$childIndent")
|
||||
if ($refNode) {
|
||||
$container.InsertBefore($ws, $refNode) | Out-Null
|
||||
$container.InsertBefore($newNode, $ws) | Out-Null
|
||||
@@ -1450,7 +1464,7 @@ function Insert-BeforeElement($container, $newNode, $refNode, $childIndent) {
|
||||
$container.AppendChild($ws) | Out-Null
|
||||
$container.AppendChild($newNode) | Out-Null
|
||||
$parentIndent = if ($childIndent.Length -gt 1) { $childIndent.Substring(0, $childIndent.Length - 1) } else { "" }
|
||||
$closeWs = $xmlDoc.CreateWhitespace("`r`n$parentIndent")
|
||||
$closeWs = $xmlDoc.CreateWhitespace("`n$parentIndent")
|
||||
$container.AppendChild($closeWs) | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -1729,6 +1743,10 @@ $script:RawOriginal = [System.IO.File]::ReadAllText($resolvedPath, [System.Text.
|
||||
$rootOpenMatch = [regex]::Match($script:RawOriginal, '<DataCompositionSchema\b[^>]*>')
|
||||
if ($rootOpenMatch.Success) { $script:RawRootOpening = $rootOpenMatch.Value } else { $script:RawRootOpening = $null }
|
||||
|
||||
# Detect line ending convention so save can normalize back to whatever the source used.
|
||||
# 1С Designer writes CRLF on Windows; LF-edited files should stay LF.
|
||||
$script:LineEnding = if ($script:RawOriginal.Contains("`r`n")) { "`r`n" } else { "`n" }
|
||||
|
||||
$xmlDoc = New-Object System.Xml.XmlDocument
|
||||
$xmlDoc.PreserveWhitespace = $true
|
||||
$xmlDoc.Load($resolvedPath)
|
||||
@@ -2021,7 +2039,7 @@ switch ($Operation) {
|
||||
}
|
||||
}
|
||||
$valueLines = Build-ParamValueXml -type $declaredType -value $value -indent $childIndent
|
||||
$fragXml = $valueLines -join "`r`n"
|
||||
$fragXml = $valueLines -join "`n"
|
||||
|
||||
$wasExisting = ($null -ne $existing)
|
||||
if ($existing) {
|
||||
@@ -2107,7 +2125,7 @@ switch ($Operation) {
|
||||
}
|
||||
foreach ($av in $avItems) {
|
||||
$avLines = Build-AvailableValueFragment -item $av -declaredType $declaredType -indent $childIndent
|
||||
$fragXml = $avLines -join "`r`n"
|
||||
$fragXml = $avLines -join "`n"
|
||||
$nodes = Import-Fragment $xmlDoc $fragXml
|
||||
foreach ($node in $nodes) {
|
||||
Insert-BeforeElement $paramEl $node $refNode $childIndent
|
||||
@@ -2671,7 +2689,7 @@ switch ($Operation) {
|
||||
$lines += "$itemIndent`t<dcsset:periodAdditionBegin xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</dcsset:periodAdditionBegin>"
|
||||
$lines += "$itemIndent`t<dcsset:periodAdditionEnd xsi:type=`"xs:dateTime`">0001-01-01T00:00:00</dcsset:periodAdditionEnd>"
|
||||
$lines += "$itemIndent</dcsset:item>"
|
||||
$fragXml = $lines -join "`r`n"
|
||||
$fragXml = $lines -join "`n"
|
||||
$nodes = Import-Fragment $xmlDoc $fragXml
|
||||
foreach ($node in $nodes) {
|
||||
Insert-BeforeElement $giEl $node $null $itemIndent
|
||||
@@ -2992,7 +3010,7 @@ switch ($Operation) {
|
||||
} else {
|
||||
$valLines += "$itemIndent<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$($parsed.value)")</dcscor:value>"
|
||||
}
|
||||
$valXml = $valLines -join "`r`n"
|
||||
$valXml = $valLines -join "`n"
|
||||
$valNodes = Import-Fragment $xmlDoc $valXml
|
||||
foreach ($node in $valNodes) {
|
||||
Insert-BeforeElement $dpItem $node $null $itemIndent
|
||||
@@ -3056,6 +3074,9 @@ switch ($Operation) {
|
||||
type = if ($parsed.type) { $parsed.type } else { $existing.type }
|
||||
roles = if ($parsed.roles -and $parsed.roles.Count -gt 0) { $parsed.roles } else { $existing.roles }
|
||||
restrict = if ($parsed.restrict -and $parsed.restrict.Count -gt 0) { $parsed.restrict } else { $existing.restrict }
|
||||
# Preserve raw <valueType> only when user did NOT override type via shorthand —
|
||||
# otherwise the override path rebuilds valueType from $parsed.type.
|
||||
rawValueType = if ($parsed.type) { $null } else { $existing._rawValueType }
|
||||
}
|
||||
|
||||
# Remember position (NextSibling after whitespace)
|
||||
@@ -3143,7 +3164,7 @@ switch ($Operation) {
|
||||
$lines += "$fieldIndent`t<dcscom:$k>$(Esc-Xml $kv[$k])</dcscom:$k>"
|
||||
}
|
||||
$lines += "$fieldIndent</role>"
|
||||
$fragXml = $lines -join "`r`n"
|
||||
$fragXml = $lines -join "`n"
|
||||
|
||||
# Insert before <valueType>, else before <inputParameters>, else at end
|
||||
$refNode = $null
|
||||
@@ -3461,6 +3482,14 @@ $content = [regex]::Replace(
|
||||
# (`<foo bar="x" />`) but 1C-Designer writes `<foo bar="x"/>`. Strip the space.
|
||||
$content = [regex]::Replace($content, '(?<=\S) />', '/>')
|
||||
|
||||
# (4) normalize line endings to match source — operations may mix LF (from new
|
||||
# fragments) with whatever the source used (CRLF on Windows, LF on Linux/git).
|
||||
if ($script:LineEnding -eq "`r`n") {
|
||||
$content = $content -replace '(?<!\r)\n', "`r`n"
|
||||
} else {
|
||||
$content = $content -replace "`r`n", "`n"
|
||||
}
|
||||
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($resolvedPath, $content, $enc)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# skd-edit v1.19 — Atomic 1C DCS editor (Python port)
|
||||
# skd-edit v1.20 — Atomic 1C DCS editor (Python port)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import os
|
||||
@@ -231,6 +231,13 @@ def read_field_properties(field_el):
|
||||
if isinstance(gc.tag, str) and local_name(gc) == "content":
|
||||
props["title"] = (gc.text or "").strip()
|
||||
elif ln == "valueType":
|
||||
# Preserve full <valueType> serialization so rebuild can re-emit qualifiers
|
||||
# (StringQualifiers, NumberQualifiers, DateQualifiers, …) that aren't
|
||||
# expressible via shorthand. Strip xmlns declarations that lxml re-emits when
|
||||
# serializing a sub-element (parent context already provides them).
|
||||
raw = etree.tostring(ch, encoding="unicode")
|
||||
raw = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw)
|
||||
props["_rawValueType"] = raw
|
||||
for gc in ch:
|
||||
if isinstance(gc.tag, str) and local_name(gc) == "Type":
|
||||
props["_rawTypeText"] = (gc.text or "").strip()
|
||||
@@ -683,7 +690,7 @@ def build_value_type_xml(type_str, indent):
|
||||
|
||||
if type_str == "boolean":
|
||||
lines.append(f"{indent}<v8:Type>xs:boolean</v8:Type>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
m = re.match(r'^string(\((\d+)\))?$', type_str)
|
||||
if m:
|
||||
@@ -693,7 +700,7 @@ def build_value_type_xml(type_str, indent):
|
||||
lines.append(f"{indent}\t<v8:Length>{length}</v8:Length>")
|
||||
lines.append(f"{indent}\t<v8:AllowedLength>Variable</v8:AllowedLength>")
|
||||
lines.append(f"{indent}</v8:StringQualifiers>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
m = re.match(r'^decimal\((\d+),(\d+)(,nonneg)?\)$', type_str)
|
||||
if m:
|
||||
@@ -705,7 +712,7 @@ def build_value_type_xml(type_str, indent):
|
||||
lines.append(f"{indent}\t<v8:FractionDigits>{fraction}</v8:FractionDigits>")
|
||||
lines.append(f"{indent}\t<v8:AllowedSign>{sign}</v8:AllowedSign>")
|
||||
lines.append(f"{indent}</v8:NumberQualifiers>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
m = re.match(r'^(date|dateTime)$', type_str)
|
||||
if m:
|
||||
@@ -714,22 +721,22 @@ def build_value_type_xml(type_str, indent):
|
||||
lines.append(f"{indent}<v8:DateQualifiers>")
|
||||
lines.append(f"{indent}\t<v8:DateFractions>{fractions}</v8:DateFractions>")
|
||||
lines.append(f"{indent}</v8:DateQualifiers>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
if type_str == "StandardPeriod":
|
||||
lines.append(f"{indent}<v8:Type>v8:StandardPeriod</v8:Type>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
if re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef)\.', type_str):
|
||||
lines.append(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml(type_str)}</v8:Type>')
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
if "." in type_str:
|
||||
lines.append(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{esc_xml(type_str)}</v8:Type>')
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
lines.append(f"{indent}<v8:Type>{esc_xml(type_str)}</v8:Type>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_mltext_xml(tag, text, indent):
|
||||
@@ -741,7 +748,7 @@ def build_mltext_xml(tag, text, indent):
|
||||
f"{indent}\t</v8:item>",
|
||||
f"{indent}</{tag}>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_role_xml(roles, indent):
|
||||
@@ -755,7 +762,7 @@ def build_role_xml(roles, indent):
|
||||
else:
|
||||
lines.append(f"{indent}\t<dcscom:{role}>true</dcscom:{role}>")
|
||||
lines.append(f"{indent}</role>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_restriction_xml(restrict, indent):
|
||||
@@ -768,7 +775,7 @@ def build_restriction_xml(restrict, indent):
|
||||
if xml_name:
|
||||
lines.append(f"{indent}\t<{xml_name}>true</{xml_name}>")
|
||||
lines.append(f"{indent}</useRestriction>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_field_fragment(parsed, indent):
|
||||
@@ -787,13 +794,17 @@ def build_field_fragment(parsed, indent):
|
||||
if role_xml:
|
||||
lines.append(role_xml)
|
||||
|
||||
if parsed.get("type"):
|
||||
if parsed.get("rawValueType"):
|
||||
# Preserve original <valueType> verbatim — keeps qualifiers (StringQualifiers,
|
||||
# NumberQualifiers, DateQualifiers, …) that aren't expressible via shorthand.
|
||||
lines.append(f"{i}\t" + parsed["rawValueType"])
|
||||
elif parsed.get("type"):
|
||||
lines.append(f"{i}\t<valueType>")
|
||||
lines.append(build_value_type_xml(parsed["type"], f"{i}\t\t"))
|
||||
lines.append(f"{i}\t</valueType>")
|
||||
|
||||
lines.append(f"{i}</field>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_total_fragment(parsed, indent):
|
||||
@@ -804,7 +815,7 @@ def build_total_fragment(parsed, indent):
|
||||
f"{i}\t<expression>{esc_xml(parsed['expression'])}</expression>",
|
||||
f"{i}</totalField>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_calc_field_fragment(parsed, indent):
|
||||
@@ -823,7 +834,7 @@ def build_calc_field_fragment(parsed, indent):
|
||||
lines.append(build_value_type_xml(parsed["type"], f"{i}\t\t"))
|
||||
lines.append(f"{i}\t</valueType>")
|
||||
lines.append(f"{i}</calculatedField>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_param_value_xml(type_str, value, indent, tag_name="value", tag_ns=""):
|
||||
@@ -897,7 +908,7 @@ def build_param_fragment(parsed, indent):
|
||||
lines.append(f"{i}\t<use>Always</use>")
|
||||
|
||||
lines.append(f"{i}</parameter>")
|
||||
fragments.append("\r\n".join(lines))
|
||||
fragments.append("\n".join(lines))
|
||||
|
||||
if parsed.get("autoDates"):
|
||||
param_name = parsed["name"]
|
||||
@@ -914,7 +925,7 @@ def build_param_fragment(parsed, indent):
|
||||
f"{i}\t<expression>{esc_xml('&' + param_name + '.\u0414\u0430\u0442\u0430\u041d\u0430\u0447\u0430\u043b\u0430')}</expression>",
|
||||
f"{i}</parameter>",
|
||||
]
|
||||
fragments.append("\r\n".join(b_lines))
|
||||
fragments.append("\n".join(b_lines))
|
||||
|
||||
e_lines = [
|
||||
f"{i}<parameter>",
|
||||
@@ -928,7 +939,7 @@ def build_param_fragment(parsed, indent):
|
||||
f"{i}\t<expression>{esc_xml('&' + param_name + '.\u0414\u0430\u0442\u0430\u041e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f')}</expression>",
|
||||
f"{i}</parameter>",
|
||||
]
|
||||
fragments.append("\r\n".join(e_lines))
|
||||
fragments.append("\n".join(e_lines))
|
||||
|
||||
return fragments
|
||||
|
||||
@@ -955,7 +966,7 @@ def build_filter_item_fragment(parsed, indent):
|
||||
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml(uid)}</dcsset:userSettingID>")
|
||||
|
||||
lines.append(f"{i}</dcsset:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_selection_item_fragment(field_name, indent):
|
||||
@@ -986,13 +997,13 @@ def build_selection_item_fragment(field_name, indent):
|
||||
lines.append(f"{i}\t</dcsset:item>")
|
||||
lines.append(f"{i}\t<dcsset:placement>Auto</dcsset:placement>")
|
||||
lines.append(f"{i}</dcsset:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
lines = [
|
||||
f'{i}<dcsset:item xsi:type="dcsset:SelectedItemField">',
|
||||
f"{i}\t<dcsset:field>{esc_xml(field_name)}</dcsset:field>",
|
||||
f"{i}</dcsset:item>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_data_param_fragment(parsed, indent):
|
||||
@@ -1027,7 +1038,7 @@ def build_data_param_fragment(parsed, indent):
|
||||
lines.append(f"{i}\t<dcsset:userSettingID>{esc_xml(uid)}</dcsset:userSettingID>")
|
||||
|
||||
lines.append(f"{i}</dcscor:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_order_item_fragment(parsed, indent):
|
||||
@@ -1040,7 +1051,7 @@ def build_order_item_fragment(parsed, indent):
|
||||
f"{i}\t<dcsset:orderType>{parsed['direction']}</dcsset:orderType>",
|
||||
f"{i}</dcsset:item>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_data_set_link_fragment(parsed, indent):
|
||||
@@ -1055,7 +1066,7 @@ def build_data_set_link_fragment(parsed, indent):
|
||||
if parsed.get("parameter"):
|
||||
lines.append(f"{i}\t<parameter>{esc_xml(parsed['parameter'])}</parameter>")
|
||||
lines.append(f"{i}</dataSetLink>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_data_set_query_fragment(parsed, indent):
|
||||
@@ -1067,7 +1078,7 @@ def build_data_set_query_fragment(parsed, indent):
|
||||
f"{i}\t<query>{esc_xml(parsed['query'])}</query>",
|
||||
f"{i}</dataSet>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_variant_fragment(parsed, indent):
|
||||
@@ -1092,7 +1103,7 @@ def build_variant_fragment(parsed, indent):
|
||||
f"{i}\t</dcsset:settings>",
|
||||
f"{i}</settingsVariant>",
|
||||
]
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _emit_filter_comparison(lines, f, indent):
|
||||
@@ -1159,7 +1170,7 @@ def build_conditional_appearance_item_fragment(parsed, indent):
|
||||
lines.append(f"{i}\t</dcsset:appearance>")
|
||||
|
||||
lines.append(f"{i}</dcsset:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_structure_item_fragment(item, indent):
|
||||
@@ -1196,7 +1207,7 @@ def build_structure_item_fragment(item, indent):
|
||||
lines.append(build_structure_item_fragment(child, f"{i}\t"))
|
||||
|
||||
lines.append(f"{i}</dcsset:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def build_output_param_fragment(parsed, indent):
|
||||
@@ -1219,7 +1230,7 @@ def build_output_param_fragment(parsed, indent):
|
||||
lines.append(f'{i}\t<dcscor:value xsi:type="{ptype}">{esc_xml(val)}</dcscor:value>')
|
||||
|
||||
lines.append(f"{i}</dcscor:item>")
|
||||
return "\r\n".join(lines)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# ── 5. XML helpers ──────────────────────────────────────────
|
||||
@@ -1505,6 +1516,9 @@ raw_original_text = raw_original_bytes.lstrip(b"\xef\xbb\xbf").decode("utf-8")
|
||||
_root_open_m = re.search(r"<DataCompositionSchema\b[^>]*>", raw_original_text, re.DOTALL)
|
||||
raw_root_opening = _root_open_m.group(0) if _root_open_m else None
|
||||
|
||||
# Detect line ending convention so save can normalize back to whatever the source used.
|
||||
line_ending = "\r\n" if "\r\n" in raw_original_text else "\n"
|
||||
|
||||
xml_parser = etree.XMLParser(remove_blank_text=False)
|
||||
tree = etree.parse(resolved_path, xml_parser)
|
||||
xml_doc = tree.getroot()
|
||||
@@ -1739,7 +1753,7 @@ elif operation == "modify-parameter":
|
||||
declared_type = re.sub(r'^d\d+p\d+:', '', (tnode.text or "").strip())
|
||||
break
|
||||
value_lines = build_param_value_xml(declared_type, value, child_indent)
|
||||
frag_xml = "\r\n".join(value_lines)
|
||||
frag_xml = "\n".join(value_lines)
|
||||
was_existing = existing is not None
|
||||
if existing is not None:
|
||||
# Find next-element sibling as ref before removing
|
||||
@@ -1798,7 +1812,7 @@ elif operation == "modify-parameter":
|
||||
break
|
||||
for av in av_items:
|
||||
av_lines = build_available_value_fragment(av, declared_type, child_indent)
|
||||
frag_xml = "\r\n".join(av_lines)
|
||||
frag_xml = "\n".join(av_lines)
|
||||
nodes = import_fragment(xml_doc, frag_xml)
|
||||
for node in nodes:
|
||||
insert_before_element(param_el, node, ref_node, child_indent)
|
||||
@@ -2223,7 +2237,7 @@ elif operation == "modify-structure":
|
||||
f'{item_indent}\t<dcsset:periodAdditionEnd xsi:type="xs:dateTime">0001-01-01T00:00:00</dcsset:periodAdditionEnd>',
|
||||
f'{item_indent}</dcsset:item>',
|
||||
]
|
||||
frag_xml = "\r\n".join(lines)
|
||||
frag_xml = "\n".join(lines)
|
||||
for node in import_fragment(xml_doc, frag_xml):
|
||||
insert_before_element(gi_el, node, None, item_indent)
|
||||
|
||||
@@ -2486,7 +2500,7 @@ elif operation == "modify-dataParameter":
|
||||
else:
|
||||
val_lines.append(f'{item_indent}<dcscor:value xsi:type="xs:string">{esc_xml(str(pv))}</dcscor:value>')
|
||||
|
||||
val_xml = "\r\n".join(val_lines)
|
||||
val_xml = "\n".join(val_lines)
|
||||
val_nodes = import_fragment(xml_doc, val_xml)
|
||||
for node in val_nodes:
|
||||
insert_before_element(dp_item, node, None, item_indent)
|
||||
@@ -2532,6 +2546,8 @@ elif operation == "modify-field":
|
||||
"type": parsed["type"] if parsed.get("type") else existing["type"],
|
||||
"roles": parsed["roles"] if parsed.get("roles") else existing["roles"],
|
||||
"restrict": parsed["restrict"] if parsed.get("restrict") else existing["restrict"],
|
||||
# Preserve raw <valueType> only when user did NOT override type via shorthand.
|
||||
"rawValueType": None if parsed.get("type") else existing.get("_rawValueType"),
|
||||
}
|
||||
|
||||
# Find next element sibling for position
|
||||
@@ -2605,7 +2621,7 @@ elif operation == "set-field-role":
|
||||
for k, v in kv:
|
||||
lines.append(f"{field_indent}\t<dcscom:{k}>{esc_xml(v)}</dcscom:{k}>")
|
||||
lines.append(f"{field_indent}</role>")
|
||||
frag_xml = "\r\n".join(lines)
|
||||
frag_xml = "\n".join(lines)
|
||||
|
||||
ref_node = next((ch for ch in field_el if isinstance(ch.tag, str) and local_name(ch) in ("valueType", "inputParameters") and etree.QName(ch.tag).namespace == SCH_NS), None)
|
||||
for node in import_fragment(xml_doc, frag_xml):
|
||||
@@ -2870,6 +2886,12 @@ xml_text = re.sub(
|
||||
# Normalize self-closing tags: lxml writes `<foo bar="x"/>` already (no space), but be
|
||||
# defensive — strip any space before `/>` so PS and PY ports stay byte-equivalent.
|
||||
xml_text = re.sub(r"(?<=\S) />", "/>", xml_text)
|
||||
|
||||
# Normalize line endings to match source.
|
||||
if line_ending == "\r\n":
|
||||
xml_text = re.sub(r"(?<!\r)\n", "\r\n", xml_text)
|
||||
else:
|
||||
xml_text = xml_text.replace("\r\n", "\n")
|
||||
xml_bytes = xml_text.encode("utf-8")
|
||||
|
||||
if not xml_bytes.endswith(b"\n"):
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Период [Новый период]"
|
||||
}
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user