Vor ein paar Tagen stand ich vor der Aufgabe, eine bestehende Raumbelegungsanwendung (Excel-Sheet) in APEX abzubilden.
Den Apex-Kalenderreport wollte ich nicht verwenden, da er optisch von der abzulösenden Anwendung zu sehr abweicht.
Um das Ergebnis wie im folgenden Bild zu bekommen, ist eine Table based Function nötig, die mir pro Termin eine komplett in HTML gestaltete Tabelle als Report-Zeile ausgibt.
Über die Tabellen, die die eigentlichen Termine und Räume aufnehmen, muß ich nicht viel schreiben. Das kann jeder für sich anpassen, wie er es benötigt.
Hier aber die Funktion “get_zeitstrahl_inline_fâ€, die ich samt der entsprechenden Type-Definition in ein Package “ISC_RAEUME_VERWALTUNG_PCK†gepackt habe.
CREATE OR REPLACE PACKAGE BODY "ISC_RAEUME_VERWALTUNG_PCK" AS FUNCTION get_zeitstrahl_inline_f ( p_raum_id NUMBER, p_view NUMBER) RETURN zeitstrahl_tab pipelined IS l_row isc_raeume_verwaltung_pck.zeitstrahl_row; l_zeit_von NUMBER := null; l_zeit_bis NUMBER := null; l_strahl clob := null; l_strahl1 NUMBER := 0; l_strahl2 NUMBER := 0; l_strahl3 NUMBER := 0; l_ansprechpartner VARCHAR2(255) := ''; l_bemerkung VARCHAR2(4000) := ''; l_zeit1 VARCHAR2(10) := ''; l_zeit2 VARCHAR2(10) := ''; BEGIN -- wir holen uns die Daten des Termins FOR rec IN (SELECT id , zeit_von , zeit_bis , ansprechpartner, bemerkung FROM isc_raeume_verwaltung WHERE raum_id = p_raum_id ) LOOP -- bestimmte werte reseten l_strahl1 := 0; l_strahl2 := 0; l_strahl3 := 0; l_zeit_von := to_number(TO_CHAR(rec.zeit_von, 'SSSSS')/60); l_zeit_bis := to_number(TO_CHAR(rec.zeit_bis, 'SSSSS')/60); l_zeit1 := TO_CHAR(rec.zeit_von, 'HH24:MI'); l_zeit2 := TO_CHAR(rec.zeit_bis, 'HH24:MI'); l_row.id := rec.id; l_row.zeit_von := to_date(rec.zeit_von, 'dd.mm.yy'); l_row.zeit_bis := to_date(rec.zeit_bis, 'dd.mm.yy'); l_strahl := '<table class="zeittable" style="border-collapse: collapse; border-spacing: 0px; width: 840px;">'; l_strahl := l_strahl || '<tr><td style="width:60px; ">07:00</td><td style="width:60px;">08:00</td><td style="width:60px; ">09:00</td><td style="width:60px; ">10:00</td><td style="width:60px; ">11:00</td><td style="width:60px; ">12:00</td><td style="width:60px; ">13:00</td><td style="width:60px; ">14:00</td><td style="width:60px; ">15:00</td><td style="width:60px; ">16:00</td><td style="width:60px; ">17:00</td><td style="width:60px; ">18:00</td><td style="width:60px; ">19:00</td><td style="width:60px; ">20:00</td></tr>'; l_strahl := l_strahl || '</table>'; l_strahl := l_strahl || '<table style="border-collapse: collapse; border-spacing: 0px; width: 840px; font-size: x-small;" class="zeittable2">'; l_strahl := l_strahl || '<tr>'; FOR cur IN 420 .. 1260 LOOP IF cur < l_zeit_von THEN l_strahl1 := l_strahl1 + 1; END IF; IF cur > l_zeit_bis THEN l_strahl3 := l_strahl3 + 1; END IF; IF cur BETWEEN l_zeit_von AND l_zeit_bis THEN l_strahl2 := l_strahl2 + 1; END IF; END LOOP; l_strahl := l_strahl || '<td style="border-collapse: collapse; border-spacing: 0px; width:' || l_strahl1 || 'px;"></td>'; -- darf der Benutzer die Details sehen? IF p_view = 1 THEN l_strahl := l_strahl || '<td style="border-collapse: collapse; border-spacing: 0px;background-color: #00FF00; width:' || l_strahl2 || 'px;"><DIV TITLE="header=['|| l_zeit1 || '-' || l_zeit2 ||'] body=[Ansprechpartner: '|| rec.ansprechpartner || '<br><hr>' || rec.bemerkung || ']" STYLE="COLOR:#000">' || l_zeit1 || ' - ' || l_zeit2 || ' Uhr<br>Ansprechpartner: ' || rec.ansprechpartner || '<br><hr>' || SUBSTR(rec.bemerkung, 1, 100) || '</DIV></td>'; ELSE l_strahl := l_strahl || '<td style="border-collapse: collapse; border-spacing: 0px;background-color: #00FF00; width:' || l_strahl2 || 'px;">' || l_zeit1 || ' - ' || l_zeit2 || ' Uhr</td>'; END IF; l_strahl := l_strahl || '<td style="border-collapse: collapse; border-spacing: 0px; width:' || l_strahl3 || 'px;"></td>'; l_strahl := l_strahl || '</tr></table>'; l_row.strahl := l_strahl; pipe row(l_row); END LOOP; END get_zeitstrahl_inline_f; END isc_raeume_verwaltung_pck; /
Und hier das Package drumherum:
create or replace PACKAGE "ISC_RAEUME_VERWALTUNG_PCK" AS TYPE zeitstrahl_row is record( id number, text varchar2(20), zeit_von date, zeit_bis date, strahl clob ); type zeitstrahl_tab is table of zeitstrahl_row; FUNCTION get_zeitstrahl_inline_f (p_raum_id NUMBER, p_view NUMBER) RETURN zeitstrahl_tab pipelined; END isc_raeume_verwaltung_pck;
Um an die Funktion zu kommen, legt man in APEX eine Report-Region mit folgender Query an:
select v.* from table(isc_raeume_verwaltung_pck.get_zeitstrahl_inline_f(nvl(:P1_RAUM, 1),1)) v order by v.zeit_von
Die Table based Function (TbF) macht folgendes: Sie wird wie eine normale Funktion aufgerufen und bekommt dabei 2 Parameter übergeben. Der 1. Parameter sagt der Funktion, welcher Raum angezeigt werden soll. Der 2. Parameter bestimmt den Informationsgehalt. Bei 1 werden alle Details, bei 0 nur die Uhrzeiten angezeigt. Somit kann man die Funktion für verschiedene Benutzergruppen wiederverwenden.
In der Funktion werden dann alle Termine des gewünschten Raumes aufgerufen. Über Pipe wird eine Tabellenzeile für den Report und dessen SQL-Query zusammengesetzt und von der TbF zurückgeliefert. Die erste Zeile (Codezeile 44-46) ist immer eine Tabelle mit Spalten für die Uhrzeiten (07.00 bis 20.00 Uhr). Per CSS-Datei wird später noch ein Tabellenhintergrundbild eingefügt. Die zweite Zeile (Codezeile 47-69) rechnet anhand der Sekunden des Tages, wann der entsprechende Termin beginnt und nimmt diese Zahlen als Grundlage, um 3 Spalten zu bilden. Eine Spalte ohne Hintergrundfarbe/Text vor dem Termin, das gleiche nochmal nach dem Termin. Die mittlere Spalte der Tabelle wird dann mit Farbe und Text gefüllt, um den Termin optisch darzustellen.
In meiner Produktiv-Anwendung ist noch die Überprüfung auf Terminüberschneidungen eingebunden und es besteht die Möglichkeit, wiederkehrende Termine zu setzen. Die dann selbständig im Kalender angezeigt werden.
Die Beispielanwendung (Username: guest, Passwort: cc13com) bei apex.oracle.com ist sehr einfach aufgebaut, um diesem Beitrag eine Möglichkeit zu geben, sich näher zu erklären. Natürlich gehört in die Produktiv-Anwendung noch die Möglichkeit, Termine zu bearbeiten, oder wieder zu löschen.