feat(skd-compile): dataParameters auto — копирование value всех типов (ЕРП-паттерн)

Раньше "auto" копировал только variant для StandardPeriod, остальные типы
теряли значение по умолчанию. Теперь:

- value задан (не-Custom для StandardPeriod) → value + use=true (implicit),
  правильный xsi:type: boolean/decimal/dateTime/string, DesignTimeValue для
  ссылочных типов.
- value отсутствует или StandardPeriod=Custom → <use>false</use>
  + <value xsi:nil="true"/>.

Соответствует тому, как 1С Designer и ЕРП-отчёты персистят
SettingsParameterValue. Тест auto-data-parameters расширен покрытием
decimal/string/ref/nil.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-04-19 16:13:04 +03:00
parent 3f23be8219
commit 54d47aadad
5 changed files with 181 additions and 53 deletions
+1 -1
View File
@@ -142,7 +142,7 @@ Shorthand: `"Имя [Заголовок]: тип = значение @флаги"
}
```
В варианте настроек `"dataParameters": "auto"` автоматически генерирует записи для всех не-hidden параметров с `userSettingID`. Для `StandardPeriod` вариант периода наследуется из дефолта параметра (`Custom`, если не задан).
В варианте настроек `"dataParameters": "auto"` автоматически генерирует записи для всех не-hidden параметров с `userSettingID`. Значения по умолчанию копируются из параметра с правильным xsi:type (boolean/decimal/date/string/DesignTimeValue для ссылочных, StandardPeriod — с наследованием variant). Параметры без дефолта или с `StandardPeriod=Custom` помечаются `<use>false</use>` и `<value xsi:nil="true"/>` (активируются пользователем).
### Фильтры — shorthand
@@ -1,4 +1,4 @@
# skd-compile v1.16 — Compile 1C DCS from JSON
# skd-compile v1.17 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -1847,7 +1847,10 @@ function Emit-DataParameters {
X "$indent`t`t<dcscor:parameter>$(Esc-Xml "$($dp.parameter)")</dcscor:parameter>"
# Value
if ($null -ne $dp.value) {
if ($dp.nilValue -eq $true) {
X "$indent`t`t<dcscor:value xsi:nil=`"true`"/>"
} elseif ($null -ne $dp.value) {
$vtype = "$($dp.valueType)"
if ($dp.value -is [PSCustomObject] -and $dp.value.variant) {
# StandardPeriod (object form from JSON)
X "$indent`t`t<dcscor:value xsi:type=`"v8:StandardPeriod`">"
@@ -1862,11 +1865,17 @@ function Emit-DataParameters {
X "$indent`t`t`t<v8:startDate>0001-01-01T00:00:00</v8:startDate>"
X "$indent`t`t`t<v8:endDate>0001-01-01T00:00:00</v8:endDate>"
X "$indent`t`t</dcscor:value>"
} elseif ($dp.value -is [bool]) {
} elseif ($vtype -eq 'boolean' -or $dp.value -is [bool]) {
$bv = "$($dp.value)".ToLower()
X "$indent`t`t<dcscor:value xsi:type=`"xs:boolean`">$(Esc-Xml $bv)</dcscor:value>"
} elseif ("$($dp.value)" -match '^\d{4}-\d{2}-\d{2}T') {
} elseif ($vtype -match '^date' -or "$($dp.value)" -match '^\d{4}-\d{2}-\d{2}T') {
X "$indent`t`t<dcscor:value xsi:type=`"xs:dateTime`">$(Esc-Xml "$($dp.value)")</dcscor:value>"
} elseif ($vtype -match '^decimal') {
X "$indent`t`t<dcscor:value xsi:type=`"xs:decimal`">$(Esc-Xml "$($dp.value)")</dcscor:value>"
} elseif ($vtype -match '^string') {
X "$indent`t`t<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$($dp.value)")</dcscor:value>"
} elseif ("$($dp.value)" -match '^(ПланСчетов|Справочник|Перечисление|Документ|ПланВидовХарактеристик|ПланВидовРасчета|БизнесПроцесс|Задача|РегистрСведений|ПланОбмена)\.' -or "$($dp.value)" -match '^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.') {
X "$indent`t`t<dcscor:value xsi:type=`"dcscor:DesignTimeValue`">$(Esc-Xml "$($dp.value)")</dcscor:value>"
} else {
X "$indent`t`t<dcscor:value xsi:type=`"xs:string`">$(Esc-Xml "$($dp.value)")</dcscor:value>"
}
@@ -2162,31 +2171,45 @@ function Emit-SettingsVariants {
# DataParameters
if ($s.dataParameters -eq 'auto') {
# Auto-generate dataParameters for all non-hidden params
# Auto-generate dataParameters for all non-hidden params.
# Pattern follows 1C Designer / ERP persistence:
# - value set (non-default) → emit value, use=true (implicit)
# - value missing / Custom period → <use>false</use> + <value xsi:nil="true"/>
$autoDP = @()
foreach ($ap in $script:allParams) {
if (-not $ap.hidden) {
$dpItem = New-Object PSObject
$dpItem | Add-Member -NotePropertyName "parameter" -NotePropertyValue $ap.name
$dpItem | Add-Member -NotePropertyName "use" -NotePropertyValue $false
$dpItem | Add-Member -NotePropertyName "userSettingID" -NotePropertyValue "auto"
# For StandardPeriod emit <dcscor:value> with variant inherited from
# the parameter default (Custom if none) — matches how 1C Designer
# persists SettingsParameterValue for period parameters.
if ($ap.type -eq 'StandardPeriod') {
$variant = 'Custom'
$av = $ap.value
if ($null -ne $av) {
if (($av -is [PSCustomObject] -or $av -is [hashtable]) -and $av.variant) {
$variant = "$($av.variant)"
} elseif ("$av") {
$variant = "$av"
}
if ($ap.hidden) { continue }
$dpItem = New-Object PSObject
$dpItem | Add-Member -NotePropertyName "parameter" -NotePropertyValue $ap.name
$dpItem | Add-Member -NotePropertyName "userSettingID" -NotePropertyValue "auto"
$hasMeaningfulValue = $false
if ($ap.type -eq 'StandardPeriod') {
# Inherit variant; Custom is treated as "empty"
$variant = 'Custom'
$av = $ap.value
if ($null -ne $av) {
if (($av -is [PSCustomObject] -or $av -is [hashtable]) -and $av.variant) {
$variant = "$($av.variant)"
} elseif ("$av") {
$variant = "$av"
}
$dpItem | Add-Member -NotePropertyName "value" -NotePropertyValue @{ variant = $variant }
}
$autoDP += $dpItem
$dpItem | Add-Member -NotePropertyName "value" -NotePropertyValue @{ variant = $variant }
if ($variant -ne 'Custom') { $hasMeaningfulValue = $true }
} elseif ($null -ne $ap.value -and "$($ap.value)" -ne '') {
$dpItem | Add-Member -NotePropertyName "value" -NotePropertyValue $ap.value
$dpItem | Add-Member -NotePropertyName "valueType" -NotePropertyValue "$($ap.type)"
$hasMeaningfulValue = $true
} else {
$dpItem | Add-Member -NotePropertyName "nilValue" -NotePropertyValue $true
}
if (-not $hasMeaningfulValue) {
$dpItem | Add-Member -NotePropertyName "use" -NotePropertyValue $false
}
$autoDP += $dpItem
}
if ($autoDP.Count -gt 0) {
Emit-DataParameters -items $autoDP -indent "`t`t`t"
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# skd-compile v1.16 — Compile 1C DCS from JSON
# skd-compile v1.17 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -1569,8 +1569,11 @@ def emit_data_parameters(lines, items, indent):
lines.append(f'{indent}\t\t<dcscor:parameter>{esc_xml(str(dp["parameter"]))}</dcscor:parameter>')
# Value
if dp.get('value') is not None:
if dp.get('nilValue') is True:
lines.append(f'{indent}\t\t<dcscor:value xsi:nil="true"/>')
elif dp.get('value') is not None:
val = dp['value']
vtype = str(dp.get('valueType') or '')
if isinstance(val, dict) and val.get('variant'):
# StandardPeriod
lines.append(f'{indent}\t\t<dcscor:value xsi:type="v8:StandardPeriod">')
@@ -1578,11 +1581,17 @@ def emit_data_parameters(lines, items, indent):
lines.append(f'{indent}\t\t\t<v8:startDate>0001-01-01T00:00:00</v8:startDate>')
lines.append(f'{indent}\t\t\t<v8:endDate>0001-01-01T00:00:00</v8:endDate>')
lines.append(f'{indent}\t\t</dcscor:value>')
elif isinstance(val, bool):
elif vtype == 'boolean' or isinstance(val, bool):
bv = str(val).lower()
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:boolean">{esc_xml(bv)}</dcscor:value>')
elif re.match(r'^\d{4}-\d{2}-\d{2}T', str(val)):
elif re.match(r'^date', vtype) or re.match(r'^\d{4}-\d{2}-\d{2}T', str(val)):
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:dateTime">{esc_xml(str(val))}</dcscor:value>')
elif re.match(r'^decimal', vtype):
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:decimal">{esc_xml(str(val))}</dcscor:value>')
elif re.match(r'^string', vtype):
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:string">{esc_xml(str(val))}</dcscor:value>')
elif re.match(r'^(\u041f\u043b\u0430\u043d\u0421\u0447\u0435\u0442\u043e\u0432|\u0421\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a|\u041f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435|\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442|\u041f\u043b\u0430\u043d\u0412\u0438\u0434\u043e\u0432\u0425\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a|\u041f\u043b\u0430\u043d\u0412\u0438\u0434\u043e\u0432\u0420\u0430\u0441\u0447\u0435\u0442\u0430|\u0411\u0438\u0437\u043d\u0435\u0441\u041f\u0440\u043e\u0446\u0435\u0441\u0441|\u0417\u0430\u0434\u0430\u0447\u0430|\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0421\u0432\u0435\u0434\u0435\u043d\u0438\u0439|\u041f\u043b\u0430\u043d\u041e\u0431\u043c\u0435\u043d\u0430)\.', str(val)) or re.match(r'^(ChartOfAccounts|Catalog|Enum|Document|ChartOfCharacteristicTypes|ChartOfCalculationTypes|BusinessProcess|Task|InformationRegister|ExchangePlan)\.', str(val)):
lines.append(f'{indent}\t\t<dcscor:value xsi:type="dcscor:DesignTimeValue">{esc_xml(str(val))}</dcscor:value>')
else:
lines.append(f'{indent}\t\t<dcscor:value xsi:type="xs:string">{esc_xml(str(val))}</dcscor:value>')
@@ -1815,28 +1824,42 @@ def emit_settings_variants(lines, defn):
# DataParameters
if s.get('dataParameters') == 'auto':
# Auto-generate dataParameters for all non-hidden params
# Auto-generate dataParameters for all non-hidden params.
# Pattern follows 1C Designer / ERP persistence:
# value set (non-default) → emit value, use=true (implicit)
# value missing / Custom period → <use>false</use> + <value xsi:nil="true"/>
auto_dp = []
for ap in _all_params:
if not ap['hidden']:
item = {
'parameter': ap['name'],
'use': False,
'userSettingID': 'auto',
}
# For StandardPeriod emit <dcscor:value> with variant inherited
# from the parameter default (Custom if none) — matches how
# 1C Designer persists SettingsParameterValue for period params.
if ap.get('type') == 'StandardPeriod':
variant = 'Custom'
av = ap.get('value')
if av is not None:
if isinstance(av, dict) and av.get('variant'):
variant = str(av['variant'])
elif str(av):
variant = str(av)
item['value'] = {'variant': variant}
auto_dp.append(item)
if ap['hidden']:
continue
item = {
'parameter': ap['name'],
'userSettingID': 'auto',
}
has_meaningful_value = False
if ap.get('type') == 'StandardPeriod':
variant = 'Custom'
av = ap.get('value')
if av is not None:
if isinstance(av, dict) and av.get('variant'):
variant = str(av['variant'])
elif str(av):
variant = str(av)
item['value'] = {'variant': variant}
if variant != 'Custom':
has_meaningful_value = True
elif ap.get('value') is not None and str(ap.get('value')) != '':
item['value'] = ap['value']
item['valueType'] = str(ap.get('type') or '')
has_meaningful_value = True
else:
item['nilValue'] = True
if not has_meaningful_value:
item['use'] = False
auto_dp.append(item)
if auto_dp:
emit_data_parameters(lines, auto_dp, '\t\t\t')
elif s.get('dataParameters'):
@@ -1,5 +1,5 @@
{
"name": "dataParameters: auto — наследование variant для StandardPeriod",
"name": "dataParameters: auto — наследование значений по всем типам",
"params": { "outputPath": "Template.xml" },
"input": {
"dataSets": [{
@@ -9,7 +9,12 @@
"parameters": [
"Период: СтандартныйПериод = LastMonth @autoDates",
"ПериодБезДефолта: СтандартныйПериод",
"Флаг: boolean = true"
"Флаг: boolean = true",
"Сумма: decimal(15,2) = 0",
"Ставка: decimal(5,2) = 13.5",
"Метка: string(50) = ТестовоеЗначение",
"ПустаяСтрока: string(50)",
"Валюта: СправочникСсылка.Валюты = Справочник.Валюты.EmptyRef"
],
"settingsVariants": [{
"name": "Основной",
@@ -83,6 +83,58 @@
</valueType>
<value xsi:type="xs:boolean">true</value>
</parameter>
<parameter>
<name>Сумма</name>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>15</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<value xsi:type="xs:decimal">0</value>
</parameter>
<parameter>
<name>Ставка</name>
<valueType>
<v8:Type>xs:decimal</v8:Type>
<v8:NumberQualifiers>
<v8:Digits>5</v8:Digits>
<v8:FractionDigits>2</v8:FractionDigits>
<v8:AllowedSign>Any</v8:AllowedSign>
</v8:NumberQualifiers>
</valueType>
<value xsi:type="xs:decimal">13.5</value>
</parameter>
<parameter>
<name>Метка</name>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>50</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
<value xsi:type="xs:string">ТестовоеЗначение</value>
</parameter>
<parameter>
<name>ПустаяСтрока</name>
<valueType>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>50</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</valueType>
</parameter>
<parameter>
<name>Валюта</name>
<valueType>
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.Валюты</v8:Type>
</valueType>
<value xsi:type="dcscor:DesignTimeValue">Справочник.Валюты.EmptyRef</value>
</parameter>
<settingsVariant>
<dcsset:name>Основной</dcsset:name>
<dcsset:presentation xsi:type="v8:LocalStringType">
@@ -94,7 +146,6 @@
<dcsset:settings xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows">
<dcsset:dataParameters>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:use>false</dcscor:use>
<dcscor:parameter>Период</dcscor:parameter>
<dcscor:value xsi:type="v8:StandardPeriod">
<v8:variant xsi:type="v8:StandardPeriodVariant">LastMonth</v8:variant>
@@ -114,10 +165,36 @@
<dcsset:userSettingID>UUID-002</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:use>false</dcscor:use>
<dcscor:parameter>Флаг</dcscor:parameter>
<dcscor:value xsi:type="xs:boolean">true</dcscor:value>
<dcsset:userSettingID>UUID-003</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Сумма</dcscor:parameter>
<dcscor:value xsi:type="xs:decimal">0</dcscor:value>
<dcsset:userSettingID>UUID-004</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Ставка</dcscor:parameter>
<dcscor:value xsi:type="xs:decimal">13.5</dcscor:value>
<dcsset:userSettingID>UUID-005</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Метка</dcscor:parameter>
<dcscor:value xsi:type="xs:string">ТестовоеЗначение</dcscor:value>
<dcsset:userSettingID>UUID-006</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:use>false</dcscor:use>
<dcscor:parameter>ПустаяСтрока</dcscor:parameter>
<dcscor:value xsi:nil="true"/>
<dcsset:userSettingID>UUID-007</dcsset:userSettingID>
</dcscor:item>
<dcscor:item xsi:type="dcsset:SettingsParameterValue">
<dcscor:parameter>Валюта</dcscor:parameter>
<dcscor:value xsi:type="dcscor:DesignTimeValue">Справочник.Валюты.EmptyRef</dcscor:value>
<dcsset:userSettingID>UUID-008</dcsset:userSettingID>
</dcscor:item>
</dcsset:dataParameters>
<dcsset:item xsi:type="dcsset:StructureItemGroup">
<dcsset:order>