/*  $Revision$
 *  $Id$
 */

/*
 * FAC.remember_: Functions voor het bewaren en teruggeven van primary keys binnen triggers.
 * Dit om in de after-update nog te weten welke records er nog een nabehandeling hoeven
 * Dit kan vaak niet direct omdat de muutatie dan nog bezig is of omdat je pas aan het einde weet of en wat.
 *
 * De volgende functies zijn beschikbaar:
 * - ResetSelectie(TableName) RETURN BOOLEAN:
 *   Verwijderd de vorige selectie van de Primary-keys van de tabel. Hou hier rekening met multi-user dmv. de
 *   huidige SessionId. Retourneerd of het gelukt (TRUE) is of niet (FALSE).
 *   Deze moet aangeroepen worden in de BEFORE-statement-trigger,
 *   bijvoorbeeld: BEFORE INSERT OR UPDATE OR DELETE ON mld_opdr.
 * - SavePrimaryKey(TableName, PrimaryKey) RETURN BOOLEAN:
 *   Onthou de PrimaryKey-waarde, hou hiet ook rekening met multi-user dmv. de huidige SessionId. Retourneerd
 *   of het gelukt (TRUE) is of niet (FALSE).
 *   Deze moet aangeroepen worden in de BEFORE-row-trigger,
 *   bijvoorbeeld: BEFORE INSERT OR UPDATE ON mld_opdr FOR EACH ROW
 *   EN          : BEFORE DELETE ON mld_opdr FOR EACH ROW
 * - GetPrimaryKey(TableName, IndexNr) RETURN NUMBER:
 *   Geef de nth onthouden PrimaryKey terug, hou hiet ook rekening met multi-user dmv. de huidige SessionId.
 *   Het IndexNr begint met 1 en loopt door totdat er geen volgende PrimaryKey onthouden is.
 *   Retourneert de gevonden PrimaryKey of NULL (als er geen volgende is).
 *   Deze moet aangeroepen worden in de AFTER-statement-trigger,
 *   bijvoorbeeld: AFTER INSERT OR UPDATE OR DELETE ON mld_opdr.
 *
 */

#include "comsql.h"
#include "fac_paca.src"
#include "fac_pacx.src"
#include "fac_pacf.src"
#include "fac_pacl.src"
#include "fac_pacflex.src"
#include "fac_pacano.src"

CREATE OR REPLACE PACKAGE fac
    AUTHID CURRENT_USER -- zodat CREATE TABLE lukt
  AS
    BeginWerkUur      NUMBER(4,2);
    EindWerkUur       NUMBER(4,2);

   -- Maak de vorige selectie van de tabel en de huidige sessie leeg.
   FUNCTION Remember_ResetSelectie( TableName IN VARCHAR2 ) RETURN BOOLEAN;
   -- Onthou de primary key van de tabel en de huidige sessie.
   FUNCTION Remember_SavePrimaryKey( TableName IN VARCHAR2, PrimaryKey IN NUMBER ) RETURN BOOLEAN;
   -- Geef de nth primary key van de tabel en de huidige sessie. IndexNr begint met 1.
   FUNCTION Remember_GetPrimaryKey( TableName IN VARCHAR2, IndexNr IN NUMBER ) RETURN NUMBER;
   -- Onthou naast Primary key ook de datum i.v.m. een eventuele UNDO vanuit AutoCAD.
   FUNCTION Remember_SavePrimaryKeyAndDate( TableName IN VARCHAR2, PrimaryKey IN NUMBER, Datum IN Date ) RETURN BOOLEAN;
   -- Geef verwijderdatum of NULL terug van primary key i.v.m. eventuele UNDO vanuit AutoCAD.
   FUNCTION Remember_GetDate( TableName IN VARCHAR2, PrimaryKey IN NUMBER ) RETURN DATE;
   PRAGMA RESTRICT_REFERENCES (Remember_GetPrimaryKey,WNDS, WNPS);


    FUNCTION addMonths (pdate1 IN DATE, padd_mon IN NUMBER) RETURN DATE;
    FUNCTION safe_To_Integer( str IN VARCHAR2 ) RETURN NUMBER;
    FUNCTION safe_To_Number( str IN VARCHAR2 ) RETURN NUMBER;
    FUNCTION safe_To_Date  ( pchar IN VARCHAR2, pfmt IN VARCHAR2 ) RETURN DATE;
    FUNCTION getweekdaynum (d IN DATE) RETURN NUMBER;
    FUNCTION getdomeinwaarde (dkey IN NUMBER, waarde IN VARCHAR2, ignorewhenxmlnode IN NUMBER DEFAULT 0) RETURN VARCHAR2;
    FUNCTION count_Work_Days  ( pdate1 IN DATE , pdate2 IN DATE  ) RETURN NUMBER;
    FUNCTION count_Work_Days_InclTime ( pdate1 IN DATE , pdate2 IN DATE  ) RETURN NUMBER;
    FUNCTION iswerkdag (begindatum IN DATE) RETURN NUMBER;
    FUNCTION iswerktijd RETURN NUMBER;
    FUNCTION datumtijdplusuitvoertijd (begindatum IN DATE, uitvoertijd IN NUMBER, uitvoertijdtype IN VARCHAR2) RETURN DATE;
    FUNCTION datumtijdplusuitvoertijd (begindatum IN DATE, uitvoertijd IN NUMBER, uitvoertijdtype IN VARCHAR2,
                      pBeginWerkUur IN NUMBER, pEindWerkUur IN NUMBER, pMode IN NUMBER) RETURN DATE;
    FUNCTION datumtijdnaaruitvoertijd (begindatum IN DATE, einddatum IN DATE, uitvoertijdtype IN VARCHAR2) RETURN MLD_T_UITVOERTIJD;
    FUNCTION datumtijdnaaruitvoertijd (begindatum IN DATE, einddatum IN DATE, uitvoertijdtype IN VARCHAR2,
                      pBeginWerkUur IN NUMBER, pEindWerkUur IN NUMBER, pMode IN NUMBER) RETURN MLD_T_UITVOERTIJD;
    FUNCTION getdatemillisec (i_date IN DATE) RETURN NUMBER;
    FUNCTION usrrap_query (p_tabelnaam IN VARCHAR2) RETURN VARCHAR2;
    FUNCTION usrrap_orderby (p_tabelnaam IN VARCHAR2) RETURN VARCHAR2;
    PROCEDURE tabelize_usrraps;
    PROCEDURE imp_getfield ( p_parseline  IN OUT   VARCHAR2,
                             p_fielddelimitor   IN       VARCHAR2,
                             p_field            OUT      VARCHAR2
                           );

    PROCEDURE imp_getfield_nr (p_parseline IN VARCHAR2,
                               p_fielddelimitor   IN       VARCHAR2,
                               p_nr               IN       NUMBER,
                               p_field            OUT      VARCHAR2
                            );

    PROCEDURE imp_writelog( p_import_key IN NUMBER,
                            pLevel IN VARCHAR2,
                            pMsg IN VARCHAR2,
                            pHint IN VARCHAR2
                          );

    PROCEDURE writelog( pAppl IN VARCHAR2,
                            pLevel IN VARCHAR2,
                            pMsg IN VARCHAR2,
                            pHint IN VARCHAR2
                          );

    PROCEDURE imp_alg_delete_onrgoed (p_import_key IN NUMBER, p_mode IN NUMBER);
    PROCEDURE imp_prs_delete_organisatie (p_import_key IN NUMBER, p_mode IN NUMBER);
    PROCEDURE imp_mld_delete_mld (p_import_key IN NUMBER, p_mode IN NUMBER);
    PROCEDURE fac_perfmon_incr(limiet IN NUMBER, zwaarte IN NUMBER);
    PROCEDURE putnotificationprio (pfrom NUMBER, pto NUMBER, pmessage VARCHAR2, pmode NUMBER,
                                   poptemail VARCHAR2, poptmobile VARCHAR2, pprio NUMBER, pattach VARCHAR2 DEFAULT NULL);
    PROCEDURE putnotification (pfrom NUMBER, pto NUMBER, pmessage VARCHAR2, pmode NUMBER,
                               poptemail VARCHAR2, poptmobile VARCHAR2);
    PROCEDURE putsystemnotification (pmessage VARCHAR2, pmode NUMBER);
    PROCEDURE putnotificationsrtprio (pfrom NUMBER, pto NUMBER, pcode VARCHAR2, pref NUMBER,
              poptmessage VARCHAR2, poptstatus NUMBER, poptemail VARCHAR2, poptmobile VARCHAR2, pxref NUMBER,
              pprio NUMBER, psender VARCHAR2, pattach VARCHAR2 DEFAULT NULL);
    PROCEDURE clrnotifications (pcode VARCHAR2, pref NUMBER, puitz VARCHAR2 DEFAULT NULL);
    PROCEDURE clrnotifications (pcode VARCHAR2, pref NUMBER, psubject VARCHAR2, preceiver NUMBER, pextrakey NUMBER);
    PROCEDURE putnotificationjobs (pcust VARCHAR2);
    PROCEDURE putjobnotifications (pviewname VARCHAR2, pmode NUMBER, pflags NUMBER);
    FUNCTION getMobile  ( pkey IN NUMBER ) RETURN VARCHAR2;
    PROCEDURE executeschedules;
    PROCEDURE markorderassent(pxmlnode VARCHAR2, pkey NUMBER, presult NUMBER, presulttext VARCHAR2, pperslid_key NUMBER DEFAULT NULL);
    FUNCTION getSetting (pname IN VARCHAR2 ) RETURN VARCHAR2;
    PROCEDURE trackaction (pcode VARCHAR2, prefkey NUMBER, puserkey NUMBER, pdatum DATE, poms VARCHAR2);
    PROCEDURE backtrackaction (pcode VARCHAR2, prefkey NUMBER, puserkey NUMBER, pdatum DATE);
    PROCEDURE notifytracking(psrtnotificatiekey NUMBER, pperslid_key NUMBER, ptracking_oms VARCHAR2, prefkey NUMBER);
    FUNCTION gettrackingdate (peventcode IN VARCHAR2, pkey IN number) RETURN date;
    FUNCTION gettrackinguserkey (peventcode IN VARCHAR2, pkey IN number) RETURN number;
    PROCEDURE initsession (flcode IN VARCHAR2);
    PROCEDURE initsession (pkey NUMBER, flcode IN VARCHAR2, usertimezone IN VARCHAR2);
    PROCEDURE registerversion (pmaj IN NUMBER, pmin IN NUMBER, ppatch IN VARCHAR2, pschema IN VARCHAR2, plang IN VARCHAR2);
    PROCEDURE registercustversion (pcustid IN VARCHAR2, pcustnr IN NUMBER);
    FUNCTION getdbversion RETURN VARCHAR2;
    FUNCTION getDefaultBehandelgroep(p_stdm IN NUMBER) RETURN NUMBER;
    PROCEDURE processemail (pfrom IN VARCHAR2, pto IN VARCHAR2, psubject IN VARCHAR2, pbody IN VARCHAR2, psessionid IN VARCHAR2);
    FUNCTION isdatefeestdag(p_date IN DATE) RETURN BOOLEAN;
    FUNCTION calcnextcyclusdate (p_date IN DATE, p_mode IN NUMBER, p_eenheid IN NUMBER, p_periode IN NUMBER, p_bits IN NUMBER) RETURN DATE;
    FUNCTION nextcyclusdate (p_date IN DATE, p_mode IN NUMBER, p_eenheid IN NUMBER, p_periode IN NUMBER, p_bits IN NUMBER, p_steps IN NUMBER DEFAULT 0, p_ismjob IN NUMBER DEFAULT 0) RETURN DATE;
    FUNCTION nextcyclusdatedeel (p_insdeel IN NUMBER, p_srtcontrole IN NUMBER, p_steps IN NUMBER DEFAULT 0) RETURN DATE;
    FUNCTION nextcyclusdatesteps (p_date IN DATE, p_mode IN NUMBER, p_eenheid IN NUMBER, p_periode IN NUMBER, p_bits IN NUMBER, p_steps IN NUMBER DEFAULT 1, p_enddate IN DATE DEFAULT SYSDATE) RETURN NUMBER;
    FUNCTION makehash (p_in VARCHAR2, method IN VARCHAR2 DEFAULT 'MD5') RETURN VARCHAR2;
    FUNCTION testhash (p_hash VARCHAR2, p_in VARCHAR2) RETURN NUMBER;
    PROCEDURE createlikebookmark (pxmlnode VARCHAR2, prefkey NUMBER);
    PROCEDURE sequence_nextval(p_seqname IN VARCHAR2, p_initval IN NUMBER DEFAULT 1, p_initexpire IN DATE DEFAULT NULL);
END fac;
/

