APEX: Raumbelegung mit Table based Function

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.

Raumbelegung

Ü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=&#91;'|| l_zeit1 || '-' || l_zeit2 ||'&#93; body=&#91;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.

Dieser Beitrag wurde unter APEX, Oracle abgelegt und mit , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.