mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-14 18:04:58 +03:00
fix(skd-edit): preserve multi-lang title + unknown children in modify-*
В типовых конфигурациях (ERP, БП, ЗУП и т.д.) у полей и параметров обычно есть мульти-язык title (ru + en, иногда + локализация). До этого modify-field / modify-parameter / modify-dataParameter, перестраивая элемент через Build-MLTextXml, оставляли только последнее найденное <v8:content> в ru — en/uk/kk siblings молча терялись, и при следующей выгрузке Designer ломал миграцию. Read-FieldProperties сохраняет полный OuterXml <title> в _rawTitle и коллекционирует OuterXml неизвестных дочерних элементов (<editFormat>, <appearance>, кастомные расширения) в _unknownChildren. Build-FieldFragment эмитит: * _rawTitle как есть, если user не задал новый title; * Patch-MLTextRu(_rawTitle, newRu) если user задал ru-override — патчит только <v8:content> в <v8:lang>ru</v8:lang>, остальные языки сохраняет; * _unknownChildren в конце поля (после valueType). modify-parameter аналогично: при title-override проверяет multi-lang (>1 <v8:item>) и патчит ru через Patch-MLTextRu, иначе ребилдит ru-only. set-field-role сохраняет нестандартные подэлементы <role> (например <dcscom:addition>, <dcscom:groupFields>), не входящие в фиксированный known-children set и не указанные через kv в shorthand. xmlns-стрип на захваченных OuterXml — лишние декларации (которые сериализаторы добавляют для standalone-фрагментов) убираются. PY: lxml etree.tostring по умолчанию включает .tail (whitespace после закрывающего тега), что приводило к non-idempotent ростy whitespace при повторных прогонах. Везде добавлен with_tail=False. Новые тесты с idempotent: true: * preserve-multilang-modify-field (ru-override на multi-lang title); * preserve-multilang-modify-parameter (то же для параметра); * preserve-unknown-children-modify-field (role flag, проверяем что <editFormat> и en title не теряются). Общая fixture: multilang-base/Template.xml с полем и параметром, у каждого ru + en title; поле также имеет <editFormat>. 39/39 PS + 39/39 PY. skd-edit v1.20 -> v1.21. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# skd-edit v1.20 — Atomic 1C DCS editor
|
||||
# skd-edit v1.21 — Atomic 1C DCS editor
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -183,6 +183,8 @@ function Read-FieldProperties($fieldEl) {
|
||||
$props = @{
|
||||
dataPath = ""; field = ""; title = ""; type = ""
|
||||
roles = @(); restrict = @()
|
||||
_rawTitle = $null
|
||||
_unknownChildren = @()
|
||||
}
|
||||
|
||||
foreach ($ch in $fieldEl.ChildNodes) {
|
||||
@@ -191,14 +193,22 @@ function Read-FieldProperties($fieldEl) {
|
||||
"dataPath" { $props.dataPath = $ch.InnerText.Trim() }
|
||||
"field" { $props.field = $ch.InnerText.Trim() }
|
||||
"title" {
|
||||
# Extract text from LocalStringType
|
||||
# Preserve full multi-lang title OuterXml — used to keep en/uk/etc.
|
||||
# siblings when shorthand overrides only the ru content. Strip xmlns
|
||||
# redeclarations that OuterXml adds for sub-elements.
|
||||
$raw = $ch.OuterXml
|
||||
$raw = [regex]::Replace($raw, ' xmlns(?::\w+)?="[^"]*"', '')
|
||||
$props._rawTitle = $raw
|
||||
# Also extract ru content as plain string (backward compat — used by
|
||||
# external consumers reading $existing.title).
|
||||
foreach ($item in $ch.ChildNodes) {
|
||||
if ($item.NodeType -eq 'Element' -and $item.LocalName -eq 'item') {
|
||||
$lang = $null; $content = $null
|
||||
foreach ($gc in $item.ChildNodes) {
|
||||
if ($gc.NodeType -eq 'Element' -and $gc.LocalName -eq 'content') {
|
||||
$props.title = $gc.InnerText.Trim()
|
||||
}
|
||||
if ($gc.NodeType -eq 'Element' -and $gc.LocalName -eq 'lang') { $lang = $gc.InnerText.Trim() }
|
||||
if ($gc.NodeType -eq 'Element' -and $gc.LocalName -eq 'content') { $content = $gc.InnerText.Trim() }
|
||||
}
|
||||
if ($lang -eq 'ru' -and $null -ne $content) { $props.title = $content }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,6 +252,13 @@ function Read-FieldProperties($fieldEl) {
|
||||
}
|
||||
}
|
||||
}
|
||||
default {
|
||||
# Defense in depth: preserve OuterXml of unknown children so rebuild
|
||||
# doesn't silently drop them (custom <editFormat>, <appearance>, etc.).
|
||||
$raw = $ch.OuterXml
|
||||
$raw = [regex]::Replace($raw, ' xmlns(?::\w+)?="[^"]*"', '')
|
||||
$props._unknownChildren += $raw
|
||||
}
|
||||
}
|
||||
}
|
||||
return $props
|
||||
@@ -804,6 +821,28 @@ function Build-MLTextXml {
|
||||
return $lines -join "`n"
|
||||
}
|
||||
|
||||
# Patches the ru <v8:content> within an existing multi-lang title OuterXml, preserving
|
||||
# en/uk/etc. siblings. Used when modify-* operates with a title-override shorthand on a
|
||||
# field/parameter that already has multi-language titles (typical in ERP/БП/ЗУП).
|
||||
# If no ru item exists, one is prepended before the first existing item.
|
||||
function Patch-MLTextRu {
|
||||
param([string]$rawOuterXml, [string]$newRuText, [string]$indent)
|
||||
$escaped = Esc-Xml $newRuText
|
||||
$ruItemPat = '(<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>)[^<]*(</v8:content>\s*</v8:item>)'
|
||||
if ([regex]::IsMatch($rawOuterXml, $ruItemPat)) {
|
||||
return [regex]::Replace($rawOuterXml, $ruItemPat, { param($m) $m.Groups[1].Value + $escaped + $m.Groups[2].Value })
|
||||
}
|
||||
# No ru item — prepend one inside the title element, before first <v8:item>.
|
||||
$prep = "$indent`t<v8:item>`n$indent`t`t<v8:lang>ru</v8:lang>`n$indent`t`t<v8:content>$escaped</v8:content>`n$indent`t</v8:item>"
|
||||
if ($rawOuterXml -match '<v8:item>') {
|
||||
$re = New-Object System.Text.RegularExpressions.Regex('(\s*)<v8:item>')
|
||||
return $re.Replace($rawOuterXml, "`n$prep`$1<v8:item>", 1)
|
||||
}
|
||||
# Empty title — inject after opening tag.
|
||||
$re2 = New-Object System.Text.RegularExpressions.Regex('(<(?:\w+:)?title[^>]*>)')
|
||||
return $re2.Replace($rawOuterXml, "`$1`n$prep`n$indent", 1)
|
||||
}
|
||||
|
||||
function Build-RoleXml {
|
||||
param([string[]]$roles, [string]$indent)
|
||||
|
||||
@@ -854,7 +893,16 @@ function Build-FieldFragment {
|
||||
$lines += "$i`t<dataPath>$(Esc-Xml $parsed.dataPath)</dataPath>"
|
||||
$lines += "$i`t<field>$(Esc-Xml $parsed.field)</field>"
|
||||
|
||||
if ($parsed.title) {
|
||||
# Title: prefer raw multi-lang title (preserves en/uk/etc.). When shorthand provides
|
||||
# a new ru text, patch ru content inside the raw title; otherwise emit raw as-is.
|
||||
# When no raw title exists, fall back to ru-only build from shorthand.
|
||||
if ($parsed._rawTitle) {
|
||||
if ($parsed.title -and $parsed.title -ne $parsed._existingTitleRu) {
|
||||
$lines += "$i`t" + (Patch-MLTextRu $parsed._rawTitle $parsed.title "$i`t")
|
||||
} else {
|
||||
$lines += "$i`t" + $parsed._rawTitle
|
||||
}
|
||||
} elseif ($parsed.title) {
|
||||
$lines += (Build-MLTextXml -tag "title" -text $parsed.title -indent "$i`t")
|
||||
}
|
||||
|
||||
@@ -875,6 +923,14 @@ function Build-FieldFragment {
|
||||
$lines += "$i`t</valueType>"
|
||||
}
|
||||
|
||||
# Defense in depth: re-emit OuterXml of unknown children (e.g. <editFormat>,
|
||||
# <appearance>, custom extensions) that Read-FieldProperties captured.
|
||||
if ($parsed._unknownChildren) {
|
||||
foreach ($raw in $parsed._unknownChildren) {
|
||||
$lines += "$i`t" + $raw
|
||||
}
|
||||
}
|
||||
|
||||
$lines += "$i</field>"
|
||||
return $lines -join "`n"
|
||||
}
|
||||
@@ -1981,9 +2037,23 @@ switch ($Operation) {
|
||||
$existingTitle = $ch; break
|
||||
}
|
||||
}
|
||||
# If the existing title has multiple <v8:item> (multi-language: ru + en + …),
|
||||
# patch only the ru <v8:content> via raw-string surgery to preserve other langs.
|
||||
# Otherwise rebuild as ru-only fragment.
|
||||
$titleFrag = $null
|
||||
if ($existingTitle) {
|
||||
$rawTitle = $existingTitle.OuterXml
|
||||
$rawTitle = [regex]::Replace($rawTitle, ' xmlns(?::\w+)?="[^"]*"', '')
|
||||
# Count <v8:item> occurrences — if >1, treat as multi-lang.
|
||||
$itemCount = ([regex]::Matches($rawTitle, '<v8:item>')).Count
|
||||
if ($itemCount -gt 1) {
|
||||
$titleFrag = $childIndent + (Patch-MLTextRu $rawTitle $titleVal $childIndent)
|
||||
}
|
||||
Remove-NodeWithWhitespace $existingTitle
|
||||
}
|
||||
if (-not $titleFrag) {
|
||||
$titleFrag = Build-MLTextXml -tag "title" -text $titleVal -indent $childIndent
|
||||
}
|
||||
# Insert before first of (valueType, value, useRestriction, expression, availableAsField, ...)
|
||||
$titleRef = $null
|
||||
foreach ($ch in $paramEl.ChildNodes) {
|
||||
@@ -1991,7 +2061,6 @@ switch ($Operation) {
|
||||
$titleRef = $ch; break
|
||||
}
|
||||
}
|
||||
$titleFrag = Build-MLTextXml -tag "title" -text $titleVal -indent $childIndent
|
||||
$titleNodes = Import-Fragment $xmlDoc $titleFrag
|
||||
foreach ($node in $titleNodes) {
|
||||
Insert-BeforeElement $paramEl $node $titleRef $childIndent
|
||||
@@ -3077,6 +3146,11 @@ switch ($Operation) {
|
||||
# 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 }
|
||||
# Preserve raw multi-lang title; pass existing ru content for change detection.
|
||||
_rawTitle = $existing._rawTitle
|
||||
_existingTitleRu = $existing.title
|
||||
# Pass-through unknown children (e.g. <editFormat>, <appearance>, custom extensions).
|
||||
_unknownChildren = $existing._unknownChildren
|
||||
}
|
||||
|
||||
# Remember position (NextSibling after whitespace)
|
||||
@@ -3136,12 +3210,28 @@ switch ($Operation) {
|
||||
|
||||
$fieldIndent = Get-ChildIndent $fieldEl
|
||||
|
||||
# Remove existing <role>
|
||||
# Remove existing <role> — but first capture OuterXml of any sub-children that
|
||||
# Build-RoleXml won't re-emit (e.g. <dcscom:addition>, <dcscom:groupFields>,
|
||||
# custom extension elements). Preserved across rebuild.
|
||||
$oldRole = $null
|
||||
foreach ($ch in $fieldEl.ChildNodes) {
|
||||
if ($ch.NodeType -eq 'Element' -and $ch.LocalName -eq 'role' -and $ch.NamespaceURI -eq $schNs) { $oldRole = $ch; break }
|
||||
}
|
||||
if ($oldRole) { Remove-NodeWithWhitespace $oldRole }
|
||||
$knownRoleChildren = @('periodNumber','periodType','dimension','ignoreNullsInGroups','balance','account','accountTypeExpression','additionType','addition')
|
||||
$preservedRoleChildren = @()
|
||||
if ($oldRole) {
|
||||
foreach ($gc in $oldRole.ChildNodes) {
|
||||
if ($gc.NodeType -ne 'Element') { continue }
|
||||
if ($knownRoleChildren -contains $gc.LocalName) { continue }
|
||||
# kv keys override the same-named sub-element on rebuild — don't preserve
|
||||
# what the user explicitly set.
|
||||
if ($kv.Contains($gc.LocalName)) { continue }
|
||||
$raw = $gc.OuterXml
|
||||
$raw = [regex]::Replace($raw, ' xmlns(?::\w+)?="[^"]*"', '')
|
||||
$preservedRoleChildren += $raw
|
||||
}
|
||||
Remove-NodeWithWhitespace $oldRole
|
||||
}
|
||||
|
||||
# Empty spec — remove only
|
||||
if ($flags.Count -eq 0 -and $kv.Count -eq 0) {
|
||||
@@ -3163,6 +3253,9 @@ switch ($Operation) {
|
||||
foreach ($k in $kv.Keys) {
|
||||
$lines += "$fieldIndent`t<dcscom:$k>$(Esc-Xml $kv[$k])</dcscom:$k>"
|
||||
}
|
||||
foreach ($raw in $preservedRoleChildren) {
|
||||
$lines += "$fieldIndent`t" + $raw
|
||||
}
|
||||
$lines += "$fieldIndent</role>"
|
||||
$fragXml = $lines -join "`n"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# skd-edit v1.20 — Atomic 1C DCS editor (Python port)
|
||||
# skd-edit v1.21 — Atomic 1C DCS editor (Python port)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import os
|
||||
@@ -214,7 +214,8 @@ def parse_field_shorthand(s):
|
||||
|
||||
|
||||
def read_field_properties(field_el):
|
||||
props = {"dataPath": "", "field": "", "title": "", "type": "", "roles": [], "restrict": [], "_rawTypeText": ""}
|
||||
props = {"dataPath": "", "field": "", "title": "", "type": "", "roles": [], "restrict": [], "_rawTypeText": "",
|
||||
"_rawTitle": None, "_unknownChildren": []}
|
||||
|
||||
for ch in field_el:
|
||||
if not isinstance(ch.tag, str):
|
||||
@@ -225,17 +226,27 @@ def read_field_properties(field_el):
|
||||
elif ln == "field":
|
||||
props["field"] = (ch.text or "").strip()
|
||||
elif ln == "title":
|
||||
# Preserve full multi-lang title OuterXml; also extract ru content for compat.
|
||||
raw = etree.tostring(ch, encoding="unicode", with_tail=False)
|
||||
raw = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw)
|
||||
props["_rawTitle"] = raw
|
||||
for item in ch:
|
||||
if isinstance(item.tag, str) and local_name(item) == "item":
|
||||
lang = None
|
||||
content = None
|
||||
for gc in item:
|
||||
if isinstance(gc.tag, str) and local_name(gc) == "lang":
|
||||
lang = (gc.text or "").strip()
|
||||
if isinstance(gc.tag, str) and local_name(gc) == "content":
|
||||
props["title"] = (gc.text or "").strip()
|
||||
content = (gc.text or "").strip()
|
||||
if lang == "ru" and content is not None:
|
||||
props["title"] = content
|
||||
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 = etree.tostring(ch, encoding="unicode", with_tail=False)
|
||||
raw = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw)
|
||||
props["_rawValueType"] = raw
|
||||
for gc in ch:
|
||||
@@ -257,6 +268,12 @@ def read_field_properties(field_el):
|
||||
mapped = rev_map.get(local_name(gc))
|
||||
if mapped:
|
||||
props["restrict"].append(mapped)
|
||||
else:
|
||||
# Defense in depth: preserve OuterXml of unknown children so rebuild
|
||||
# doesn't silently drop them (custom <editFormat>, <appearance>, etc.).
|
||||
raw = etree.tostring(ch, encoding="unicode", with_tail=False)
|
||||
raw = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw)
|
||||
props["_unknownChildren"].append(raw)
|
||||
return props
|
||||
|
||||
|
||||
@@ -751,6 +768,19 @@ def build_mltext_xml(tag, text, indent):
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def patch_mltext_ru(raw_outer_xml, new_ru_text, indent):
|
||||
"""Patch the ru <v8:content> within an existing multi-lang title OuterXml,
|
||||
preserving en/uk/etc. siblings. Mirrors PS Patch-MLTextRu."""
|
||||
escaped = esc_xml(new_ru_text)
|
||||
ru_item_pat = r"(<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>)[^<]*(</v8:content>\s*</v8:item>)"
|
||||
if re.search(ru_item_pat, raw_outer_xml):
|
||||
return re.sub(ru_item_pat, lambda m: m.group(1) + escaped + m.group(2), raw_outer_xml)
|
||||
prep = f"{indent}\t<v8:item>\n{indent}\t\t<v8:lang>ru</v8:lang>\n{indent}\t\t<v8:content>{escaped}</v8:content>\n{indent}\t</v8:item>"
|
||||
if "<v8:item>" in raw_outer_xml:
|
||||
return re.sub(r"(\s*)<v8:item>", lambda m: "\n" + prep + m.group(1) + "<v8:item>", raw_outer_xml, count=1)
|
||||
return re.sub(r"(<(?:\w+:)?title[^>]*>)", lambda m: m.group(1) + "\n" + prep + "\n" + indent, raw_outer_xml, count=1)
|
||||
|
||||
|
||||
def build_role_xml(roles, indent):
|
||||
if not roles:
|
||||
return ""
|
||||
@@ -784,7 +814,15 @@ def build_field_fragment(parsed, indent):
|
||||
lines.append(f"{i}\t<dataPath>{esc_xml(parsed['dataPath'])}</dataPath>")
|
||||
lines.append(f"{i}\t<field>{esc_xml(parsed['field'])}</field>")
|
||||
|
||||
if parsed.get("title"):
|
||||
# Title: prefer raw multi-lang OuterXml (preserves en/uk/etc.). When shorthand
|
||||
# provides a new ru text different from existing, patch the ru content. Otherwise
|
||||
# emit raw as-is or build ru-only from shorthand if there was no prior title.
|
||||
if parsed.get("_rawTitle"):
|
||||
if parsed.get("title") and parsed["title"] != parsed.get("_existingTitleRu"):
|
||||
lines.append(f"{i}\t" + patch_mltext_ru(parsed["_rawTitle"], parsed["title"], f"{i}\t"))
|
||||
else:
|
||||
lines.append(f"{i}\t" + parsed["_rawTitle"])
|
||||
elif parsed.get("title"):
|
||||
lines.append(build_mltext_xml("title", parsed["title"], f"{i}\t"))
|
||||
|
||||
if parsed.get("restrict"):
|
||||
@@ -803,6 +841,10 @@ def build_field_fragment(parsed, indent):
|
||||
lines.append(build_value_type_xml(parsed["type"], f"{i}\t\t"))
|
||||
lines.append(f"{i}\t</valueType>")
|
||||
|
||||
# Defense in depth: re-emit OuterXml of unknown children captured by Read.
|
||||
for raw in (parsed.get("_unknownChildren") or []):
|
||||
lines.append(f"{i}\t" + raw)
|
||||
|
||||
lines.append(f"{i}</field>")
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -1720,11 +1762,19 @@ elif operation == "modify-parameter":
|
||||
# Set/replace title (must come right after <name>, before <valueType>)
|
||||
if title_val is not None:
|
||||
existing_title = next((ch for ch in param_el if isinstance(ch.tag, str) and local_name(ch) == "title"), None)
|
||||
# If existing title is multi-lang (has >1 <v8:item>), patch ru content
|
||||
# while preserving other languages. Otherwise rebuild as ru-only.
|
||||
title_frag = None
|
||||
if existing_title is not None:
|
||||
raw_title = etree.tostring(existing_title, encoding="unicode", with_tail=False)
|
||||
raw_title = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw_title)
|
||||
if raw_title.count("<v8:item>") > 1:
|
||||
title_frag = child_indent + patch_mltext_ru(raw_title, title_val, child_indent)
|
||||
remove_node_with_whitespace(existing_title)
|
||||
if title_frag is None:
|
||||
title_frag = build_mltext_xml("title", title_val, child_indent)
|
||||
# Insert before the first child after <name>
|
||||
title_ref = next((ch for ch in param_el if isinstance(ch.tag, str) and local_name(ch) != "name"), None)
|
||||
title_frag = build_mltext_xml("title", title_val, child_indent)
|
||||
for node in import_fragment(xml_doc, title_frag):
|
||||
insert_before_element(param_el, node, title_ref, child_indent)
|
||||
dirty = True; print(f'[OK] Parameter "{param_name}": title set to "{title_val}"')
|
||||
@@ -2548,6 +2598,11 @@ elif operation == "modify-field":
|
||||
"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"),
|
||||
# Preserve raw multi-lang title; pass existing ru content for change detection.
|
||||
"_rawTitle": existing.get("_rawTitle"),
|
||||
"_existingTitleRu": existing.get("title"),
|
||||
# Pass-through unknown children (e.g. <editFormat>, <appearance>, custom extensions).
|
||||
"_unknownChildren": existing.get("_unknownChildren"),
|
||||
}
|
||||
|
||||
# Find next element sibling for position
|
||||
@@ -2600,9 +2655,25 @@ elif operation == "set-field-role":
|
||||
|
||||
field_indent = get_child_indent(field_el)
|
||||
|
||||
# Remove existing <role>
|
||||
# Remove existing <role> — but first capture OuterXml of sub-children that the
|
||||
# rebuild won't re-emit (custom <dcscom:groupFields>, <dcscom:addition>, etc.).
|
||||
old_role = next((ch for ch in field_el if isinstance(ch.tag, str) and local_name(ch) == "role" and etree.QName(ch.tag).namespace == SCH_NS), None)
|
||||
known_role_children = {"periodNumber", "periodType", "dimension", "ignoreNullsInGroups",
|
||||
"balance", "account", "accountTypeExpression", "additionType", "addition"}
|
||||
kv_keys = {k for k, _ in kv}
|
||||
preserved_role_children = []
|
||||
if old_role is not None:
|
||||
for gc in old_role:
|
||||
if not isinstance(gc.tag, str):
|
||||
continue
|
||||
ln = local_name(gc)
|
||||
if ln in known_role_children:
|
||||
continue
|
||||
if ln in kv_keys:
|
||||
continue
|
||||
raw = etree.tostring(gc, encoding="unicode", with_tail=False)
|
||||
raw = re.sub(r' xmlns(?::\w+)?="[^"]*"', "", raw)
|
||||
preserved_role_children.append(raw)
|
||||
remove_node_with_whitespace(old_role)
|
||||
|
||||
# Empty spec — remove only
|
||||
@@ -2620,6 +2691,8 @@ elif operation == "set-field-role":
|
||||
lines.append(f"{field_indent}\t<dcscom:{flag}>true</dcscom:{flag}>")
|
||||
for k, v in kv:
|
||||
lines.append(f"{field_indent}\t<dcscom:{k}>{esc_xml(v)}</dcscom:{k}>")
|
||||
for raw in preserved_role_children:
|
||||
lines.append(f"{field_indent}\t" + raw)
|
||||
lines.append(f"{field_indent}</role>")
|
||||
frag_xml = "\n".join(lines)
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<editFormat xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>L=ru_RU</v8:content>
|
||||
</v8:item>
|
||||
</editFormat>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "modify-field сохраняет en/uk title (multi-lang) при ru-override",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-field",
|
||||
"value": "Контрагент [Имя контрагента]"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "modify-parameter сохраняет en title (multi-lang) при ru-override",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-parameter",
|
||||
"value": "Период [Новый период]"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "modify-field сохраняет неизвестные дочерние элементы (editFormat, appearance и т.п.)",
|
||||
"setup": "fixture:multilang-base",
|
||||
"params": {
|
||||
"templatePath": "Template.xml",
|
||||
"operation": "modify-field",
|
||||
"value": "Контрагент @ignoreNullsInGroups"
|
||||
},
|
||||
"idempotent": true
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Имя контрагента</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<editFormat xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>L=ru_RU</v8:content>
|
||||
</v8:item>
|
||||
</editFormat>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:dimension>true</dcscom:dimension>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<editFormat xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>L=ru_RU</v8:content>
|
||||
</v8:item>
|
||||
</editFormat>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Новый период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
|
||||
xmlns:dcscom="http://v8.1c.ru/8.1/data-composition-system/common"
|
||||
xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core"
|
||||
xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings"
|
||||
xmlns:v8="http://v8.1c.ru/8.1/data/core"
|
||||
xmlns:v8ui="http://v8.1c.ru/8.1/data/ui"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<dataSource>
|
||||
<name>ИсточникДанных1</name>
|
||||
<dataSourceType>Local</dataSourceType>
|
||||
</dataSource>
|
||||
<dataSet xsi:type="DataSetQuery">
|
||||
<name>DS</name>
|
||||
<field xsi:type="DataSetFieldField">
|
||||
<dataPath>Контрагент</dataPath>
|
||||
<field>Контрагент</field>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Контрагент</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Counterparty</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<role>
|
||||
<dcscom:ignoreNullsInGroups>true</dcscom:ignoreNullsInGroups>
|
||||
</role>
|
||||
<valueType>
|
||||
<v8:Type>xs:string</v8:Type>
|
||||
<v8:StringQualifiers>
|
||||
<v8:Length>100</v8:Length>
|
||||
<v8:AllowedLength>Variable</v8:AllowedLength>
|
||||
</v8:StringQualifiers>
|
||||
</valueType>
|
||||
<editFormat xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>L=ru_RU</v8:content>
|
||||
</v8:item>
|
||||
</editFormat>
|
||||
</field>
|
||||
<dataSource>ИсточникДанных1</dataSource>
|
||||
<query>ВЫБРАТЬ 1 КАК Контрагент</query>
|
||||
</dataSet>
|
||||
<parameter>
|
||||
<name>Период</name>
|
||||
<title xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Период</v8:content>
|
||||
</v8:item>
|
||||
<v8:item>
|
||||
<v8:lang>en</v8:lang>
|
||||
<v8:content>Period</v8:content>
|
||||
</v8:item>
|
||||
</title>
|
||||
<valueType>
|
||||
<v8:Type>xs:dateTime</v8:Type>
|
||||
<v8:DateQualifiers>
|
||||
<v8:DateFractions>DateTime</v8:DateFractions>
|
||||
</v8:DateQualifiers>
|
||||
</valueType>
|
||||
</parameter>
|
||||
<settingsVariant>
|
||||
<dcsset:name>Основной</dcsset:name>
|
||||
<dcsset:presentation xsi:type="v8:LocalStringType">
|
||||
<v8:item>
|
||||
<v8:lang>ru</v8:lang>
|
||||
<v8:content>Основной</v8:content>
|
||||
</v8:item>
|
||||
</dcsset:presentation>
|
||||
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
|
||||
<dcsset:item xsi:type="dcsset:StructureItemGroup">
|
||||
<dcsset:order>
|
||||
<dcsset:item xsi:type="dcsset:OrderItemAuto"/>
|
||||
</dcsset:order>
|
||||
<dcsset:selection>
|
||||
<dcsset:item xsi:type="dcsset:SelectedItemAuto"/>
|
||||
</dcsset:selection>
|
||||
</dcsset:item>
|
||||
</dcsset:settings>
|
||||
</settingsVariant>
|
||||
</DataCompositionSchema>
|
||||
Reference in New Issue
Block a user