diff --git a/.claude/skills/meta-edit/scripts/meta-edit.ps1 b/.claude/skills/meta-edit/scripts/meta-edit.ps1 index 0b7101f1..e665ab34 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.ps1 +++ b/.claude/skills/meta-edit/scripts/meta-edit.ps1 @@ -1,4 +1,4 @@ -# meta-edit v1.16 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks/ChoiceParameters) +# meta-edit v1.17 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks/ChoiceParameters/FillValue) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills param( [string]$DefinitionFile, @@ -2287,6 +2287,11 @@ function Modify-ChildElements($modifyDef, [string]$childType) { Info "Set $xmlTag '$elemName'.ChoiceParameters"; $script:modifyCount++ } } + "FillValue" { + if (Set-AttrPropertyElement $propsEl "FillValue" (Build-FillValueExplicitXml (Get-AttrTypeStrFromXml $propsEl) $changeValue)) { + Info "Set $xmlTag '$elemName'.FillValue"; $script:modifyCount++ + } + } default { # Scalar property change (Indexing, FillChecking, Use, etc.) $scalarEl = $null @@ -2698,6 +2703,93 @@ function Build-ChoiceParametersXml([string]$indent, $cp) { return $sb.ToString() } +# --- Порт из meta-compile: явное значение заполнения (FillValue) --- + +$script:fillBoolTrue = @('true','истина','да') +$script:fillBoolFalse = @('false','ложь','нет') + +function Esc-XmlText([string]$s) { return $s.Replace('&','&').Replace('<','<').Replace('>','>') } + +function Get-FillTypeCategory([string]$typeStr) { + if (-not $typeStr) { return 'String' } + if ($typeStr -match '\+') { return 'Other' } + $t = Resolve-TypeStr $typeStr + if ($t -match '^Boolean$') { return 'Boolean' } + if ($t -match '^String(\(|$)') { return 'String' } + if ($t -match '^Number(\(|$)') { return 'Number' } + if ($t -match '^(Date|DateTime)$') { return 'Date' } + return 'Other' +} + +function Expand-FillShortRef([string]$s, [string]$typeStr) { + if (-not $typeStr) { return $null } + if ($typeStr -match '\+') { return $null } + $t = Resolve-TypeStr $typeStr + if ($t -notmatch '^(\w+Ref)\.(.+)$') { return $null } + $root = $script:fillRefKindRoot[$Matches[1].ToLower()] + if (-not $root) { return $null } + $typeName = $Matches[2] + if ($script:fillEmptyRefWords -contains $s.ToLower()) { return "$root.$typeName.EmptyRef" } + if ($root -eq 'Enum') { return "Enum.$typeName.EnumValue.$s" } + return "$root.$typeName.$s" +} + +function Resolve-FillValueSpec([string]$s, [string]$typeStr) { + $cat = Get-FillTypeCategory $typeStr + if ($s -eq '') { return @{ XsiType='xs:string'; Text='' } } + if ($cat -eq 'String') { return @{ XsiType='xs:string'; Text=$s } } + if ($cat -eq 'Boolean' -or ($script:fillBoolTrue -contains $s.ToLower()) -or ($script:fillBoolFalse -contains $s.ToLower())) { + if ($script:fillBoolTrue -contains $s.ToLower()) { return @{ XsiType='xs:boolean'; Text='true' } } + if ($script:fillBoolFalse -contains $s.ToLower()) { return @{ XsiType='xs:boolean'; Text='false' } } + } + if ($cat -eq 'Number') { return @{ XsiType='xs:decimal'; Text=$s } } + if ($cat -eq 'Date' -or $s -match '^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$') { + if ($s -match '^\d{4}-\d{2}-\d{2}$') { $s = "${s}T00:00:00" } + return @{ XsiType='xs:dateTime'; Text=$s } + } + $ref = Normalize-FillRef $s + if ($ref) { return @{ XsiType='xr:DesignTimeRef'; Text=$ref } } + $short = Expand-FillShortRef $s $typeStr + if ($short) { return @{ XsiType='xr:DesignTimeRef'; Text=$short } } + return @{ XsiType='xs:string'; Text=$s } +} + +# Извлечь тип реквизита из XML (/) → DSL-typeStr для категоризации FillValue. +function Get-AttrTypeStrFromXml($propsEl) { + $typeEl = $null + foreach ($ch in $propsEl.ChildNodes) { if ($ch.NodeType -eq 'Element' -and $ch.LocalName -eq 'Type') { $typeEl = $ch; break } } + if (-not $typeEl) { return "" } + $mapped = @() + foreach ($ch in $typeEl.ChildNodes) { + if ($ch.NodeType -eq 'Element' -and $ch.LocalName -eq 'Type') { + $t = $ch.InnerText.Trim() + $colon = $t.IndexOf(':'); if ($colon -ge 0) { $t = $t.Substring($colon + 1) } + switch -Regex ($t) { + '^string$' { $mapped += 'String'; break } + '^decimal$' { $mapped += 'Number'; break } + '^boolean$' { $mapped += 'Boolean'; break } + '^dateTime$' { $mapped += 'Date'; break } + default { $mapped += $t } + } + } + } + if ($mapped.Count -eq 0) { return "" } + if ($mapped.Count -gt 1) { return ($mapped -join ' + ') } + return $mapped[0] +} + +# FillValue — явное значение (порт Emit-FillValue, ветка hasSpec). Маркеры {nil}/{emptyRef}; иначе по типу. +function Build-FillValueExplicitXml([string]$typeStr, $spec) { + if ($null -eq $spec) { return "" } + if ($spec -is [bool]) { return "$(if ($spec) { 'true' } else { 'false' })" } + if ($spec -is [int] -or $spec -is [long] -or $spec -is [double] -or $spec -is [decimal]) { return "$(Format-FillNum $spec)" } + if ((Get-ChElProp $spec @('nil')) -eq $true) { return "" } + if ((Get-ChElProp $spec @('emptyRef','пустаяссылка')) -eq $true) { return "" } + $r = Resolve-FillValueSpec "$spec" $typeStr + if ($r.Text -eq '' -and $r.XsiType -eq 'xs:string') { return "" } + return "$(Esc-XmlText $r.Text)" +} + function Find-PropertyElement([string]$propName) { foreach ($child in $script:propertiesEl.ChildNodes) { if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $propName) { diff --git a/.claude/skills/meta-edit/scripts/meta-edit.py b/.claude/skills/meta-edit/scripts/meta-edit.py index 21c67487..1736d1b8 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.py +++ b/.claude/skills/meta-edit/scripts/meta-edit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-edit v1.16 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks/ChoiceParameters) +# meta-edit v1.17 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks/ChoiceParameters/FillValue) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -2127,6 +2127,10 @@ def modify_child_elements(modify_def, child_type): if set_attr_property_element(props_el, "ChoiceParameters", build_choice_parameters_xml(get_child_indent(props_el), change_value)): info(f"Set {xml_tag} '{elem_name}'.ChoiceParameters") modify_count += 1 + elif change_prop == "FillValue": + if set_attr_property_element(props_el, "FillValue", build_fill_value_explicit_xml(get_attr_type_str_from_xml(props_el), change_value)): + info(f"Set {xml_tag} '{elem_name}'.FillValue") + modify_count += 1 else: # Scalar property change (Indexing, FillChecking, Use, etc.) @@ -2577,6 +2581,128 @@ def build_choice_parameters_xml(indent, cp): return "\r\n".join(parts) +# --- Порт из meta-compile: явное значение заполнения (FillValue) --- + +fill_bool_true = {'true', 'истина', 'да'} +fill_bool_false = {'false', 'ложь', 'нет'} + + +def esc_xml_text(s): + return s.replace('&', '&').replace('<', '<').replace('>', '>') + + +def get_fill_type_category(type_str): + if not type_str: + return 'String' + if '+' in type_str: + return 'Other' + t = resolve_type_str(type_str) + if re.match(r'^Boolean$', t): + return 'Boolean' + if re.match(r'^String(\(|$)', t): + return 'String' + if re.match(r'^Number(\(|$)', t): + return 'Number' + if re.match(r'^(Date|DateTime)$', t): + return 'Date' + return 'Other' + + +def expand_fill_short_ref(s, type_str): + if not type_str or '+' in type_str: + return None + t = resolve_type_str(type_str) + m = re.match(r'^(\w+Ref)\.(.+)$', t) + if not m: + return None + root = fill_ref_kind_root.get(m.group(1).lower()) + if not root: + return None + type_name = m.group(2) + if s.lower() in fill_empty_ref_words: + return f"{root}.{type_name}.EmptyRef" + if root == 'Enum': + return f"Enum.{type_name}.EnumValue.{s}" + return f"{root}.{type_name}.{s}" + + +def resolve_fill_value_spec(s, type_str): + cat = get_fill_type_category(type_str) + if s == '': + return {'XsiType': 'xs:string', 'Text': ''} + if cat == 'String': + return {'XsiType': 'xs:string', 'Text': s} + if cat == 'Boolean' or s.lower() in fill_bool_true or s.lower() in fill_bool_false: + if s.lower() in fill_bool_true: + return {'XsiType': 'xs:boolean', 'Text': 'true'} + if s.lower() in fill_bool_false: + return {'XsiType': 'xs:boolean', 'Text': 'false'} + if cat == 'Number': + return {'XsiType': 'xs:decimal', 'Text': s} + if cat == 'Date' or re.match(r'^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2})?$', s): + if re.match(r'^\d{4}-\d{2}-\d{2}$', s): + s = s + 'T00:00:00' + return {'XsiType': 'xs:dateTime', 'Text': s} + ref = normalize_fill_ref(s) + if ref: + return {'XsiType': 'xr:DesignTimeRef', 'Text': ref} + short = expand_fill_short_ref(s, type_str) + if short: + return {'XsiType': 'xr:DesignTimeRef', 'Text': short} + return {'XsiType': 'xs:string', 'Text': s} + + +def get_attr_type_str_from_xml(props_el): + """Извлечь тип реквизита из XML (/) → DSL-typeStr для категоризации FillValue.""" + type_el = None + for ch in props_el: + if localname(ch) == 'Type': + type_el = ch + break + if type_el is None: + return "" + mapped = [] + for ch in type_el: + if localname(ch) == 'Type': + t = (ch.text or '').strip() + colon = t.find(':') + if colon >= 0: + t = t[colon + 1:] + if t == 'string': + mapped.append('String') + elif t == 'decimal': + mapped.append('Number') + elif t == 'boolean': + mapped.append('Boolean') + elif t == 'dateTime': + mapped.append('Date') + else: + mapped.append(t) + if not mapped: + return "" + if len(mapped) > 1: + return ' + '.join(mapped) + return mapped[0] + + +def build_fill_value_explicit_xml(type_str, spec): + """FillValue — явное значение (порт Emit-FillValue, ветка hasSpec).""" + if spec is None: + return '' + if isinstance(spec, bool): + return f'{"true" if spec else "false"}' + if isinstance(spec, (int, float)): + return f'{format_fill_num(spec)}' + if get_ch_el_prop(spec, ['nil']) is True: + return '' + if get_ch_el_prop(spec, ['emptyRef', 'пустаяссылка']) is True: + return '' + r = resolve_fill_value_spec(str(spec), type_str) + if r['Text'] == '' and r['XsiType'] == 'xs:string': + return '' + return f'{esc_xml_text(r["Text"])}' + + def find_property_element(prop_name): for child in properties_el: if localname(child) == prop_name: diff --git a/tests/skills/cases/meta-edit/modify-attribute-structural.json b/tests/skills/cases/meta-edit/modify-attribute-structural.json index 48ecc214..2ffa4bd9 100644 --- a/tests/skills/cases/meta-edit/modify-attribute-structural.json +++ b/tests/skills/cases/meta-edit/modify-attribute-structural.json @@ -24,9 +24,10 @@ "ChoiceParameters": [ { "name": "Отбор.ПометкаУдаления", "value": false }, { "name": "Отбор.Ссылка", "value": "Catalog.Спр.EmptyRef" } - ] + ], + "FillValue": "EmptyRef" }, - "Сумма": { "MinValue": 0, "MaxValue": 1000000 } + "Сумма": { "MinValue": 0, "MaxValue": 1000000, "FillValue": 0 } } } } diff --git a/tests/skills/cases/meta-edit/snapshots/modify-attribute-structural/Catalogs/Спр.xml b/tests/skills/cases/meta-edit/snapshots/modify-attribute-structural/Catalogs/Спр.xml index bc8edd86..d2d2d513 100644 --- a/tests/skills/cases/meta-edit/snapshots/modify-attribute-structural/Catalogs/Спр.xml +++ b/tests/skills/cases/meta-edit/snapshots/modify-attribute-structural/Catalogs/Спр.xml @@ -169,7 +169,7 @@ false - + Catalog.Спр.EmptyRef DontCheck Items