mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
8c7c442705
meta-compile v1.12 (ps1 + py): Parse-AttributeShorthand принимает поле choiceHistoryOnInput в object-форме аттрибута, Emit-Attribute эмитит его вместо хардкода Auto. Покрывает атрибуты Catalog/Document/TabularSection (Emit-Attribute, единственная точка эмиссии в работе). Другие контексты (register dimensions, resources, etc.) пока эмитят Auto — расширим при необходимости. build-webtest-config: реквизит Документ.ПриходнаяНакладная.Контрагент получил choiceHistoryOnInput='DontUse'. Это убирает 1С-историю выбора для поля и фиксит pre-existing flake 04-selectvalue/direct-form: после 03 значение «ООО Север» оставалось в истории и selectValue выбирал его через dropdown вместо ожидаемой формы выбора. Live: полный регресс 12/12 впервые зелёный (5m 28s). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2742 lines
124 KiB
Python
2742 lines
124 KiB
Python
#!/usr/bin/env python3
|
||
# meta-compile v1.12 — Compile 1C metadata object from JSON
|
||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||
|
||
import argparse
|
||
import json
|
||
import os
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
import tempfile
|
||
import uuid
|
||
import xml.etree.ElementTree as ET
|
||
|
||
sys.stdout.reconfigure(encoding="utf-8")
|
||
sys.stderr.reconfigure(encoding="utf-8")
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Inline utilities
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def esc_xml(s):
|
||
return s.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"')
|
||
|
||
def new_uuid():
|
||
return str(uuid.uuid4())
|
||
|
||
def write_utf8_bom(path, content):
|
||
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
|
||
f.write(content)
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# XML builder (lines list)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
lines = []
|
||
|
||
def X(text):
|
||
lines.append(text)
|
||
|
||
def emit_mltext(indent, tag, text):
|
||
if not text:
|
||
X(f'{indent}<{tag}/>')
|
||
return
|
||
X(f'{indent}<{tag}>')
|
||
X(f'{indent}\t<v8:item>')
|
||
X(f'{indent}\t\t<v8:lang>ru</v8:lang>')
|
||
X(f'{indent}\t\t<v8:content>{esc_xml(text)}</v8:content>')
|
||
X(f'{indent}\t</v8:item>')
|
||
X(f'{indent}</{tag}>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# CamelCase splitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def split_camel_case(name):
|
||
if not name:
|
||
return name
|
||
result = re.sub(r'([а-яё])([А-ЯЁ])', r'\1 \2', name)
|
||
result = re.sub(r'([a-z])([A-Z])', r'\1 \2', result)
|
||
if len(result) > 1:
|
||
result = result[0] + result[1:].lower()
|
||
return result
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 1. Load and validate JSON
|
||
# ---------------------------------------------------------------------------
|
||
|
||
parser = argparse.ArgumentParser(allow_abbrev=False)
|
||
parser.add_argument('-JsonPath', required=True)
|
||
parser.add_argument('-OutputDir', required=True)
|
||
args = parser.parse_args()
|
||
|
||
json_path = args.JsonPath
|
||
output_dir = args.OutputDir
|
||
|
||
if not os.path.isfile(json_path):
|
||
print(f'File not found: {json_path}', file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
with open(json_path, 'r', encoding='utf-8-sig') as f:
|
||
json_text = f.read()
|
||
|
||
defn = json.loads(json_text)
|
||
|
||
# --- Batch mode: JSON array of objects ---
|
||
if isinstance(defn, list):
|
||
batch_ok = 0
|
||
batch_fail = 0
|
||
for idx, item in enumerate(defn, 1):
|
||
tmp_fd, tmp_path = tempfile.mkstemp(suffix='.json', prefix=f'meta-compile-batch-{idx}-')
|
||
try:
|
||
with os.fdopen(tmp_fd, 'w', encoding='utf-8') as f:
|
||
json.dump(item, f, ensure_ascii=False, indent=2)
|
||
rc = subprocess.call([sys.executable, __file__, '-JsonPath', tmp_path, '-OutputDir', output_dir])
|
||
if rc == 0:
|
||
batch_ok += 1
|
||
else:
|
||
batch_fail += 1
|
||
finally:
|
||
if os.path.exists(tmp_path):
|
||
os.unlink(tmp_path)
|
||
print()
|
||
print(f"=== Batch: {len(defn)} objects, {batch_ok} compiled, {batch_fail} failed ===")
|
||
sys.exit(1 if batch_fail > 0 else 0)
|
||
|
||
# Normalize field synonyms: accept "objectType" as alias for "type"
|
||
if not defn.get('type') and defn.get('objectType'):
|
||
defn['type'] = defn['objectType']
|
||
|
||
# Object type synonyms (Russian -> English)
|
||
object_type_synonyms = {
|
||
'Справочник': 'Catalog',
|
||
'Каталог': 'Catalog',
|
||
'Документ': 'Document',
|
||
'Перечисление': 'Enum',
|
||
'Константа': 'Constant',
|
||
'РегистрСведений': 'InformationRegister',
|
||
'РегистрНакопления': 'AccumulationRegister',
|
||
'РегистрБухгалтерии': 'AccountingRegister',
|
||
'РегистрРасчёта': 'CalculationRegister',
|
||
'РегистрРасчета': 'CalculationRegister',
|
||
'ПланСчетов': 'ChartOfAccounts',
|
||
'ПланВидовХарактеристик': 'ChartOfCharacteristicTypes',
|
||
'ПланВидовРасчёта': 'ChartOfCalculationTypes',
|
||
'ПланВидовРасчета': 'ChartOfCalculationTypes',
|
||
'БизнесПроцесс': 'BusinessProcess',
|
||
'Задача': 'Task',
|
||
'ПланОбмена': 'ExchangePlan',
|
||
'ЖурналДокументов': 'DocumentJournal',
|
||
'Отчёт': 'Report',
|
||
'Отчет': 'Report',
|
||
'Обработка': 'DataProcessor',
|
||
'ОбщийМодуль': 'CommonModule',
|
||
'РегламентноеЗадание': 'ScheduledJob',
|
||
'ПодпискаНаСобытие': 'EventSubscription',
|
||
'HTTPСервис': 'HTTPService',
|
||
'ВебСервис': 'WebService',
|
||
'ОпределяемыйТип': 'DefinedType',
|
||
}
|
||
|
||
# Enum property value synonyms — model often gets these slightly wrong
|
||
enum_value_aliases = {
|
||
# RegisterType (AccumulationRegister)
|
||
'Balances': 'Balance', 'Остатки': 'Balance', 'Обороты': 'Turnovers',
|
||
# WriteMode (InformationRegister)
|
||
'RecordSubordinate': 'RecorderSubordinate', 'Subordinate': 'RecorderSubordinate',
|
||
'ПодчинениеРегистратору': 'RecorderSubordinate', 'Независимый': 'Independent',
|
||
# DependenceOnCalculationTypes (ChartOfCalculationTypes)
|
||
'NotDependOnCalculationTypes': 'DontUse', 'NoDependence': 'DontUse', 'NotUsed': 'DontUse',
|
||
'Depend': 'OnActionPeriod', 'ПоПериодуДействия': 'OnActionPeriod',
|
||
# InformationRegisterPeriodicity
|
||
'None': 'Nonperiodical', 'Daily': 'Day', 'Monthly': 'Month',
|
||
'Quarterly': 'Quarter', 'Yearly': 'Year',
|
||
'Непериодический': 'Nonperiodical', 'Секунда': 'Second', 'День': 'Day',
|
||
'Месяц': 'Month', 'Квартал': 'Quarter', 'Год': 'Year',
|
||
'ПозицияРегистратора': 'RecorderPosition',
|
||
# DataLockControlMode
|
||
'Автоматический': 'Automatic', 'Управляемый': 'Managed',
|
||
# FullTextSearch
|
||
'Использовать': 'Use', 'НеИспользовать': 'DontUse',
|
||
# Posting
|
||
'Разрешить': 'Allow', 'Запретить': 'Deny',
|
||
# EditType
|
||
'ВДиалоге': 'InDialog', 'ВСписке': 'InList', 'ОбаСпособа': 'BothWays',
|
||
# DefaultPresentation
|
||
'ВВидеНаименования': 'AsDescription', 'ВВидеКода': 'AsCode',
|
||
# FillChecking
|
||
'НеПроверять': 'DontCheck', 'Ошибка': 'ShowError', 'Предупреждение': 'ShowWarning',
|
||
# Indexing
|
||
'НеИндексировать': 'DontIndex', 'Индексировать': 'Index',
|
||
'ИндексироватьСДопУпорядочиванием': 'IndexWithAdditionalOrder',
|
||
}
|
||
|
||
# Valid enum values per property (from meta-validate)
|
||
valid_enum_values = {
|
||
'RegisterType': ['Balance', 'Turnovers'],
|
||
'WriteMode': ['Independent', 'RecorderSubordinate'],
|
||
'InformationRegisterPeriodicity': ['Nonperiodical', 'Second', 'Day', 'Month', 'Quarter', 'Year', 'RecorderPosition'],
|
||
'DependenceOnCalculationTypes': ['DontUse', 'OnActionPeriod'],
|
||
'DataLockControlMode': ['Automatic', 'Managed'],
|
||
'FullTextSearch': ['Use', 'DontUse'],
|
||
'DataHistory': ['Use', 'DontUse'],
|
||
'DefaultPresentation': ['AsDescription', 'AsCode'],
|
||
'Posting': ['Allow', 'Deny'],
|
||
'RealTimePosting': ['Allow', 'Deny'],
|
||
'EditType': ['InDialog', 'InList', 'BothWays'],
|
||
'HierarchyType': ['HierarchyFoldersAndItems', 'HierarchyItemsOnly'],
|
||
'CodeType': ['String', 'Number'],
|
||
'CodeAllowedLength': ['Variable', 'Fixed'],
|
||
'NumberType': ['String', 'Number'],
|
||
'NumberAllowedLength': ['Variable', 'Fixed'],
|
||
'RegisterRecordsDeletion': ['AutoDelete', 'AutoDeleteOnUnpost', 'AutoDeleteOff'],
|
||
'RegisterRecordsWritingOnPost': ['WriteModified', 'WriteSelected', 'WriteAll'],
|
||
'ReturnValuesReuse': ['DontUse', 'DuringRequest', 'DuringSession'],
|
||
'ReuseSessions': ['DontUse', 'AutoUse'],
|
||
'FillChecking': ['DontCheck', 'ShowError', 'ShowWarning'],
|
||
'Indexing': ['DontIndex', 'Index', 'IndexWithAdditionalOrder'],
|
||
'SubordinationUse': ['ToItems', 'ToFolders', 'ToFoldersAndItems'],
|
||
'CodeSeries': ['WholeCatalog', 'WithinSubordination'],
|
||
'ChoiceMode': ['BothWays', 'QuickChoice', 'FromForm'],
|
||
}
|
||
|
||
def normalize_enum_value(prop_name, value):
|
||
# 1. Check alias dictionary — silent auto-correct
|
||
if value in enum_value_aliases:
|
||
return enum_value_aliases[value]
|
||
# 2. Case-insensitive match against valid values — silent
|
||
valid = valid_enum_values.get(prop_name)
|
||
if valid:
|
||
for v in valid:
|
||
if v.lower() == value.lower():
|
||
return v
|
||
# 3. Known property, unknown value — error with hint
|
||
print(f"Invalid value '{value}' for property '{prop_name}'. Valid values: {', '.join(valid)}", file=sys.stderr)
|
||
sys.exit(1)
|
||
# 4. Unknown property — pass-through (no validation data)
|
||
return value
|
||
|
||
def get_enum_prop(prop_name, field_name, default):
|
||
val = defn.get(field_name)
|
||
raw = str(val) if val else default
|
||
return normalize_enum_value(prop_name, raw)
|
||
|
||
if not defn.get('type'):
|
||
print("JSON must have 'type' field", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
obj_type = str(defn['type'])
|
||
if obj_type in object_type_synonyms:
|
||
obj_type = object_type_synonyms[obj_type]
|
||
|
||
valid_types = [
|
||
'Catalog', 'Document', 'Enum', 'Constant', 'InformationRegister',
|
||
'AccumulationRegister', 'AccountingRegister', 'CalculationRegister',
|
||
'ChartOfAccounts', 'ChartOfCharacteristicTypes', 'ChartOfCalculationTypes',
|
||
'BusinessProcess', 'Task', 'ExchangePlan', 'DocumentJournal',
|
||
'Report', 'DataProcessor', 'CommonModule', 'ScheduledJob',
|
||
'EventSubscription', 'HTTPService', 'WebService', 'DefinedType',
|
||
]
|
||
if obj_type not in valid_types:
|
||
print(f"Unsupported type: {obj_type}. Valid: {', '.join(valid_types)}", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
if not defn.get('name'):
|
||
print("JSON must have 'name' field", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
obj_name = str(defn['name'])
|
||
|
||
# Auto-synonym
|
||
synonym = str(defn['synonym']) if defn.get('synonym') else split_camel_case(obj_name)
|
||
comment = str(defn['comment']) if defn.get('comment') else ''
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 4. Type system
|
||
# ---------------------------------------------------------------------------
|
||
|
||
type_synonyms = {
|
||
'число': 'Number',
|
||
'строка': 'String',
|
||
'булево': 'Boolean',
|
||
'дата': 'Date',
|
||
'датавремя': 'DateTime',
|
||
'number': 'Number',
|
||
'string': 'String',
|
||
'boolean': 'Boolean',
|
||
'date': 'Date',
|
||
'datetime': 'DateTime',
|
||
'bool': 'Boolean',
|
||
# Reference synonyms (Russian, lowercase)
|
||
'справочникссылка': 'CatalogRef',
|
||
'документссылка': 'DocumentRef',
|
||
'перечислениессылка': 'EnumRef',
|
||
'плансчетовссылка': 'ChartOfAccountsRef',
|
||
'планвидовхарактеристикссылка': 'ChartOfCharacteristicTypesRef',
|
||
'планвидоврасчётассылка': 'ChartOfCalculationTypesRef',
|
||
'планвидоврасчетассылка': 'ChartOfCalculationTypesRef',
|
||
'планобменассылка': 'ExchangePlanRef',
|
||
'бизнеспроцессссылка': 'BusinessProcessRef',
|
||
'задачассылка': 'TaskRef',
|
||
'определяемыйтип': 'DefinedType',
|
||
'definedtype': 'DefinedType',
|
||
# English lowercase ref synonyms
|
||
'catalogref': 'CatalogRef',
|
||
'documentref': 'DocumentRef',
|
||
'enumref': 'EnumRef',
|
||
}
|
||
|
||
def resolve_type_str(type_str):
|
||
if not type_str:
|
||
return type_str
|
||
# Parameterized types: Number(15,2), Строка(100), etc.
|
||
m = re.match(r'^([^(]+)\((.+)\)$', type_str)
|
||
if m:
|
||
base_name = m.group(1).strip()
|
||
params = m.group(2)
|
||
resolved = type_synonyms.get(base_name.lower())
|
||
if resolved:
|
||
return f'{resolved}({params})'
|
||
return type_str
|
||
# Reference types: СправочникСсылка.Организации -> CatalogRef.Организации
|
||
if '.' in type_str:
|
||
dot_idx = type_str.index('.')
|
||
prefix = type_str[:dot_idx]
|
||
suffix = type_str[dot_idx:] # includes the dot
|
||
resolved = type_synonyms.get(prefix.lower())
|
||
if resolved:
|
||
return f'{resolved}{suffix}'
|
||
return type_str
|
||
# Simple name lookup
|
||
resolved = type_synonyms.get(type_str.lower())
|
||
if resolved:
|
||
return resolved
|
||
return type_str
|
||
|
||
def emit_type_content(indent, type_str):
|
||
if not type_str:
|
||
return
|
||
# Composite type: "Type1 + Type2 + Type3"
|
||
if ' + ' in type_str:
|
||
parts = [p.strip() for p in type_str.split('+')]
|
||
for part in parts:
|
||
emit_type_content(indent, part)
|
||
return
|
||
type_str = resolve_type_str(type_str)
|
||
# Boolean
|
||
if type_str == 'Boolean':
|
||
X(f'{indent}<v8:Type>xs:boolean</v8:Type>')
|
||
return
|
||
# String or String(N)
|
||
m = re.match(r'^String(\((\d+)\))?$', type_str)
|
||
if m:
|
||
length = m.group(2) if m.group(2) else '10'
|
||
X(f'{indent}<v8:Type>xs:string</v8:Type>')
|
||
X(f'{indent}<v8:StringQualifiers>')
|
||
X(f'{indent}\t<v8:Length>{length}</v8:Length>')
|
||
X(f'{indent}\t<v8:AllowedLength>Variable</v8:AllowedLength>')
|
||
X(f'{indent}</v8:StringQualifiers>')
|
||
return
|
||
# Number without params -> Number(10,0)
|
||
if type_str == 'Number':
|
||
X(f'{indent}<v8:Type>xs:decimal</v8:Type>')
|
||
X(f'{indent}<v8:NumberQualifiers>')
|
||
X(f'{indent}\t<v8:Digits>10</v8:Digits>')
|
||
X(f'{indent}\t<v8:FractionDigits>0</v8:FractionDigits>')
|
||
X(f'{indent}\t<v8:AllowedSign>Any</v8:AllowedSign>')
|
||
X(f'{indent}</v8:NumberQualifiers>')
|
||
return
|
||
|
||
# Number(D,F) or Number(D,F,nonneg)
|
||
m = re.match(r'^Number\((\d+),(\d+)(,nonneg)?\)$', type_str)
|
||
if m:
|
||
digits = m.group(1)
|
||
fraction = m.group(2)
|
||
sign = 'Nonnegative' if m.group(3) else 'Any'
|
||
X(f'{indent}<v8:Type>xs:decimal</v8:Type>')
|
||
X(f'{indent}<v8:NumberQualifiers>')
|
||
X(f'{indent}\t<v8:Digits>{digits}</v8:Digits>')
|
||
X(f'{indent}\t<v8:FractionDigits>{fraction}</v8:FractionDigits>')
|
||
X(f'{indent}\t<v8:AllowedSign>{sign}</v8:AllowedSign>')
|
||
X(f'{indent}</v8:NumberQualifiers>')
|
||
return
|
||
# Date / DateTime
|
||
if type_str == 'Date':
|
||
X(f'{indent}<v8:Type>xs:dateTime</v8:Type>')
|
||
X(f'{indent}<v8:DateQualifiers>')
|
||
X(f'{indent}\t<v8:DateFractions>Date</v8:DateFractions>')
|
||
X(f'{indent}</v8:DateQualifiers>')
|
||
return
|
||
if type_str == 'DateTime':
|
||
X(f'{indent}<v8:Type>xs:dateTime</v8:Type>')
|
||
X(f'{indent}<v8:DateQualifiers>')
|
||
X(f'{indent}\t<v8:DateFractions>DateTime</v8:DateFractions>')
|
||
X(f'{indent}</v8:DateQualifiers>')
|
||
return
|
||
# DefinedType
|
||
m = re.match(r'^DefinedType\.(.+)$', type_str)
|
||
if m:
|
||
dt_name = m.group(1)
|
||
X(f'{indent}<v8:TypeSet>cfg:DefinedType.{dt_name}</v8:TypeSet>')
|
||
return
|
||
# ValueStorage
|
||
if type_str == 'ValueStorage':
|
||
X(f'{indent}<v8:Type>xs:base64Binary</v8:Type>')
|
||
return
|
||
|
||
# Reference types — use local xmlns declaration for 1C compatibility
|
||
m = re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.(.+)$', type_str)
|
||
if m:
|
||
X(f'{indent}<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{type_str}</v8:Type>')
|
||
return
|
||
# Fallback
|
||
X(f'{indent}<v8:Type>{type_str}</v8:Type>')
|
||
|
||
def emit_value_type(indent, type_str):
|
||
X(f'{indent}<Type>')
|
||
emit_type_content(f'{indent}\t', type_str)
|
||
X(f'{indent}</Type>')
|
||
|
||
def emit_fill_value(indent, type_str):
|
||
if not type_str:
|
||
X(f'{indent}<FillValue xsi:nil="true"/>')
|
||
return
|
||
type_str = resolve_type_str(type_str)
|
||
if type_str == 'Boolean':
|
||
X(f'{indent}<FillValue xsi:type="xs:boolean">false</FillValue>')
|
||
return
|
||
if re.match(r'^String', type_str):
|
||
X(f'{indent}<FillValue xsi:type="xs:string"/>')
|
||
return
|
||
if re.match(r'^Number', type_str):
|
||
X(f'{indent}<FillValue xsi:type="xs:decimal">0</FillValue>')
|
||
return
|
||
if re.match(r'^(Date|DateTime)$', type_str):
|
||
X(f'{indent}<FillValue xsi:nil="true"/>')
|
||
return
|
||
X(f'{indent}<FillValue xsi:nil="true"/>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 5. Attribute shorthand parser
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def build_type_str(obj):
|
||
t = str(obj.get('valueType') or obj.get('type') or '')
|
||
if t and '(' not in t:
|
||
if t == 'String' and obj.get('length'):
|
||
t = f"String({obj['length']})"
|
||
elif t == 'Number' and obj.get('length'):
|
||
prec = obj.get('precision', 0)
|
||
nn = ',nonneg' if obj.get('nonneg') or obj.get('nonnegative') else ''
|
||
t = f"Number({obj['length']},{prec}{nn})"
|
||
return t
|
||
|
||
def parse_attribute_shorthand(val):
|
||
if isinstance(val, str):
|
||
parsed = {
|
||
'name': '',
|
||
'type': '',
|
||
'synonym': '',
|
||
'comment': '',
|
||
'flags': [],
|
||
'fillChecking': '',
|
||
'indexing': '',
|
||
}
|
||
parts = val.split('|', 1)
|
||
main_part = parts[0].strip()
|
||
if len(parts) > 1:
|
||
flag_str = parts[1].strip()
|
||
parsed['flags'] = [f.strip().lower() for f in flag_str.split(',') if f.strip()]
|
||
colon_parts = main_part.split(':', 1)
|
||
parsed['name'] = colon_parts[0].strip()
|
||
if len(colon_parts) > 1:
|
||
parsed['type'] = colon_parts[1].strip()
|
||
parsed['synonym'] = split_camel_case(parsed['name'])
|
||
return parsed
|
||
# Object form
|
||
name = str(val.get('name', ''))
|
||
return {
|
||
'name': name,
|
||
'type': build_type_str(val),
|
||
'synonym': str(val['synonym']) if val.get('synonym') else split_camel_case(name),
|
||
'comment': str(val['comment']) if val.get('comment') else '',
|
||
'flags': list(val.get('flags', [])),
|
||
'fillChecking': str(val['fillChecking']) if val.get('fillChecking') else '',
|
||
'indexing': str(val['indexing']) if val.get('indexing') else '',
|
||
'multiLine': True if val.get('multiLine') is True else False,
|
||
'choiceHistoryOnInput': str(val['choiceHistoryOnInput']) if val.get('choiceHistoryOnInput') else '',
|
||
}
|
||
|
||
def parse_enum_value_shorthand(val):
|
||
if isinstance(val, str):
|
||
return {
|
||
'name': val,
|
||
'synonym': split_camel_case(val),
|
||
'comment': '',
|
||
}
|
||
name = str(val.get('name', ''))
|
||
return {
|
||
'name': name,
|
||
'synonym': str(val['synonym']) if val.get('synonym') else split_camel_case(name),
|
||
'comment': str(val['comment']) if val.get('comment') else '',
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 6. GeneratedType categories
|
||
# ---------------------------------------------------------------------------
|
||
|
||
generated_types = {
|
||
'Catalog': [
|
||
{'prefix': 'CatalogObject', 'category': 'Object'},
|
||
{'prefix': 'CatalogRef', 'category': 'Ref'},
|
||
{'prefix': 'CatalogSelection', 'category': 'Selection'},
|
||
{'prefix': 'CatalogList', 'category': 'List'},
|
||
{'prefix': 'CatalogManager', 'category': 'Manager'},
|
||
],
|
||
'Document': [
|
||
{'prefix': 'DocumentObject', 'category': 'Object'},
|
||
{'prefix': 'DocumentRef', 'category': 'Ref'},
|
||
{'prefix': 'DocumentSelection', 'category': 'Selection'},
|
||
{'prefix': 'DocumentList', 'category': 'List'},
|
||
{'prefix': 'DocumentManager', 'category': 'Manager'},
|
||
],
|
||
'Enum': [
|
||
{'prefix': 'EnumRef', 'category': 'Ref'},
|
||
{'prefix': 'EnumManager', 'category': 'Manager'},
|
||
{'prefix': 'EnumList', 'category': 'List'},
|
||
],
|
||
'Constant': [
|
||
{'prefix': 'ConstantManager', 'category': 'Manager'},
|
||
{'prefix': 'ConstantValueManager', 'category': 'ValueManager'},
|
||
{'prefix': 'ConstantValueKey', 'category': 'ValueKey'},
|
||
],
|
||
'InformationRegister': [
|
||
{'prefix': 'InformationRegisterRecord', 'category': 'Record'},
|
||
{'prefix': 'InformationRegisterManager', 'category': 'Manager'},
|
||
{'prefix': 'InformationRegisterSelection', 'category': 'Selection'},
|
||
{'prefix': 'InformationRegisterList', 'category': 'List'},
|
||
{'prefix': 'InformationRegisterRecordSet', 'category': 'RecordSet'},
|
||
{'prefix': 'InformationRegisterRecordKey', 'category': 'RecordKey'},
|
||
{'prefix': 'InformationRegisterRecordManager', 'category': 'RecordManager'},
|
||
],
|
||
'AccumulationRegister': [
|
||
{'prefix': 'AccumulationRegisterRecord', 'category': 'Record'},
|
||
{'prefix': 'AccumulationRegisterManager', 'category': 'Manager'},
|
||
{'prefix': 'AccumulationRegisterSelection', 'category': 'Selection'},
|
||
{'prefix': 'AccumulationRegisterList', 'category': 'List'},
|
||
{'prefix': 'AccumulationRegisterRecordSet', 'category': 'RecordSet'},
|
||
{'prefix': 'AccumulationRegisterRecordKey', 'category': 'RecordKey'},
|
||
],
|
||
'AccountingRegister': [
|
||
{'prefix': 'AccountingRegisterRecord', 'category': 'Record'},
|
||
{'prefix': 'AccountingRegisterExtDimensions', 'category': 'ExtDimensions'},
|
||
{'prefix': 'AccountingRegisterRecordSet', 'category': 'RecordSet'},
|
||
{'prefix': 'AccountingRegisterRecordKey', 'category': 'RecordKey'},
|
||
{'prefix': 'AccountingRegisterSelection', 'category': 'Selection'},
|
||
{'prefix': 'AccountingRegisterList', 'category': 'List'},
|
||
{'prefix': 'AccountingRegisterManager', 'category': 'Manager'},
|
||
],
|
||
'CalculationRegister': [
|
||
{'prefix': 'CalculationRegisterRecord', 'category': 'Record'},
|
||
{'prefix': 'CalculationRegisterManager', 'category': 'Manager'},
|
||
{'prefix': 'CalculationRegisterSelection', 'category': 'Selection'},
|
||
{'prefix': 'CalculationRegisterList', 'category': 'List'},
|
||
{'prefix': 'CalculationRegisterRecordSet', 'category': 'RecordSet'},
|
||
{'prefix': 'CalculationRegisterRecordKey', 'category': 'RecordKey'},
|
||
{'prefix': 'RecalculationsManager', 'category': 'Recalcs'},
|
||
],
|
||
'ChartOfAccounts': [
|
||
{'prefix': 'ChartOfAccountsObject', 'category': 'Object'},
|
||
{'prefix': 'ChartOfAccountsRef', 'category': 'Ref'},
|
||
{'prefix': 'ChartOfAccountsSelection', 'category': 'Selection'},
|
||
{'prefix': 'ChartOfAccountsList', 'category': 'List'},
|
||
{'prefix': 'ChartOfAccountsManager', 'category': 'Manager'},
|
||
{'prefix': 'ChartOfAccountsExtDimensionTypes', 'category': 'ExtDimensionTypes'},
|
||
{'prefix': 'ChartOfAccountsExtDimensionTypesRow', 'category': 'ExtDimensionTypesRow'},
|
||
],
|
||
'ChartOfCharacteristicTypes': [
|
||
{'prefix': 'ChartOfCharacteristicTypesObject', 'category': 'Object'},
|
||
{'prefix': 'ChartOfCharacteristicTypesRef', 'category': 'Ref'},
|
||
{'prefix': 'ChartOfCharacteristicTypesSelection', 'category': 'Selection'},
|
||
{'prefix': 'ChartOfCharacteristicTypesList', 'category': 'List'},
|
||
{'prefix': 'ChartOfCharacteristicTypesCharacteristic', 'category': 'Characteristic'},
|
||
{'prefix': 'ChartOfCharacteristicTypesManager', 'category': 'Manager'},
|
||
],
|
||
'ChartOfCalculationTypes': [
|
||
{'prefix': 'ChartOfCalculationTypesObject', 'category': 'Object'},
|
||
{'prefix': 'ChartOfCalculationTypesRef', 'category': 'Ref'},
|
||
{'prefix': 'ChartOfCalculationTypesSelection', 'category': 'Selection'},
|
||
{'prefix': 'ChartOfCalculationTypesList', 'category': 'List'},
|
||
{'prefix': 'ChartOfCalculationTypesManager', 'category': 'Manager'},
|
||
{'prefix': 'DisplacingCalculationTypes', 'category': 'DisplacingCalculationTypes'},
|
||
{'prefix': 'DisplacingCalculationTypesRow', 'category': 'DisplacingCalculationTypesRow'},
|
||
{'prefix': 'BaseCalculationTypes', 'category': 'BaseCalculationTypes'},
|
||
{'prefix': 'BaseCalculationTypesRow', 'category': 'BaseCalculationTypesRow'},
|
||
{'prefix': 'LeadingCalculationTypes', 'category': 'LeadingCalculationTypes'},
|
||
{'prefix': 'LeadingCalculationTypesRow', 'category': 'LeadingCalculationTypesRow'},
|
||
],
|
||
'BusinessProcess': [
|
||
{'prefix': 'BusinessProcessObject', 'category': 'Object'},
|
||
{'prefix': 'BusinessProcessRef', 'category': 'Ref'},
|
||
{'prefix': 'BusinessProcessSelection', 'category': 'Selection'},
|
||
{'prefix': 'BusinessProcessList', 'category': 'List'},
|
||
{'prefix': 'BusinessProcessManager', 'category': 'Manager'},
|
||
{'prefix': 'BusinessProcessRoutePointRef', 'category': 'RoutePointRef'},
|
||
],
|
||
'Task': [
|
||
{'prefix': 'TaskObject', 'category': 'Object'},
|
||
{'prefix': 'TaskRef', 'category': 'Ref'},
|
||
{'prefix': 'TaskSelection', 'category': 'Selection'},
|
||
{'prefix': 'TaskList', 'category': 'List'},
|
||
{'prefix': 'TaskManager', 'category': 'Manager'},
|
||
],
|
||
'ExchangePlan': [
|
||
{'prefix': 'ExchangePlanObject', 'category': 'Object'},
|
||
{'prefix': 'ExchangePlanRef', 'category': 'Ref'},
|
||
{'prefix': 'ExchangePlanSelection', 'category': 'Selection'},
|
||
{'prefix': 'ExchangePlanList', 'category': 'List'},
|
||
{'prefix': 'ExchangePlanManager', 'category': 'Manager'},
|
||
],
|
||
'DefinedType': [
|
||
{'prefix': 'DefinedType', 'category': 'DefinedType'},
|
||
],
|
||
'DocumentJournal': [
|
||
{'prefix': 'DocumentJournalSelection', 'category': 'Selection'},
|
||
{'prefix': 'DocumentJournalList', 'category': 'List'},
|
||
{'prefix': 'DocumentJournalManager', 'category': 'Manager'},
|
||
],
|
||
'Report': [
|
||
{'prefix': 'ReportObject', 'category': 'Object'},
|
||
{'prefix': 'ReportManager', 'category': 'Manager'},
|
||
],
|
||
'DataProcessor': [
|
||
{'prefix': 'DataProcessorObject', 'category': 'Object'},
|
||
{'prefix': 'DataProcessorManager', 'category': 'Manager'},
|
||
],
|
||
}
|
||
|
||
def emit_internal_info(indent, object_type, object_name):
|
||
types = generated_types.get(object_type)
|
||
if not types:
|
||
return
|
||
X(f'{indent}<InternalInfo>')
|
||
if object_type == 'ExchangePlan':
|
||
X(f'{indent}\t<xr:ThisNode>{new_uuid()}</xr:ThisNode>')
|
||
for gt in types:
|
||
full_name = f"{gt['prefix']}.{object_name}"
|
||
X(f'{indent}\t<xr:GeneratedType name="{full_name}" category="{gt["category"]}">')
|
||
X(f'{indent}\t\t<xr:TypeId>{new_uuid()}</xr:TypeId>')
|
||
X(f'{indent}\t\t<xr:ValueId>{new_uuid()}</xr:ValueId>')
|
||
X(f'{indent}\t</xr:GeneratedType>')
|
||
X(f'{indent}</InternalInfo>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 7. StandardAttributes
|
||
# ---------------------------------------------------------------------------
|
||
|
||
standard_attributes_by_type = {
|
||
'Catalog': ['PredefinedDataName', 'Predefined', 'Ref', 'DeletionMark', 'IsFolder', 'Owner', 'Parent', 'Description', 'Code'],
|
||
'Document': ['Posted', 'Ref', 'DeletionMark', 'Date', 'Number'],
|
||
'Enum': ['Order', 'Ref'],
|
||
'InformationRegister': ['Active', 'LineNumber', 'Recorder', 'Period'],
|
||
'AccumulationRegister': ['Active', 'LineNumber', 'Recorder', 'Period'],
|
||
'AccountingRegister': ['Active', 'Period', 'Recorder', 'LineNumber', 'Account'],
|
||
'CalculationRegister': ['Active', 'Recorder', 'LineNumber', 'RegistrationPeriod', 'CalculationType', 'ReversingEntry'],
|
||
'ChartOfAccounts': ['PredefinedDataName', 'Predefined', 'Ref', 'DeletionMark', 'Description', 'Code', 'Parent', 'Order', 'Type', 'OffBalance'],
|
||
'ChartOfCharacteristicTypes': ['PredefinedDataName', 'Predefined', 'Ref', 'DeletionMark', 'Description', 'Code', 'Parent', 'ValueType'],
|
||
'ChartOfCalculationTypes': ['PredefinedDataName', 'Predefined', 'Ref', 'DeletionMark', 'Description', 'Code', 'ActionPeriodIsBasic'],
|
||
'BusinessProcess': ['Ref', 'DeletionMark', 'Date', 'Number', 'Started', 'Completed', 'HeadTask'],
|
||
'Task': ['Ref', 'DeletionMark', 'Date', 'Number', 'Executed', 'Description', 'RoutePoint', 'BusinessProcess'],
|
||
'ExchangePlan': ['Ref', 'DeletionMark', 'Code', 'Description', 'ThisNode', 'SentNo', 'ReceivedNo'],
|
||
'DocumentJournal': ['Type', 'Ref', 'Date', 'Posted', 'DeletionMark', 'Number'],
|
||
}
|
||
|
||
def emit_standard_attribute(indent, attr_name):
|
||
X(f'{indent}<xr:StandardAttribute name="{attr_name}">')
|
||
X(f'{indent}\t<xr:LinkByType/>')
|
||
X(f'{indent}\t<xr:FillChecking>DontCheck</xr:FillChecking>')
|
||
X(f'{indent}\t<xr:MultiLine>false</xr:MultiLine>')
|
||
X(f'{indent}\t<xr:FillFromFillingValue>false</xr:FillFromFillingValue>')
|
||
X(f'{indent}\t<xr:CreateOnInput>Auto</xr:CreateOnInput>')
|
||
X(f'{indent}\t<xr:MaxValue xsi:nil="true"/>')
|
||
X(f'{indent}\t<xr:ToolTip/>')
|
||
X(f'{indent}\t<xr:ExtendedEdit>false</xr:ExtendedEdit>')
|
||
X(f'{indent}\t<xr:Format/>')
|
||
X(f'{indent}\t<xr:ChoiceForm/>')
|
||
X(f'{indent}\t<xr:QuickChoice>Auto</xr:QuickChoice>')
|
||
X(f'{indent}\t<xr:ChoiceHistoryOnInput>Auto</xr:ChoiceHistoryOnInput>')
|
||
X(f'{indent}\t<xr:EditFormat/>')
|
||
X(f'{indent}\t<xr:PasswordMode>false</xr:PasswordMode>')
|
||
X(f'{indent}\t<xr:DataHistory>Use</xr:DataHistory>')
|
||
X(f'{indent}\t<xr:MarkNegatives>false</xr:MarkNegatives>')
|
||
X(f'{indent}\t<xr:MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t<xr:Synonym/>')
|
||
X(f'{indent}\t<xr:Comment/>')
|
||
X(f'{indent}\t<xr:FullTextSearch>Use</xr:FullTextSearch>')
|
||
X(f'{indent}\t<xr:ChoiceParameterLinks/>')
|
||
X(f'{indent}\t<xr:FillValue xsi:nil="true"/>')
|
||
X(f'{indent}\t<xr:Mask/>')
|
||
X(f'{indent}\t<xr:ChoiceParameters/>')
|
||
X(f'{indent}</xr:StandardAttribute>')
|
||
|
||
def emit_standard_attributes(indent, object_type):
|
||
attrs = standard_attributes_by_type.get(object_type)
|
||
if not attrs:
|
||
return
|
||
X(f'{indent}<StandardAttributes>')
|
||
for a in attrs:
|
||
emit_standard_attribute(f'{indent}\t', a)
|
||
X(f'{indent}</StandardAttributes>')
|
||
|
||
def emit_tabular_standard_attributes(indent):
|
||
X(f'{indent}<StandardAttributes>')
|
||
emit_standard_attribute(f'{indent}\t', 'LineNumber')
|
||
X(f'{indent}</StandardAttributes>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 8. Attribute emitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
RESERVED_ATTR_NAMES = {
|
||
'Ref', 'DeletionMark', 'Code', 'Description', 'Date', 'Number', 'Posted',
|
||
'Parent', 'Owner', 'IsFolder', 'Predefined', 'PredefinedDataName',
|
||
'Recorder', 'Period', 'LineNumber', 'Active', 'Order', 'Type', 'OffBalance',
|
||
'Started', 'Completed', 'HeadTask', 'Executed', 'RoutePoint', 'BusinessProcess',
|
||
'ThisNode', 'SentNo', 'ReceivedNo', 'CalculationType', 'RegistrationPeriod',
|
||
'ReversingEntry', 'Account', 'ValueType', 'ActionPeriodIsBasic',
|
||
}
|
||
RESERVED_ATTR_NAMES_RU = {
|
||
'\u0421\u0441\u044b\u043b\u043a\u0430', '\u041f\u043e\u043c\u0435\u0442\u043a\u0430\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u044f',
|
||
'\u041a\u043e\u0434', '\u041d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435',
|
||
'\u0414\u0430\u0442\u0430', '\u041d\u043e\u043c\u0435\u0440', '\u041f\u0440\u043e\u0432\u0435\u0434\u0435\u043d',
|
||
'\u0420\u043e\u0434\u0438\u0442\u0435\u043b\u044c', '\u0412\u043b\u0430\u0434\u0435\u043b\u0435\u0446',
|
||
'\u042d\u0442\u043e\u0413\u0440\u0443\u043f\u043f\u0430', '\u041f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439',
|
||
'\u0418\u043c\u044f\u041f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445\u0414\u0430\u043d\u043d\u044b\u0445',
|
||
'\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440', '\u041f\u0435\u0440\u0438\u043e\u0434',
|
||
'\u041d\u043e\u043c\u0435\u0440\u0421\u0442\u0440\u043e\u043a\u0438', '\u0410\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c',
|
||
'\u041f\u043e\u0440\u044f\u0434\u043e\u043a', '\u0422\u0438\u043f', '\u0417\u0430\u0431\u0430\u043b\u0430\u043d\u0441\u043e\u0432\u044b\u0439',
|
||
'\u0421\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u043d', '\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d',
|
||
'\u0412\u0435\u0434\u0443\u0449\u0430\u044f\u0417\u0430\u0434\u0430\u0447\u0430',
|
||
'\u0412\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430', '\u0422\u043e\u0447\u043a\u0430\u041c\u0430\u0440\u0448\u0440\u0443\u0442\u0430',
|
||
'\u0411\u0438\u0437\u043d\u0435\u0441\u041f\u0440\u043e\u0446\u0435\u0441\u0441',
|
||
'\u042d\u0442\u043e\u0442\u0423\u0437\u0435\u043b', '\u041d\u043e\u043c\u0435\u0440\u041e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e',
|
||
'\u041d\u043e\u043c\u0435\u0440\u041f\u0440\u0438\u043d\u044f\u0442\u043e\u0433\u043e',
|
||
'\u0412\u0438\u0434\u0420\u0430\u0441\u0447\u0435\u0442\u0430', '\u041f\u0435\u0440\u0438\u043e\u0434\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438',
|
||
'\u0421\u0442\u043e\u0440\u043d\u043e\u0417\u0430\u043f\u0438\u0441\u044c',
|
||
'\u0421\u0447\u0435\u0442', '\u0422\u0438\u043f\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f',
|
||
'\u041f\u0435\u0440\u0438\u043e\u0434\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f\u0411\u0430\u0437\u043e\u0432\u044b\u0439',
|
||
}
|
||
|
||
def emit_attribute(indent, parsed, context):
|
||
attr_name = parsed['name']
|
||
if context not in ('tabular', 'processor-tabular') and (attr_name in RESERVED_ATTR_NAMES or attr_name in RESERVED_ATTR_NAMES_RU):
|
||
print(f"WARNING: Attribute '{attr_name}' conflicts with a standard attribute name. This may cause errors when loading into 1C.", file=sys.stderr)
|
||
uid = new_uuid()
|
||
X(f'{indent}<Attribute uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(parsed["name"])}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', parsed['synonym'])
|
||
X(f'{indent}\t\t<Comment/>')
|
||
type_str = parsed['type']
|
||
if type_str:
|
||
emit_value_type(f'{indent}\t\t', type_str)
|
||
else:
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:string</v8:Type>')
|
||
X(f'{indent}\t\t</Type>')
|
||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||
X(f'{indent}\t\t<Format/>')
|
||
X(f'{indent}\t\t<EditFormat/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{indent}\t\t<Mask/>')
|
||
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
|
||
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
|
||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
|
||
# FillFromFillingValue / FillValue — not for tabular/processor/chart/register-other
|
||
# (Chart*, AccumulationRegister/AccountingRegister/CalculationRegister don't support these)
|
||
if context not in ('tabular', 'processor', 'chart', 'register-other'):
|
||
X(f'{indent}\t\t<FillFromFillingValue>false</FillFromFillingValue>')
|
||
if context not in ('tabular', 'processor', 'chart', 'register-other'):
|
||
emit_fill_value(f'{indent}\t\t', type_str)
|
||
fill_checking = 'DontCheck'
|
||
if 'req' in parsed.get('flags', []):
|
||
fill_checking = 'ShowError'
|
||
if parsed.get('fillChecking'):
|
||
fill_checking = parsed['fillChecking']
|
||
X(f'{indent}\t\t<FillChecking>{fill_checking}</FillChecking>')
|
||
X(f'{indent}\t\t<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>')
|
||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{indent}\t\t<CreateOnInput>Auto</CreateOnInput>')
|
||
X(f'{indent}\t\t<ChoiceForm/>')
|
||
X(f'{indent}\t\t<LinkByType/>')
|
||
chi = parsed.get('choiceHistoryOnInput') or 'Auto'
|
||
X(f'{indent}\t\t<ChoiceHistoryOnInput>{chi}</ChoiceHistoryOnInput>')
|
||
if context == 'catalog':
|
||
X(f'{indent}\t\t<Use>ForItem</Use>')
|
||
if context not in ('processor', 'processor-tabular'):
|
||
indexing = 'DontIndex'
|
||
if 'index' in parsed.get('flags', []):
|
||
indexing = 'Index'
|
||
if 'indexadditional' in parsed.get('flags', []):
|
||
indexing = 'IndexWithAdditionalOrder'
|
||
if parsed.get('indexing'):
|
||
indexing = parsed['indexing']
|
||
X(f'{indent}\t\t<Indexing>{indexing}</Indexing>')
|
||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||
# DataHistory — not for Chart* types and non-InformationRegister register family
|
||
if context not in ('chart', 'register-other'):
|
||
X(f'{indent}\t\t<DataHistory>Use</DataHistory>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</Attribute>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 9. TabularSection emitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def emit_tabular_section(indent, ts_name, columns, object_type, object_name):
|
||
uid = new_uuid()
|
||
X(f'{indent}<TabularSection uuid="{uid}">')
|
||
type_prefix = f'{object_type}TabularSection'
|
||
row_prefix = f'{object_type}TabularSectionRow'
|
||
X(f'{indent}\t<InternalInfo>')
|
||
X(f'{indent}\t\t<xr:GeneratedType name="{type_prefix}.{object_name}.{ts_name}" category="TabularSection">')
|
||
X(f'{indent}\t\t\t<xr:TypeId>{new_uuid()}</xr:TypeId>')
|
||
X(f'{indent}\t\t\t<xr:ValueId>{new_uuid()}</xr:ValueId>')
|
||
X(f'{indent}\t\t</xr:GeneratedType>')
|
||
X(f'{indent}\t\t<xr:GeneratedType name="{row_prefix}.{object_name}.{ts_name}" category="TabularSectionRow">')
|
||
X(f'{indent}\t\t\t<xr:TypeId>{new_uuid()}</xr:TypeId>')
|
||
X(f'{indent}\t\t\t<xr:ValueId>{new_uuid()}</xr:ValueId>')
|
||
X(f'{indent}\t\t</xr:GeneratedType>')
|
||
X(f'{indent}\t</InternalInfo>')
|
||
ts_synonym = split_camel_case(ts_name)
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(ts_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', ts_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<FillChecking>DontCheck</FillChecking>')
|
||
emit_tabular_standard_attributes(f'{indent}\t\t')
|
||
if object_type == 'Catalog':
|
||
X(f'{indent}\t\t<Use>ForItem</Use>')
|
||
X(f'{indent}\t</Properties>')
|
||
ts_context = 'processor-tabular' if object_type in ('DataProcessor', 'Report') else 'tabular'
|
||
X(f'{indent}\t<ChildObjects>')
|
||
for col in columns:
|
||
parsed = parse_attribute_shorthand(col)
|
||
emit_attribute(f'{indent}\t\t', parsed, ts_context)
|
||
X(f'{indent}\t</ChildObjects>')
|
||
X(f'{indent}</TabularSection>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 10. EnumValue emitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def emit_enum_value(indent, parsed):
|
||
uid = new_uuid()
|
||
X(f'{indent}<EnumValue uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(parsed["name"])}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', parsed['synonym'])
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</EnumValue>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 11. Dimension emitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def emit_dimension(indent, parsed, register_type):
|
||
uid = new_uuid()
|
||
X(f'{indent}<Dimension uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(parsed["name"])}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', parsed['synonym'])
|
||
X(f'{indent}\t\t<Comment/>')
|
||
type_str = parsed['type']
|
||
if type_str:
|
||
emit_value_type(f'{indent}\t\t', type_str)
|
||
else:
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:string</v8:Type>')
|
||
X(f'{indent}\t\t</Type>')
|
||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||
X(f'{indent}\t\t<Format/>')
|
||
X(f'{indent}\t\t<EditFormat/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{indent}\t\t<Mask/>')
|
||
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
|
||
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
|
||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
|
||
flags = parsed.get('flags', [])
|
||
if register_type == 'InformationRegister':
|
||
fill_from = 'true' if 'master' in flags else 'false'
|
||
X(f'{indent}\t\t<FillFromFillingValue>{fill_from}</FillFromFillingValue>')
|
||
X(f'{indent}\t\t<FillValue xsi:nil="true"/>')
|
||
fill_checking = 'DontCheck'
|
||
if 'req' in flags:
|
||
fill_checking = 'ShowError'
|
||
X(f'{indent}\t\t<FillChecking>{fill_checking}</FillChecking>')
|
||
X(f'{indent}\t\t<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>')
|
||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{indent}\t\t<CreateOnInput>Auto</CreateOnInput>')
|
||
X(f'{indent}\t\t<ChoiceForm/>')
|
||
X(f'{indent}\t\t<LinkByType/>')
|
||
X(f'{indent}\t\t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
if register_type == 'InformationRegister':
|
||
master = 'true' if 'master' in flags else 'false'
|
||
main_filter = 'true' if 'mainfilter' in flags else 'false'
|
||
deny_incomplete = 'true' if 'denyincomplete' in flags else 'false'
|
||
X(f'{indent}\t\t<Master>{master}</Master>')
|
||
X(f'{indent}\t\t<MainFilter>{main_filter}</MainFilter>')
|
||
X(f'{indent}\t\t<DenyIncompleteValues>{deny_incomplete}</DenyIncompleteValues>')
|
||
if register_type == 'AccumulationRegister':
|
||
deny_incomplete = 'true' if 'denyincomplete' in flags else 'false'
|
||
X(f'{indent}\t\t<DenyIncompleteValues>{deny_incomplete}</DenyIncompleteValues>')
|
||
indexing = 'DontIndex'
|
||
if 'index' in flags:
|
||
indexing = 'Index'
|
||
X(f'{indent}\t\t<Indexing>{indexing}</Indexing>')
|
||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||
if register_type == 'AccumulationRegister':
|
||
use_in_totals = 'false' if 'nouseintotals' in flags else 'true'
|
||
X(f'{indent}\t\t<UseInTotals>{use_in_totals}</UseInTotals>')
|
||
if register_type == 'InformationRegister':
|
||
X(f'{indent}\t\t<DataHistory>Use</DataHistory>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</Dimension>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 12. Resource emitter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def emit_resource(indent, parsed, register_type):
|
||
uid = new_uuid()
|
||
X(f'{indent}<Resource uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(parsed["name"])}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', parsed['synonym'])
|
||
X(f'{indent}\t\t<Comment/>')
|
||
type_str = parsed['type']
|
||
if type_str:
|
||
emit_value_type(f'{indent}\t\t', type_str)
|
||
else:
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:decimal</v8:Type>')
|
||
X(f'{indent}\t\t\t<v8:NumberQualifiers>')
|
||
X(f'{indent}\t\t\t\t<v8:Digits>15</v8:Digits>')
|
||
X(f'{indent}\t\t\t\t<v8:FractionDigits>2</v8:FractionDigits>')
|
||
X(f'{indent}\t\t\t\t<v8:AllowedSign>Any</v8:AllowedSign>')
|
||
X(f'{indent}\t\t\t</v8:NumberQualifiers>')
|
||
X(f'{indent}\t\t</Type>')
|
||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||
X(f'{indent}\t\t<Format/>')
|
||
X(f'{indent}\t\t<EditFormat/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{indent}\t\t<Mask/>')
|
||
multi_line = 'true' if (parsed.get('multiLine') is True or 'multiline' in parsed.get('flags', [])) else 'false'
|
||
X(f'{indent}\t\t<MultiLine>{multi_line}</MultiLine>')
|
||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
|
||
if register_type == 'InformationRegister':
|
||
X(f'{indent}\t\t<FillFromFillingValue>false</FillFromFillingValue>')
|
||
X(f'{indent}\t\t<FillValue xsi:nil="true"/>')
|
||
flags = parsed.get('flags', [])
|
||
fill_checking = 'DontCheck'
|
||
if 'req' in flags:
|
||
fill_checking = 'ShowError'
|
||
X(f'{indent}\t\t<FillChecking>{fill_checking}</FillChecking>')
|
||
X(f'{indent}\t\t<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>')
|
||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{indent}\t\t<CreateOnInput>Auto</CreateOnInput>')
|
||
X(f'{indent}\t\t<ChoiceForm/>')
|
||
X(f'{indent}\t\t<LinkByType/>')
|
||
X(f'{indent}\t\t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
if register_type == 'InformationRegister':
|
||
X(f'{indent}\t\t<Indexing>DontIndex</Indexing>')
|
||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||
X(f'{indent}\t\t<DataHistory>Use</DataHistory>')
|
||
if register_type == 'AccumulationRegister':
|
||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</Resource>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 13. Property emitters per type
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def emit_catalog_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
hierarchical = 'true' if defn.get('hierarchical') is True else 'false'
|
||
hierarchy_type = get_enum_prop('HierarchyType', 'hierarchyType', 'HierarchyFoldersAndItems')
|
||
X(f'{i}<Hierarchical>{hierarchical}</Hierarchical>')
|
||
X(f'{i}<HierarchyType>{hierarchy_type}</HierarchyType>')
|
||
limit_level_count = 'true' if defn.get('limitLevelCount') is True else 'false'
|
||
level_count = str(defn['levelCount']) if defn.get('levelCount') is not None else '2'
|
||
folders_on_top = 'false' if defn.get('foldersOnTop') is False else 'true'
|
||
X(f'{i}<LimitLevelCount>{limit_level_count}</LimitLevelCount>')
|
||
X(f'{i}<LevelCount>{level_count}</LevelCount>')
|
||
X(f'{i}<FoldersOnTop>{folders_on_top}</FoldersOnTop>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
owners = defn.get('owners', [])
|
||
if owners:
|
||
X(f'{i}<Owners>')
|
||
for owner_ref in owners:
|
||
full_ref = owner_ref if '.' in str(owner_ref) else f'Catalog.{owner_ref}'
|
||
X(f'{i}\t<xr:Item xsi:type="xr:MDObjectRef">{full_ref}</xr:Item>')
|
||
X(f'{i}</Owners>')
|
||
else:
|
||
X(f'{i}<Owners/>')
|
||
subordination_use = get_enum_prop('SubordinationUse', 'subordinationUse', 'ToItems')
|
||
X(f'{i}<SubordinationUse>{subordination_use}</SubordinationUse>')
|
||
code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '25'
|
||
code_type = get_enum_prop('CodeType', 'codeType', 'String')
|
||
code_allowed_length = get_enum_prop('CodeAllowedLength', 'codeAllowedLength', 'Variable')
|
||
autonumbering = 'false' if defn.get('autonumbering') is False else 'true'
|
||
check_unique = 'true' if defn.get('checkUnique') is True else 'false'
|
||
X(f'{i}<CodeLength>{code_length}</CodeLength>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
X(f'{i}<CodeType>{code_type}</CodeType>')
|
||
X(f'{i}<CodeAllowedLength>{code_allowed_length}</CodeAllowedLength>')
|
||
code_series = get_enum_prop('CodeSeries', 'codeSeries', 'WholeCatalog')
|
||
X(f'{i}<CodeSeries>{code_series}</CodeSeries>')
|
||
X(f'{i}<CheckUnique>{check_unique}</CheckUnique>')
|
||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||
default_presentation = get_enum_prop('DefaultPresentation', 'defaultPresentation', 'AsDescription')
|
||
X(f'{i}<DefaultPresentation>{default_presentation}</DefaultPresentation>')
|
||
emit_standard_attributes(i, 'Catalog')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||
choice_mode = get_enum_prop('ChoiceMode', 'choiceMode', 'BothWays')
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>{choice_mode}</ChoiceMode>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>Catalog.{obj_name}.StandardAttribute.Description</xr:Field>')
|
||
X(f'{i}\t<xr:Field>Catalog.{obj_name}.StandardAttribute.Code</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultFolderForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<DefaultFolderChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryFolderForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<AuxiliaryFolderChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_document_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<Numerator/>')
|
||
number_type = get_enum_prop('NumberType', 'numberType', 'String')
|
||
number_length = str(defn['numberLength']) if defn.get('numberLength') is not None else '11'
|
||
number_allowed_length = get_enum_prop('NumberAllowedLength', 'numberAllowedLength', 'Variable')
|
||
number_periodicity = get_enum_prop('InformationRegisterPeriodicity', 'numberPeriodicity', 'Year')
|
||
check_unique = 'false' if defn.get('checkUnique') is False else 'true'
|
||
autonumbering = 'false' if defn.get('autonumbering') is False else 'true'
|
||
X(f'{i}<NumberType>{number_type}</NumberType>')
|
||
X(f'{i}<NumberLength>{number_length}</NumberLength>')
|
||
X(f'{i}<NumberAllowedLength>{number_allowed_length}</NumberAllowedLength>')
|
||
X(f'{i}<NumberPeriodicity>{number_periodicity}</NumberPeriodicity>')
|
||
X(f'{i}<CheckUnique>{check_unique}</CheckUnique>')
|
||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||
emit_standard_attributes(i, 'Document')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>Document.{obj_name}.StandardAttribute.Number</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
posting = get_enum_prop('Posting', 'posting', 'Allow')
|
||
real_time_posting = get_enum_prop('RealTimePosting', 'realTimePosting', 'Deny')
|
||
reg_records_deletion = get_enum_prop('RegisterRecordsDeletion', 'registerRecordsDeletion', 'AutoDelete')
|
||
reg_records_writing = get_enum_prop('RegisterRecordsWritingOnPost', 'registerRecordsWritingOnPost', 'WriteModified')
|
||
sequence_filling = str(defn['sequenceFilling']) if defn.get('sequenceFilling') else 'AutoFill'
|
||
post_in_priv = 'false' if defn.get('postInPrivilegedMode') is False else 'true'
|
||
unpost_in_priv = 'false' if defn.get('unpostInPrivilegedMode') is False else 'true'
|
||
X(f'{i}<Posting>{posting}</Posting>')
|
||
X(f'{i}<RealTimePosting>{real_time_posting}</RealTimePosting>')
|
||
X(f'{i}<RegisterRecordsDeletion>{reg_records_deletion}</RegisterRecordsDeletion>')
|
||
X(f'{i}<RegisterRecordsWritingOnPost>{reg_records_writing}</RegisterRecordsWritingOnPost>')
|
||
X(f'{i}<SequenceFilling>{sequence_filling}</SequenceFilling>')
|
||
# RegisterRecords
|
||
reg_records = []
|
||
if defn.get('registerRecords'):
|
||
for rr in defn['registerRecords']:
|
||
rr_str = str(rr)
|
||
if '.' in rr_str:
|
||
dot_idx = rr_str.index('.')
|
||
rr_prefix = rr_str[:dot_idx]
|
||
rr_suffix = rr_str[dot_idx + 1:]
|
||
if rr_prefix in object_type_synonyms:
|
||
rr_prefix = object_type_synonyms[rr_prefix]
|
||
reg_records.append(f'{rr_prefix}.{rr_suffix}')
|
||
else:
|
||
reg_records.append(rr_str)
|
||
if reg_records:
|
||
X(f'{i}<RegisterRecords>')
|
||
for rr in reg_records:
|
||
X(f'{i}\t<xr:Item xsi:type="xr:MDObjectRef">{rr}</xr:Item>')
|
||
X(f'{i}</RegisterRecords>')
|
||
else:
|
||
X(f'{i}<RegisterRecords/>')
|
||
X(f'{i}<PostInPrivilegedMode>{post_in_priv}</PostInPrivilegedMode>')
|
||
X(f'{i}<UnpostInPrivilegedMode>{unpost_in_priv}</UnpostInPrivilegedMode>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_enum_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>false</UseStandardCommands>')
|
||
emit_standard_attributes(i, 'Enum')
|
||
X(f'{i}<Characteristics/>')
|
||
quick_choice = 'false' if defn.get('quickChoice') is False else 'true'
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>BothWays</ChoiceMode>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
|
||
def emit_constant_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
# Type
|
||
value_type = build_type_str(defn) or 'String'
|
||
emit_value_type(i, value_type)
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<DefaultForm/>')
|
||
X(f'{i}<ExtendedPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<PasswordMode>false</PasswordMode>')
|
||
X(f'{i}<Format/>')
|
||
X(f'{i}<EditFormat/>')
|
||
X(f'{i}<ToolTip/>')
|
||
X(f'{i}<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{i}<Mask/>')
|
||
X(f'{i}<MultiLine>false</MultiLine>')
|
||
X(f'{i}<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{i}<MinValue xsi:nil="true"/>')
|
||
X(f'{i}<MaxValue xsi:nil="true"/>')
|
||
X(f'{i}<FillChecking>DontCheck</FillChecking>')
|
||
X(f'{i}<ChoiceFoldersAndItems>Items</ChoiceFoldersAndItems>')
|
||
X(f'{i}<ChoiceParameterLinks/>')
|
||
X(f'{i}<ChoiceParameters/>')
|
||
X(f'{i}<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{i}<ChoiceForm/>')
|
||
X(f'{i}<LinkByType/>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_information_register_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
X(f'{i}<DefaultRecordForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<AuxiliaryRecordForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
emit_standard_attributes(i, 'InformationRegister')
|
||
periodicity = get_enum_prop('InformationRegisterPeriodicity', 'periodicity', 'Nonperiodical')
|
||
write_mode = get_enum_prop('WriteMode', 'writeMode', 'Independent')
|
||
main_filter_on_period = 'false'
|
||
if defn.get('mainFilterOnPeriod') is not None:
|
||
main_filter_on_period = 'true' if defn['mainFilterOnPeriod'] is True else 'false'
|
||
elif periodicity != 'Nonperiodical':
|
||
main_filter_on_period = 'true'
|
||
X(f'{i}<InformationRegisterPeriodicity>{periodicity}</InformationRegisterPeriodicity>')
|
||
X(f'{i}<WriteMode>{write_mode}</WriteMode>')
|
||
X(f'{i}<MainFilterOnPeriod>{main_filter_on_period}</MainFilterOnPeriod>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<EnableTotalsSliceFirst>false</EnableTotalsSliceFirst>')
|
||
X(f'{i}<EnableTotalsSliceLast>false</EnableTotalsSliceLast>')
|
||
X(f'{i}<RecordPresentation/>')
|
||
X(f'{i}<ExtendedRecordPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_accumulation_register_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
register_type = get_enum_prop('RegisterType', 'registerType', 'Balance')
|
||
X(f'{i}<RegisterType>{register_type}</RegisterType>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
emit_standard_attributes(i, 'AccumulationRegister')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
enable_totals_splitting = 'false' if defn.get('enableTotalsSplitting') is False else 'true'
|
||
X(f'{i}<EnableTotalsSplitting>{enable_totals_splitting}</EnableTotalsSplitting>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
# --- 13a. DefinedType, CommonModule, ScheduledJob, EventSubscription ---
|
||
|
||
def emit_defined_type_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
# Accept both valueType and valueTypes
|
||
value_types = list(defn.get('valueTypes', []))
|
||
if not value_types and defn.get('valueType'):
|
||
vt_raw = defn['valueType']
|
||
value_types = list(vt_raw) if isinstance(vt_raw, list) else [vt_raw]
|
||
if value_types:
|
||
X(f'{i}<Type>')
|
||
for vt in value_types:
|
||
resolved = resolve_type_str(str(vt))
|
||
if re.match(r'^(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef|ExchangePlanRef|BusinessProcessRef|TaskRef)\.', resolved):
|
||
X(f'{i}\t<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{resolved}</v8:Type>')
|
||
elif resolved == 'Boolean':
|
||
X(f'{i}\t<v8:Type>xs:boolean</v8:Type>')
|
||
elif re.match(r'^String', resolved):
|
||
X(f'{i}\t<v8:Type>xs:string</v8:Type>')
|
||
X(f'{i}\t<v8:StringQualifiers>')
|
||
X(f'{i}\t\t<v8:Length>0</v8:Length>')
|
||
X(f'{i}\t\t<v8:AllowedLength>Variable</v8:AllowedLength>')
|
||
X(f'{i}\t</v8:StringQualifiers>')
|
||
else:
|
||
X(f'{i}\t<v8:Type>cfg:{resolved}</v8:Type>')
|
||
X(f'{i}</Type>')
|
||
else:
|
||
X(f'{i}<Type/>')
|
||
|
||
def emit_common_module_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
context = str(defn['context']) if defn.get('context') else ''
|
||
global_val = 'true' if defn.get('global') is True else 'false'
|
||
server = 'false'
|
||
server_call = 'false'
|
||
client_managed = 'false'
|
||
client_ordinary = 'false'
|
||
external_connection = 'false'
|
||
privileged = 'false'
|
||
if context == 'server' or context == 'serverCall':
|
||
server = 'true'
|
||
server_call = 'true'
|
||
elif context == 'client':
|
||
client_managed = 'true'
|
||
elif context == 'serverClient':
|
||
server = 'true'
|
||
client_managed = 'true'
|
||
else:
|
||
if defn.get('server') is True:
|
||
server = 'true'
|
||
if defn.get('serverCall') is True:
|
||
server_call = 'true'
|
||
if defn.get('clientManagedApplication') is True:
|
||
client_managed = 'true'
|
||
if defn.get('clientOrdinaryApplication') is True:
|
||
client_ordinary = 'true'
|
||
if defn.get('externalConnection') is True:
|
||
external_connection = 'true'
|
||
if defn.get('privileged') is True:
|
||
privileged = 'true'
|
||
X(f'{i}<Global>{global_val}</Global>')
|
||
X(f'{i}<ClientManagedApplication>{client_managed}</ClientManagedApplication>')
|
||
X(f'{i}<Server>{server}</Server>')
|
||
X(f'{i}<ExternalConnection>{external_connection}</ExternalConnection>')
|
||
X(f'{i}<ClientOrdinaryApplication>{client_ordinary}</ClientOrdinaryApplication>')
|
||
X(f'{i}<ServerCall>{server_call}</ServerCall>')
|
||
X(f'{i}<Privileged>{privileged}</Privileged>')
|
||
return_values_reuse = get_enum_prop('ReturnValuesReuse', 'returnValuesReuse', 'DontUse')
|
||
X(f'{i}<ReturnValuesReuse>{return_values_reuse}</ReturnValuesReuse>')
|
||
|
||
def emit_scheduled_job_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
method_name = str(defn['methodName']) if defn.get('methodName') else ''
|
||
# Ensure CommonModule. prefix
|
||
if method_name and not method_name.startswith('CommonModule.'):
|
||
method_name = f'CommonModule.{method_name}'
|
||
X(f'{i}<MethodName>{esc_xml(method_name)}</MethodName>')
|
||
description = str(defn['description']) if defn.get('description') else synonym
|
||
X(f'{i}<Description>{esc_xml(description)}</Description>')
|
||
key = str(defn['key']) if defn.get('key') else ''
|
||
X(f'{i}<Key>{esc_xml(key)}</Key>')
|
||
use = 'true' if defn.get('use') is True else 'false'
|
||
X(f'{i}<Use>{use}</Use>')
|
||
predefined = 'true' if defn.get('predefined') is True else 'false'
|
||
X(f'{i}<Predefined>{predefined}</Predefined>')
|
||
restart_count = str(defn['restartCountOnFailure']) if defn.get('restartCountOnFailure') is not None else '3'
|
||
restart_interval = str(defn['restartIntervalOnFailure']) if defn.get('restartIntervalOnFailure') is not None else '10'
|
||
X(f'{i}<RestartCountOnFailure>{restart_count}</RestartCountOnFailure>')
|
||
X(f'{i}<RestartIntervalOnFailure>{restart_interval}</RestartIntervalOnFailure>')
|
||
|
||
def emit_event_subscription_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
sources = list(defn.get('source', []))
|
||
if sources:
|
||
X(f'{i}<Source>')
|
||
for src in sources:
|
||
resolved = resolve_type_str(str(src))
|
||
X(f'{i}\t<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:{resolved}</v8:Type>')
|
||
X(f'{i}</Source>')
|
||
else:
|
||
X(f'{i}<Source/>')
|
||
event = str(defn['event']) if defn.get('event') else 'BeforeWrite'
|
||
X(f'{i}<Event>{event}</Event>')
|
||
handler = str(defn['handler']) if defn.get('handler') else ''
|
||
# Ensure CommonModule. prefix
|
||
if handler and not handler.startswith('CommonModule.'):
|
||
handler = f'CommonModule.{handler}'
|
||
X(f'{i}<Handler>{esc_xml(handler)}</Handler>')
|
||
|
||
# --- 13b. Report, DataProcessor ---
|
||
|
||
def emit_report_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
default_form = str(defn['defaultForm']) if defn.get('defaultForm') else ''
|
||
if default_form:
|
||
X(f'{i}<DefaultForm>{default_form}</DefaultForm>')
|
||
else:
|
||
X(f'{i}<DefaultForm/>')
|
||
aux_form = str(defn['auxiliaryForm']) if defn.get('auxiliaryForm') else ''
|
||
if aux_form:
|
||
X(f'{i}<AuxiliaryForm>{aux_form}</AuxiliaryForm>')
|
||
else:
|
||
X(f'{i}<AuxiliaryForm/>')
|
||
main_dcs = str(defn['mainDataCompositionSchema']) if defn.get('mainDataCompositionSchema') else ''
|
||
if main_dcs:
|
||
X(f'{i}<MainDataCompositionSchema>{main_dcs}</MainDataCompositionSchema>')
|
||
else:
|
||
X(f'{i}<MainDataCompositionSchema/>')
|
||
def_settings = str(defn['defaultSettingsForm']) if defn.get('defaultSettingsForm') else ''
|
||
if def_settings:
|
||
X(f'{i}<DefaultSettingsForm>{def_settings}</DefaultSettingsForm>')
|
||
else:
|
||
X(f'{i}<DefaultSettingsForm/>')
|
||
aux_settings = str(defn['auxiliarySettingsForm']) if defn.get('auxiliarySettingsForm') else ''
|
||
if aux_settings:
|
||
X(f'{i}<AuxiliarySettingsForm>{aux_settings}</AuxiliarySettingsForm>')
|
||
else:
|
||
X(f'{i}<AuxiliarySettingsForm/>')
|
||
def_variant = str(defn['defaultVariantForm']) if defn.get('defaultVariantForm') else ''
|
||
if def_variant:
|
||
X(f'{i}<DefaultVariantForm>{def_variant}</DefaultVariantForm>')
|
||
else:
|
||
X(f'{i}<DefaultVariantForm/>')
|
||
X(f'{i}<VariantsStorage/>')
|
||
X(f'{i}<SettingsStorage/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<ExtendedPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
def emit_data_processor_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>false</UseStandardCommands>')
|
||
default_form = str(defn['defaultForm']) if defn.get('defaultForm') else ''
|
||
if default_form:
|
||
X(f'{i}<DefaultForm>{default_form}</DefaultForm>')
|
||
else:
|
||
X(f'{i}<DefaultForm/>')
|
||
aux_form = str(defn['auxiliaryForm']) if defn.get('auxiliaryForm') else ''
|
||
if aux_form:
|
||
X(f'{i}<AuxiliaryForm>{aux_form}</AuxiliaryForm>')
|
||
else:
|
||
X(f'{i}<AuxiliaryForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<ExtendedPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
# --- 13c. ExchangePlan, ChartOfCharacteristicTypes, DocumentJournal ---
|
||
|
||
def emit_exchange_plan_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '100'
|
||
code_allowed_length = get_enum_prop('CodeAllowedLength', 'codeAllowedLength', 'Variable')
|
||
X(f'{i}<CodeLength>{code_length}</CodeLength>')
|
||
X(f'{i}<CodeAllowedLength>{code_allowed_length}</CodeAllowedLength>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
X(f'{i}<DefaultPresentation>AsDescription</DefaultPresentation>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
emit_standard_attributes(i, 'ExchangePlan')
|
||
distributed = 'true' if defn.get('distributedInfoBase') is True else 'false'
|
||
include_ext = 'true' if defn.get('includeConfigurationExtensions') is True else 'false'
|
||
X(f'{i}<DistributedInfoBase>{distributed}</DistributedInfoBase>')
|
||
X(f'{i}<IncludeConfigurationExtensions>{include_ext}</IncludeConfigurationExtensions>')
|
||
X(f'{i}<BasedOn/>')
|
||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>BothWays</ChoiceMode>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>ExchangePlan.{obj_name}.StandardAttribute.Description</xr:Field>')
|
||
X(f'{i}\t<xr:Field>ExchangePlan.{obj_name}.StandardAttribute.Code</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_chart_of_characteristic_types_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '25'
|
||
code_allowed_length = get_enum_prop('CodeAllowedLength', 'codeAllowedLength', 'Variable')
|
||
autonumbering = 'false' if defn.get('autonumbering') is False else 'true'
|
||
check_unique = 'true' if defn.get('checkUnique') is True else 'false'
|
||
X(f'{i}<CodeLength>{code_length}</CodeLength>')
|
||
X(f'{i}<CodeAllowedLength>{code_allowed_length}</CodeAllowedLength>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
X(f'{i}<CheckUnique>{check_unique}</CheckUnique>')
|
||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||
X(f'{i}<DefaultPresentation>AsDescription</DefaultPresentation>')
|
||
char_ext_values = str(defn['characteristicExtValues']) if defn.get('characteristicExtValues') else ''
|
||
if char_ext_values:
|
||
X(f'{i}<CharacteristicExtValues>{char_ext_values}</CharacteristicExtValues>')
|
||
else:
|
||
X(f'{i}<CharacteristicExtValues/>')
|
||
value_types = list(defn.get('valueTypes', []))
|
||
if value_types:
|
||
X(f'{i}<Type>')
|
||
for vt in value_types:
|
||
emit_type_content(f'{i}\t', str(vt))
|
||
X(f'{i}</Type>')
|
||
else:
|
||
X(f'{i}<Type>')
|
||
X(f'{i}\t<v8:Type>xs:boolean</v8:Type>')
|
||
X(f'{i}\t<v8:Type>xs:string</v8:Type>')
|
||
X(f'{i}\t<v8:StringQualifiers>')
|
||
X(f'{i}\t\t<v8:Length>100</v8:Length>')
|
||
X(f'{i}\t\t<v8:AllowedLength>Variable</v8:AllowedLength>')
|
||
X(f'{i}\t</v8:StringQualifiers>')
|
||
X(f'{i}\t<v8:Type>xs:decimal</v8:Type>')
|
||
X(f'{i}\t<v8:NumberQualifiers>')
|
||
X(f'{i}\t\t<v8:Digits>15</v8:Digits>')
|
||
X(f'{i}\t\t<v8:FractionDigits>2</v8:FractionDigits>')
|
||
X(f'{i}\t\t<v8:AllowedSign>Any</v8:AllowedSign>')
|
||
X(f'{i}\t</v8:NumberQualifiers>')
|
||
X(f'{i}\t<v8:Type>xs:dateTime</v8:Type>')
|
||
X(f'{i}\t<v8:DateQualifiers>')
|
||
X(f'{i}\t\t<v8:DateFractions>DateTime</v8:DateFractions>')
|
||
X(f'{i}\t</v8:DateQualifiers>')
|
||
X(f'{i}</Type>')
|
||
hierarchical = 'true' if defn.get('hierarchical') is True else 'false'
|
||
X(f'{i}<Hierarchical>{hierarchical}</Hierarchical>')
|
||
X(f'{i}<FoldersOnTop>true</FoldersOnTop>')
|
||
emit_standard_attributes(i, 'ChartOfCharacteristicTypes')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>BothWays</ChoiceMode>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>ChartOfCharacteristicTypes.{obj_name}.StandardAttribute.Description</xr:Field>')
|
||
X(f'{i}\t<xr:Field>ChartOfCharacteristicTypes.{obj_name}.StandardAttribute.Code</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultFolderForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<DefaultFolderChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryFolderForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<AuxiliaryFolderChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_document_journal_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
default_form = str(defn['defaultForm']) if defn.get('defaultForm') else ''
|
||
if default_form:
|
||
X(f'{i}<DefaultForm>{default_form}</DefaultForm>')
|
||
else:
|
||
X(f'{i}<DefaultForm/>')
|
||
aux_form = str(defn['auxiliaryForm']) if defn.get('auxiliaryForm') else ''
|
||
if aux_form:
|
||
X(f'{i}<AuxiliaryForm>{aux_form}</AuxiliaryForm>')
|
||
else:
|
||
X(f'{i}<AuxiliaryForm/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
reg_docs = list(defn.get('registeredDocuments', []))
|
||
if reg_docs:
|
||
X(f'{i}<RegisteredDocuments>')
|
||
for rd in reg_docs:
|
||
rd_str = str(rd)
|
||
if '.' in rd_str:
|
||
dot_idx = rd_str.index('.')
|
||
rd_prefix = rd_str[:dot_idx]
|
||
rd_suffix = rd_str[dot_idx + 1:]
|
||
if rd_prefix in object_type_synonyms:
|
||
rd_prefix = object_type_synonyms[rd_prefix]
|
||
rd_str = f'{rd_prefix}.{rd_suffix}'
|
||
X(f'{i}\t<xr:Item xsi:type="xr:MDObjectRef">{rd_str}</xr:Item>')
|
||
X(f'{i}</RegisteredDocuments>')
|
||
else:
|
||
X(f'{i}<RegisteredDocuments/>')
|
||
emit_standard_attributes(i, 'DocumentJournal')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
def emit_chart_of_accounts_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
ext_dim_types = str(defn['extDimensionTypes']) if defn.get('extDimensionTypes') else ''
|
||
if ext_dim_types:
|
||
X(f'{i}<ExtDimensionTypes>{ext_dim_types}</ExtDimensionTypes>')
|
||
else:
|
||
X(f'{i}<ExtDimensionTypes/>')
|
||
max_ext_dim = str(defn['maxExtDimensionCount']) if defn.get('maxExtDimensionCount') is not None else '3'
|
||
X(f'{i}<MaxExtDimensionCount>{max_ext_dim}</MaxExtDimensionCount>')
|
||
code_mask = str(defn['codeMask']) if defn.get('codeMask') else ''
|
||
if code_mask:
|
||
X(f'{i}<CodeMask>{code_mask}</CodeMask>')
|
||
else:
|
||
X(f'{i}<CodeMask/>')
|
||
code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '8'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '120'
|
||
code_series = str(defn['codeSeries']) if defn.get('codeSeries') else 'WholeChartOfAccounts'
|
||
auto_order = 'false' if defn.get('autoOrderByCode') is False else 'true'
|
||
order_length = str(defn['orderLength']) if defn.get('orderLength') is not None else '5'
|
||
X(f'{i}<CodeLength>{code_length}</CodeLength>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
X(f'{i}<CodeSeries>{code_series}</CodeSeries>')
|
||
X(f'{i}<CheckUnique>false</CheckUnique>')
|
||
X(f'{i}<DefaultPresentation>AsDescription</DefaultPresentation>')
|
||
X(f'{i}<AutoOrderByCode>{auto_order}</AutoOrderByCode>')
|
||
X(f'{i}<OrderLength>{order_length}</OrderLength>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
emit_standard_attributes(i, 'ChartOfAccounts')
|
||
X(f'{i}<StandardTabularSections>')
|
||
X(f'{i}\t<xr:StandardTabularSection name="ExtDimensionTypes">')
|
||
X(f'{i}\t\t<xr:StandardAttributes>')
|
||
for st_attr in ['TurnoversOnly', 'Predefined', 'ExtDimensionType', 'LineNumber']:
|
||
emit_standard_attribute(f'{i}\t\t\t', st_attr)
|
||
X(f'{i}\t\t</xr:StandardAttributes>')
|
||
X(f'{i}\t</xr:StandardTabularSection>')
|
||
X(f'{i}</StandardTabularSections>')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
|
||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>BothWays</ChoiceMode>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>ChartOfAccounts.{obj_name}.StandardAttribute.Description</xr:Field>')
|
||
X(f'{i}\t<xr:Field>ChartOfAccounts.{obj_name}.StandardAttribute.Code</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_accounting_register_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
chart_of_accounts = str(defn['chartOfAccounts']) if defn.get('chartOfAccounts') else ''
|
||
if chart_of_accounts:
|
||
X(f'{i}<ChartOfAccounts>{chart_of_accounts}</ChartOfAccounts>')
|
||
else:
|
||
X(f'{i}<ChartOfAccounts/>')
|
||
correspondence = 'true' if defn.get('correspondence') is True else 'false'
|
||
X(f'{i}<Correspondence>{correspondence}</Correspondence>')
|
||
period_adj_len = str(defn['periodAdjustmentLength']) if defn.get('periodAdjustmentLength') is not None else '0'
|
||
X(f'{i}<PeriodAdjustmentLength>{period_adj_len}</PeriodAdjustmentLength>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
emit_standard_attributes(i, 'AccountingRegister')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
def emit_chart_of_calculation_types_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
code_length = str(defn['codeLength']) if defn.get('codeLength') is not None else '9'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '25'
|
||
code_type = get_enum_prop('CodeType', 'codeType', 'String')
|
||
code_allowed_length = get_enum_prop('CodeAllowedLength', 'codeAllowedLength', 'Variable')
|
||
X(f'{i}<CodeLength>{code_length}</CodeLength>')
|
||
X(f'{i}<CodeType>{code_type}</CodeType>')
|
||
X(f'{i}<CodeAllowedLength>{code_allowed_length}</CodeAllowedLength>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
X(f'{i}<DefaultPresentation>AsDescription</DefaultPresentation>')
|
||
dependence = get_enum_prop('DependenceOnCalculationTypes', 'dependenceOnCalculationTypes', 'DontUse')
|
||
X(f'{i}<DependenceOnCalculationTypes>{dependence}</DependenceOnCalculationTypes>')
|
||
base_types = list(defn.get('baseCalculationTypes', []))
|
||
if base_types:
|
||
X(f'{i}<BaseCalculationTypes>')
|
||
for bt in base_types:
|
||
X(f'{i}\t<xr:Item xsi:type="xr:MDObjectRef">{bt}</xr:Item>')
|
||
X(f'{i}</BaseCalculationTypes>')
|
||
else:
|
||
X(f'{i}<BaseCalculationTypes/>')
|
||
action_period_use = 'true' if defn.get('actionPeriodUse') is True else 'false'
|
||
X(f'{i}<ActionPeriodUse>{action_period_use}</ActionPeriodUse>')
|
||
emit_standard_attributes(i, 'ChartOfCalculationTypes')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
|
||
X(f'{i}<EditType>InDialog</EditType>')
|
||
quick_choice = 'true' if defn.get('quickChoice') is True else 'false'
|
||
X(f'{i}<QuickChoice>{quick_choice}</QuickChoice>')
|
||
X(f'{i}<ChoiceMode>BothWays</ChoiceMode>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>ChartOfCalculationTypes.{obj_name}.StandardAttribute.Description</xr:Field>')
|
||
X(f'{i}\t<xr:Field>ChartOfCalculationTypes.{obj_name}.StandardAttribute.Code</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
|
||
def emit_calculation_register_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
chart_of_calc_types = str(defn['chartOfCalculationTypes']) if defn.get('chartOfCalculationTypes') else ''
|
||
if chart_of_calc_types:
|
||
X(f'{i}<ChartOfCalculationTypes>{chart_of_calc_types}</ChartOfCalculationTypes>')
|
||
else:
|
||
X(f'{i}<ChartOfCalculationTypes/>')
|
||
periodicity = get_enum_prop('InformationRegisterPeriodicity', 'periodicity', 'Month')
|
||
X(f'{i}<Periodicity>{periodicity}</Periodicity>')
|
||
action_period = 'true' if defn.get('actionPeriod') is True else 'false'
|
||
X(f'{i}<ActionPeriod>{action_period}</ActionPeriod>')
|
||
base_period = 'true' if defn.get('basePeriod') is True else 'false'
|
||
X(f'{i}<BasePeriod>{base_period}</BasePeriod>')
|
||
schedule = str(defn['schedule']) if defn.get('schedule') else ''
|
||
if schedule:
|
||
X(f'{i}<Schedule>{schedule}</Schedule>')
|
||
else:
|
||
X(f'{i}<Schedule/>')
|
||
schedule_value = str(defn['scheduleValue']) if defn.get('scheduleValue') else ''
|
||
if schedule_value:
|
||
X(f'{i}<ScheduleValue>{schedule_value}</ScheduleValue>')
|
||
else:
|
||
X(f'{i}<ScheduleValue/>')
|
||
schedule_date = str(defn['scheduleDate']) if defn.get('scheduleDate') else ''
|
||
if schedule_date:
|
||
X(f'{i}<ScheduleDate>{schedule_date}</ScheduleDate>')
|
||
else:
|
||
X(f'{i}<ScheduleDate/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
emit_standard_attributes(i, 'CalculationRegister')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
|
||
def emit_business_process_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
edit_type = get_enum_prop('EditType', 'editType', 'InDialog')
|
||
X(f'{i}<EditType>{edit_type}</EditType>')
|
||
number_type = get_enum_prop('NumberType', 'numberType', 'String')
|
||
number_length = str(defn['numberLength']) if defn.get('numberLength') is not None else '11'
|
||
number_allowed_length = get_enum_prop('NumberAllowedLength', 'numberAllowedLength', 'Variable')
|
||
check_unique = 'false' if defn.get('checkUnique') is False else 'true'
|
||
autonumbering = 'false' if defn.get('autonumbering') is False else 'true'
|
||
X(f'{i}<NumberType>{number_type}</NumberType>')
|
||
X(f'{i}<NumberLength>{number_length}</NumberLength>')
|
||
X(f'{i}<NumberAllowedLength>{number_allowed_length}</NumberAllowedLength>')
|
||
X(f'{i}<CheckUnique>{check_unique}</CheckUnique>')
|
||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||
emit_standard_attributes(i, 'BusinessProcess')
|
||
X(f'{i}<Characteristics/>')
|
||
task_ref = str(defn['task']) if defn.get('task') else ''
|
||
if task_ref:
|
||
X(f'{i}<Task>{task_ref}</Task>')
|
||
else:
|
||
X(f'{i}<Task/>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>BusinessProcess.{obj_name}.StandardAttribute.Number</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_task_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
X(f'{i}<UseStandardCommands>true</UseStandardCommands>')
|
||
number_type = get_enum_prop('NumberType', 'numberType', 'String')
|
||
number_length = str(defn['numberLength']) if defn.get('numberLength') is not None else '14'
|
||
number_allowed_length = get_enum_prop('NumberAllowedLength', 'numberAllowedLength', 'Variable')
|
||
check_unique = 'false' if defn.get('checkUnique') is False else 'true'
|
||
autonumbering = 'false' if defn.get('autonumbering') is False else 'true'
|
||
task_number_auto_prefix = str(defn['taskNumberAutoPrefix']) if defn.get('taskNumberAutoPrefix') else 'BusinessProcessNumber'
|
||
description_length = str(defn['descriptionLength']) if defn.get('descriptionLength') is not None else '150'
|
||
X(f'{i}<NumberType>{number_type}</NumberType>')
|
||
X(f'{i}<NumberLength>{number_length}</NumberLength>')
|
||
X(f'{i}<NumberAllowedLength>{number_allowed_length}</NumberAllowedLength>')
|
||
X(f'{i}<CheckUnique>{check_unique}</CheckUnique>')
|
||
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
|
||
X(f'{i}<TaskNumberAutoPrefix>{task_number_auto_prefix}</TaskNumberAutoPrefix>')
|
||
X(f'{i}<DescriptionLength>{description_length}</DescriptionLength>')
|
||
addressing = str(defn['addressing']) if defn.get('addressing') else ''
|
||
if addressing:
|
||
X(f'{i}<Addressing>{addressing}</Addressing>')
|
||
else:
|
||
X(f'{i}<Addressing/>')
|
||
main_addressing = str(defn['mainAddressingAttribute']) if defn.get('mainAddressingAttribute') else ''
|
||
if main_addressing:
|
||
X(f'{i}<MainAddressingAttribute>{main_addressing}</MainAddressingAttribute>')
|
||
else:
|
||
X(f'{i}<MainAddressingAttribute/>')
|
||
current_performer = str(defn['currentPerformer']) if defn.get('currentPerformer') else ''
|
||
if current_performer:
|
||
X(f'{i}<CurrentPerformer>{current_performer}</CurrentPerformer>')
|
||
else:
|
||
X(f'{i}<CurrentPerformer/>')
|
||
emit_standard_attributes(i, 'Task')
|
||
X(f'{i}<Characteristics/>')
|
||
X(f'{i}<BasedOn/>')
|
||
X(f'{i}<InputByString>')
|
||
X(f'{i}\t<xr:Field>Task.{obj_name}.StandardAttribute.Number</xr:Field>')
|
||
X(f'{i}</InputByString>')
|
||
X(f'{i}<CreateOnInput>DontUse</CreateOnInput>')
|
||
X(f'{i}<SearchStringModeOnInputByString>Begin</SearchStringModeOnInputByString>')
|
||
X(f'{i}<FullTextSearchOnInputByString>DontUse</FullTextSearchOnInputByString>')
|
||
X(f'{i}<ChoiceDataGetModeOnInputByString>Directly</ChoiceDataGetModeOnInputByString>')
|
||
X(f'{i}<DefaultObjectForm/>')
|
||
X(f'{i}<DefaultListForm/>')
|
||
X(f'{i}<DefaultChoiceForm/>')
|
||
X(f'{i}<AuxiliaryObjectForm/>')
|
||
X(f'{i}<AuxiliaryListForm/>')
|
||
X(f'{i}<AuxiliaryChoiceForm/>')
|
||
X(f'{i}<IncludeHelpInContents>false</IncludeHelpInContents>')
|
||
X(f'{i}<DataLockFields/>')
|
||
data_lock_control_mode = get_enum_prop('DataLockControlMode', 'dataLockControlMode', 'Automatic')
|
||
X(f'{i}<DataLockControlMode>{data_lock_control_mode}</DataLockControlMode>')
|
||
full_text_search = get_enum_prop('FullTextSearch', 'fullTextSearch', 'Use')
|
||
X(f'{i}<FullTextSearch>{full_text_search}</FullTextSearch>')
|
||
X(f'{i}<ObjectPresentation/>')
|
||
X(f'{i}<ExtendedObjectPresentation/>')
|
||
X(f'{i}<ListPresentation/>')
|
||
X(f'{i}<ExtendedListPresentation/>')
|
||
X(f'{i}<Explanation/>')
|
||
X(f'{i}<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{i}<DataHistory>DontUse</DataHistory>')
|
||
X(f'{i}<UpdateDataHistoryImmediatelyAfterWrite>false</UpdateDataHistoryImmediatelyAfterWrite>')
|
||
X(f'{i}<ExecuteAfterWriteDataHistoryVersionProcessing>false</ExecuteAfterWriteDataHistoryVersionProcessing>')
|
||
|
||
def emit_http_service_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
root_url = str(defn['rootURL']) if defn.get('rootURL') else obj_name.lower()
|
||
X(f'{i}<RootURL>{esc_xml(root_url)}</RootURL>')
|
||
reuse_sessions = get_enum_prop('ReuseSessions', 'reuseSessions', 'DontUse')
|
||
X(f'{i}<ReuseSessions>{reuse_sessions}</ReuseSessions>')
|
||
session_max_age = str(defn['sessionMaxAge']) if defn.get('sessionMaxAge') is not None else '20'
|
||
X(f'{i}<SessionMaxAge>{session_max_age}</SessionMaxAge>')
|
||
|
||
def emit_web_service_properties(indent):
|
||
i = indent
|
||
X(f'{i}<Name>{esc_xml(obj_name)}</Name>')
|
||
emit_mltext(i, 'Synonym', synonym)
|
||
X(f'{i}<Comment/>')
|
||
namespace = str(defn['namespace']) if defn.get('namespace') else ''
|
||
X(f'{i}<Namespace>{esc_xml(namespace)}</Namespace>')
|
||
xdto_packages = str(defn['xdtoPackages']) if defn.get('xdtoPackages') else ''
|
||
if xdto_packages:
|
||
X(f'{i}<XDTOPackages>{xdto_packages}</XDTOPackages>')
|
||
else:
|
||
X(f'{i}<XDTOPackages/>')
|
||
reuse_sessions = get_enum_prop('ReuseSessions', 'reuseSessions', 'DontUse')
|
||
X(f'{i}<ReuseSessions>{reuse_sessions}</ReuseSessions>')
|
||
session_max_age = str(defn['sessionMaxAge']) if defn.get('sessionMaxAge') is not None else '20'
|
||
X(f'{i}<SessionMaxAge>{session_max_age}</SessionMaxAge>')
|
||
|
||
|
||
# --- 13g. ChildObjects emitters for new types ---
|
||
|
||
def emit_column(indent, col_def):
|
||
uid = new_uuid()
|
||
name = ''
|
||
col_synonym = ''
|
||
indexing = 'DontIndex'
|
||
references = []
|
||
if isinstance(col_def, str):
|
||
name = col_def
|
||
col_synonym = split_camel_case(name)
|
||
else:
|
||
name = str(col_def.get('name', ''))
|
||
col_synonym = str(col_def['synonym']) if col_def.get('synonym') else split_camel_case(name)
|
||
if col_def.get('indexing'):
|
||
indexing = str(col_def['indexing'])
|
||
if col_def.get('references'):
|
||
references = list(col_def['references'])
|
||
X(f'{indent}<Column uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', col_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t\t<Indexing>{indexing}</Indexing>')
|
||
if references:
|
||
X(f'{indent}\t\t<References>')
|
||
for ref in references:
|
||
X(f'{indent}\t\t\t<xr:Item xsi:type="xr:MDObjectRef">{ref}</xr:Item>')
|
||
X(f'{indent}\t\t</References>')
|
||
else:
|
||
X(f'{indent}\t\t<References/>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</Column>')
|
||
|
||
def emit_accounting_flag(indent, flag_name):
|
||
uid = new_uuid()
|
||
flag_synonym = split_camel_case(flag_name)
|
||
X(f'{indent}<AccountingFlag uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(flag_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', flag_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:boolean</v8:Type>')
|
||
X(f'{indent}\t\t</Type>')
|
||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||
X(f'{indent}\t\t<Format/>')
|
||
X(f'{indent}\t\t<EditFormat/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{indent}\t\t<Mask/>')
|
||
X(f'{indent}\t\t<MultiLine>false</MultiLine>')
|
||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<FillChecking>DontCheck</FillChecking>')
|
||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{indent}\t\t<ChoiceForm/>')
|
||
X(f'{indent}\t\t<LinkByType/>')
|
||
X(f'{indent}\t\t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</AccountingFlag>')
|
||
|
||
def emit_ext_dimension_accounting_flag(indent, flag_name):
|
||
uid = new_uuid()
|
||
flag_synonym = split_camel_case(flag_name)
|
||
X(f'{indent}<ExtDimensionAccountingFlag uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(flag_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', flag_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:boolean</v8:Type>')
|
||
X(f'{indent}\t\t</Type>')
|
||
X(f'{indent}\t\t<PasswordMode>false</PasswordMode>')
|
||
X(f'{indent}\t\t<Format/>')
|
||
X(f'{indent}\t\t<EditFormat/>')
|
||
X(f'{indent}\t\t<ToolTip/>')
|
||
X(f'{indent}\t\t<MarkNegatives>false</MarkNegatives>')
|
||
X(f'{indent}\t\t<Mask/>')
|
||
X(f'{indent}\t\t<MultiLine>false</MultiLine>')
|
||
X(f'{indent}\t\t<ExtendedEdit>false</ExtendedEdit>')
|
||
X(f'{indent}\t\t<MinValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<MaxValue xsi:nil="true"/>')
|
||
X(f'{indent}\t\t<FillChecking>DontCheck</FillChecking>')
|
||
X(f'{indent}\t\t<ChoiceParameterLinks/>')
|
||
X(f'{indent}\t\t<ChoiceParameters/>')
|
||
X(f'{indent}\t\t<QuickChoice>Auto</QuickChoice>')
|
||
X(f'{indent}\t\t<ChoiceForm/>')
|
||
X(f'{indent}\t\t<LinkByType/>')
|
||
X(f'{indent}\t\t<ChoiceHistoryOnInput>Auto</ChoiceHistoryOnInput>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</ExtDimensionAccountingFlag>')
|
||
|
||
def emit_url_template(indent, tmpl_name, tmpl_def):
|
||
uid = new_uuid()
|
||
tmpl_synonym = split_camel_case(tmpl_name)
|
||
template = ''
|
||
methods = {}
|
||
if isinstance(tmpl_def, str):
|
||
template = tmpl_def
|
||
else:
|
||
template = str(tmpl_def['template']) if tmpl_def.get('template') else f'/{tmpl_name.lower()}'
|
||
if tmpl_def.get('methods'):
|
||
for k, v in tmpl_def['methods'].items():
|
||
methods[k] = str(v)
|
||
X(f'{indent}<URLTemplate uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(tmpl_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', tmpl_synonym)
|
||
X(f'{indent}\t\t<Template>{esc_xml(template)}</Template>')
|
||
X(f'{indent}\t</Properties>')
|
||
if methods:
|
||
X(f'{indent}\t<ChildObjects>')
|
||
for method_name, http_method in sorted(methods.items()):
|
||
method_uuid = new_uuid()
|
||
method_synonym = split_camel_case(method_name)
|
||
handler = f'{tmpl_name}{method_name}'
|
||
X(f'{indent}\t\t<Method uuid="{method_uuid}">')
|
||
X(f'{indent}\t\t\t<Properties>')
|
||
X(f'{indent}\t\t\t\t<Name>{esc_xml(method_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t\t\t', 'Synonym', method_synonym)
|
||
X(f'{indent}\t\t\t\t<HTTPMethod>{http_method}</HTTPMethod>')
|
||
X(f'{indent}\t\t\t\t<Handler>{esc_xml(handler)}</Handler>')
|
||
X(f'{indent}\t\t\t</Properties>')
|
||
X(f'{indent}\t\t</Method>')
|
||
X(f'{indent}\t</ChildObjects>')
|
||
else:
|
||
X(f'{indent}\t<ChildObjects/>')
|
||
X(f'{indent}</URLTemplate>')
|
||
|
||
def emit_operation(indent, op_name, op_def):
|
||
uid = new_uuid()
|
||
op_synonym = split_camel_case(op_name)
|
||
return_type = 'xs:string'
|
||
nillable = 'false'
|
||
transactioned = 'false'
|
||
handler = op_name
|
||
params = {}
|
||
if isinstance(op_def, str):
|
||
return_type = op_def
|
||
else:
|
||
if op_def.get('returnType'):
|
||
return_type = str(op_def['returnType'])
|
||
if op_def.get('nillable') is True:
|
||
nillable = 'true'
|
||
if op_def.get('transactioned') is True:
|
||
transactioned = 'true'
|
||
if op_def.get('handler'):
|
||
handler = str(op_def['handler'])
|
||
if op_def.get('parameters'):
|
||
for k, v in op_def['parameters'].items():
|
||
params[k] = v
|
||
X(f'{indent}<Operation uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(op_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', op_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
X(f'{indent}\t\t<XDTOReturningValueType>{return_type}</XDTOReturningValueType>')
|
||
X(f'{indent}\t\t<Nillable>{nillable}</Nillable>')
|
||
X(f'{indent}\t\t<Transactioned>{transactioned}</Transactioned>')
|
||
X(f'{indent}\t\t<ProcedureName>{esc_xml(handler)}</ProcedureName>')
|
||
X(f'{indent}\t</Properties>')
|
||
if params:
|
||
X(f'{indent}\t<ChildObjects>')
|
||
for param_name, param_def in sorted(params.items()):
|
||
param_uuid = new_uuid()
|
||
param_synonym = split_camel_case(param_name)
|
||
param_type = 'xs:string'
|
||
param_nillable = 'true'
|
||
param_dir = 'In'
|
||
if isinstance(param_def, str):
|
||
param_type = param_def
|
||
else:
|
||
if param_def.get('type'):
|
||
param_type = str(param_def['type'])
|
||
if param_def.get('nillable') is False:
|
||
param_nillable = 'false'
|
||
if param_def.get('direction'):
|
||
param_dir = str(param_def['direction'])
|
||
X(f'{indent}\t\t<Parameter uuid="{param_uuid}">')
|
||
X(f'{indent}\t\t\t<Properties>')
|
||
X(f'{indent}\t\t\t\t<Name>{esc_xml(param_name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t\t\t', 'Synonym', param_synonym)
|
||
X(f'{indent}\t\t\t\t<XDTOValueType>{param_type}</XDTOValueType>')
|
||
X(f'{indent}\t\t\t\t<Nillable>{param_nillable}</Nillable>')
|
||
X(f'{indent}\t\t\t\t<TransferDirection>{param_dir}</TransferDirection>')
|
||
X(f'{indent}\t\t\t</Properties>')
|
||
X(f'{indent}\t\t</Parameter>')
|
||
X(f'{indent}\t</ChildObjects>')
|
||
else:
|
||
X(f'{indent}\t<ChildObjects/>')
|
||
X(f'{indent}</Operation>')
|
||
|
||
def emit_addressing_attribute(indent, addr_def):
|
||
uid = new_uuid()
|
||
name = ''
|
||
attr_synonym = ''
|
||
type_str = ''
|
||
addressing_dimension = ''
|
||
indexing = 'Index'
|
||
parsed = parse_attribute_shorthand(addr_def)
|
||
name = parsed['name']
|
||
attr_synonym = parsed['synonym']
|
||
type_str = parsed['type']
|
||
if not isinstance(addr_def, str):
|
||
if addr_def.get('addressingDimension'):
|
||
addressing_dimension = str(addr_def['addressingDimension'])
|
||
if addr_def.get('indexing'):
|
||
indexing = str(addr_def['indexing'])
|
||
X(f'{indent}<AddressingAttribute uuid="{uid}">')
|
||
X(f'{indent}\t<Properties>')
|
||
X(f'{indent}\t\t<Name>{esc_xml(name)}</Name>')
|
||
emit_mltext(f'{indent}\t\t', 'Synonym', attr_synonym)
|
||
X(f'{indent}\t\t<Comment/>')
|
||
if type_str:
|
||
emit_value_type(f'{indent}\t\t', type_str)
|
||
else:
|
||
X(f'{indent}\t\t<Type>')
|
||
X(f'{indent}\t\t\t<v8:Type>xs:string</v8:Type>')
|
||
X(f'{indent}\t\t</Type>')
|
||
if addressing_dimension:
|
||
X(f'{indent}\t\t<AddressingDimension>{addressing_dimension}</AddressingDimension>')
|
||
else:
|
||
X(f'{indent}\t\t<AddressingDimension/>')
|
||
X(f'{indent}\t\t<Indexing>{indexing}</Indexing>')
|
||
X(f'{indent}\t\t<FullTextSearch>Use</FullTextSearch>')
|
||
X(f'{indent}\t\t<DataHistory>Use</DataHistory>')
|
||
X(f'{indent}\t</Properties>')
|
||
X(f'{indent}</AddressingAttribute>')
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 14. Namespaces
|
||
# ---------------------------------------------------------------------------
|
||
|
||
xmlns_decl = 'xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 14a. Detect format version from existing Configuration.xml
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def detect_format_version(d):
|
||
while d:
|
||
cfg_path = os.path.join(d, "Configuration.xml")
|
||
if os.path.isfile(cfg_path):
|
||
with open(cfg_path, "r", encoding="utf-8-sig") as f:
|
||
head = f.read(2000)
|
||
m = re.search(r'<MetaDataObject[^>]+version="(\d+\.\d+)"', head)
|
||
if m:
|
||
return m.group(1)
|
||
parent = os.path.dirname(d)
|
||
if parent == d:
|
||
break
|
||
d = parent
|
||
return "2.17"
|
||
|
||
format_version = detect_format_version(output_dir)
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 15. Main assembler
|
||
# ---------------------------------------------------------------------------
|
||
|
||
obj_uuid = new_uuid()
|
||
|
||
X('<?xml version="1.0" encoding="UTF-8"?>')
|
||
X(f'<MetaDataObject {xmlns_decl} version="{format_version}">')
|
||
X(f'\t<{obj_type} uuid="{obj_uuid}">')
|
||
|
||
# InternalInfo
|
||
emit_internal_info('\t\t', obj_type, obj_name)
|
||
|
||
# Properties
|
||
X('\t\t<Properties>')
|
||
|
||
property_emitters = {
|
||
'Catalog': emit_catalog_properties,
|
||
'Document': emit_document_properties,
|
||
'Enum': emit_enum_properties,
|
||
'Constant': emit_constant_properties,
|
||
'InformationRegister': emit_information_register_properties,
|
||
'AccumulationRegister': emit_accumulation_register_properties,
|
||
'DefinedType': emit_defined_type_properties,
|
||
'CommonModule': emit_common_module_properties,
|
||
'ScheduledJob': emit_scheduled_job_properties,
|
||
'EventSubscription': emit_event_subscription_properties,
|
||
'Report': emit_report_properties,
|
||
'DataProcessor': emit_data_processor_properties,
|
||
'ExchangePlan': emit_exchange_plan_properties,
|
||
'ChartOfCharacteristicTypes': emit_chart_of_characteristic_types_properties,
|
||
'DocumentJournal': emit_document_journal_properties,
|
||
'ChartOfAccounts': emit_chart_of_accounts_properties,
|
||
'AccountingRegister': emit_accounting_register_properties,
|
||
'ChartOfCalculationTypes': emit_chart_of_calculation_types_properties,
|
||
'CalculationRegister': emit_calculation_register_properties,
|
||
'BusinessProcess': emit_business_process_properties,
|
||
'Task': emit_task_properties,
|
||
'HTTPService': emit_http_service_properties,
|
||
'WebService': emit_web_service_properties,
|
||
}
|
||
|
||
property_emitters[obj_type]('\t\t\t')
|
||
|
||
X('\t\t</Properties>')
|
||
|
||
# ChildObjects
|
||
has_children = False
|
||
|
||
# --- Types with Attributes + TabularSections ---
|
||
types_with_attr_ts = [
|
||
'Catalog', 'Document', 'Report', 'DataProcessor', 'ExchangePlan',
|
||
'ChartOfCharacteristicTypes', 'ChartOfAccounts', 'ChartOfCalculationTypes',
|
||
'BusinessProcess', 'Task',
|
||
]
|
||
|
||
if obj_type in types_with_attr_ts:
|
||
def _as_list(val):
|
||
"""Normalize attributes: dict {"K":"V"} → ["K:V"], list/other → list."""
|
||
if val is None:
|
||
return []
|
||
if isinstance(val, dict):
|
||
return [f"{k}:{v}" for k, v in val.items()]
|
||
return list(val)
|
||
|
||
attrs = []
|
||
if defn.get('attributes'):
|
||
for a in _as_list(defn['attributes']):
|
||
attrs.append(parse_attribute_shorthand(a))
|
||
ts_sections = {}
|
||
ts_order = []
|
||
if defn.get('tabularSections'):
|
||
ts_data = defn['tabularSections']
|
||
if isinstance(ts_data, list):
|
||
for ts in ts_data:
|
||
ts_name = ts['name']
|
||
ts_cols = _as_list(ts.get('attributes', []))
|
||
ts_sections[ts_name] = ts_cols
|
||
ts_order.append(ts_name)
|
||
else:
|
||
for k, v in ts_data.items():
|
||
ts_sections[k] = _as_list(v)
|
||
ts_order.append(k)
|
||
# ChartOfAccounts: AccountingFlags + ExtDimensionAccountingFlags
|
||
acct_flags = []
|
||
ext_dim_flags = []
|
||
if obj_type == 'ChartOfAccounts':
|
||
if defn.get('accountingFlags'):
|
||
acct_flags = _as_list(defn['accountingFlags'])
|
||
if defn.get('extDimensionAccountingFlags'):
|
||
ext_dim_flags = _as_list(defn['extDimensionAccountingFlags'])
|
||
# Task: AddressingAttributes
|
||
addr_attrs = []
|
||
if obj_type == 'Task' and defn.get('addressingAttributes'):
|
||
addr_attrs = _as_list(defn['addressingAttributes'])
|
||
child_count = len(attrs) + len(ts_sections) + len(acct_flags) + len(ext_dim_flags) + len(addr_attrs)
|
||
if child_count > 0:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
if obj_type == 'Catalog':
|
||
context = 'catalog'
|
||
elif obj_type == 'Document':
|
||
context = 'document'
|
||
elif obj_type in ('DataProcessor', 'Report'):
|
||
context = 'processor'
|
||
elif obj_type in ('ChartOfAccounts', 'ChartOfCharacteristicTypes', 'ChartOfCalculationTypes'):
|
||
context = 'chart'
|
||
else:
|
||
context = 'object'
|
||
for a in attrs:
|
||
emit_attribute('\t\t\t', a, context)
|
||
for ts_name in ts_order:
|
||
columns = ts_sections[ts_name]
|
||
emit_tabular_section('\t\t\t', ts_name, columns, obj_type, obj_name)
|
||
for af in acct_flags:
|
||
af_name = af['name'] if isinstance(af, dict) else str(af)
|
||
emit_accounting_flag('\t\t\t', af_name)
|
||
for edf in ext_dim_flags:
|
||
edf_name = edf['name'] if isinstance(edf, dict) else str(edf)
|
||
emit_ext_dimension_accounting_flag('\t\t\t', edf_name)
|
||
for aa in addr_attrs:
|
||
emit_addressing_attribute('\t\t\t', aa)
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- Enum: enum values ---
|
||
if obj_type == 'Enum':
|
||
values = []
|
||
if defn.get('values'):
|
||
for v in defn['values']:
|
||
values.append(parse_enum_value_shorthand(v))
|
||
if values:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
for v in values:
|
||
emit_enum_value('\t\t\t', v)
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- Constant, DefinedType, ScheduledJob, EventSubscription: no ChildObjects ---
|
||
|
||
# --- Registers: dimensions + resources + attributes ---
|
||
if obj_type in ('InformationRegister', 'AccumulationRegister', 'AccountingRegister', 'CalculationRegister'):
|
||
dims = []
|
||
resources = []
|
||
reg_attrs = []
|
||
if defn.get('dimensions'):
|
||
for d in defn['dimensions']:
|
||
dims.append(parse_attribute_shorthand(d))
|
||
if defn.get('resources'):
|
||
for r in defn['resources']:
|
||
resources.append(parse_attribute_shorthand(r))
|
||
if defn.get('attributes'):
|
||
for a in defn['attributes']:
|
||
reg_attrs.append(parse_attribute_shorthand(a))
|
||
if dims or resources or reg_attrs:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
for r in resources:
|
||
emit_resource('\t\t\t', r, obj_type)
|
||
for d in dims:
|
||
emit_dimension('\t\t\t', d, obj_type)
|
||
# InformationRegister.Attribute supports FillFromFillingValue/FillValue/DataHistory;
|
||
# AccumulationRegister/AccountingRegister/CalculationRegister.Attribute do NOT.
|
||
reg_ctx = 'register-info' if obj_type == 'InformationRegister' else 'register-other'
|
||
for a in reg_attrs:
|
||
emit_attribute('\t\t\t', a, reg_ctx)
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- DocumentJournal: columns ---
|
||
if obj_type == 'DocumentJournal':
|
||
columns = list(defn.get('columns', []))
|
||
if columns:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
for col in columns:
|
||
emit_column('\t\t\t', col)
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- HTTPService: URLTemplates ---
|
||
if obj_type == 'HTTPService':
|
||
url_templates = {}
|
||
url_tmpl_order = []
|
||
if defn.get('urlTemplates'):
|
||
for k, v in defn['urlTemplates'].items():
|
||
url_templates[k] = v
|
||
url_tmpl_order.append(k)
|
||
if url_templates:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
for tmpl_name in sorted(url_tmpl_order):
|
||
emit_url_template('\t\t\t', tmpl_name, url_templates[tmpl_name])
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- WebService: Operations ---
|
||
if obj_type == 'WebService':
|
||
operations = {}
|
||
op_order = []
|
||
if defn.get('operations'):
|
||
for k, v in defn['operations'].items():
|
||
operations[k] = v
|
||
op_order.append(k)
|
||
if operations:
|
||
has_children = True
|
||
X('\t\t<ChildObjects>')
|
||
for op_name in sorted(op_order):
|
||
emit_operation('\t\t\t', op_name, operations[op_name])
|
||
X('\t\t</ChildObjects>')
|
||
else:
|
||
X('\t\t<ChildObjects/>')
|
||
|
||
# --- CommonModule: no ChildObjects ---
|
||
|
||
X(f'\t</{obj_type}>')
|
||
X('</MetaDataObject>')
|
||
|
||
metadata_xml = '\n'.join(lines) + '\n'
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 16. Write files
|
||
# ---------------------------------------------------------------------------
|
||
|
||
type_plural_map = {
|
||
'Catalog': 'Catalogs',
|
||
'Document': 'Documents',
|
||
'Enum': 'Enums',
|
||
'Constant': 'Constants',
|
||
'InformationRegister': 'InformationRegisters',
|
||
'AccumulationRegister': 'AccumulationRegisters',
|
||
'AccountingRegister': 'AccountingRegisters',
|
||
'CalculationRegister': 'CalculationRegisters',
|
||
'ChartOfAccounts': 'ChartsOfAccounts',
|
||
'ChartOfCharacteristicTypes': 'ChartsOfCharacteristicTypes',
|
||
'ChartOfCalculationTypes': 'ChartsOfCalculationTypes',
|
||
'BusinessProcess': 'BusinessProcesses',
|
||
'Task': 'Tasks',
|
||
'ExchangePlan': 'ExchangePlans',
|
||
'DocumentJournal': 'DocumentJournals',
|
||
'Report': 'Reports',
|
||
'DataProcessor': 'DataProcessors',
|
||
'CommonModule': 'CommonModules',
|
||
'ScheduledJob': 'ScheduledJobs',
|
||
'EventSubscription': 'EventSubscriptions',
|
||
'HTTPService': 'HTTPServices',
|
||
'WebService': 'WebServices',
|
||
'DefinedType': 'DefinedTypes',
|
||
}
|
||
|
||
type_plural = type_plural_map[obj_type]
|
||
type_dir = os.path.join(output_dir, type_plural)
|
||
|
||
# Main XML file
|
||
main_xml_path = os.path.join(type_dir, f'{obj_name}.xml')
|
||
|
||
# Types that don't have subdirectory structure
|
||
types_no_sub_dir = ['DefinedType', 'ScheduledJob', 'EventSubscription']
|
||
|
||
obj_sub_dir = os.path.join(type_dir, obj_name)
|
||
ext_dir = os.path.join(obj_sub_dir, 'Ext')
|
||
|
||
os.makedirs(type_dir, exist_ok=True)
|
||
if obj_type not in types_no_sub_dir:
|
||
os.makedirs(obj_sub_dir, exist_ok=True)
|
||
|
||
write_utf8_bom(main_xml_path, metadata_xml)
|
||
|
||
# Module files
|
||
modules_created = []
|
||
|
||
types_with_object_module = [
|
||
'Catalog', 'Document', 'Report', 'DataProcessor', 'ExchangePlan',
|
||
'ChartOfAccounts', 'ChartOfCharacteristicTypes', 'ChartOfCalculationTypes',
|
||
'BusinessProcess', 'Task',
|
||
]
|
||
types_with_record_set_module = [
|
||
'InformationRegister', 'AccumulationRegister', 'AccountingRegister', 'CalculationRegister',
|
||
]
|
||
types_with_manager_module = ['Report', 'DataProcessor', 'Constant', 'Enum']
|
||
types_with_value_manager_module = ['Constant']
|
||
types_with_module = ['CommonModule', 'HTTPService', 'WebService']
|
||
|
||
def ensure_ext_dir():
|
||
os.makedirs(ext_dir, exist_ok=True)
|
||
|
||
if obj_type in types_with_object_module:
|
||
module_path = os.path.join(ext_dir, 'ObjectModule.bsl')
|
||
if not os.path.isfile(module_path):
|
||
ensure_ext_dir()
|
||
write_utf8_bom(module_path, '')
|
||
modules_created.append(module_path)
|
||
|
||
if obj_type in types_with_manager_module:
|
||
module_path = os.path.join(ext_dir, 'ManagerModule.bsl')
|
||
if not os.path.isfile(module_path):
|
||
ensure_ext_dir()
|
||
write_utf8_bom(module_path, '')
|
||
modules_created.append(module_path)
|
||
|
||
if obj_type in types_with_value_manager_module:
|
||
module_path = os.path.join(ext_dir, 'ValueManagerModule.bsl')
|
||
if not os.path.isfile(module_path):
|
||
ensure_ext_dir()
|
||
write_utf8_bom(module_path, '')
|
||
modules_created.append(module_path)
|
||
|
||
if obj_type in types_with_record_set_module:
|
||
module_path = os.path.join(ext_dir, 'RecordSetModule.bsl')
|
||
if not os.path.isfile(module_path):
|
||
ensure_ext_dir()
|
||
write_utf8_bom(module_path, '')
|
||
modules_created.append(module_path)
|
||
|
||
if obj_type in types_with_module:
|
||
module_path = os.path.join(ext_dir, 'Module.bsl')
|
||
if not os.path.isfile(module_path):
|
||
ensure_ext_dir()
|
||
write_utf8_bom(module_path, '')
|
||
modules_created.append(module_path)
|
||
|
||
# Special files
|
||
if obj_type == 'ExchangePlan':
|
||
content_path = os.path.join(ext_dir, 'Content.xml')
|
||
if not os.path.isfile(content_path):
|
||
ensure_ext_dir()
|
||
content_xml = f'<?xml version="1.0" encoding="UTF-8"?>\r\n<ExchangePlanContent xmlns="http://v8.1c.ru/8.3/xcf/extrnprops" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" version="{format_version}"/>\r\n'
|
||
write_utf8_bom(content_path, content_xml)
|
||
modules_created.append(content_path)
|
||
|
||
if obj_type == 'BusinessProcess':
|
||
flowchart_path = os.path.join(ext_dir, 'Flowchart.xml')
|
||
if not os.path.isfile(flowchart_path):
|
||
ensure_ext_dir()
|
||
flowchart_xml = f'<?xml version="1.0" encoding="UTF-8"?>\r\n<Flowchart xmlns="http://v8.1c.ru/8.3/MDClasses" version="{format_version}"/>\r\n'
|
||
write_utf8_bom(flowchart_path, flowchart_xml)
|
||
modules_created.append(flowchart_path)
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 17. Register in Configuration.xml
|
||
# ---------------------------------------------------------------------------
|
||
|
||
config_xml_path = os.path.join(output_dir, 'Configuration.xml')
|
||
reg_result = None
|
||
|
||
child_tag = obj_type
|
||
|
||
if os.path.isfile(config_xml_path):
|
||
# Parse preserving whitespace via raw string manipulation
|
||
with open(config_xml_path, 'r', encoding='utf-8-sig') as f:
|
||
config_content = f.read()
|
||
|
||
ns = 'http://v8.1c.ru/8.3/MDClasses'
|
||
ET.register_namespace('', ns)
|
||
# Parse all namespaces used in the file
|
||
# Use iterparse to collect namespace prefixes
|
||
namespaces_in_file = {}
|
||
for evt, elem in ET.iterparse(config_xml_path, events=['start-ns']):
|
||
prefix, uri = elem
|
||
if prefix:
|
||
namespaces_in_file[prefix] = uri
|
||
ET.register_namespace(prefix, uri)
|
||
|
||
tree = ET.parse(config_xml_path)
|
||
root = tree.getroot()
|
||
|
||
child_objects = root.find(f'{{{ns}}}Configuration/{{{ns}}}ChildObjects')
|
||
if child_objects is None:
|
||
# Try direct path
|
||
config_elem = root.find(f'{{{ns}}}Configuration')
|
||
if config_elem is not None:
|
||
child_objects = config_elem.find(f'{{{ns}}}ChildObjects')
|
||
|
||
if child_objects is not None:
|
||
existing = child_objects.findall(f'{{{ns}}}{child_tag}')
|
||
already_exists = False
|
||
for e in existing:
|
||
if (e.text or '').strip() == obj_name:
|
||
already_exists = True
|
||
break
|
||
|
||
if already_exists:
|
||
reg_result = 'already'
|
||
else:
|
||
new_elem = ET.SubElement(child_objects, f'{{{ns}}}{child_tag}')
|
||
new_elem.text = obj_name
|
||
|
||
if existing:
|
||
# Insert after last existing element of same type
|
||
last_elem = existing[-1]
|
||
all_children = list(child_objects)
|
||
idx = all_children.index(last_elem)
|
||
child_objects.remove(new_elem)
|
||
child_objects.insert(idx + 1, new_elem)
|
||
|
||
# Write back preserving BOM
|
||
tree.write(config_xml_path, encoding='utf-8', xml_declaration=True)
|
||
# Re-read to add BOM, fix declaration quotes, ensure trailing newline
|
||
with open(config_xml_path, 'r', encoding='utf-8') as f:
|
||
raw = f.read()
|
||
if raw.startswith("<?xml version='1.0' encoding='utf-8'?>"):
|
||
raw = raw.replace("<?xml version='1.0' encoding='utf-8'?>", '<?xml version="1.0" encoding="UTF-8"?>', 1)
|
||
if not raw.endswith('\n'):
|
||
raw += '\n'
|
||
write_utf8_bom(config_xml_path, raw)
|
||
reg_result = 'added'
|
||
else:
|
||
reg_result = 'no-childobj'
|
||
else:
|
||
reg_result = 'no-config'
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 18. Summary
|
||
# ---------------------------------------------------------------------------
|
||
|
||
attr_count = len(defn.get('attributes', []))
|
||
ts_count = 0
|
||
if defn.get('tabularSections'):
|
||
ts_data = defn['tabularSections']
|
||
if isinstance(ts_data, list):
|
||
ts_count = len(ts_data)
|
||
else:
|
||
ts_count = len(ts_data)
|
||
dim_count = len(defn.get('dimensions', []))
|
||
res_count = len(defn.get('resources', []))
|
||
val_count = len(defn.get('values', []))
|
||
col_count = len(defn.get('columns', []))
|
||
|
||
print(f"[OK] {obj_type} '{obj_name}' compiled")
|
||
print(f' UUID: {obj_uuid}')
|
||
print(f' File: {main_xml_path}')
|
||
|
||
details = []
|
||
if attr_count > 0:
|
||
details.append(f'Attributes: {attr_count}')
|
||
if ts_count > 0:
|
||
details.append(f'TabularSections: {ts_count}')
|
||
if dim_count > 0:
|
||
details.append(f'Dimensions: {dim_count}')
|
||
if res_count > 0:
|
||
details.append(f'Resources: {res_count}')
|
||
if val_count > 0:
|
||
details.append(f'Values: {val_count}')
|
||
if col_count > 0:
|
||
details.append(f'Columns: {col_count}')
|
||
|
||
if details:
|
||
print(f" {', '.join(details)}")
|
||
|
||
for mc in modules_created:
|
||
print(f' Module: {mc}')
|
||
|
||
if reg_result == 'added':
|
||
print(f' Configuration.xml: <{child_tag}>{obj_name}</{child_tag}> added to ChildObjects')
|
||
elif reg_result == 'already':
|
||
print(f' Configuration.xml: <{child_tag}>{obj_name}</{child_tag}> already registered')
|
||
elif reg_result == 'no-childobj':
|
||
print('WARNING: Configuration.xml found but <ChildObjects> not found', file=sys.stderr)
|
||
elif reg_result == 'no-config':
|
||
print(f' Configuration.xml: not found at {config_xml_path} (register manually)')
|
||
|
||
# Cross-reference hints
|
||
if obj_type == 'AccountingRegister' and not defn.get('chartOfAccounts'):
|
||
print('[HINT] AccountingRegister requires ChartOfAccounts reference:')
|
||
print(' /meta-edit -Operation modify-property -Value "ChartOfAccounts=ChartOfAccounts.XXX"')
|
||
if obj_type == 'CalculationRegister' and not defn.get('chartOfCalculationTypes'):
|
||
print('[HINT] CalculationRegister requires ChartOfCalculationTypes reference:')
|
||
print(' /meta-edit -Operation modify-property -Value "ChartOfCalculationTypes=ChartOfCalculationTypes.XXX"')
|
||
if obj_type == 'BusinessProcess' and not defn.get('task'):
|
||
print('[HINT] BusinessProcess requires Task reference:')
|
||
print(' /meta-edit -Operation modify-property -Value "Task=Task.XXX"')
|
||
if obj_type == 'ChartOfAccounts':
|
||
max_ext_dim = int(defn['maxExtDimensionCount']) if defn.get('maxExtDimensionCount') is not None else 0
|
||
if max_ext_dim > 0 and not defn.get('extDimensionTypes'):
|
||
print('[HINT] ChartOfAccounts with MaxExtDimensionCount>0 requires ExtDimensionTypes:')
|
||
print(' /meta-edit -Operation modify-property -Value "ExtDimensionTypes=ChartOfCharacteristicTypes.XXX"')
|