#!/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}' 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 """ 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()