From 706cc919a20440cab4ecec1122a0f9e96ff714b3 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Fri, 10 Jul 2026 20:09:36 +0300 Subject: [PATCH] =?UTF-8?q?feat(meta-compile,meta-decompile):=20HE-=D1=8D?= =?UTF-8?q?=D0=B2=D1=80=D0=B8=D1=81=D1=82=D0=B8=D0=BA=D0=B0=20=D0=B0=D0=B1?= =?UTF-8?q?=D0=B1=D1=80=D0=B5=D0=B2=D0=B8=D0=B0=D1=82=D1=83=D1=80=20=D0=B2?= =?UTF-8?q?=20=D0=B0=D0=B2=D1=82=D0=BE-=D1=81=D0=B8=D0=BD=D0=BE=D0=BD?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=20+=20=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D1=87=D1=83=D0=B2=D1=81=D1=82=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=B0=D0=B2=D1=82=D0=BE?= =?UTF-8?q?-=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20(v1.61/v0.52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Корень: split_camel_case слепо лоуэркейзил хвост, теряя аббревиатуры (НДС/ЕГАИС/ОС/ЭП), которые платформа сохраняет. Вскрыто py-раундтрипом на маке (АбонентыЭДО: синоним "Абоненты ЭДО" регенерился как "эдо"). Дважды замаскировано: декомпилятор опускал синоним регистронезависимо (ne_ci), харнес Compare-Object был без -CaseSensitive. Масштаб survey: 7706. Фикс (правило подтверждено эмпирически по корпусу): сохранять максимальный прогон заглавных >=2, если сразу за ним НЕ буква (пробел/цифра/спецсимвол/конец). ">=2" ловит ОС/ЭП; граница "не буква" отсекает предлоги (РасчетыСКлиентами->склиентами) и бренды (ЮКасса). - Компилятор Split-CamelCase -> HE (ps1+py), радиус = авто-синонимы + авто-описания предопределённых. - Декомпилятор Split-CamelWords -> HE (зеркало) + регистрочувствительная страховка (-ne->-cne / ne_ci->ne_cs) в 9 точках: кастом-синонимы (ВетИС/МИР) эмитятся дословно. Валидация 8.3.24 (1765): синонимы/описания закрыты полностью, ноль case-диффов. Декомпилятор ps1==py 189/189. Регресс 511/511 ps+py. spec §2. Снэпшоты meta-compile — казинг аббревиатур. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../meta-compile/scripts/meta-compile.ps1 | 29 +++++++++- .../meta-compile/scripts/meta-compile.py | 32 ++++++++++- .../meta-decompile/scripts/meta-decompile.ps1 | 46 +++++++++++---- .../meta-decompile/scripts/meta-decompile.py | 57 ++++++++++++++----- docs/meta-dsl-spec.md | 18 ++++-- .../Catalogs/ТестВыбора.xml | 2 +- .../Catalogs/Контрагенты.xml | 2 +- .../Catalogs/ТестХарактеристик.xml | 2 +- .../Catalogs/Контрагенты.xml | 2 +- .../constant-full/Constants/СтавкаНДС.xml | 2 +- .../http-service/HTTPServices/ТоварныйAPI.xml | 2 +- 11 files changed, 153 insertions(+), 41 deletions(-) diff --git a/.claude/skills/meta-compile/scripts/meta-compile.ps1 b/.claude/skills/meta-compile/scripts/meta-compile.ps1 index 7332905b..c51b5db2 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.ps1 +++ b/.claude/skills/meta-compile/scripts/meta-compile.ps1 @@ -1,4 +1,4 @@ -# meta-compile v1.60 — Compile 1C metadata object from JSON +# meta-compile v1.61 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [Parameter(Mandatory)] @@ -421,9 +421,32 @@ function Split-CamelCase { # Insert space before uppercase that follows lowercase (Cyrillic + Latin) $result = [regex]::Replace($name, '([а-яё])([А-ЯЁ])', '$1 $2') $result = [regex]::Replace($result, '([a-z])([A-Z])', '$1 $2') - # Lowercase all but first character of the result + # Лоуэркейзим хвост, СОХРАНЯЯ аббревиатуры (зеркало эвристики платформы): максимальный прогон + # заглавных длиной >=2, если сразу за ним НЕ буква (пробел/цифра/спецсимвол/конец) — остаётся заглавным + # (НДС, ЕГАИС, ОС, ЭП). Прилипшие предлоги (СКлиентами) и бренды (ЮКасса) идут перед буквой → лоуэркейз. + # Первый символ строки — как есть. if ($result.Length -gt 1) { - $result = $result.Substring(0,1) + $result.Substring(1).ToLower() + $chars = $result.ToCharArray() + $n = $chars.Length + $keep = New-Object 'bool[]' $n + $i = 0 + while ($i -lt $n) { + if ([char]::IsUpper($chars[$i])) { + $j = $i + while ($j -lt $n -and [char]::IsUpper($chars[$j])) { $j++ } + $afterBoundary = ($j -eq $n) -or (-not [char]::IsLetter($chars[$j])) + if (($j - $i) -ge 2 -and $afterBoundary) { for ($k = $i; $k -lt $j; $k++) { $keep[$k] = $true } } + $i = $j + } else { $i++ } + } + $sb = New-Object System.Text.StringBuilder + for ($idx = 0; $idx -lt $n; $idx++) { + $c = $chars[$idx] + if ($idx -eq 0 -or $keep[$idx]) { [void]$sb.Append($c) } + elseif ([char]::IsUpper($c)) { [void]$sb.Append([char]::ToLower($c)) } + else { [void]$sb.Append($c) } + } + $result = $sb.ToString() } return $result } diff --git a/.claude/skills/meta-compile/scripts/meta-compile.py b/.claude/skills/meta-compile/scripts/meta-compile.py index 7888dbc3..c93914cb 100644 --- a/.claude/skills/meta-compile/scripts/meta-compile.py +++ b/.claude/skills/meta-compile/scripts/meta-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-compile v1.60 — Compile 1C metadata object from JSON +# meta-compile v1.61 — Compile 1C metadata object from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -233,8 +233,36 @@ def split_camel_case(name): return name result = re.sub(r'([а-яё])([А-ЯЁ])', r'\1 \2', name) result = re.sub(r'([a-z])([A-Z])', r'\1 \2', result) + # Лоуэркейзим хвост, СОХРАНЯЯ аббревиатуры (зеркало эвристики платформы): максимальный прогон + # заглавных длиной >=2, если сразу за ним НЕ буква (пробел/цифра/спецсимвол/конец) — остаётся заглавным + # (НДС, ЕГАИС, ОС, ЭП). Прилипшие предлоги (СКлиентами)/бренды (ЮКасса) идут перед буквой → лоуэркейз. + # Первый символ строки — как есть. if len(result) > 1: - result = result[0] + result[1:].lower() + chars = list(result) + n = len(chars) + keep = [False] * n + i = 0 + while i < n: + if chars[i].isupper(): + j = i + while j < n and chars[j].isupper(): + j += 1 + after_boundary = (j == n) or (not chars[j].isalpha()) + if j - i >= 2 and after_boundary: + for k in range(i, j): + keep[k] = True + i = j + else: + i += 1 + out = [] + for idx, c in enumerate(chars): + if idx == 0 or keep[idx]: + out.append(c) + elif c.isupper(): + out.append(c.lower()) + else: + out.append(c) + result = ''.join(out) return result # --------------------------------------------------------------------------- diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 index 373236d7..50ee1fb0 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.ps1 @@ -1,4 +1,4 @@ -# meta-decompile v0.51 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.52 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Поддержаны: Catalog, ExchangePlan, ChartOfCharacteristicTypes, ChartOfAccounts, ChartOfCalculationTypes, Document, @@ -135,7 +135,31 @@ function Split-CamelWords { if (-not $name) { return $name } $result = [regex]::Replace($name, '([а-яё])([А-ЯЁ])', '$1 $2') $result = [regex]::Replace($result, '([a-z])([A-Z])', '$1 $2') - if ($result.Length -gt 1) { $result = $result.Substring(0,1) + $result.Substring(1).ToLower() } + # HE-эвристика (зеркало Split-CamelCase компилятора): сохраняем прогон заглавных >=2, если сразу за ним + # НЕ буква (пробел/цифра/спецсимвол/конец); прилипшие предлоги/бренды перед буквой → лоуэркейз. Первый символ — как есть. + if ($result.Length -gt 1) { + $chars = $result.ToCharArray() + $n = $chars.Length + $keep = New-Object 'bool[]' $n + $i = 0 + while ($i -lt $n) { + if ([char]::IsUpper($chars[$i])) { + $j = $i + while ($j -lt $n -and [char]::IsUpper($chars[$j])) { $j++ } + $afterBoundary = ($j -eq $n) -or (-not [char]::IsLetter($chars[$j])) + if (($j - $i) -ge 2 -and $afterBoundary) { for ($k = $i; $k -lt $j; $k++) { $keep[$k] = $true } } + $i = $j + } else { $i++ } + } + $sb = New-Object System.Text.StringBuilder + for ($idx = 0; $idx -lt $n; $idx++) { + $c = $chars[$idx] + if ($idx -eq 0 -or $keep[$idx]) { [void]$sb.Append($c) } + elseif ([char]::IsUpper($c)) { [void]$sb.Append([char]::ToLower($c)) } + else { [void]$sb.Append($c) } + } + $result = $sb.ToString() + } return $result } @@ -271,7 +295,7 @@ function Attr-ToDsl { # Пустой (узел есть, значения нет) ≠ авто-синоним из имени → явный пустой (synonym:""). # У Catalog-реквизитов не встречается (0/4018), у части ExchangePlan — да. $synEmpty = ($synNode -and $null -eq $synVal) - if ($synVal -is [string]) { if ($synVal -ne (Split-CamelWords $nm)) { $synCustom = $true } } + if ($synVal -is [string]) { if ($synVal -cne (Split-CamelWords $nm)) { $synCustom = $true } } elseif ($null -ne $synVal) { $synCustom = $true } # {ru,en} = всегда кастом $ttVal = Get-MLValue ($ap.SelectSingleNode('md:ToolTip', $nsm)) @@ -395,7 +419,7 @@ $dsl = [ordered]@{ type = $objType; name = $objName } # Пустой (узел есть, значения нет) ≠ авто-синоним из имени → явный synonym:"" (иначе компилятор до-генерит из имени). $synNodeObj = $props.SelectSingleNode('md:Synonym', $nsm) $synVal = Get-MLValue $synNodeObj -if ($synVal -is [string]) { if ($synVal -ne (Split-CamelWords $objName)) { $dsl['synonym'] = $synVal } } +if ($synVal -is [string]) { if ($synVal -cne (Split-CamelWords $objName)) { $dsl['synonym'] = $synVal } } elseif ($null -ne $synVal) { $dsl['synonym'] = $synVal } elseif ($synNodeObj) { $dsl['synonym'] = '' } $cmt = P 'Comment'; if ($cmt) { $dsl['comment'] = $cmt } @@ -1101,7 +1125,7 @@ if ($childObjs) { # $evSynVal: строка ≠ авто → кастом; {ru,en} → кастом; пустой `` (node есть, значения нет) → # явный '' (≠ авто-синоним из имени, аналог object-level фикса); авто/отсутствие → $null (короткая строка). $evSynVal = $null - if ($evSyn -is [string]) { if ($evSyn -ne (Split-CamelWords $evName)) { $evSynVal = $evSyn } } + if ($evSyn -is [string]) { if ($evSyn -cne (Split-CamelWords $evName)) { $evSynVal = $evSyn } } elseif ($null -ne $evSyn) { $evSynVal = $evSyn } elseif ($evSynNode) { $evSynVal = '' } if (($null -ne $evSynVal) -or $evCmt) { @@ -1126,7 +1150,7 @@ if ($childObjs) { $o = [ordered]@{ name = $cName } $cSynNode = $cp.SelectSingleNode('md:Synonym', $nsm) $cSyn = Get-MLValue $cSynNode - if ($cSyn -is [string]) { if ($cSyn -ne (Split-CamelWords $cName)) { $o['synonym'] = $cSyn } } + if ($cSyn -is [string]) { if ($cSyn -cne (Split-CamelWords $cName)) { $o['synonym'] = $cSyn } } elseif ($null -ne $cSyn) { $o['synonym'] = $cSyn } elseif ($cSynNode) { $o['synonym'] = '' } # пустой ≠ авто-синоним → явный '' $cCmtN = $cp.SelectSingleNode('md:Comment', $nsm); if ($cCmtN -and $cCmtN.InnerText) { $o['comment'] = $cCmtN.InnerText } @@ -1163,7 +1187,7 @@ if ($childObjs) { $dName = ($dp.SelectSingleNode('md:Name', $nsm)).InnerText $o = [ordered]@{ name = $dName } $dSyn = Get-MLValue ($dp.SelectSingleNode('md:Synonym', $nsm)) - if ($dSyn -is [string]) { if ($dSyn -ne (Split-CamelWords $dName)) { $o['synonym'] = $dSyn } } + if ($dSyn -is [string]) { if ($dSyn -cne (Split-CamelWords $dName)) { $o['synonym'] = $dSyn } } elseif ($null -ne $dSyn) { $o['synonym'] = $dSyn } $dCmtN = $dp.SelectSingleNode('md:Comment', $nsm); if ($dCmtN -and $dCmtN.InnerText) { $o['comment'] = $dCmtN.InnerText } $dt = Get-TypeShorthand ($dp.SelectSingleNode('md:Type', $nsm)); if ($dt) { $o['type'] = $dt } @@ -1207,7 +1231,7 @@ if ($childObjs) { # Синоним/подсказка/комментарий ТЧ. Кастом → объектная форма {synonym?, tooltip?, comment?, attributes}. $tsSyn = Get-MLValue ($tsp.SelectSingleNode('md:Synonym', $nsm)) $tsSynCustom = $false - if ($tsSyn -is [string]) { if ($tsSyn -ne (Split-CamelWords $tsName)) { $tsSynCustom = $true } } + if ($tsSyn -is [string]) { if ($tsSyn -cne (Split-CamelWords $tsName)) { $tsSynCustom = $true } } elseif ($null -ne $tsSyn) { $tsSynCustom = $true } $tsTt = Get-MLValue ($tsp.SelectSingleNode('md:ToolTip', $nsm)) $tsCmtN = $tsp.SelectSingleNode('md:Comment', $nsm); $tsCmt = if ($tsCmtN) { $tsCmtN.InnerText } else { '' } @@ -1260,7 +1284,7 @@ if ($childObjs) { $cn = ($cp.SelectSingleNode('md:Name', $nsm)).InnerText $o = [ordered]@{} $syn = Get-MLValue ($cp.SelectSingleNode('md:Synonym', $nsm)) - if ($syn -is [string]) { if ($syn -ne (Split-CamelWords $cn)) { $o['synonym'] = $syn } } + if ($syn -is [string]) { if ($syn -cne (Split-CamelWords $cn)) { $o['synonym'] = $syn } } elseif ($null -ne $syn) { $o['synonym'] = $syn } $cmtN = $cp.SelectSingleNode('md:Comment', $nsm); if ($cmtN -and $cmtN.InnerText) { $o['comment'] = $cmtN.InnerText } $grpN = $cp.SelectSingleNode('md:Group', $nsm); if ($grpN -and $grpN.InnerText) { $o['group'] = $grpN.InnerText } @@ -1328,7 +1352,7 @@ if (Test-Path -LiteralPath $predefPath) { if (-not $isFolder -and $kids.Count -eq 0 -and ($null -eq $typeStr -or $typeStr -ne '')) { $s = if ($code) { "($code) $name" } else { $name } if ($desc -eq '') { $s = "$s []" } - elseif ($desc -ne $auto) { $s = "$s [$desc]" } + elseif ($desc -cne $auto) { $s = "$s [$desc]" } if ($typeStr) { $s = "${s}: $typeStr" } return $s } @@ -1336,7 +1360,7 @@ if (Test-Path -LiteralPath $predefPath) { $o = [ordered]@{ name = $name } if ($code) { $o['code'] = $code } if ($desc -eq '') { $o['description'] = '' } - elseif ($desc -ne $auto) { $o['description'] = $desc } + elseif ($desc -cne $auto) { $o['description'] = $desc } if ($null -ne $typeStr) { $o['type'] = $typeStr } if ($isFolder) { $o['isFolder'] = $true } if ($kids.Count -gt 0) { diff --git a/.claude/skills/meta-decompile/scripts/meta-decompile.py b/.claude/skills/meta-decompile/scripts/meta-decompile.py index 5b465589..cfe05ca6 100644 --- a/.claude/skills/meta-decompile/scripts/meta-decompile.py +++ b/.claude/skills/meta-decompile/scripts/meta-decompile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-decompile v0.51 — XML объекта метаданных 1С → JSON-черновик формата meta-compile +# meta-decompile v0.52 — XML объекта метаданных 1С → JSON-черновик формата meta-compile # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # # Зеркало meta-decompile.ps1 (КАНОН). Структура 1:1 — те же имена функций, порядок, комментарии. @@ -143,20 +143,47 @@ def quote_json(s): # --- Сравнение регистров (зеркало PS -ne / -cne) --- -def ne_ci(a, b): - """PS -ne на строках (регистронезависимо). True когда различаются без учёта регистра.""" - return (a if a is not None else '').lower() != (b if b is not None else '').lower() +def ne_cs(a, b): + """PS -cne на строках (регистрочувствительно). True когда различаются с учётом регистра. + Зеркало компилятора: авто-синоним/описание опускаем только при ТОЧНОМ совпадении со split_camel_words.""" + return (a if a is not None else '') != (b if b is not None else '') def split_camel_words(name): - """Авто-синоним: точное зеркало Split-CamelCase из meta-compile (split_camel_case). + """Авто-синоним: точное зеркало Split-CamelCase из meta-compile (split_camel_case, HE-эвристика). ВАЖНО: логика должна совпадать байт-в-байт с компилятором, иначе ложные «синоним==авто» → диффы.""" if not name: return name result = re.sub(r'([а-яё])([А-ЯЁ])', r'\1 \2', name) result = re.sub(r'([a-z])([A-Z])', r'\1 \2', result) + # HE: сохраняем прогон заглавных >=2, если сразу за ним НЕ буква (пробел/цифра/спецсимвол/конец); + # предлоги/бренды перед буквой → лоуэркейз. Первый символ — как есть. if len(result) > 1: - result = result[0] + result[1:].lower() + chars = list(result) + n = len(chars) + keep = [False] * n + i = 0 + while i < n: + if chars[i].isupper(): + j = i + while j < n and chars[j].isupper(): + j += 1 + after_boundary = (j == n) or (not chars[j].isalpha()) + if j - i >= 2 and after_boundary: + for k in range(i, j): + keep[k] = True + i = j + else: + i += 1 + out = [] + for idx, c in enumerate(chars): + if idx == 0 or keep[idx]: + out.append(c) + elif c.isupper(): + out.append(c.lower()) + else: + out.append(c) + result = ''.join(out) return result @@ -359,7 +386,7 @@ def attr_to_dsl(attr_node): # Пустой (узел есть, значения нет) ≠ авто-синоним из имени → явный пустой (synonym:""). syn_empty = (syn_node is not None and syn_val is None) if isinstance(syn_val, str): - if ne_ci(syn_val, split_camel_words(nm)): + if ne_cs(syn_val, split_camel_words(nm)): syn_custom = True elif syn_val is not None: syn_custom = True # {ru,en} = всегда кастом @@ -654,7 +681,7 @@ def predef_item_to_dsl(item_el): s = ("(%s) %s" % (code, name)) if code else name if desc == '': s = s + " []" - elif ne_ci(desc, auto): + elif ne_cs(desc, auto): s = s + (" [%s]" % desc) if type_str: s = "%s: %s" % (s, type_str) @@ -665,7 +692,7 @@ def predef_item_to_dsl(item_el): o['code'] = code if desc == '': o['description'] = '' - elif ne_ci(desc, auto): + elif ne_cs(desc, auto): o['description'] = desc if type_str is not None: o['type'] = type_str @@ -778,7 +805,7 @@ def build_dsl(): syn_node_obj = _single(props, 'md:Synonym') syn_val = get_ml_value(syn_node_obj) if isinstance(syn_val, str): - if ne_ci(syn_val, split_camel_words(obj_name)): + if ne_cs(syn_val, split_camel_words(obj_name)): dsl['synonym'] = syn_val elif syn_val is not None: dsl['synonym'] = syn_val @@ -1588,7 +1615,7 @@ def build_dsl(): ev_cmt = _text(ev_cmt_n) if ev_cmt_n is not None else '' ev_syn_val = None if isinstance(ev_syn, str): - if ne_ci(ev_syn, split_camel_words(ev_name)): + if ne_cs(ev_syn, split_camel_words(ev_name)): ev_syn_val = ev_syn elif ev_syn is not None: ev_syn_val = ev_syn @@ -1615,7 +1642,7 @@ def build_dsl(): c_syn_node = _single(cp, 'md:Synonym') c_syn = get_ml_value(c_syn_node) if isinstance(c_syn, str): - if ne_ci(c_syn, split_camel_words(c_name)): + if ne_cs(c_syn, split_camel_words(c_name)): o['synonym'] = c_syn elif c_syn is not None: o['synonym'] = c_syn @@ -1658,7 +1685,7 @@ def build_dsl(): o = {'name': d_name} d_syn = get_ml_value(_single(dp, 'md:Synonym')) if isinstance(d_syn, str): - if ne_ci(d_syn, split_camel_words(d_name)): + if ne_cs(d_syn, split_camel_words(d_name)): o['synonym'] = d_syn elif d_syn is not None: o['synonym'] = d_syn @@ -1709,7 +1736,7 @@ def build_dsl(): ts_syn = get_ml_value(_single(tsp, 'md:Synonym')) ts_syn_custom = False if isinstance(ts_syn, str): - if ne_ci(ts_syn, split_camel_words(ts_name)): + if ne_cs(ts_syn, split_camel_words(ts_name)): ts_syn_custom = True elif ts_syn is not None: ts_syn_custom = True @@ -1779,7 +1806,7 @@ def build_dsl(): o = {} syn = get_ml_value(_single(cp, 'md:Synonym')) if isinstance(syn, str): - if ne_ci(syn, split_camel_words(cn)): + if ne_cs(syn, split_camel_words(cn)): o['synonym'] = syn elif syn is not None: o['synonym'] = syn diff --git a/docs/meta-dsl-spec.md b/docs/meta-dsl-spec.md index a198b3eb..43aae8e3 100644 --- a/docs/meta-dsl-spec.md +++ b/docs/meta-dsl-spec.md @@ -35,19 +35,29 @@ JSON DSL для описания объектов метаданных конф ## 2. Автогенерация синонима (CamelCase → слова) -Если `synonym` не указан, имя автоматически разбивается на слова: +Если `synonym` не указан, имя автоматически разбивается на слова (зеркало эвристики платформы): | Вход | Результат | |------|-----------| | `АвансовыйОтчет` | `Авансовый отчет` | | `ОсновнаяВалюта` | `Основная валюта` | -| `НДС20` | `НДС20` | +| `АбонентыЭДО` | `Абоненты ЭДО` | +| `ВыработкаОС` | `Выработка ОС` | +| `СчетУчетаНДС` | `Счет учета НДС` | +| `СтатусЭДО2` | `Статус ЭДО2` | +| `РасчетыСКлиентами` | `Расчеты склиентами` | +| `ЮКассаЛоготип` | `Юкасса логотип` | | `IncomingDocument` | `Incoming document` | Правила: - Граница на переходе `[а-яё][А-ЯЁ]` и `[a-z][A-Z]` -- Первое слово сохраняет заглавную, остальные — строчные -- Явный `synonym` перекрывает автогенерацию +- Первое слово сохраняет заглавную, остальные — строчные, **кроме аббревиатур**: максимальный прогон + заглавных длиной **≥2, если сразу за ним НЕ буква** (пробел/цифра/спецсимвол/конец строки) — остаётся + заглавным (`НДС`, `ЕГАИС`, `ОС`, `ЭП`) +- Прилипшие предлоги и бренды (`СКлиентами`, `ЮКасса`) стоят перед буквой → строчные (не аббревиатура) +- То же правило — для авто-Наименования предопределённых (§7.1.2) и авто-описаний +- Явный `synonym` перекрывает автогенерацию; при расхождении с авто хотя бы в регистре синоним + сохраняется дословно (напр. брендовый `ВетИС`, `МИР`) --- diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml index 22d48e49..f1085b7c 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-choice-params/Catalogs/ТестВыбора.xml @@ -183,7 +183,7 @@ ru - Тип ттн + Тип ТТН diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-props/Catalogs/Контрагенты.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-props/Catalogs/Контрагенты.xml index f857e51c..2b6dbbd1 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-attr-props/Catalogs/Контрагенты.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-props/Catalogs/Контрагенты.xml @@ -93,7 +93,7 @@ ru - Инн + ИНН Налоговый номер diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-attr-typeset-linkbytype/Catalogs/ТестХарактеристик.xml b/tests/skills/cases/meta-compile/snapshots/catalog-attr-typeset-linkbytype/Catalogs/ТестХарактеристик.xml index 3283182f..523b31d0 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-attr-typeset-linkbytype/Catalogs/ТестХарактеристик.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-attr-typeset-linkbytype/Catalogs/ТестХарактеристик.xml @@ -299,7 +299,7 @@ ru - Любая ссылка иб + Любая ссылка ИБ diff --git a/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml b/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml index e2e35448..d4c53697 100644 --- a/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml +++ b/tests/skills/cases/meta-compile/snapshots/catalog-mixed-types/Catalogs/Контрагенты.xml @@ -93,7 +93,7 @@ ru - Инн + ИНН diff --git a/tests/skills/cases/meta-compile/snapshots/constant-full/Constants/СтавкаНДС.xml b/tests/skills/cases/meta-compile/snapshots/constant-full/Constants/СтавкаНДС.xml index cdd2f953..b03a2d2f 100644 --- a/tests/skills/cases/meta-compile/snapshots/constant-full/Constants/СтавкаНДС.xml +++ b/tests/skills/cases/meta-compile/snapshots/constant-full/Constants/СтавкаНДС.xml @@ -20,7 +20,7 @@ ru - Ставка ндс + Ставка НДС (Демо) diff --git a/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml b/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml index 37cfc8a6..bce6ee06 100644 --- a/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml +++ b/tests/skills/cases/meta-compile/snapshots/http-service/HTTPServices/ТоварныйAPI.xml @@ -6,7 +6,7 @@ ru - Товарныйapi + ТоварныйAPI