CREATE OR REPLACE PACKAGE BODY fac AS

    FUNCTION remember_resetselectie (tablename IN VARCHAR2)
        RETURN BOOLEAN
    IS
        sessionid   VARCHAR2 (20);
    BEGIN
        sessionid := USERENV ('SESSIONID');

        DELETE FROM fac_selectie
              WHERE fac_selectie_node = sessionid AND fac_selectie_tabel = tablename;

        RETURN TRUE;
    EXCEPTION
        WHEN OTHERS
        THEN
            RETURN FALSE;
    END;

    FUNCTION remember_saveprimarykey (tablename IN VARCHAR2, primarykey IN NUMBER)
        RETURN BOOLEAN
    IS
    BEGIN
        INSERT INTO fac_selectie (fac_selectie_node, fac_selectie_tabel, fac_selectie_key)
            VALUES (USERENV ('SESSIONID'), tablename, primarykey);

        RETURN TRUE;
    EXCEPTION
        WHEN OTHERS
        THEN
            RETURN FALSE;
    END;

    -- Geef de nth Fac_Selectie_Key van de geselecteerde records
    -- terug. IndexNr begint met 1.
    FUNCTION remember_getprimarykey (tablename IN VARCHAR2, indexnr IN NUMBER)
        RETURN NUMBER
    IS
        CURSOR selectie
        IS
              SELECT DISTINCT fac_selectie_key
                FROM fac_selectie
               WHERE fac_selectie_node = USERENV ('SESSIONID') AND fac_selectie_tabel = tablename
            ORDER BY fac_selectie_key;

        selectierec   selectie%ROWTYPE;
        tmpindex      NUMBER;
        returnkey     NUMBER (10);
    BEGIN
        returnkey := NULL;
        tmpindex := 1;

        FOR selectierec IN selectie
        LOOP
            IF indexnr = tmpindex
            THEN
                returnkey := selectierec.fac_selectie_key;
                EXIT;
            END IF;

            tmpindex := tmpindex + 1;
        END LOOP;

        RETURN returnkey;
    END;

    FUNCTION remember_saveprimarykeyanddate (tablename    IN VARCHAR2,
                                             primarykey   IN NUMBER,
                                             datum        IN DATE)
        RETURN BOOLEAN
    IS
    BEGIN
        INSERT INTO fac_selectie (fac_selectie_node,
                                  fac_selectie_tabel,
                                  fac_selectie_key,
                                  fac_selectie_datum)
            VALUES (USERENV ('SESSIONID'),
                    tablename,
                    primarykey,
                    datum);

        RETURN TRUE;
    EXCEPTION
        WHEN OTHERS
        THEN
            RETURN FALSE;
    END;

    FUNCTION remember_getdate (tablename IN VARCHAR2, primarykey IN NUMBER)
        RETURN DATE
    IS
        returndate    DATE;

        CURSOR selectie
        IS
            SELECT DISTINCT fac_selectie_datum
              FROM fac_selectie
             WHERE     fac_selectie_node = USERENV ('SESSIONID')
                   AND fac_selectie_key = primarykey
                   AND fac_selectie_tabel = tablename;

        selectierec   selectie%ROWTYPE;
        tmpindex      NUMBER;
        returnkey     NUMBER (10);
    BEGIN
        returndate := NULL;

        FOR selectierec IN selectie
        LOOP
            returndate := selectierec.fac_selectie_datum;
            EXIT;
        END LOOP;

        RETURN returndate;
    END;

    -- This function is to be used instead of the native ADD_MONTHS function
    -- If 29, 30 or 31 date doesn't exist in the  month then de first of the next month is taken
    FUNCTION addMonths (pdate1 IN DATE, padd_mon IN NUMBER) RETURN DATE IS
      pdate2 date;
    BEGIN
      pdate2:= ADD_MONTHS(pdate1-1, padd_mon);
      IF LAST_DAY(pdate1) = pdate1 OR LAST_DAY(pdate2) = pdate2
      THEN
        RETURN pdate2+1;
      ELSE
        RETURN ADD_MONTHS(pdate1, padd_mon);
      END IF;
    END;

    -- This function is to be used instead of the native TO_NUMBER in situations
    -- where the value might be an invalid number like '206E9547' ('Numeric overflow')
    FUNCTION safe_To_Integer( str IN VARCHAR2 ) RETURN NUMBER IS
    BEGIN
     RETURN TO_NUMBER(REGEXP_SUBSTR(str, '^\d+') ); -- alleen leading cijfers
    EXCEPTION
     WHEN OTHERS THEN RETURN NULL;
    END;

    -- This function is to be used instead of the native TO_NUMBER in situations
    -- where the value might be an invalid number. This is the case for (FSN#203)
    -- flexible properties as in FAC.SAFE_TO_NUMBER(alg_onrgoedkenmerk_waarde)<200
    -- It returns NULL instead of an ORA1722:error (or even another error)
    FUNCTION safe_To_Number( str IN VARCHAR2 ) RETURN NUMBER IS
    BEGIN
     RETURN TO_NUMBER(str);
    EXCEPTION
     WHEN OTHERS THEN RETURN NULL;
    END;

    FUNCTION safe_To_Date( pchar IN VARCHAR2, pfmt IN VARCHAR2 ) RETURN DATE IS
    BEGIN
     RETURN TO_DATE(pchar, pfmt);
    EXCEPTION
     WHEN OTHERS THEN RETURN NULL;
    END;

    -- Levert het dow-nummer op (1-7), op een nls-onafhankelijke wijze
    FUNCTION getweekdaynum (d IN DATE) RETURN NUMBER IS
    BEGIN
        RETURN MOD (TO_CHAR (d, 'J') + 1, 7) + 1;
    END;

    /*
     * Deprecated. Gebruik de flx versie.
     */
    FUNCTION getdomeinwaarde (dkey IN NUMBER, waarde IN VARCHAR2, ignorewhenxmlnode IN NUMBER DEFAULT 0)
       RETURN VARCHAR2
    IS
    BEGIN
       RETURN flx.getdomeinwaarde (dkey, waarde, ignorewhenxmlnode);
    END;

    -- Sluit weekend en vrije dagen uit
    FUNCTION iswerkdag (begindatum IN DATE)
       RETURN NUMBER
    AS
       dagvdweek    NUMBER;
       vrijedagen   NUMBER;
    BEGIN
       dagvdweek := fac.getweekdaynum(begindatum);

       IF dagvdweek = 1 OR dagvdweek = 7
       THEN
          RETURN 0;
       END IF;

       -- kijk of de begindatum op een vrije dag valt.
       SELECT COUNT ( * )
         INTO vrijedagen
         FROM mld_vrije_dagen
        WHERE mld_vrije_dagen_datum = TRUNC (begindatum);

       IF vrijedagen = 1
       THEN
          RETURN 0;
       ELSE
          RETURN 1;
       END IF;
    END;

    FUNCTION iswerktijd
       RETURN NUMBER
    AS
       lBeginWerkUur NUMBER;
       lEindWerkUur NUMBER;
    BEGIN
       lBeginWerkUur := fac.safe_to_number(fac.getsetting('fac_t_startofworkday'));
       lEindWerkUur := fac.safe_to_number(fac.getsetting('fac_t_endofworkday'));
       IF SYSDATE BETWEEN TRUNC(SYSDATE) + (lBeginWerkUur / 24)
                      AND TRUNC(SYSDATE) + (lEindWerkUur  / 24)
       THEN
          RETURN 1;
       ELSE
          RETURN 0;
       END IF;
    END;

    -- Return number of workdays between two dates, not including the startdate, weekends and holidays
    FUNCTION count_Work_Days0( pdate1 IN DATE , pdate2 IN DATE, pInclTime IN BOOLEAN ) RETURN NUMBER IS
    eikdag NUMBER;
    v_date_from DATE;
    v_date_to DATE;
    v_time_from NUMBER;
    v_time_to NUMBER;
    v_extra_day NUMBER;
    lbackwards BOOLEAN;
    v_week NUMBER;
    v_mod NUMBER;
    v_day_from NUMBER;
    v_day_to NUMBER;
    v_holidays NUMBER;
    BEGIN
     -- Remove time-part from dates (ie. set to 0:00) and set dates chronologically
     IF pdate1 > pdate2 THEN
       v_date_from := TRUNC(pdate2);
       v_date_to := TRUNC(pdate1);
       v_time_from := to_char(pdate2,'sssss');
       v_time_to := to_char(pdate1,'sssss');
       lbackwards := TRUE;
     ELSE
       v_date_from := TRUNC(pdate1);
       v_date_to := TRUNC(pdate2);
       v_time_from := to_char(pdate1,'sssss');
       v_time_to := to_char(pdate2,'sssss');
       lbackwards := FALSE;
     END IF;

     -- Determine whole weeks (v_week) and remaining days (v_mod) between FROM and TO
     v_week := trunc((v_date_to - v_date_from)/7);
     v_mod := mod((v_date_to - v_date_from),7);

     -- Correct remaining days (v_mod) depending on the FROM and TO "day-number"
     v_day_from := getweekdaynum(v_date_from);
     v_day_to := getweekdaynum(v_date_to);
     IF v_day_from = 1 THEN               /* FROM is sunday */
       IF v_day_to = 7 THEN               /* TO is saturday */
         v_mod := v_mod - 1;              /* should always be 5! */
       END IF;
     ELSIF v_day_from = 7 THEN            /* FROM is saturday */
       IF v_day_to != 7 THEN              /* TO is not saturday */
         v_mod := v_mod - 1;              /* exclude sunday succeeding FROM-saturday */
       END IF;
     ELSE                                 /* FROM is workday */
       IF v_day_to = 7 THEN               /* TO is saturday */
         v_mod := v_mod - 1;              /* exclude TO-saturday */
       ELSIF v_day_to < v_day_from THEN   /* weekend between FROM and TO */
         v_mod := v_mod - 2;              /* exclude weekend */
       END IF;
     END IF;

     -- Tbv. incltime-variant geldt extra dag als TO-tijd > FROM-tijd, behalve als TO
     -- in het weekend of op een vrije dag valt!
     v_extra_day := 0;
     IF pInclTime THEN
       SELECT COUNT(*)
         INTO v_extra_day
         FROM DUAL
        WHERE v_time_to > v_time_from
          AND v_day_to NOT IN (1, 7)
          AND NOT EXISTS
                (SELECT 1
                   FROM MLD_VRIJE_DAGEN
                  WHERE TRUNC (MLD_VRIJE_DAGEN_DATUM) = v_date_to);
     END IF;

     -- Determine the registered holidays between FROM+1 (excluding startdate!) and TO
     SELECT count(*)
       INTO v_holidays
       FROM MLD_VRIJE_DAGEN
      WHERE MLD_VRIJE_DAGEN_DATUM BETWEEN v_date_from+1 AND v_date_to;

     IF lbackwards THEN
       RETURN  0 - (5 * v_week + v_mod + v_extra_day - v_holidays);
     ELSE
       RETURN  5 * v_week + v_mod + v_extra_day - v_holidays;
     END IF;
    END;

   FUNCTION count_Work_Days_InclTime( pdate1 IN DATE , pdate2 IN DATE ) RETURN NUMBER IS
   	BEGIN
    	RETURN count_Work_Days0(pdate1,pdate2, TRUE);
    END;

    FUNCTION count_Work_Days( pdate1 IN DATE , pdate2 IN DATE ) RETURN NUMBER IS
   	BEGIN
    	RETURN count_Work_Days0(pdate1,pdate2, FALSE);
    END;

   /*
   ** Bepaal een nieuwe datum van de begindatum waarbij de uitvoertijd is
   ** opgeteld. De uitvoertijd (UitvoerTijdType) kan in 'UREN' of in 'DAGEN' zijn.
   ** Er wordt rekening gehouden met Werkuren, BeginWerkTijd en EindWerkTijd en
   ** Weekenddagen (Zondag = 1, Zaterdag = 7) en Vrije dagen (MLD_VRIJE_DAGEN).
   ** Testscript beschikbaar: /SRC/SQL/_UTIL/test_datumtijd.sql.
   */
    FUNCTION datumtijdplusuitvoertijd (begindatum IN DATE, uitvoertijd IN NUMBER, uitvoertijdtype IN VARCHAR2) RETURN DATE
    IS
    BEGIN
      IF BeginWerkUur IS NULL OR EindWerkUur IS NULL
      THEN
          BeginWerkUur := fac.safe_to_number (fac.getsetting ('fac_t_startofworkday')); -- half 8 is 7.5 en niet 7.30
          EindWerkUur  := fac.safe_to_number (fac.getsetting ('fac_t_endofworkday'));
          DBMS_OUTPUT.put_line ('Recaching Werkuren ' || BeginWerkUur||'..'||EindWerkUur);
      END IF;
      RETURN datumtijdplusuitvoertijd (begindatum, uitvoertijd, uitvoertijdtype,
                      BeginWerkUur, EindWerkUur, 1); -- 1=alleen werkdagen excl. vrije dagen
    END;

   -- Bereken de einddatum gegeven de begindatum en uitvoertijd. De mode bepaalt
   -- hoe dagen/tijden meetellen als volgt:
   -- 0 => alle dagen van de week excl. vrije dagen en uren tussen begin- en eindtijd
   -- 1 => alleen werkdagen excl. vrije dagen en uren tussen begin- en eindtijd
   -- 2 => 24/7 => alle 7 dagen van de week incl. vrije dagen en alle 24 uren per dag
   FUNCTION datumtijdplusuitvoertijd (begindatum        IN DATE,
                                      uitvoertijd       IN NUMBER,
                                      uitvoertijdtype   IN VARCHAR2,
                                      pBeginWerkUur     IN NUMBER,
                                      pEindWerkUur      IN NUMBER,
                                      pMode             IN NUMBER)
      RETURN DATE
   IS
      returnval         DATE;
      eikdag            NUMBER;
      vrijedag          NUMBER;
      datumbegin        DATE;
      dagenperweek      NUMBER;
      weekdagbegin      NUMBER;
      olddatumgereed    DATE;
      newdatumgereed    DATE;
      delta             NUMBER;
      aantalwerkweken   NUMBER;
      aantaldagenover   NUMBER;
      weekdaggereed     NUMBER;
      urenperdag        NUMBER (7, 4);  -- 4 decimalen i.v.m. afrondingsfouten
      aantalwerkdagen   NUMBER;
      aantalurenover    NUMBER;
      beginuur          NUMBER (7, 4);  -- 4 decimalen i.v.m. afrondingsfouten
      gereeduur         NUMBER (7, 4);  -- 4 decimalen i.v.m. afrondingsfouten
   BEGIN
      returnval := NULL;

      IF     begindatum IS NOT NULL
         AND uitvoertijd IS NOT NULL
         AND pMode BETWEEN 0 AND 2
      THEN
         IF pMode = 2
         THEN
            -- Zo simpel kan het zijn (igv. 24/7)!
            IF uitvoertijdtype IN ('D', 'DAGEN')
            THEN
               returnval := begindatum + uitvoertijd;
            ELSIF uitvoertijdtype IN ('U', 'UREN')
            THEN
               returnval := begindatum + (uitvoertijd / 24);
            ELSE
               returnval := NULL;
            END IF;
         ELSIF     pBeginWerkUur BETWEEN 0 AND 24
               AND pEindWerkUur BETWEEN 0 AND 24
               AND pEindWerkUur > pBeginWerkUur
         THEN
            -- Verschuif begindatum als deze valt [a] buiten de meegegeven uren of [b] op
            -- een vrije dag. Dit geldt Kantoortijden- of Openingstijden-regime!
            SELECT COUNT ( * )
              INTO vrijedag
              FROM mld_vrije_dagen
             WHERE mld_vrije_dagen_datum = TRUNC (begindatum);

            IF (begindatum > TRUNC (begindatum) + (pEindWerkUur / 24) OR vrijedag = 1)
            THEN
               -- Als begintijd na 'pEindWerkUur', dan naar 'pBeginWerkUur' volgende dag;
               -- hetzelfde als begindag is een vrije dag.
               -- En als deze volgende dag is een vrije dag, dan volgt vanzelf compensatie!
               datumbegin := TRUNC (begindatum + 1) + (pBeginWerkUur / 24);
            ELSIF (begindatum < TRUNC (begindatum) + (pBeginWerkUur / 24))
            THEN
               -- Als begintijd voor 'pBeginWerkUur', dan naar 'pBeginWerkUur' huidige dag.
               datumbegin := TRUNC (begindatum) + (pBeginWerkUur / 24);
            ELSE
               -- Anders 1-op-1 overnemen.
               datumbegin := begindatum;
            END IF;

            IF pMode = 0
            THEN
               -- Alle dagen van de week excl. vrije dagen (= alle dagen-variant van
               -- Openingstijden-regime)!
               dagenperweek := 7;
            ELSE                                                  -- pMode = 1
               -- Alleen werkdagen excl. vrije dagen (= Kantoortijden-regime en alleen
               -- werkdagen-variant van Openingstijden-regime)!
               dagenperweek := 5;

               -- Verschuif (gewijzigde) datumbegin als deze in het weekend valt.
               weekdagbegin := fac.getweekdaynum (datumbegin); -- zo=1 t/m za=7

               IF weekdagbegin = 1
               THEN
                  -- Als zo=1, dan schuif 1 dag door naar maandag 'pBeginWerkUur'.
                  datumbegin := TRUNC (datumbegin + 1) + (pBeginWerkUur / 24);
               ELSIF weekdagbegin = 7
               THEN
                  -- Als za=7, dan schuif 2 dagen door naar maandag 'pBeginWerkUur'.
                  datumbegin := TRUNC (datumbegin + 2) + (pBeginWerkUur / 24);
               END IF;
            END IF;

            IF uitvoertijdtype IN ('D', 'DAGEN')
            THEN
               newdatumgereed := datumbegin;
               delta := uitvoertijd;

               -- Herhaal onderstaande zolang er een delta is:
               -- [a] bereken datumgereed = datumbegin + delta-werkdagen.
               -- [b] bepaal nieuwe delta = vrije dagen tussen datumbegin en datumgereed.
               LOOP
                  olddatumgereed := newdatumgereed;
                  weekdagbegin := fac.getweekdaynum (olddatumgereed); -- ma=2 t/m vr=6
                  aantalwerkweken := TRUNC (delta / dagenperweek);
                  aantaldagenover := delta MOD dagenperweek;
                  -- Een/elke werkweek zorgt voor een doorlooptijd van 7 kalenderdagen.
                  newdatumgereed := olddatumgereed + (aantalwerkweken * 7) + aantaldagenover;

                  -- Verschuif newdatumgereed als gereedtijd buiten de meegegeven uren valt.
                  -- LET OP: Komt alleen voor als uitvoertijd decimalen bevat! Kan dat?
                  IF (newdatumgereed < TRUNC (newdatumgereed) + (pBeginWerkUur / 24))
                  THEN
                     -- Als gereedtijd voor 'pBeginWerkUur', dan naar 'pBeginWerkUur' huidige dag.
                     newdatumgereed := TRUNC (newdatumgereed) + (pBeginWerkUur / 24);
                  ELSIF (newdatumgereed > TRUNC (newdatumgereed) + (pEindWerkUur / 24))
                  THEN
                     -- Als gereedtijd na 'pEindWerkUur', dan naar 'pBeginWerkUur' volgende dag.
                     newdatumgereed := TRUNC (newdatumgereed + 1) + (pBeginWerkUur / 24);
                  END IF;

                  -- Compenseer eventueel (eind)weekend afhankelijk van 'aantaldagenover'.
                  weekdaggereed := fac.getweekdaynum (newdatumgereed); -- zo=1 t/m za=7

                  IF     pMode = 1
                     AND (weekdaggereed = 7 OR weekdagbegin > weekdaggereed)
                     AND delta <> 0
                  THEN
                     -- Als za=7 of weekdagbegin > weekdaggereed, dan 2 dagen optellen!
                     newdatumgereed := newdatumgereed + 2;
                  END IF;

                  -- Bepaal nieuwe delta.
                  SELECT COUNT ( * )
                    INTO delta
                    FROM (SELECT * FROM mld_vrije_dagen
                          MINUS -- Igv. pMode=1 za+zo niet meetellen; deze worden sowieso geskipt!
                          SELECT * FROM mld_vrije_dagen
                           WHERE pMode = 1 AND fac.getweekdaynum (mld_vrije_dagen_datum) IN (1,7))
                   WHERE mld_vrije_dagen_datum BETWEEN TRUNC (olddatumgereed + 1) AND newdatumgereed;

                  EXIT WHEN delta = 0;
               END LOOP;

               returnval := newdatumgereed;
            ELSIF uitvoertijdtype IN ('U', 'UREN')
            THEN
               -- Bepaal gereedtijd, dan hoeft daarna alleen nog maar in werkdagen te worden geschoven!
               urenperdag := pEindWerkUur - pBeginWerkUur;
               aantalwerkdagen := TRUNC (uitvoertijd / urenperdag); -- werkdagen
               aantalurenover := uitvoertijd MOD urenperdag;      -- rest uren
               beginuur := (datumbegin - TRUNC (datumbegin)) * 24;
               gereeduur := beginuur + aantalurenover;

               IF (gereeduur > pEindWerkUur)
               THEN
                  -- Als gereeduur na 'pEindWerkUur', dan corrigeren en dus een extra dag.
                  gereeduur := pBeginWerkUur + aantalurenover - (pEindWerkUur - beginuur);
                  aantalwerkdagen := aantalwerkdagen + 1; -- inclusief extra dag
               END IF;

               newdatumgereed := datumtijdplusuitvoertijd (datumbegin, aantalwerkdagen, 'DAGEN', pBeginWerkUur, pEindWerkUur, pMode);

               --Belangrijk: nu nog de eerder bepaalde gereedtijd zetten!
               returnval := TRUNC (newdatumgereed) + (gereeduur / 24);
            END IF;
         END IF;
      END IF;
      RETURN returnval;
   END;

    -- datumtijdnaaruitvoertijd is bedoeld als 100% de inverse van datumtijdplusuitvoertijd
    -- Invariant:
    --      datumtijdplusuitvoertijd(begin, datumtijdnaaruitvoertijd(begin, eind, type), type) == eind
    --      datumtijdnaaruitvoertijd(begin, datumtijdplusuitvoertijd(begin, uitvoertijd, type), type) == uitvoertijd
    -- Is de luxe versie van count_Work_Days
    FUNCTION datumtijdnaaruitvoertijd (begindatum IN DATE, einddatum IN DATE, uitvoertijdtype IN VARCHAR2) RETURN MLD_T_UITVOERTIJD
    AS
    BEGIN
      IF BeginWerkUur IS NULL OR EindWerkUur IS NULL
      THEN
          BeginWerkUur := fac.safe_to_number (fac.getsetting ('fac_t_startofworkday')); -- half 8 is 7.5 en niet 7.30
          EindWerkUur  := fac.safe_to_number (fac.getsetting ('fac_t_endofworkday'));
          DBMS_OUTPUT.put_line ('Recaching Werkuren ' || BeginWerkUur||'..'||EindWerkUur);
      END IF;
      RETURN datumtijdnaaruitvoertijd (begindatum, einddatum, uitvoertijdtype,
                      BeginWerkUur, EindWerkUur, 1); -- 1=alleen werkdagen excl. vrije dagen
    END datumtijdnaaruitvoertijd;

    FUNCTION datumtijdnaaruitvoertijd (begindatum        IN DATE,
                                       einddatum         IN DATE,
                                       uitvoertijdtype   IN VARCHAR2,
                                       pBeginWerkUur     IN NUMBER,
                                       pEindWerkUur      IN NUMBER,
                                       pMode             IN NUMBER)
       RETURN MLD_T_UITVOERTIJD
    AS
       uitvoertijd   mld_stdmelding.mld_stdmelding_t_uitvoertijd%TYPE := MLD_T_UITVOERTIJD(NULL, NULL); -- constructor initialisatie
       urenperdag    NUMBER (10, 3);
       eerstedag     NUMBER (10, 3);
       laatstedag    NUMBER (10, 3);
       heledagen     NUMBER (10);
       vrijedagen    NUMBER;
    BEGIN
       IF     begindatum IS NOT NULL
          AND einddatum IS NOT NULL
          AND pMode BETWEEN 0 AND 2
       THEN
          IF begindatum > einddatum
          THEN
             uitvoertijd := datumtijdnaaruitvoertijd (einddatum, begindatum, uitvoertijdtype, pBeginWerkUur, pEindWerkUur, pMode);
             uitvoertijd.tijdsduur := -uitvoertijd.tijdsduur;
          ELSIF pMode = 2
          THEN
             -- Zo simpel kan het zijn igv. 24/7!
             IF uitvoertijdtype IN ('D', 'DAGEN')
             THEN
                -- Per 5.4.3 kunnen hier gebroken dagen worden teruggegeven!
                uitvoertijd.tijdsduur := einddatum - begindatum;
                uitvoertijd.eenheid := 'D';
             ELSE                  -- default/uitvoertijdtype IN ('U', 'UREN')
                uitvoertijd.tijdsduur := (einddatum - begindatum) * 24;
                uitvoertijd.eenheid := 'U';
             END IF;
          ELSIF     pBeginWerkUur BETWEEN 0 AND 24
                AND pEindWerkUur BETWEEN 0 AND 24
                AND pEindWerkUur > pBeginWerkUur
          THEN
             urenperdag := pEindWerkUur - pBeginWerkUur;
             eerstedag := pEindWerkUur - (TRUNC (begindatum, 'MI') - TRUNC (begindatum)) * 24;
             eerstedag := LEAST (GREATEST (0, eerstedag), urenperdag);
             laatstedag := (TRUNC (einddatum, 'MI') - TRUNC (einddatum)) * 24 - pBeginWerkUur;
             laatstedag := LEAST (GREATEST (0, laatstedag), urenperdag);

             IF pMode = 0
             THEN
                -- Oppassen als begin/eind op een vrije dag valt,
                -- dan moeten eerste/laatste dag gewoon 0 uur zijn.
                SELECT COUNT ( * )
                  INTO vrijedagen
                  FROM mld_vrije_dagen
                 WHERE mld_vrije_dagen_datum = TRUNC (begindatum);

                IF vrijedagen = 1
                THEN
                   eerstedag := 0;
                END IF;

                SELECT COUNT ( * )
                  INTO vrijedagen
                  FROM mld_vrije_dagen
                 WHERE mld_vrije_dagen_datum = TRUNC (einddatum);

                IF vrijedagen = 1
                THEN
                   laatstedag := 0;
                END IF;

                IF TRUNC (begindatum) = TRUNC (einddatum)
                THEN
                   IF eerstedag + laatstedag > 0            -- geen vrije dag!
                   THEN
                      -- Als begindatum=einddatum én werkdag, dan 1 dag corrigeren!
                      heledagen := -1;
                   ELSE
                      -- Als begindatum=einddatum én vrije dag, dan geen correctie!
                      heledagen := 0;
                   END IF;
                ELSE
                   -- Als begindatum<>einddatum, dan alleen nog vrije dagen tussen
                   -- begin+1 en eind-1 corrigeren (en altijd 0 als begin+1=eind)!
                   SELECT COUNT ( * )
                     INTO vrijedagen
                     FROM mld_vrije_dagen
                    WHERE mld_vrije_dagen_datum BETWEEN TRUNC (begindatum + 1) AND einddatum - 1;

                   IF TRUNC (begindatum+1) = TRUNC (einddatum)
                   THEN
                      heledagen := 0;
                   ELSE
                      heledagen := TRUNC (einddatum) - TRUNC (begindatum + 1) - vrijedagen;
                   END IF;
                END IF;
             ELSE                                                 -- pMode = 1
                -- Oppassen als begin/eind in het weekend of zo valt,
                -- dan moeten eerste/laatste dag gewoon 0 uur zijn.
                IF iswerkdag (begindatum) = 0
                THEN
                   eerstedag := 0;
                END IF;

                IF iswerkdag (einddatum) = 0
                THEN
                   laatstedag := 0;
                   heledagen := fac.count_Work_Days (TRUNC (begindatum), TRUNC (einddatum));
                ELSE
                   heledagen := fac.count_Work_Days (TRUNC (begindatum), TRUNC (einddatum)) - 1;
                END IF;
             END IF;

             DBMS_OUTPUT.put_line ('Eerstedag ' || TO_CHAR (eerstedag));
             DBMS_OUTPUT.put_line ('Laatstedag ' || TO_CHAR (laatstedag));
             DBMS_OUTPUT.put_line ('Heledagen ' || TO_CHAR (heledagen));

             IF uitvoertijdtype IN ('D', 'DAGEN')
             THEN
                -- Per 5.4.3 kunnen hier gebroken dagen worden teruggegeven!
                uitvoertijd.tijdsduur := (eerstedag + laatstedag + heledagen * urenperdag) / urenperdag;
                uitvoertijd.eenheid := 'D';
             ELSE                  -- default/uitvoertijdtype IN ('U', 'UREN')
                uitvoertijd.tijdsduur := eerstedag + laatstedag + heledagen * urenperdag;
                uitvoertijd.eenheid := 'U';
             END IF;
          END IF;
       END IF;
       RETURN uitvoertijd;
    END datumtijdnaaruitvoertijd;

    FUNCTION getdatemillisec (i_date IN DATE)
        RETURN NUMBER
    IS
    BEGIN
        RETURN TO_NUMBER (TO_DATE (TO_CHAR ( (CAST (i_date AS TIMESTAMP WITH TIME ZONE)) AT TIME ZONE 'gmt', 'yyyy.mm.dd hh24:mi:ss'), 'yyyy.mm.dd hh24:mi:ss')
                          - TO_DATE ('01-01-1970', 'dd-mm-yyyy'))
               * (24 * 60 * 60 * 1000);
    END;

   FUNCTION usrrap_query ( p_tabelnaam IN VARCHAR2 ) RETURN VARCHAR2 IS
     QueryString VARCHAR2(4000);
     Kolomnaam  VARCHAR2(64);
     mainuser   VARCHAR2(80);
   BEGIN
     BEGIN
       DECLARE
         CURSOR Zoek_label IS
         SELECT column_name
         ,      data_type
         FROM   user_tab_columns
         WHERE  table_name = UPPER(p_tabelnaam)
         ORDER BY COLUMN_ID;

       BEGIN

         QueryString := 'Select ';
         FOR LabelRec in zoek_label LOOP

             IF LabelRec.data_type = 'NUMBER'
             THEN
               QueryString := QueryString ||'to_number(TO_CHAR('|| LabelRec.column_name ||',''99999999D9999'')) "'||LabelRec.column_name||'",';
             ELSE
               QueryString := QueryString ||' ' || LabelRec.column_name ||',';
             END IF;

         END LOOP;
       END;
     END;
       QueryString := Substr(QueryString,1,(LENGTH(QueryString)-1))||' from '|| p_tabelnaam ;
     RETURN QueryString;
   END usrrap_query;


  FUNCTION usrrap_orderby ( p_tabelnaam IN VARCHAR2 ) RETURN VARCHAR2 IS
    AantalRecords  NUMBER(10);
    OrderBy        VARCHAR2(255);
    mainuser       VARCHAR2(80);
    i              NUMBER(10);

  BEGIN
    SELECT count(*)
      INTO   AantalRecords
      FROM   user_tab_columns
      WHERE  table_name = UPPER(p_tabelnaam);
  --
    OrderBy := '';
    i := 1;

    FOR i IN 1..AantalRecords LOOP
        OrderBy := OrderBy||TO_CHAR(i)||',';
    END LOOP;
    OrderBy := SUBSTR(OrderBy,1,(LENGTH(OrderBy)-1));
    RETURN ' ORDER BY ' || OrderBy;
  END usrrap_orderby;

  -- Re-tabelizes selected views (functie and 8) into tables
  PROCEDURE tabelize_usrraps
  IS
      PRAGMA AUTONOMOUS_TRANSACTION;

      CURSOR c1
      IS
          SELECT fac_usrrap_view_name
            FROM fac_usrrap
           WHERE BITAND (fac_usrrap_functie, 8) = 8;

      dropddl     VARCHAR2 (200);
      createddl   VARCHAR2 (200);
  BEGIN
      FOR rec1 IN c1
      LOOP
          dropddl := 'DROP TABLE T_' || SUBSTR (rec1.fac_usrrap_view_name, 1, 28)||' PURGE';
          createddl :=
                 'CREATE TABLE T_'
              || SUBSTR (rec1.fac_usrrap_view_name, 1, 28)
              || ' AS SELECT * FROM '
              || rec1.fac_usrrap_view_name;

          BEGIN
              DBMS_OUTPUT.put_line (dropddl);

              EXECUTE IMMEDIATE dropddl;
          EXCEPTION
              WHEN OTHERS
              THEN
                  IF SQLCODE = -942
                  THEN
                      NULL;
                  ELSE
                      raise_application_error (-20001, 'Error trying to DROP: ' || SQLERRM);
                  END IF;
          END;

          DBMS_OUTPUT.put_line (createddl);

          -- Dit moet om 1031 error te vermijden
          --EXECUTE IMMEDIATE 'grant create table to '||user;
          BEGIN
              EXECUTE IMMEDIATE createddl;
          EXCEPTION
              WHEN OTHERS
              THEN
                  IF SQLCODE = -1031                             --  insufficient privileges
                  THEN
                      raise_application_error (
                          -20001,
                          'CREATE TABLE must be granted to this user: ' || USER);
                  ELSE
                      raise_application_error (-20001, 'Error trying to DROP: ' || SQLERRM);
                  END IF;
          END;
      END LOOP;
  END;

  PROCEDURE imp_getfield (
       p_parseline        IN OUT   VARCHAR2,
       p_fielddelimitor   IN       VARCHAR2,
       p_field            OUT      VARCHAR2
    )
    AS
       v_firstdelim    NUMBER;
       v_first_quote   NUMBER;
       v_next_quote    NUMBER;
       v_quote_count   NUMBER;
       v_found         BOOLEAN;
    BEGIN
       v_firstdelim   := INSTR (p_parseline, p_fielddelimitor, 1);

       IF v_firstdelim = 0
       THEN
          -- no delimiter found, so this is the last field of the row
          -- check if we start with a quote
          IF INSTR (p_parseline, '"', 1) = 1
          THEN
             -- strip leading and trailing quote
             -- we assume that the last character is a quote
             p_field   := substr2 (p_parseline, 2, LENGTH (p_parseline) - 2);
             -- now we only have the unescape the double quotes
             p_field := replace(p_field, '""', '"');
          ELSE
             p_field   := substr2 (p_parseline, 1);
          END IF;
          p_parseline   := '';
       ELSE
          -- we found a delimiter
          p_field        := SUBSTR (p_parseline, 1, v_firstdelim - 1);
          v_first_quote   := INSTR (p_field, '"', 1);

          IF v_first_quote <> 1 OR p_field IS NULL
          THEN
             -- line does not start with a quote or field is empty
             -- (two delimiters after each other)
             p_parseline   := substr2 (p_parseline, v_firstdelim + 1);
          ELSE
             -- double quote found. Search for the end double quote.
             v_found         := FALSE;
             v_quote_count   := 2;

             WHILE v_found = FALSE
             LOOP
                -- look for the next double quote
                v_next_quote   := INSTR (p_parseline, '"', 1, v_quote_count);

                IF v_next_quote = 0
                THEN
                   -- no end quote found. So, it is the last field in the row
                   -- lets remove the leading quote
                   -- this should not happen since a quoted field should always have an end quote
                   p_field       := substr2 (p_parseline, 2, LENGTH (p_parseline) - 1);
                   p_parseline   := '';
                   v_found := TRUE;
                ELSE
                   -- we have found a double quote, lets see what the next char is?
                   IF SUBSTR (p_parseline, v_next_quote + 1, 1) = '"'
                   THEN
                      -- two quotes found, so lets unescape the quote
                      v_quote_count   := v_quote_count + 1;
                      p_parseline     :=
                         SUBSTR (p_parseline, 1, v_next_quote)
                         || SUBSTR (p_parseline, v_next_quote + 2);
                   ELSIF SUBSTR (p_parseline, v_next_quote + 1, 1) = p_fielddelimitor
                      OR v_next_quote = LENGTH(p_parseline)
                   THEN
                      -- quote + delimiter found OR quote found at end of p_parseline
                      p_field         := SUBSTR (p_parseline, 2, v_next_quote - 2);
                      p_parseline     := SUBSTR (p_parseline, v_next_quote + 2);
                      v_found         := TRUE;
                   ELSE
                      -- quote without delimiter found
                      -- this should never happen.
                      v_quote_count   := v_quote_count + 1;
                   END IF;
                END IF;
             END LOOP;
          END IF;
       END IF;
    END;

   -- Retourneert het gegevens uit de kolom met volgnummer 'p_nr', beginnend met kolomnr 1.
   -- 'Uiteraard' is deze functie gebaseerd op imp_getfield!
   -- Verschil is dat p_parseline hier alleen IN-parameter is (en niet IN-OUT), en natuurlijk de extra parameter p_nr.
   -- En de gelezen waarde wordt itt imp_getfield gewoon getrimd (weg met die overbodige spaties die alleen voor de rommel zorgen): VOD D'R MET!
   -- Gebaseerd op het gebruik in de importfuncties is de lengte van een line 4000 posities (chars of bytes, daar wil ik van af zijn)
   PROCEDURE imp_getfield_nr (
       p_parseline        IN       VARCHAR2,
       p_fielddelimitor   IN       VARCHAR2,
       p_nr               IN       NUMBER,
       p_field            OUT      VARCHAR2
   )
   AS
        l_parseline VARCHAR2(4000);
        l_nr        NUMBER(10);
        l_field     VARCHAR2(4000);
   BEGIN
        l_parseline :=p_parseline;
        l_nr        :=p_nr;
        WHILE l_nr >= 1
        LOOP
           -- look for the next kolom in p_parseline
           fac.imp_getfield (l_parseline, p_fielddelimitor, l_field);
           l_nr := l_nr -1;
        END LOOP;
        -- Trimmen met die hap!
        -- En nu pas het veld toekennen aan de parameter van de procedure
        -- omdat de lengte korter kan zijn dan de lengte van de eerdere velden.
        p_field:=TRIM(l_field);
   END;

   PROCEDURE imp_writelog (
      p_import_key IN NUMBER,
      plevel   IN   VARCHAR2,
      pmsg     IN   VARCHAR2,
      phint    IN   VARCHAR2
   )
   AS
   BEGIN
      INSERT INTO imp_log
                  (fac_import_key, imp_log_datum,
                   imp_log_status, imp_log_omschrijving, imp_log_hint
                  )
           VALUES (p_import_key, SYSDATE,
                   plevel, pmsg, phint
                  );
   END;

   PROCEDURE writelog (
      pAppl    IN VARCHAR2,
      plevel   IN   VARCHAR2,
      pmsg     IN   VARCHAR2,
      phint    IN   VARCHAR2
   )
   AS
   BEGIN
      INSERT INTO imp_log
                  (imp_log_applicatie, imp_log_datum,
                   imp_log_status, imp_log_omschrijving, imp_log_hint
                  )
           VALUES (pAppl, SYSDATE,
                   plevel, pmsg, phint
                  );
   END;

   PROCEDURE imp_alg_delete_onrgoed (p_import_key IN NUMBER, p_mode IN NUMBER)
   AS
      v_errormsg       VARCHAR2 (1000);
      v_errorhint      VARCHAR2 (1000);
      oracle_err_num   NUMBER;
      oracle_err_mes   VARCHAR2 (200);
   BEGIN
      DELETE FROM bez_bezoekers;

      DELETE FROM bez_afspraak;

      DELETE FROM res_rsv_deel;

      DELETE FROM res_rsv_artikel;

      DELETE FROM res_rsv_ruimte;

      DELETE FROM res_reservering;

      DELETE FROM res_kenmerkwaarde;

      DELETE FROM res_alg_ruimte;

      DELETE FROM res_ruimte_opstelling;

      DELETE FROM res_ruimte;

      DELETE FROM res_opstelling;

      DELETE FROM res_activiteitdiscipline;

      DELETE FROM res_disc_params;

      DELETE FROM ins_tab_discipline
            WHERE ins_discipline_module = 'RES' AND ins_discipline_min_level = '3';

      DELETE FROM res_srtartikel_onrgoed;

      DELETE FROM fin_factuur
            WHERE mld_opdr_key IS NOT NULL;

      DELETE FROM cnt_contract_plaats;

      DELETE FROM mld_kenmerkmelding;

      DELETE FROM mld_melding_object;

      DELETE FROM mld_opdr_materiaal;

      DELETE FROM mld_opdr;

      DELETE FROM mld_melding;

      DELETE FROM mld_adres;

      DELETE FROM prs_perslidwerkplek;

      DELETE FROM prs_werkplek;

      IF (p_mode < 2)
      THEN
         DELETE FROM prs_contactpersoon_locatie;
      END IF;

      DELETE FROM ins_verbinding;

      DELETE FROM ins_deelkoppeling;

      DELETE FROM ins_deel
            WHERE ins_deel_parent_key IS NOT NULL;

      DELETE FROM ins_deel
            WHERE ins_alg_ruimte_type IS NOT NULL;

      DELETE FROM alg_terreinsector;

      DELETE FROM alg_srtterreinsector;

      DELETE FROM alg_ruimte;

      DELETE FROM alg_srtruimte;

      DELETE FROM alg_verdieping;

      IF (p_mode < 2)
      THEN
         DELETE FROM alg_onrgoedkenmerk;

         DELETE FROM alg_gebouw;

         DELETE FROM alg_srtgebouw;

         DELETE FROM alg_locatie;

         DELETE FROM alg_district;

         DELETE FROM alg_regio;
      ELSE
         DELETE FROM alg_onrgoedkenmerk
               WHERE alg_onrgoed_niveau IN ('R', 'T');
      END IF;
   EXCEPTION
      WHEN OTHERS
      THEN
         oracle_err_num := SQLCODE;
         oracle_err_mes := SUBSTR (SQLERRM, 1, 200);

         IF (v_errormsg = '')
         THEN
            v_errormsg := 'Niet alle onroerendgoed gegevens zijn verwijderd!';
         END IF;

         v_errorhint :=
               v_errorhint
            || ' (ORACLE error NUMBER = <'
            || oracle_err_num
            || '> ORACLE error message = <'
            || oracle_err_mes
            || '>)';
         fac.imp_writelog (p_import_key, 'E', v_errormsg, v_errorhint);
         COMMIT;
   END;

   PROCEDURE imp_prs_delete_organisatie (p_import_key IN NUMBER, p_mode IN NUMBER)
   AS
      v_errormsg       VARCHAR2 (1000);
      v_errorhint      VARCHAR2 (1000);
      oracle_err_num   NUMBER;
      oracle_err_mes   VARCHAR2 (200);

      v_bedrijf_key           NUMBER(10);

      -- PRS_CONTACTPERSOON_LOCATIE
      CURSOR c1
      IS
         SELECT prs_contactpersoon_locatie_key
           FROM prs_contactpersoon_locatie;

      rec1             c1%ROWTYPE;

      -- PRS_CONTACTPERSOON
      CURSOR c2
      IS
         SELECT prs_contactpersoon_key
           FROM prs_contactpersoon;

      rec2             c2%ROWTYPE;

      -- PRS_BEDRIJFDIENSTLOCATIE
      CURSOR c3
      IS
         SELECT prs_bedrijfdienstlocatie_key
           FROM prs_bedrijfdienstlocatie;

      rec3             c3%ROWTYPE;

      -- PRS_PERSLIDWERKPLEK
      CURSOR c4
      IS
         SELECT prs_perslidwerkplek_key
           FROM prs_perslidwerkplek;

      rec4             c4%ROWTYPE;

      -- PRS_WERKPLEK
      CURSOR c5
      IS
         SELECT prs_werkplek_key
           FROM prs_werkplek;

      rec5             c5%ROWTYPE;

      -- PRS_PERSLID
      CURSOR c6
      IS
         SELECT   prs_perslid_key
           FROM   prs_perslid
           WHERE  prs_perslid_oslogin not in ('_GAST','_FACILITOR') /* van vroeger */
              OR prs_perslid_oslogin IS NULL;

      rec6             c6%ROWTYPE;

      -- PRS_SRTPERSLID
      CURSOR c7
      IS
         SELECT   prs_srtperslid_key
           FROM   prs_srtperslid
           where  prs_srtperslid_upper <> 'ONBEKEND';

      rec7             c7%ROWTYPE;

      -- PRS_KOSTENPLAATS
      CURSOR c8
      IS
         SELECT prs_kostenplaats_key
           FROM prs_kostenplaats;

      rec8             c8%ROWTYPE;

   BEGIN
      IF (p_mode <= 1)
      THEN

        FOR rec1 IN c1
        LOOP
          BEGIN
            delete from prs_contactpersoon_locatie where prs_contactpersoon_locatie_key = rec1.prs_contactpersoon_locatie_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....';
          END;
        END LOOP;

        commit;

        FOR rec2 IN c2
        LOOP
          BEGIN
            delete from prs_contactpersoon where prs_contactpersoon_key = rec2.prs_contactpersoon_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....';
          END;
        END LOOP;

        commit;

        FOR rec3 IN c3
        LOOP
          BEGIN
            delete from prs_bedrijfdienstlocatie where prs_bedrijfdienstlocatie_key = rec3.prs_bedrijfdienstlocatie_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....';
          END;
        END LOOP;

        commit;

      END IF;

      IF (p_mode <= 2)
      THEN

        FOR rec4 IN c4
        LOOP
          BEGIN
            delete from prs_perslidwerkplek where prs_perslidwerkplek_key = rec4.prs_perslidwerkplek_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....'; -- agv prs_v_verplichting_all/fac_gebruikersgroep
          END;
        END LOOP;

        commit;

        FOR rec5 IN c5
        LOOP
          BEGIN
            delete from prs_werkplek where prs_werkplek_key = rec5.prs_werkplek_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....'; --
          END;
        END LOOP;

        commit;

        FOR rec6 IN c6
        LOOP
          BEGIN
            delete from prs_perslid where prs_perslid_key = rec6.prs_perslid_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....'; -- agv prs_v_verplichting_all/fac_gebruikersgroep/prs_perslidwerkplek
          END;
        END LOOP;

        commit;

        FOR rec7 IN c7
        LOOP
          BEGIN
            delete from prs_srtperslid where prs_srtperslid_key = rec7.prs_srtperslid_key;
            EXCEPTION
            WHEN OTHERS THEN
              v_errormsg := 'Ok, dan niet. Neem het volgende record ....'; -- agv prs_perslid
          END;
        END LOOP;

        commit;

      END IF;

      IF (p_mode <= 1)
      THEN

         -- Verwijder ALLE afdelingen indien mogelijk. Doe dit wel in de 'juiste' volgorde.

         DELETE FROM prs_afdeling
               WHERE prs_afdeling_key IN (SELECT prs_afdeling_key
                                            FROM prs_v_afdeling
                                           WHERE niveau = 5) AND prs_afdeling_key NOT IN (SELECT prs_afdeling_key
                                                                                            FROM prs_perslid);

         DELETE FROM prs_afdeling
               WHERE prs_afdeling_key IN (SELECT prs_afdeling_key
                                            FROM prs_v_afdeling
                                           WHERE niveau = 4)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_key
                                                FROM prs_perslid)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_parentkey
                                                FROM prs_v_afdeling
                                               WHERE niveau = 5);

         DELETE FROM prs_afdeling
               WHERE prs_afdeling_key IN (SELECT prs_afdeling_key
                                            FROM prs_v_afdeling
                                           WHERE niveau = 3)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_key
                                                FROM prs_perslid)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_parentkey
                                                FROM prs_v_afdeling
                                               WHERE niveau = 4);

         DELETE FROM prs_afdeling
               WHERE prs_afdeling_key IN (SELECT prs_afdeling_key
                                            FROM prs_v_afdeling
                                           WHERE niveau = 2)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_key
                                                FROM prs_perslid)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_parentkey
                                                FROM prs_v_afdeling
                                               WHERE niveau = 3);

         DELETE FROM prs_afdeling
               WHERE prs_afdeling_key IN (SELECT prs_afdeling_key
                                            FROM prs_v_afdeling
                                           WHERE niveau = 1)
                 AND prs_afdeling_upper <> 'ONBEKEND'
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_key
                                                FROM prs_perslid)
                 AND prs_afdeling_key NOT IN (SELECT prs_afdeling_parentkey
                                                FROM prs_v_afdeling
                                               WHERE niveau = 2);

         -- Verwijder ALLE bedrijven voor zover mogelijk
         DELETE FROM prs_bedrijf
               WHERE prs_bedrijf_key NOT IN (SELECT prs_bedrijf_key
                                               FROM prs_afdeling)
               AND   prs_bedrijf_naam_upper <> 'ONBEKEND';

        commit;

      END IF;

      -- Verwijder ALLE kostenplaatsen voor zover mogelijk
      FOR rec8 IN c8
      LOOP
        BEGIN
          delete from prs_kostenplaats where prs_kostenplaats_key = rec8.prs_kostenplaats_key;
          EXCEPTION
          WHEN OTHERS THEN
            v_errormsg := 'Ok, dan niet. Neem het volgende record ....';
        END;
      END LOOP;

      COMMIT;

      -- Maak initiele vulling aan
      BEGIN
        INSERT INTO prs_srtperslid (prs_srtperslid_omschrijving, prs_bedrijf_key)
             VALUES ('Onbekend', NULL);
            EXCEPTION
              WHEN OTHERS THEN
                v_errormsg := 'Ok. Kennelijk bestaat het record al';
      END;

      BEGIN
        INSERT INTO prs_bedrijf (prs_bedrijf_naam, prs_bedrijf_intern)
             VALUES ('Onbekend', 1);
            EXCEPTION
              WHEN OTHERS THEN
                v_errormsg := 'Ok. Kennelijk bestaat het record al';
      END;

      SELECT  prs_bedrijf_key
      INTO    v_bedrijf_key
      FROM    prs_bedrijf
      WHERE   prs_bedrijf_naam_upper = 'ONBEKEND';

      BEGIN
        INSERT INTO prs_afdeling (prs_bedrijf_key, prs_afdeling_naam)
             VALUES (v_bedrijf_key, 'Onbekend');
            EXCEPTION
              WHEN OTHERS THEN
                v_errormsg := 'Ok. Kennelijk bestaat het record al';
      END;

      commit;

      EXCEPTION
        WHEN OTHERS
        THEN
           oracle_err_num := SQLCODE;
           oracle_err_mes := SUBSTR (SQLERRM, 1, 200);

           IF (v_errormsg = '')
           THEN
              v_errormsg := 'Niet alle bedrijven zijn verwijderd';
           END IF;

           v_errorhint :=
                 v_errorhint
              || ' (ORACLE error NUMBER = <'
              || oracle_err_num
              || '> ORACLE error message = <'
              || oracle_err_mes
              || '>)';
           fac.imp_writelog (p_import_key, 'E', v_errormsg, v_errorhint);
           COMMIT;
   END;

    PROCEDURE imp_mld_delete_mld (p_import_key IN NUMBER, p_mode IN NUMBER)
    AS
      v_errormsg       VARCHAR2 (1000);
      v_errorhint      VARCHAR2 (1000);
      oracle_err_num   NUMBER;
      oracle_err_mes   VARCHAR2 (200);
    BEGIN

        delete from mld_opdr;

        delete from mld_melding;

        delete from mld_stdmelding;

        delete from prs_kostensoort;

        delete from mld_disc_params;

        delete from ins_tab_discipline where ins_discipline_module='MLD';

        delete from ins_srtdiscipline;

    EXCEPTION
      WHEN OTHERS
      THEN
        oracle_err_num := SQLCODE;
        oracle_err_mes := SUBSTR (SQLERRM, 1, 200);

        v_errormsg := 'Niet alle servicedesk gegevens zijn verwijderd!';

        v_errorhint :=
             v_errorhint
          || ' (ORACLE error NUMBER = <'
          || oracle_err_num
          || '> ORACLE error message = <'
          || oracle_err_mes
          || '>)';
        fac.imp_writelog (p_import_key, 'E', v_errormsg, v_errorhint);
        COMMIT;
    END; -- imp_mld_delete_mld

   PROCEDURE fac_perfmon_incr (limiet IN NUMBER, zwaarte IN NUMBER)
   AS
      dummy   VARCHAR2 (1);
   BEGIN
      BEGIN
         SELECT ''
           INTO dummy
           FROM fac_perfmon
          WHERE fac_perfmon_datum = TRUNC (SYSDATE);

         UPDATE fac_perfmon
            SET fac_perfmon_waarde = fac_perfmon_waarde + zwaarte,
                fac_perfmon_aantal = fac_perfmon_aantal + 1
          WHERE fac_perfmon_datum = TRUNC (SYSDATE);
      EXCEPTION
         WHEN NO_DATA_FOUND
         THEN
            -- a brandnew day. vul het aantal gebruikers van de vorige dag in en start een nieuwe
            UPDATE fac_perfmon
               SET fac_perfmon_users =
                         (SELECT COUNT (prs_perslid_key)
                            FROM prs_perslid
                           WHERE TRUNC (prs_perslid_login) = fac_perfmon_datum)
             WHERE fac_perfmon_users IS NULL;

            INSERT INTO fac_perfmon
                        (fac_perfmon_aantal, fac_perfmon_waarde,
                         fac_perfmon_datum, fac_perfmon_threshold
                        )
                 SELECT 1, zwaarte,
                         TRUNC (SYSDATE), limiet
                   FROM DUAL
                      WHERE TRUNC (SYSDATE) NOT IN -- multi-user safe maken
                        (SELECT fac_perfmon_datum FROM fac_perfmon);
      END;
   END;

   -- Retourneert string met emailadres van persoon pkey, '' indien niet beschikbaar
   FUNCTION getemail (pkey IN NUMBER)
      RETURN VARCHAR2
   IS
      lemail   prs_perslid.prs_perslid_email%TYPE;
   BEGIN
      lemail := '';

      BEGIN
         -- emailadres van ontvanger
         SELECT prs_perslid_email
           INTO lemail
           FROM prs_perslid
          WHERE prs_perslid_key = pkey;
      EXCEPTION
         WHEN OTHERS
         THEN
            NULL;
      END;

      RETURN lemail;
   END;

   -- Retourneert string met mobielnummer van persoon pkey, '' indien niet beschikbaar
   FUNCTION getmobile (pkey IN NUMBER)
      RETURN VARCHAR2
   IS
      lphone   prs_perslid.prs_perslid_mobiel%TYPE;
   BEGIN
      lphone := '';

      BEGIN
         -- mobiele telefoon van ontvanger
         SELECT prs_perslid_mobiel
           INTO lphone
           FROM prs_perslid
          WHERE prs_perslid_key = pkey;
      EXCEPTION
         WHEN OTHERS
         THEN
            NULL;
      END;

      RETURN lphone;
   END;

   -- Retourneert de taal van een user
   FUNCTION getlang (pkey IN NUMBER)
      RETURN VARCHAR2
   IS
      llang   prs_perslid.prs_perslid_lang%TYPE;
   BEGIN
      llang := '';

      BEGIN
         -- taal van persoon; te overwegen (gelijk gedrag) is om alleen
         -- afwijking terug te geven, dus NULL als default ipv default
         SELECT COALESCE(prs_perslid_lang, lcl.getuserlanguage())
           INTO llang
           FROM prs_perslid
          WHERE prs_perslid_key = pkey;
      EXCEPTION
         WHEN OTHERS
         THEN
            NULL;
      END;

      RETURN llang;
   END;

   -- Zet een vrij bericht in de queue ter verspreiding volgens pmode. Dit is geen bestaande srtnotificatie
   -- In plaats van pto (een intern prs_perslid) kan optioneel ook expliciete adressering worden meegegeven.
   -- Bij een interne ontvanger wordt diens taal bijgezocht.
   PROCEDURE putnotificationprio (pfrom NUMBER, pto NUMBER, pmessage VARCHAR2, pmode NUMBER,
                                  poptemail VARCHAR2, poptmobile VARCHAR2, pprio NUMBER, pattach VARCHAR2 DEFAULT NULL)
   AS
      lemail   prs_perslid.prs_perslid_email%TYPE;
      lphone   prs_perslid.prs_perslid_mobiel%TYPE;
      llang    prs_perslid.prs_perslid_lang%TYPE;
      lsysteem prs_perslid.prs_perslid_systeemadres%TYPE;
	  lmode    NUMBER;
   BEGIN
      lmode := pmode;
      IF poptemail IS NULL
      THEN
         lemail := fac.getemail (pto);
      ELSE
         lemail := poptemail;
      END IF;

      IF poptmobile IS NULL
      THEN
         lphone := fac.getmobile (pto);
      ELSE
         lphone := poptmobile;
      END IF;

      IF pto IS NOT NULL
      THEN
        llang := fac.getlang(pto);
      ELSE
        llang := lcl.getuserlanguage();
      END IF;

      IF pto IS NOT NULL
      THEN
          BEGIN
            SELECT prs_perslid_systeemadres
              INTO lsysteem
              FROM prs_perslid
             WHERE prs_perslid_key = pto;
          EXCEPTION
            WHEN NO_DATA_FOUND THEN NULL;
          END;
      END IF;

      -- Stuur notificatie
      IF BITAND(pmode, 2) = 2 AND lemail IS NULL
      THEN
        lmode := BITAND(lmode, 255-2);
      END IF;

      IF BITAND(pmode, 4) = 4 AND lphone IS NULL
      THEN
        lmode := BITAND(lmode, 255-4);
      END IF;

      IF lmode > 0 OR lsysteem IS NOT NULL THEN
      INSERT INTO fac_notificatie
                  (fac_srtnotificatie_key, fac_notificatie_status, prs_perslid_key_sender, prs_perslid_key_receiver,
                   fac_notificatie_receiver_email, fac_notificatie_receiver_phone, fac_notificatie_oms,
                   fac_notificatie_refkey, fac_notificatie_prioriteit, fac_notificatie_lang, fac_notificatie_systeemadres,
                   fac_notificatie_attachments
                  )
           VALUES (NULL, lmode, pfrom, pto,
                   lemail, lphone, SUBSTR(pmessage, 1, 2048),
                   NULL, pprio, llang, lsysteem,
                   pattach
                  );
      END IF;
   END;

   /* for code compatibility only */
   PROCEDURE putnotification (pfrom NUMBER, pto NUMBER, pmessage VARCHAR2, pmode NUMBER,
                              poptemail VARCHAR2, poptmobile VARCHAR2)
   AS
   BEGIN
        putnotificationprio (pfrom, pto, pmessage, pmode, poptemail, poptmobile, 2);
   END;

   -- Zet een bericht in de queue ter verspreiding aan de applicatiebeheerders
   PROCEDURE putsystemnotification (pmessage VARCHAR2, pmode NUMBER)
   AS
      CURSOR c1
      IS
         SELECT wg.prs_perslid_key
           FROM fac_functie f, fac_v_webgebruiker wg
          WHERE f.fac_functie_key = wg.fac_functie_key
            AND f.fac_functie_code = 'WEB_PRSSYS';
   BEGIN
      FOR rec1 IN c1
      LOOP
         putnotification (NULL, rec1.prs_perslid_key, pmessage, pmode, NULL, NULL);
      END LOOP;
   END;

   -- Zet een bericht in de queue ter verspreiding volgens definitie van een bestaande srtnotificatie
   -- Eventueel gebeurt er dus niets, als de srtnotificatie uitgeschakeld (mode=0) is.
   -- Als het bericht wordt meegegeven in poptmessage dan wordt die tekst gebruikt ipv de standaardtekst van pcode
   -- Evenzo wordt de standaardmode overruled door poptstatus als die meegegeven is, en
   -- kan de email en mobile van pto optioneel worden overruled door poptemail en poptmobile
   -- Als email of sms gevraagt wordt (smode) en email of mobile kan niet bepaald worden, wordt niet gequeued
   -- pprio is de prioriteit (1, 2 of 3) die geen effect heeft op de quebehandeling, maar wel wordt meegegeven
   -- psender is een optioneel emailadres dat als afzender kan worden gehanteerd.
   -- pattach is a list of filenames (tbd)
   PROCEDURE putnotificationsrtprio (pfrom          NUMBER,
                                     pto            NUMBER,
                                     pcode          VARCHAR2,
                                     pref           NUMBER,
                                     poptmessage    VARCHAR2,
                                     poptstatus     NUMBER,
                                     poptemail      VARCHAR2,
                                     poptmobile     VARCHAR2,
                                     pxref          NUMBER,
                                     pprio          NUMBER,
                                     psender        VARCHAR2,
                                     pattach        VARCHAR2 DEFAULT NULL)
   AS
      soms         fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
      smode        fac_srtnotificatie.fac_srtnotificatie_mode%TYPE;
      oldercode    fac_srtnotificatie.fac_srtnotificatie_code%TYPE;
      oldersoms    fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
      oldersmode   fac_srtnotificatie.fac_srtnotificatie_mode%TYPE;
      susermode    fac_srtnotificatie.fac_srtnotificatie_usermode%TYPE;
      lmode        fac_srtnotificatie.fac_srtnotificatie_mode%TYPE;
      skey         fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
      lemail       fac_notificatie.fac_notificatie_receiver_email%TYPE;
      lphone       fac_notificatie.fac_notificatie_receiver_phone%TYPE;
      llang        prs_perslid.prs_perslid_lang%TYPE;
      lsysteem     prs_perslid.prs_perslid_systeemadres%TYPE;
      immediate    NUMBER(1);
   BEGIN
      IF pcode IS NOT NULL
      THEN
         BEGIN
            immediate := 0;
            IF poptstatus IS NULL
            THEN
               -- Geen status/mode meegegeven, bepaal em zelf
               SELECT lcl.x('fac_srtnotificatie_oms', sn.fac_srtnotificatie_key, sn.fac_srtnotificatie_oms),
                      sn.fac_srtnotificatie_mode,
                      sn.fac_srtnotificatie_key,
                      fac_srtnotificatie_usermode
                 INTO soms,
                      smode,
                      skey,
                      susermode
                 FROM fac_srtnotificatie sn
                WHERE sn.fac_srtnotificatie_code = pcode
                      AND sn.fac_srtnotificatie_mode > 0;

               -- Als deze notificatiesoort userafhankelijk kan zijn, kijken we
               -- naar de status/mode die bij de user is aangegeven
               IF susermode = 1 AND pto IS NOT NULL
               THEN
                  BEGIN
                     SELECT prs_perslid_srtnoti_mode
                       INTO smode
                       FROM prs_perslid
                      WHERE prs_perslid_key = pto
                            AND prs_perslid_srtnoti_mode IS NOT NULL;
                  EXCEPTION
                     WHEN NO_DATA_FOUND
                     THEN
                        -- geen gebruikersoverrule, gebruik gewoon de centrale mode
                        NULL;
                  END;
               END IF;
            ELSE
               -- Wel status meegegeven; die is dan vast
               -- Als status=2 en pcode=xxxMAI, dan moet de mail altijd vertuurd worden.
               smode := poptstatus;
               IF SUBSTR(pcode, -3) = 'MAI' AND poptstatus = 2
               THEN
                 immediate := 1;
               END IF;

               SELECT lcl.x('fac_srtnotificatie_oms', sn.fac_srtnotificatie_key, sn.fac_srtnotificatie_oms),
                      sn.fac_srtnotificatie_key
                 INTO soms, skey
                 FROM fac_srtnotificatie sn
                WHERE sn.fac_srtnotificatie_code = pcode;
            END IF;
         EXCEPTION
            WHEN NO_DATA_FOUND
            THEN
               -- Nothing to do
               RETURN;
         END;

         IF poptmessage IS NOT NULL
         THEN
            soms := poptmessage;
         END IF;

         soms := REPLACE (soms, '##KEY##', TO_CHAR (pref));

         IF poptemail IS NULL
         THEN
            lemail := fac.getemail (pto);
         ELSE
            lemail := poptemail;
         END IF;

         IF poptmobile IS NULL
         THEN
            lphone := fac.getmobile (pto);
         ELSE
            lphone := poptmobile;
         END IF;

         IF pto IS NOT NULL
         THEN
           llang := fac.getlang(pto);
         ELSE
           llang := lcl.getuserlanguage();
         END IF;

         IF pto IS NOT NULL
         THEN
             BEGIN
               SELECT prs_perslid_systeemadres
                 INTO lsysteem
                 FROM prs_perslid
                WHERE prs_perslid_key = pto
                  AND prs_perslid_verwijder IS NULL;
             EXCEPTION
               WHEN NO_DATA_FOUND THEN
                 RETURN;
             END;
         END IF;

         lmode := smode;

         IF BITAND (smode, 2) = 2 AND lemail IS NULL
         THEN
            lmode := BITAND (lmode, 255 - 2);
         END IF;

         IF BITAND (smode, 4) = 4 AND lphone IS NULL
         THEN
            lmode := BITAND (lmode, 255 - 4);
         END IF;

         -- Als de omschrijving uit meerdere regels bestaat dan alleen de eerste regel notificeren.
         -- Empirisch gebleken dat chr(10) wel wat doet, en chr(13) niet echt
         -- hangt dat van iets af? Moeten we dan misschien allebei doen?
         IF INSTR(soms, CHR(10)) > 0
         THEN
            soms := SUBSTR(soms, 1, INSTR (soms, CHR(10)) - 1);
         END IF;

         -- Gooi bestaande identieke notificaties eerst maar eens uit de queue. Voor het gemak.
         -- De nieuwe is minstens zo goed en gaat per definitie via dezelfde kanalen
         fac.clrnotifications(pcode, pref, soms, pto, pxref); -- Dubbele notificaties verwijderen indien dezelfde omschrijving en voor dezelfde ontvanger.

         -- Voorkom e-mail/sms van deze zwakkere *UPD als de *NEW nog niet eens de deur uit is door Putorders
         -- of meer algemeen: elke e-mail/sms die nog niet de deur is.
         -- Je wilt wel op alle gedefinieerde kanalen precies een bericht houden
         IF ((BITAND (smode, 2 + 4) <> 0) AND (immediate = 0))
         THEN
            -- Zoek de lmode van een al/nog aanwezige fac_notificatie die over hetzelfde gaat
            BEGIN
               SELECT fac_srtnotificatie_code, fac_notificatie_status, fac_notificatie_oms
                 INTO oldercode, oldersmode, oldersoms
                 FROM fac_v_notifyqueue ander
                WHERE (pcode = ander.fac_srtnotificatie_code  -- die is meestal al gewist, zou nog kunnen met andere tekst
                       OR pcode = 'BESUPD'
                         AND ander.fac_srtnotificatie_code IN ('BESNEW')
                       OR pcode IN ('RESUPD','RESBEV','RESOPT','RESCLN')
                         AND ander.fac_srtnotificatie_code IN ('RESNEW')
                       OR pcode IN ('RESUPD') -- geen RESUPD als pending RESBEV of RESOPT
                         AND ander.fac_srtnotificatie_code IN ('RESBEV','RESOPT')
                       OR pcode = 'MLDUPD'
                         AND ander.fac_srtnotificatie_code IN ('MLDNEW')
                      )
                      AND ander.fac_notificatie_refkey = pref
                      AND COALESCE (ander.fac_notificatie_extrakey, -1) =
                            COALESCE (pxref, -1)
                      AND COALESCE (ander.fac_notificatie_receiver_email, '@') =
                            COALESCE (lemail, '@')
                      AND COALESCE (ander.fac_notificatie_receiver_phone, '@') =
                            COALESCE (lphone, '@')
                      AND BITAND (fac_notificatie_status, 2 + 4) <> 0
                      AND ROWNUM=1; -- anders te ingewikkeld
            EXCEPTION
               WHEN NO_DATA_FOUND
               THEN
                  NULL;
            END;

            IF (oldercode IS NOT NULL)
            THEN
               -- Deze is zwakker dan de bestaande, deze hoeft dus niet nog eens via diezelfde kanalen.
               -- Het kan dus best zijn dat er niets over blijft.
               lmode := BITAND (lmode, 255 - oldersmode);
            END IF;

            -- Dan nog omgekeerd: trek eerdere zwakkere notificaties terug als deze pcode sterker is
            -- en die oudere nog niet de deur uit is. NB: de tracking blijft natuurlijk gewoon
            -- Ook portalberichten worden zo verminderd, en niet alleen die van de laatste 5 minuten
            -- De tracking tells it all! Gooi je zelf niks weg, dan resteert in de portal meestal de AFM, tof.
            -- Wellicht dat deze lijst nog wat groeit. Voor MLD durf ik alle weg te doen bij afmelden,
            -- Bij die andere is dat misschien wat riskanter als na TV nog KTV kan volgen en zo.
            CASE pcode
            WHEN 'MLDAFM'
            THEN
                fac.clrnotifications('MLD%', pref, 'MLDMAI');
            WHEN 'MLD2BO'
            THEN
                fac.clrnotifications('MLD2B3', pref);
            WHEN 'RESAFM'
            THEN
                fac.clrnotifications('RESUPD', pref, 'RESMAI');
                fac.clrnotifications('RESOPT', pref, 'RESMAI');
                fac.clrnotifications('RESBEV', pref, 'RESMAI');
            WHEN 'RESOPT'
            THEN
                fac.clrnotifications('RESUPD', pref);
            WHEN 'RESBEV'
            THEN
                fac.clrnotifications('RESUPD', pref);
            WHEN 'RESCLN'
            THEN
                fac.clrnotifications('RESUPD', pref);
            WHEN 'BESAFM'
            THEN
                fac.clrnotifications('BESUPD', pref, 'BESMAI');
            WHEN 'ORDAFM'
            THEN
                fac.clrnotifications('ORDUPD', pref);
            ELSE
                NULL;
            END CASE;

         END IF;
      END IF;

      IF lmode > 0 OR lsysteem IS NOT NULL
      THEN
         -- Stuur notificatie.
         INSERT INTO fac_notificatie (fac_srtnotificatie_key,
                                      fac_notificatie_status,
                                      prs_perslid_key_sender,
                                      prs_perslid_key_receiver,
                                      fac_notificatie_receiver_email,
                                      fac_notificatie_receiver_phone,
                                      fac_notificatie_oms,
                                      fac_notificatie_refkey,
                                      fac_notificatie_extrakey,
                                      fac_notificatie_prioriteit,
                                      fac_notificatie_sender_email,
                                      fac_notificatie_lang,
                                      fac_notificatie_systeemadres,
                                      fac_notificatie_attachments)
             VALUES (skey,
                     lmode,
                     pfrom,
                     pto,
                     lemail,
                     lphone,
                     soms,
                     pref,
                     pxref,
                     pprio,
                     psender,
                     llang,
                     lsysteem,
                     pattach);
      END IF;
   END;

   -- Ruim zoveel mogelijk notificatiespul op voor dit item uitgezonderd de meegegeven notificatie. Pcode mag een wildcard zijn!
   PROCEDURE clrnotifications (pcode VARCHAR2, pref NUMBER, puitz VARCHAR2 DEFAULT NULL)
   AS
      skey     fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
   BEGIN
      -- gequeuede berichten
      DELETE FROM fac_notificatie
       WHERE fac_srtnotificatie_key IN (SELECT sn.fac_srtnotificatie_key
                                          FROM fac_srtnotificatie sn
                                         WHERE sn.fac_srtnotificatie_code LIKE pcode
                                         AND sn.fac_srtnotificatie_code NOT IN (COALESCE(puitz, ' ')))
         AND fac_notificatie_refkey = pref;

      -- portalberichten. Hier moet je wel de dubbele webuser_messages verwijderen. Hier gaat het om ander notificaties als dan die net aangemaakt zijn.
      DELETE FROM web_user_messages
       WHERE fac_srtnotificatie_key IN (SELECT sn.fac_srtnotificatie_key
                                          FROM fac_srtnotificatie sn
                                         WHERE sn.fac_srtnotificatie_code LIKE pcode
                                         AND sn.fac_srtnotificatie_code NOT IN (COALESCE(puitz, ' ')))
         AND web_user_mess_action_params = pref;

   END;
   -- Nu de functie met twee extra parameters, de omschrijving en de ontvanger van de notificatie.
   PROCEDURE clrnotifications (pcode VARCHAR2, pref NUMBER, psubject VARCHAR2, preceiver NUMBER, pextrakey NUMBER)
   AS
      skey     fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
   BEGIN
      -- gequeuede berichten
      DELETE FROM fac_notificatie
       WHERE fac_srtnotificatie_key IN (SELECT sn.fac_srtnotificatie_key
                                          FROM fac_srtnotificatie sn
                                         WHERE sn.fac_srtnotificatie_code LIKE pcode)
         AND fac_notificatie_refkey = pref
         AND fac_notificatie_oms = psubject
         AND prs_perslid_key_receiver = preceiver
         AND COALESCE (fac_notificatie_extrakey, -1) = COALESCE (pextrakey, -1);

      -- portalberichten: fac_notificatie verwijderd de dubbelen wel in zijn insert trigger (nieuwste notificatie wordt hierna geinsert).
   END;

   -- Kijk welke jobs gescheduled zijn en nu uitgevoerd moeten worden.
   PROCEDURE putnotificationjobs (pcust VARCHAR2)
   AS
      CURSOR c1
      IS
         SELECT fac_notificatie_job_key, fac_notificatie_job_view, fac_notificatie_job_oms,
                fac_notificatie_job_interval, fac_notificatie_job_mode, fac_notificatie_job_nextrun,
                fac_notificatie_job_flags
           FROM fac_notificatie_job
          WHERE fac_notificatie_job_nextrun <= SYSDATE OR fac_notificatie_job_nextrun IS NULL;           -- eerste keer
       tnextrun fac_notificatie_job.fac_notificatie_job_nextrun%TYPE;
   BEGIN
      FOR rec1 IN c1
      LOOP
         -- Voer de job uit
         putjobnotifications (rec1.fac_notificatie_job_view, rec1.fac_notificatie_job_mode, rec1.fac_notificatie_job_flags);
         -- Notificeer de uitvoering van de job aan de beheerder(s)? Regel dat zelf maar!
         -- putsystemnotification ('Notificatiejob '  ||  rec1.fac_notificatie_job_oms  ||  ' uitgevoerd.', 1);
         -- Bepaal de volgende keer voor deze job, en garandeer dat dat altijd NA NU gaat gebeuren
         -- dus zonder dat eventueel overgeslagen keren nog zullen worden ingehaald.
         tnextrun := COALESCE (rec1.fac_notificatie_job_nextrun, SYSDATE);

         WHILE tnextrun <= SYSDATE
         LOOP
            -- Interval in hrs, DATE arithmetics are in days, so we will have to divide by 24 hrs a day
            UPDATE fac_notificatie_job
               SET fac_notificatie_job_nextrun =
                                 COALESCE (fac_notificatie_job_nextrun, SYSDATE)
                                 + rec1.fac_notificatie_job_interval / 24
             WHERE fac_notificatie_job_key = rec1.fac_notificatie_job_key;

            SELECT fac_notificatie_job_nextrun
              INTO tnextrun
              FROM fac_notificatie_job
             WHERE fac_notificatie_job_key = rec1.fac_notificatie_job_key;
         END LOOP;
      END LOOP;
   END;

   -- Voer nu job pcode uit.
   PROCEDURE putjobnotifications (pviewname VARCHAR2, pmode NUMBER, pflags NUMBER)
   AS
       TYPE noticursortype IS REF CURSOR;

       messages      noticursortype;
       lsender       fac_notificatie.prs_perslid_key_sender%TYPE;
       lreceiver     fac_notificatie.prs_perslid_key_receiver%TYPE;
       ltext         fac_notificatie.fac_notificatie_oms%TYPE;
       lcode         fac_srtnotificatie.fac_srtnotificatie_code%TYPE;
       lsrt_key      fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
       lrefkey       fac_notificatie.fac_notificatie_refkey%TYPE;
       lxkey         fac_notificatie.fac_notificatie_extrakey%TYPE;
       lxsender      fac_notificatie.fac_notificatie_sender_email%TYPE;
       lxemail       fac_notificatie.fac_notificatie_receiver_email%TYPE;
       lxmobile      fac_notificatie.fac_notificatie_receiver_phone%TYPE;
       attachments   fac_notificatie.fac_notificatie_attachments%TYPE;
   BEGIN
       -- The flags are for compatibility mostly. Flags 2 and 4 cannot be combined (useless)
       -- flags: 0=default,  1=view has xemail and xmobile, 2=use fac.notifytracking instead of fac.putnotification(srtprio)
       --        4=view has attachments, 8=view has xsender
       -- Kijk of de extra kolommen xemail en xmobile in de view zitten, dan gebruiken we die ook

       IF BITAND(pflags, 2) = 2 -- Alleen sender, text, code en xkey verplicht (met xkey zoals in fac_tracking zou komen)
       THEN
           OPEN messages FOR
               'SELECT sender, NULL receiver, text, code, fac_srtnotificatie_key, NULL key, xkey, NULL xsender, NULL xemail, NULL xmobile, NULL attachments'
               || ' FROM fac_srtnotificatie, ' || pviewname
               || ' WHERE code = fac_srtnotificatie_code';
       ELSIF BITAND(pflags, 13) = 13  /* 8+4+1 */
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, xsender, xemail, xmobile, attachments FROM '
               || pviewname;
       ELSIF BITAND(pflags, 9) = 9  /* 8+1 */
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, xsender, xemail, xmobile, NULL attachments FROM '
               || pviewname;
       ELSIF BITAND(pflags, 8) = 8
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, xsender, NULL xemail, NULL xmobile, NULL attachments FROM '
               || pviewname;
       ELSIF BITAND(pflags, 5) = 5  /* 4+1 */
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, NULL xsender, xemail, xmobile, attachments FROM '
               || pviewname;
       ELSIF BITAND(pflags, 4) = 4
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, NULL xsender, NULL xemail, NULL xmobile, attachments FROM '
               || pviewname;
       ELSIF BITAND(pflags, 1) = 1
       THEN
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, NULL xsender, xemail, xmobile, NULL attachments FROM '
               || pviewname;
       ELSE -- BITAND(pflags, 1) = 0
           OPEN messages FOR
               'SELECT sender, receiver, text, code, NULL fac_srtnotificatie_key, key, xkey, NULL xsender, NULL xemail, NULL xmobile, NULL attachments FROM '
               || pviewname;
       END IF;


       LOOP
           FETCH messages
           INTO lsender, lreceiver, ltext, lcode, lsrt_key, lrefkey, lxkey, lxsender, lxemail, lxmobile, attachments;

           EXIT WHEN messages%NOTFOUND;

           IF BITAND(pflags, 2) = 2
           THEN
               notifytracking(lsrt_key,
                              lsender,
                              ltext,
                              lxkey);
           ELSIF lcode IS NULL
           THEN
               putnotificationprio (lsender,
                                    lreceiver,
                                    ltext,
                                    pmode,
                                    lxemail,
                                    lxmobile,
                                    2,
                                    attachments);
           ELSE
               putnotificationsrtprio (lsender,
                                       lreceiver,
                                       lcode,
                                       lrefkey,
                                       ltext,
                                       pmode,
                                       lxemail,
                                       lxmobile,
                                       lxkey,
                                       2,
                                       lxsender,
                                       attachments);
           END IF;
       END LOOP;

       CLOSE messages;
   EXCEPTION
       WHEN OTHERS
       THEN
           raise_application_error (-20000,
                                    'FACILITOR: invalid notification job/view ' || pviewname);
   END;

   PROCEDURE executeactiviteit (pxmlnode VARCHAR2, pactkey NUMBER)
   AS
      CURSOR berekendeflex (
         mkey      IN            NUMBER,
         stdmkey   IN            NUMBER)
      IS
         -- de gedefinieerde verplichte sql-kenmerken die niet ingevuld zijn
         SELECT k.mld_kenmerk_key,
                REPLACE (k.mld_kenmerk_default, '##SQL##', '') mld_kenmerk_defaultsql
           FROM mld_srtkenmerk t,
                mld_kenmerk k,
                mld_stdmelding s,
                mld_discipline d
          WHERE s.mld_stdmelding_key = stdmkey
                AND s.mld_ins_discipline_key = d.ins_discipline_key
                AND ( (k.mld_stdmelding_key = s.mld_stdmelding_key AND k.mld_kenmerk_niveau = 'S')
                     OR (k.mld_stdmelding_key = s.mld_ins_discipline_key
                         AND k.mld_kenmerk_niveau = 'D')
                     OR (k.mld_stdmelding_key = d.ins_srtdiscipline_key
                         AND k.mld_kenmerk_niveau = 'T'))
                AND k.mld_kenmerk_verwijder IS NULL
                AND k.mld_srtkenmerk_key = t.mld_srtkenmerk_key
                AND k.mld_kenmerk_default LIKE '%##SQL##%'
                AND k.mld_kenmerk_verplicht = 1
                AND t.mld_srtkenmerk_verwijder IS NULL
                AND k.mld_kenmerk_volgnummer <= 900
                AND NOT EXISTS
                      (SELECT ''
                         FROM mld_kenmerkmelding km
                        WHERE     mld_kenmerkmelding_verwijder IS NULL
                              AND km.mld_kenmerk_key = k.mld_kenmerk_key
                              AND mld_melding_key = mkey);

      lkey               NUMBER (10);
      lstdmkey           mld_melding.mld_stdmelding_key%TYPE;
      lmld_ins_disc_key  mld_melding.mld_ins_discipline_key%TYPE;
      lresolveddefault   mld_kenmerkmelding.mld_kenmerkmelding_waarde%TYPE;
      lkey_m             NUMBER (10);
      lkey_new           NUMBER (10);
      luserkey           NUMBER (10);
      lvolgnr            mld_opdr.mld_opdr_bedrijfopdr_volgnr%TYPE;
      leinddatum         mld_melding.mld_melding_einddatum_std%TYPE;
   BEGIN
      BEGIN
         CASE pxmlnode
            WHEN 'melding'
            THEN
               SELECT mld_melding_key, mld_stdmelding_key
                 INTO lkey, lstdmkey
                 FROM mld_melding
                WHERE fac_activiteit_key = pactkey;

               SELECT mld_s_mld_melding_key.NEXTVAL INTO lkey_new FROM DUAL;

               INSERT INTO mld_melding (mld_melding_key,
                                        mld_melding_module,
                                        mld_meldbron_key,
                                        mld_alg_locatie_key,
                                        mld_alg_onroerendgoed_keys,
                                        mld_melding_datum,
                                        mld_melding_onderwerp,
                                        mld_melding_omschrijving,
                                        mld_melding_opmerking,
                                        mld_stdmelding_key,
                                        mld_melding_t_uitvoertijd,
                                        mld_melding_t_accepttijd,
                                        mld_kosten_klant,
                                        mld_melding_document,
                                        prs_kostenplaats_key,
                                        prs_perslid_key,
                                        mld_adres_key,
                                        mld_workflowstep_key,
                                        mld_melding_start_key,
                                        mld_melding_ordernr,
                                        mld_melding_spoed,
                                        mld_ins_discipline_key)
                  SELECT lkey_new,
                         mld_melding_module,
                         mld_meldbron_key,
                         mld_alg_locatie_key,
                         mld_alg_onroerendgoed_keys,
                         SYSDATE,
                         mld_melding_onderwerp,
                         mld_melding_omschrijving,
                            lcl.l ('lcl_mld_activity_generated_by')
                         || pactkey
                         || '. '
                         || mld_melding_opmerking,
                         mld_stdmelding_key,
                         mld_melding_t_uitvoertijd,
                         mld_melding_t_accepttijd,
                         mld_kosten_klant,
                         mld_melding_document,
                         prs_kostenplaats_key,
                         prs_perslid_key,
                         mld_adres_key,
                         NULL,
                         NULL,
                         mld_melding_ordernr,
                         mld_melding_spoed,
                         mld_ins_discipline_key
                    FROM mld_melding
                   WHERE mld_melding_key = lkey;

               -- Alle ingevulde kenmerken gewoon overnemen
               INSERT INTO mld_kenmerkmelding (mld_melding_key, mld_kenmerk_key, mld_kenmerkmelding_waarde)
                  SELECT lkey_new, mld_kenmerk_key, mld_kenmerkmelding_waarde
                    FROM mld_kenmerkmelding
                   WHERE mld_kenmerkmelding_verwijder IS NULL AND mld_melding_key = lkey;

               -- En van alle niet-ingevulde verplichte kenmerken de defaultwaarde berekenen als
               -- dat een dynamische waarde is. We ondersteunen hier niet de substitutie van
               -- ##PRS_PERSLID_KEY##, de melder is nl fixed en een eventueel daarvan afhangende
               -- waarde kan dat dan logischerwijs doorgaans ook zijn (versimpeling).
               FOR bf IN berekendeflex (lkey, lstdmkey) LOOP
                  BEGIN
                     EXECUTE IMMEDIATE (bf.mld_kenmerk_defaultsql) INTO lresolveddefault;

                     INSERT INTO mld_kenmerkmelding (mld_melding_key,
                                                     mld_kenmerk_key,
                                                     mld_kenmerkmelding_waarde)
                         VALUES (lkey_new, bf.mld_kenmerk_key, lresolveddefault);
                  END;
               END LOOP;

               INSERT INTO mld_melding_object (mld_melding_key, ins_deel_key)
                  SELECT lkey_new, ins_deel_key
                    FROM mld_melding_object
                   WHERE mld_melding_object_verwijder IS NULL AND mld_melding_key = lkey;

               mld.setmeldingstatus (lkey_new, 2, NULL);

               SELECT mld_ins_discipline_key
                 INTO lmld_ins_disc_key
                 FROM mld_melding
                WHERE mld_melding_key = lkey_new;

               IF lmld_ins_disc_key IS NOT NULL -- mldbhg notificatie
               THEN
                  mld.notifybackoffice (lkey_new, 'MLDBHG', 2);
               END IF;

               -- Eventuele (gewijzigde) objecten zijn nu ook opgeslagen.
               -- Nu kunnen pas de definitieve mld_melding.mld_melding_einddatum en mld_melding.mld_melding_einddatum_std bepaald en gezet worden.
               leinddatum := mld.geteinddatum (lkey_new);

               UPDATE mld_melding
                  SET mld_melding_einddatum = leinddatum, mld_melding_einddatum_std = leinddatum
                WHERE mld_melding_key = lkey_new;
            WHEN 'opdracht'
            THEN
               SELECT MAX (mld_opdr_key), MAX (mld_melding_key)
                 INTO lkey, lkey_m
                 FROM mld_opdr
                WHERE fac_activiteit_key = pactkey;

               SELECT mld.bepaalopdrmeldingvolgnr (lkey_m) INTO lvolgnr FROM DUAL;

               SELECT mld_s_mld_opdr_key.NEXTVAL INTO lkey_new FROM DUAL;

               INSERT INTO mld_opdr (mld_opdr_key,
                                     mld_opdr_module,
                                     mld_melding_key,
                                     mld_uitvoerende_keys,
                                     mld_typeopdr_key,
                                     mld_statusopdr_key,
                                     mld_standaardopdr_key,
                                     mld_opdr_omschrijving,
                                     mld_opdr_datumbegin,
                                     mld_opdr_einddatum,
                                     mld_opdr_werkzaamheden,
                                     mld_opdr_uren,
                                     mld_opdr_materiaal,
                                     mld_opdr_kosten,
                                     mld_opdr_contactpersoon,
                                     mld_opdr_bedrijfopdr_volgnr,
                                     mld_opdr_uurloon,
                                     prs_perslid_key,
                                     prs_contactpersoon_key,
                                     cnt_contract_key,
                                     cnt_contract_dienst_key,
                                     prs_kostenplaats_key,
                                     mld_opdr_ordernr,
                                     mld_opdr_teverzenden)
                  SELECT lkey_new,
                         mld_opdr_module,
                         mld_melding_key,
                         mld_uitvoerende_keys,
                         mld_typeopdr_key,
                         mld_statusopdr_key,
                         mld_standaardopdr_key,
                         mld_opdr_omschrijving,
                         SYSDATE,
                         mld_opdr_einddatum,
                            'Automatisch gegenereerd door activiteit '
                         || pactkey
                         || '. '
                         || mld_opdr_werkzaamheden,
                         mld_opdr_uren,
                         mld_opdr_materiaal,
                         mld_opdr_kosten,
                         mld_opdr_contactpersoon,
                         lvolgnr,
                         mld_opdr_uurloon,
                         prs_perslid_key,
                         prs_contactpersoon_key,
                         cnt_contract_key,
                         cnt_contract_dienst_key,
                         prs_kostenplaats_key,
                         mld_opdr_ordernr,
                         mld_opdr_teverzenden
                    FROM mld_opdr
                   WHERE mld_opdr_key = lkey;

               INSERT INTO mld_kenmerkopdr (mld_opdr_key, mld_kenmerk_key, mld_kenmerkopdr_waarde)
                  SELECT lkey_new, mld_kenmerk_key, mld_kenmerkopdr_waarde
                    FROM mld_kenmerkopdr
                   WHERE mld_kenmerkopdr_verwijder IS NULL AND mld_opdr_key = lkey;

               mld.setopdrachtstatus (lkey_new, 2, NULL);
            WHEN 'bestelling'
            THEN
               SELECT MAX (bes_bestelling_key), MAX (prs_perslid_key)
                 INTO lkey, luserkey
                 FROM bes_bestelling
                WHERE fac_activiteit_key = pactkey;

               SELECT bes_s_bes_bestelling_key.NEXTVAL INTO lkey_new FROM DUAL;

               -- Bestelling kopiëren.
               INSERT INTO bes_bestelling (bes_bestelling_key,
                                           bes_bestelling_module,
                                           bes_bestelling_status,
                                           bes_bestelling_datum,
                                           prs_perslid_key,
                                           prs_perslid_key_voor,
                                           bes_bestelling_opmerking,
                                           bes_bestelling_ordernr,
                                           bes_bestelling_plaats,
                                           mld_adres_key_lev,
                                           prs_kostenplaats_key,
                                           bes_bestelling_leverdatum,
                                           bes_bestelling_kosten_klant)
                  SELECT lkey_new,
                         bes_bestelling_module,
                         bes_bestelling_status,
                         SYSDATE,
                         prs_perslid_key,
                         prs_perslid_key_voor,
                            'Automatisch gegenereerd door activiteit '
                         || pactkey
                         || '. '
                         || bes_bestelling_opmerking,
                         bes_bestelling_ordernr,
                         bes_bestelling_plaats,
                         mld_adres_key_lev,
                         prs_kostenplaats_key,
                         TRUNC (SYSDATE + (bes_bestelling_leverdatum - bes_bestelling_datum)),
                         bes_bestelling_kosten_klant
                    FROM bes_bestelling
                   WHERE bes_bestelling_key = lkey;

               -- Kenmerken van bestelling kopiëren.
               INSERT INTO bes_kenmerkbestell (bes_bestelling_key, bes_kenmerk_key, bes_kenmerkbestell_waarde)
                  SELECT lkey_new, bes_kenmerk_key, bes_kenmerkbestell_waarde
                    FROM bes_kenmerkbestell
                   WHERE bes_kenmerkbestell_verwijder IS NULL AND bes_bestelling_key = lkey;

               -- Bestelregels kopiëren.
               INSERT INTO bes_bestelling_item (bes_bestelling_key,
                                                bes_bestelling_item_aantal,
                                                bes_bestelling_item_aantalontv,
                                                bes_bestelling_item_brutoprijs,
                                                bes_bestelling_item_prijs,
                                                bes_srtdeel_key)
                  SELECT lkey_new,
                         bes_bestelling_item_aantal,
                         bes_bestelling_item_aantalontv,
                         bes.getsrtdeelprijsinfo (bi.bes_srtdeel_key, SYSDATE, 1),
                         bes.getsrtdeelprijsinfo (bi.bes_srtdeel_key, SYSDATE, 1),
                         bi.bes_srtdeel_key
                    FROM bes_bestelling_item bi, bes_srtdeel isd
                   WHERE bes_bestelling_key = lkey AND isd.bes_srtdeel_key = bi.bes_srtdeel_key;

               -- Kenmerken van bestelregels kopiëren.
               INSERT INTO bes_kenmerkbesteli (bes_bestelling_item_key,
                                               bes_kenmerk_key,
                                               bes_kenmerkbesteli_waarde,
                                               bes_kenmerkbesteli_aanmaak)
                  SELECT tab1.bes_bestelling_item_key_new,
                         kbi.bes_kenmerk_key,
                         kbi.bes_kenmerkbesteli_waarde,
                         SYSDATE
                    FROM bes_kenmerkbesteli kbi,
                         (SELECT bi1.bes_bestelling_item_key bes_bestelling_item_key_new,
                                 bi.bes_bestelling_item_key,
                                 bi.bes_srtdeel_key
                            FROM bes_bestelling_item bi, bes_bestelling_item bi1
                           WHERE     bi.bes_bestelling_key = lkey
                                 AND bi1.bes_srtdeel_key = bi.bes_srtdeel_key
                                 AND bi1.bes_bestelling_key = lkey_new) tab1
                   WHERE kbi.bes_bestelling_item_key = tab1.bes_bestelling_item_key
                         AND kbi.bes_bestelling_item_key IN (SELECT bes_bestelling_item_key
                                                               FROM bes_bestelling_item bi
                                                              WHERE bi.bes_bestelling_key = lkey);

               -- Het gaat hier om een geplande actie. Je mag er van uit gaan dat deze goed gekeurd is voordat deze is aangemaakt.
               -- Daarom bestellingstatus op 4 zetten (Geaccepteerd) en bestelopdracht automatisch aanmaken.
               bes.setbestellingstatus (lkey_new, 4, luserkey);                      -- Geaccepteerd
               bes.makeorders (luserkey, lkey_new); -- bes_makeorders notificeert zelf de leveranciers.
         END CASE;
      EXCEPTION
         WHEN OTHERS
         THEN
            putsystemnotification (
                  'Facilitor scheduler: opdracht voor activiteit '
               || pactkey
               || ' kan niet worden aangemaakt: '
               || SQLERRM,
               3);
      END;
   END;

   PROCEDURE executeschedules
   AS
      newaantal   NUMBER;

      -- Interval in hrs, DATE arithmetics are in days, so we will have to divide interval by 24 hrs a day
      CURSOR c1
      IS
        SELECT fac_activiteit_key, fac_activiteit_xmlnode, fac_activiteit_status_key, fac_activiteit_interval,
           fac_activiteit_aantal
        FROM fac_activiteit
        WHERE ((fac_activiteit_volgende <= SYSDATE AND fac_activiteit_einddatum IS NULL)
            OR (fac_activiteit_volgende <= SYSDATE AND SYSDATE < fac_activiteit_einddatum)
            OR (fac_activiteit_volgende IS NULL AND SYSDATE BETWEEN fac_activiteit_eerste AND fac_activiteit_einddatum)
            OR (fac_activiteit_volgende IS NULL AND fac_activiteit_einddatum IS NULL AND SYSDATE > fac_activiteit_eerste)
           )
        AND fac_activiteit_status_key IN (2, 3)
        AND fac_activiteit_xmlnode IS NOT NULL
        AND NVL (fac_activiteit_aantal, 1) > 0
        AND fac_activiteit_verwijder IS NULL;
   BEGIN
      FOR rec1 IN c1
      LOOP
         -- Voer de job uit
         newaantal := NULL;
         IF rec1.fac_activiteit_aantal IS NOT NULL
         THEN
            newaantal := rec1.fac_activiteit_aantal - 1;
         END IF;

         IF rec1.fac_activiteit_status_key = 2
         THEN
            executeactiviteit (rec1.fac_activiteit_xmlnode, rec1.fac_activiteit_key);

            -- Remove time-part from volgende (ie. set to 0:00)!
            UPDATE fac_activiteit
               SET fac_activiteit_volgende = TRUNC(COALESCE (fac_activiteit_volgende, fac_activiteit_eerste) + fac_activiteit_interval / 24),
                   fac_activiteit_laatste = SYSDATE,
                   fac_activiteit_aantal = newaantal
             WHERE fac_activiteit_key = rec1.fac_activiteit_key;
         ELSE
            -- 1 keer overslaan, de volgende keer weer wel dus. Ook hier: remove time-part from volgende (ie. set to 0:00)!
            UPDATE fac_activiteit
               SET fac_activiteit_volgende = TRUNC(COALESCE (fac_activiteit_volgende, fac_activiteit_eerste) + fac_activiteit_interval / 24),
                   fac_activiteit_status_key = 2,
                   fac_activiteit_aantal = newaantal
             WHERE fac_activiteit_key = rec1.fac_activiteit_key;
         END IF;
      END LOOP;
   END;

   -- MarkOrderAsSent
   -- Markeert dat een opdracht verzonden is, zoals door PutOrders.exe
   -- Hoe dat moet is afhankelijk van het soort opdracht: bestelopdracht, meldingsopdracht e.d.
   -- pxmlnode is (lowercase!) de xml-naam van de entiteit (opdracht, bestelopdr)
   -- pkey is de key daarvan
   -- presult geeft het succes van de verzending aan: 0=succesvol, 1=geweigerd, 2=geeninhoudelijkrespons
   --  wat gebruikt *kan* worden bij het bepalen van de status die gezet moet worden.
   -- Schijnbaar is ook presult<0 nog mogelijk als er technisch wat mis ging. Die tracken we ook maar eens gewoon?
   -- presulttext bevat (optioneel) de feedback van de leverancier (bijv. reden van afwijzing bestelopdracht)
   -- Voor customer-orderqueues dient overeenkomstige CUST.markorderassent() te worden gedefinieerd.
   -- Bij presult=2 weet je niet zeker wat de leverancier heeft gedaan, bij mail veronderstel je ontvangst, maar weet je
   --   het niet zeker. Met 5.1.2 (UWVA#21074) is daarvoor de status 4 tussengevoegd
   PROCEDURE markorderassent (pxmlnode VARCHAR2, pkey NUMBER, presult NUMBER, presulttext VARCHAR2, pperslid_key NUMBER DEFAULT NULL)
   AS
      order_confirm prs_bedrijf.prs_bedrijf_order_confirm%TYPE;
      melding_key   mld_opdr.mld_melding_key%TYPE;
   BEGIN
      CASE pxmlnode
         WHEN 'opdracht'
         THEN
            IF presult = 0
            THEN
               UPDATE   mld_opdr
                  SET   mld_opdr_verzonden = SYSDATE
                WHERE   mld_opdr_key = pkey;

               fac.trackaction ('ORDSNT', pkey, pperslid_key, NULL, presulttext);
               mld.setopdrachtstatus (pkey, 5, NULL);
            ELSIF presult = 1 -- (Logische) fout
            THEN
               UPDATE   mld_opdr
                  SET   mld_opdr_verzonden = SYSDATE
                WHERE   mld_opdr_key = pkey;

               fac.trackaction ('ORDSNT', pkey, pperslid_key, NULL, presulttext);
               -- status van de opdracht wordt afgewezen.
               mld.setopdrachtstatus (pkey, 1, NULL);
            ELSIF presult = 2
            THEN
               fac.trackaction ('ORDSNT', pkey, pperslid_key, NULL, presulttext);
               -- De leverancier heeft geen feedback gegeven, wat is het gedrag dan voor deze leverancier?
               SELECT b.prs_bedrijf_order_confirm
                 INTO order_confirm
                 FROM prs_bedrijf b, mld_opdr o
                WHERE b.prs_bedrijf_key = o.mld_uitvoerende_keys
                  AND o.mld_opdr_key = pkey;

               -- NOTA BENE
               -- Zo is deze nog prima compatible. Status 8 is in het huidige gebruik nog niet identiek, dus
               -- daar wachten we nog even mee op een diepgaander design, vandaar if true.
               IF TRUE OR order_confirm = 1
               THEN
                  -- Er volgt later nog een expliciete bevestiging (5=Ter Bevestiging)
                  UPDATE   mld_opdr
                     SET   mld_opdr_verzonden = SYSDATE
                   WHERE   mld_opdr_key = pkey;
                  mld.setopdrachtstatus (pkey, 5, pperslid_key);
               ELSE
                  UPDATE   mld_opdr
                     SET   mld_opdr_verzonden = SYSDATE
                   WHERE   mld_opdr_key = pkey;
                  mld.setopdrachtstatus (pkey, 8, pperslid_key);
               END IF;
            ELSIF presult < 0
            THEN
               -- just track, nothing changes really
               fac.trackaction ('ORDSNX', pkey, pperslid_key, NULL, presulttext);
            END IF;

            SELECT mld_melding_key
              INTO melding_key
              FROM mld_opdr o
             WHERE o.mld_opdr_key = pkey;

            -- als laatste de status van de melding aanpassen.
            mld.updatemeldingstatus (melding_key, 0, pperslid_key);
         WHEN 'bestelopdr'
         THEN
            IF presult = 0
            THEN
               -- Succesvol
               fac.trackaction ('BES2SN', pkey, pperslid_key, NULL, presulttext);
               bes.acceptopdracht(pkey, NULL, presulttext);
            ELSIF presult = 1 -- (Logische) fout
            THEN
               fac.trackaction ('BES2SX', pkey, pperslid_key, NULL, presulttext);
               bes.rejectopdracht(pkey, NULL, presulttext);
            ELSIF presult = 2
            THEN
                -- De leverancier heeft geen feedback gegeven, wat is het gedrag dan voor deze leverancier?
               SELECT b.prs_bedrijf_order_confirm
                 INTO order_confirm
                 FROM prs_bedrijf b, bes_bestelopdr o
                WHERE b.prs_bedrijf_key = o.prs_bedrijf_key
                  AND o.bes_bestelopdr_key = pkey;
                fac.trackaction ('BES2SN', pkey, pperslid_key, NULL, presulttext);

                IF order_confirm = 1
                THEN
                  -- Er volgt later nog een expliciete bevestiging (5=Ter Bevestiging)
                  UPDATE   bes_bestelopdr
                     SET   bes_bestelopdr_status = 5
                   WHERE   bes_bestelopdr_key = pkey;
                  fac.trackaction ('BES2BE', pkey, pperslid_key, NULL, presulttext);
                ELSE
                    bes.acceptopdracht(pkey, pperslid_key, presulttext);
                END IF;
            ELSIF presult < 0
            THEN
               -- just track, nothing changes really
               fac.trackaction ('BES2SX', pkey, pperslid_key, NULL, presulttext);
            END IF;
         WHEN 'contract'
         THEN
            IF presult = 1
            THEN  -- (Logische) fout; kan nog niet voorkomen voorlopig, maar toch
               fac.trackaction ('CNTSNX', pkey, pperslid_key, NULL, presulttext);
            ELSE
               -- Succesvol
               fac.trackaction ('CNTSNT', pkey, pperslid_key, NULL, presulttext);
            END IF;
            -- reset
            UPDATE cnt_contract
               SET cnt_contract_teverzenden = NULL
             WHERE cnt_contract_key = pkey;
      END CASE;
   END;

   -- Returnt de geldende waarde voor setting pname, bepaald uit de settingstabel
   FUNCTION getSetting (pname IN VARCHAR2 ) RETURN VARCHAR2
   AS
     lres fac_setting.fac_setting_default%TYPE;
   BEGIN
     SELECT COALESCE(fac_setting_pvalue, fac_setting_default)
       INTO lres
       FROM fac_setting
      WHERE fac_setting_name = pname;

     RETURN lres;
   END;

   -- add a trackrecord for action pcode, entity prefkey. pdatum may be NULL
   -- Doet niets als er een onbekende pcode wordt meegegeven
   -- We willen (blijkt) toch ook kunnen regelen dat een tracking per se NIET wordt genotificeerd
   -- Dat doen we door pcode te prefixen met #. Deze wordt dan stiekem voor aan poms geplakt
   -- (want anders kunnen we niet inserten) en de trigger op fac_tracking doet dan de rest.
   PROCEDURE trackaction (pcode VARCHAR2, prefkey NUMBER, puserkey NUMBER, pdatum DATE, poms VARCHAR2)
   AS
      tkey   fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
      loms   fac_tracking.fac_tracking_oms%TYPE;
      lcode  VARCHAR2(10);  -- tabelsize is 6

   BEGIN
      IF pcode IS NULL
      THEN
         RETURN;
      END IF;

      IF prefkey IS NULL
      THEN
         RETURN;
      END IF;

      lcode := pcode;
      loms := poms;

      IF SUBSTR(lcode,1,1) = '#'
      THEN
        lcode := SUBSTR(lcode, 2);
        IF poms IS NULL
        THEN
            loms := '#';
        ELSE
            loms := '#' || loms;
        END IF;
      END IF;

      SELECT   fac_srtnotificatie_key
        INTO   tkey
        FROM   fac_srtnotificatie
       WHERE   fac_srtnotificatie_code = lcode;

      IF pdatum IS NULL
      THEN
         INSERT INTO fac_tracking (fac_tracking_refkey, prs_perslid_key, fac_srtnotificatie_key, fac_tracking_oms
                                  )
           VALUES   (prefkey, puserkey, tkey, loms
                    );
      ELSE
         INSERT INTO fac_tracking (fac_tracking_refkey, prs_perslid_key, fac_srtnotificatie_key, fac_tracking_datum, fac_tracking_oms
                                  )
           VALUES   (prefkey, puserkey, tkey, pdatum, loms
                    );
      END IF;
   EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
         NULL;
   END;

   -- Pas de datum van een tracking record aan
   -- Userkey wordt nog genegeerd
   PROCEDURE backtrackaction (pcode       VARCHAR2,
                              prefkey     NUMBER,
                              puserkey    NUMBER,
                              pdatum      DATE)
   AS
      tkey   fac_srtnotificatie.fac_srtnotificatie_key%TYPE;
   BEGIN
      SELECT fac_srtnotificatie_key
        INTO tkey
        FROM fac_srtnotificatie
       WHERE fac_srtnotificatie_code = pcode;

      UPDATE fac_tracking
         SET fac_tracking_datum = pdatum
       WHERE fac_tracking_refkey = prefkey AND fac_srtnotificatie_key = tkey
             AND fac_tracking_key =
                   (SELECT MAX (fac_tracking_key)
                      FROM fac_tracking T
                     WHERE t.fac_srtnotificatie_key = tkey
                           AND fac_tracking_refkey = prefkey);
   END;

   -- create a notification using only trackrecord parameters
   -- Indeed, we could do this using only the fac_tracking_key, but we do not
   -- if no ptracking_oms is given, we construct one!
   -- Binnen deze procedure weten we nog net wat we doen
   -- (nl. notificeren van een trackrecord van een bekende entiteit)
   -- Een stap verder niet meer, dan is het gewoon een bericht met
   -- een zender en ontvanger.
    PROCEDURE notifytracking (psrtnotificatiekey    NUMBER,
                              pperslid_key          NUMBER,
                              ptracking_oms         VARCHAR2,
                              prefkey               NUMBER)
    AS
        lxmlnode                 fac_srtnotificatie.fac_srtnotificatie_xmlnode%TYPE;
        lcode                    fac_srtnotificatie.fac_srtnotificatie_code%TYPE;
        lcode2                   fac_srtnotificatie.fac_srtnotificatie_code%TYPE;
        loms                     fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
        loms2                    fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
        lsrtnotigebouw           fac_srtnotificatie.fac_srtnotificatie_gebouw%TYPE;
        bericht                  fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
        bericht2                 fac_srtnotificatie.fac_srtnotificatie_oms%TYPE;
        lreceiver                fac_notificatie.prs_perslid_key_receiver%TYPE;
        lreceiver2               fac_notificatie.prs_perslid_key_receiver%TYPE;
        lsender                  VARCHAR2 (255 CHAR);
        lmld_stdmelding_key      mld_stdmelding.mld_stdmelding_key%TYPE;
        lnotify                  mld_disc_params.mld_disc_params_notify%TYPE;
        lmnotify                 mld_stdmelding.mld_stdmelding_notify%TYPE;
        lbonotify                mld_disc_params.mld_disc_params_bonotify%TYPE;
        lbenotify                mld_disc_params.mld_disc_params_benotify%TYPE;
        lworkflowstep_key        mld_melding.mld_workflowstep_key%TYPE;
        lstart_key               mld_melding.mld_melding_start_key%TYPE;
        luitvtype                mld_v_uitvoerende.TYPE%TYPE;
        lemail                   prs_bedrijf.prs_bedrijf_email%TYPE;
        lemail3                  alg_gebouw.alg_gebouw_email%TYPE;
        lphone                   prs_bedrijf.prs_bedrijf_telefoon2%TYPE;
        lreskey                  res_rsv_ruimte.res_reservering_key%TYPE;
        lres_ruimte_opstel_key   res_rsv_ruimte.res_ruimte_opstel_key%TYPE;
        lrefkey                  NUMBER (10);
        lxrefkey                 res_rsv_ruimte.res_rsv_ruimte_key%TYPE;
        lalg_ruimte_key          res_rsv_ruimte.alg_ruimte_key%TYPE;
        lalglocatiekey           alg_locatie.alg_locatie_key%TYPE;
        lalgonrgoedkey           mld_melding.mld_alg_onroerendgoed_keys%TYPE;
        lmld_adres_key           mld_adres.mld_adres_key%TYPE;
        lprs_bedrijf_key         prs_bedrijf.prs_bedrijf_key%TYPE;
        lmldbehandelaar          mld_melding.mld_melding_behandelaar_key%TYPE;
        lmldbehandelaar2         mld_melding.mld_melding_behandelaar2_key%TYPE;
        lafs_key                 bez_afspraak.bez_afspraak_key%TYPE;
        lins_deel_ley            ins_deel.ins_deel_key%TYPE;
        lpoolnotified            NUMBER;
        lspoed                   mld_melding.mld_melding_spoed%TYPE;
    BEGIN
        -- Het gaat om een notificatie bij een entiteit prefkey,
        -- waarvan het type alleen nog moet worden bepaald via
        -- de srtnotificatie. Dan kan van het record van die
        -- entiteit de ontvanger van de notificatie worden bepaald
        -- (normaal de aanvrager) en wordt het bericht gequeueud.
        -- De afzender is nu ook bekend (vroeger niet)
        -- DIT VERVANGT (5I) ALLE NOTIFICATIETRIGGERS bij de verschillende
        -- entiteiten (met uitzondering van opdrachtnotificatie naar buiten!?
        IF psrtnotificatiekey IS NULL
        THEN
            RETURN;
        END IF;

        IF prefkey IS NULL
        THEN
            RETURN;
        END IF;

        lrefkey := prefkey;                            -- dan kunnen we bij reservering nog wijzigen
        bericht := ptracking_oms;                                  -- als die leeg is zoeken we zelf
        lpoolnotified := 0;

        -- welk type?
        SELECT fac_srtnotificatie_xmlnode,
               fac_srtnotificatie_code,
               lcl.x('fac_srtnotificatie_oms', fac_srtnotificatie_key, fac_srtnotificatie_oms),
               fac_srtnotificatie_gebouw
          INTO lxmlnode,
               lcode,
               loms,
               lsrtnotigebouw
          FROM fac_srtnotificatie
         WHERE fac_srtnotificatie_key = psrtnotificatiekey;

        -- Bepaal de ontvanger enzo van dit bericht
        CASE
            WHEN lxmlnode = 'melding'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := mld.mldsprintf (loms, prefkey);
                END IF;

                -- basisgegevens, de ontvanger enzo
                SELECT CASE WHEN fac.getsetting ('progress_notification_for') = 'true'
                            THEN m.prs_perslid_key_voor
                            ELSE m.prs_perslid_key
                            END  receiver,
                       m.mld_stdmelding_key,
                       m.mld_adres_key,
                       m.mld_alg_locatie_key,
                       m.mld_alg_onroerendgoed_keys,
                       mld_disc_params_notify,
                       stdm.mld_stdmelding_notify,
                       d.ins_discipline_email,
                       mp.mld_disc_params_bonotify,
                       mp.mld_disc_params_benotify,
                       m.mld_melding_behandelaar_key,
                       m.mld_melding_behandelaar2_key,
                       m.mld_workflowstep_key,
                       m.mld_melding_start_key,
                       m.mld_melding_spoed
                  INTO lreceiver,
                       lmld_stdmelding_key,
                       lmld_adres_key,
                       lalglocatiekey,
                       lalgonrgoedkey,
                       lnotify,
                       lmnotify,
                       lsender,
                       lbonotify,
                       lbenotify,
                       lmldbehandelaar,
                       lmldbehandelaar2,
                       lworkflowstep_key,
                       lstart_key,
                       lspoed
                  FROM mld_melding m,
                       mld_stdmelding stdm,
                       ins_tab_discipline d,
                       mld_disc_params mp
                 WHERE     stdm.mld_stdmelding_key = m.mld_stdmelding_key
                       AND d.ins_discipline_key = stdm.mld_ins_discipline_key
                       AND mp.mld_ins_discipline_key = d.ins_discipline_key
                       AND m.mld_melding_key = prefkey;

                IF ((lspoed<=2 OR lbonotify > 0) AND (lcode = 'MLDNEW' OR lcode = 'MLDFWD' OR lcode = 'MLDACP')) -- Bij aanmaak, doorsturen of accepteren van de melding.
                THEN
                    -- merk op: bij doorsturen binnen dezelfde discipline of met dezelfde bo-ers ontvangt men
                    -- dan dus nogmaals een notificatie. Dat accepteren we vooralsnog gewoon maar.
                    mld.notifybackoffice (prefkey, lcode);
                END IF;
                -- Notificatie bij nieuwe melding per prioriteit
                IF (lcode = 'MLDNEW')
                THEN
                    mld.notifyprio (prefkey, lspoed, pperslid_key);
                END IF;
                -- We sturen soms TWEE notificaties, naar melder en nieuwe behandelaar
                -- De normale notificatie gaat naar de melder
                -- Moeten dus kijken of psrtnotificatiekey naar MLDBEH wijst
                -- Dan moeten we ook MLDBE2 versturen naar de behandelaar, al was ik het zelf.
                IF lbenotify > 0
                THEN
                   IF lcode = 'MLDBEH' AND lmldbehandelaar IS NOT NULL
                   THEN
                       lcode2 := 'MLDBE2';
                       lreceiver2 := lmldbehandelaar;

                       SELECT lcl.x('fac_srtnotificatie_oms', fac_srtnotificatie_key, fac_srtnotificatie_oms)
                         INTO loms2
                         FROM fac_srtnotificatie
                        WHERE fac_srtnotificatie_code = lcode2;

                       bericht2 := mld.mldsprintf (loms2, prefkey);
                   ELSIF lcode = 'MLDABH' AND lmldbehandelaar2 IS NOT NULL
                   THEN
                       lcode2 := 'MLDAB2';
                       lreceiver2 := lmldbehandelaar2;

                       SELECT lcl.x('fac_srtnotificatie_oms', fac_srtnotificatie_key, fac_srtnotificatie_oms)
                         INTO loms2
                         FROM fac_srtnotificatie
                        WHERE fac_srtnotificatie_code = lcode2;

                       bericht2 := mld.mldsprintf (loms2, prefkey);
                   END IF;
                END IF;
                -- Meldingen worden eventueel niet genotificeerd naar de melder als
                -- settings daartoe aanleiding geven. We weten genoeg om te checken of dat zo is
                -- Maar een MLDNOT-notificatie moet altijd, ongeacht deze settings
                IF lcode <> 'MLDNOT'
                   AND (lnotify = 0
                       OR (lnotify = 2 AND lmnotify = 0)
                       OR (    lworkflowstep_key IS NOT NULL
                           AND lstart_key <> prefkey
                           AND fac.getsetting ('mld_notify_workflowsteps') = 0)
                       )
                THEN
                    -- No first notification: disc says no OR disc says: see stdmelding and stdmelding says no
                    -- OR this is part of a workflow and settings says not to notify workflowsteps
                    IF lcode2 IS NULL
                    THEN
                        -- Also no second notification
                        RETURN;
                    ELSE
                        -- Only a second notification, so that will be the first
                        lcode := lcode2;
                        lreceiver := lreceiver2;
                        bericht := bericht2;
                        lcode2 := NULL;
                        lreceiver2 := NULL;
                        bericht2 := NULL;
                    END IF;
                END IF;

                IF lsender IS NULL
                THEN
                    -- (kennelijk) is de verantwoordelijke voor van het afleveradres iets sterker of zo
                    IF lmld_adres_key IS NOT NULL
                    THEN
                        SELECT l1.alg_locatie_email
                          INTO lsender
                          FROM mld_adres ma, alg_locatie l1
                         WHERE ma.mld_adres_key = lmld_adres_key
                               AND ma.alg_locatie_key = l1.alg_locatie_key(+);
                    END IF;

                    IF lsender IS NULL AND lalglocatiekey IS NOT NULL         -- nog steeds onbekend
                    THEN
                        SELECT l2.alg_locatie_email
                          INTO lsender
                          FROM alg_locatie l2
                         WHERE l2.alg_locatie_key = lalglocatiekey;
                    END IF;
                END IF;

                -- Controleren of er ook naar het gebouw een mailtje gestuurd moet worden.
                IF lsrtnotigebouw = 1 AND lalgonrgoedkey IS NOT NULL
                THEN
                    -- Stuur een notificatie naar het e-mail adres van het gebouw.
                    SELECT g.alg_gebouw_email
                      INTO lemail3
                      FROM alg_v_allonroerendgoed aog,
                           alg_gebouw g
                     WHERE aog.alg_gebouw_key = g.alg_gebouw_key
                       AND aog.alg_onroerendgoed_keys = lalgonrgoedkey;
                END IF;
            WHEN lxmlnode = 'opdracht'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := mld.opdrsprintf (loms, prefkey);
                END IF;

                -- In 4i notificeerden we alleen toekenning aan de uitvoerder
                -- In 5i tracken we veel meer, en is er dus ook de mogelijkheid
                -- tot notificeren van tracking
                -- Echter: wie houden we hiervan nou actief op de hoogte?
                -- Dat wordt de interne contacpersoon van de opdracht
                -- behalve:
                -- 1) ORDMLE/ORDMLI en ORDONO/ORDOOK, die naar de uitvoerder
                -- 2) ORDDAT, die naar de gebouwverantwoordelijke van het betreffende gebouw van de onderliggende melding
                IF lcode IN ('ORDMLI', 'ORDMLE', 'ORDONO', 'ORDOOK')
                THEN
                    SELECT mld_uitvoerende_keys
                      INTO lreceiver
                      FROM mld_opdr
                     WHERE mld_opdr_key = prefkey;

                    SELECT u.TYPE
                      INTO luitvtype
                      FROM mld_v_uitvoerende u
                     WHERE u.mld_uitvoerende_key = lreceiver;

                    IF luitvtype = 'B'
                    THEN
                        -- Het gaat om een bedrijf receiver
                        BEGIN
                            SELECT prs_bedrijf_email, prs_bedrijf_telefoon2
                              INTO lemail, lphone
                              FROM prs_bedrijf
                             WHERE prs_bedrijf_key = lreceiver;

                            -- Nooit op een portal voor externen
                            -- want dan zou iedereen die notificatie zien ivm lege receiver
                            -- dit overrulet dus iedere setting!
                            -- TODO smode :=    BITAND (smode, 255 - 1);
                            -- voorkom dat er een bericht wordt gestuurd
                            -- aan een perslid_key als deze bedrijf_key
                            lreceiver := NULL;
                        EXCEPTION
                            WHEN NO_DATA_FOUND
                            THEN
                                RETURN;
                        END;
                    END IF;
                ELSIF lcode = 'ORDDAT'
                THEN
                    BEGIN
                        SELECT COALESCE(g.prs_perslid_key_verantw, g.prs_perslid_key_verantw2) verantwoordelijke
                          INTO lreceiver
                          FROM mld_opdr o,
                               mld_melding m,
                               alg_v_onroerendgoed org,
                               alg_gebouw g
                         WHERE o.mld_melding_key = m.mld_melding_key
                           AND m.mld_alg_onroerendgoed_keys = org.alg_onroerendgoed_keys
                           AND org.alg_gebouw_key = g.alg_gebouw_key
                           AND COALESCE(g.prs_perslid_key_verantw, g.prs_perslid_key_verantw2) IS NOT NULL
                           AND o.mld_opdr_key = prefkey;
                    EXCEPTION
                        WHEN NO_DATA_FOUND
                        THEN
                            RETURN;
                    END;
                ELSE
                    -- ons intern contactpersoon krijgt in alle andere gevallen het bericht
                    SELECT prs_perslid_key
                      INTO lreceiver
                      FROM mld_opdr
                     WHERE mld_opdr_key = prefkey;
                END IF;

                -- NOTE TO SELF:
                -- Bij ORDMLI/ORDMLE en eigenlijk ook ORDGOE worden derden ingelicht,
                -- bij ORDGOE tevens de opdrachtcontactpersoon, bij die andere twee niet
                -- De mode (mail e.d.) van de notificatie bij dat event kan maar 1 waarde bevatten
                -- Momenteel is dat
                -- ORDMLI/ORDMLE: hoe ontvangt de uitvoerder dit bericht, contactpersoon krijgt niks
                -- ORDGOE: hoe ontvangt de contactpersoon
                -- ORD2GO: hoe ontvangt de uitvoerder dit bericht
                -- Bij ORDGOE willen we *ook* de goedkeurder inlichten
                IF lcode = 'ORDGOE'
                THEN
                    mld.notifyopdrgoedkeurders (prefkey);
                END IF;

                -- de afzender
                BEGIN
                    SELECT ins_discipline_email,
                           m.mld_alg_onroerendgoed_keys
                      INTO lsender,
                           lalgonrgoedkey
                      FROM ins_tab_discipline d,
                           mld_stdmelding sm,
                           mld_melding m,
                           mld_opdr o
                     WHERE     d.ins_discipline_key = sm.mld_ins_discipline_key
                           AND sm.mld_stdmelding_key = m.mld_stdmelding_key
                           AND o.mld_melding_key = m.mld_melding_key
                           AND o.mld_opdr_key = prefkey;

                    IF lsender IS NULL
                    THEN
                        SELECT COALESCE (l1.alg_locatie_email, l2.alg_locatie_email),
                               m.mld_alg_onroerendgoed_keys
                          INTO lsender,
                               lalgonrgoedkey
                          FROM mld_melding m,
                               mld_adres ma,
                               alg_locatie l1,
                               alg_locatie l2,
                               mld_opdr o
                         WHERE     m.mld_adres_key = ma.mld_adres_key(+)
                               AND ma.alg_locatie_key = l1.alg_locatie_key(+)
                               AND m.mld_alg_locatie_key = l2.alg_locatie_key(+)
                               AND o.mld_melding_key = m.mld_melding_key
                               AND o.mld_opdr_key = prefkey;
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        NULL;
                END;

                -- Controleren of er ook naar het gebouw een mailtje gestuurd moet worden.
                IF lsrtnotigebouw = 1 AND lalgonrgoedkey IS NOT NULL
                THEN
                    -- Stuur een notificatie naar het e-mail adres van het gebouw.
                    SELECT g.alg_gebouw_email
                      INTO lemail3
                      FROM alg_v_allonroerendgoed aog,
                           alg_gebouw g
                     WHERE aog.alg_gebouw_key = g.alg_gebouw_key
                       AND aog.alg_onroerendgoed_keys = lalgonrgoedkey;
                END IF;
            WHEN lxmlnode = 'reservering' OR lxmlnode = 'xreservering'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := res.sprintf (loms, prefkey);
                END IF;

                BEGIN
                    SELECT res_rsv_ruimte_contact_key,
                           res_ruimte_opstel_key,
                           alg_ruimte_key,
                           res_reservering_key
                      INTO lreceiver,
                           lres_ruimte_opstel_key,
                           lalg_ruimte_key,
                           lreskey
                      FROM res_rsv_ruimte
                     WHERE res_rsv_ruimte_key = prefkey
                       AND (res_rsv_ruimte_dirtlevel = 0 OR lcode = 'RESDIR');
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        RETURN;         -- Normaliter geen niet bestaande of dirty reserveringen notificeren
                END;

                -- de afzender, en moeten we wel?
                lnotify := NULL;
                lbonotify := 0;
                BEGIN
                    -- Bepaal afzender, afhankelijk van catalogus (als res_ruimte) of anders de locatie
                    -- en check of er voor deze ruimtecatalogus uberhaupt moet worden genotificeerd
                    IF lres_ruimte_opstel_key IS NOT NULL
                    THEN
                        SELECT COALESCE (MAX (d.ins_discipline_email), MAX (l.alg_locatie_email)),
                               MAX (res_disc_params_notify),
                               MAX (res_disc_params_bonotify)
                          INTO lsender,
                               lnotify,
                               lbonotify
                          FROM res_ruimte_opstelling ro,
                               res_ruimte rr,
                               res_alg_ruimte rag,
                               alg_ruimte r,
                               alg_verdieping v,
                               alg_gebouw g,
                               alg_locatie l,
                               ins_tab_discipline d,
                               res_disc_params rdp
                         WHERE     l.alg_locatie_key = g.alg_locatie_key
                               AND g.alg_gebouw_key = v.alg_gebouw_key
                               AND r.alg_verdieping_key = v.alg_verdieping_key
                               AND r.alg_ruimte_key = rag.alg_ruimte_key
                               AND rag.res_ruimte_key = rr.res_ruimte_key
                               AND d.ins_discipline_key = rr.res_discipline_key
                               AND rr.res_discipline_key = rdp.res_ins_discipline_key
                               AND rr.res_ruimte_key = ro.res_ruimte_key
                               AND ro.res_ruimte_opstel_key = lres_ruimte_opstel_key;
                    ELSIF lalg_ruimte_key IS NOT NULL
                    THEN
                        -- Een voorzieningenreservering kan uit meerdere catalogi items hebben,
                        -- maar bij maar 1 afzender. Zwaarst geldt een cateringcatalogusafzender
                        SELECT MAX (d.ins_discipline_email),
                               MAX (res_disc_params_bonotify)
                          INTO lsender,
                               lbonotify
                          FROM ins_tab_discipline d,
                               res_artikel a,
                               res_rsv_artikel ra,
                               res_disc_params rdp
                         WHERE     d.ins_discipline_key = a.res_discipline_key
                               AND a.res_discipline_key = rdp.res_ins_discipline_key
                               AND a.res_artikel_key = ra.res_artikel_key
                               AND ra.res_rsv_artikel_verwijder IS NULL
                               AND ra.res_rsv_ruimte_key = prefkey;

                        -- Als die er niet is, dan de voorzieningscatalogusafzender
                        IF lsender IS NULL
                        THEN
                            SELECT MAX (d.ins_discipline_email),
                                   MAX (res_disc_params_bonotify)
                              INTO lsender,
                                   lbonotify
                              FROM ins_tab_discipline d,
                                   res_deel r,
                                   res_rsv_deel rd,
                                   res_disc_params rdp
                             WHERE     d.ins_discipline_key = r.res_discipline_key
                                   AND r.res_discipline_key = rdp.res_ins_discipline_key
                                   AND r.res_deel_key = rd.res_deel_key
                                   AND rd.res_rsv_deel_verwijder IS NULL
                                   AND rd.res_rsv_ruimte_key = prefkey;
                        END IF;

                        -- Als die er niet is, dan de locatieafzender
                        IF lsender IS NULL
                        THEN
                            SELECT MAX (l.alg_locatie_email)
                              INTO lsender
                              FROM alg_ruimte r,
                                   alg_verdieping v,
                                   alg_gebouw g,
                                   alg_locatie l
                             WHERE     l.alg_locatie_key = g.alg_locatie_key
                                   AND g.alg_gebouw_key = v.alg_gebouw_key
                                   AND r.alg_verdieping_key = v.alg_verdieping_key
                                   AND r.alg_ruimte_key = lalg_ruimte_key;
                        END IF;
                    ELSE
                        lsender := NULL;
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        lsender := NULL;
                END;

                IF (lbonotify > 0) AND (lcode = 'RESNEW') -- Bij aanmaken van de reservering, of bij wijzigen van een bestaande reservering door toevoegen van voorzieningen.
                THEN
                    res.notifybackoffice (prefkey, lcode);
                END IF;

                -- Reserveringen worden eventueel niet genotificeerd als settings daartoe aanleiding geven.
                -- We weten genoeg om te checken of dat zo is
                IF (lnotify = 0)
                THEN
                    -- Nothing to do: disc says no
                    RETURN;
                END IF;

                lxrefkey := lrefkey;                                           -- de deelreservering
                lrefkey := lreskey;                                           -- de hoofdreservering
            WHEN lxmlnode = 'bestelling'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := bes.bessprintf (loms, prefkey, '');
                END IF;
                --
                SELECT CASE WHEN fac.getsetting ('progress_notification_for') = 'true'
                            THEN prs_perslid_key_voor
                            ELSE prs_perslid_key
                            END  receiver
                     , mld_adres_key_lev
                  INTO lreceiver
                     , lmld_adres_key
                  FROM bes_bestelling bb
                 WHERE bb.bes_bestelling_key = prefkey;
                --
                -- Bij BESACP willen we *ook* de goedkeurders inlichten
                IF lcode = 'BESACP'
                THEN
                    bes.notifybesgoedkeurders (prefkey);
                END IF;
                --
                -- Bepaal de afzender
                -- die komt van de (strikt: een) catalogus van de artikelen uit deze bestelling
                -- of anders bepaald door de locatie van het afleveradres
                BEGIN
                    -- Merk op dat tijdens insert nog niet bekend is welke items nog gaan worden toegevoegd
                    -- en dan is het dus niet mogelijk hier de catalogus te bepalen en te gebruiken
                    -- Tijdens update zou dat wel kunnen, maar de update wordt soms veroorzaakt door
                    -- een andere update, in het bijzonder makeorders(). Dat levert dan weer een
                    -- table is mutating error op. Dat betekent dus dat de notificatie eigenlijk dieper
                    -- moet worden gegenereerd, op bestelling-item. Dit valt nu buiten de scope van RWSN#14861
                    -- Met 5i kunnen we dit hier nu wel doen toch? Nee, ook niet!
                    -- SELECT   MAX (ins_discipline_email)
                    --  INTO   lsender
                    --  FROM   ins_tab_discipline d, bes_bestelling_item bi, ins_srtdeel sd, ins_srtgroep sg
                    -- WHERE       bi.ins_srtdeel_key = sd.ins_srtdeel_key
                    --         AND sd.ins_srtgroep_key = sg.ins_srtgroep_key
                    --         AND sg.ins_discipline_key = d.ins_discipline_key
                    --         AND bi.bes_bestelling_key = prefkey;
                    IF lsender IS NULL
                    THEN
                        SELECT l.alg_locatie_email
                          INTO lsender
                          FROM mld_adres ma, alg_locatie l
                         WHERE lmld_adres_key = ma.mld_adres_key(+)
                           AND ma.alg_locatie_key = l.alg_locatie_key(+);
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        lsender := NULL;
                END;
            WHEN lxmlnode = 'bestelopdr'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := bes.opdrsprintf (loms, prefkey);
                END IF;

                -- Wie ontvangt deze? (besteller/leverancier)
                lreceiver := NULL;
                lnotify := NULL;                                                   -- de leverancier

                IF lcode IN ('BES2SN', 'BES2SX')
                THEN
                    -- Ontvanger = misschien de besteller (afh. van catalogus)
                    -- Mochten er (technisch) meerdere zijn, pak er maar eentje
                    BEGIN
                        SELECT b.prs_perslid_key,
                               b.prs_perslid_key_voor,
                               b.mld_adres_key_lev,
                               p.prs_perslid_email,
                               p.prs_perslid_telefoonnr,
                               bdp.bes_disc_params_noti_opdr
                          INTO lreceiver,
                               lreceiver2,
                               lmld_adres_key,
                               lemail,
                               lphone,
                               lnotify
                          FROM prs_perslid p,
                               bes_bestelling b,
                               bes_bestelling_item bi,
                               bes_bestelopdr_item boi,
                               bes_srtdeel sd,
                               bes_srtgroep sg,
                               bes_disc_params bdp
                         WHERE     p.prs_perslid_key = b.prs_perslid_key
                               AND bi.bes_bestelling_key = b.bes_bestelling_key
                               AND sd.bes_srtdeel_key = bi.bes_srtdeel_key
                               AND sg.bes_srtgroep_key = sd.bes_srtgroep_key
                               AND bdp.bes_ins_discipline_key = sg.ins_discipline_key
                               AND boi.bes_bestelopdr_item_key = bi.bes_bestelopdr_item_key
                               AND boi.bes_bestelopdr_key = prefkey
                               AND ROWNUM = 1;
                    EXCEPTION
                        WHEN NO_DATA_FOUND
                        THEN
                            RETURN;
                    END;

                    -- Geen dubbele sturen; ik zeg zelfs: de bestel_voor moet m.i. helemaal niet ingelicht worden?!
                    --IF (lreceiver2 = lreceiver)
                    --THEN
                    lreceiver2 := NULL;
                --END IF;
                END IF;

                IF (lnotify != 1 OR lnotify IS NULL)
                THEN
                    -- Ontvanger = leverancier, toch niet de besteller
                    lreceiver := NULL;
                    lreceiver2 := NULL;

                    SELECT prs_bedrijf_key
                      INTO lprs_bedrijf_key
                      FROM bes_bestelopdr
                     WHERE bes_bestelopdr_key = prefkey;

                    BEGIN
                        SELECT prs_bedrijf_email, prs_bedrijf_telefoon2
                          INTO lemail, lphone
                          FROM prs_bedrijf
                         WHERE prs_bedrijf_key = lprs_bedrijf_key;
                    EXCEPTION
                        WHEN NO_DATA_FOUND
                        THEN
                            RETURN;
                    END;
                END IF;

                -- Afzender
                BEGIN
                    SELECT MAX (ins_discipline_email)
                      INTO lsender
                      FROM ins_tab_discipline d,
                           bes_bestelling_item bi,
                           bes_srtdeel sd,
                           bes_srtgroep sg,
                           bes_bestelopdr_item boi
                     WHERE     bi.bes_srtdeel_key = sd.bes_srtdeel_key
                           AND sd.bes_srtgroep_key = sg.bes_srtgroep_key
                           AND sg.ins_discipline_key = d.ins_discipline_key
                           AND boi.bes_bestelopdr_item_key = bi.bes_bestelopdr_item_key
                           AND boi.bes_bestelopdr_key = prefkey;

                    IF lsender IS NULL
                    THEN
                        SELECT l.alg_locatie_email
                          INTO lsender
                          FROM mld_adres ma, alg_locatie l
                         WHERE lmld_adres_key = ma.mld_adres_key(+)
                               AND ma.alg_locatie_key = l.alg_locatie_key(+);
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        NULL;
                END;
            WHEN lxmlnode = 'afspraak' OR lxmlnode = 'bezoeker'
            THEN
                IF lxmlnode = 'afspraak'
                THEN
                    IF ptracking_oms IS NULL
                    THEN
                        bericht := bez.afssprintf (loms, prefkey);
                    END IF;

                    lafs_key := prefkey;
                ELSE                                                        -- lxmlnode = 'bezoeker'
                    IF ptracking_oms IS NULL
                    THEN
                        bericht := bez.bzksprintf (loms, prefkey);
                    END IF;

                    SELECT bez_afspraak_key
                      INTO lafs_key
                      FROM bez_bezoekers
                     WHERE bez_bezoekers_key = prefkey;
                END IF;

                -- We sturen bij bezoekers soms TWEE notificaties, naar contact en host
                -- De normale notificatie gaat naar de contact (UWVA#19008)
                -- Moeten dus kijken of psrtnotificatiekey naar BEZDON of BEZOUT wijst
                -- Dan moeten we ook BEZDO2 of BEZOU2 versturen naar de host.
               IF lcode = 'BEZDON'
                THEN
                    lcode2 := 'BEZDO2';
                    lpoolnotified := res.notifypool(prefkey, lcode2);
                ELSIF lcode = 'BEZUNI'
                THEN
                    lcode2 := 'BEZDO2';
                ELSIF lcode = 'BEZOUT'
                THEN
                    lcode2 := 'BEZOU2';
                END IF;

                IF lcode2 IS NOT NULL
                THEN
                    SELECT lcl.x ('fac_srtnotificatie_oms',
                                  fac_srtnotificatie_key,
                                  fac_srtnotificatie_oms)
                      INTO loms2
                      FROM fac_srtnotificatie
                     WHERE fac_srtnotificatie_code = lcode2;

                    bericht2 := bez.bzksprintf (loms2, prefkey);
                END IF;

                SELECT bez_afspraak_contact_key, bez_afspraak_host_key, alg_locatie_key
                  INTO lreceiver, lreceiver2, lalglocatiekey
                  FROM bez_afspraak
                 WHERE bez_afspraak_key = lafs_key;

                -- afzender
                BEGIN
                    SELECT l.alg_locatie_email
                      INTO lsender
                      FROM alg_locatie l
                     WHERE l.alg_locatie_key = lalglocatiekey;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        lsender := NULL;
                END;
            WHEN lxmlnode = 'deel'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := ins.sprintf (loms, prefkey);
                END IF;

                -- Beheeracties notificeren we niet, zou niet weten naar wie.
                IF lcode IN ('INSOUT', 'INSINN', 'INSIN2')
                THEN
                    -- Bij uitlenen sturen we een notificatie naar de tijdelijke nieuwe eigenaar (UWVA#21528)
                    -- Bij innemen sturen we een notificatie naar de oorspronkelijke eigenaar (indien persoon; zeldzaam)
                    SELECT ins_alg_ruimte_key, ins_alg_locatie_key
                      INTO lreceiver, lalglocatiekey
                      FROM ins_deel
                     WHERE ins_alg_ruimte_type = 'P' AND ins_deel_key = prefkey;

                    -- Afzender
                    BEGIN
                        SELECT MAX (ins_discipline_email)
                          INTO lsender
                          FROM ins_tab_discipline d,
                               ins_deel dl,
                               ins_srtdeel sd,
                               ins_srtgroep sg
                         WHERE     dl.ins_srtdeel_key = sd.ins_srtdeel_key
                               AND sd.ins_srtgroep_key = sg.ins_srtgroep_key
                               AND sg.ins_discipline_key = d.ins_discipline_key
                               AND dl.ins_deel_key = prefkey;

                        IF lsender IS NULL
                        THEN
                            SELECT l.alg_locatie_email
                              INTO lsender
                              FROM alg_locatie l
                             WHERE l.alg_locatie_key = lalglocatiekey;
                        END IF;
                    EXCEPTION
                        WHEN NO_DATA_FOUND
                        THEN
                            NULL;
                    END;
                END IF;
            WHEN lxmlnode = 'controle'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := ins.contrsprintf (loms, prefkey);
                END IF;

                -- Bij Controle, Vervanging of Certificering sturen we een notificatie naar diegene die de Controle, Vervanging of Certificering heeft uitgevoerd.
                SELECT prs_perslid_key, ins_deel_key
                  INTO lreceiver, lins_deel_ley
                  FROM ins_deelsrtcontrole
                 WHERE ins_deelsrtcontrole_key = prefkey;

                -- Afzender via ins_deel.
                BEGIN
                    SELECT MAX (ins_discipline_email)
                      INTO lsender
                      FROM ins_tab_discipline d,
                           ins_deel dl,
                           ins_srtdeel sd,
                           ins_srtgroep sg
                     WHERE     dl.ins_srtdeel_key = sd.ins_srtdeel_key
                           AND sd.ins_srtgroep_key = sg.ins_srtgroep_key
                           AND sg.ins_discipline_key = d.ins_discipline_key
                           AND dl.ins_deel_key = lins_deel_ley;

                    IF lsender IS NULL
                    THEN
                        SELECT l.alg_locatie_email
                          INTO lsender
                          FROM alg_locatie l
                         WHERE l.alg_locatie_key = lalglocatiekey;
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        NULL;
                END;
            WHEN lxmlnode = 'contract'
            THEN
                IF ptracking_oms IS NULL
                THEN
                    bericht := cnt.sprintf (loms, prefkey);
                END IF;

                -- Ontvanger = contractbeheerder
                SELECT prs_perslid_key_beh
                  INTO lreceiver
                  FROM cnt_contract
                 WHERE cnt_contract_key = prefkey;

                -- Bepaal de afzender
                -- Afzender is het email adres van de contractsoort (discipline)
                -- Of wordt anders bepaald door de locatiescope van het contract
                BEGIN
                    SELECT MAX (cd.ins_discipline_email)
                      INTO lsender
                      FROM cnt_contract c, cnt_discipline cd
                     WHERE c.ins_discipline_key = cd.ins_discipline_key
                       AND c.cnt_contract_key = prefkey;
                    IF lsender IS NULL
                    THEN
                        SELECT MAX(email)
                          INTO lsender
                          FROM (SELECT l.alg_locatie_email email
                                  FROM cnt_contract_plaats,
                                       alg_locatie l
                                 WHERE cnt_alg_plaats_key = l.alg_locatie_key
                                   AND cnt_alg_plaats_code = 'L'
                                   AND cnt_contract_plaats_verwijder IS NULL
                                   AND cnt_contract_key = prefkey
                                UNION
                                SELECT l.alg_locatie_email email
                                  FROM cnt_contract_plaats,
                                       alg_locatie l,
                                       alg_gebouw g
                                 WHERE cnt_alg_plaats_code = 'G'
                                   AND cnt_alg_plaats_key = g.alg_gebouw_key
                                   AND g.alg_locatie_key = l.alg_locatie_key
                                   AND cnt_contract_plaats_verwijder IS NULL
                                   AND cnt_contract_key = prefkey
                                UNION
                                SELECT l.alg_locatie_email email
                                  FROM cnt_contract_plaats,
                                       alg_locatie l,
                                       alg_terreinsector t
                                 WHERE cnt_alg_plaats_code = 'T'
                                   AND cnt_alg_plaats_key = t.alg_terreinsector_key
                                   AND t.alg_locatie_key = l.alg_locatie_key
                                   AND cnt_contract_plaats_verwijder IS NULL
                                   AND cnt_contract_key = prefkey);
                    END IF;
                EXCEPTION
                    WHEN NO_DATA_FOUND
                    THEN
                        lsender := NULL;
                END;
            WHEN lxmlnode = 'message'
            THEN
                NULL;     -- kan nog niet via tracking worden veroorzaakt (later: ontvangstbericht?)
            ELSE
                NULL;                                                                -- force error?
        END CASE;

        fac.putnotificationsrtprio (pperslid_key,
                                    lreceiver,
                                    lcode,
                                    lrefkey,
                                    bericht,
                                    NULL,
                                    lemail,
                                    lphone,
                                    lxrefkey,
                                    2,
                                    lsender);

        IF lcode2 IS NOT NULL AND lreceiver2 IS NOT NULL
        THEN
            IF lpoolnotified = 0 THEN
                fac.putnotificationsrtprio (pperslid_key,
                                            lreceiver2,
                                            lcode2,
                                            lrefkey,
                                            bericht2,
                                            NULL,
                                            lemail,
                                            lphone,
                                            lxrefkey,
                                            2,
                                            lsender);
            END IF;
        END IF;

        IF lemail3 IS NOT NULL
        THEN
            fac.putnotificationsrtprio (pperslid_key,
                                        NULL,
                                        lcode,
                                        lrefkey,
                                        bericht,
                                        NULL,
                                        lemail3,
                                        lphone,
                                        lxrefkey,
                                        2,
                                        lsender);
        END IF;
    EXCEPTION
        WHEN NO_DATA_FOUND
        THEN
            NULL;
    END;

    -- Levert de (nieuwste) datum op van een getrackt event op key
    -- bv de laatste wijzigingsdatum van melding 123 is gettrackingdate('MLDUPD', 123)
    FUNCTION gettrackingdate (peventcode IN VARCHAR2, pkey IN number) RETURN date
    AS
     retdatum date := NULL;
    BEGIN
       IF peventcode IS NOT NULL AND pkey IS NOT NULL THEN
       BEGIN
         SELECT MAX(fac_tracking_datum)
           INTO retdatum
           FROM fac_tracking t, fac_srtnotificatie st
          WHERE t.fac_srtnotificatie_key = st.fac_srtnotificatie_key
            AND st.fac_srtnotificatie_code = peventcode
            AND fac_tracking_refkey=pkey;
       EXCEPTION
           WHEN NO_DATA_FOUND
            THEN NULL;
       END;
       END IF;
    RETURN retdatum;
    END;

    -- Levert de (nieuwste) muteerder op van een getrackt event op key
    -- bv de laatste wijziger van melding 123 is gettrackinguserkey('MLDUPD', 123)
    FUNCTION gettrackinguserkey (peventcode IN VARCHAR2, pkey IN NUMBER)
       RETURN NUMBER
    AS
       retkey fac_tracking.prs_perslid_key%TYPE := NULL;
    BEGIN
       BEGIN
          SELECT   prs_perslid_key
            INTO   retkey
            FROM   fac_tracking t, fac_srtnotificatie st
           WHERE       t.fac_srtnotificatie_key = st.fac_srtnotificatie_key
                   AND st.fac_srtnotificatie_code = peventcode
                   AND fac_tracking_refkey = pkey;
       EXCEPTION
          WHEN NO_DATA_FOUND
          THEN
             NULL;
          WHEN TOO_MANY_ROWS
          THEN
             -- Veronderstel dat de tracking meestal uniek is, daarom slechts in
             -- uitzonderingsgevallen de nieuwste bepalen
             SELECT   prs_perslid_key
               INTO retkey
               FROM   fac_tracking t, fac_srtnotificatie st
              WHERE       t.fac_srtnotificatie_key = st.fac_srtnotificatie_key
                      AND st.fac_srtnotificatie_code = peventcode
                      AND fac_tracking_refkey = pkey
                      AND fac_tracking_key =
                            (SELECT   MAX (fac_tracking_key)
                               FROM   fac_tracking t, fac_srtnotificatie st
                              WHERE       t.fac_srtnotificatie_key = st.fac_srtnotificatie_key
                                      AND st.fac_srtnotificatie_code = peventcode
                                      AND fac_tracking_refkey = pkey);
       END;

       RETURN retkey;
    END;

   -- Keep initsession (flcode IN VARCHAR2) for branches (OLD)
   PROCEDURE initsession (flcode IN VARCHAR2)
   AS
      setsql VARCHAR2(200);
   BEGIN
        -- Initialiseert deze Oracle session met de vereiste parameters
        -- De flcode (FACILITOR Language code) mag null zijn, dan wordt de databasedefault gebruikt
        lcl.setuserlanguage(flcode);
        setsql := 'ALTER SESSION SET '
             || ' NLS_NUMERIC_CHARACTERS = ''.,'''
             || ' NLS_SORT = ''BINARY'''
             || ' NLS_TERRITORY = ''AMERICA'''
             || ' NLS_DATE_FORMAT = ''YYYY-MM-DD HH24:MI:SS''';
        EXECUTE IMMEDIATE setsql;
   END;

   PROCEDURE initsession (pkey NUMBER, flcode IN VARCHAR2, usertimezone IN VARCHAR2)
   AS
      setsql VARCHAR2(200);
   BEGIN
        DBMS_SESSION.SET_IDENTIFIER(pkey);
        -- Initialiseert deze Oracle session met de vereiste parameters
        -- De flcode (FACILITOR Language code) mag null zijn, dan wordt de databasedefault gebruikt
        lcl.setuserlanguage(flcode);
        setsql := 'ALTER SESSION SET '
             || ' NLS_NUMERIC_CHARACTERS = ''.,'''
             || ' NLS_SORT = ''BINARY'''
             || ' NLS_TERRITORY = ''AMERICA'''
             || ' NLS_DATE_FORMAT = ''YYYY-MM-DD HH24:MI:SS'''
             || ' TIME_ZONE = ''' || COALESCE(REPLACE(usertimezone, '''', ''''''), 'Europe/Amsterdam') || '''';
        EXECUTE IMMEDIATE setsql;
   END;

   PROCEDURE registerversion (pmaj IN NUMBER, pmin IN NUMBER, ppatch IN VARCHAR2, pschema IN VARCHAR2, plang IN VARCHAR2)
   AS
       verstring fac_module.fac_module_version%TYPE;
       revisionnr fac_version.fac_version_schema%TYPE;
     BEGIN
       IF pmaj IS NOT NULL AND pmin IS NOT NULL AND ppatch IS NOT NULL
       THEN
          verstring := TO_CHAR (pmaj) || '.' || TO_CHAR (pmin) || '.' || TO_CHAR (ppatch);
          UPDATE   fac_module
             SET   fac_module_version = verstring;

          UPDATE   fac_version
             SET   fac_version_major = pmaj,
                   fac_version_minor = pmin,
                   fac_version_patch = ppatch,
                   fac_version_schema = pschema,
                   fac_version_date = SYSDATE;

          UPDATE   fac_message
             SET   fac_message_text = 'FACILITOR ' || verstring || 'DB' || TO_CHAR(pschema)
           WHERE   fac_message_code = 'FAC_M002';
       END IF;

       IF plang IS NOT NULL
       THEN
          UPDATE   fac_version
             SET   fac_version_lang = plang;
       END IF;
    END;

    -- fac_version_custnr is deprecated sinds 2016.3
    PROCEDURE registercustversion (pcustid IN VARCHAR2, pcustnr IN NUMBER)
    AS
    BEGIN
       IF pcustid IS NOT NULL AND pcustnr IS NOT NULL
       THEN
          UPDATE   fac_version
             SET   fac_version_cust = pcustid,
                   fac_version_custnr = pcustnr,
                   fac_version_date = SYSDATE;
       END IF;
    END;

    FUNCTION getdbversion RETURN VARCHAR2
    AS
       verstring VARCHAR2(100);
       maxdate DATE;
    BEGIN
       -- Hoogste klant-sql. Onderken ook uwva_prs.sql
       SELECT MAX(adm_tracking_date)
         INTO maxdate
         FROM adm_tracking, fac_version
        WHERE UPPER(adm_tracking_name) LIKE '%.SQL';

       SELECT 'DB'||fac_version_schema||'/'||fac_version_cust||'@'||TO_CHAR(maxdate, 'DD-MM-YYYY')||'-'||COALESCE(fac_version_otap,'P*')
         INTO verstring
         FROM fac_version;
      RETURN verstring;
    END;

    -- Haalt default Behandelgroep op
    FUNCTION getDefaultBehandelgroep(p_stdm IN NUMBER)
        RETURN NUMBER
    IS
        stdm_default_disc   mld_stdmelding.mld_stdmelding_default_disc%TYPE;
    BEGIN
      SELECT mld_stdmelding_default_disc
        INTO stdm_default_disc
        FROM mld_stdmelding
       WHERE mld_stdmelding_key = p_stdm;
      RETURN stdm_default_disc;
    EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
        RETURN NULL;
    END;

   -- Status: proven concept
   PROCEDURE processemail (pfrom      IN VARCHAR2,
                           pto        IN VARCHAR2,
                           psubject   IN VARCHAR2,
                           pbody      IN VARCHAR2,
                           psessionid IN VARCHAR2
                          )
   AS
      sender              prs_perslid.prs_perslid_key%TYPE;
      kostenplaats        prs_afdeling.prs_kostenplaats_key%TYPE;
      newkey              mld_melding.mld_melding_key%TYPE;
      defaultstdmelding   fac_setting.fac_setting_default%TYPE;
      stdm_default_disc   mld_stdmelding.mld_stdmelding_default_disc%TYPE;
      kkey                mld_kenmerk.mld_kenmerk_key%TYPE;
      errormsg            fac_result.fac_result_waarde%TYPE;
   BEGIN
      -- Valideer de sender in pfrom: kennen we deze?
      SELECT prs_perslid_key, d.prs_kostenplaats_key
        INTO sender, kostenplaats
        FROM prs_perslid p, prs_afdeling d
       WHERE p.prs_afdeling_key = d.prs_afdeling_key
             AND UPPER (prs_perslid_email) = UPPER (pfrom);

      CASE
         WHEN UPPER (pto) LIKE 'SERVICEDESK@%'
         THEN
            defaultstdmelding := fac.getsetting ('defaultstdmelding');
            stdm_default_disc := getDefaultBehandelgroep(defaultstdmelding);

            -- suggested extensions:
            -- check for MLDUSE-write autorisations
            -- parse the subject to find an appropriate stdmelding, if uniquely possible
            -- append (as a note?) to an existing melding if #key is found in the subject
            BEGIN
               INSERT INTO mld_melding (mld_melding_module,
                                        mld_meldbron_key,
                                        mld_melding_datum,
                                        mld_melding_omschrijving,
                                        mld_melding_status,
                                        mld_stdmelding_key,
                                        prs_perslid_key,
                                        prs_perslid_key_voor,
                                        prs_kostenplaats_key,
                                        mld_melding_spoed,
                                        mld_ins_discipline_key)
                   VALUES ('MLD',
                           4,                                            -- email
                           SYSDATE,
                           SUBSTR (
                              psubject || CHR (13)
                              || REPLACE (
                                    SUBSTR (pbody,
                                       1,
                                       4000 - (LENGTH (psubject) + 2)),
                                    CHR (13) || CHR (10) || CHR (13) || CHR (10),
                                    CHR (13) || CHR (10)),
                              1,
                              4000),              -- verwijder onnodige witregels
                           NULL,
                           defaultstdmelding,
                           sender,
                           sender,
                           kostenplaats,
                           3,
                           stdm_default_disc)
                RETURNING mld_melding_key
                     INTO newkey;

               -- find the lowest volgnummer of the flexfield of type folder.
               SELECT MIN (mld_kenmerk_key)
                 INTO kkey
                 FROM mld_kenmerk k,
                      mld_srtkenmerk sk,
                      mld_stdmelding std,
                      ins_tab_discipline d
                WHERE     mld_srtkenmerk_kenmerktype = 'M'
                      AND sk.mld_srtkenmerk_key = k.mld_srtkenmerk_key
                      AND std.mld_stdmelding_key = defaultstdmelding
                      AND std.mld_ins_discipline_key = d.ins_discipline_key
                      AND ( (k.mld_stdmelding_key = std.mld_stdmelding_key
                             AND k.mld_kenmerk_niveau = 'S')
                           OR (k.mld_stdmelding_key = d.ins_discipline_key
                               AND k.mld_kenmerk_niveau = 'D')
                           OR (k.mld_stdmelding_key = d.ins_srtdiscipline_key
                               AND k.mld_kenmerk_niveau = 'T'))
                      AND k.mld_kenmerk_verwijder IS NULL
                      AND NOT EXISTS
                            (SELECT mld_kenmerk_volgnummer
                               FROM mld_kenmerk k1,
                                    mld_srtkenmerk sk1,
                                    mld_stdmelding std1,
                                    ins_tab_discipline d1
                              WHERE sk1.mld_srtkenmerk_kenmerktype = 'M'
                                    AND sk1.mld_srtkenmerk_key =
                                          k1.mld_srtkenmerk_key
                                    AND std1.mld_stdmelding_key =
                                          defaultstdmelding
                                    AND std1.mld_ins_discipline_key =
                                          d1.ins_discipline_key
                                    AND ( (k1.mld_stdmelding_key =
                                              std1.mld_stdmelding_key
                                           AND k1.mld_kenmerk_niveau = 'S')
                                         OR (k1.mld_stdmelding_key =
                                                d1.ins_discipline_key
                                             AND k1.mld_kenmerk_niveau = 'D')
                                         OR (k1.mld_stdmelding_key =
                                                d1.ins_srtdiscipline_key
                                             AND k1.mld_kenmerk_niveau = 'T'))
                                    AND k1.mld_kenmerk_verwijder IS NULL
                                    AND k1.mld_kenmerk_volgnummer >
                                          k.mld_kenmerk_volgnummer);

               IF kkey IS NOT NULL
               THEN
                  INSERT INTO fac_result (fac_result_sessionid,
                                          fac_result_naam,
                                          fac_result_waarde)
                      VALUES (psessionid,
                              'kenmerkpath',
                              'MLD\M' || to_char( TRUNC(newkey/1000), 'FM0000') || '___\M' || newkey || '\' || kkey || '\');
               END IF;

               mld.setmeldingstatus (newkey, 2, sender);

               IF stdm_default_disc IS NOT NULL
               THEN
                  mld.notifybackoffice (newkey, 'MLDBHG', 2);
               END IF;
            END;
         ELSE
            NULL;
      END CASE;

      IF errormsg IS NOT NULL
      THEN
         INSERT INTO fac_result (fac_result_sessionid,
                                 fac_result_naam,
                                 fac_result_waarde)
             VALUES (psessionid, 'errormsg', errormsg);
      END IF;
   EXCEPTION
      WHEN OTHERS
      THEN
         fac.writelog (
                  'PROCESSEMAIL',
                  'W',
                     'Mail kon niet verwerkt worden afzender: '
                  || pfrom
                  || '['
                  || errormsg
                  || ']',
                     'OTHERS (error '
                  || SQLCODE
                  || '/'
                  || SUBSTR (SQLERRM, 1, 100)
                  || ')');
         INSERT INTO fac_result (fac_result_sessionid,
                                 fac_result_naam,
                                 fac_result_waarde)
             VALUES (psessionid,
                     'errormsg',
                     'Database fout - Neem contact op met uw systeembeheerder');
   END;

    -- Is de meegegeven datum een feestdag
    FUNCTION isdatefeestdag(p_date      IN DATE)
        RETURN BOOLEAN
    IS
        inspdate   DATE;
    BEGIN
      SELECT mvd.mld_vrije_dagen_datum
        INTO inspdate
        FROM mld_vrije_dagen mvd
       WHERE mvd.mld_vrije_dagen_datum = TRUNC(p_date, 'DD');
      RETURN TRUE;
    EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
         -- Datum is geen feestdag.
         RETURN FALSE;
    END;

    -- Calculation of next cyclus date
    -- Deze functie moet snel en efficient zijn.
    FUNCTION calcnextcyclusdate(p_date      IN DATE,
                                p_mode      IN NUMBER,
                                p_eenheid   IN NUMBER,
                                p_periode   IN NUMBER,
                                p_bits      IN NUMBER)
        RETURN DATE
    IS
        fromdate            DATE;
        fromdateplusperiode DATE;
        fromdow             NUMBER; -- Day of week.
        fromdom             NUMBER; -- Day of month.
        frommoy             NUMBER; -- Month of year.
        todaydow            NUMBER;
        inspdate            DATE;
        nextday             NUMBER;
        nextmonth           NUMBER;
        lastday             DATE;
        w_bits              NUMBER;
        m_bits              NUMBER;
        y_bits              NUMBER;
        w_day_n             NUMBER;
        w_day_v             VARCHAR2(10);
        gevonden            BOOLEAN;
        month1              NUMBER;
        month2              NUMBER;
    BEGIN
        IF p_periode = 0
        THEN
          RETURN p_date;
        END IF;
        -- moment-modus of interval-modus?
        IF p_mode = 0
        THEN
            -- momentmodus UIT TE WERKEN!
            --fromdate := GREATEST (SYSDATE, p_date);
            fromdate := p_date;
            CASE p_eenheid
                WHEN 0
                THEN
                    BEGIN
                        -- uurlijks
                        inspdate := fromdate + p_periode / 24;
                        IF (BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate)
                        THEN
                          -- Checkbox "Niet op feestdagen" is aangevinkt en de datum is een feestdag. De datum overslaan en bepaal de volgende inspectiedatum.
                          inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
                        END IF;
                        -- werking van cyclus_bits tbd
                    END;
                WHEN 1
                THEN
                    BEGIN
                        -- dagelijks, elke cperiode dagen
                        inspdate := fromdate + p_periode;
                        IF (BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate)
                        THEN
                          -- Checkbox "Niet op feestdagen" is aangevinkt en de datum is een feestdag. De datum overslaan en bepaal de volgende inspectiedatum.
                          inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
                        END IF;
                    -- cyclus_bits heeft geen effect
                    END;
                WHEN 2
                THEN
                    BEGIN
                        -- wekelijks
                        -- werking van cyclus_bits
                        -- bits voor elke dag (als in: tweewekelijks elke dinsdag en donderdag = 4+16=20)
                        -- 0 = geen dag (gewoon 7 dagen verder)
                        -- 1 = zondags (2^0): 1 dow
                        -- 2 = maandags (2^1): 2 dow
                        -- 4 = dinsdags (2^2): 3 dow
                        -- 8 == woensdag (2^3): 4 dow
                        -- 16 = donderdags (2^4): 5 dow
                        -- 32 = vrijdags (2^5): 6 dow
                        -- 64 = zaterdags (2^6): 7 dow

                        -- Is er deze week nog een inspectiedag?
                        --   Ja:  De volgende bits dag.
                        --   Nee: Als bits is 0: exact x (eenheid) weken verder.
                        --     Als bits > 0:  x (eenheid) weken verder de eerste bits dag.
                        fromdow := fac.getweekdaynum (fromdate);
                        IF p_bits IS NULL OR p_bits = 0
                        THEN
                          -- Dan maar een waarde kiezen. We kiezen vandaag.
                          todaydow := fac.getweekdaynum (SYSDATE);
                          w_bits := POWER(2, fromdow - 1) ;
                        ELSIF p_bits >= 128
                        THEN
                          w_bits := BITAND(p_bits, 127);
                        ELSE
                          w_bits := p_bits;
                        END IF;
                        IF w_bits IS NOT NULL AND w_bits >= POWER(2, fromdow)  -- Bijvoorbeeld donderdag(5): p_bits >= 2^5(32). (32 is vrijdags)
                        THEN
                          -- Er is deze week nog een inspectiedag.
                          -- Bepaal de volgende bits dag deze week.
                          FOR i IN fromdow..6 LOOP
                            nextday := i;
                            EXIT WHEN BITAND(w_bits, POWER(2, i)) = POWER(2, i);
                          END LOOP;
                          inspdate := fromdate - fromdow + 1 + nextday;
                        ELSE
                          -- Er is deze week geen inspectiedag meer.
                          -- Eerste bits dag x (eenheid) weken verder.
                          IF w_bits IS NULL OR w_bits = 0
                          THEN
                            -- Exact eenheid weken verder.
                            inspdate := fromdate + p_periode * 7;
                          ELSE
                            -- Bepaal de eerste bits dag x (eenheid) weken verder.
                            inspdate := fromdate + p_periode * 7 + 1 - fromdow; -- zondag
                            FOR i IN 0..6 LOOP
                              nextday := i;
                              EXIT WHEN BITAND(w_bits, POWER(2, i)) = POWER(2, i);
                            END LOOP;
                            inspdate := inspdate + nextday;
                          END IF;
                        END IF;
                        IF (BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate)
                        THEN
                          -- Checkbox "Niet op feestdagen" is aangevinkt en de datum is een feestdag. De datum overslaan en bepaal de volgende inspectiedatum.
                          inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
                        END IF;
                    END;
                WHEN 3
                THEN
                    BEGIN
                        -- Maandelijks
                        -- werking van cyclus_bits
                        -- bits 1 t/m 8 gebruiken we op dezelfde wijze als bij eenheid week
                        -- bits voor elke dag (als in: tweemaandelijks elke dinsdag en donderdag = 4+16=20)
                        -- 0 = geen dag (gewoon 7 dagen verder)
                        -- 1 = zondags (2^0): 1 dow (day of week/dag van de week)
                        -- 2 = maandags (2^1): 2 dow
                        -- 4 = dinsdags (2^2): 3 dow
                        -- 8 == woensdag (2^3): 4 dow
                        -- 16 = donderdags (2^4): 5 dow
                        -- 32 = vrijdags (2^5): 6 dow
                        -- 64 = zaterdags (2^6): 7 dow
                        -- 128 = niet op een feestdag
                        -- bits 9 t/m 11 geven de dag van de maand aan
                        -- 1 = eerste xxxdag van de maand (2^9 = 256)
                        -- 2 = tweede xxxdag van de maand (2^10 = 512)
                        -- 3 = derde xxxdag van de maand (2^9 + 2^10 = 256 + 512 = 768)
                        -- 4 = vierde xxxdag van de maand (2^11 = 1024)
                        -- 5 = vijfde xxxdag van de maand (2^9 + 2^11 = 256 + 1024 = 1280)
                        -- 6 = één na laatste xxxdag van de maand (2^10 + 2^11 = 512 + 1024 = 1536)
                        -- 7 = laatste dag van de maand (2^9 + 2^10 + 2^11 = 256 + 512 + 1024 = 1792)

                        fromdow := fac.getweekdaynum (fromdate);
                        -- Alleen de gezette bits van de dagen van de week.
                        w_bits := BITAND(p_bits, 127);
                        -- Alleen de gezette bits van de y-ste xxxdag van de maand.
                        m_bits := BITAND(p_bits, 256 + 512 + 1024) / 256;

                        -- NEXT_DAY verwacht als tweede parameter de dagnaam als string. De dag van de week (nummer) werkt niet in een package.
                        -- Bepaal de taalonafhankelijke weekdag die je zoekt, zodat je NEXT_DAY kunt gaan gebruiken.
                        CASE
                        WHEN BITAND(w_bits, 1) = 1
                        THEN w_day_n := 1; -- 'ZONDAG' / 'SUNDAY';
                        WHEN BITAND(w_bits, 2) = 2
                        THEN w_day_n := 2; -- 'MAANDAG' / 'MONDAY';
                        WHEN BITAND(w_bits, 4) = 4
                        THEN w_day_n := 3; -- 'DINSDAG' / 'TUESDAY';
                        WHEN BITAND(w_bits, 8) = 8
                        THEN w_day_n := 4; -- 'WOENSDAG' / 'WEDNESDAY';
                        WHEN BITAND(w_bits, 16) = 16
                        THEN w_day_n := 5; -- 'DONDERDAG' / 'THURSDAY';
                        WHEN BITAND(w_bits, 32) = 32
                        THEN w_day_n := 6; -- 'VRIJDAG' / 'FRIDAY';
                        WHEN BITAND(w_bits, 64) = 64
                        THEN w_day_n := 7; -- 'ZATERDAG' / 'SATURDAY';
                        ELSE w_day_n := fac.getweekdaynum (SYSDATE); -- bits is NULL of 0: Dag van vandaag nemen. Wordt in dit geval verder niets mee gedaan.
                        END CASE;
                        -- Tweede parameter van NEXT_DAY is de dag naam in de ingestelde taal
                        -- En we willen niet taal afhankelijk zijn. Dus de naam onafhankelijk bepalen
                        -- 01-06-2014 is een ZONDAG
                        w_day_v := TO_CHAR(TO_DATE('01-06-2014', 'DD-MM-YYYY') + w_day_n - 1, 'DAY');  -- Levert de naam van de weekdag op, taalonafhankelijk (ZONDAG.....ZATERDAG).

                        -- Is er deze maand nog een inspectiedag?
                        -- Eerst de eerste inspectiedag bepalen
                        IF w_bits IS NULL OR w_bits = 0
                        THEN
                          -- Exact eenheid maanden verder.
                          inspdate := ADD_MONTHS (fromdate, p_periode);
                        ELSIF m_bits > 5
                        THEN
                          CASE
                          WHEN m_bits = 6 OR m_bits = 7
                          THEN
                            -- Één na laatste (6) of laatste (7) xxxdag van de maand bepalen
                            -- Bepaal de eerste bits dag van de volgende maand de y-ste xxxdag van de maand.
                            -- en neem dan de datum van twee weken of één week daarvoor.
                            inspdate := NEXT_DAY (TRUNC(ADD_MONTHS (fromdate, 1), 'MONTH') - 1, w_day_v) - ((8 - m_bits) * 7); -- m_bits 6 resp. 7 dan 2 resp. 1 week/weken terug.

                            -- Als inspectie datum niet in de toekomst ligt dan de eerste inspectie datum bepalen in de volgende periode maanden verder.
                            IF (inspdate <= fromdate)
                            THEN
                              inspdate := NEXT_DAY (TRUNC(ADD_MONTHS (inspdate, p_periode + 1), 'MONTH') - 1, w_day_v) - ((8 - m_bits) * 7); -- m_bits 6 resp. 7 dan 2 resp. 1 week/weken terug.
                            ELSIF p_periode > 1  -- (inspdate > fromdate) geldt automatisch.
                            THEN
                              inspdate := NEXT_DAY (TRUNC(ADD_MONTHS (inspdate, p_periode), 'MONTH') - 1, w_day_v) - ((8 - m_bits) * 7); -- m_bits 6 resp. 7 dan 2 resp. 1 week/weken terug.
                            END IF;
                          ELSE
                            -- Niet ondersteunende instelling.
                            -- Exact eenheid maanden verder.
                            inspdate := ADD_MONTHS (fromdate, p_periode);
                          END CASE;
                        ELSE -- 0 < m_bits <= 5
                          gevonden := FALSE;
                          IF (p_periode = 1)
                          THEN
                            -- Eerste bits dag.
                            -- Bepaal de eerste bits dag deze maand de y-ste xxxdag van de maand. Misschien is er deze maand nog een dag te vinden.
                            inspdate := NEXT_DAY (TRUNC(fromdate, 'MONTH') - 1 + ((m_bits - 1) * 7), w_day_v);

                            -- Als de nieuwe dag in de toekomst is en in dezelfde maand (i.v.m. 5de dag) dan heb je de volgende datum gevonden.
                            -- Maar dan mag de datum niet een feestdag zijn als de checkbox "Niet op feestdagen" is aangevinkt.
                            month1 := TO_NUMBER(TO_CHAR (fromdate, 'MM'));
                            month2 := TO_NUMBER(TO_CHAR (inspdate, 'MM'));
                            IF (inspdate > fromdate AND month1 = month2) AND NOT ((BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate))
                            THEN
                              gevonden := TRUE;
                            END IF;
                          END IF;

                          IF NOT gevonden
                          THEN
                            -- Voor (p_periode = 1) niets gevonden of (p_periode > 1).
                            inspdate := NEXT_DAY (TRUNC(ADD_MONTHS (fromdate, p_periode), 'MONTH') - 1 + ((m_bits - 1) * 7), w_day_v);

                            IF (m_bits = 5)
                            THEN
                              -- Is er wel een vijfde dag in de maand. Is de fromdate + periode in dezelfde maand als de nieuw berekende inspdate?
                              fromdateplusperiode := ADD_MONTHS (fromdate, p_periode);
                              month1 := TO_NUMBER(TO_CHAR (fromdateplusperiode, 'MM'));
                              month2 := TO_NUMBER(TO_CHAR (inspdate, 'MM'));
                              IF (month1 <> month2)
                              THEN
                                -- Er is geen vijfde dag in de maand. De volgende bepalen.
                                inspdate := calcnextcyclusdate (fromdateplusperiode, p_mode, p_eenheid, p_periode, p_bits);
                              ELSIF (BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate)
                              THEN
                                -- De datum is een feestdag en de checkbox "Niet op feestdagen" is aangevinkt. De datum overslaan en bepaal de volgende inspectiedatum.
                                inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
                              END IF;
                            ELSE
                              IF (BITAND(p_bits, 128) = 128) AND isdatefeestdag(inspdate)
                              THEN
                                -- De datum is een feestdag en de checkbox "Niet op feestdagen" is aangevinkt. De datum overslaan en bepaal de volgende inspectiedatum.
                                inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
                              END IF;
                            END IF;
                          END IF;
                        END IF;
                    END;
                WHEN 4
                THEN
                    BEGIN
                        -- jaarlijks
                        -- werking van cyclus_bits
                        -- bits voor elke maand (als in: tweejaarlijks elke maand maart en mei = 4+16=20)
                        --  0 = geen maand (gewoon 12 maanden verder)
                        --  1 = januari (2^0): 1 moy
                        --  2 = februari (2^1): 2 moy
                        --  4 = maart (2^2): 3 moy
                        --  8 == april (2^3): 4 moy
                        --  16 = mei (2^4): 5 moy
                        --  32 = juni (2^5): 6 moy
                        --  64 = juli (2^6): 7 moy
                        -- 128 = augustus (2^7): 8 moy
                        -- 256 = september (2^8): 9 moy
                        -- 512 = oktober (2^9): 10 moy
                        -- 1024 = november (2^10): 11 moy
                        -- 2048 = december (2^11): 12 moy

                        -- Is er dit jaar nog een inspectiemaand?
                        --   Ja:  De volgende bits maand.
                        --   Nee: Als bits is 0: exact x (eenheid) jaren verder.
                        --        Als bits > 0:  x (eenheid) jaren verder de eerste bits maand.

                        frommoy := EXTRACT(month FROM fromdate);
                        IF p_bits IS NULL OR p_bits = 0
                        THEN
                          -- Dan maar een waarde kiezen. We kiezen deze maand.
                          todaydow := fac.getweekdaynum (SYSDATE);
                          y_bits := POWER(2, frommoy - 1) ;
                        ELSIF p_bits >= 4096
                        THEN
                          y_bits := BITAND(p_bits, 2048);
                        ELSE
                          y_bits := p_bits;
                        END IF;
                        IF y_bits IS NOT NULL AND y_bits >= POWER(2, frommoy)  -- Bijvoorbeeld mei(5): p_bits >= 2^5(32). (32 is juni)
                        THEN
                          -- Er is dit jaar nog een inspectiemaand.
                          -- Bepaal de volgende bits maand dit jaar.
                          FOR i IN frommoy..11 LOOP
                            nextmonth := i;
                            EXIT WHEN BITAND(y_bits, POWER(2, i)) = POWER(2, i);
                          END LOOP;
                          inspdate := ADD_MONTHS(fromdate, nextmonth - EXTRACT(month FROM fromdate) + 1);
                        ELSE
                          -- Er is dit jaar geen inspectiemaand meer.
                          -- Eerste bits maand x (eenheid) jaren verder.
                          IF y_bits IS NULL OR y_bits = 0
                          THEN
                            -- Exact eenheid maanden verder.
                            inspdate := ADD_MONTHS(fromdate, p_periode * 12);
                          ELSE
                            -- Bepaal de eerste bits maand x (eenheid) jaren verder.
                            inspdate := ADD_MONTHS(fromdate, (p_periode * 12) - (1 * EXTRACT(month FROM fromdate)) + 1); -- Januari
                            FOR i IN 0..11 LOOP
                              nextmonth := i;
                              EXIT WHEN BITAND(y_bits, POWER(2, i)) = POWER(2, i);
                            END LOOP;
                            inspdate := ADD_MONTHS(inspdate, nextmonth);
                          END IF;
                        END IF;

                        -- werking van cyclus_bits tbd
                    END;
            END CASE;
        ELSE
            -- intervalmodus, p_bits doet er niet toe
            fromdate := p_date;
            CASE p_eenheid
                WHEN 0
                THEN
                    BEGIN
                        -- uurlijks
                        inspdate := fromdate + p_periode / 24;
                    END;
                WHEN 1
                THEN
                    BEGIN
                        -- dagelijks, elke cperiode dagen
                        inspdate := fromdate + p_periode;
                    END;
                WHEN 2
                THEN
                    BEGIN
                        -- wekelijks
                        inspdate := fromdate + p_periode * 7;
                    END;
                WHEN 3
                THEN
                    BEGIN
                        -- maandelijks
                        inspdate := ADD_MONTHS (fromdate, p_periode);
                    END;
                WHEN 4
                THEN
                    BEGIN
                        -- jaarlijks
                        inspdate := ADD_MONTHS (fromdate, 12 * p_periode);
                    END;
            END CASE;
        END IF;
        -- Stukje veiligheid inbouwen die een oneindige loop (of recursie) van de datumberekening voorkomt zodat de Oracle Server niet gaat hangen.
        -- Berekende datum moet later zijn als de meegegeven datum.
        IF p_date >= inspdate AND p_periode <> 0
        THEN
          raise_application_error (-20001, 'FACILITOR: Invalid inspection date calculated! Check interval parameters');
        END IF;
        RETURN inspdate;
    END;

    -- Berekent de n-de (p_steps) controledatum na p_date.
    -- Indien p_steps niet is meegegeven dan wordt de eerstvolgende controledatum na nu teruggegeven.
    -- steps = 0, (NULL): 1-ste vandaag of in de toekomst (als meegegeven datum (p_date) de huidite datum is, dan deze weer teruggeven).
    -- steps = n: n-volgende vanaf meegegeven datum (p_date). Als deze berekende datum in de toekomst ligt en steps is nog niet null, dan wordt NULL teruggegeven).
    FUNCTION nextcyclusdate (p_date      IN DATE,
                             p_mode      IN NUMBER,
                             p_eenheid   IN NUMBER,
                             p_periode   IN NUMBER,
                             p_bits      IN NUMBER,
                             p_steps     IN NUMBER DEFAULT 0,
                             p_ismjob    IN NUMBER DEFAULT 0)
        RETURN DATE
    IS
        inspdate       DATE;
        inspdate_trunc DATE;
        vandaagdate    DATE;
        toekomst       BOOLEAN;
        nuoftoekomst   BOOLEAN;
    BEGIN
        inspdate := p_date;
        IF p_periode = 0
        THEN
          RETURN NULL;
        END IF;
        IF p_eenheid IS NULL
        THEN
          RETURN NULL;
        ELSIF p_eenheid = 0
        THEN
            inspdate_trunc := TRUNC(p_date, 'HH24');
            vandaagdate := TRUNC(SYSDATE, 'HH24');
        ELSE
            inspdate_trunc := TRUNC(p_date, 'DDD');
            vandaagdate := TRUNC(SYSDATE, 'DDD');
        END IF;
        toekomst := inspdate_trunc > vandaagdate;
        -- De volgende inspectiedatum berekenen.
        -- Maar let op, er kan een datum in de toekomst meegegeven zijn. Dan deze ook weer opleveren en niet de volgende berekenen.
        -- Bij het meegegeven van de huidige datum en steps is 0 de huidige controledatum weer teruggeven.
        IF NOT (p_steps = 0 AND toekomst)  -- p_steps != 0 || !toekomst
        THEN
            -- Calculate next cyclus date m.b.v. volledige datum met tijd.
            inspdate := calcnextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits);
        END IF;

        -- Nu insdate_trunc van de next cyclus date weer bepalen
        IF p_eenheid = 0
        THEN
            inspdate_trunc := TRUNC(inspdate, 'HH24');
        ELSE
            inspdate_trunc := TRUNC(inspdate, 'DDD');
        END IF;

        -- Voor MJOB is nu het hele jaar.
        IF p_ismjob = 1
        THEN
            nuoftoekomst := EXTRACT (YEAR FROM inspdate_trunc) >= EXTRACT (YEAR FROM vandaagdate);
        ELSE
            nuoftoekomst := inspdate_trunc >= vandaagdate;
        END IF;

        IF p_steps = 0
        THEN
            IF nuoftoekomst
            THEN
                RETURN inspdate;
            ELSE
                RETURN nextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits, 0);  -- volledige datum met tijd gebruiken
            END IF;
        ELSIF p_steps > 1 AND nuoftoekomst
        THEN
            RETURN NULL;
        ELSIF p_steps = 1 OR nuoftoekomst
        THEN
            RETURN inspdate;
        ELSE
            RETURN nextcyclusdate (inspdate, p_mode, p_eenheid, p_periode, p_bits, p_steps - 1);  -- volledige datum met tijd gebruiken
        END IF;
    END;

    -- Berekent de n-de (p_steps) controledatum van een object na p_date.
    -- Indien p_steps niet is meegegeven dan wordt de eerstvolgende controledatum na nu teruggegeven.
    -- steps = 0, (NULL): 1-ste vandaag of in de toekomst (als meegegeven datum (p_date) de huidige datum is, dan deze weer teruggeven).
    -- steps = n: n-volgende vanaf meegegeven datum (p_date).
    -- Als deze berekende datum in de toekomst ligt en steps is nog niet null, dan wordt NULL teruggegeven).
    -- De einddatum van het object (xcp.ins_srtcontroledl_xcp_eind) wordt niet meegenomen. De volgende inspectiedatum wordt gewoon teruggeven.
    FUNCTION nextcyclusdatedeel (p_insdeel     IN NUMBER,
                                 p_srtcontrole IN NUMBER,
                                 p_steps       IN NUMBER DEFAULT 0) -- Pas op: diverse klant-sql gebruiken deze functie
        RETURN DATE
    IS
        inspdate   DATE;
        scenario   ins_scenario.ins_scenario_key%TYPE := 1; // Altijd actuele situatie

    BEGIN
        SELECT fac.nextcyclusdate (COALESCE ( (SELECT CASE xcp.ins_srtcontrole_mode
                                                      WHEN 0
                                                      THEN idsc.ins_deelsrtcontrole_datum_org
                                                      ELSE idsc.ins_deelsrtcontrole_datum
                                                      END ins_deelsrtcontrole_datum
                                                 FROM ins_deelsrtcontrole idsc
                                                WHERE idsc.ins_srtcontrole_key = xcp.ins_srtcontrole_key
                                                  AND idsc.ins_deelsrtcontrole_status IN (5,6)
                                                  AND idsc.ins_scenario_key = 1
                                                  AND idsc.ins_deel_key = xcp.ins_deel_key
                                                  -- Voor mjob moeten de NIET succesvolle inspecties niet worden meegenomen.
                                                  -- Voor periodieke inspectie laten we het vooralsnog hetzelfde.
                                                  AND idsc.ins_deelsrtcontrole_key = (SELECT MAX(ins_deelsrtcontrole_key)
                                                                                        FROM (SELECT idsc2.ins_deelsrtcontrole_key
                                                                                                   , idsc2.ins_deel_key
                                                                                                   , idsc2.ins_srtcontrole_key
                                                                                                   , 0 ctr_ismjob
                                                                                                FROM ins_deelsrtcontrole idsc2
                                                                                               WHERE idsc2.ins_deelsrtcontrole_status IN (5,6)
                                                                                                 AND idsc2.ins_scenario_key = 1
                                                                                              UNION
                                                                                              SELECT idsc2.ins_deelsrtcontrole_key
                                                                                                   , idsc2.ins_deel_key
                                                                                                   , idsc2.ins_srtcontrole_key
                                                                                                   , 1 ctr_ismjob
                                                                                                FROM ins_deelsrtcontrole idsc2,
                                                                                                     ins_controlemode cm
                                                                                               WHERE idsc2.ins_controlemode_key = cm.ins_controlemode_key
                                                                                                 AND idsc2.ins_deelsrtcontrole_status IN (5,6)
                                                                                                 AND idsc2.ins_scenario_key = 1
                                                                                                 AND cm.ins_controlemode_success = 1) aa
                                                                                       WHERE aa.ins_deel_key = xcp.ins_deel_key
                                                                                         AND aa.ins_srtcontrole_key = xcp.ins_srtcontrole_key
                                                                                         AND aa.ctr_ismjob = xcp.ctr_ismjob)),
                                              xcp.ins_srtcontroledl_xcp_startdat,
                                              xcp.ins_deel_aanmaak),
                                   xcp.ins_srtcontrole_mode,
                                   xcp.ins_srtcontrole_eenheid,
                                   xcp.ins_srtcontrole_periode,
                                   xcp.ins_srtcontrole_bits,
                                   p_steps,
                                   xcp.ctr_ismjob
                                  ) inspectie_next
           INTO inspdate
           FROM ins_v_defined_inspect_xcp xcp
          WHERE xcp.ins_deel_key = p_insdeel
            AND xcp.ins_srtcontrole_key = p_srtcontrole
            AND xcp.ins_scenario_key = scenario;

        RETURN inspdate;
    END;

    -- Bereken hoeveel controlestappen het is naar de eerste controle datum in de toekomst (standaard) of na de meegegeven datum.
    FUNCTION nextcyclusdatesteps (p_date      IN DATE,
                                  p_mode      IN NUMBER,
                                  p_eenheid   IN NUMBER,
                                  p_periode   IN NUMBER,
                                  p_bits      IN NUMBER,
                                  p_steps     IN NUMBER DEFAULT 1,     -- Standaard beginnen te tellen bij 1.
                                  p_enddate   IN DATE DEFAULT SYSDATE)
        RETURN NUMBER
    IS
        fromdate   DATE;
        fromdow    NUMBER;
        fromdom    NUMBER;
        inspdate   DATE;
        enddate    DATE;
    BEGIN
        IF p_periode = 0
        THEN
          RETURN NULL;
        END IF;
        -- Calculate next cyclus date
        inspdate := calcnextcyclusdate (p_date, p_mode, p_eenheid, p_periode, p_bits);
        IF ((p_eenheid = 0 AND TRUNC(inspdate, 'HH24') >= TRUNC(p_enddate, 'HH24')) OR
            (p_eenheid > 0 AND TRUNC(inspdate, 'DDD') >= TRUNC(p_enddate, 'DDD')))
        THEN
            RETURN p_steps;
        ELSE
            RETURN nextcyclusdatesteps (inspdate, p_mode, p_eenheid, p_periode, p_bits, p_steps + 1, p_enddate);
        END IF;
    END;

   -- We garanderen maximaal 64 characters
   -- We ondersteunen alleen nog maar 'MD5'
   FUNCTION makehash (p_in VARCHAR2, method IN VARCHAR2 DEFAULT 'MD5')
      RETURN VARCHAR2
   IS
      l_hash   VARCHAR2 (2000);
   BEGIN
      l_hash := RAWTOHEX(UTL_RAW.cast_to_raw(DBMS_OBFUSCATION_TOOLKIT.MD5 (input_string=> p_in)));
      RETURN l_hash;
   END;

   FUNCTION testhash (p_hash VARCHAR2, p_in VARCHAR2)
      RETURN NUMBER
   IS
   BEGIN
      IF p_hash = makehash(p_in, 'MD5')
      THEN
        RETURN 1;
      ELSE
        RETURN 0;
      END IF;
   END;

   PROCEDURE createlikebookmark (pxmlnode         VARCHAR2,
                                 prefkey          NUMBER)
   IS
       expiredays   NUMBER (10);
       likerkey     NUMBER(10);
       bookmark_id  fac_bookmark.fac_bookmark_id%TYPE;
    BEGIN
       CASE pxmlnode
            WHEN 'melding'
            THEN
                expiredays := fac.safe_To_Number (fac.getsetting ('fac_mld_deeplike_expire'));
                SELECT   prs_perslid_key_voor
                  INTO   likerkey
                  FROM   mld_melding
                 WHERE   mld_melding_key = prefkey;
            WHEN 'bestelling'
            THEN
                SELECT   prs_perslid_key_voor
                  INTO   likerkey
                  FROM   bes_bestelling
                 WHERE   bes_bestelling_key = prefkey;
            WHEN 'reservering'
            THEN
                SELECT   res_rsv_ruimte_host_key
                  INTO   likerkey
                  FROM   res_rsv_ruimte
                 WHERE   res_rsv_ruimte_key = prefkey;
       END CASE;

       IF expiredays > 0
       THEN
           BEGIN
              -- Als onvoorzien de bookmark er al was geven we weer iets meer tijd
              -- We laten hem wel staan omdat er mogelijk al een bon bestaat die hem gebruikt.
              UPDATE fac_bookmark
                 SET fac_bookmark_expire = fac.datumtijdplusuitvoertijd (SYSDATE, expiredays, 'DAGEN')
                WHERE fac_bookmark_xmlnode = pxmlnode
                  AND fac_bookmark_refkey = prefkey
                  AND fac_bookmark_naam = 'faclikedeeplink';
              IF sql%rowcount = 0
              THEN
                  INSERT INTO fac_bookmark (fac_bookmark_id,
                                        fac_bookmark_naam,
                                        fac_bookmark_path,
                                        fac_bookmark_query,
                                        fac_bookmark_xmlnode,
                                        fac_bookmark_refkey,
                                        fac_bookmark_expire,
                                        prs_perslid_key_auth)
                  VALUES (DBMS_RANDOM.string ('a', 16),
                          'faclikedeeplink',
                          'appl/fac/fac_like_deep.asp',
                          'node=' || pxmlnode || '&key=' || prefkey, -- is straks beschermd met HMAC
                          pxmlnode,
                          prefkey,
                          fac.datumtijdplusuitvoertijd (SYSDATE, expiredays, 'DAGEN'),
                          likerkey);
              END IF;
           END;
       END IF;
    END;

   PROCEDURE sequence_nextval(p_seqname IN VARCHAR2, p_initval IN NUMBER DEFAULT 1, p_initexpire IN DATE DEFAULT NULL)
   IS
     v_nextval  NUMBER;
     v_expire   DATE;
   BEGIN
      SELECT fac_sequence_lastval
           , fac_sequence_expire
        INTO v_nextval
           , v_expire
        FROM fac_sequence
       WHERE fac_sequence_name = p_seqname;
      --
      IF ((p_initexpire IS NOT NULL) AND ( v_expire < p_initexpire))
      THEN
         -- Reset teller wegens overschrijden van expire_date.
         v_nextval := p_initval;
      ELSE
         -- Alleen teller ophogen.
         v_nextval := v_nextval + 1;
      END IF;
      UPDATE fac_sequence
         SET fac_sequence_lastval = v_nextval
           , fac_sequence_expire = p_initexpire
       WHERE fac_sequence_name = p_seqname;
   EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
         INSERT INTO fac_sequence (fac_sequence_name,
                                   fac_sequence_lastval,
                                   fac_sequence_expire)
              VALUES (p_seqname,
                      p_initval,
                      p_initexpire);
   END;
END fac;
/

REGISTERRUN('$Id$')
