mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-13 22:35:16 +03:00
feat(meta-edit): структурные свойства LinkByType/ChoiceParameterLinks (Фаза 2 Шаг 3b-1, v1.15)
modify-attribute/-dimension/-resource: LinkByType ({dataPath,linkItem}) и
ChoiceParameterLinks ([{name,dataPath,valueChange}]) — порт эмиттеров Emit-LinkByType/
Emit-ChoiceParameterLinks в meta-edit (используют xr: namespace, уже в Import-Fragment;
app не нужен). Портированы вспомогательные Expand-DataPath (+Resolve-StdAttrEn на
существующих reserved-картах), Get-ChElProp, ConvertFrom-ChLinkShorthand. Прощающий
ввод путей (короткое имя реквизита → полный путь).
Cert декомпиляцией: выход meta-edit БАЙТ-В-БАЙТ совпадает с meta-compile для того же
ввода (ps1 и py; py-lxml пишет LF vs ps1 CRLF — нормализуется раннером/git).
verify-snapshots --case modify-attribute-structural (расширен) грузится в 1С.
meta-edit 14/14 ps1+py.
Остаток Шага 3: fillValue явный + choiceParameters (fill-ref машинерия + фикс app-namespace).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# meta-edit v1.14 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue)
|
||||
# meta-edit v1.15 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[string]$DefinitionFile,
|
||||
@@ -2270,6 +2270,16 @@ function Modify-ChildElements($modifyDef, [string]$childType) {
|
||||
Info "Set $xmlTag '$elemName'.MaxValue"; $script:modifyCount++
|
||||
}
|
||||
}
|
||||
"LinkByType" {
|
||||
if (Set-AttrPropertyElement $propsEl "LinkByType" (Build-LinkByTypeXml (Get-ChildIndent $propsEl) $changeValue)) {
|
||||
Info "Set $xmlTag '$elemName'.LinkByType"; $script:modifyCount++
|
||||
}
|
||||
}
|
||||
"ChoiceParameterLinks" {
|
||||
if (Set-AttrPropertyElement $propsEl "ChoiceParameterLinks" (Build-ChoiceParameterLinksXml (Get-ChildIndent $propsEl) $changeValue)) {
|
||||
Info "Set $xmlTag '$elemName'.ChoiceParameterLinks"; $script:modifyCount++
|
||||
}
|
||||
}
|
||||
default {
|
||||
# Scalar property change (Indexing, FillChecking, Use, etc.)
|
||||
$scalarEl = $null
|
||||
@@ -2437,6 +2447,103 @@ function Build-MinMaxValueXml([string]$tag, $val) {
|
||||
return "<$tag xsi:type=`"$t`">$(Esc-Xml "$val")</$tag>"
|
||||
}
|
||||
|
||||
# --- Порт из meta-compile: развёртка путей данных + связи выбора / тип по ссылке (structural modify) ---
|
||||
|
||||
# Свойство из dict/PSCustomObject по списку синонимов (первый найденный, иначе $null).
|
||||
function Get-ChElProp($obj, [string[]]$names) {
|
||||
if ($null -eq $obj) { return $null }
|
||||
foreach ($n in $names) {
|
||||
if ($obj -is [System.Collections.IDictionary]) { if ($obj.Contains($n)) { return $obj[$n] } }
|
||||
elseif ($obj.PSObject -and $obj.PSObject.Properties[$n]) { return $obj.PSObject.Properties[$n].Value }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Стандартный реквизит рус/англ → английский (для Catalog/Document); использует существующие reserved-карты.
|
||||
function Resolve-StdAttrEn([string]$name) {
|
||||
$ctx = switch ("$script:objType") { 'Catalog' { 'catalog' } 'Document' { 'document' } default { $null } }
|
||||
if (-not $ctx) { return $null }
|
||||
$stdSet = $script:reservedByContext[$ctx]
|
||||
foreach ($en in $stdSet) {
|
||||
$ru = $script:reservedAttrNames[$en]
|
||||
if (($name -ieq $en) -or ($ru -and $name -ieq $ru)) { return $en }
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
# Прощающий ввод пути данных: короткое имя реквизита → полный путь объекта (порт Expand-DataPath).
|
||||
function Expand-DataPath([string]$dp) {
|
||||
if (-not $dp) { return $dp }
|
||||
$s = "$dp"
|
||||
if ($s -match '[:/]') { return $s }
|
||||
if ($s -match '^-?\d+$') { return $s }
|
||||
if ($s -match '^(StandardAttribute|Attribute)\.') { return "$($script:objType).$($script:objName).$s" }
|
||||
if (-not $s.Contains('.')) {
|
||||
$en = Resolve-StdAttrEn $s
|
||||
if ($en) { return "$($script:objType).$($script:objName).StandardAttribute.$en" }
|
||||
return "$($script:objType).$($script:objName).Attribute.$s"
|
||||
}
|
||||
return $s
|
||||
}
|
||||
|
||||
# Shorthand "name=path" | "name=path:Clear|DontChange" → {name, dataPath, valueChange?}.
|
||||
function ConvertFrom-ChLinkShorthand([string]$s) {
|
||||
$eq = $s.IndexOf('=')
|
||||
if ($eq -lt 0) { return @{ name = $s.Trim() } }
|
||||
$o = @{ name = $s.Substring(0, $eq).Trim() }; $rest = $s.Substring($eq + 1).Trim()
|
||||
if ($rest -match '^(.*):(?i:(Clear|DontChange|очистить|неизменять))$') { $o['dataPath'] = $matches[1].Trim(); $o['valueChange'] = $matches[2] }
|
||||
else { $o['dataPath'] = $rest }
|
||||
return $o
|
||||
}
|
||||
|
||||
# LinkByType — {dataPath, linkItem?} (порт Emit-LinkByType). Строка → dataPath, linkItem=0.
|
||||
function Build-LinkByTypeXml([string]$indent, $spec) {
|
||||
if (-not $spec) { return "$indent<LinkByType/>" }
|
||||
if ($spec -is [string]) { $dp = "$spec"; $li = 0 }
|
||||
else {
|
||||
$dp = "$(Get-ChElProp $spec @('dataPath','path','путь'))"
|
||||
$liRaw = Get-ChElProp $spec @('linkItem','элементСвязи')
|
||||
$li = if ($null -ne $liRaw) { $liRaw } else { 0 }
|
||||
}
|
||||
if (-not $dp) { return "$indent<LinkByType/>" }
|
||||
$dp = Expand-DataPath $dp
|
||||
$lines = @(
|
||||
"$indent<LinkByType>"
|
||||
"$indent`t<xr:DataPath>$(Esc-Xml "$dp")</xr:DataPath>"
|
||||
"$indent`t<xr:LinkItem>$li</xr:LinkItem>"
|
||||
"$indent</LinkByType>"
|
||||
)
|
||||
return $lines -join "`r`n"
|
||||
}
|
||||
|
||||
# ChoiceParameterLinks — [{name, dataPath, valueChange?}] (порт Emit-ChoiceParameterLinks). valueChange дефолт Clear.
|
||||
function Build-ChoiceParameterLinksXml([string]$indent, $cpl) {
|
||||
if (-not $cpl -or @($cpl).Count -eq 0) { return "$indent<ChoiceParameterLinks/>" }
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
$sb.Append("$indent<ChoiceParameterLinks>") | Out-Null
|
||||
foreach ($lk in @($cpl)) {
|
||||
if ($lk -is [string]) { $lk = ConvertFrom-ChLinkShorthand $lk }
|
||||
$name = Get-ChElProp $lk @('name','имя')
|
||||
$dp = Expand-DataPath (Get-ChElProp $lk @('dataPath','path','путь'))
|
||||
$vcRaw = Get-ChElProp $lk @('valueChange','режимИзменения')
|
||||
$vc = 'Clear'
|
||||
if ($vcRaw) {
|
||||
$vc = switch -Regex ("$vcRaw".ToLower()) {
|
||||
'^(clear|очистить|очистка)$' { 'Clear'; break }
|
||||
'^(dontchange|неизменять|неменять|нет)$' { 'DontChange'; break }
|
||||
default { "$vcRaw" }
|
||||
}
|
||||
}
|
||||
$sb.Append("`r`n$indent`t<xr:Link>") | Out-Null
|
||||
$sb.Append("`r`n$indent`t`t<xr:Name>$(Esc-Xml "$name")</xr:Name>") | Out-Null
|
||||
$sb.Append("`r`n$indent`t`t<xr:DataPath xsi:type=`"xs:string`">$(Esc-Xml "$dp")</xr:DataPath>") | Out-Null
|
||||
$sb.Append("`r`n$indent`t`t<xr:ValueChange>$vc</xr:ValueChange>") | Out-Null
|
||||
$sb.Append("`r`n$indent`t</xr:Link>") | Out-Null
|
||||
}
|
||||
$sb.Append("`r`n$indent</ChoiceParameterLinks>") | Out-Null
|
||||
return $sb.ToString()
|
||||
}
|
||||
|
||||
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.14 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue)
|
||||
# meta-edit v1.15 — Edit existing 1C metadata object XML (+structural attr props Format/EditFormat/ToolTip/ChoiceForm/MinValue/MaxValue/LinkByType/ChoiceParameterLinks)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -2113,6 +2113,14 @@ def modify_child_elements(modify_def, child_type):
|
||||
if set_attr_property_element(props_el, "MaxValue", build_min_max_value_xml("MaxValue", change_value)):
|
||||
info(f"Set {xml_tag} '{elem_name}'.MaxValue")
|
||||
modify_count += 1
|
||||
elif change_prop == "LinkByType":
|
||||
if set_attr_property_element(props_el, "LinkByType", build_link_by_type_xml(get_child_indent(props_el), change_value)):
|
||||
info(f"Set {xml_tag} '{elem_name}'.LinkByType")
|
||||
modify_count += 1
|
||||
elif change_prop == "ChoiceParameterLinks":
|
||||
if set_attr_property_element(props_el, "ChoiceParameterLinks", build_choice_parameter_links_xml(get_child_indent(props_el), change_value)):
|
||||
info(f"Set {xml_tag} '{elem_name}'.ChoiceParameterLinks")
|
||||
modify_count += 1
|
||||
|
||||
else:
|
||||
# Scalar property change (Indexing, FillChecking, Use, etc.)
|
||||
@@ -2266,6 +2274,128 @@ def build_min_max_value_xml(tag, val):
|
||||
return f'<{tag} xsi:type="{t}">{esc_xml(str(val))}</{tag}>'
|
||||
|
||||
|
||||
# --- Порт из meta-compile: развёртка путей данных + связи выбора / тип по ссылке (structural modify) ---
|
||||
|
||||
def get_ch_el_prop(obj, names):
|
||||
"""Свойство из dict по списку синонимов (первый найденный, иначе None)."""
|
||||
if obj is None:
|
||||
return None
|
||||
if isinstance(obj, dict):
|
||||
for n in names:
|
||||
if n in obj:
|
||||
return obj[n]
|
||||
return None
|
||||
|
||||
|
||||
# Пары EN↔RU стандартных реквизитов Catalog/Document (для resolve_std_attr_en; py-карты плоские, восстанавливаем).
|
||||
_STD_ATTR_BY_CTX = {
|
||||
'Catalog': [('Ref', 'Ссылка'), ('DeletionMark', 'ПометкаУдаления'), ('Predefined', 'Предопределенный'),
|
||||
('PredefinedDataName', 'ИмяПредопределенныхДанных'), ('Code', 'Код'), ('Description', 'Наименование'),
|
||||
('Owner', 'Владелец'), ('Parent', 'Родитель'), ('IsFolder', 'ЭтоГруппа')],
|
||||
'Document': [('Ref', 'Ссылка'), ('DeletionMark', 'ПометкаУдаления'), ('Date', 'Дата'),
|
||||
('Number', 'Номер'), ('Posted', 'Проведен')],
|
||||
}
|
||||
|
||||
|
||||
def resolve_std_attr_en(name):
|
||||
"""Стандартный реквизит рус/англ → английский (Catalog/Document)."""
|
||||
pairs = _STD_ATTR_BY_CTX.get(obj_type)
|
||||
if not pairs:
|
||||
return None
|
||||
nl = str(name).lower()
|
||||
for en, ru in pairs:
|
||||
if nl == en.lower() or nl == ru.lower():
|
||||
return en
|
||||
return None
|
||||
|
||||
|
||||
def expand_data_path(dp):
|
||||
"""Прощающий ввод пути данных: короткое имя реквизита → полный путь объекта (порт Expand-DataPath)."""
|
||||
if not dp:
|
||||
return dp
|
||||
s = str(dp)
|
||||
if re.search(r'[:/]', s):
|
||||
return s
|
||||
if re.match(r'^-?\d+$', s):
|
||||
return s
|
||||
if re.match(r'^(StandardAttribute|Attribute)\.', s):
|
||||
return f"{obj_type}.{obj_name}.{s}"
|
||||
if '.' not in s:
|
||||
en = resolve_std_attr_en(s)
|
||||
if en:
|
||||
return f"{obj_type}.{obj_name}.StandardAttribute.{en}"
|
||||
return f"{obj_type}.{obj_name}.Attribute.{s}"
|
||||
return s
|
||||
|
||||
|
||||
def convert_from_ch_link_shorthand(s):
|
||||
"""Shorthand "name=path" | "name=path:Clear|DontChange" → {name, dataPath, valueChange?}."""
|
||||
eq = s.find('=')
|
||||
if eq < 0:
|
||||
return {'name': s.strip()}
|
||||
o = {'name': s[:eq].strip()}
|
||||
rest = s[eq + 1:].strip()
|
||||
m = re.match(r'^(.*):(Clear|DontChange|очистить|неизменять)$', rest, re.IGNORECASE)
|
||||
if m:
|
||||
o['dataPath'] = m.group(1).strip()
|
||||
o['valueChange'] = m.group(2)
|
||||
else:
|
||||
o['dataPath'] = rest
|
||||
return o
|
||||
|
||||
|
||||
def build_link_by_type_xml(indent, spec):
|
||||
"""LinkByType — {dataPath, linkItem?} (порт Emit-LinkByType)."""
|
||||
if not spec:
|
||||
return f"{indent}<LinkByType/>"
|
||||
if isinstance(spec, str):
|
||||
dp = spec
|
||||
li = 0
|
||||
else:
|
||||
dp = str(get_ch_el_prop(spec, ['dataPath', 'path', 'путь']) or '')
|
||||
li_raw = get_ch_el_prop(spec, ['linkItem', 'элементСвязи'])
|
||||
li = li_raw if li_raw is not None else 0
|
||||
if not dp:
|
||||
return f"{indent}<LinkByType/>"
|
||||
dp = expand_data_path(dp)
|
||||
return "\r\n".join([
|
||||
f"{indent}<LinkByType>",
|
||||
f"{indent}\t<xr:DataPath>{esc_xml(str(dp))}</xr:DataPath>",
|
||||
f"{indent}\t<xr:LinkItem>{li}</xr:LinkItem>",
|
||||
f"{indent}</LinkByType>",
|
||||
])
|
||||
|
||||
|
||||
def build_choice_parameter_links_xml(indent, cpl):
|
||||
"""ChoiceParameterLinks — [{name, dataPath, valueChange?}] (порт Emit-ChoiceParameterLinks)."""
|
||||
items = cpl if isinstance(cpl, list) else ([cpl] if cpl else [])
|
||||
if not items:
|
||||
return f"{indent}<ChoiceParameterLinks/>"
|
||||
parts = [f"{indent}<ChoiceParameterLinks>"]
|
||||
for lk in items:
|
||||
if isinstance(lk, str):
|
||||
lk = convert_from_ch_link_shorthand(lk)
|
||||
name = get_ch_el_prop(lk, ['name', 'имя'])
|
||||
dp = expand_data_path(get_ch_el_prop(lk, ['dataPath', 'path', 'путь']))
|
||||
vc_raw = get_ch_el_prop(lk, ['valueChange', 'режимИзменения'])
|
||||
vc = 'Clear'
|
||||
if vc_raw:
|
||||
low = str(vc_raw).lower()
|
||||
if re.match(r'^(clear|очистить|очистка)$', low):
|
||||
vc = 'Clear'
|
||||
elif re.match(r'^(dontchange|неизменять|неменять|нет)$', low):
|
||||
vc = 'DontChange'
|
||||
else:
|
||||
vc = str(vc_raw)
|
||||
parts.append(f"{indent}\t<xr:Link>")
|
||||
parts.append(f"{indent}\t\t<xr:Name>{esc_xml(str(name) if name is not None else '')}</xr:Name>")
|
||||
parts.append(f'{indent}\t\t<xr:DataPath xsi:type="xs:string">{esc_xml(str(dp) if dp is not None else "")}</xr:DataPath>')
|
||||
parts.append(f"{indent}\t\t<xr:ValueChange>{vc}</xr:ValueChange>")
|
||||
parts.append(f"{indent}\t</xr:Link>")
|
||||
parts.append(f"{indent}</ChoiceParameterLinks>")
|
||||
return "\r\n".join(parts)
|
||||
|
||||
|
||||
def find_property_element(prop_name):
|
||||
for child in properties_el:
|
||||
if localname(child) == prop_name:
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
"modify": {
|
||||
"attributes": {
|
||||
"Дата": { "Format": "ДФ=dd.MM.yyyy", "EditFormat": "ДФ=dd.MM.yyyy", "ToolTip": "Дата документа" },
|
||||
"Контр": { "ChoiceForm": "Catalog.Спр.Form.ФормаВыбора" },
|
||||
"Контр": {
|
||||
"ChoiceForm": "Catalog.Спр.Form.ФормаВыбора",
|
||||
"ChoiceParameterLinks": ["Отбор.Владелец=Ссылка"],
|
||||
"LinkByType": { "dataPath": "Ссылка", "linkItem": 0 }
|
||||
},
|
||||
"Сумма": { "MinValue": 0, "MaxValue": 1000000 }
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -172,12 +172,21 @@
|
||||
<FillValue xsi:nil="true" />
|
||||
<FillChecking>DontCheck</FillChecking>
|
||||
<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>
|
||||
<ChoiceParameterLinks />
|
||||
<ChoiceParameterLinks>
|
||||
<xr:Link>
|
||||
<xr:Name>Отбор.Владелец</xr:Name>
|
||||
<xr:DataPath xsi:type="xs:string">Catalog.Спр.StandardAttribute.Ref</xr:DataPath>
|
||||
<xr:ValueChange>Clear</xr:ValueChange>
|
||||
</xr:Link>
|
||||
</ChoiceParameterLinks>
|
||||
<ChoiceParameters />
|
||||
<QuickChoice>Auto</QuickChoice>
|
||||
<CreateOnInput>Auto</CreateOnInput>
|
||||
<ChoiceForm>Catalog.Спр.Form.ФормаВыбора</ChoiceForm>
|
||||
<LinkByType />
|
||||
<LinkByType>
|
||||
<xr:DataPath>Catalog.Спр.StandardAttribute.Ref</xr:DataPath>
|
||||
<xr:LinkItem>0</xr:LinkItem>
|
||||
</LinkByType>
|
||||
<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>
|
||||
<Use>ForItem</Use>
|
||||
<Indexing>DontIndex</Indexing>
|
||||
|
||||
Reference in New Issue
Block a user