mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-14 06:45:17 +03:00
feat(meta-edit): структурное свойство FillValue явный (Фаза 2 Шаг 3b-2b, v1.17)
modify-attribute/-dimension/-resource: FillValue с явным значением — порт Emit-FillValue
+ Resolve-FillValueSpec/Get-FillTypeCategory/Expand-FillShortRef/fillBool-таблицы в
meta-edit. Тип реквизита извлекается из XML (<Type>/<v8:Type>, Get-AttrTypeStrFromXml) —
по нему категоризация значения. Маркеры {nil}/{emptyRef}; bool→xs:boolean, число→
xs:decimal, ref-путь/короткая ссылка→xr:DesignTimeRef (EmptyRef разворачивается по типу),
строка→xs:string. Esc-XmlText (&<> без ") для паритета текста FillValue.
Завершает Шаг 3: все структурные свойства реквизита (Format/EditFormat/ToolTip/ChoiceForm/
MinValue/MaxValue/LinkByType/ChoiceParameterLinks/ChoiceParameters/FillValue) доступны в
modify, БАЙТ-В-БАЙТ = meta-compile (ps1 и py), платформенный cert в 1С.
Cert декомпиляцией: fillValue на 4 типах реквизита = meta-compile. verify-snapshots
--case modify-attribute-structural (FillValue EmptyRef+decimal) грузится в 1С.
meta-edit 14/14 ps1+py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 (<Type>/<v8:Type>) → 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 "<FillValue xsi:nil=`"true`"/>" }
|
||||
if ($spec -is [bool]) { return "<FillValue xsi:type=`"xs:boolean`">$(if ($spec) { 'true' } else { 'false' })</FillValue>" }
|
||||
if ($spec -is [int] -or $spec -is [long] -or $spec -is [double] -or $spec -is [decimal]) { return "<FillValue xsi:type=`"xs:decimal`">$(Format-FillNum $spec)</FillValue>" }
|
||||
if ((Get-ChElProp $spec @('nil')) -eq $true) { return "<FillValue xsi:nil=`"true`"/>" }
|
||||
if ((Get-ChElProp $spec @('emptyRef','пустаяссылка')) -eq $true) { return "<FillValue xsi:type=`"xr:DesignTimeRef`"/>" }
|
||||
$r = Resolve-FillValueSpec "$spec" $typeStr
|
||||
if ($r.Text -eq '' -and $r.XsiType -eq 'xs:string') { return "<FillValue xsi:type=`"xs:string`"/>" }
|
||||
return "<FillValue xsi:type=`"$($r.XsiType)`">$(Esc-XmlText $r.Text)</FillValue>"
|
||||
}
|
||||
|
||||
function Find-PropertyElement([string]$propName) {
|
||||
foreach ($child in $script:propertiesEl.ChildNodes) {
|
||||
if ($child.NodeType -eq 'Element' -and $child.LocalName -eq $propName) {
|
||||
|
||||
@@ -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 (<Type>/<v8:Type>) → 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 '<FillValue xsi:nil="true"/>'
|
||||
if isinstance(spec, bool):
|
||||
return f'<FillValue xsi:type="xs:boolean">{"true" if spec else "false"}</FillValue>'
|
||||
if isinstance(spec, (int, float)):
|
||||
return f'<FillValue xsi:type="xs:decimal">{format_fill_num(spec)}</FillValue>'
|
||||
if get_ch_el_prop(spec, ['nil']) is True:
|
||||
return '<FillValue xsi:nil="true"/>'
|
||||
if get_ch_el_prop(spec, ['emptyRef', 'пустаяссылка']) is True:
|
||||
return '<FillValue xsi:type="xr:DesignTimeRef"/>'
|
||||
r = resolve_fill_value_spec(str(spec), type_str)
|
||||
if r['Text'] == '' and r['XsiType'] == 'xs:string':
|
||||
return '<FillValue xsi:type="xs:string"/>'
|
||||
return f'<FillValue xsi:type="{r["XsiType"]}">{esc_xml_text(r["Text"])}</FillValue>'
|
||||
|
||||
|
||||
def find_property_element(prop_name):
|
||||
for child in properties_el:
|
||||
if localname(child) == prop_name:
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
"ChoiceParameters": [
|
||||
{ "name": "Отбор.ПометкаУдаления", "value": false },
|
||||
{ "name": "Отбор.Ссылка", "value": "Catalog.Спр.EmptyRef" }
|
||||
]
|
||||
],
|
||||
"FillValue": "EmptyRef"
|
||||
},
|
||||
"Сумма": { "MinValue": 0, "MaxValue": 1000000 }
|
||||
"Сумма": { "MinValue": 0, "MaxValue": 1000000, "FillValue": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
<MinValue xsi:nil="true" />
|
||||
<MaxValue xsi:nil="true" />
|
||||
<FillFromFillingValue>false</FillFromFillingValue>
|
||||
<FillValue xsi:nil="true" />
|
||||
<FillValue xsi:type="xr:DesignTimeRef">Catalog.Спр.EmptyRef</FillValue>
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
|
||||
<ChoiceParameterLinks>
|
||||
|
||||
Reference in New Issue
Block a user