From 79db5de6ee1131ec1d56895207b1e8c67a19f656 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Tue, 19 May 2026 14:08:24 +0300 Subject: [PATCH] fix(skd-edit): preserve multi-lang title + unknown children in modify-* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В типовых конфигурациях (ERP, БП, ЗУП и т.д.) у полей и параметров обычно есть мульти-язык title (ru + en, иногда + локализация). До этого modify-field / modify-parameter / modify-dataParameter, перестраивая элемент через Build-MLTextXml, оставляли только последнее найденное в ru — en/uk/kk siblings молча терялись, и при следующей выгрузке Designer ломал миграцию. Read-FieldProperties сохраняет полный OuterXml в _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> --- .claude/skills/skd-edit/scripts/skd-edit.ps1 | 111 ++++++++++++++++-- .claude/skills/skd-edit/scripts/skd-edit.py | 87 ++++++++++++-- .../fixtures/multilang-base/Template.xml | 87 ++++++++++++++ .../preserve-multilang-modify-field.json | 10 ++ .../preserve-multilang-modify-parameter.json | 10 ++ ...reserve-unknown-children-modify-field.json | 10 ++ .../Template.xml | 87 ++++++++++++++ .../Template.xml | 87 ++++++++++++++ .../Template.xml | 87 ++++++++++++++ 9 files changed, 560 insertions(+), 16 deletions(-) create mode 100644 tests/skills/cases/skd-edit/fixtures/multilang-base/Template.xml create mode 100644 tests/skills/cases/skd-edit/preserve-multilang-modify-field.json create mode 100644 tests/skills/cases/skd-edit/preserve-multilang-modify-parameter.json create mode 100644 tests/skills/cases/skd-edit/preserve-unknown-children-modify-field.json create mode 100644 tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-field/Template.xml create mode 100644 tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-parameter/Template.xml create mode 100644 tests/skills/cases/skd-edit/snapshots/preserve-unknown-children-modify-field/Template.xml diff --git a/.claude/skills/skd-edit/scripts/skd-edit.ps1 b/.claude/skills/skd-edit/scripts/skd-edit.ps1 index 784264a4..773f2970 100644 --- a/.claude/skills/skd-edit/scripts/skd-edit.ps1 +++ b/.claude/skills/skd-edit/scripts/skd-edit.ps1 @@ -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" diff --git a/.claude/skills/skd-edit/scripts/skd-edit.py b/.claude/skills/skd-edit/scripts/skd-edit.py index a1e0d2e5..6030cf37 100644 --- a/.claude/skills/skd-edit/scripts/skd-edit.py +++ b/.claude/skills/skd-edit/scripts/skd-edit.py @@ -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) diff --git a/tests/skills/cases/skd-edit/fixtures/multilang-base/Template.xml b/tests/skills/cases/skd-edit/fixtures/multilang-base/Template.xml new file mode 100644 index 00000000..bcf4633d --- /dev/null +++ b/tests/skills/cases/skd-edit/fixtures/multilang-base/Template.xml @@ -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> + + + true + + + xs:string + + 100 + Variable + + + + + ru + L=ru_RU + + + + ИсточникДанных1 + ВЫБРАТЬ 1 КАК Контрагент + + + Период + + <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> + + + xs:dateTime + + DateTime + + + + + Основной + + + ru + Основной + + + + + + + + + + + + + + diff --git a/tests/skills/cases/skd-edit/preserve-multilang-modify-field.json b/tests/skills/cases/skd-edit/preserve-multilang-modify-field.json new file mode 100644 index 00000000..6bd464e3 --- /dev/null +++ b/tests/skills/cases/skd-edit/preserve-multilang-modify-field.json @@ -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 +} diff --git a/tests/skills/cases/skd-edit/preserve-multilang-modify-parameter.json b/tests/skills/cases/skd-edit/preserve-multilang-modify-parameter.json new file mode 100644 index 00000000..672cbb92 --- /dev/null +++ b/tests/skills/cases/skd-edit/preserve-multilang-modify-parameter.json @@ -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 +} diff --git a/tests/skills/cases/skd-edit/preserve-unknown-children-modify-field.json b/tests/skills/cases/skd-edit/preserve-unknown-children-modify-field.json new file mode 100644 index 00000000..62a86b55 --- /dev/null +++ b/tests/skills/cases/skd-edit/preserve-unknown-children-modify-field.json @@ -0,0 +1,10 @@ +{ + "name": "modify-field сохраняет неизвестные дочерние элементы (editFormat, appearance и т.п.)", + "setup": "fixture:multilang-base", + "params": { + "templatePath": "Template.xml", + "operation": "modify-field", + "value": "Контрагент @ignoreNullsInGroups" + }, + "idempotent": true +} diff --git a/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-field/Template.xml b/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-field/Template.xml new file mode 100644 index 00000000..014403b1 --- /dev/null +++ b/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-field/Template.xml @@ -0,0 +1,87 @@ + + + + ИсточникДанных1 + Local + + + DS + + Контрагент + Контрагент + + <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> + + + true + + + xs:string + + 100 + Variable + + + + + ru + L=ru_RU + + + + ИсточникДанных1 + ВЫБРАТЬ 1 КАК Контрагент + + + Период + + <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> + + + xs:dateTime + + DateTime + + + + + Основной + + + ru + Основной + + + + + + + + + + + + + + diff --git a/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-parameter/Template.xml b/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-parameter/Template.xml new file mode 100644 index 00000000..e1444c18 --- /dev/null +++ b/tests/skills/cases/skd-edit/snapshots/preserve-multilang-modify-parameter/Template.xml @@ -0,0 +1,87 @@ + + + + ИсточникДанных1 + Local + + + DS + + Контрагент + Контрагент + + <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> + + + true + + + xs:string + + 100 + Variable + + + + + ru + L=ru_RU + + + + ИсточникДанных1 + ВЫБРАТЬ 1 КАК Контрагент + + + Период + + <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> + + + xs:dateTime + + DateTime + + + + + Основной + + + ru + Основной + + + + + + + + + + + + + + diff --git a/tests/skills/cases/skd-edit/snapshots/preserve-unknown-children-modify-field/Template.xml b/tests/skills/cases/skd-edit/snapshots/preserve-unknown-children-modify-field/Template.xml new file mode 100644 index 00000000..55194c4c --- /dev/null +++ b/tests/skills/cases/skd-edit/snapshots/preserve-unknown-children-modify-field/Template.xml @@ -0,0 +1,87 @@ + + + + ИсточникДанных1 + Local + + + DS + + Контрагент + Контрагент + + <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> + + + true + + + xs:string + + 100 + Variable + + + + + ru + L=ru_RU + + + + ИсточникДанных1 + ВЫБРАТЬ 1 КАК Контрагент + + + Период + + <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> + + + xs:dateTime + + DateTime + + + + + Основной + + + ru + Основной + + + + + + + + + + + + + +