#!/usr/bin/env python3
# stub-db-create v1.0 — Create temp 1C infobase with metadata stubs for EPF/ERF build
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
import random
import re
import subprocess
import sys
import tempfile
import uuid
def new_uuid():
return str(uuid.uuid4())
def scan_ref_types(source_dir):
"""Scan XML files for reference/object/recordset types. Returns {metaType: {name: True}}."""
type_map = {}
ref_pattern = re.compile(
r'(?:cfg:|d\dp1:)(CatalogRef|DocumentRef|EnumRef|ChartOfAccountsRef'
r'|ChartOfCharacteristicTypesRef|ChartOfCalculationTypesRef'
r'|ExchangePlanRef|BusinessProcessRef|TaskRef)'
r'\.([A-Za-z\u0400-\u04FF\d_]+)'
)
obj_pattern = re.compile(
r'(?:cfg:|d\dp1:)(CatalogObject|DocumentObject|ChartOfAccountsObject'
r'|ChartOfCharacteristicTypesObject|ChartOfCalculationTypesObject'
r'|ExchangePlanObject|BusinessProcessObject|TaskObject)'
r'\.([A-Za-z\u0400-\u04FF\d_]+)'
)
rs_pattern = re.compile(
r'(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet'
r'|AccountingRegisterRecordSet|CalculationRegisterRecordSet)'
r'\.([A-Za-z\u0400-\u04FF\d_]+)'
)
char_pattern = re.compile(r'cfg:Characteristic\.([A-Za-z\u0400-\u04FF\d_]+)')
dt_pattern = re.compile(r'cfg:DefinedType\.([A-Za-z\u0400-\u04FF\d_]+)')
ref_map = {
'CatalogRef': 'Catalog', 'DocumentRef': 'Document', 'EnumRef': 'Enum',
'ChartOfAccountsRef': 'ChartOfAccounts',
'ChartOfCharacteristicTypesRef': 'ChartOfCharacteristicTypes',
'ChartOfCalculationTypesRef': 'ChartOfCalculationTypes',
'ExchangePlanRef': 'ExchangePlan', 'BusinessProcessRef': 'BusinessProcess', 'TaskRef': 'Task',
}
obj_map = {
'CatalogObject': 'Catalog', 'DocumentObject': 'Document',
'ChartOfAccountsObject': 'ChartOfAccounts',
'ChartOfCharacteristicTypesObject': 'ChartOfCharacteristicTypes',
'ChartOfCalculationTypesObject': 'ChartOfCalculationTypes',
'ExchangePlanObject': 'ExchangePlan', 'BusinessProcessObject': 'BusinessProcess', 'TaskObject': 'Task',
}
rs_map = {
'InformationRegisterRecordSet': 'InformationRegister',
'AccumulationRegisterRecordSet': 'AccumulationRegister',
'AccountingRegisterRecordSet': 'AccountingRegister',
'CalculationRegisterRecordSet': 'CalculationRegister',
}
for dirpath, _, filenames in os.walk(source_dir):
for fn in filenames:
if not fn.endswith('.xml'):
continue
fp = os.path.join(dirpath, fn)
try:
with open(fp, 'r', encoding='utf-8-sig') as f:
content = f.read()
except Exception:
continue
for m in ref_pattern.finditer(content):
mt = ref_map[m.group(1)]
type_map.setdefault(mt, {})[m.group(2)] = True
for m in obj_pattern.finditer(content):
mt = obj_map[m.group(1)]
type_map.setdefault(mt, {})[m.group(2)] = True
for m in rs_pattern.finditer(content):
mt = rs_map[m.group(1)]
type_map.setdefault(mt, {})[m.group(2)] = True
for m in char_pattern.finditer(content):
type_map.setdefault('ChartOfCharacteristicTypes', {})[m.group(1)] = True
for m in dt_pattern.finditer(content):
type_map.setdefault('DefinedType', {})[m.group(1)] = True
return type_map
def scan_register_columns(source_dir):
"""Scan Form.xml for register record set columns referenced via DataPath.
Returns {"RegisterType.RegisterName": {"col1": True, "col2": True}}."""
import xml.etree.ElementTree as ET
register_columns = {}
std_cols = {'LineNumber', 'Period', 'Recorder', 'Active', 'RecordType'}
rs_type_map = {
'InformationRegisterRecordSet': 'InformationRegister',
'AccumulationRegisterRecordSet': 'AccumulationRegister',
'AccountingRegisterRecordSet': 'AccountingRegister',
'CalculationRegisterRecordSet': 'CalculationRegister',
}
rs_pattern = re.compile(
r'^(?:cfg:|d\dp1:)(InformationRegisterRecordSet|AccumulationRegisterRecordSet'
r'|AccountingRegisterRecordSet|CalculationRegisterRecordSet)\.(.+)$'
)
dp_pattern = re.compile(r'([A-Za-z\u0400-\u04FF\d_]+)\.([A-Za-z\u0400-\u04FF\d_]+)')
ns = {
'v8': 'http://v8.1c.ru/8.1/data/core',
'f': 'http://v8.1c.ru/8.3/xcf/logform',
}
for dirpath, _, filenames in os.walk(source_dir):
for fn in filenames:
if fn != 'Form.xml':
continue
fp = os.path.join(dirpath, fn)
try:
with open(fp, 'r', encoding='utf-8-sig') as fh:
content = fh.read()
except Exception:
continue
if '' not in content:
continue
# Parse form attributes to find register recordset types
reg_attr_map = {} # formAttrName -> "RegisterType.RegisterName"
try:
root = ET.fromstring(content)
for attr_node in root.iter('{http://v8.1c.ru/8.3/xcf/logform}Attribute'):
attr_name = attr_node.get('name', '')
for type_node in attr_node.iter('{http://v8.1c.ru/8.1/data/core}Type'):
m = rs_pattern.match(type_node.text or '')
if m:
reg_type = rs_type_map[m.group(1)]
reg_key = f"{reg_type}.{m.group(2)}"
reg_attr_map[attr_name] = reg_key
register_columns.setdefault(reg_key, {})
except Exception:
continue
# Find DataPath references like "AttrName.ColumnName"
for m in dp_pattern.finditer(content):
attr_name, col_name = m.group(1), m.group(2)
if attr_name in reg_attr_map and col_name not in std_cols:
register_columns[reg_attr_map[attr_name]][col_name] = True
return register_columns
NS = (
'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" version="2.17"'
)
CLASS_IDS = [
"9cd510cd-abfc-11d4-9434-004095e12fc7",
"9fcd25a0-4822-11d4-9414-008048da11f9",
"e3687481-0a87-462c-a166-9f34594f9bba",
"9de14907-ec23-4a07-96f0-85521cb6b53b",
"51f2d5d8-ea4d-4064-8892-82951750031e",
"e68182ea-4237-4383-967f-90c1e3370bc7",
"fb282519-d103-4dd3-bc12-cb271d631dfc",
]
GT_DEFS = {
'Catalog': [('CatalogObject','Object'),('CatalogRef','Ref'),('CatalogSelection','Selection'),('CatalogList','List'),('CatalogManager','Manager')],
'Document': [('DocumentObject','Object'),('DocumentRef','Ref'),('DocumentSelection','Selection'),('DocumentList','List'),('DocumentManager','Manager')],
'Enum': [('EnumRef','Ref'),('EnumManager','Manager'),('EnumList','List')],
'ChartOfAccounts': [('ChartOfAccountsObject','Object'),('ChartOfAccountsRef','Ref'),('ChartOfAccountsSelection','Selection'),('ChartOfAccountsList','List'),('ChartOfAccountsManager','Manager')],
'ChartOfCharacteristicTypes': [('ChartOfCharacteristicTypesObject','Object'),('ChartOfCharacteristicTypesRef','Ref'),('ChartOfCharacteristicTypesSelection','Selection'),('ChartOfCharacteristicTypesList','List'),('Characteristic','Characteristic'),('ChartOfCharacteristicTypesManager','Manager')],
'ChartOfCalculationTypes': [('ChartOfCalculationTypesObject','Object'),('ChartOfCalculationTypesRef','Ref'),('ChartOfCalculationTypesSelection','Selection'),('ChartOfCalculationTypesList','List'),('ChartOfCalculationTypesManager','Manager')],
'ExchangePlan': [('ExchangePlanObject','Object'),('ExchangePlanRef','Ref'),('ExchangePlanSelection','Selection'),('ExchangePlanList','List'),('ExchangePlanManager','Manager')],
'BusinessProcess': [('BusinessProcessObject','Object'),('BusinessProcessRef','Ref'),('BusinessProcessSelection','Selection'),('BusinessProcessList','List'),('BusinessProcessManager','Manager')],
'Task': [('TaskObject','Object'),('TaskRef','Ref'),('TaskSelection','Selection'),('TaskList','List'),('TaskManager','Manager')],
'InformationRegister': [('InformationRegisterRecord','Record'),('InformationRegisterManager','Manager'),('InformationRegisterSelection','Selection'),('InformationRegisterList','List'),('InformationRegisterRecordSet','RecordSet'),('InformationRegisterRecordKey','RecordKey'),('InformationRegisterRecordManager','RecordManager')],
'AccumulationRegister': [('AccumulationRegisterRecord','Record'),('AccumulationRegisterManager','Manager'),('AccumulationRegisterSelection','Selection'),('AccumulationRegisterList','List'),('AccumulationRegisterRecordSet','RecordSet'),('AccumulationRegisterRecordKey','RecordKey')],
'AccountingRegister': [('AccountingRegisterRecord','Record'),('AccountingRegisterManager','Manager'),('AccountingRegisterSelection','Selection'),('AccountingRegisterExtDimensions','ExtDimensions'),('AccountingRegisterList','List'),('AccountingRegisterRecordSet','RecordSet'),('AccountingRegisterRecordKey','RecordKey')],
'CalculationRegister': [('CalculationRegisterRecord','Record'),('CalculationRegisterManager','Manager'),('CalculationRegisterSelection','Selection'),('CalculationRegisterList','List'),('CalculationRegisterRecordSet','RecordSet'),('CalculationRegisterRecordKey','RecordKey')],
'DefinedType': [('DefinedType','DefinedType')],
}
META_INFO = {
'Catalog': ('Catalog', 'Catalogs'),
'Document': ('Document', 'Documents'),
'Enum': ('Enum', 'Enums'),
'ChartOfAccounts': ('ChartOfAccounts', 'ChartsOfAccounts'),
'ChartOfCharacteristicTypes': ('ChartOfCharacteristicTypes', 'ChartsOfCharacteristicTypes'),
'ChartOfCalculationTypes': ('ChartOfCalculationTypes', 'ChartsOfCalculationTypes'),
'ExchangePlan': ('ExchangePlan', 'ExchangePlans'),
'BusinessProcess': ('BusinessProcess', 'BusinessProcesses'),
'Task': ('Task', 'Tasks'),
'InformationRegister': ('InformationRegister', 'InformationRegisters'),
'AccumulationRegister': ('AccumulationRegister', 'AccumulationRegisters'),
'AccountingRegister': ('AccountingRegister', 'AccountingRegisters'),
'CalculationRegister': ('CalculationRegister', 'CalculationRegisters'),
'DefinedType': ('DefinedType', 'DefinedTypes'),
}
STD_ATTRS_BY_TYPE = {
'Catalog': ['PredefinedDataName','Predefined','Ref','DeletionMark','IsFolder','Owner','Parent','Description','Code'],
'Document': ['Posted','Ref','DeletionMark','Date','Number'],
'Enum': ['Order','Ref'],
'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'],
'ExchangePlan': ['Ref','DeletionMark','Code','Description','ThisNode','SentNo','ReceivedNo'],
'BusinessProcess': ['Ref','DeletionMark','Date','Number','Started','Completed','HeadTask'],
'Task': ['Ref','DeletionMark','Date','Number','Executed','Description','RoutePoint','BusinessProcess'],
'InformationRegister': ['Active','LineNumber','Recorder','Period'],
'AccumulationRegister': ['Active','LineNumber','Recorder','Period'],
'AccountingRegister': ['Active','Period','Recorder','LineNumber','Account'],
'CalculationRegister': ['Active','Recorder','LineNumber','RegistrationPeriod','CalculationType','ReversingEntry'],
}
STD_ATTR_BODY = """\t\t\t\t
\t\t\t\tDontCheck
\t\t\t\tfalse
\t\t\t\tfalse
\t\t\t\tAuto
\t\t\t\t
\t\t\t\t
\t\t\t\tfalse
\t\t\t\t
\t\t\t\t
\t\t\t\tAuto
\t\t\t\tAuto
\t\t\t\t
\t\t\t\tfalse
\t\t\t\tUse
\t\t\t\tfalse
\t\t\t\t
\t\t\t\t
\t\t\t\t
\t\t\t\tUse
\t\t\t\t
\t\t\t\t
\t\t\t\t
\t\t\t\t"""
def build_std_attrs(meta_type):
attrs = STD_ATTRS_BY_TYPE.get(meta_type)
if not attrs:
return ''
lines = ['\t\t\t']
for a in attrs:
lines.append(f'\t\t\t\t')
lines.append(STD_ATTR_BODY)
lines.append(f'\t\t\t\t')
lines.append('\t\t\t')
return '\n'.join(lines) + '\n'
def build_internal_info(meta_type, obj_name):
gts = GT_DEFS.get(meta_type)
if not gts:
return ''
lines = ['\t\t']
if meta_type == 'ExchangePlan':
lines.append(f'\t\t\t{new_uuid()}')
for prefix, cat in gts:
full = f'{prefix}.{obj_name}'
lines.append(f'\t\t\t')
lines.append(f'\t\t\t\t{new_uuid()}')
lines.append(f'\t\t\t\t{new_uuid()}')
lines.append(f'\t\t\t')
lines.append('\t\t')
return '\n'.join(lines)
# Properties templates per type — returns the Properties content (without tags)
PROPS = {}
PROPS['Catalog'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\tHierarchyFoldersAndItems
\t\t\tfalse
\t\t\t2
\t\t\ttrue
\t\t\tfalse
\t\t\t
\t\t\tToItems
\t\t\t9
\t\t\t25
\t\t\tString
\t\t\tVariable
\t\t\tWholeCatalog
\t\t\tfalse
\t\t\ttrue
\t\t\tAsDescription
{sa}\t\t\t
\t\t\tAuto
\t\t\tInDialog
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['Enum'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
{sa}\t\t\t
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tAuto"""
PROPS['InformationRegister'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\tInDialog
\t\t\t
\t\t\t
\t\t\t
\t\t\t
{sa}\t\t\tNonperiodical
\t\t\tIndependent
\t\t\tfalse
\t\t\tfalse
\t\t\tAutomatic
\t\t\tUse
\t\t\tfalse
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['AccumulationRegister'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tBalance
\t\t\tfalse
{sa}\t\t\tAutomatic
\t\t\tUse
\t\t\ttrue
\t\t\t
\t\t\t
\t\t\t"""
PROPS['AccountingRegister'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\tfalse
{sa}\t\t\tAutomatic
\t\t\tUse
\t\t\ttrue
\t\t\t
\t\t\t
\t\t\t"""
PROPS['CalculationRegister'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
{sa}\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t"""
PROPS['ChartOfAccounts'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t20
\t\t\t100
\t\t\tWholeCatalog
\t\t\tfalse
\t\t\ttrue
\t\t\tAsDescription
{sa}\t\t\t
\t\t\tAuto
\t\t\tInDialog
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\ttrue
\t\t\t5
\t\t\t0
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['ChartOfCharacteristicTypes'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t9
\t\t\tVariable
\t\t\t25
\t\t\tfalse
\t\t\ttrue
\t\t\tAsDescription
\t\t\t
\t\t\t
\t\t\t\txs:boolean
\t\t\t\txs:string
\t\t\t\t
\t\t\t\t\t0
\t\t\t\t\tVariable
\t\t\t\t
\t\t\t\txs:decimal
\t\t\t\t
\t\t\t\t\t15
\t\t\t\t\t2
\t\t\t\t\tAny
\t\t\t\t
\t\t\t\txs:dateTime
\t\t\t\t
\t\t\t\t\tDateTime
\t\t\t\t
\t\t\t
\t\t\tfalse
\t\t\ttrue
{sa}\t\t\t
\t\t\tAuto
\t\t\tInDialog
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['ChartOfCalculationTypes'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t9
\t\t\t25
\t\t\tString
\t\t\tVariable
\t\t\tWholeCatalog
\t\t\tfalse
\t\t\ttrue
\t\t\tAsDescription
{sa}\t\t\t
\t\t\tAuto
\t\t\tInDialog
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\tNotDepend
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['ExchangePlan'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t9
\t\t\t25
\t\t\tVariable
{sa}\t\t\tAsDescription
\t\t\t
\t\t\tAuto
\t\t\tInDialog
\t\t\ttrue
\t\t\tBothWays
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tAuto
\t\t\tfalse
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['BusinessProcess'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\tString
\t\t\t11
\t\t\tVariable
\t\t\tYear
\t\t\tfalse
\t\t\ttrue
{sa}\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['Task'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\tString
\t\t\t11
\t\t\tVariable
\t\t\tYear
\t\t\tfalse
\t\t\ttrue
\t\t\t25
{sa}\t\t\t
\t\t\t
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
PROPS['DefinedType'] = lambda n, sa: f"""\t\t\t{n}
\t\t\t
\t\t\t
\t\t\t
\t\t\t\txs:string
\t\t\t\t
\t\t\t\t\t0
\t\t\t\t\tVariable
\t\t\t\t
\t\t\t"""
def build_doc_props(obj_name, std_attrs, reg_records_xml):
return f"""\t\t\t{obj_name}
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\t
\t\t\tString
\t\t\t11
\t\t\tVariable
\t\t\tYear
\t\t\tfalse
\t\t\ttrue
{std_attrs}\t\t\t
\t\t\t
\t\t\t
\t\t\tDontUse
\t\t\tBegin
\t\t\tDontUse
\t\t\tDirectly
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tAllow
\t\t\tDeny
\t\t\tAutoDelete
\t\t\tWriteModified
\t\t\tAutoFill
\t\t\t{reg_records_xml}
\t\t\ttrue
\t\t\ttrue
\t\t\tfalse
\t\t\t
\t\t\tAutomatic
\t\t\tUse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tAuto
\t\t\tDontUse
\t\t\tfalse
\t\t\tfalse"""
def write_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def main():
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
parser = argparse.ArgumentParser(description='Create temp 1C infobase with metadata stubs')
parser.add_argument('-SourceDir', required=True)
parser.add_argument('-V8Path', required=True)
parser.add_argument('-TempBasePath', default='')
args = parser.parse_args()
type_map = scan_ref_types(args.SourceDir)
register_columns = scan_register_columns(args.SourceDir)
has_ref_types = len(type_map) > 0
temp_base = args.TempBasePath or os.path.join(tempfile.gettempdir(), f'epf_stub_db_{random.randint(0,999999)}')
# Add registrator stub document if needed
registrator_types = ['AccumulationRegister', 'AccountingRegister', 'CalculationRegister']
needs_registrator = any(rt in type_map and len(type_map[rt]) > 0 for rt in registrator_types)
if needs_registrator:
type_map.setdefault('Document', {})['\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430'] = True # ЗаглушкаРегистратора
if has_ref_types:
cfg_dir = os.path.join(temp_base, 'cfg')
os.makedirs(cfg_dir, exist_ok=True)
# Configuration.xml
uuid_cfg = new_uuid()
uuid_lang = new_uuid()
co_ids = [new_uuid() for _ in range(7)]
co_xml = ''
for i in range(7):
co_xml += f'\n\t\t\t\n\t\t\t\t{CLASS_IDS[i]}\n\t\t\t\t{co_ids[i]}\n\t\t\t'
child_xml = '\n\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439' # Русский
for meta_type, names in type_map.items():
if meta_type not in META_INFO:
continue
tag = META_INFO[meta_type][0]
for name in names:
child_xml += f'\n\t\t\t<{tag}>{name}{tag}>'
cfg_xml = f"""
\t
\t\t{co_xml}
\t\t
\t\t
\t\t\tStubConfig
\t\t\t
\t\t\t
\t\t\t
\t\t\tVersion8_3_24
\t\t\tManagedApplication
\t\t\t
\t\t\t\tPlatformApplication
\t\t\t
\t\t\tRussian
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tfalse
\t\t\tfalse
\t\t\tfalse
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tNormal
\t\t\t
\t\t\t
\t\t\tLanguage.\u0420\u0443\u0441\u0441\u043a\u0438\u0439
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\t
\t\t\tManaged
\t\t\tNotAutoFree
\t\t\tDontUse
\t\t\tDontUse
\t\t\tTaxi
\t\t\tDontUse
\t\t\tVersion8_3_24
\t\t\t
\t\t
\t\t{child_xml}
\t\t
\t
"""
write_bom(os.path.join(cfg_dir, 'Configuration.xml'), cfg_xml)
# Language
lang_dir = os.path.join(cfg_dir, 'Languages')
os.makedirs(lang_dir, exist_ok=True)
lang_xml = f"""
\t
\t\t
\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439
\t\t\t
\t\t\t\t
\t\t\t\t\tru
\t\t\t\t\t\u0420\u0443\u0441\u0441\u043a\u0438\u0439
\t\t\t\t
\t\t\t
\t\t\t
\t\t\tru
\t\t
\t
"""
write_bom(os.path.join(lang_dir, '\u0420\u0443\u0441\u0441\u043a\u0438\u0439.xml'), lang_xml)
# Metadata stubs
for meta_type, names in type_map.items():
if meta_type not in META_INFO:
continue
tag, dirname = META_INFO[meta_type]
obj_dir = os.path.join(cfg_dir, dirname)
os.makedirs(obj_dir, exist_ok=True)
for obj_name in names:
obj_uuid = new_uuid()
internal_xml = build_internal_info(meta_type, obj_name)
if internal_xml:
internal_xml = '\n' + internal_xml
sa = build_std_attrs(meta_type)
if meta_type == 'Document':
rr_xml = ''
if obj_name == '\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430': # ЗаглушкаРегистратора
rr_lines = []
for rt in registrator_types:
if rt in type_map:
for rn in type_map[rt]:
rr_lines.append(f'\t\t\t\t{rt}.{rn}')
if rr_lines:
rr_xml = '\n' + '\n'.join(rr_lines) + '\n\t\t\t'
props_xml = build_doc_props(obj_name, sa, rr_xml)
elif meta_type in PROPS:
props_xml = PROPS[meta_type](obj_name, sa)
else:
props_xml = f'\t\t\t{obj_name}\n\t\t\t\n\t\t\t'
# ChildObjects — varies by type
if meta_type == 'DefinedType':
child_obj_xml = ''
elif meta_type == 'InformationRegister':
reg_key = f'InformationRegister.{obj_name}'
cols = list(register_columns.get(reg_key, {}).keys()) or ['\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430']
parts = []
for i, col in enumerate(cols):
u = new_uuid()
if i == 0:
parts.append(f"""\t\t\t
\t\t\t\t
\t\t\t\t\t{col}
\t\t\t\t\t
\t\t\t\t\txs:string10Variable
\t\t\t\t\tfalsefalse
\t\t\t\t\tfalsefalse
\t\t\t\t\t
\t\t\t\t\tfalseDontCheck
\t\t\t\t\tItems
\t\t\t\t\tAutoAutoAuto
\t\t\t\t\tfalsetruefalse
\t\t\t\t\tDontIndexUseUse
\t\t\t\t
\t\t\t""")
else:
parts.append(f"""\t\t\t
\t\t\t\t
\t\t\t\t\t{col}
\t\t\t\t\t
\t\t\t\t\txs:string10Variable
\t\t\t\t\tfalsefalse
\t\t\t\t\tfalsefalse
\t\t\t\t\t
\t\t\t\t\tfalseDontCheck
\t\t\t\t\tItems
\t\t\t\t\tAutoAutoAuto
\t\t\t\t\tDontIndexUseUse
\t\t\t\t
\t\t\t""")
child_obj_xml = '\n\t\t\n' + '\n'.join(parts) + '\n\t\t'
elif meta_type in ('AccumulationRegister', 'AccountingRegister', 'CalculationRegister'):
reg_key = f'{meta_type}.{obj_name}'
cols = list(register_columns.get(reg_key, {}).keys())
parts = []
# Required stub Resource
parts.append(f"""\t\t\t
\t\t\t\t
\t\t\t\t\t\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430
\t\t\t\t\t
\t\t\t\t\txs:decimal152Any
\t\t\t\t\tfalsefalse
\t\t\t\t\tfalsefalse
\t\t\t\t\tDontCheck
\t\t\t\t\tItems
\t\t\t\t\tAutoAutoAuto
\t\t\t\t\tUse
\t\t\t\t
\t\t\t""")
# Form-referenced columns as Dimensions
for col in cols:
parts.append(f"""\t\t\t
\t\t\t\t
\t\t\t\t\t{col}
\t\t\t\t\t
\t\t\t\t\txs:string10Variable
\t\t\t\t\tfalsefalse
\t\t\t\t\tfalsefalse
\t\t\t\t\tDontCheck
\t\t\t\t\tItems
\t\t\t\t\tAutoAutoAuto
\t\t\t\t\tUse
\t\t\t\t
\t\t\t""")
child_obj_xml = '\n\t\t\n' + '\n'.join(parts) + '\n\t\t'
else:
child_obj_xml = '\n\t\t'
obj_xml = f"""
\t<{tag} uuid="{obj_uuid}">{internal_xml}
\t\t
{props_xml}
\t\t{child_obj_xml}
\t{tag}>
"""
write_bom(os.path.join(obj_dir, f'{obj_name}.xml'), obj_xml)
print(f'Generated stub configuration with {len(type_map)} metadata types')
if register_columns:
print('WARNING: Register column categories (Dimension/Resource/Attribute) are guessed. Form field bindings may not survive round-trip through a real database.')
# Create infobase
print(f'Creating infobase: {temp_base}')
result = subprocess.run(
[args.V8Path, 'CREATEINFOBASE', f'File={temp_base}', '/DisableStartupDialogs'],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f'Failed to create infobase (code: {result.returncode})', file=sys.stderr)
sys.exit(1)
if has_ref_types:
cfg_dir = os.path.join(temp_base, 'cfg')
# LoadConfigFromFiles
print('Loading configuration from files...')
result = subprocess.run(
[args.V8Path, 'DESIGNER', f'/F{temp_base}', '/LoadConfigFromFiles', cfg_dir, '/DisableStartupDialogs'],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f'Failed to load config (code: {result.returncode})', file=sys.stderr)
sys.exit(1)
# UpdateDBCfg
print('Updating database configuration...')
update_log = os.path.join(tempfile.gettempdir(), 'stub_update_log.txt')
result = subprocess.run(
[args.V8Path, 'DESIGNER', f'/F{temp_base}', '/UpdateDBCfg', '/Out', update_log, '/DisableStartupDialogs'],
capture_output=True, text=True,
)
if result.returncode != 0:
if os.path.isfile(update_log):
try:
with open(update_log, 'r', encoding='utf-8-sig') as f:
print(f.read())
except Exception:
pass
print(f'Failed to update DB config (code: {result.returncode})', file=sys.stderr)
sys.exit(1)
# Cleanup cfg dir
import shutil
shutil.rmtree(cfg_dir, ignore_errors=True)
print(f'[OK] Stub database created: {temp_base}')
print(temp_base)
if __name__ == '__main__':
main()