feat: content import system - Markdown parser, importer, 7 decks + 3 tutorials + 5 cheatsheets
Dieser Commit ist enthalten in:
Ursprung
1c59b667f2
Commit
6a504254b0
17 geänderte Dateien mit 2658 neuen und 0 gelöschten Zeilen
145
edu/api/import.php
Normale Datei
145
edu/api/import.php
Normale Datei
|
|
@ -0,0 +1,145 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Content Import - reads Markdown files from content/ and upserts into DB.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function import_all_content($pdo) {
|
||||||
|
$stats = ['decks' => 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, '-');
|
||||||
|
}
|
||||||
81
edu/content/cheatsheets/commands-cheatsheet.md
Normale Datei
81
edu/content/cheatsheets/commands-cheatsheet.md
Normale Datei
|
|
@ -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
|
||||||
|
```
|
||||||
88
edu/content/cheatsheets/notation-cheatsheet.md
Normale Datei
88
edu/content/cheatsheets/notation-cheatsheet.md
Normale Datei
|
|
@ -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() !
|
||||||
|
```
|
||||||
112
edu/content/cheatsheets/solution2-cheatsheet.md
Normale Datei
112
edu/content/cheatsheets/solution2-cheatsheet.md
Normale Datei
|
|
@ -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
|
||||||
|
```
|
||||||
104
edu/content/cheatsheets/sql-cheatsheet.md
Normale Datei
104
edu/content/cheatsheets/sql-cheatsheet.md
Normale Datei
|
|
@ -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
|
||||||
|
```
|
||||||
143
edu/content/cheatsheets/windows-cheatsheet.md
Normale Datei
143
edu/content/cheatsheets/windows-cheatsheet.md
Normale Datei
|
|
@ -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
|
||||||
|
```
|
||||||
163
edu/content/flashcards/deck-commands.md
Normale Datei
163
edu/content/flashcards/deck-commands.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
102
edu/content/flashcards/deck-gotchas.md
Normale Datei
102
edu/content/flashcards/deck-gotchas.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
166
edu/content/flashcards/deck-lists.md
Normale Datei
166
edu/content/flashcards/deck-lists.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
193
edu/content/flashcards/deck-notation.md
Normale Datei
193
edu/content/flashcards/deck-notation.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
132
edu/content/flashcards/deck-solution2.md
Normale Datei
132
edu/content/flashcards/deck-solution2.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
136
edu/content/flashcards/deck-sql.md
Normale Datei
136
edu/content/flashcards/deck-sql.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
170
edu/content/flashcards/deck-windows.md
Normale Datei
170
edu/content/flashcards/deck-windows.md
Normale Datei
|
|
@ -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) | | | |
|
||||||
289
edu/content/tutorials/01-omnis-kickstart.md
Normale Datei
289
edu/content/tutorials/01-omnis-kickstart.md
Normale Datei
|
|
@ -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` -> ???
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Loesung</summary>
|
||||||
|
|
||||||
|
- `lCounter` -> Lokal (nur in der aktuellen Methode)
|
||||||
|
- `iOrderList` -> Instanz (gehoert zum Objekt)
|
||||||
|
- `tCurrentUser` -> Task (global in der Session)
|
||||||
|
- `pAmount` -> Parameter (wurde der Methode uebergeben)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Loesung</summary>
|
||||||
|
|
||||||
|
```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]}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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`?
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Antworten</summary>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Naechstes Tutorial:** [02 - Notation Mastery](02-notation-mastery.md) - Tiefer eintauchen in das $-Notationssystem
|
||||||
457
edu/content/tutorials/08-window-hierarchy-deepdive.md
Normale Datei
457
edu/content/tutorials/08-window-hierarchy-deepdive.md
Normale Datei
|
|
@ -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!
|
||||||
121
edu/content/tutorials/onboarding-path.md
Normale Datei
121
edu/content/tutorials/onboarding-path.md
Normale Datei
|
|
@ -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.
|
||||||
56
edu/js/markdown.js
Normale Datei
56
edu/js/markdown.js
Normale Datei
|
|
@ -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 '<pre><code class="lang-' + lang + '">' + Markdown.esc(code.trim()) + '</code></pre>';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inline code
|
||||||
|
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||||
|
|
||||||
|
// Bold
|
||||||
|
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||||
|
|
||||||
|
// Italic
|
||||||
|
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
||||||
|
|
||||||
|
// Headings
|
||||||
|
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
|
||||||
|
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
|
||||||
|
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
|
||||||
|
|
||||||
|
// HR
|
||||||
|
html = html.replace(/^---$/gm, '<hr>');
|
||||||
|
|
||||||
|
// Tables
|
||||||
|
html = html.replace(/^\|(.+)\|\s*\n\|[-| :]+\|\s*\n((?:\|.+\|\s*\n?)*)/gm, function(_, header, rows) {
|
||||||
|
var ths = header.split('|').map(function(h) { return '<th>' + h.trim() + '</th>'; }).join('');
|
||||||
|
var trs = rows.trim().split('\n').map(function(row) {
|
||||||
|
var tds = row.replace(/^\||\|$/g, '').split('|').map(function(c) { return '<td>' + c.trim() + '</td>'; }).join('');
|
||||||
|
return '<tr>' + tds + '</tr>';
|
||||||
|
}).join('');
|
||||||
|
return '<table><thead><tr>' + ths + '</tr></thead><tbody>' + trs + '</tbody></table>';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
|
||||||
|
html = html.replace(/(<li>[\s\S]*?<\/li>)/g, function(match) {
|
||||||
|
if (match.indexOf('<ul>') === -1) return '<ul>' + match + '</ul>';
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Paragraphs
|
||||||
|
html = html.replace(/^(?!<[huptlo]|<\/|<hr|<pre|<li|<ul)(.+)$/gm, '<p>$1</p>');
|
||||||
|
|
||||||
|
return html;
|
||||||
|
},
|
||||||
|
|
||||||
|
esc: function(str) {
|
||||||
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
};
|
||||||
Laden …
Tabelle hinzufügen
Einen Link hinzufügen
In neuem Issue referenzieren