# Modellbaum-Vergleich (5.0 ↔ 8.0) Werkzeug für die [Testdaten-Pipeline](TESTDATEN.md): Bevor Testdaten von Hand ergänzt werden, erstmal statisch (kein laufendes Tryton nötig) Schwarz auf Weiß sehen, welche Modelle/Felder es in einer Version überhaupt gibt - damit weder Felder aus 8.0 übernommen werden, die es in 5.0 noch gar nicht gibt (würde beim Anlegen nur Fehler werfen), noch Aufwand in Modelle gesteckt wird, die sich zwischen den Versionen gar nicht verändert haben. ## Workflow ### 1. `extract_model_tree.py` – Modellbaum aus dem Quellcode ziehen Scannt den Python-Code ausgewählter Module per AST (kein Import, keine laufende Instanz) und baut pro Modell (`__name__`) die Liste aller per `fields.X(...)` definierten Felder, mit Typ, Relationsziel (Many2One/One2Many/Many2Many/One2One/Reference) und - falls literal im Code erkennbar - `required`. Modelle werden über PoolMeta-Erweiterungen hinweg zusammengeführt: Fügt z. B. `stock_split` eine Klasse mit `__name__ = 'stock.move'` hinzu, landen deren Felder unter demselben Modell-Eintrag, mit Vermerk, aus welchem Modul sie stammen. Pro Modell wird zusätzlich `persisted` erkannt (erbt die Klasse `ModelSQL` o. ä., oder nur `ModelView` - dann ist es ein reines Wizard-/ Transient-Formular ohne eigene Tabelle). ```bash python3 extract_model_tree.py --repo /mnt/tryton \ --modules company country currency party product ... \ --output reports/model_tree_5.0.json --report reports/model_tree_5.0.md ``` Für eine zweite Version denselben Befehl gegen einen zweiten Checkout laufen lassen (z. B. per `git worktree add /tmp/tryton-8.0 8.0`, ohne den Haupt-Checkout umzuschalten). ### 2. `model_diff.py` – zwei Modellbäume vergleichen Sortiert jedes Modell in eine von fünf Kategorien: - **entfernt** – Modell gibt es in Version A, nicht mehr in B - **geändert** – Feld(er) entfernt oder Typ/Relation/required geändert → echter Kandidat für einen Vergleichslauf - **neu** – Modell gibt es erst in B - **nur ergänzt** – nur neue Felder dazugekommen, Rest unverändert - **unverändert** – komplett identisch geblieben ```bash python3 model_diff.py --tree-a reports/model_tree_5.0.json --label-a 5.0 \ --tree-b reports/model_tree_8.0.json --label-b 8.0 \ --output reports/model_diff.json --report reports/model_diff.md ``` ### 3. `model_coverage.py` – gegen die aktuelle Testdaten-Abdeckung abgleichen Vergleicht einen Modellbaum mit einer gemergten `dataset.yaml` (`reports/_testdata_merged.yaml`, siehe [TESTDATEN.md](TESTDATEN.md)) und zeigt komplett unabgedeckte Modelle sowie pro teilweise abgedecktem Modell die ungenutzten Felder (required zuerst). Drei Kategorien werden aus der Liste unabgedeckter Modelle per Default herausgefiltert (nie von Hand testdatenrelevant, jeweils per Flag wieder einschaltbar): - `ir.*`/`res.*`-Kernmodelle (`--include-framework`) - Wizard-/Transient-Formulare, `persisted: false` (`--include-transient`) - automatisch verwaltete Many2Many-Verknüpfungstabellen, erkennbar am Bindestrich im Namen wie `party.party-party.category` (`--include-relations`) Mit `--diff reports/model_diff.json` wird zusätzlich pro Modell die Diff-Kategorie aus Schritt 2 eingeblendet und die Liste danach sortiert: `geändert`/`entfernt` zuerst (lohnt sich, dort Testdaten zu ergänzen), `nur ergänzt`/`unverändert` zuletzt (dort ist über den reinen Codevergleich hinaus erstmal nichts Auffälliges zu erwarten). ```bash python3 model_coverage.py --model-tree reports/model_tree_5.0.json \ --dataset reports/5_testdata_merged.yaml --diff reports/model_diff.json \ --report reports/coverage_5.0.md ``` ## Bekannte Grenzen - Rein statische Analyse: Felder, die nicht als einfaches `name = fields.X(...)` auf Klassenebene stehen (z. B. per Schleife/Helper dynamisch erzeugt), werden nicht erkannt. - `persisted`-Erkennung ist eine Textsuche über die Basisklassen-Ausdrücke (`ModelSQL`/`Workflow`/`DeactivableMixin`) - deckt den Normalfall ab, ist aber kein echtes Type-Checking. - Erkennt nur Unterschiede auf Ebene der Feld-**Definition** im Code (Typ/Relation/required). Laufzeitverhalten wie Constraints, Defaults oder DB-Spaltenpräzision kann trotzdem abweichen, ohne hier aufzufallen - dafür bleibt der eigentliche Testlauf über `create_dataset.py`/ `compare_snapshots.py` nötig.