From 6a504254b06ec942385788f4d97d7da17bacf835 Mon Sep 17 00:00:00 2001 From: hafroese Date: Thu, 2 Apr 2026 23:09:27 +0200 Subject: [PATCH] feat: content import system - Markdown parser, importer, 7 decks + 3 tutorials + 5 cheatsheets --- edu/api/import.php | 145 ++++++ .../cheatsheets/commands-cheatsheet.md | 81 ++++ .../cheatsheets/notation-cheatsheet.md | 88 ++++ .../cheatsheets/solution2-cheatsheet.md | 112 +++++ edu/content/cheatsheets/sql-cheatsheet.md | 104 ++++ edu/content/cheatsheets/windows-cheatsheet.md | 143 ++++++ edu/content/flashcards/deck-commands.md | 163 +++++++ edu/content/flashcards/deck-gotchas.md | 102 ++++ edu/content/flashcards/deck-lists.md | 166 +++++++ edu/content/flashcards/deck-notation.md | 193 ++++++++ edu/content/flashcards/deck-solution2.md | 132 +++++ edu/content/flashcards/deck-sql.md | 136 ++++++ edu/content/flashcards/deck-windows.md | 170 +++++++ edu/content/tutorials/01-omnis-kickstart.md | 289 +++++++++++ .../tutorials/08-window-hierarchy-deepdive.md | 457 ++++++++++++++++++ edu/content/tutorials/onboarding-path.md | 121 +++++ edu/js/markdown.js | 56 +++ 17 files changed, 2658 insertions(+) create mode 100644 edu/api/import.php create mode 100644 edu/content/cheatsheets/commands-cheatsheet.md create mode 100644 edu/content/cheatsheets/notation-cheatsheet.md create mode 100644 edu/content/cheatsheets/solution2-cheatsheet.md create mode 100644 edu/content/cheatsheets/sql-cheatsheet.md create mode 100644 edu/content/cheatsheets/windows-cheatsheet.md create mode 100644 edu/content/flashcards/deck-commands.md create mode 100644 edu/content/flashcards/deck-gotchas.md create mode 100644 edu/content/flashcards/deck-lists.md create mode 100644 edu/content/flashcards/deck-notation.md create mode 100644 edu/content/flashcards/deck-solution2.md create mode 100644 edu/content/flashcards/deck-sql.md create mode 100644 edu/content/flashcards/deck-windows.md create mode 100644 edu/content/tutorials/01-omnis-kickstart.md create mode 100644 edu/content/tutorials/08-window-hierarchy-deepdive.md create mode 100644 edu/content/tutorials/onboarding-path.md create mode 100644 edu/js/markdown.js diff --git a/edu/api/import.php b/edu/api/import.php new file mode 100644 index 0000000..58c321a --- /dev/null +++ b/edu/api/import.php @@ -0,0 +1,145 @@ + 0, 'cards' => 0, 'tutorials' => 0, 'cheatsheets' => 0]; + + $flashcard_dir = CONTENT_DIR . '/flashcards'; + if (is_dir($flashcard_dir)) { + $sort = 0; + foreach (glob($flashcard_dir . '/deck-*.md') as $file) { + $count = import_flashcard_deck($pdo, $file, $sort++); + $stats['decks']++; + $stats['cards'] += $count; + } + } + + $tutorial_dir = CONTENT_DIR . '/tutorials'; + if (is_dir($tutorial_dir)) { + $sort = 0; + foreach (glob($tutorial_dir . '/*.md') as $file) { + $basename = basename($file); + if ($basename === 'onboarding-path.md' || $basename === 'progress.md') continue; + import_tutorial($pdo, $file, $sort++); + $stats['tutorials']++; + } + } + + $cheatsheet_dir = CONTENT_DIR . '/cheatsheets'; + if (is_dir($cheatsheet_dir)) { + foreach (glob($cheatsheet_dir . '/*-cheatsheet.md') as $file) { + import_cheatsheet($pdo, $file); + $stats['cheatsheets']++; + } + } + + return $stats; +} + +function import_flashcard_deck($pdo, $filepath, $deck_sort) { + $content = file_get_contents($filepath); + $basename = basename($filepath); + + preg_match('/^# Flashcard Deck: (.+)$/m', $content, $m); + $deck_name = $m[1] ?? str_replace(['deck-', '.md'], '', $basename); + $slug = slugify($deck_name); + + $stmt = $pdo->prepare(" + INSERT INTO decks (slug, name, sort_order, source_file, updated_at) + VALUES (:slug, :name, :sort, :src, NOW()) + ON CONFLICT (slug) DO UPDATE SET name = :name, sort_order = :sort, source_file = :src, updated_at = NOW() + RETURNING id + "); + $stmt->execute([':slug' => $slug, ':name' => $deck_name, ':sort' => $deck_sort, ':src' => $basename]); + $deck_id = $stmt->fetchColumn(); + + // Parse cards + preg_match_all( + '/## Karte (\d+) \| (\w+)\s*\n\*\*Q:\*\*\s*(.+?)(?:\n)\*\*A:\*\*\s*(.+?)(?=\n---|\n## Karte|\n## Spaced|\Z)/s', + $content, $matches, PREG_SET_ORDER + ); + + // Delete old cards for this deck, then re-insert + $pdo->prepare("DELETE FROM cards WHERE deck_id = :did")->execute([':did' => $deck_id]); + + $card_count = 0; + foreach ($matches as $i => $match) { + $level = strtolower(trim($match[2])); + $question = trim($match[3]); + $answer = trim($match[4]); + + $stmt = $pdo->prepare(" + INSERT INTO cards (deck_id, question, answer, level, sort_order, source_file) + VALUES (:did, :q, :a, :lvl, :sort, :src) + "); + $stmt->execute([ + ':did' => $deck_id, ':q' => $question, ':a' => $answer, + ':lvl' => $level, ':sort' => $i, ':src' => $basename + ]); + $card_count++; + } + + $pdo->prepare("UPDATE decks SET card_count = :c WHERE id = :id") + ->execute([':c' => $card_count, ':id' => $deck_id]); + + return $card_count; +} + +function import_tutorial($pdo, $filepath, $sort_order) { + $content = file_get_contents($filepath); + $basename = basename($filepath); + + preg_match('/^# (.+)$/m', $content, $m); + $title = $m[1] ?? str_replace('.md', '', $basename); + $slug = slugify($basename); + + preg_match('/\*\*Dauer\*\*:\s*ca\.\s*(\d+)/i', $content, $dm); + $duration = $dm[1] ?? null; + + preg_match('/\*\*Ziel\*\*:\s*(.+)$/m', $content, $desc); + $description = $desc[1] ?? ''; + + $stmt = $pdo->prepare(" + INSERT INTO tutorials (slug, title, description, content_md, duration_min, sort_order, source_file, updated_at) + VALUES (:slug, :title, :desc, :content, :dur, :sort, :src, NOW()) + ON CONFLICT (slug) DO UPDATE SET + title = :title, description = :desc, content_md = :content, + duration_min = :dur, sort_order = :sort, source_file = :src, updated_at = NOW() + "); + $stmt->execute([ + ':slug' => $slug, ':title' => $title, ':desc' => $description, + ':content' => $content, ':dur' => $duration, + ':sort' => $sort_order, ':src' => $basename + ]); +} + +function import_cheatsheet($pdo, $filepath) { + $content = file_get_contents($filepath); + $basename = basename($filepath); + + preg_match('/^# (.+)$/m', $content, $m); + $title = $m[1] ?? str_replace(['-cheatsheet', '.md'], ['', ''], $basename); + $slug = slugify($basename); + $category = ucfirst(str_replace(['-cheatsheet.md', '-'], ['', ' '], $basename)); + + $stmt = $pdo->prepare(" + INSERT INTO cheatsheets (slug, title, category, content_md, source_file, updated_at) + VALUES (:slug, :title, :cat, :content, :src, NOW()) + ON CONFLICT (slug) DO UPDATE SET + title = :title, category = :cat, content_md = :content, source_file = :src, updated_at = NOW() + "); + $stmt->execute([ + ':slug' => $slug, ':title' => $title, ':cat' => $category, + ':content' => $content, ':src' => $basename + ]); +} + +function slugify($text) { + $text = str_replace(['.md', 'deck-'], '', $text); + $text = strtolower($text); + $text = preg_replace('/[^a-z0-9\-]/', '-', $text); + $text = preg_replace('/-+/', '-', $text); + return trim($text, '-'); +} diff --git a/edu/content/cheatsheets/commands-cheatsheet.md b/edu/content/cheatsheets/commands-cheatsheet.md new file mode 100644 index 0000000..ac835a0 --- /dev/null +++ b/edu/content/cheatsheets/commands-cheatsheet.md @@ -0,0 +1,81 @@ +# Omnis Commands Cheat Sheet + +## Die 10 wichtigsten Befehle + +| # | Befehl | Was es tut | JS-Aequivalent | +|---|--------|-----------|----------------| +| 1 | `Calculate lX as expr` | Berechnung + Zuweisung | `let x = expr` | +| 2 | `Do obj.$method()` | Methodenaufruf | `obj.method()` | +| 3 | `If ... Else ... End If` | Bedingung | `if ... else ...` | +| 4 | `For i from a to b step c` | Zaehlschleife | `for (let i=a; i<=b; i+=c)` | +| 5 | `For each line in list` | Listen-Iteration | `for (const item of arr)` | +| 6 | `While ... End While` | Bedingungsschleife | `while (...)` | +| 7 | `Quit method lVal` | Methode beenden + Return | `return val` | +| 8 | `OK message {text}` | Nachricht anzeigen | `alert(text)` | +| 9 | `Switch ... Case ... End Switch` | Fallunterscheidung | `switch ... case` | +| 10 | `Do inherited` | Super-Methode rufen | `super.method()` | + +## Variablen & Berechnung +```omnis +Calculate lName as "Harry" String zuweisen +Calculate lSum as lA + lB Berechnen +Calculate lText as con("Hallo ", lName) String verketten +Calculate lUpper as upp(lName) Grossbuchstaben +Calculate lLen as len(lName) String-Laenge +Calculate lSub as mid(lName, 2, 3) Substring +Calculate lPos as pos("r", lName) Zeichen finden +``` + +## Kontrollfluss +```omnis +If lAge >= 18 If lStatus = 'A' | lStatus = 'B' + # erwachsen # A oder B +Else If lAge >= 14 End If + # jugendlich +Else Switch lStatus + # kind Case 'A' +End If # aktiv + Case 'I' +While lX < 100 # inaktiv + Calculate lX as lX + 1 Default +End While # sonstiges + End Switch +For lI from 1 to 10 step 1 + # lI = 1, 2, 3, ...10 +End For +``` + +## Dialoge +```omnis +OK message {Fertig!} Info-Dialog +Yes/No message {Loeschen?} Ja/Nein Dialog + If flag true ... End If Ergebnis pruefen +Prompt for input {Name?} lName Eingabe-Dialog +No/Yes message {Abbrechen?} Nein/Ja (Default=Nein) +``` + +## Debugging +```omnis +Breakpoint Debugger oeffnen (= debugger; in JS) +OK message {Debug: [lVar]} Quick-Debug mit Message +Trace log {[lVar]} In Trace-Log schreiben +``` + +## SQL-Befehle (ueber Session/Statement) +```omnis +Do lSess.$logon(host, user, pw, 'sess') Returns #F Verbinden +Do lStmt.$prepare('SELECT...WHERE id=:1') Returns #F Vorbereiten +Do lStmt.$execute(lID) Returns #F Ausfuehren +Do lStmt.$fetch(lList, kFetchAll) Returns #F Ergebnis holen +Do lStmt.$execdirect('INSERT...') Returns #F Direkt ausfuehren +``` + +## Merkhilfe: Die Befehls-Familien +``` +DATEN: Calculate, Do, Set current list +FLUSS: If/Else/End If, For/End For, While/End While, Switch/Case/End Switch +METHODE: Quit method, Do inherited, Quit all methods +DIALOG: OK message, Yes/No message, Prompt for input +FENSTER: Open window, Close window, Redraw, Enter data +SQL: $logon, $prepare, $execute, $fetch, $execdirect +``` diff --git a/edu/content/cheatsheets/notation-cheatsheet.md b/edu/content/cheatsheets/notation-cheatsheet.md new file mode 100644 index 0000000..df96b3b --- /dev/null +++ b/edu/content/cheatsheets/notation-cheatsheet.md @@ -0,0 +1,88 @@ +# Omnis $-Notation Cheat Sheet + +## Navigation durch die Hierarchie +``` +$root Die Omnis-Wurzel + .$libs Alle geoeffneten Libraries + .myLib Bestimmte Library + .$classes Alle Klassen + .myClass Bestimmte Klasse + .$methods Alle Methoden + .$objs Alle UI-Objekte + .$ivars Alle Instanz-Variablen +``` + +## Kontext-Shortcuts +| Pfad | Bedeutung | JS-Aequivalent | +|------|-----------|---------------| +| `$cinst` | Aktuelle Instanz | `this` | +| `$cclass` | Aktuelle Klasse | `this.constructor` | +| `$clib` | Aktuelle Library | - | +| `$ctask` | Aktueller Task | Global scope | +| `$cmethod` | Aktuelle Methode | - | +| `$cwind` | Aktuelles Window | `window` | + +## UI-Objekte +```omnis +$cinst.$objs.feldName.$contents Wert lesen +$cinst.$objs.feldName.$contents.$assign(x) Wert setzen +$cinst.$objs.feldName.$visible Sichtbar? +$cinst.$objs.feldName.$enabled Aktiv? +$cinst.$objs.feldName.$textcolor Textfarbe +$cinst.$objs.feldName.$backcolor Hintergrundfarbe +$cinst.$objs.feldName.$font Schriftart +$cinst.$objs.feldName.$tooltip Tooltip-Text +$cinst.$objs.feldName.$dataname Gebundene Variable +``` + +## Listen-Notation +```omnis +lList.$linecount Anzahl Zeilen +lList.$line Aktuelle Zeile (1-basiert) +lList.$colcount Anzahl Spalten +lList.$cols Spalten-Collection +lList.$cols.$add(...) Spalte hinzufuegen +lList.$add() Zeile hinzufuegen +lList.$remove(n) Zeile n entfernen +lList.$clear() Alle Zeilen loeschen +lList.$search(expr) Suchen +lList.$sort(col, asc) Sortieren +lList.$sendall(expr) Auf alle Zeilen anwenden +lList.$merge(other) Listen zusammenfuegen +lList.$selected Auswahl-Flag der akt. Zeile +lList.[3].colname Spalte in Zeile 3 +lList.colname Spalte in aktueller Zeile +``` + +## Methoden aufrufen +```omnis +Do iObj.$method() Ohne Rueckgabe +Do iObj.$method() Returns lResult Mit Rueckgabe +Do iObj.$method(p1, p2) Returns lR Mit Parametern +Do $cinst.$objs.btn.$visible.$assign(kTrue) Property setzen +Do inherited Super-Methode +``` + +## Wichtige Konstanten +``` +kTrue / kFalse Boolean +kCharacter String-Typ +kInteger Ganzzahl-Typ +kNumber Dezimal-Typ +kDate Datum-Typ +kList Listen-Typ +kRow Row-Typ +kBoolean Boolean-Typ +kFetchAll Alle Zeilen holen +kFetchOne Eine Zeile holen +kSimplechar Einfacher String-Subtyp +k2dp 2 Dezimalstellen +``` + +## Merkhilfe: LESEN vs SETZEN +``` +LESEN: Calculate lVal as $cinst.$objs.feld.$contents +SETZEN: Do $cinst.$objs.feld.$contents.$assign(lVal) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Nie = verwenden! Immer $assign() ! +``` diff --git a/edu/content/cheatsheets/solution2-cheatsheet.md b/edu/content/cheatsheets/solution2-cheatsheet.md new file mode 100644 index 0000000..7b35357 --- /dev/null +++ b/edu/content/cheatsheets/solution2-cheatsheet.md @@ -0,0 +1,112 @@ +# Solution2 ERP Cheat Sheet + +## Klassen-Praefixe - Was ist was? +``` +w = Window (UI-Formular) wOrders, wLogin, wCustomerInvoicesWithList +o = Object (Business-Logik) oOrders, oLogin, oCreateInvoice +T_ = Table (Daten-Cursor) T_Orders, T_Parts, T_Super +sc = Schema (Datenstruktur) scOrderItems, scStaffWorkTimeLog +Q = Query QMOResourcesProcessingQuantity +r = Report (Druckbericht) rInvoice, rDeliveryNote, rCompanyList +js = JS Remote Form (Web) jsMainForm, jsSubForm, jsMainMenu +b = Base-Objekt (Submodul) bobjs +``` + +## Superklassen-Hierarchie +``` +wMainWindow -----> Haupt-Dateneingabe-Fenster +wBaseWindow -----> Modale/Hilfs-Fenster +wSubWindow -----> Eingebettete Sub-Panels +oSuperData -----> Basis fuer Business-Logik-Objekte +T_Super -----> Basis fuer alle Table-Klassen +T_SuperVersion --> Versionierte Table-Klasse +``` + +## Die 22 Libraries +| Library | Bereich | +|---------|---------| +| `solution2` | Kern: Login, Navigation, Enterprise, Mitarbeiter, Kalender | +| `solution2Services` | Hintergrund-Services, Push, Callbacks | +| `BasicTools` | HTML, Import, Edit-Utilities | +| `DBAccess` | Datenbank-Zugriff, Berechtigungen | +| `Sales` | Auftraege, Lieferscheine, Rechnungen | +| `Purchase` | Einkauf, Lieferanten, E-Rechnungen | +| `Customer` | Kundenstamm, Berichte, Aktivitaeten | +| `Manufacturing` | Fertigungsauftraege, Stuecklisten, Kalkulation | +| `QualityInspection` | Qualitaetsprotokolle, Labor | +| `AdminAccounting` | Buchhaltung, Export, ZUGFeRD | +| `PPS` | Produktionsplanung (MRP) | +| `StaffWorkTime` | Arbeitszeit, Urlaub | +| `MobileWork` | Mobile/Web-Client (Remote Forms) | +| `_Analysis_SIM` | Analyse/Simulation | + +## Datenbank-Pattern +``` +DB: masterdemo Schema: soluser Engine: PostgreSQL + +JEDE Tabelle hat: +- created (TIMESTAMP) Erstellungszeitpunkt +- modtime (TIMESTAMP) Letzte Aenderung +- revisor (INTEGER) Wer hat geaendert (Employee-ID) +- valid (BOOLEAN) Soft-Delete Flag + +VIELE Tabellen haben Dual-Signature: +- firstsigned (BOOLEAN) +- needsecondsignature (BOOLEAN) +- secondrevisor (INTEGER) +- secondsigned (BOOLEAN) +``` + +## Die wichtigsten Tabellen +``` +STAMMDATEN: + contact Kontakte/Ansprechpartner + warehouse Lager + dunninglevels Mahnstufen + +VERKAUF: + orders Auftraege (103 Spalten!) + orderitems Auftragspositionen + deliverynoteitems Lieferscheinpositionen + customerinvoices Ausgangsrechnungen (90 Spalten) + customerinvoiceitems Rechnungspositionen + +EINKAUF: + purchaseorder Bestellungen (72 Spalten) + supplierinvoices Eingangsrechnungen + stockreceipt Wareneingaenge + stockreceiptitems Wareneingangs-Positionen + +LAGER: + stockrotationlog Lagerbewegungen + inventoryquantities Bestandsmengen + inventoryvalues Bestandswerte + +FERTIGUNG: + qualityprotocolpending Qualitaetspruefungen +``` + +## Typischer Geschaeftsprozess-Flow +``` +Auftrag (orders) + | + v +Lieferschein (deliverynoteitems) + | + v +Rechnung-Freigabe (customerinvoicerequirement) + | + v +Rechnung (customerinvoices) + | + v +Buchhaltung-Export (transfertoadminaccounting) +``` + +## RBAC-System +``` +Berechtigungen = Form-Path-basiert + -> Berechtigungsgruppen werden Form-Pfaden zugewiesen + -> Nicht pro Screen, sondern pro Modul/Navigation + -> T_ModuleAccess2Methods steuert Methoden-Zugriff +``` diff --git a/edu/content/cheatsheets/sql-cheatsheet.md b/edu/content/cheatsheets/sql-cheatsheet.md new file mode 100644 index 0000000..b3dab06 --- /dev/null +++ b/edu/content/cheatsheets/sql-cheatsheet.md @@ -0,0 +1,104 @@ +# SQL in Omnis Cheat Sheet + +## Verbindung aufbauen +```omnis +# Session-Objekt erstellen (im IDE: Variable lSess vom Typ Object) +Do lSess.$logon('localhost', 'postgres', 'passwort', 'meineSess') Returns #F +If flag true + # Verbunden! +End If +``` + +## SELECT - Daten lesen +```omnis +# SICHER mit Bind-Variablen (:1, :2) +Do lStmt.$prepare('SELECT * FROM contact WHERE companyid = :1 AND valid = :2') Returns #F +Do lStmt.$execute(lCompanyID, kTrue) Returns #F +Do lStmt.$fetch(lList, kFetchAll) Returns #F + +# Alternative: @[]-Syntax (auch sicher) +Do lStmt.$execdirect('SELECT * FROM orders WHERE orderid = @[lOrderID]') Returns #F +Do lStmt.$fetch(lList, kFetchAll) Returns #F +``` + +## INSERT - Daten einfuegen +```omnis +Do lStmt.$execdirect(con( | + 'INSERT INTO mytable (name, status, created, revisor, valid) ', | + 'VALUES (@[lName], @[lStatus], now(), @[tEmployeeID], true)' | + )) Returns #F +``` + +## UPDATE - Daten aendern +```omnis +Do lStmt.$prepare(con( | + 'UPDATE orders SET status = :1, modtime = now(), revisor = :2 ', | + 'WHERE orderid = :3' | + )) Returns #F +Do lStmt.$execute(lNewStatus, tEmployeeID, lOrderID) Returns #F +``` + +## DELETE - Daten loeschen (VORSICHT!) +```omnis +# In Solution2: Soft-Delete bevorzugen! +Do lStmt.$execdirect(con( | + 'UPDATE orders SET valid = false, modtime = now(), revisor = @[tEmployeeID] ', | + 'WHERE orderid = @[lOrderID]' | + )) Returns #F +``` + +## Transaktionen +```omnis +Do lSess.$begin() Returns #F ;; START TRANSACTION + # ... mehrere SQL-Operationen ... +If lAllesOK + Do lSess.$commit() Returns #F ;; COMMIT +Else + Do lSess.$rollback() Returns #F ;; ROLLBACK +End If +``` + +## Fehlerbehandlung +```omnis +Do lStmt.$execute(lParam) Returns #F +If not(flag true) + Calculate lError as lSess.$lasterrortext + OK message {SQL-Fehler: [lError]} +End If +``` + +## Fetch-Modi +``` +kFetchAll -> Alle Zeilen in Liste (fuer kleine/mittlere Ergebnisse) +kFetchOne -> Eine Zeile (fuer Schleifen bei grossen Ergebnissen) +kFetchBulk -> Block von N Zeilen (Kompromiss) +``` + +## WICHTIGE REGELN +``` +IMMER: @[lVar] oder :1 (Bind-Variablen = SICHER) +NIE: [lVar] (Text-Substitution = SQL-INJECTION!) + +IMMER: WHERE valid = true (Soft-Delete beachten!) +IMMER: modtime, revisor setzen bei UPDATE +IMMER: created, revisor, valid setzen bei INSERT +IMMER: #F pruefen nach jedem SQL-Aufruf +``` + +## Solution2-spezifische SQL-Tipps +``` +Schema: soluser +Datenbank: masterdemo +Engine: PostgreSQL + +Typische WHERE-Klausel: + WHERE valid = true + AND companyid = @[lCompanyID] + ORDER BY modtime DESC + +Datum/Zeit: + now() -> aktueller Timestamp + current_date -> aktuelles Datum + @[#D] -> Omnis-Datum als Parameter + @[#T] -> Omnis-Timestamp als Parameter +``` diff --git a/edu/content/cheatsheets/windows-cheatsheet.md b/edu/content/cheatsheets/windows-cheatsheet.md new file mode 100644 index 0000000..5925fd9 --- /dev/null +++ b/edu/content/cheatsheets/windows-cheatsheet.md @@ -0,0 +1,143 @@ +# Window-Architektur Cheat Sheet + +## Klassenhierarchie +``` +wBaseWindow (Wurzel - keine Superklasse) + +-- wMainWindow (eigenstaendige Fenster: wOrders, wParts...) + +-- wSubWindow (eingebettete Panels: wOrder, wOrderItems...) +``` + +## Wichtigste Instanzvariablen + +### Auf ALLEN Fenstern (von wBaseWindow): +| Variable | Zweck | +|----------|-------| +| `ioCode` | Business-Logik-Objekt | +| `iiRef2SubWindow` | Referenz auf aktives SubWindow | +| `ibControl2SubWindow` | `kTrue` = Commands gehen an SubWindow | +| `ilList` | Haupt-Datenliste | +| `irRow` | Aktuelle Datenzeile | +| `inEDMode` | Bearbeitungsmodus | +| `icCurrTabPane` | Aktiver Tab-Reiter | + +### Nur auf wSubWindow: +| Variable | Zweck | +|----------|-------| +| `iiRef2Parent` | Referenz ZURUECK zum Elter | +| `icFormWindow` | Name fuer Rechte-Check | +| `icSubWindow` | SubWindow-Feldname | + +## DAS zentrale Pattern: Command-Delegation +```omnis +## Auf jedem Fenster identisch: +If $cinst.ibControl2SubWindow + Do iiRef2SubWindow.$cXxx() ## -> Weiter ans SubWindow +Else + Do $cinst.ioCode.$cXxx() ## -> Business-Logik +End If +``` + +## Lebenszyklus + +### $construct (SubWindow meldet sich an): +```omnis +Do inherited +Set reference iiRef2SubWindow to $cinst.$objs.wSubWindow.$ref +Set reference iiRef2Parent to pRef2Parent +Do iiRef2Parent.$setControl2SubWindow(kTrue) ## KONTROLLE UEBERNEHMEN +``` + +### $destruct (SubWindow meldet sich ab): +```omnis +If iiRef2Parent + Do iiRef2Parent.$setControl2SubWindow(kFalse) ## KONTROLLE ZURUECKGEBEN +End If +Do ilList.$define() +Do irRow.$define() +``` + +### $closeWindow (delegiert nach oben): +```omnis +Do iiRef2Parent.$closeWindow() ## Parent schliesst das Fenster +``` + +### $EnableTabPane (sperrt Tabs nach oben): +```omnis +Do iiRef2Parent.$EnableTabPane($cinst.inEDMode) ## Parent sperrt seine Tabs +``` + +## Command-Fluss Beispiel (Sales/wOrders) +``` +User klickt [Edit] + -> wOrders.$cEdit (wMainWindow) + -> $closeRelatedWindow() + -> ibControl2SubWindow = kTrue + -> wOrder.$cEdit (wSubWindow Ebene 1) + -> ibControl2SubWindow = kTrue + -> wOrderItems.$cEdit (wSubWindow Ebene 2) + -> ibControl2SubWindow = kFalse + -> ioCode.$cEdit() == oOrderItems.$cEdit() + -> HIER passiert die Aktion! +``` + +## Alle delegierten Commands + +### wMainWindow (mit $closeRelatedWindow): +`$cEdit`, `$cCancel`, `$cAudit`, `$cNextVersion` + +### wSubWindow (ohne $closeRelatedWindow): +`$cEdit`, `$cCancel`, `$cInsert`, `$cDelete`, `$cSave`, `$cPrint`, `$cAudit`, `$cNextVersion`, `$cFirst`, `$cLast` + +## Referenz-Kette +``` + iiRef2SubWindow iiRef2SubWindow +MainWindow ----------> SubWindow ----------> SubSubWindow + <---------- <---------- + iiRef2Parent iiRef2Parent +``` + +## Cross-Library Vererbung +``` +# Innerhalb solution2-Library: +wMeinFenster extends wSubWindow + +# Aus anderer Library (Sales, Manufacturing...): +wMeinFenster extends solution2.wSubWindow ## Library-Prefix noetig! +``` + +## TabPane-Konvention +``` +TabPane-Objekt heisst IMMER: TP +Zugriff: $cwind.$objs.TP.$enableTabPanes(pEDMode) +``` + +## 7 Goldene Regeln +``` +1. ibControl2SubWindow bestimmt ALLES +2. $construct: inherited aufrufen = Kette aufbauen +3. $destruct: Kontrolle zurueckgeben +4. Commands fliessen ABWAERTS (Main -> Sub -> SubSub) +5. TabPane-Sperren fliessen AUFWAERTS (Sub -> Parent) +6. $closeWindow delegiert AUFWAERTS +7. ioCode ist der letzte Stopp (kein SubWindow aktiv) +``` + +## Debugging Quick-Check +```omnis +# 1. Wer hat die Kontrolle? +Calculate lDebug as $cinst.ibControl2SubWindow + +# 2. Ist SubWindow-Referenz gueltig? +If iiRef2SubWindow + # Referenz existiert +End If + +# 3. Welches SubWindow ist aktiv? +Calculate lClass as iiRef2SubWindow.$classname + +# 4. Hat $construct inherited aufgerufen? +# -> Breakpoint in wSubWindow.$construct setzen + +# 5. Ist Superklasse korrekt? +# -> Class Inspector -> Superclass pruefen +``` diff --git a/edu/content/flashcards/deck-commands.md b/edu/content/flashcards/deck-commands.md new file mode 100644 index 0000000..61debf4 --- /dev/null +++ b/edu/content/flashcards/deck-commands.md @@ -0,0 +1,163 @@ +# Flashcard Deck: Omnis Commands + +**Thema**: Die wichtigsten Omnis-Befehle +**Schwierigkeit**: Anfaenger -> Fortgeschritten +**Karten**: 25 + +--- + +## Karte 1 | Basis +**Q:** Was macht der Befehl `Calculate`? +**A:** Berechnet einen Ausdruck und weist das Ergebnis einer Variablen zu. `Calculate lX as 5 + 3` -> lX = 8. Aequivalent zu `let x = 5 + 3` in JS. + +--- + +## Karte 2 | Basis +**Q:** Was macht der Befehl `Do`? +**A:** Fuehrt eine Methode oder einen Notation-Ausdruck aus. `Do iObj.$method() Returns lResult`. Wie ein Funktionsaufruf. + +--- + +## Karte 3 | Basis +**Q:** Was macht `Quit method`? +**A:** Beendet die aktuelle Methode und gibt optional einen Wert zurueck. `Quit method lResult` = `return lResult` in JS. + +--- + +## Karte 4 | Basis +**Q:** Was macht `OK message`? +**A:** Zeigt ein Dialogfeld mit einer Nachricht. `OK message {Hallo [lName]!}` - wie `alert()` in JS. Variablen in `[]` werden ersetzt. + +--- + +## Karte 5 | Basis +**Q:** Was ist der Unterschied zwischen `[lVar]` und `@[lVar]` in SQL? +**A:** `[lVar]` = direkte Text-Substitution (SQL-Injection-Gefahr!). `@[lVar]` = Bind-Variable (sicher, parametrisiert). **Immer `@[lVar]` in SQL verwenden!** + +--- + +## Karte 6 | Basis +**Q:** Was macht `If / Else / End If`? +**A:** Bedingte Ausfuehrung - identisch zu anderen Sprachen. `If lAge >= 18 ... Else ... End If`. WICHTIG: `End If` ist Pflicht! + +--- + +## Karte 7 | Basis +**Q:** Was macht `For ... End For`? +**A:** Zaehlschleife: `For lI from 1 to 10 step 1 ... End For`. Oder Listen-Iteration: `For each line in list from 1 to lList.$linecount step 1 ... End For`. + +--- + +## Karte 8 | Basis +**Q:** Was macht `While ... End While`? +**A:** Bedingungsschleife: `While lCounter < 100 ... End While`. Wie `while` in jeder anderen Sprache. + +--- + +## Karte 9 | Mittel +**Q:** Was macht `Switch / Case / Default / End Switch`? +**A:** Fallunterscheidung: `Switch lStatus / Case 'A' ... Case 'B' ... Default ... End Switch`. Wie `switch` in JS, aber OHNE `break` noetig! + +--- + +## Karte 10 | Mittel +**Q:** Was macht `Quit all methods`? +**A:** Bricht die GESAMTE Methodenkette ab (alle aufrufenden Methoden). Wie ein sofortiger Hard-Exit. Vorsichtig verwenden! + +--- + +## Karte 11 | Mittel +**Q:** Was macht `Enter data`? +**A:** Uebergibt die Kontrolle an den Benutzer, damit er Daten eingeben kann. Die Methode pausiert, bis der User fertig ist. Gibt es in modernen Sprachen nicht! + +--- + +## Karte 12 | Mittel +**Q:** Was macht `Yes/No message`? +**A:** Zeigt einen Dialog mit Ja/Nein-Buttons. Ergebnis in `#F`: `Yes/No message {Sicher?} / If flag true ... End If`. Wie `confirm()` in JS. + +--- + +## Karte 13 | Mittel +**Q:** Was ist `#F` (der Flag)? +**A:** Ein globaler Boolean-Wert, der von vielen Befehlen gesetzt wird. `Returns #F` bei SQL-Operationen. Wie ein impliziter Return-Code. Abfrage mit `If flag true`. + +--- + +## Karte 14 | Mittel +**Q:** Was macht `Breakpoint`? +**A:** Haelt die Ausfuehrung an und oeffnet den Debugger - wie `debugger;` in JavaScript. + +--- + +## Karte 15 | Mittel +**Q:** Was macht `Begin/End reversible block`? +**A:** Definiert Code, der automatisch rueckgaengig gemacht wird, wenn die aeussere Methode endet. Fuer temporaere Aenderungen (Cursor, Fenster-Zustand etc.). + +--- + +## Karte 16 | Mittel +**Q:** Was macht `Set main file`? +**A:** Setzt die Hauptdatei (Tabelle) fuer Datenoperationen. Legacy-Konzept aus der Omnis-eigenen DB, wird bei SQL-DAMs weniger gebraucht. + +--- + +## Karte 17 | Fortgeschritten +**Q:** Was macht `Do inherited`? +**A:** Ruft die gleichnamige Methode der Superklasse auf - wie `super.method()` in JS oder `super().method()` in Python. + +--- + +## Karte 18 | Fortgeschritten +**Q:** Was macht `Install menu`? +**A:** Installiert ein Menu-Objekt in der Menueleiste. Omnis hat ein eigenes Menu-System (nicht HTML-basiert fuer Desktop-Apps). + +--- + +## Karte 19 | Fortgeschritten +**Q:** Was macht `Prepare for insert` / `Prepare for edit`? +**A:** Bereitet einen Datensatz zum Einfuegen/Bearbeiten vor. Setzt interne Flags und initialisiert Felder. Teil des Omnis-Daten-Workflows. + +--- + +## Karte 20 | Fortgeschritten +**Q:** Was macht `Update files`? +**A:** Schreibt geaenderte Daten zurueck in die Datenbank. Wie `COMMIT` in SQL + `$flush()`. Teil des legacy Daten-Workflow. + +--- + +## Karte 21 | Basis +**Q:** Was macht `Set current list`? +**A:** Setzt die "aktive" Liste fuer Listenbefehle. `Set current list lMyList`. Legacy-Ansatz - besser: direkte $-Notation verwenden. + +--- + +## Karte 22 | Mittel +**Q:** Was macht `Redraw`? +**A:** Erzwingt das Neuzeichnen eines UI-Elements. `Do $cinst.$objs.myField.$redraw()` oder `Redraw {myField}`. Wie `forceUpdate()` in React. + +--- + +## Karte 23 | Mittel +**Q:** Was macht `Send to back / Bring to front`? +**A:** Aendert die Z-Order von UI-Elementen (welches Element vorne liegt). Desktop-UI-Konzept. + +--- + +## Karte 24 | Fortgeschritten +**Q:** Was macht `Do redirect`? +**A:** Leitet eine Methode auf ein anderes Objekt um. `Do redirect (lOtherObj, 'methodName')`. Wie Delegation/Proxy in JS. + +--- + +## Karte 25 | Fortgeschritten +**Q:** Was macht `Queue` Befehle (Queue set, Queue send, etc.)? +**A:** Reiht Events in die Event-Queue ein, die nach der aktuellen Methode verarbeitet werden. Wie `setTimeout(fn, 0)` / `queueMicrotask()` in JS - asynchrone Ausfuehrung. + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-gotchas.md b/edu/content/flashcards/deck-gotchas.md new file mode 100644 index 0000000..a2c6600 --- /dev/null +++ b/edu/content/flashcards/deck-gotchas.md @@ -0,0 +1,102 @@ +# Flashcard Deck: Gotchas & Haeufige Fehler + +**Thema**: Typische Fallen und wie man sie vermeidet +**Karten**: 15 + +--- + +## Karte 1 | Kritisch +**Q:** Warum liefert meine Berechnung manchmal unerwartet 0 oder leer? +**A:** NULL-Propagation! Wenn IRGEND ein Wert in einem Ausdruck NULL ist, wird das GANZE Ergebnis NULL. Loesung: Immer vorher auf NULL pruefen mit `isnull(lVar)` oder `nullValuesWhenORtestedBecomeZero` setzen. + +--- + +## Karte 2 | Kritisch +**Q:** Ich rufe eine Methode auf, aber sie wird nicht gefunden. Warum? +**A:** Die Methode ist wahrscheinlich auf der SUPERKLASSE definiert. Omnis-Klassen erben Methoden. Pruefe: `oSuperData`, `T_Super`, `wMainWindow`, `wBaseWindow`. Auch: Tippfehler im $-Methodennamen! + +--- + +## Karte 3 | Kritisch +**Q:** Mein `lList.spalte` gibt immer leer zurueck. Warum? +**A:** Die Liste hat keine aktuelle Zeile gesetzt (`$line = 0`). Setze zuerst `$line`: `Do lList.$line.$assign(1)` oder nutze `For each line in list`. + +--- + +## Karte 4 | Kritisch +**Q:** `Do $cinst.$objs.feld.$contents = "Test"` funktioniert nicht. Warum? +**A:** Man kann Notation-Properties NICHT mit `=` setzen! Immer `$assign()` verwenden: `Do $cinst.$objs.feld.$contents.$assign("Test")`. Das ist DER haeufigste Anfaengerfehler! + +--- + +## Karte 5 | Wichtig +**Q:** Mein Remote Form reagiert traege. Warum? +**A:** Jeder `$event()` macht einen Server-Roundtrip! Loesung: Validierung und UI-Logik in `$eventclient()` (Browser-seitig) ausfuehren. Nur Datenbankzugriff im Server-Event. + +--- + +## Karte 6 | Wichtig +**Q:** Mein SQL mit `[lVar]` erzeugt seltsame Fehler bei Sonderzeichen. Warum? +**A:** `[lVar]` macht direkte Text-Substitution - Apostroph im Namen -> SQL-Fehler oder SQL-Injection! Immer `@[lVar]` (Bind-Variable) oder `:1`-Parameter verwenden! + +--- + +## Karte 7 | Wichtig +**Q:** Meine Aenderungen an einer Variable werden in einem anderen Window nicht sichtbar. Warum? +**A:** Variable-Scoping! `lVar` (lokal) lebt nur in der Methode. `iVar` (Instanz) nur im Objekt. Fuer fenster-uebergreifend: `tVar` (Task) verwenden oder Daten explizit uebergeben. + +--- + +## Karte 8 | Wichtig +**Q:** Ich habe eine Klasse geaendert, aber die Aenderung wirkt sich nicht aus. Warum? +**A:** Moeglicherweise laeuft noch eine alte Instanz! Schliesse alle offenen Fenster/Instanzen und oeffne neu. Oder: Die Methode ist auf der Superklasse und du aenderst die falsche Klasse. + +--- + +## Karte 9 | Mittel +**Q:** Warum zaehlt `$linecount` von 1 und nicht von 0? +**A:** Omnis-Listen sind 1-basiert (wie VB, anders als JS/Python). `$line = 1` ist die ERSTE Zeile. `$line = 0` bedeutet "keine Zeile gewaehlt". + +--- + +## Karte 10 | Mittel +**Q:** Mein `$search` findet nichts, obwohl der Wert existiert. Warum? +**A:** String-Vergleich ist Case-Sensitive in Omnis! `"Harry" <> "harry"`. Loesung: `Do lList.$search(upp(lList.name) = upp(lSearchTerm))` oder `low()` verwenden. + +--- + +## Karte 11 | Mittel +**Q:** Was passiert wenn ich `Calculate lList as lOtherList` mache? +**A:** Du bekommst eine KOPIE, keine Referenz! Aenderungen an `lList` aendern `lOtherList` NICHT. Das ist anders als bei Arrays in JavaScript! + +--- + +## Karte 12 | Mittel +**Q:** Mein `$sendall` wirft einen Fehler bei leeren Listen. Warum? +**A:** `$sendall` auf eine leere Liste ist eigentlich ok, aber wenn der Ausdruck auf Spalten zugreift die nicht existieren, kommt ein Fehler. Immer vorher pruefen: `If lList.$linecount > 0`. + +--- + +## Karte 13 | Fortgeschritten +**Q:** Mein `Quit method` gibt nichts zurueck, obwohl ich einen Wert angebe. Warum? +**A:** Der Aufrufer muss `Returns lVar` verwenden! Ohne `Returns` geht der Rueckgabewert verloren: `Do iObj.$method() Returns lResult` (richtig) vs `Do iObj.$method()` (Wert geht verloren). + +--- + +## Karte 14 | Fortgeschritten +**Q:** In Solution2: Warum sehe ich einen Datensatz mit `valid = false`? +**A:** Soft-Delete! `valid = false` bedeutet "geloescht" aber noch in der DB. Immer `WHERE valid = true` in Abfragen verwenden. Solution2 nutzt `valid` konsequent statt physischem DELETE. + +--- + +## Karte 15 | Fortgeschritten +**Q:** Mein Report druckt leere Seiten. Warum? +**A:** Die Report-Klasse hat keine Daten! Reports brauchen eine definierte Liste als Datenquelle (`$dataname`). Pruefe: Wurde die Liste vor dem Report-Aufruf korrekt befuellt? + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-lists.md b/edu/content/flashcards/deck-lists.md new file mode 100644 index 0000000..5cf8fb2 --- /dev/null +++ b/edu/content/flashcards/deck-lists.md @@ -0,0 +1,166 @@ +# Flashcard Deck: Listen & Rows + +**Thema**: Omnis Listen - der universelle Datencontainer +**Schwierigkeit**: Anfaenger -> Fortgeschritten +**Karten**: 20 + +--- + +## Karte 1 | Basis +**Q:** Was ist eine "List" in Omnis? +**A:** Ein zweidimensionaler Datencontainer mit benannten, typisierten Spalten und beliebig vielen Zeilen. Wie ein Array von Objects in JS, ein DataFrame in Python, oder ein RecordSet in VB. + +--- + +## Karte 2 | Basis +**Q:** Was ist eine "Row" in Omnis? +**A:** Eine einzelne Datenzeile mit benannten Spalten. Wie ein Object/Dictionary. Eine List besteht aus vielen Rows. + +--- + +## Karte 3 | Basis +**Q:** Wie erstellst du eine Liste mit Spalten? +**A:** +```omnis +Do lList.$cols.$add('id', kInteger) +Do lList.$cols.$add('name', kCharacter, kSimplechar, 100) +Do lList.$cols.$add('price', kNumber, k2dp, 10) +``` + +--- + +## Karte 4 | Basis +**Q:** Wie fuegst du eine Zeile zu einer Liste hinzu? +**A:** +```omnis +Do lList.$add() +Calculate lList.id as 1 +Calculate lList.name as "Produkt A" +Calculate lList.price as 29.99 +``` +`$add()` fuegt eine Zeile hinzu UND macht sie zur aktuellen Zeile. + +--- + +## Karte 5 | Basis +**Q:** Wie iterierst du ueber alle Zeilen einer Liste? +**A:** +```omnis +For each line in list from 1 to lList.$linecount step 1 + # lList.spaltenname gibt den Wert der aktuellen Zeile + Calculate lTotal as lTotal + lList.price +End For +``` + +--- + +## Karte 6 | Mittel +**Q:** Wie suchst du in einer Liste? +**A:** `Do lList.$search(lList.name = "Produkt A")` - Setzt `$line` auf den Treffer. `If flag true` prueft ob gefunden. + +--- + +## Karte 7 | Mittel +**Q:** Wie sortierst du eine Liste? +**A:** `Do lList.$sort(lList.name, kTrue)` - Sortiert nach Spalte "name". `kTrue` = aufsteigend, `kFalse` = absteigend. + +--- + +## Karte 8 | Mittel +**Q:** Wie entfernst du eine Zeile aus einer Liste? +**A:** `Do lList.$remove(lList.$line)` entfernt die aktuelle Zeile. `Do lList.$remove(3)` entfernt Zeile 3. `Do lList.$clear()` leert alle Zeilen. + +--- + +## Karte 9 | Mittel +**Q:** Was ist `$linecount`? +**A:** Die Anzahl der Zeilen in einer Liste. `Calculate lAnzahl as lList.$linecount`. Wie `array.length` in JS. + +--- + +## Karte 10 | Mittel +**Q:** Was ist `$line`? +**A:** Die aktuelle Zeilennummer einer Liste (1-basiert!). Wird durch `$search`, `$add`, Klick auf Grid gesetzt. `0` = keine Zeile gewaehlt. + +--- + +## Karte 11 | Mittel +**Q:** Wie liest du eine bestimmte Zeile (nicht die aktuelle)? +**A:** `Calculate lVal as lList.[3].name` liest Spalte "name" von Zeile 3. Die eckigen Klammern addressieren die Zeilennummer. + +--- + +## Karte 12 | Fortgeschritten +**Q:** Was macht `$sendall()`? +**A:** Fuehrt einen Ausdruck fuer JEDE Zeile der Liste aus (wie `forEach` + `map` in einem): +```omnis +Do lList.$sendall($ref.price.$assign($ref.price * 1.19)) +``` +`$ref` = aktuelle Zeile. + +--- + +## Karte 13 | Fortgeschritten +**Q:** Wie filterst du eine Liste? +**A:** `$search` mit `kTrue` im 2. Parameter zeigt nur Treffer: +```omnis +Do lList.$search(lList.status = 'active', kTrue) +``` +Nicht-Treffer werden "ausgeblendet" (nicht geloescht). `Do lList.$search(kTrue)` zeigt wieder alle. + +--- + +## Karte 14 | Fortgeschritten +**Q:** Was ist der Unterschied zwischen `$merge()` und `$add()`? +**A:** `$add()` fuegt eine leere Zeile hinzu. `$merge(lOtherList)` fuegt ALLE Zeilen einer anderen Liste hinzu - wie `array.concat()` in JS. + +--- + +## Karte 15 | Fortgeschritten +**Q:** Wie definierst du eine Liste aus einer Schema-Klasse? +**A:** `Do lList.$definefromsqlclass('scMySchema')` - Uebernimmt die Spaltenstruktur aus dem Schema. Wie ein Typ/Interface fuer die Liste. + +--- + +## Karte 16 | Fortgeschritten +**Q:** Was ist `$colcount`? +**A:** Die Anzahl der Spalten einer Liste. `Calculate lCols as lList.$colcount`. Fuer dynamische Listen nuetzlich. + +--- + +## Karte 17 | Fortgeschritten +**Q:** Wie kopierst du eine Liste? +**A:** `Calculate lCopy as lOriginal` erstellt eine **Kopie** (nicht Referenz!). Omnis-Listen sind Value-Types, nicht Reference-Types wie in JS! + +--- + +## Karte 18 | Fortgeschritten +**Q:** Wie befuellst du eine Liste aus SQL? +**A:** +```omnis +Do lStmt.$execdirect('SELECT * FROM orders') Returns #F +If flag true + Do lStmt.$fetch(lList, kFetchAll) Returns #F +End If +``` +`kFetchAll` holt alle Zeilen auf einmal. + +--- + +## Karte 19 | Fortgeschritten +**Q:** Was ist `$selected` bei einer Liste? +**A:** Ein Boolean-Flag pro Zeile, das anzeigt ob die Zeile "ausgewaehlt" ist (z.B. per Checkbox im Grid). `lList.$selected` liest/setzt den Wert der aktuellen Zeile. + +--- + +## Karte 20 | Fortgeschritten +**Q:** Was ist der Unterschied zwischen Listen und Rows beim SQL-Fetch? +**A:** `$fetch(lList, kFetchAll)` holt mehrere Zeilen in eine Liste. `$fetch(lRow, kFetchOne)` holt nur eine Zeile in eine Row. Row = einzelner Datensatz, Liste = Ergebnismenge. + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-notation.md b/edu/content/flashcards/deck-notation.md new file mode 100644 index 0000000..59420e0 --- /dev/null +++ b/edu/content/flashcards/deck-notation.md @@ -0,0 +1,193 @@ +# Flashcard Deck: $ Notation + +**Thema**: Das Omnis $-Notationssystem +**Schwierigkeit**: Anfaenger -> Fortgeschritten +**Karten**: 30 + +--- + +## Karte 1 | Basis +**Q:** Was bedeutet `$cinst` in Omnis? +**A:** Die aktuelle Instanz - aequivalent zu `this` in JavaScript oder `self` in Python. Zeigt auf das aktuelle Window/Objekt. + +--- + +## Karte 2 | Basis +**Q:** Wie liest du den Inhalt eines Feldes "customerName" auf dem aktuellen Window? +**A:** `Calculate lVal as $cinst.$objs.customerName.$contents` + +--- + +## Karte 3 | Basis +**Q:** Wie SETZT du den Inhalt eines Feldes? Warum geht `= ` nicht? +**A:** `Do $cinst.$objs.customerName.$contents.$assign("Meier")` - Man muss `$assign()` verwenden, weil Notation-Pfade Objekte sind, keine einfachen Variablen. + +--- + +## Karte 4 | Basis +**Q:** Was ist der Unterschied zwischen `$objs` und `$bobjs`? +**A:** `$objs` = Objekte auf dem aktuellen Fenster/Form. `$bobjs` = "Background objects" / Objekte auf einer Superklasse (geerbte Objekte). + +--- + +## Karte 5 | Basis +**Q:** Was gibt `$clib` zurueck? +**A:** Die aktuelle Library (das Omnis-Projekt / .lbs Datei), in der der Code laeuft. + +--- + +## Karte 6 | Basis +**Q:** Wie greifst du auf eine andere Klasse in der gleichen Library zu? +**A:** `Do $clib.$classes.ClassName.$open() Returns lRef` + +--- + +## Karte 7 | Mittel +**Q:** Was bedeutet `$ctask`? +**A:** Der aktuelle Task - eine Art globaler Kontext/Session. Task-Variablen (`tVar`) leben hier. + +--- + +## Karte 8 | Mittel +**Q:** Wie machst du ein Feld unsichtbar? +**A:** `Do $cinst.$objs.fieldName.$visible.$assign(kFalse)` + +--- + +## Karte 9 | Mittel +**Q:** Wie rufst du eine Methode auf einem anderen Objekt auf? +**A:** `Do iMyObject.$methodName(param1, param2) Returns lResult` + +--- + +## Karte 10 | Mittel +**Q:** Was ist `$cols` bei einer Liste? +**A:** Die Spalten-Sammlung einer Liste. `lList.$cols.$add('name', kCharacter, kSimplechar, 255)` fuegt eine Spalte hinzu. + +--- + +## Karte 11 | Mittel +**Q:** Wie zaehlt man die Zeilen einer Liste? +**A:** `Calculate lCount as lList.$linecount` + +--- + +## Karte 12 | Mittel +**Q:** Was macht `$line`? +**A:** Gibt die aktuelle Zeilennummer einer Liste zurueck oder setzt sie. `Do lList.$line.$assign(5)` springt zu Zeile 5. + +--- + +## Karte 13 | Mittel +**Q:** Wie greifst du auf den Wert einer Spalte "price" in der aktuellen Zeile einer Liste zu? +**A:** `Calculate lPrice as lList.price` (wenn `$line` gesetzt ist) oder `Calculate lPrice as lList.[lRow].price` fuer eine bestimmte Zeile. + +--- + +## Karte 14 | Fortgeschritten +**Q:** Was ist `$sendall()`? +**A:** Fuehrt einen Befehl fuer ALLE Zeilen einer Liste aus - wie `forEach` in JS. Beispiel: `Do lList.$sendall($ref.price.$assign($ref.price * 1.19))` (19% MwSt auf alle Preise) + +--- + +## Karte 15 | Fortgeschritten +**Q:** Was bedeutet `$ref` in `$sendall`? +**A:** `$ref` ist die Referenz auf die aktuelle Zeile innerhalb von `$sendall` - wie der Callback-Parameter in `Array.forEach(item => ...)`. + +--- + +## Karte 16 | Fortgeschritten +**Q:** Wie findest du eine Zeile in einer Liste per Suche? +**A:** `Do lList.$search(lList.id = lSearchID)` - Setzt `$line` auf die gefundene Zeile. `#F` (Flag) zeigt Erfolg. + +--- + +## Karte 17 | Fortgeschritten +**Q:** Was ist `$getAllCols()`? +**A:** Laedt alle Spalten einer Datenbankabfrage in die Liste, ohne sie einzeln zu benennen. + +--- + +## Karte 18 | Basis +**Q:** Was macht `kTrue` vs `kFalse`? +**A:** Omnis-Konstanten fuer Boolean true/false. Immer mit `k`-Praefix (k = Konstante). + +--- + +## Karte 19 | Mittel +**Q:** Wie oeffnest du ein Window (Fenster)? +**A:** `Do $clib.$windows.wMyWindow.$open('*') Returns lWinRef` + +--- + +## Karte 20 | Mittel +**Q:** Was bedeutet der `'*'` Parameter bei `$open`? +**A:** Oeffnet das Fenster als neue Instanz. Ohne `'*'` wird eine bestehende Instanz wiederverwendet. + +--- + +## Karte 21 | Fortgeschritten +**Q:** Wie iterierst du ueber alle Objekte auf einem Window? +**A:** `For each $cinst.$objs as lObj` - wie `for (const el of document.querySelectorAll('*'))` in JS. + +--- + +## Karte 22 | Fortgeschritten +**Q:** Was ist der Unterschied zwischen `$cclass` und `$cinst`? +**A:** `$cclass` = die Klasse (Bauplan/Template), `$cinst` = die konkrete Instanz (laufendes Objekt). Wie `MyClass` vs `myInstance` in JS. + +--- + +## Karte 23 | Basis +**Q:** Wie gibt eine Methode einen Wert zurueck? +**A:** `Quit method lReturnValue` - aequivalent zu `return lReturnValue` in JS. + +--- + +## Karte 24 | Mittel +**Q:** Was ist `$event`? +**A:** Die Standard-Event-Handler-Methode einer Klasse. Wird bei UI-Events aufgerufen (wie `onClick`). Parameter `pEventCode` enthaelt den Event-Typ. + +--- + +## Karte 25 | Fortgeschritten +**Q:** Unterschied `$event` vs `$eventclient` bei Remote Forms? +**A:** `$event` = laeuft auf dem SERVER. `$eventclient` = laeuft im BROWSER (JavaScript). Fuer schnelle UI-Reaktionen `$eventclient` verwenden. + +--- + +## Karte 26 | Fortgeschritten +**Q:** Wie greifst du von einer Instanz auf die Instanz-Variable eines anderen offenen Windows zu? +**A:** `Calculate lVal as $iwindows.wOtherWindow.$ivar.iMyVariable` + +--- + +## Karte 27 | Mittel +**Q:** Was macht `$add()` bei einer Liste? +**A:** Fuegt eine neue leere Zeile am Ende hinzu. `Do lList.$add()`. Danach Werte setzen mit `Calculate lList.colName as "wert"`. + +--- + +## Karte 28 | Mittel +**Q:** Wie loeschst du eine Zeile aus einer Liste? +**A:** `Do lList.$remove(lList.$line)` - entfernt die aktuelle Zeile. Oder `Do lList.$remove(5)` fuer Zeile 5. + +--- + +## Karte 29 | Fortgeschritten +**Q:** Was ist `$define()` bei einem Schema/Query? +**A:** Definiert die Spaltenstruktur. `Do lSchema.$define(col1, col2, ...)` - wie ein CREATE TABLE fuer eine Omnis-Datenstruktur. + +--- + +## Karte 30 | Fortgeschritten +**Q:** Wie navigierst du durch die gesamte Omnis-Objekthierarchie? +**A:** Von oben: `$root` -> `$libs` -> Library -> `$classes` -> Class -> `$methods` / `$objs` / `$ivars`. Alles ist ein Baum mit `$` Pfaden! + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-solution2.md b/edu/content/flashcards/deck-solution2.md new file mode 100644 index 0000000..4631208 --- /dev/null +++ b/edu/content/flashcards/deck-solution2.md @@ -0,0 +1,132 @@ +# Flashcard Deck: Solution2 Architektur & Konventionen + +**Thema**: Solution2 ERP-System kennenlernen +**Karten**: 20 + +--- + +## Karte 1 | Basis +**Q:** Was ist Solution2? +**A:** Ein vollstaendiges deutsches ERP-System (Enterprise Resource Planning) gebaut auf Omnis Studio. Abdeckung: Verkauf, Einkauf, Fertigung, Lager, Qualitaet, Buchhaltung, HR, Mobile. + +--- + +## Karte 2 | Basis +**Q:** Welche Datenbank nutzt Solution2? +**A:** PostgreSQL. Datenbank `masterdemo`, Schema `soluser`. Verbindung ueber den PostgreSQL-DAM (`PGSQLDAM`). + +--- + +## Karte 3 | Basis +**Q:** Was bedeutet das Praefix `w` bei einer Solution2-Klasse? +**A:** Window-Klasse = UI-Formular. z.B. `wOrders` = das Auftragsformular, `wLogin` = der Login-Dialog. + +--- + +## Karte 4 | Basis +**Q:** Was bedeutet das Praefix `o`? +**A:** Object-Klasse = Business-Logik. z.B. `oCreateInvoice` = Logik fuer Rechnungserstellung, `oLogin` = Login-Validierung. + +--- + +## Karte 5 | Basis +**Q:** Was bedeutet `T_` als Praefix? +**A:** Table-Klasse = Daten-Cursor/Datenliste. z.B. `T_Orders` = Auftragsliste, `T_Super` = Basis-Table-Klasse fuer alle. + +--- + +## Karte 6 | Basis +**Q:** Welche 4 Audit-Spalten hat JEDE Solution2-Tabelle? +**A:** `created` (TIMESTAMP), `modtime` (TIMESTAMP), `revisor` (INTEGER = Employee-ID), `valid` (BOOLEAN = Soft-Delete). Immer pruefen und setzen! + +--- + +## Karte 7 | Mittel +**Q:** Was ist das Dual-Signature-Pattern? +**A:** Viele Geschaeftsdokumente benoetigen Vier-Augen-Prinzip: `firstsigned` (1. Unterschrift), `needsecondsignature` (braucht 2.?), `secondrevisor` (wer prueft), `secondsigned` (2. Unterschrift erteilt). + +--- + +## Karte 8 | Mittel +**Q:** Was ist `oSuperData`? +**A:** Die Basis-Superklasse fuer fast alle Business-Logik-Objekte (`o`-Klassen). Enthaelt gemeinsame Methoden fuer Datenzugriff, Validierung, Navigation. + +--- + +## Karte 9 | Mittel +**Q:** Wie funktioniert das RBAC (Berechtigungssystem) in Solution2? +**A:** Form-Path-basiert! Berechtigungsgruppen werden Navigation-Pfaden zugewiesen. `T_ModuleAccess2Methods` steuert den Zugriff auf Modul-Methoden. Nicht per Screen, sondern per Modul. + +--- + +## Karte 10 | Mittel +**Q:** Welche Library enthaelt den Login und die Navigation? +**A:** `solution2` (die Haupt-Library). Klassen: `wLogin`, `oLogin`, `oLogon`, `oNavigate`, `wEnterprise`. + +--- + +## Karte 11 | Mittel +**Q:** Was macht die `solution2Services`-Library? +**A:** Hintergrund-/Service-Layer: Push-Notifications, Warehouse-Success-Handler, Order-Callbacks, Container-Management, Zugriffskontrolle. + +--- + +## Karte 12 | Mittel +**Q:** Wie heisst der Auftrags-zu-Rechnung-Prozess? +**A:** Orders -> Lieferschein (deliverynoteitems) -> Rechnungsfreigabe (customerinvoicerequirement mit invoicingallowed=true) -> Rechnung (customerinvoices) -> Buchhaltungsexport. + +--- + +## Karte 13 | Fortgeschritten +**Q:** Was ist die `MobileWork`-Library? +**A:** Der Web/Mobile-Client von Solution2, gebaut mit Omnis JS Remote Forms. Klassen: `jsMainForm`, `jsMainFrame`, `jsMainMenu`. Laeuft im Browser. + +--- + +## Karte 14 | Fortgeschritten +**Q:** Was sind die wichtigsten Tabellen im Verkauf? +**A:** `orders` (Auftraege, 103 Spalten!), `orderitems` (Positionen), `deliverynoteitems` (Lieferschein), `customerinvoices` (Rechnungen, 90 Spalten), `customerinvoiceitems` (Rechnungspositionen). + +--- + +## Karte 15 | Fortgeschritten +**Q:** Was ist ZUGFeRD/Factur-X in Solution2? +**A:** E-Rechnungsformate. Solution2 erzeugt und verarbeitet elektronische Rechnungen ueber `oOmnis2Zugferd` und `oXML_eInvoice` in der AdminAccounting-Library. Pflicht in EU seit 2025! + +--- + +## Karte 16 | Fortgeschritten +**Q:** Was ist die `BasicTools`-Library? +**A:** Shared Utilities: HTML-Generierung (`cHTML`), Datei-Import (`oImportFile`), Edit-Utilities (`oEdit`), Fehleranzeige. + +--- + +## Karte 17 | Mittel +**Q:** In welcher Library ist der Einkauf? +**A:** `Purchase` - Bestellungen, Lieferanten, Wareneingaenge, E-Rechnungen. Klassen: `wSupplierInvoices`, `wPurchaseOrderStockReceipt`, `T_SupplierInvoices`. + +--- + +## Karte 18 | Mittel +**Q:** Wie heissen die Klassen fuer die Fertigung? +**A:** In der `Manufacturing`-Library: `oPartList` (Stueckliste), `rCalculateProductCosts` (Kalkulation), `scResourceSpecification` (Ressourcen). + +--- + +## Karte 19 | Fortgeschritten +**Q:** Was ist `T_SuperVersion`? +**A:** Erweiterte Basis-Table-Klasse mit Versionierung. Fuer Tabellen die eine Aenderungshistorie benoetigen. + +--- + +## Karte 20 | Fortgeschritten +**Q:** Wie werden Custom Fields (Zusatzfelder) in Solution2 umgesetzt? +**A:** Ueber die Tabellen `additionalfields` (Felddefinition: Typ, Zuordnung, Druck-Flags) und `additionalfieldsgroups` (Gruppierung). Koennen an verschiedene Entitaeten gehaengt werden (Auftraege, Teile, Fertigungsschritte...). + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-sql.md b/edu/content/flashcards/deck-sql.md new file mode 100644 index 0000000..aa2489b --- /dev/null +++ b/edu/content/flashcards/deck-sql.md @@ -0,0 +1,136 @@ +# Flashcard Deck: SQL & Datenbankzugriff in Omnis + +**Thema**: SQL-Zugriff ueber DAMs (Database Access Modules) +**Karten**: 15 + +--- + +## Karte 1 | Basis +**Q:** Wie verbindet sich Omnis mit einer Datenbank? +**A:** Ueber Session-Objekte und DAMs (Database Access Modules). Erst Session erstellen, dann `$logon()`: +```omnis +Do lSess.$logon('host', 'user', 'pass', 'sessName') Returns #F +``` + +--- + +## Karte 2 | Basis +**Q:** Was ist der Unterschied zwischen `$prepare/$execute` und `$execdirect`? +**A:** `$prepare`+`$execute` = vorbereitete Abfrage (parameterisiert, wiederverwendbar, sicher). `$execdirect` = direkte Ausfuehrung (einmalig). Fuer SELECT immer `$prepare/$execute` bevorzugen! + +--- + +## Karte 3 | Basis +**Q:** Wie holst du Daten aus einer SQL-Abfrage in eine Omnis-Liste? +**A:** +```omnis +Do lStmt.$prepare('SELECT * FROM contact WHERE valid = :1') Returns #F +Do lStmt.$execute(kTrue) Returns #F +Do lStmt.$fetch(lResultList, kFetchAll) Returns #F +``` + +--- + +## Karte 4 | Basis +**Q:** Was bedeutet `:1`, `:2` etc. in SQL-Statements? +**A:** Bind-Variablen (Parameter). `:1` = erster Parameter, `:2` = zweiter etc. Werden bei `$execute` der Reihe nach uebergeben. **SICHER** gegen SQL-Injection! + +--- + +## Karte 5 | Mittel +**Q:** Was ist der Unterschied zwischen `@[lVar]` und `[lVar]` in Omnis-SQL? +**A:** `@[lVar]` = Bind-Variable (sicher, parametrisiert). `[lVar]` = direkte Text-Substitution (UNSICHER, SQL-Injection moeglich!). **Immer `@[lVar]` verwenden!** + +--- + +## Karte 6 | Mittel +**Q:** Was macht `kFetchAll` vs `kFetchOne`? +**A:** `kFetchAll` holt ALLE Ergebniszeilen in eine Liste. `kFetchOne` holt nur EINE Zeile (in eine Row oder die naechste Zeile einer Liste). Fuer grosse Ergebnismengen: `kFetchOne` in Schleife fuer bessere Speicher-Kontrolle. + +--- + +## Karte 7 | Mittel +**Q:** Wie fuehrst du ein INSERT aus? +**A:** +```omnis +Do lStmt.$execdirect(con('INSERT INTO mytable (name, created) VALUES (@[lName], @[#D])')) Returns #F +``` +`#D` = aktuelles Datum. Oder mit prepare: `$prepare('INSERT INTO mytable (name) VALUES (:1)')` -> `$execute(lName)` + +--- + +## Karte 8 | Mittel +**Q:** Wie machst du ein UPDATE? +**A:** +```omnis +Do lStmt.$prepare('UPDATE orders SET status = :1, modtime = :2 WHERE orderid = :3') Returns #F +Do lStmt.$execute('completed', #T, lOrderID) Returns #F +``` +`#T` = aktueller Timestamp. + +--- + +## Karte 9 | Mittel +**Q:** Wie pruefst du ob eine SQL-Operation erfolgreich war? +**A:** Jede SQL-Methode setzt `#F` (den Flag). `Returns #F` faengt den Wert auf: +```omnis +Do lStmt.$execute(lID) Returns #F +If flag true + # Erfolg +Else + # Fehler - lSess.$lasterror fuer Details +End If +``` + +--- + +## Karte 10 | Fortgeschritten +**Q:** Was ist `$lasterror` und `$lasterrortext`? +**A:** Session-Properties die den letzten SQL-Fehler enthalten. `lSess.$lasterror` = Fehlercode, `lSess.$lasterrortext` = Fehlerbeschreibung. Immer bei `#F = false` pruefen! + +--- + +## Karte 11 | Fortgeschritten +**Q:** Wie machst du eine Transaktion in Omnis? +**A:** +```omnis +Do lSess.$begin() Returns #F ;; BEGIN TRANSACTION +# ... SQL Operationen ... +If lAllesOK + Do lSess.$commit() Returns #F ;; COMMIT +Else + Do lSess.$rollback() Returns #F ;; ROLLBACK +End If +``` + +--- + +## Karte 12 | Fortgeschritten +**Q:** Was ist ein "DAM" in Omnis? +**A:** Database Access Module - ein Treiber fuer verschiedene DB-Systeme. Wichtige DAMs: `PGSQLDAM` (PostgreSQL), `OABORDAM` (Oracle), `MYSQLDAM` (MySQL), `ODBCDAM` (ODBC). Solution2 nutzt `PGSQLDAM`. + +--- + +## Karte 13 | Fortgeschritten +**Q:** Wie definierst du die Spalten einer Liste aus einem SQL-Ergebnis automatisch? +**A:** Entweder `$getAllCols()` nach dem Fetch, oder `$definefromsqlclass('scMySchema')` vor dem Fetch. Automatisch: Omnis erkennt die Spalten beim Fetch von selbst, wenn die Liste leer ist. + +--- + +## Karte 14 | Fortgeschritten +**Q:** Was ist der Unterschied zwischen Server-Side SQL und Client-Side SQL in Remote Forms? +**A:** Server-Side: SQL laeuft auf dem Omnis-Server (normal). Client-Side: Geht NICHT - der JS-Client im Browser hat keinen DB-Zugriff. Alle Daten muessen ueber Server-Methoden geladen werden. + +--- + +## Karte 15 | Fortgeschritten +**Q:** Wie wird in Solution2 die DB-Verbindung verwaltet? +**A:** Ueber die `DBAccess`-Library. Sie verwaltet Sessions, Berechtigungen und Connection-Pooling. Die Session wird beim Login erstellt (`oLogin`/`oLogon`) und ueber `ioDBAccessStartup` im Navigator initialisiert. + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/flashcards/deck-windows.md b/edu/content/flashcards/deck-windows.md new file mode 100644 index 0000000..b09758f --- /dev/null +++ b/edu/content/flashcards/deck-windows.md @@ -0,0 +1,170 @@ +# Flashcard Deck: Fensterprogrammierung & SubWindows + +**Thema**: Window-Hierarchie, SubWindow-Muster, Command-Delegation in Solution2 +**Karten**: 25 + +--- + +## Karte 1 | Basis +**Q:** Welche 3 Fenster-Superklassen hat Solution2? +**A:** `wBaseWindow` (Wurzel), `wMainWindow` (Hauptfenster, erbt von wBaseWindow), `wSubWindow` (eingebettete Panels, erbt auch von wBaseWindow). Alle in `solution2/SuperClasses/`. + +--- + +## Karte 2 | Basis +**Q:** Was ist `wBaseWindow`? +**A:** Die Wurzel-Superklasse ALLER Solution2-Fenster (hat KEINE eigene Superklasse). Definiert gemeinsame Infrastruktur: `ioCode`, `iiRef2SubWindow`, `ibControl2SubWindow`, `ilList`, `irRow`, `inEDMode`, `icCurrTabPane`. + +--- + +## Karte 3 | Basis +**Q:** Was ist `wMainWindow`? +**A:** Superklasse fuer eigenstaendige Dateneingabe-Fenster (z.B. wOrders, wParts). Erbt von `wBaseWindow`. Fuegt hinzu: `irRowCount`, `irRowMax`, `irStatusBarPane`. Ruft bei Commands immer `$closeRelatedWindow()` auf. + +--- + +## Karte 4 | Basis +**Q:** Was ist `wSubWindow`? +**A:** Superklasse fuer eingebettete Fenster-Panels (in Tabs/Paged Panes). Erbt von `wBaseWindow`. Fuegt hinzu: `iiRef2Parent` (Rueck-Referenz), `icFormWindow` (Rechte), `icSubWindow`, `iiRef2Window`. Ermoeglicht rekursive Verschachtelung. + +--- + +## Karte 5 | Kritisch +**Q:** Was macht `ibControl2SubWindow`? +**A:** DER zentrale Schalter! Wenn `kTrue`: Commands gehen an `iiRef2SubWindow`. Wenn `kFalse`: Commands gehen an `ioCode` (Business-Logik). Wird von `$setControl2SubWindow()` gesetzt/zurueckgesetzt. + +--- + +## Karte 6 | Kritisch +**Q:** Was passiert in `wSubWindow.$construct`? +**A:** 1) `Do inherited` (wBaseWindow-Init), 2) `Set reference iiRef2SubWindow` auf eigenes SubWindow-Feld, 3) `Set reference iiRef2Parent` auf pRef2Parent, 4) `$translateObjects`, 5) `icFormWindow` merken, 6) **`iiRef2Parent.$setControl2SubWindow(kTrue)`** = Kontrolle uebernehmen!, 7+8) Row/List-Namen setzen. + +--- + +## Karte 7 | Kritisch +**Q:** Was passiert in `wSubWindow.$destruct`? +**A:** 1) `iiRef2Parent.$setControl2SubWindow(kFalse)` = Kontrolle ZURUECKGEBEN!, 2) `ilList.$define()` = Liste leeren, 3) `irRow.$define()` = Row leeren, 4) `irRowSearch.$define()` = Such-Row leeren. **Ohne diesen Code denkt der Parent das SubWindow existiert noch!** + +--- + +## Karte 8 | Kritisch +**Q:** Wie sieht das Command-Delegation-Pattern aus? +**A:** Auf JEDEM Fenster identisch: +```omnis +If $cinst.ibControl2SubWindow + Do iiRef2SubWindow.$cXxx() ## -> SubWindow +Else + Do $cinst.ioCode.$cXxx() ## -> Business-Logik +End If +``` +Gilt fuer: $cEdit, $cInsert, $cSave, $cCancel, $cDelete, $cPrint, $cAudit, $cNextVersion. + +--- + +## Karte 9 | Wichtig +**Q:** Was ist die bidirektionale Referenz-Kette? +**A:** Jedes SubWindow hat `iiRef2Parent` (zeigt auf den Elter) UND der Elter hat `iiRef2SubWindow` (zeigt auf das Kind). So koennen Commands abwaerts und TabPane-Sperren aufwaerts fliessen. + +--- + +## Karte 10 | Wichtig +**Q:** Wie fliesst ein Edit-Command durch die Hierarchie? +**A:** `wMainWindow.$cEdit` -> prueft `ibControl2SubWindow=kTrue` -> `wSubWindow.$cEdit` (Ebene 1) -> prueft `ibControl2SubWindow` -> entweder an Sub-SubWindow ODER an `ioCode.$cEdit()`. Der Command "sinkt" bis zur tiefsten aktiven Ebene. + +--- + +## Karte 11 | Wichtig +**Q:** Was macht `$closeRelatedWindow()` und wann wird es aufgerufen? +**A:** Schliesst alle zugehoerigen Popup-Fenster. Wird auf `wMainWindow` IMMER VOR der Command-Delegation aufgerufen. Auf `wSubWindow` wird es NICHT aufgerufen - der Unterschied zwischen Main und Sub! + +--- + +## Karte 12 | Wichtig +**Q:** Wie funktioniert `$EnableTabPane`? +**A:** SubWindow ruft `iiRef2Parent.$EnableTabPane($cinst.inEDMode)` auf -> Parent ruft `$cwind.$objs.TP.$enableTabPanes(pEDMode)` auf -> Tab-Reiter werden gesperrt/freigegeben. Die Sperre fliesst AUFWAERTS in der Hierarchie! + +--- + +## Karte 13 | Wichtig +**Q:** Wie heisst das TabPane-Objekt in Solution2 standardmaessig? +**A:** `TP` - das ist Konvention! Zugriff ueber `$cwind.$objs.TP.$enableTabPanes(pEDMode)`. + +--- + +## Karte 14 | Wichtig +**Q:** Wie schliesst sich ein SubWindow? +**A:** Es schliesst sich NICHT selbst! `wSubWindow.$closeWindow` delegiert an: `Do iiRef2Parent.$closeWindow()`. Der Parent entscheidet ueber das Schliessen des gesamten Fensters. + +--- + +## Karte 15 | Mittel +**Q:** Was ist `ioCode`? +**A:** Das Business-Logik-Objekt (z.B. `oOrders`, `oOrderItems`). Es ist der "letzte Stopp" fuer Commands wenn kein aktives SubWindow existiert. Wird als Instanzvariable im Fenster definiert und enthaelt Methoden wie `$cEdit()`, `$cSave()`, `$cDelete()`. + +--- + +## Karte 16 | Mittel +**Q:** Was ist `icFormWindow`? +**A:** Der Name des Formulars, der im `$construct` aus `pFormWiindow` (ja, mit Tippfehler im Original!) gesetzt wird. Wird fuer Pfad- und Rechtebestimmung (RBAC) verwendet. + +--- + +## Karte 17 | Mittel +**Q:** Wie weit kann die SubWindow-Verschachtelung gehen? +**A:** Theoretisch unbegrenzt (rekursiv). Jedes SubWindow kann ein eigenes SubWindow-Feld haben. Praktisches Beispiel: `wOrders` (Main) -> `wOrder` (Sub, Details) -> `wOrderItems` (SubSub, Positionen). Typisch: 2-3 Ebenen. + +--- + +## Karte 18 | Mittel +**Q:** Was macht `$setControl2SubWindow(pBool)`? +**A:** Setzt `ibControl2SubWindow` auf den uebergebenen Wert. Wird von SubWindows aufgerufen: `kTrue` in `$construct` (Kontrolle uebernehmen), `kFalse` in `$destruct` (Kontrolle zurueckgeben). + +--- + +## Karte 19 | Mittel +**Q:** Was macht `wOrder.$SubWindowSavesAttachments`? +**A:** Prueft welches SubWindow aktiv ist (`iiRef2SubWindow.$classname`) und ruft je nach Typ verschiedene Save-Methoden auf. Zeigt: Man kann per `$classname` auf dem SubWindow verschiedene Aktionen ausloesen. + +--- + +## Karte 20 | Mittel +**Q:** Was ist der Unterschied bei Cross-Library-Vererbung? +**A:** Innerhalb der `solution2`-Library: `wSubWindow` reicht. Aus anderen Libraries (Sales, Manufacturing, etc.): `solution2.wSubWindow` mit Library-Prefix noetig! Beispiel: `wOrder extends solution2.wSubWindow`. + +--- + +## Karte 21 | Fortgeschritten +**Q:** Was passiert wenn ein SubWindow die Kontrolle ABLEHNT? +**A:** Es ruft sofort `$setControl2SubWindow(kFalse)` im `$construct` auf. Beispiel: `wWebPortal.$construct` macht `Do pRef2Parent.$setControl2SubWindow(kFalse)`. Das Fenster ist dann nur ein Anzeige-Panel ohne Command-Routing. + +--- + +## Karte 22 | Fortgeschritten +**Q:** Was macht `wOrderItems.$CallParentDoCommand`? +**A:** Delegiert einen Command AUFWAERTS: `Do iiRef2Parent.$CallDoCommand`. Damit kann ein SubWindow seinen Parent bitten, einen bestimmten Command auszufuehren - die umgekehrte Richtung vom normalen Fluss! + +--- + +## Karte 23 | Fortgeschritten +**Q:** Was passiert wenn $destruct das `$setControl2SubWindow(kFalse)` vergisst? +**A:** Das Elternfenster denkt, das SubWindow existiert noch (`ibControl2SubWindow = kTrue`). Alle Commands werden an eine tote Referenz geschickt -> "Method not found"-Fehler. Der haefigste SubWindow-Bug! + +--- + +## Karte 24 | Fortgeschritten +**Q:** Wie debuggt man Command-Routing-Probleme? +**A:** 1) `Calculate lDebug as $cinst.ibControl2SubWindow` pruefen, 2) `iiRef2SubWindow` auf NULL pruefen, 3) Breakpoint in `$cEdit`/`$cCancel` des SubWindows setzen, 4) Pruefen ob `$construct` mit `Do inherited` aufgerufen wird, 5) Pruefen ob Superklasse korrekt ist (`wSubWindow` vs `wBaseWindow`). + +--- + +## Karte 25 | Fortgeschritten +**Q:** Was sind die 7 goldenen Regeln der Fensterprogrammierung? +**A:** 1) `ibControl2SubWindow` bestimmt ALLES, 2) $construct baut die Kette auf (`inherited`!), 3) $destruct baut sie ab (Kontrolle zurueckgeben!), 4) Commands fliessen ABWAERTS, 5) TabPane-Sperren fliessen AUFWAERTS, 6) $closeWindow delegiert AUFWAERTS, 7) `ioCode` ist der letzte Stopp. + +--- + +## Spaced Repetition Tracking + +| Karte | Zuletzt | Naechste | Schwierigkeit | +|-------|---------|----------|---------------| +| (wird beim Ueben gefuellt) | | | | diff --git a/edu/content/tutorials/01-omnis-kickstart.md b/edu/content/tutorials/01-omnis-kickstart.md new file mode 100644 index 0000000..0d5e4e3 --- /dev/null +++ b/edu/content/tutorials/01-omnis-kickstart.md @@ -0,0 +1,289 @@ +# Tutorial 1: Omnis Kickstart - Von Null zum ersten Verstaendnis + +**Dauer**: ca. 30 Minuten +**Voraussetzung**: Grundkenntnisse in irgendeiner Programmiersprache (JS, Python, VB, C#, Java...) +**Ziel**: Du verstehst die Grundstruktur von Omnis und kannst einfachen Code lesen + +--- + +## 1. Was ist Omnis Studio? + +Stell dir vor: Visual Studio + Access + Node.js in einem Tool. Das ist Omnis. + +| Was du kennst | Omnis-Aequivalent | +|---------------|-------------------| +| VS Code / IDE | Omnis Studio IDE (integriert) | +| .sln / Projekt | Library (.lbs Datei) | +| Klassen / Dateien | Classes (in der Library) | +| Funktionen | Methods | +| npm packages | Externe Libraries | +| HTML/React | Windows (Desktop) oder Remote Forms (Web) | +| SQL Workbench | Eingebauter SQL Browser | + +**Der groesste Unterschied:** Omnis ist eine **Kommando-basierte Sprache**, keine Expression-basierte. Jede Zeile beginnt mit einem Befehl. + +--- + +## 2. Die Sprache: Befehle statt Ausdruecke + +### In JavaScript: +```javascript +let name = "Harry"; // Deklaration + Zuweisung +let total = price * quantity; // Ausdruck +console.log(name); // Funktionsaufruf +``` + +### In Omnis: +```omnis +Calculate lName as "Harry" ;; Zuweisung mit dem Befehl "Calculate" +Calculate lTotal as lPrice * lQty ;; Berechnung mit "Calculate" +OK message {[lName]} ;; Nachricht anzeigen (wie alert/console.log) +``` + +**Merke:** +- `Calculate` = "Berechne und weise zu" +- Variablen haben Praefix: `l` = lokal, `i` = Instanz, `t` = Task, `p` = Parameter +- `;;` oder `#` fuer Kommentare +- `{[variable]}` fuer String-Interpolation in Texten + +--- + +## 3. Variablen: Das Praefix-System + +In Omnis hat JEDE Variable einen Praefix, der ihren **Scope** anzeigt: + +| Praefix | Scope | Wie in JS/Python | +|---------|-------|-----------------| +| `l` | Lokal (nur diese Methode) | `let x` / lokale Variable | +| `i` | Instanz (dieses Objekt) | `this.x` / `self.x` | +| `t` | Task (globale Session) | Globale Variable | +| `p` | Parameter (Eingabe) | Funktionsparameter | +| `c` | Klassen-Variable (statisch) | `static` / Klassenvariable | + +```omnis +# Beispiel: Methode mit Parameter und lokaler Variable +Calculate lFullName as con(pFirstName, " ", pLastName) +Calculate iCustomerName as lFullName ;; In Instanz-Var speichern +``` + +**Uebung 1:** Was ist der Scope dieser Variablen? +- `lCounter` -> ??? +- `iOrderList` -> ??? +- `tCurrentUser` -> ??? +- `pAmount` -> ??? + +
+Loesung + +- `lCounter` -> Lokal (nur in der aktuellen Methode) +- `iOrderList` -> Instanz (gehoert zum Objekt) +- `tCurrentUser` -> Task (global in der Session) +- `pAmount` -> Parameter (wurde der Methode uebergeben) + +
+ +--- + +## 4. Datentypen + +| Omnis Typ | Konstante | Wie in JS | +|-----------|-----------|-----------| +| Character (String) | kCharacter | string | +| Integer | kInteger | number (int) | +| Number (Dezimal) | kNumber | number (float) | +| Boolean | kBoolean | boolean | +| Date | kDate | Date | +| List | kList | Array of Objects | +| Row | kRow | Object / Record | +| Object Reference | kObjectref | Referenz auf ein Objekt | + +```omnis +# Variablen werden im IDE deklariert (nicht im Code!) +# Im IDE: Variable "lName" anlegen, Typ = Character +# Dann im Code: +Calculate lName as "Harry" +Calculate lAge as 42 +Calculate lActive as kTrue +``` + +**Wichtig:** Variablen werden im Omnis IDE deklariert (wie in VB mit Dim), NICHT im Code! + +--- + +## 5. Kontrollstrukturen + +### If / Else +```omnis +;; JavaScript: if (age >= 18) { ... } else { ... } +;; Omnis: +If lAge >= 18 + OK message {Erwachsen} +Else + OK message {Minderjaehrig} +End If +``` + +### For-Schleife +```omnis +;; JavaScript: for (let i = 1; i <= 10; i++) { ... } +;; Omnis: +For lCounter from 1 to 10 step 1 + Calculate lSum as lSum + lCounter +End For +``` + +### While-Schleife +```omnis +;; JavaScript: while (condition) { ... } +;; Omnis: +While lCounter < 100 + Calculate lCounter as lCounter + 1 +End While +``` + +### Switch/Case +```omnis +;; JavaScript: switch(status) { case 'A': ... } +;; Omnis: +Switch lStatus + Case 'A' + # Aktiv + Case 'I' + # Inaktiv + Default + # Unbekannt +End Switch +``` + +**Uebung 2:** Uebersetze diesen JavaScript-Code in Omnis: +```javascript +let sum = 0; +for (let i = 1; i <= 100; i++) { + if (i % 2 === 0) { + sum += i; + } +} +console.log("Summe: " + sum); +``` + +
+Loesung + +```omnis +Calculate lSum as 0 +For lI from 1 to 100 step 1 + If mod(lI, 2) = 0 + Calculate lSum as lSum + lI + End If +End For +OK message {Summe: [lSum]} +``` + +
+ +--- + +## 6. Methoden (Funktionen) + +In Omnis sind Methoden immer Teil einer Klasse (wie in Java/C#, nie wie freie Funktionen in JS/Python). + +### Methode aufrufen: +```omnis +;; JavaScript: let result = myObject.calculate(10, 20); +;; Omnis: +Do iMyObject.$calculate(10, 20) Returns lResult +``` + +### Das `Do` Kommando: +- `Do` = "Fuehre aus" (wie ein Funktionsaufruf) +- `$methodname` = Dollar-Praefix fuer Methoden +- `Returns lVar` = Fange den Rueckgabewert auf + +### Methode definieren (im IDE): +Methoden werden im Omnis IDE erstellt, nicht im Code deklariert. +```omnis +# Methode "$calculate" mit Parametern pA, pB: +Calculate lResult as pA + pB +Quit method lResult ;; = return lResult +``` + +--- + +## 7. Die $-Notation: Das Herz von Omnis + +Das ist DER groesste Unterschied zu allen anderen Sprachen. In Omnis adressierst du ALLES ueber Dollar-Pfade: + +```omnis +;; Objekt-Property lesen: +Calculate lVal as $cinst.$objs.myField.$contents + +;; Aufgeschluesselt: +;; $cinst = aktuelle Instanz (= this) +;; .$objs = alle UI-Objekte darauf +;; .myField = das Feld namens "myField" +;; .$contents = dessen Inhalt +``` + +### Die wichtigsten $-Pfade: + +| Pfad | Bedeutung | JS-Aequivalent | +|------|-----------|----------------| +| `$cinst` | Aktuelle Instanz | `this` | +| `$cclass` | Aktuelle Klasse | `this.constructor` | +| `$clib` | Aktuelle Library | - | +| `$ctask` | Aktueller Task | - | +| `$objs` | UI-Objekte auf einem Window | `document.querySelectorAll` | +| `$contents` | Wert/Inhalt | `.value` / `.textContent` | +| `$visible` | Sichtbarkeit | `.style.display` | +| `$enabled` | Aktiv/Inaktiv | `.disabled` | + +### Property SETZEN (nicht = sondern $assign!): +```omnis +;; JavaScript: element.style.display = 'none'; +;; Omnis: +Do $cinst.$objs.myField.$visible.$assign(kFalse) + +;; JavaScript: element.value = "Hello"; +;; Omnis: +Do $cinst.$objs.myField.$contents.$assign("Hello") +``` + +**Das ist anfangs verwirrend, wird aber schnell natuerlich!** + +--- + +## 8. Zusammenfassung: Die 7 goldenen Regeln + +1. **Jede Zeile ist ein Befehl**: `Calculate`, `Do`, `If`, `For`, etc. +2. **Variablen haben Scope-Praefixe**: `l`okal, `i`nstanz, `t`ask, `p`arameter +3. **$ ist ueberall**: Methoden, Properties, Pfade - alles mit Dollar +4. **$assign() zum Setzen**: Nie `=` fuer Notation-Properties +5. **Variablen werden im IDE deklariert**: Nicht im Code +6. **`Do` fuer Aufrufe, `Calculate` fuer Berechnungen**: Die zwei Hauptbefehle +7. **`Returns` / `#F` fuer Ergebnisse**: Rueckgabewerte und der globale Flag + +--- + +## Mini-Quiz: Teste dich selbst! + +**Frage 1:** Was macht dieser Code? +```omnis +Calculate lList as $cinst.$objs.orderGrid.$dataname +``` + +**Frage 2:** Wie liest man die `$visible`-Property eines Feldes namens "btnSave"? + +**Frage 3:** Was ist der Unterschied zwischen `Calculate` und `Do`? + +
+Antworten + +1. Holt den Datennamen (= gebundene Variable) des UI-Elements "orderGrid" auf der aktuellen Instanz. +2. `Calculate lVisible as $cinst.$objs.btnSave.$visible` +3. `Calculate` = Berechnung/Zuweisung, `Do` = Methodenaufruf/Befehlsausfuehrung + +
+ +--- + +**Naechstes Tutorial:** [02 - Notation Mastery](02-notation-mastery.md) - Tiefer eintauchen in das $-Notationssystem diff --git a/edu/content/tutorials/08-window-hierarchy-deepdive.md b/edu/content/tutorials/08-window-hierarchy-deepdive.md new file mode 100644 index 0000000..9a44b4a --- /dev/null +++ b/edu/content/tutorials/08-window-hierarchy-deepdive.md @@ -0,0 +1,457 @@ +# Deep-Dive: Fensterprogrammierung in Solution2 + +## Warum dieses Tutorial? + +Die Fensterprogrammierung in Solution2 ist einer der **komplexesten Bereiche** des Systems. Das liegt am **rekursiven SubWindow-Muster**: Fenster enthalten SubWindows, die wiederum SubWindows enthalten koennen - und alle muessen koordiniert kommunizieren. + +Dieses Tutorial erklaert die Architektur von Grund auf, mit echtem Code aus dem Solution2-Repository. + +--- + +## 1. Die 3-Stufen-Klassenhierarchie + +Solution2 hat **drei zentrale Fenster-Superklassen**, alle in der `solution2`-Library unter `SuperClasses`: + +``` +wBaseWindow <-- Wurzel (keine Superklasse) + | + +-- wMainWindow <-- Hauptfenster (Auftraege, Teile, Rechnungen...) + | + +-- wSubWindow <-- Eingebettete Panels (Tab-Inhalte, Detail-Ansichten) +``` + +### Visuelle Darstellung der Architektur + +``` ++============================================================+ +| wMainWindow (z.B. Sales/wOrders) | +| extends wBaseWindow | +| | +| Instanzvariablen: | +| ioCode -> oOrders (Business-Logik) | +| iiRef2SubWindow -> Referenz auf aktives SubWindow | +| ibControl2SubWindow -> true/false (Wer hat Kontrolle?) | +| ilList, irRow -> Haupt-Datenliste/Zeile | +| inEDMode -> Aktueller Bearbeitungsmodus | +| | +| +------------------------------------------------------+ | +| | TabPane "TP" | | +| | | | +| | +-----------------------------------------------+ | | +| | | wSubWindow (z.B. Sales/wOrder) | | | +| | | extends wBaseWindow | | | +| | | | | | +| | | Instanzvariablen: | | | +| | | iiRef2Parent -> zurueck zum MainWindow | | | +| | | iiRef2SubWindow -> eigenes SubWindow | | | +| | | ibControl2SubWindow -> true/false | | | +| | | ioCode -> eigene Business-Logik | | | +| | | icFormWindow -> Name fuer Rechtepruefung | | | +| | | | | | +| | | +------------------------------------------+ | | | +| | | | wSubWindow (Ebene 3, z.B. wOrderItems) | | | | +| | | | extends wBaseWindow | | | | +| | | | | | | | +| | | | iiRef2Parent -> zurueck zu wOrder | | | | +| | | | ioCode -> oOrderItems | | | | +| | | +------------------------------------------+ | | | +| | +-----------------------------------------------+ | | +| +------------------------------------------------------+ | ++============================================================+ +``` + +--- + +## 2. wBaseWindow - Die Wurzel + +`wBaseWindow` ist die Wurzel-Superklasse ALLER Solution2-Fenster. Sie definiert die **gemeinsame Infrastruktur**: + +### Wichtige Instanzvariablen (geerbt von ALLEN Fenstern) + +| Variable | Typ | Bedeutung | +|----------|-----|-----------| +| `ioCode` | Object-Ref | Business-Logik-Objekt (z.B. `oOrders`) | +| `iiRef2SubWindow` | Item-Ref | Referenz auf das aktive SubWindow | +| `ibControl2SubWindow` | Boolean | `kTrue` = SubWindow hat Kontrolle | +| `ilList` | List | Haupt-Datenliste des Fensters | +| `irRow` | Row | Aktuelle Daten-Zeile | +| `irRowSearch` | Row | Such-Kriterien | +| `inEDMode` | Integer | Aktueller Modus (Anzeige/Edit/Insert/...) | +| `icCurrTabPane` | Char | Name des aktuell aktiven Tab-Reiters | + +### Wichtige Methoden auf wBaseWindow + +| Methode | Zweck | +|---------|-------| +| `$EnableTabPane(pEDMode)` | Sperrt/Entsperrt Tab-Reiter basierend auf Modus | +| `$setRowName(pName)` | Setzt den Namen der Row-Variable | +| `$setListName(pName)` | Setzt den Namen der List-Variable | +| `$translateObjects($cinst)` | Uebersetzt UI-Beschriftungen | + +### $EnableTabPane - Code + +```omnis +## wBaseWindow.$EnableTabPane: +# Freigabe und Sperren von TabPane in Abhaengigkeit von angewaehltem Bearbeitungsmodus +Do $cwind.$objs.TP.$enableTabPanes(pEDMode) +``` + +> **Merke**: Das TabPane-Objekt heisst immer `TP` - das ist Konvention in Solution2! + +--- + +## 3. wMainWindow - Das Hauptfenster + +`wMainWindow` erbt von `wBaseWindow` und ist die Superklasse fuer alle **eigenstaendigen Dateneingabe-Fenster** (Auftraege, Teile, Rechnungen, etc.). + +### Zusaetzliche Instanzvariablen + +| Variable | Typ | Bedeutung | +|----------|-----|-----------| +| `irRowCount` | Row | Zaehler fuer Datensaetze | +| `irRowMax` | Row | Maximum-Zaehler | +| `irStatusBarPane` | Row | Statusleisten-Daten | + +### Das Command-Routing-Pattern + +**DAS ist das zentrale Muster!** Jeder Befehl (Edit, Save, Cancel, Delete, Print...) wird nach dem gleichen Schema delegiert: + +```omnis +## wMainWindow.$cCancel: +Do $cinst.$closeRelatedWindow() ## 1. Zugehoerige Fenster schliessen +If $cinst.ibControl2SubWindow ## 2. Wer hat die Kontrolle? + Do iiRef2SubWindow.$cCancel() ## -> SubWindow! +Else + Do $cinst.ioCode.$cCancel() ## -> Business-Logik! +End If +``` + +### Alle Commands folgen diesem Muster + +| Methode | Aktion | +|---------|--------| +| `$cEdit` | Bearbeitung starten | +| `$cInsert` | Neuen Datensatz anlegen | +| `$cSave` | Speichern | +| `$cCancel` | Abbrechen | +| `$cDelete` | Loeschen | +| `$cPrint` | Drucken | +| `$cAudit` | Aenderungshistorie anzeigen | +| `$cNextVersion` | Naechste Version | +| `$cFirst` / `$cLast` | Navigation | + +**Essentielle Erkenntnis**: `wMainWindow` ruft IMMER `$closeRelatedWindow()` VOR der Command-Delegation auf. Das schliesst alle zugehoerigen Popup-Fenster. + +--- + +## 4. wSubWindow - Das eingebettete Fenster + +`wSubWindow` erbt ebenfalls von `wBaseWindow` und ist die Superklasse fuer alle **eingebetteten Panels/Tabs**. Es ist der Schluessel zur rekursiven Architektur. + +### Zusaetzliche Instanzvariablen + +| Variable | Typ | Bedeutung | +|----------|-----|-----------| +| `iiRef2Parent` | Item-Ref | Referenz ZURUECK zum Elternfenster | +| `icFormWindow` | Char | Name des Formulars (fuer Rechte-Check) | +| `icSubWindow` | Char | Name des SubWindow-Felds | +| `iiRef2Window` | Item-Ref | Referenz zum Hauptfenster | + +### Der Lebenszyklus eines SubWindows + +#### Phase 1: $construct - Aufbau + +```omnis +## wSubWindow.$construct: +Do inherited ## 1. Geerbte Initialisierung +Set reference $cinst.iiRef2SubWindow to $cinst.$objs.wSubWindow.$ref ## 2. Eigenes SubWindow finden +Set reference $cinst.iiRef2Parent to pRef2Parent ## 3. Rueck-Referenz zum Eltern setzen +Do $cinst.$translateObjects($cinst) ## 4. UI uebersetzen +Calculate icFormWindow as pFormWiindow ## 5. Formular-Name merken +Do iiRef2Parent.$setControl2SubWindow(kTrue) ## 6. KONTROLLE UEBERNEHMEN! +Do $cinst.$setRowName('irRow') ## 7. Standard Row-Name +Do $cinst.$setListName('ilList') ## 8. Standard List-Name +``` + +> **Schritt 6 ist ENTSCHEIDEND!** Hier meldet sich das SubWindow beim Elternfenster an: "Ab jetzt gehen alle Commands an MICH!" + +#### Phase 2: Command-Delegation (identisch zum MainWindow) + +```omnis +## wSubWindow.$cEdit: +If $cinst.ibControl2SubWindow ## Hat DIESES SubWindow ein eigenes SubWindow? + Do iiRef2SubWindow.$cEdit ## -> Ja: Weiterleiten! +Else + Do $cinst.ioCode.$cEdit() ## -> Nein: Eigene Business-Logik! +End If +``` + +#### Phase 3: $destruct - Aufraemen + +```omnis +## wSubWindow.$destruct: +If iiRef2Parent + Do iiRef2Parent.$setControl2SubWindow(kFalse) ## 1. Kontrolle ZURUECKGEBEN! +End If +Do ilList.$define() ## 2. Liste leeren +Do irRow.$define() ## 3. Row leeren +Do irRowSearch.$define() ## 4. Such-Row leeren +``` + +> **WICHTIG**: Ohne diesen $destruct wuerde das Elternfenster denken, das SubWindow existiert noch! + +#### Phase 4: $closeWindow - Fenster schliessen + +```omnis +## wSubWindow.$closeWindow: +Do iiRef2Parent.$closeWindow() ## Delegiert das Schliessen an das Elternfenster! +``` + +> SubWindows schliessen sich NICHT selbst - sie bitten den Parent, das ganze Fenster zu schliessen! + +--- + +## 5. Die bidirektionale Referenz-Kette + +Das Herzstueck der Architektur ist die **bidirektionale Verkettung**: + +``` +wMainWindow wSubWindow (Ebene 1) wSubWindow (Ebene 2) ++-----------------+ +--------------------+ +------------------+ +| iiRef2SubWindow | --------> | $cinst | | | +| | | iiRef2Parent |----+--------> | (existiert nicht | +| ibControl2Sub- | | iiRef2SubWindow|----|--------> | auf dieser Ebene)| +| Window = kTrue | | | | | ++-----------------+ | ibControl2Sub- | +------------------+ + | Window = kFalse | + +--------------------+ + +Aufwaerts: iiRef2Parent (Kind -> Elter) +Abwaerts: iiRef2SubWindow (Elter -> Kind) +``` + +### Wie die Kontrolle fliesst + +1. **User klickt "Edit"** im Hauptfenster +2. `wMainWindow.$cEdit` wird aufgerufen +3. `ibControl2SubWindow` ist `kTrue` -> an `iiRef2SubWindow.$cEdit` delegieren +4. `wSubWindow.$cEdit` (Ebene 1) wird aufgerufen +5. `ibControl2SubWindow` auf Ebene 1 ist `kFalse` -> `ioCode.$cEdit()` aufrufen +6. Die Business-Logik (`oOrders`) fuehrt die eigentliche Aktion aus + +### Konkretes Beispiel: Sales/wOrders + +``` +wOrders (wMainWindow) + | + +-- iiRef2SubWindow -> wOrder (wSubWindow) + | + +-- iiRef2Parent -> wOrders (zurueck) + +-- iiRef2SubWindow -> wOrderItems (wSubWindow) + | + +-- iiRef2Parent -> wOrder (zurueck) + +-- ioCode -> oOrderItems (Business-Logik) +``` + +**Command-Fluss bei "Edit":** +``` +wOrders.$cEdit + -> ibControl2SubWindow = kTrue + -> wOrder.$cEdit + -> ibControl2SubWindow = kTrue (falls OrderItems aktiv) + -> wOrderItems.$cEdit + -> ibControl2SubWindow = kFalse + -> oOrderItems.$cEdit() <-- HIER wird die Aktion ausgefuehrt! +``` + +--- + +## 6. Das $EnableTabPane-Pattern + +Wenn ein SubWindow in den Edit-Modus wechselt, muessen die Tab-Reiter gesperrt werden - und zwar **aufwaerts in der Hierarchie**! + +### Im SubWindow (z.B. wAdditionalCosts): +```omnis +## wAdditionalCosts.$EnableTabPane: +# Verriegelt Reiter der TabPane gegeneinander +# und ruft im Elternfenster die Methode $EnablePane +Do iiRef2Parent.$EnableTabPane($cinst.inEDMode) +``` + +### Im wBaseWindow: +```omnis +## wBaseWindow.$EnableTabPane: +Do $cwind.$objs.TP.$enableTabPanes(pEDMode) +``` + +**Ablauf:** +1. SubWindow wechselt in Edit-Modus (`inEDMode = kEdit`) +2. SubWindow ruft `iiRef2Parent.$EnableTabPane(kEdit)` +3. Parent sperrt seine TabPane-Reiter (`TP.$enableTabPanes(kEdit)`) +4. User kann nicht mehr zwischen Tabs wechseln waehrend er editiert! +5. Bei Cancel/Save: `$EnableTabPane(kFalse)` -> Tabs wieder frei + +--- + +## 7. Sonderfaelle und Varianten + +### wWebPortal - Kontrolle ablehnen + +```omnis +## wWebPortal.$construct: +Do inherited +Do pRef2Parent.$setControl2SubWindow(kFalse) ## Kontrolle NICHT uebernehmen! +Do $cinst.$translateObjects($cinst) +``` + +> Manche SubWindows wollen die Kontrolle NICHT haben! Sie sind nur Anzeige-Panels. + +### wOrder.$ActiveWindow.$assign - Upstream-Delegation + +```omnis +## wOrder (Sales).$ActiveWindow.$assign: +Do iiRef2Parent.$ActiveWindow.$assign(pActive) ## CGrid im Elternfenster ausschalten +``` + +> SubWindows koennen auch beliebige Custom-Methoden an den Parent delegieren! + +### wOrderItems.$CallParentDoCommand - Command nach oben + +```omnis +## wOrderItems.$CallParentDoCommand: +Do iiRef2Parent.$CallDoCommand +``` + +> Manchmal muss ein SubWindow den Parent bitten, einen Command auszufuehren. + +### wOrder.$SubWindowSavesAttachments - SubWindow-spezifische Logik + +```omnis +## wOrder.$SubWindowSavesAttachments: +Switch iiRef2SubWindow.$classname + Case 'solution2.wText' + Do iiRef2SubWindow.$SaveData() + Quit method kTrue + Default + Quit method kFalse +End Switch +``` + +> Das SubWindow kann abfragen, WELCHES SubWindow gerade aktiv ist und unterschiedlich reagieren! + +--- + +## 8. Praxis-Anleitung: Eigenes SubWindow erstellen + +### Schritt 1: Klasse anlegen +- Erstelle eine neue Window-Klasse (z.B. `wMeinSubWindow`) +- Setze `wSubWindow` als Superklasse (`solution2.wSubWindow`) + +### Schritt 2: SubWindow-Feld einbetten +- Fuege in das Elternfenster ein **Subwindow-Feld** ein +- Name: `wSubWindow` (Konvention!) +- Setze die `$classname`-Property auf deine neue Klasse + +### Schritt 3: Business-Logik-Objekt +- Erstelle ein Object (z.B. `oMeinCode`) mit den Commands +- Instanziiere es als `ioCode` in deinem SubWindow + +### Schritt 4: $construct ueberschreiben (optional) +```omnis +## wMeinSubWindow.$construct: +Do inherited ## IMMER inherited aufrufen! Setzt die Referenzen! +# ... eigene Initialisierung ... +``` + +### Schritt 5: Commands implementieren +Wenn dein SubWindow eigene Commands braucht: +```omnis +## wMeinSubWindow.$cEdit: +Do inherited ## Oder: eigene Logik wenn noetig +# ... zusaetzliche Aktionen ... +``` + +### Schritt 6: $EnableTabPane ueberschreiben (wenn noetig) +```omnis +## wMeinSubWindow.$EnableTabPane: +Do iiRef2Parent.$EnableTabPane($cinst.inEDMode) +``` + +--- + +## 9. Haeufige Fehler und Debugging + +### Fehler 1: "Method not found" bei Command-Aufruf +**Ursache**: Das SubWindow hat `ibControl2SubWindow = kTrue`, aber das referenzierte SubWindow existiert nicht mehr. +**Loesung**: Pruefe ob `$destruct` korrekt `$setControl2SubWindow(kFalse)` aufruft. + +### Fehler 2: Tabs lassen sich nicht sperren +**Ursache**: `$EnableTabPane` ruft den Parent nicht auf. +**Loesung**: `Do iiRef2Parent.$EnableTabPane($cinst.inEDMode)` im SubWindow. + +### Fehler 3: Aenderungen gehen verloren beim Tab-Wechsel +**Ursache**: Der Tab-Wechsel zerstoert das SubWindow, aber die Daten wurden nicht gespeichert. +**Loesung**: Im `$destruct` oder vor dem Tab-Wechsel `$cSave` aufrufen. + +### Fehler 4: Commands landen im falschen Fenster +**Ursache**: `ibControl2SubWindow` ist nicht korrekt gesetzt. +**Debug**: Pruefe zur Laufzeit: `Calculate lDebug as $cinst.ibControl2SubWindow` + +### Fehler 5: SubWindow erhaelt keine Daten +**Ursache**: Die bidirektionale Referenz wurde nicht aufgebaut. +**Loesung**: Sicherstellen dass `Do inherited` in `$construct` aufgerufen wird! + +### Fehler 6: "Inherited does nothing" +**Ursache**: Die Superklasse ist falsch gesetzt (z.B. `wBaseWindow` statt `wSubWindow`). +**Loesung**: Superklasse pruefen: Muss `solution2.wSubWindow` sein (mit Library-Prefix bei cross-library!). + +--- + +## 10. Zusammenfassung: Die 7 goldenen Regeln + +1. **`ibControl2SubWindow` bestimmt ALLES** - Es entscheidet wohin Commands gehen +2. **$construct baut die Kette auf** - `inherited` IMMER aufrufen! +3. **$destruct baut die Kette ab** - Kontrolle zurueckgeben! +4. **Commands fliessen ABWAERTS** - MainWindow -> SubWindow -> SubSubWindow +5. **TabPane-Sperren fliessen AUFWAERTS** - SubWindow -> Parent -> MainWindow +6. **$closeWindow delegiert AUFWAERTS** - SubWindows schliessen sich nie selbst +7. **ioCode ist der letzte Stopp** - Wenn kein SubWindow aktiv ist, macht ioCode die Arbeit + +--- + +## 11. Referenz: Alle delegierten Commands + +### Auf wMainWindow (mit $closeRelatedWindow vorab): +`$cEdit`, `$cCancel`, `$cAudit`, `$cNextVersion` + +### Auf wSubWindow (ohne $closeRelatedWindow): +`$cEdit`, `$cCancel`, `$cInsert`, `$cDelete`, `$cSave`, `$cPrint`, `$cAudit`, `$cNextVersion`, `$cFirst`, `$cLast` + +### Gemeinsames Pattern: +```omnis +If $cinst.ibControl2SubWindow + Do iiRef2SubWindow.$cXxx() ## Weiterleiten +Else + Do $cinst.ioCode.$cXxx() ## Selbst ausfuehren +End If +``` + +--- + +## 12. Cross-Library Verwendung + +SubWindows werden oft **library-uebergreifend** genutzt: + +| Library | Klasse | Superklasse | +|---------|--------|-------------| +| `Sales` | `wOrder` | `solution2.wSubWindow` | +| `Sales` | `wOrderItems` | `solution2.wSubWindow` | +| `Manufacturing` | `wResourceBasicData` | `solution2.wSubWindow` | +| `Manufacturing` | `wVariantsExclLock` | `solution2.wSubWindow` | +| `solution2` | `wReminder` | `wSubWindow` | +| `solution2` | `wAdditionalCosts` | `wSubWindow` | +| `solution2` | `wRTF` | `wSubWindow` | +| `solution2` | `wNotice` | `wSubWindow` | +| `solution2` | `wNoticeboard` | `wSubWindow` | + +> **Beachte**: Innerhalb der `solution2`-Library reicht `wSubWindow`. Bei Cross-Library-Vererbung muss `solution2.wSubWindow` angegeben werden! diff --git a/edu/content/tutorials/onboarding-path.md b/edu/content/tutorials/onboarding-path.md new file mode 100644 index 0000000..9e2e0a0 --- /dev/null +++ b/edu/content/tutorials/onboarding-path.md @@ -0,0 +1,121 @@ +# Onboarding-Plan: Vom Programmierer zum Solution2/Omnis-Entwickler + +**Gesamtdauer**: ca. 4-6 Wochen (bei ca. 4h/Tag Lernzeit) +**Voraussetzung**: Erfahrung in mindestens einer Programmiersprache + +--- + +## Woche 1: Omnis Grundlagen + +### Tag 1-2: Orientierung +- [ ] Tutorial 01: Omnis Kickstart durcharbeiten +- [ ] Omnis Studio IDE installieren und starten +- [ ] Ein leeres Projekt (Library) anlegen +- [ ] Flashcards: `deck-commands` - erste 10 Karten lernen +- [ ] **Praxis**: Ein einfaches Window mit einem Button erstellen, der "Hallo Welt" anzeigt + +### Tag 3-4: Notation & Variablen +- [ ] Tutorial 02: Notation Mastery durcharbeiten +- [ ] Cheatsheet "Notation" ausdrucken und neben Monitor haengen +- [ ] Flashcards: `deck-notation` - erste 15 Karten +- [ ] **Praxis**: Window mit 3 Feldern (Name, Alter, Ergebnis) + Button der eine Berechnung macht + +### Tag 5: Listen - Einfuehrung +- [ ] Tutorial 03: Lists & Rows - erste Haelfte +- [ ] Flashcards: `deck-lists` - erste 10 Karten +- [ ] **Praxis**: Eine Liste mit 5 Produkten erstellen und in einem Grid anzeigen + +--- + +## Woche 2: Daten & UI + +### Tag 1-2: Listen Vertiefung +- [ ] Tutorial 03: Lists & Rows - zweite Haelfte +- [ ] Flashcards: `deck-lists` - alle Karten +- [ ] **Praxis**: Produktliste mit Suchen, Sortieren, Hinzufuegen, Loeschen + +### Tag 3-4: SQL & Datenbankzugriff +- [ ] Tutorial 04: SQL and Data Access +- [ ] Flashcards: `deck-sql` +- [ ] PostgreSQL-Zugang zu masterdemo erhalten +- [ ] **Praxis**: Daten aus der `contact`-Tabelle laden und in einer Liste anzeigen + +### Tag 5: UI-Design +- [ ] Tutorial 05: Windows & UI +- [ ] Verschiedene Fenster-Typen ausprobieren (Main, Modal, Sub) +- [ ] **Praxis**: Ein Kontakt-Formular mit Validierung bauen + +--- + +## Woche 3: Solution2 kennenlernen + +### Tag 1-2: Architektur-Ueberblick +- [ ] Tutorial 07: Solution2 Deep Dive +- [ ] Cheatsheet "Solution2" studieren +- [ ] Flashcards: `deck-solution2` +- [ ] Die Library-Struktur in Omnis oeffnen und durchblaettern +- [ ] **Praxis**: Die Login-Klassen (wLogin, oLogin) oeffnen und den Code nachvollziehen + +### Tag 3-4: Einen Geschaeftsprozess nachvollziehen +- [ ] Den Auftragsprozess verfolgen: orders -> deliverynoteitems -> customerinvoices +- [ ] Die Klassen wOrders, oOrders, T_Orders im Code oeffnen +- [ ] Die Superklassen-Hierarchie (wMainWindow, oSuperData, T_Super) verstehen +- [ ] **Praxis**: Einen bestehenden Auftrag in der UI oeffnen und nachvollziehen welche Methoden aufgerufen werden (Debugger nutzen!) + +### Tag 5: Reports & Druckausgabe +- [ ] Report-Klassen (r-Praefix) verstehen +- [ ] Einen bestehenden Report (z.B. rInvoice) oeffnen und analysieren +- [ ] **Praxis**: Einen einfachen Report fuer die Kontaktliste erstellen + +--- + +## Woche 4: Web & Fortgeschrittenes + +### Tag 1-2: Remote Forms (Web-Client) +- [ ] Tutorial 06: Remote Forms +- [ ] Die MobileWork-Library und jsMainForm analysieren +- [ ] Unterschied $event vs $eventclient verstehen +- [ ] **Praxis**: Ein einfaches Remote Form mit Dateneingabe erstellen + +### Tag 3-4: Eigenes Feature entwickeln +- [ ] Aufgabe: Einen kleinen Erweiterung an Solution2 vornehmen (z.B. ein neues Zusatzfeld) +- [ ] Dabei die Solution2-Konventionen beachten (Namenskonventionen, Audit-Spalten, Superklassen) +- [ ] Code Review durch erfahrenen Entwickler + +### Tag 5: Wiederholung & Flashcards +- [ ] Alle Flashcard-Decks wiederholen (Spaced Repetition!) +- [ ] Schwache Bereiche identifizieren und gezielt ueeben +- [ ] **Quiz**: Gemischtes Quiz ueber alle Themen + +--- + +## Woche 5-6: Vertiefung & Spezialisierung + +### Optionale Module (je nach Projekt): +- [ ] E-Rechnung: ZUGFeRD/Factur-X/XRechnung im AdminAccounting-Modul +- [ ] Fertigung: Manufacturing-Library und PPS +- [ ] Qualitaet: QualityInspection-Modul +- [ ] Arbeitszeit: StaffWorkTime-Modul +- [ ] Mobile: MobileWork-Library vertiefen + +### Laufend: +- [ ] Taeglich 15 Min Flashcards (Spaced Repetition) +- [ ] Woechentlich 1 "Code Reading" Session (fremden Solution2-Code lesen und verstehen) +- [ ] Bei jedem Problem: Erst MCP-Suche, dann Docs, dann Kollegen fragen + +--- + +## Erfolgskriterien: Wann bist du "bereit"? + +| Kompetenz | Selbsteinschaetzung | +|-----------|---------------------| +| Kann Omnis-Code lesen und verstehen | [ ] 1-5 | +| Kann einfache Windows/Forms erstellen | [ ] 1-5 | +| Versteht die $-Notation | [ ] 1-5 | +| Kann Listen erstellen und manipulieren | [ ] 1-5 | +| Kann SQL-Abfragen in Omnis schreiben | [ ] 1-5 | +| Kennt die Solution2-Architektur | [ ] 1-5 | +| Kann eine kleine Aenderung selbstaendig umsetzen | [ ] 1-5 | +| Kann den Debugger effektiv nutzen | [ ] 1-5 | + +**Ziel**: Alle Bereiche mindestens 3 von 5. diff --git a/edu/js/markdown.js b/edu/js/markdown.js new file mode 100644 index 0000000..453bd53 --- /dev/null +++ b/edu/js/markdown.js @@ -0,0 +1,56 @@ +'use strict'; + +var Markdown = { + render: function(md) { + if (!md) return ''; + var html = md; + + // Code blocks + html = html.replace(/```(\w*)\n([\s\S]*?)```/g, function(_, lang, code) { + return '
' + Markdown.esc(code.trim()) + '
'; + }); + + // Inline code + html = html.replace(/`([^`]+)`/g, '$1'); + + // Bold + html = html.replace(/\*\*(.+?)\*\*/g, '$1'); + + // Italic + html = html.replace(/\*(.+?)\*/g, '$1'); + + // Headings + html = html.replace(/^### (.+)$/gm, '

$1

'); + html = html.replace(/^## (.+)$/gm, '

$1

'); + html = html.replace(/^# (.+)$/gm, '

$1

'); + + // HR + html = html.replace(/^---$/gm, '
'); + + // Tables + html = html.replace(/^\|(.+)\|\s*\n\|[-| :]+\|\s*\n((?:\|.+\|\s*\n?)*)/gm, function(_, header, rows) { + var ths = header.split('|').map(function(h) { return '' + h.trim() + ''; }).join(''); + var trs = rows.trim().split('\n').map(function(row) { + var tds = row.replace(/^\||\|$/g, '').split('|').map(function(c) { return '' + c.trim() + ''; }).join(''); + return '' + tds + ''; + }).join(''); + return '' + ths + '' + trs + '
'; + }); + + // Lists + html = html.replace(/^- (.+)$/gm, '
  • $1
  • '); + html = html.replace(/(
  • [\s\S]*?<\/li>)/g, function(match) { + if (match.indexOf('