#!/usr/bin/env python3 # erf-init v1.4 — Init 1C external report scaffold # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """Generates minimal XML source files for a 1C external report.""" import sys, os, re, argparse, uuid 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) def write_xml_file(path, content): """XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце. Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл. """ text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n') write_utf8_bom(path, text) def format_rank(ver): """"2.20" → 220, "2.9" → 209. Строковое сравнение неверно ("2.9" > "2.17").""" m = re.match(r'^(\d+)\.(\d+)$', ver or '') return int(m.group(1)) * 100 + int(m.group(2)) if m else 0 def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser(description='Init 1C external report scaffold', allow_abbrev=False) parser.add_argument('-Name', dest='Name', required=True) parser.add_argument('-Synonym', dest='Synonym', default=None) parser.add_argument('-SrcDir', dest='SrcDir', default='src') # Версия формата выгрузки. Своей конфигурации у автономного объекта нет, наследовать # версию неоткуда — поэтому её задают явно. Формы, макеты и справку внутри объекта # навыки берут уже отсюда: их детектор читает version из корня этого файла. parser.add_argument('-FormatVersion', dest='FormatVersion', default='2.17', choices=['2.17', '2.18', '2.19', '2.20', '2.21']) parser.add_argument('-WithSKD', dest='WithSKD', action='store_true') args = parser.parse_args() name = args.Name synonym = args.Synonym if args.Synonym else name src_dir = args.SrcDir uuid1 = new_uuid() uuid2 = new_uuid() uuid3 = new_uuid() uuid4 = new_uuid() # Объявления пространств имён — одной переменной: места эмиссии её только подставляют. 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"' ) format_version = args.FormatVersion # 2.21 (8.5) добавила в шапку пространство палитры. Вставляем НА МЕСТО (после lf, перед # style): платформа держит объявления по алфавиту, дописать в конец нельзя. if format_rank(format_version) >= 221: xmlns_decl = xmlns_decl.replace( ' xmlns:style=', ' xmlns:pal="http://v8.1c.ru/8.1/data/ui/colors/palette" xmlns:style=') # --- Properties --- main_dcs_value = "" child_objects_content = "" if args.WithSKD: main_dcs_value = f"ExternalReport.{name}.Template.ОсновнаяСхемаКомпоновкиДанных" child_objects_content = f"\n\t\t\t\n" main_dcs_element = f"{main_dcs_value}" if main_dcs_value else "" child_objects_xml = f"{child_objects_content}\t\t" if child_objects_content else "" xml = f''' \t \t\t \t\t\t \t\t\t\te41aff26-25cf-4bb6-b6c1-3f478a75f374 \t\t\t\t{uuid2} \t\t\t \t\t\t \t\t\t\t{uuid3} \t\t\t\t{uuid4} \t\t\t \t\t \t\t \t\t\t{esc_xml(name)} \t\t\t \t\t\t\t \t\t\t\t\tru \t\t\t\t\t{esc_xml(synonym)} \t\t\t\t \t\t\t \t\t\t \t\t\t \t\t\t \t\t\t{main_dcs_element} \t\t\t \t\t\t \t\t\t \t\t\t \t\t\t \t\t \t\t{child_objects_xml} \t ''' root_file = os.path.join(src_dir, f"{name}.xml") report_dir = os.path.join(src_dir, name) if os.path.exists(root_file): print(f"Файл уже существует: {root_file}", file=sys.stderr) sys.exit(1) os.makedirs(src_dir, exist_ok=True) ext_dir = os.path.join(report_dir, "Ext") os.makedirs(ext_dir, exist_ok=True) write_xml_file(os.path.join(os.path.abspath(src_dir), f"{name}.xml"), xml) # --- Модуль объекта --- module_bsl = """\ #Область ОписаниеПеременных #КонецОбласти #Область ПрограммныйИнтерфейс #КонецОбласти #Область СлужебныеПроцедурыИФункции #КонецОбласти""" module_path = os.path.join(ext_dir, "ObjectModule.bsl") # Модуль пишем в каноне платформы: CRLF в разделителях строк (корпус: 2643 CRLF, # чисто-LF 0 из 3001). Хвостовой перевод НЕ навязываем — у платформы он # неканоничен (1235 модулей с ним, 766 без). write_utf8_bom(module_path, module_bsl.replace('\r\n', '\n').replace('\n', '\r\n')) print(f"[OK] Создан отчёт: {root_file}") print(f" Каталог: {report_dir}") print(f" Модуль: {module_path}") # --- СКД-макет --- if args.WithSKD: templates_dir = os.path.join(report_dir, "Templates") skd_name = "ОсновнаяСхемаКомпоновкиДанных" skd_meta_path = os.path.join(templates_dir, f"{skd_name}.xml") skd_ext_dir = os.path.join(templates_dir, skd_name, "Ext") os.makedirs(skd_ext_dir, exist_ok=True) skd_uuid = new_uuid() skd_meta_xml = f''' \t ''' write_xml_file(skd_meta_path, skd_meta_xml) skd_content = ''' \t \t\tИсточникДанных1 \t\tLocal \t ''' skd_file_path = os.path.join(skd_ext_dir, "Template.xml") write_xml_file(skd_file_path, skd_content) print(f" СКД: {skd_meta_path}") print(f" Тело: {skd_file_path}") if __name__ == '__main__': main()