#!/usr/bin/env python3 # stub-db-create v1.3 — 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 IBCMD_NOUSER_HINT = ( "[ibcmd] No -UserName/-Password given; the infobase may require authentication. " "On Windows ibcmd reads credentials from the console (stdin is ignored), so this " "call may block instead of failing. If it does not return promptly, abort and " "re-run with -UserName and -Password.\n" ) def run_ibcmd(cmd, has_username=False, warn_no_user=True): """Run an ibcmd command non-interactively. input="" closes stdin (EOF) so ibcmd's auth prompt fast-fails instead of hanging. On Windows without -UserName ibcmd reads the console directly and may still block — that residual case is flagged via IBCMD_NOUSER_HINT (model-facing). """ if warn_no_user and os.name == "nt" and not has_username: sys.stderr.write(IBCMD_NOUSER_HINT) sys.stderr.flush() return subprocess.run(cmd, input="", capture_output=True, encoding="utf-8", errors="replace") 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.') # Stub via ibcmd (one call: create [--import --apply]) stub_engine = "ibcmd" if os.path.basename(args.V8Path).lower().startswith("ibcmd") else "1cv8" if stub_engine == "ibcmd": import shutil print(f'Creating infobase (ibcmd): {temp_base}') ib_data = tempfile.mkdtemp(prefix="stub_data_") ib_args = [args.V8Path, 'infobase', 'create', f'--db-path={temp_base}', '--create-database'] if has_ref_types: ib_args += [f'--import={os.path.join(temp_base, "cfg")}', '--apply', '--force'] ib_args.append(f'--data={ib_data}') result = run_ibcmd(ib_args, warn_no_user=False) shutil.rmtree(ib_data, ignore_errors=True) if result.returncode != 0: if result.stdout: print(result.stdout) if result.stderr: print(result.stderr, file=sys.stderr) print(f'Failed to create stub infobase (code: {result.returncode})', file=sys.stderr) sys.exit(1) if has_ref_types: import shutil shutil.rmtree(os.path.join(temp_base, 'cfg'), ignore_errors=True) print(f'[OK] Stub database created: {temp_base}') print(temp_base) sys.exit(0) # 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()