mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-07 20:20:20 +03:00
fix(meta-compile): позиция условных стандартных реквизитов бухрегистра
У бухрегистра условные стандартные реквизиты стоят по обе стороны фикс-списка: PeriodAdjustment — перед Account, пары субконто — после Period. Эмиттер же клал все ключи, которых нет в списке типа, скопом вперёд, поэтому порядок расходился с выгрузкой. Разделено на три части: ключи вне списка по умолчанию идут ПЕРЕД ним (легаси вроде ExchangeDate у части планов обмена), подходящие под хвостовой шаблон типа — ПОСЛЕ, а именованные условные (PeriodAdjustment) занимают своё место в самом списке и эмитятся при наличии в DSL. Пары субконто заданы ШАБЛОНОМ, а не списком имён: их количество определяется свойством MaxExtDimensionCount плана счетов. В корпусе везде 3, но это однородность выборки, а не правило, и список из ExtDimension1..3 закрыл бы только наблюдаемый случай. Хвост сортируется по номеру, внутри номера ExtDimensionN идёт перед ExtDimensionTypeN. Проверка: - синтетика с ПЯТЬЮ парами субконто и перемешанными ключами на входе даёт канонический порядок с пропусками номеров; порты байт-в-байт (modulo UUID); - раундтрип «все виды» (112 объектов, 42 вида): порядок ≠ 0, TOTAL diff 0; - юнит-тесты 648/648 ps1, 645/648 py; 1С-сертификация accounting-register ✓. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0fc2d738a2
commit
7892c9fbfa
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.86 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.87 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -1300,7 +1300,7 @@ $script:standardAttributesByType = @{
|
||||
"Enum" = @("Order","Ref")
|
||||
"InformationRegister" = @("Active","LineNumber","Recorder","Period")
|
||||
"AccumulationRegister" = @("Active","LineNumber","Recorder","Period")
|
||||
"AccountingRegister" = @("Account","Active","LineNumber","Recorder","Period")
|
||||
"AccountingRegister" = @("PeriodAdjustment","Account","Active","LineNumber","Recorder","Period")
|
||||
"CalculationRegister" = @("Active","Recorder","LineNumber","RegistrationPeriod","CalculationType","ReversingEntry")
|
||||
"ChartOfAccounts" = @("PredefinedDataName","Order","OffBalance","Type","Description","Code","Parent","Predefined","DeletionMark","Ref")
|
||||
"ChartOfCharacteristicTypes" = @("PredefinedDataName","ValueType","Description","Code","IsFolder","Parent","Predefined","DeletionMark","Ref")
|
||||
@@ -1308,6 +1308,8 @@ $script:standardAttributesByType = @{
|
||||
"BusinessProcess" = @("Started","HeadTask","Completed","Ref","DeletionMark","Date","Number")
|
||||
"Task" = @("Executed","Description","RoutePoint","BusinessProcess","Ref","DeletionMark","Date","Number")
|
||||
# Порядок в каждом списке — канон выгрузки, снят с корпуса acc+erp (внутри типа разброса нет).
|
||||
# Условные члены перечислены в $script:stdAttrOptional — они эмитятся только при наличии
|
||||
# ключа в DSL, но позицию берут отсюда.
|
||||
# У ПВХ IsFolder входит в фикс-список: он есть у всех 23 объектов корпуса с этим блоком.
|
||||
# У бухрегистра PeriodAdjustment, наоборот, условен (1 из 3) — он приходит как «лишний»
|
||||
# ключ и эмитится ПЕРЕД фикс-списком, что совпадает с платформой.
|
||||
@@ -1430,6 +1432,20 @@ function Emit-StandardAttribute {
|
||||
# - stdAttrProfile[тип]: профиль материализованного блока (пусто = schema-дефолт), поверх — DSL-override.
|
||||
# Миграция типа = добавить его в stdAttrConditionalTypes + stdAttrProfile и переснять снэпшоты; КОД НЕ ТРОГАЕМ.
|
||||
$script:stdAttrConditionalTypes = @('Catalog', 'ExchangePlan', 'ChartOfCharacteristicTypes', 'ChartOfAccounts', 'ChartOfCalculationTypes', 'Document')
|
||||
|
||||
# Условные члены списка типа: позиция берётся из $script:standardAttributesByType, а сам
|
||||
# реквизит эмитится только при наличии ключа в DSL (у бухрегистра PeriodAdjustment — 2 из 5).
|
||||
$script:stdAttrOptional = @{
|
||||
"AccountingRegister" = @("PeriodAdjustment")
|
||||
}
|
||||
|
||||
# Хвостовая группа: реквизиты, которых нет в списке типа и которые идут ПОСЛЕ него.
|
||||
# У бухрегистра это пары субконто. Именами их не перечислить: их количество задаётся
|
||||
# свойством MaxExtDimensionCount плана счетов, а не константой (в корпусе везде 3, но
|
||||
# это однородность выборки, а не правило). Поэтому — шаблон, а не список.
|
||||
$script:stdAttrTailPattern = @{
|
||||
"AccountingRegister" = '^ExtDimension(Type)?\d+$'
|
||||
}
|
||||
function Emit-StandardAttributes {
|
||||
param([string]$indent, [string]$objectType)
|
||||
$attrs = $script:standardAttributesByType[$objectType]
|
||||
@@ -1439,12 +1455,27 @@ function Emit-StandardAttributes {
|
||||
if ($conditional -and $null -eq $sa) { return } # условный тип без кастомизации → блока нет
|
||||
if ($sa -is [string] -and $sa -eq '') { return } # opt-out `standardAttributes:""` (дом-конвенция суппресса, ~5% регистров опускают all-default блок — правило не выводимо)
|
||||
$profile = $script:stdAttrProfile[$objectType]; if (-not $profile) { $profile = @{} }
|
||||
# Доп. (опциональные) стандартные реквизиты вне фикс-списка типа — напр. ExchangeDate у части ПланОбмена
|
||||
# (легаси, присутствие не выводится из свойств). Эмитим по факту наличия ключа в DSL, ПЕРЕД фикс-списком (их позиция).
|
||||
$extra = @()
|
||||
if ($sa) { foreach ($k in $sa.PSObject.Properties.Name) { if ($attrs -notcontains $k) { $extra += $k } } }
|
||||
# Список типа задаёт ПОРЯДОК всех известных стандартных реквизитов, включая условные:
|
||||
# их позиция бывает и до, и после обязательных (у бухрегистра PeriodAdjustment идёт
|
||||
# перед Account, а ExtDimension1..3/ExtDimensionType1..3 — после Period), поэтому
|
||||
# «условные скопом вперёд» не выражает канон. Условный эмитим по факту наличия в DSL.
|
||||
$optional = $script:stdAttrOptional[$objectType]; if (-not $optional) { $optional = @() }
|
||||
# Ключи, которых нет в списке типа ВООБЩЕ. По умолчанию их позиция — ПЕРЕД списком
|
||||
# (легаси вроде ExchangeDate у части планов обмена). Подходящие под хвостовой шаблон
|
||||
# типа идут ПОСЛЕ, в порядке номера, а внутри номера — сначала ExtDimensionN, затем
|
||||
# ExtDimensionTypeN (порядок платформы).
|
||||
$tailRe = $script:stdAttrTailPattern[$objectType]
|
||||
$extra = @(); $tail = @()
|
||||
if ($sa) {
|
||||
foreach ($k in $sa.PSObject.Properties.Name) {
|
||||
if ($attrs -contains $k) { continue }
|
||||
if ($tailRe -and $k -match $tailRe) { $tail += $k } else { $extra += $k }
|
||||
}
|
||||
}
|
||||
$tail = @($tail | Sort-Object @{e={[int]([regex]::Match($_, '\d+').Value)}}, @{e={ if ($_ -match 'Type\d+$') { 1 } else { 0 } }})
|
||||
X "$indent<StandardAttributes>"
|
||||
foreach ($a in ($extra + $attrs)) {
|
||||
foreach ($a in ($extra + $attrs + $tail)) {
|
||||
if (($optional -contains $a) -and (-not ($sa -and $sa.PSObject.Properties[$a]))) { continue }
|
||||
$ov = @{}
|
||||
if ($profile.ContainsKey($a)) { foreach ($k in $profile[$a].Keys) { $ov[$k] = $profile[$a][$k] } }
|
||||
if ($sa) { # DSL-override применяем всегда при наличии ключа (для не-условных типов тоже, напр. ExchangePlan)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# meta-compile v1.86 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.87 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -1333,7 +1333,7 @@ standard_attributes_by_type = {
|
||||
'Enum': ['Order', 'Ref'],
|
||||
'InformationRegister': ['Active', 'LineNumber', 'Recorder', 'Period'],
|
||||
'AccumulationRegister': ['Active', 'LineNumber', 'Recorder', 'Period'],
|
||||
'AccountingRegister': ['Account', 'Active', 'LineNumber', 'Recorder', 'Period'],
|
||||
'AccountingRegister': ['PeriodAdjustment', 'Account', 'Active', 'LineNumber', 'Recorder', 'Period'],
|
||||
'CalculationRegister': ['Active', 'Recorder', 'LineNumber', 'RegistrationPeriod', 'CalculationType', 'ReversingEntry'],
|
||||
'ChartOfAccounts': ['PredefinedDataName', 'Order', 'OffBalance', 'Type', 'Description', 'Code', 'Parent', 'Predefined', 'DeletionMark', 'Ref'],
|
||||
'ChartOfCharacteristicTypes': ['PredefinedDataName', 'ValueType', 'Description', 'Code', 'IsFolder', 'Parent', 'Predefined', 'DeletionMark', 'Ref'],
|
||||
@@ -1467,6 +1467,20 @@ def emit_standard_attribute(indent, attr_name, ov=None):
|
||||
# std_attr_conditional_types: типы, где блок только при кастомизации (DSL-ключ standardAttributes).
|
||||
# Прочие типы → блок всегда (текущее поведение). Миграция типа = +строчка в оба справочника + снэпшоты.
|
||||
std_attr_conditional_types = {'Catalog', 'ExchangePlan', 'ChartOfCharacteristicTypes', 'ChartOfAccounts', 'ChartOfCalculationTypes', 'Document'}
|
||||
|
||||
# Условные члены списка типа: позиция берётся из standard_attributes_by_type, а сам
|
||||
# реквизит эмитится только при наличии ключа в DSL (у бухрегистра PeriodAdjustment — 2 из 5).
|
||||
std_attr_optional = {
|
||||
'AccountingRegister': ('PeriodAdjustment',),
|
||||
}
|
||||
|
||||
# Хвостовая группа: реквизиты, которых нет в списке типа и которые идут ПОСЛЕ него.
|
||||
# У бухрегистра это пары субконто. Именами их не перечислить: их количество задаётся
|
||||
# свойством MaxExtDimensionCount плана счетов, а не константой (в корпусе везде 3, но
|
||||
# это однородность выборки, а не правило). Поэтому — шаблон, а не список.
|
||||
std_attr_tail_pattern = {
|
||||
'AccountingRegister': r'^ExtDimension(Type)?\d+$',
|
||||
}
|
||||
def emit_standard_attributes(indent, object_type):
|
||||
attrs = standard_attributes_by_type.get(object_type)
|
||||
if not attrs:
|
||||
@@ -1478,11 +1492,30 @@ def emit_standard_attributes(indent, object_type):
|
||||
if isinstance(sa, str) and sa == '':
|
||||
return # opt-out `standardAttributes:""` (дом-конвенция суппресса, ~5% регистров опускают all-default блок)
|
||||
profile = std_attr_profile.get(object_type, {})
|
||||
# Доп. (опциональные) стандартные реквизиты вне фикс-списка — напр. ExchangeDate у части ПланОбмена
|
||||
# (легаси, присутствие не выводится). Эмитим по факту ключа в DSL, ПЕРЕД фикс-списком (их позиция).
|
||||
extra = [k for k in sa if k not in attrs] if isinstance(sa, dict) else []
|
||||
# Список типа задаёт ПОРЯДОК всех известных стандартных реквизитов, включая условные:
|
||||
# их позиция бывает и до, и после обязательных (у бухрегистра PeriodAdjustment идёт
|
||||
# перед Account, а ExtDimension1..3/ExtDimensionType1..3 — после Period), поэтому
|
||||
# «условные скопом вперёд» не выражает канон. Условный эмитим по факту наличия в DSL.
|
||||
optional = std_attr_optional.get(object_type, ())
|
||||
# Ключи, которых нет в списке типа ВООБЩЕ. По умолчанию их позиция — ПЕРЕД списком
|
||||
# (легаси вроде ExchangeDate у части планов обмена). Подходящие под хвостовой шаблон
|
||||
# типа идут ПОСЛЕ, в порядке номера, а внутри номера — сначала ExtDimensionN, затем
|
||||
# ExtDimensionTypeN (порядок платформы).
|
||||
tail_re = std_attr_tail_pattern.get(object_type)
|
||||
extra, tail = [], []
|
||||
if isinstance(sa, dict):
|
||||
for k in sa:
|
||||
if k in attrs:
|
||||
continue
|
||||
if tail_re and re.match(tail_re, k):
|
||||
tail.append(k)
|
||||
else:
|
||||
extra.append(k)
|
||||
tail.sort(key=lambda k: (int(re.search(r'\d+', k).group()), 1 if re.search(r'Type\d+$', k) else 0))
|
||||
X(f'{indent}<StandardAttributes>')
|
||||
for a in extra + list(attrs):
|
||||
for a in extra + list(attrs) + tail:
|
||||
if a in optional and not (isinstance(sa, dict) and a in sa):
|
||||
continue
|
||||
ov = dict(profile.get(a, {}))
|
||||
if isinstance(sa, dict):
|
||||
d = sa.get(a)
|
||||
|
||||
Reference in New Issue
Block a user