From 1694b14c5586969b7b19f9350c087f33e7a3683c Mon Sep 17 00:00:00 2001 From: leocrm-bot Date: Tue, 7 Jul 2026 07:16:11 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20Core=20Plugin=20&=20Dependency=20concep?= =?UTF-8?q?t=20=E2=80=94=20is=5Fcore=20flag,=20topological=20sort,=20repor?= =?UTF-8?q?t=5Fgenerator,=20ERP=20roadmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/core-plugin-concept.md | 336 ++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 docs/core-plugin-concept.md diff --git a/docs/core-plugin-concept.md b/docs/core-plugin-concept.md new file mode 100644 index 0000000..5382c49 --- /dev/null +++ b/docs/core-plugin-concept.md @@ -0,0 +1,336 @@ +# LeoCRM — Core Plugin & Dependency Konzept + +## 1. Ziel + +LeoCRM soll modular zu einem ERP ausgebaut werden. Die Plugin-Architektur ist bereits vorhanden (Manifest, BasePlugin, Registry), aber es fehlen: + +1. **Core Plugins** — unverzichtbare Basis-Plugins die immer aktiv sind +2. **Dependency Resolution** — Plugins können Abhängigkeiten deklarieren und diese werden durchgesetzt +3. **ERP-Module** — fachliche Erweiterungen die auf Core-Plugins aufbauen + +## 2. Plugin-Kategorien + +### 2.1 Core Plugins (`is_core: true`) + +- **Immer aktiv** — können nicht deaktiviert oder deinstalliert werden +- **Werden als erste geladen** — vor allen Nicht-Core-Plugins +- **Basis-Funktionalität** die andere Plugins voraussetzen +- **Beispiele:** + - `permissions` — RBAC-System + - `entity_links` — Querverweise zwischen Entitäten + - `tags` — Tagging-System + - `report_generator` — Report- & Dokumentgenerator + - `audit` — Audit-Log (bereits im Core, nicht als Plugin) + +### 2.2 Builtin Plugins + +- **Mitgeliefert aber optional** — können deaktiviert werden +- **Dürfen Core-Dependencies deklarieren** +- **Beispiele:** `dms`, `mail`, `calendar` + +### 2.3 Custom Plugins + +- **Nutzer-/Drittanbieter-Plugins** — zur Laufzeit installierbar +- **Müssen Dependencies explizit deklarieren** +- **Beispiele:** ERP-Module (Invoicing, Inventory, HR, etc.) + +## 3. Manifest-Erweiterung + +```python +class PluginManifest(BaseModel): + # ... bestehende Felder ... + + # NEU: Core-Plugin Flag + is_core: bool = Field( + default=False, + description="Core plugins cannot be deactivated and load first" + ) + + # NEU: Mindestversion für Dependencies + dependencies: list[str] = Field( + default_factory=list, + description="Plugin names this plugin requires (must be installed and active)" + ) + + # ERWEITERT: Semantic dependency with version + # dependency: str = "permissions>=1.0.0" + # Format: plugin_name[>=|>|<=|<|==version] +``` + +## 4. Dependency Resolution + +### 4.1 Topological Sort + +Die Registry muss Plugins in Abhängigkeits-Reihenfolge laden: + +``` +1. permissions (is_core, keine deps) +2. entity_links (is_core, deps: [permissions]) +3. tags (is_core, deps: [permissions]) +4. report_generator (is_core, deps: [permissions, entity_links]) +5. dms (deps: [permissions]) +6. mail (deps: [permissions]) +7. calendar (deps: [permissions]) +8. invoicing (deps: [permissions, report_generator]) ← ERP-Modul +``` + +### 4.2 Algorithmus + +```python +def resolve_load_order(plugins: dict[str, BasePlugin]) -> list[str]: + """Topological sort: Core first, then by dependency order.""" + # 1. Kahn's Algorithm oder DFS-based topo sort + # 2. Core-Plugins bekommen Priorität bei gleichrangigen Abhängigkeiten + # 3. Zyklus-Erkennung: RuntimeError bei zirkulären Dependencies + # 4. Fehlende Dependency: RuntimeError mit klarer Meldung +``` + +### 4.3 Validierung beim Installieren + +```python +async def validate_dependencies(plugin: BasePlugin, db: AsyncSession) -> None: + """Prüft vor Installation ob alle Dependencies erfüllt sind.""" + for dep_name in plugin.manifest.dependencies: + dep = await get_plugin_record(db, dep_name) + if dep is None: + raise PluginDependencyError( + f"Plugin '{plugin.name}' requires '{dep_name}' which is not installed" + ) + if not dep.active: + raise PluginDependencyError( + f"Plugin '{plugin.name}' requires '{dep_name}' to be active" + ) +``` + +### 4.4 Deaktivierungs-Schutz + +```python +async def deactivate_plugin(name: str, db: AsyncSession) -> None: + """Verhindert Deaktivierung wenn andere Plugins abhängen.""" + # 1. Prüfe ob Plugin is_core → Fehler + # 2. Prüfe ob andere aktive Plugins dieses Plugin als Dependency haben → Fehler + dependents = await get_dependent_plugins(db, name) + if dependents: + raise PluginDependencyError( + f"Cannot deactivate '{name}': {dependents} still depend on it" + ) +``` + +## 5. Report Generator als Core Plugin + +### 5.1 Konzept + +Der Report Generator ist ein **Core Plugin** das Dokumente und Reports erzeugt. ERP-Module (Invoicing, Inventory, etc.) nutzen ihn als Dependency. + +### 5.2 Manifest + +```python +class ReportGeneratorPlugin(BasePlugin): + manifest = PluginManifest( + name="report_generator", + version="1.0.0", + display_name="Report Generator", + description="Generates PDF/Excel/CSV reports from templates and data sources", + is_core=True, + dependencies=["permissions", "entity_links"], + routes=[ + PluginRouteDef( + path="/api/v1/reports", + module="app.plugins.builtins.report_generator.routes", + router_attr="router", + ), + ], + events=["report.requested", "report.generated"], + migrations=["0001_initial.sql"], + permissions=["reports.read", "reports.generate", "reports.manage_templates"], + ) +``` + +### 5.3 Funktionalität + +- **Template Engine** — Jinja2-basierte Templates für PDF/Excel/CSV +- **Data Sources** — SQL-Queries oder Python-Funktionen als Datenquelle +- **Scheduling** — Cron-basierte Report-Generierung +- **Output** — PDF (WeasyPrint), Excel (openpyxl), CSV, JSON +- **Storage** — Reports werden im DMS gespeichert (wenn DMS aktiv) +- **Distribution** — E-Mail-Versand, Download, API + +### 5.4 API Endpoints + +``` +GET /api/v1/reports/templates — Liste aller Templates +POST /api/v1/reports/templates — Template erstellen +GET /api/v1/reports/templates/{id} — Template Details +PUT /api/v1/reports/templates/{id} — Template aktualisieren +DELETE /api/v1/reports/templates/{id} — Template löschen + +POST /api/v1/reports/generate — Report generieren (async) +GET /api/v1/reports/{id} — Report Status/Download +GET /api/v1/reports/{id}/download — Report herunterladen + +GET /api/v1/reports/scheduled — Geplante Reports +POST /api/v1/reports/scheduled — Report planen +DELETE /api/v1/reports/scheduled/{id} — Geplanten Report löschen +``` + +### 5.5 ERP-Nutzung + +Ein ERP-Modul "Invoicing" würde den Report Generator nutzen: + +```python +class InvoicingPlugin(BasePlugin): + manifest = PluginManifest( + name="invoicing", + version="1.0.0", + display_name="Invoicing", + description="Invoice management with PDF generation", + dependencies=["permissions", "report_generator", "currencies", "taxes"], + routes=[ + PluginRouteDef( + path="/api/v1/invoicing", + module="app.plugins.builtins.invoicing.routes", + router_attr="router", + ), + ], + events=["invoice.created", "invoice.sent", "invoice.paid"], + ) + + async def on_invoice_created(self, payload: dict) -> None: + """When an invoice is created, generate PDF via report_generator.""" + # Ruft report_generator API auf: POST /api/v1/reports/generate + # Template: "invoice_template" + # Data: payload (invoice data) + # Output: PDF +``` + +## 6. ERP-Aufbau-Strategie + +### 6.1 Phasen + +| Phase | Plugins | Funktionalität | +|---|---|---| +| **Phase 1** | `report_generator` (Core) | Report- & Dokumentgenerator | +| **Phase 2** | `invoicing` | Rechnungen mit PDF-Generierung | +| **Phase 3** | `inventory` | Lagerverwaltung, Bestandsführung | +| **Phase 4** | `purchase_orders` | Bestellungen, Lieferanten | +| **Phase 5** | `hr` | Mitarbeiter, Gehalt, Urlaub | +| **Phase 6** | `accounting` | Buchhaltung, Buchungen, Bilanz | + +### 6.2 Abhängigkeits-Graph + +``` +permissions (Core) +├── entity_links (Core) +├── tags (Core) +├── report_generator (Core) +│ ├── invoicing +│ │ └── accounting +│ ├── purchase_orders +│ │ └── accounting +│ └── hr +├── currencies (Core, bereits als Basis-Feature) +├── taxes (Core, bereits als Basis-Feature) +├── dms (Builtin) +├── mail (Builtin) +└── calendar (Builtin) +``` + +### 6.3 Implementierungs-Prinzipien + +1. **Jedes ERP-Modul ist ein Plugin** — kein festcodiertes ERP +2. **Core-Plugins sind stabil** — brechen nie andere Plugins +3. **Versionierte Dependencies** — `dependencies=["report_generator>=1.0.0"]` +4. **Event-Driven** — Plugins kommunizieren über Events, nicht direkte Aufrufe +5. **Tenant-Isolated** — Jedes Plugin respektiert Tenant-Grenzen +6. **Frontend-Modular** — Plugin-Frontends werden dynamisch geladen + +## 7. Technische Umsetzung + +### 7.1 Registry-Erweiterung + +```python +# app/plugins/registry.py — neue Methoden + +class PluginRegistry: + # ... bestehend ... + + def resolve_load_order(self) -> list[str]: + """Topological sort of all discovered plugins.""" + # 1. Build dependency graph + # 2. Core-Plugins first + # 3. Topological sort (Kahn's algorithm) + # 4. Cycle detection + # 5. Missing dependency detection + + async def validate_dependencies( + self, plugin_name: str, db: AsyncSession + ) -> list[str]: + """Check if all dependencies are installed and active.""" + # Returns list of missing/unmet dependencies + + async def get_dependents( + self, plugin_name: str, db: AsyncSession + ) -> list[str]: + """Find all plugins that depend on this one.""" + # For deactivation protection +``` + +### 7.2 Plugin Model Erweiterung + +```python +# app/models/plugin.py — neues Feld +class Plugin(BaseModel): + # ... bestehende Felder ... + is_core: Mapped[bool] = mapped_column(Boolean, default=False) +``` + +### 7.3 Startup-Sequenz (main.py) + +```python +# Aktuell: Alle Builtins werden automatisch installiert+aktiviert +# Neu: +# 1. Discover all plugins +# 2. resolve_load_order() → [permissions, entity_links, tags, report_generator, dms, mail, calendar] +# 3. For each in order: +# a. Check if in DB → if not, create record (is_core=True for core plugins) +# b. Run migrations +# c. Activate (skip for non-core if deactivated by admin) +# d. Register routes +``` + +## 8. Frontend-Anpassung + +### 8.1 Sidebar dynamisch + +Die Sidebar sollte Plugins nicht mehr hardcoded listen, sondern dynamisch aus der Plugin-API laden: + +```typescript +// GET /api/v1/plugins/active → [{name, display_name, icon, route_prefix, is_core}] +// Sidebar rendert nur aktive Plugins +``` + +### 8.2 Plugin-Frontend-Laden + +Jedes Plugin kann ein Frontend-Modul mitbringen: + +``` +app/plugins/builtins/invoicing/ +├── __init__.py +├── plugin.py # Backend Plugin +├── routes.py # API Routes +├── manifest.py # (in plugin.py) +├── migrations/ # SQL Migrations +└── frontend/ # Frontend Module + ├── index.tsx # Plugin Entry Point + ├── pages/ # Plugin Pages + └── components/ # Plugin Components +``` + +## 9. Nächste Schritte + +1. **Manifest erweitern** — `is_core` Feld hinzufügen +2. **Registry erweitern** — `resolve_load_order()`, `validate_dependencies()`, `get_dependents()` +3. **Plugin Model erweitern** — `is_core` Spalte +4. **Startup-Sequenz anpassen** — Core-First, Topological Sort +5. **Report Generator Plugin bauen** — Templates, PDF/Excel, Scheduling +6. **ERP-Module starten** — Invoicing als erstes Modul