#!/usr/bin/env python3 # cfe-patch-method v1.1 — Generate method interceptor for 1C extension (CFE) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import os import sys import xml.etree.ElementTree as ET def main(): sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser( description="Generate method interceptor for 1C extension (CFE)", allow_abbrev=False, ) parser.add_argument("-ExtensionPath", required=True) parser.add_argument("-ModulePath", required=True) parser.add_argument("-MethodName", required=True) parser.add_argument( "-InterceptorType", required=True, choices=["Before", "After", "ModificationAndControl"], ) parser.add_argument("-Context", default="\u041d\u0430\u0421\u0435\u0440\u0432\u0435\u0440\u0435") # НаСервере parser.add_argument("-IsFunction", action="store_true") args = parser.parse_args() extension_path = args.ExtensionPath module_path = args.ModulePath method_name = args.MethodName interceptor_type = args.InterceptorType context = args.Context is_function = args.IsFunction # --- Resolve extension path --- if not os.path.isabs(extension_path): extension_path = os.path.join(os.getcwd(), extension_path) if os.path.isfile(extension_path): extension_path = os.path.dirname(extension_path) cfg_file = os.path.join(extension_path, "Configuration.xml") if not os.path.isfile(cfg_file): print(f"Configuration.xml not found in: {extension_path}", file=sys.stderr) sys.exit(1) # --- Read NamePrefix from Configuration.xml --- tree = ET.parse(cfg_file) root = tree.getroot() ns = {"md": "http://v8.1c.ru/8.3/MDClasses"} props_node = root.find(".//md:Configuration/md:Properties", ns) name_prefix = "\u0420\u0430\u0441\u0448_" # Расш_ if props_node is not None: prefix_node = props_node.find("md:NamePrefix", ns) if prefix_node is not None and prefix_node.text: name_prefix = prefix_node.text # --- Map ModulePath to file path --- # ModulePath formats: # Catalog.X.ObjectModule -> Catalogs/X/Ext/ObjectModule.bsl # Catalog.X.ManagerModule -> Catalogs/X/Ext/ManagerModule.bsl # Catalog.X.Form.Y -> Catalogs/X/Forms/Y/Ext/Form/Module.bsl # CommonModule.X -> CommonModules/X/Ext/Module.bsl # Document.X.ObjectModule -> Documents/X/Ext/ObjectModule.bsl # Document.X.ManagerModule -> Documents/X/Ext/ManagerModule.bsl # Document.X.Form.Y -> Documents/X/Forms/Y/Ext/Form/Module.bsl type_dir_map = { "Catalog": "Catalogs", "Document": "Documents", "Enum": "Enums", "CommonModule": "CommonModules", "Report": "Reports", "DataProcessor": "DataProcessors", "ExchangePlan": "ExchangePlans", "ChartOfAccounts": "ChartsOfAccounts", "ChartOfCharacteristicTypes": "ChartsOfCharacteristicTypes", "ChartOfCalculationTypes": "ChartsOfCalculationTypes", "BusinessProcess": "BusinessProcesses", "Task": "Tasks", "InformationRegister": "InformationRegisters", "AccumulationRegister": "AccumulationRegisters", "AccountingRegister": "AccountingRegisters", "CalculationRegister": "CalculationRegisters", "Catalogs": "Catalogs", "Documents": "Documents", "Enums": "Enums", "CommonModules": "CommonModules", "Reports": "Reports", "DataProcessors": "DataProcessors", "ExchangePlans": "ExchangePlans", "ChartsOfAccounts": "ChartsOfAccounts", "ChartsOfCharacteristicTypes": "ChartsOfCharacteristicTypes", "ChartsOfCalculationTypes": "ChartsOfCalculationTypes", "BusinessProcesses": "BusinessProcesses", "Tasks": "Tasks", "InformationRegisters": "InformationRegisters", "AccumulationRegisters": "AccumulationRegisters", "AccountingRegisters": "AccountingRegisters", "CalculationRegisters": "CalculationRegisters", } parts = module_path.split(".") if len(parts) < 2: print( f"Invalid ModulePath format: {module_path}. " "Expected: Type.Name.Module or CommonModule.Name", file=sys.stderr, ) sys.exit(1) obj_type = parts[0] obj_name = parts[1] if obj_type not in type_dir_map: print(f"Unknown object type: {obj_type}", file=sys.stderr) sys.exit(1) dir_name = type_dir_map[obj_type] bsl_file = None if obj_type == "CommonModule": # CommonModule.X -> CommonModules/X/Ext/Module.bsl bsl_file = os.path.join(extension_path, dir_name, obj_name, "Ext", "Module.bsl") elif len(parts) >= 4 and parts[2] == "Form": # Type.X.Form.Y -> Types/X/Forms/Y/Ext/Form/Module.bsl form_name = parts[3] bsl_file = os.path.join( extension_path, dir_name, obj_name, "Forms", form_name, "Ext", "Form", "Module.bsl" ) elif len(parts) >= 3: # Type.X.ObjectModule -> Types/X/Ext/ObjectModule.bsl module_name = parts[2] module_file_map = { "ObjectModule": "ObjectModule.bsl", "ManagerModule": "ManagerModule.bsl", "RecordSetModule": "RecordSetModule.bsl", "CommandModule": "CommandModule.bsl", } module_file_name = module_file_map.get(module_name, f"{module_name}.bsl") bsl_file = os.path.join(extension_path, dir_name, obj_name, "Ext", module_file_name) else: print( f"Invalid ModulePath format: {module_path}. " "Expected: Type.Name.Module, Type.Name.Form.FormName, or CommonModule.Name", file=sys.stderr, ) sys.exit(1) # --- Map InterceptorType to decorator --- decorator_map = { "Before": "&\u041f\u0435\u0440\u0435\u0434", # &Перед "After": "&\u041f\u043e\u0441\u043b\u0435", # &После "ModificationAndControl": "&\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u0418\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u044c", # &ИзменениеИКонтроль } decorator = decorator_map[interceptor_type] # --- Map Context to annotation --- context_map = { "\u041d\u0430\u0421\u0435\u0440\u0432\u0435\u0440\u0435": "&\u041d\u0430\u0421\u0435\u0440\u0432\u0435\u0440\u0435", # НаСервере -> &НаСервере "\u041d\u0430\u041a\u043b\u0438\u0435\u043d\u0442\u0435": "&\u041d\u0430\u041a\u043b\u0438\u0435\u043d\u0442\u0435", # НаКлиенте -> &НаКлиенте "\u041d\u0430\u0421\u0435\u0440\u0432\u0435\u0440\u0435\u0411\u0435\u0437\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430": "&\u041d\u0430\u0421\u0435\u0440\u0432\u0435\u0440\u0435\u0411\u0435\u0437\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430", # НаСервереБезКонтекста -> &НаСервереБезКонтекста } context_annotation = context_map.get(context, f"&{context}") # --- Procedure name --- proc_name = f"{name_prefix}{method_name}" # --- Generate BSL code --- keyword = "\u0424\u0443\u043d\u043a\u0446\u0438\u044f" if is_function else "\u041f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430" # Функция / Процедура end_keyword = "\u041a\u043e\u043d\u0435\u0446\u0424\u0443\u043d\u043a\u0446\u0438\u0438" if is_function else "\u041a\u043e\u043d\u0435\u0446\u041f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u044b" # КонецФункции / КонецПроцедуры body_lines = [] if interceptor_type == "Before": body_lines.append("\t// TODO: \u043a\u043e\u0434 \u043f\u0435\u0440\u0435\u0434 \u0432\u044b\u0437\u043e\u0432\u043e\u043c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430") # код перед вызовом оригинального метода elif interceptor_type == "After": body_lines.append("\t// TODO: \u043a\u043e\u0434 \u043f\u043e\u0441\u043b\u0435 \u0432\u044b\u0437\u043e\u0432\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430") # код после вызова оригинального метода elif interceptor_type == "ModificationAndControl": body_lines.append("\t// \u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0442\u0435\u043b\u043e \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0438 \u0432\u043d\u0435\u0441\u0438\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f,") # Скопируйте тело оригинального метода и внесите изменения, body_lines.append("\t// \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043c\u0430\u0440\u043a\u0435\u0440\u044b #\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 / #\u041a\u043e\u043d\u0435\u0446\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438 #\u0412\u0441\u0442\u0430\u0432\u043a\u0430 / #\u041a\u043e\u043d\u0435\u0446\u0412\u0441\u0442\u0430\u0432\u043a\u0438") # используя маркеры #Удаление / #КонецУдаления и #Вставка / #КонецВставки if is_function: body_lines.append("\t") body_lines.append("\t\u0412\u043e\u0437\u0432\u0440\u0430\u0442 \u041d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e; // TODO: \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435") # Возврат Неопределено; // TODO: заменить на реальное возвращаемое значение bsl_code = [ context_annotation, f'{decorator}("{method_name}")', f"{keyword} {proc_name}()", ] bsl_code.extend(body_lines) bsl_code.append(end_keyword) bsl_text = "\r\n".join(bsl_code) + "\r\n" # --- Check form borrowing for .Form. paths --- if len(parts) >= 4 and parts[2] == "Form": form_name = parts[3] form_meta_file = os.path.join( extension_path, dir_name, obj_name, "Forms", f"{form_name}.xml" ) form_xml_file = os.path.join( extension_path, dir_name, obj_name, "Forms", form_name, "Ext", "Form.xml" ) if not os.path.isfile(form_meta_file) or not os.path.isfile(form_xml_file): print(f"[WARN] Form '{form_name}' metadata or Form.xml not found in extension.") print(" Run /cfe-borrow first:") print( f" /cfe-borrow -ExtensionPath {extension_path} " f'-ConfigPath -Object "{obj_type}.{obj_name}.Form.{form_name}"' ) print() # --- Check if file exists and append --- bsl_dir = os.path.dirname(bsl_file) if not os.path.isdir(bsl_dir): os.makedirs(bsl_dir, exist_ok=True) if os.path.isfile(bsl_file): # Append to existing file with open(bsl_file, "r", encoding="utf-8-sig", newline="") as f: existing = f.read() separator = "\r\n" if existing and not existing.endswith("\n"): separator = "\r\n\r\n" new_content = existing + separator + bsl_text with open(bsl_file, "w", encoding="utf-8-sig", newline="") as f: f.write(new_content) print("[OK] \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0447\u0438\u043a \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u0430\u0439\u043b") # Добавлен перехватчик в существующий файл else: with open(bsl_file, "w", encoding="utf-8-sig", newline="") as f: f.write(bsl_text) print("[OK] \u0421\u043e\u0437\u0434\u0430\u043d \u0444\u0430\u0439\u043b \u043c\u043e\u0434\u0443\u043b\u044f") # Создан файл модуля print(f" \u0424\u0430\u0439\u043b: {bsl_file}") # Файл: print(f' \u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440: {decorator}("{method_name}")') # Декоратор: print(f" \u041f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430: {proc_name}()") # Процедура: print(f" \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442: {context_annotation}") # Контекст: if __name__ == "__main__": main()