<% /*
    $Revision$
    $Id$

    File:           model_reservations.inc

    Description:    Reservering model.
    Parameters:
    Context:

    Notes:          In het technisch datamodel heeft een reservering een koppeling naar een
                    res_ruimte_opstel record (een geldige ruimte<->opstelling combinatie)
                    In de API splitsen we dat in de meer natuurlijk aanvoelende res_ruimte_key
                    en res_ruimte_opstel_key
                    We ondersteunen nog niet het aanmaken van een tweede deelreservering
                    onder een reservering.
*/

%>
<!-- #include file="../Shared/discx3d.inc"-->
<!-- #include file="../res/res.inc" -->
<!-- #include file="./model_reservationequipment.inc"-->
<!-- #include file="./model_reservationconsumables.inc"-->
<!-- #include file="./model_custom_fields.inc"-->
<!-- #include file="./model_tracking.inc"-->
<%
var sqlro = "SELECT rg.res_ruimte_nr || CASE WHEN res_ruimte_opstel_default = 1 THEN '' ELSE ' (' || ro.res_opstelling_omschrijving || ')' END CASE"
            + "  FROM res_ruimte_opstelling rro"
            + "     , res_opstelling ro"
            + "     , res_v_alg_ruimte_gegevens rg"
            + " WHERE rro.res_opstelling_key = ro.res_opstelling_key"
            + "   AND rro.res_ruimte_key = rg.res_ruimte_key"
            + "   AND rro.res_ruimte_opstel_key = res_rsv_ruimte.res_ruimte_opstel_key";

function fndirtwarn(level)
{
    if (level & res.dirtlevel.ruimte.notavailable)
        return L("lcl_res_dirtyroom");
    return null;
}

function model_reservations(rsv_key, params)
{
    this.module = "RES";
    this.table = "res_rsv_ruimte";
    this.primary = "res_rsv_ruimte_key";
    this.records_name = "reservations";
    this.record_name = "reservation";

    this.fields =
            {"id"            : { dbs: "res_rsv_ruimte_key",          typ: "key",       filter: "exact", label: L("lcl_reservation") },
             "name"          : { dbs: "res_reservering_id",          sql: "res_reservering_key||'/'||res_rsv_ruimte_volgnr", typ: "varchar", label: L("lcl_reservation")},
             "from"          : { dbs: "res_rsv_ruimte_van",          typ: "datetime",  filter: "range", label: L("lcl_res_starttime"), track: true}, // TODO: Andere lcl voor catering
             "to"            : { dbs: "res_rsv_ruimte_tot",          typ: "datetime",  filter: "range", label: L("lcl_res_endtime"), track: true},
             "description"   : { dbs: "res_rsv_ruimte_omschrijving", typ: "varchar",   label: L("lcl_descr"), track: true },
             "activity"      : { dbs: "res_activiteit_key",          typ: "key",       filter: "exact", foreign: "res_activiteit", label: L("lcl_activity"), track: true },
             "remark"        : { dbs: "res_rsv_ruimte_opmerking",    typ: "varchar",   label: L("lcl_remark"), track: true},
             "host"          : { dbs: "res_rsv_ruimte_host_key",     typ: "key",       foreign: "prs_perslid",  label: L("lcl_host"), track: true},
             "contact"       : { dbs: "res_rsv_ruimte_contact_key",  typ: "key",       foreign: "prs_perslid",  label: L("lcl_name"), track: true},
             "visitorscount" : { dbs: "res_rsv_ruimte_bezoekers",    typ: "number",                             label: L("lcl_visitors"), track: true },
             "status"        : { dbs: "res_status_fo_key",           typ: "key",       foreign: res.getfostatustext },
             "flag"          : { dbs: "res_rsv_ruimte_flag",         typ: "number",    LOV: ""},
             "resroom"       : { dbs: "res_ruimte_opstelling.res_ruimte_key",      typ: "key", filter: "exact", label: L("lcl_room"), foreign: "res_ruimte" },
             "configuration" : { dbs: "res_ruimte_opstelling.res_opstelling_key",  typ: "key", foreign: "res_opstelling" },
             "approved"      : { dbs: "res_rsv_ruimte_afgerond",     typ: "number",    label: L("lcl_res_resappr"), track: true },
             "warning"       : { dbs: "res_rsv_ruimte_dirtlevel",    typ: "number",    foreign: fndirtwarn, readonly: true },
             "reservation"   : { dbs: "res_reservering_key",         typ: "key",       label: L("lcl_reservation") },
             "extern_id"     : { dbs: "res_rsv_ruimte_externnr",     typ: "varchar",   label: L("lcl_extrn_nr") }
            };

    // Mogelijke waarden voor res_rsv_ruimte_flag is afhankelijk van het aantal toegestane flags.
    if (S("res_reservering_flags") > 0)
    {
        flag_array = [];
        for (i=0; i<S("res_reservering_flags"); i++)
        {
            flag_array.push(i)
            flag_array.push(L("lcl_res_flag"+i));
        }
        this.fields.flag.LOV = flag_array.join(";");
    }


    this.list = { columns: ["id", "name", "from", "to"] };
    this.includes =
               {"reservationequipment": { model: model_reservationequipment,
                                          joinfield: "reservation",
                                          enable_update: true
                                        },
                "reservationconsumables": { model: model_reservationconsumables,
                                           joinfield: "reservation"
                                         },
                "tracking": {
                                  model: new model_tracking(['reservering', 'xreservering']),
                                  joinfield: "fac_tracking_refkey"
                                },
                "custom_fields" : { model: new model_custom_fields(this, "RES", { readman: true, readuse: true }),
                                    joinfield: "flexparentkey"
                                  }
              };

    this.impersonate_auth = "WEB_RESFOF";

    this.REST_GET = function _GET(params)
        {
            var query = api2.sqlfields(params, this );

            if (params.filter.id > 0)
            {
                var this_res = res.func_enabled(params.filter.id);
                user.auth_required_or_abort(this_res.canReadAny);
            }
            else // lijst
            {
                var scope = params.filter.scope || "fe";
                var autfunction = { fe : "WEB_RESUSE", fo : "WEB_RESFOF", bo : "WEB_RESBOF", mi : "WEB_RESBAC" } [scope];
                params.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch

                if (scope == "fe")
                {
                    query.wheres.push(user_key + " IN (res_rsv_ruimte_host_key, res_rsv_ruimte_contact_key)");
                    query.wheres.push("res_rsv_ruimte_van BETWEEN SYSDATE - " + S("facilitiespast_res") + " AND SYSDATE + " + S("facilitiesfuture_res"));
                }
                else
                {
                    if (!params.filter.start_from && !params.filter.end_from && !params.filter.start_to && !params.filter.end_to)
                        query.wheres.push("res_rsv_ruimte_van >= TRUNC(SYSDATE)"); // reserveringen van vandaag en komende dagen!
                }
            }

            query.tables.push("res_ruimte_opstelling");
            query.tables.push("res_ruimte  rr");
            query.tables.push("res_alg_ruimte  ra");
            query.tables.push("alg_v_ruimte_gegevens_all  rg");
            query.tables.push("alg_district  di");
            query.tables.push("prs_perslid  p");
            query.tables.push("prs_v_afdeling_boom  boom");

            query.wheres.push("res_rsv_ruimte.res_ruimte_opstel_key = res_ruimte_opstelling.res_ruimte_opstel_key(+)");
            query.wheres.push("rr.res_ruimte_key(+) = ra.res_ruimte_key");
            query.wheres.push("ra.res_ruimte_key(+) = res_ruimte_opstelling.res_ruimte_key");
            query.wheres.push("rg.alg_ruimte_key = COALESCE(res_rsv_ruimte.alg_ruimte_key, ra.alg_ruimte_key)");
            query.wheres.push("rg.alg_district_key = di.alg_district_key");
            query.wheres.push("res_rsv_ruimte.res_rsv_ruimte_contact_key = p.prs_perslid_key");
            query.wheres.push("boom.prs_afdeling_key = p.prs_afdeling_key");

            var wheres = api2.sqlfilter(params, this);
            query.wheres = query.wheres.concat(wheres);

            query.wheres.push("res_rsv_ruimte_verwijder IS NULL");

            var sql = "SELECT " + query.selects.join(", ")
                    + "  FROM " + query.tables.join(", ")
                    + " WHERE " + query.wheres.join(" AND " );

            if (!(params.filter.id > 0))
            {
                // Over het resultaat moet nog de 3D rasp, altijd
                sql = discx3d (sql,
                              "rr.res_discipline_key",
                              "di.alg_regio_key",
                              "rg.alg_district_key",
                              "rg.alg_locatie_key",
                              "rg.alg_gebouw_key",
                              "rg.alg_verdieping_key",
                              "rg.alg_ruimte_key",
                              "boom.prs_bedrijf_key",
                              "boom.prs_afdeling_key",
                              autfunction,
                              "",
                              2,null,3
                             );
            }

            sql += " ORDER BY res_rsv_ruimte.res_rsv_ruimte_key"; // Order by nodig voor includes
            if (query.orderbys.length)
                sql += ", " + query.orderbys.join(", ");

            var json = api2.sql2json (params, sql, this );

            return json;
        };

    function _analyze_fields(dbfields, params, jsondata) /* analyseer inkomende data, common voor PUT en POST */
                                                         /* res_ruimte+config wordt bijvoorbeeld omgezet in res_ruimte_opstel_key */
    {
        if ("resroom" in jsondata)
        {
            var res_ruimte_key = parseInt(jsondata.resroom);
            var res_opstel_key = -1;
            if  ("configuration" in jsondata)  // Pas op: opstelling 'Standaard' heeft soms key 0
                res_opstel_key = parseInt(jsondata.configuration);

            // De default komt bovenaan
            var sql = "SELECT rro.res_ruimte_opstel_key"
                    + "     , COALESCE (rro.res_ruimte_opstel_default, 0)"
                    + "     , rr.res_discipline_key"
                    + "  FROM res_ruimte_opstelling rro"
                    + "     , res_opstelling ro"
                    + "     , res_ruimte rr"
                    + " WHERE rro.res_opstelling_key = ro.res_opstelling_key"
                    + "   AND rr.res_ruimte_key = " + res_ruimte_key
                    + "   AND rro.res_ruimte_key = " + res_ruimte_key
                    + (res_opstel_key>=0 ? " AND ro.res_opstelling_key = " + res_opstel_key: "")
                    + " ORDER BY COALESCE (rro.res_ruimte_opstel_default, 0)"
                    + "     ,    res_opstelling_volgnr";
            var oRs = Oracle.Execute(sql);
            var res_disc_key = oRs("res_discipline_key").value;
            var soort = 0; // reserveerbare ruimtes
            dbfields["configuration"] = { dbs: "res_ruimte_opstel_key",  typ: "key", val: oRs("res_ruimte_opstel_key").Value };
            dbfields["room"] = { dbs: "alg_ruimte_key",  typ: "key", val: -1 };
            dbfields["bostatus"] = { dbs: "res_status_bo_key",  typ: "key", val: 2 }; // 'ingevoerd'. Altijd bij ruimte reserveringen
            oRs.Close();
        }
        else // moet er een alg_ruimte zijn
        {
            // TODO
            api2.error(500, "Missing room in input");
            // var res_disc_key = ...
            // var soort = 1; // algemene ruimtes
        }

        if ("activity" in jsondata)
        {
            // TODO: mag hij? Of controleren we dat in _validate_fields
        }
        else
        {
            if (params.isNew)
            {
                // Er is nog niet het concept 'default activiteit' dus pak gewoon de laagste
                sql = " SELECT MIN (ra.res_activiteit_key) res_activiteit_key "
                    + "   FROM res_activiteitdiscipline ad, "
                    + "        res_activiteit ra,"
                    + "        res_srtactiviteit rs"
                    + "  WHERE ad.res_discipline_key = " + res_disc_key
                    + "    AND ra.res_activiteit_key = ad.res_activiteit_key"
                    + "    AND rs.res_srtactiviteit_key = ra.res_srtactiviteit_key"
                    + "    AND res_srtactiviteit_soort = " + soort;
                oRs = Oracle.Execute(sql);
                //jsondata.activity = oRs("res_activiteit_key").Value;
                dbfields["activity"] = { dbs: "res_activiteit_key",  typ: "key", val: oRs("res_activiteit_key").Value };
                oRs.Close()
            }
        }
    }

    function _validate_fields (dbfields, params, jsondata) /* valideer dbfields, alle constraints die niet door de database worden afgevangen */
    {
    };

    this.REST_PUT = function (params, jsondata, the_key) /* update reservation */
        {
            var rsv_ruimte_key = the_key;
            var this_res = res.func_enabled(rsv_ruimte_key);
            user.auth_required_or_abort(this_res.canChange);

            var dbfields = api2.update_fields(params, this, jsondata); // Build updater
            _analyze_fields(dbfields, params, jsondata);
            _validate_fields(dbfields, params, jsondata);

            var wheres = [" res_rsv_ruimte_key = " + rsv_ruimte_key];

            // ons eigen tijdstip/zaal is mogelijk gewijzigd waardoor andere *dirty* reserveringen
            // clean wordt. Even onze oude datum onthouden zodat we niet *alles* hoeven te controleren
            // Ook even noshow onthouden
            oRs = Oracle.Execute("SELECT res_reservering_key"
                               + "     , res_rsv_ruimte_van"
                               + "     , res_rsv_ruimte_tot"
                               + "     , res_rsv_ruimte_noshow"
                               + "     , res_rsv_ruimte_volgnr"
                               + "  FROM res_rsv_ruimte"
                               + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key);
            var reservering_key = oRs("res_reservering_key").Value;
            var oldvan = new Date(oRs("res_rsv_ruimte_van").Value);
            var oldtot = new Date(oRs("res_rsv_ruimte_tot").Value);
            var oldnoshow = oRs("res_rsv_ruimte_noshow").Value == 1;
            var volgnr = oRs("res_rsv_ruimte_volgnr").Value;

            var resUpd = buildTrackingUpdate("res_rsv_ruimte", wheres.join(" AND " ), dbfields, { noValidateToken: true });
            var check_fail_sql = " if res.dirty_level_all(" + rsv_ruimte_key + ") <> 0 then"
                               + "    raise_application_error (-20000, 'res_m999 " + L("lcl_res_fe_no_dirty") + "');"
                               + " end if;"
            var sql = "BEGIN "
                    + resUpd.sql + ";"
                    + "  res.set_ruimte_dirty (" + rsv_ruimte_key + "); " // Zelf dirty geworden?
                    + "  res.set_ruimtes_clean (" + oldvan.toSQL() + "); " // Anderen 'clean' geworden
                    + "  res.follow_artikel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + "); " // catering mee verplaatsen
                    + "  res.follow_deel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + "); " // objecten mee verplaatsen
                    + "  res.follow_afspraak (" + rsv_ruimte_key + ", " + S("res_copy_to_bez") + "); " // bezoekers mee verplaatsen
                    + check_fail_sql
                    + "END;";
            var err = Oracle.Execute(sql, true);
            if (err.friendlyMsg)
                abort_with_warning(err.friendlyMsg);

            var restrack = api2.process_includes(params, this, jsondata, the_key);

            // TODO: Tracking
            // TODO: Bezoekers
            // TODO: Flex
            var flextrack = [];
            //var result = saveBezoekers(afspr_key, -1, { urole: urole, loctimechanged: changed });
            //if (result.beztrack && result.beztrack.length > 0)
            //bezUpd.trackarray.push(result.beztrack.join("\n"));
            res.trackreserveringupdate(rsv_ruimte_key, resUpd.trackarray.length || flextrack.length? L("lcl_res_is_resupdtrack").format(reservering_key + "/" + volgnr) + "\n" + resUpd.trackarray.concat(flextrack).join("\n") : null);

            return { key: rsv_ruimte_key };
        };

    this.REST_POST = function (params, jsondata) /* new reservation */
        {
            params.isNew = true;
            var dbfields = api2.update_fields(params, this, jsondata); // Build updater
            _analyze_fields(dbfields, params, jsondata);
            _validate_fields(dbfields, params, jsondata);

            // Eerst een nieuw res_reservering record aanmaken
            var resfields = { "id":     { dbs: "res_reservering_key",      typ: "key",      seq: "res_s_res_reservering_key" },
                              "create": { dbs: "res_reservering_aanmaak",  typ: "datetime", val: new Date() }
                            };
            var resIns = buildInsert("res_reservering", resfields, { noValidateToken: true });
            var reservering_key = resIns.sequences["res_reservering_key"];
            var volgnr = 1;
            Oracle.Execute(resIns.sql);

            var host_key = user_key;
            var contact_key = user_key;
            if (user.has("WEB_RESFOF"))
            {
                if ("host" in jsondata)
                    host_key = jsondata.host;
                if ("contact" in jsondata)
                    contact_key = jsondata.contact;
            }

            // Nu aan de slag met een res_rsv_ruimte record
            dbfields["id"]       = { dbs: "res_rsv_ruimte_key",         typ: "key",    seq: "res_s_res_rsv_ruimte_key" };
            dbfields["reskey"]   = { dbs: "res_reservering_key",        typ: "key",    val: reservering_key };
            dbfields["sequence"] = { dbs: "res_rsv_ruimte_volgnr",      typ: "number", val: volgnr };
            dbfields["host"]     = { dbs: "res_rsv_ruimte_host_key",    typ: "key",    val: host_key };
            dbfields["contact"]  = { dbs: "res_rsv_ruimte_contact_key", typ: "key",    val: contact_key };
            if (!jsondata.status)
                dbfields["status"] = { dbs: "res_status_fo_key", typ: "key",  val: S("res_default_fe_status_key") };
            var resIns = buildInsert("res_rsv_ruimte", dbfields, { noValidateToken: true });
            var rsv_ruimte_key = resIns.sequences["res_rsv_ruimte_key"];

            var check_fail_sql = " if res.dirty_level_all(" + rsv_ruimte_key + ") <> 0 then"
                               + "    raise_application_error (-20000, 'res_m999 " + L("lcl_res_fe_no_dirty") + "');"
                               + " end if;"
            var sql = "BEGIN "
                      + resIns.sql + ";"
                      //+ deel_sql
                      + "  res.set_ruimte_dirty (" + rsv_ruimte_key + "); " // Zelf dirty geworden?
                      // set_ruimtes_clean hoeft niet: wij zijn nieuw en kunnen daarmee nooit een ander clean maken
                      + check_fail_sql
                      + "END;";
            var err = Oracle.Execute(sql, true);
            if (err.friendlyMsg)
                abort_with_warning(err.friendlyMsg);

            var restrack = api2.process_includes(params, this, jsondata, rsv_ruimte_key);

            shared.trackaction("RESNEW", rsv_ruimte_key);

            return { key: rsv_ruimte_key };
        };

    this.REST_DELETE = function (params, the_key) /* delete reservation */
        {
            // Sterk afgekeken van res_delete_save.asp. Die moet op termijn deze functie gaan
            // gebruiken maar dan moet de auth_required_or_abort mogelijk weer iets 'zachter'
            var cost_recharge = false; // TODO?
            var rsv_ruimte_key = the_key;

            var this_res = res.func_enabled(rsv_ruimte_key); // Wat mag ik op deze deelreservering
            user.auth_required_or_abort(this_res.canDelete);

            DeleteResAfspraak(rsv_ruimte_key);
            // update ruimte
            // equipment en articles are deleted automatically if the RES_RSV_RUIMTE is deleted
            // Deze velden altijd opslaan
            var dbfields =  [ { dbs: "res_rsv_ruimte_opmerking",    typ: "varchar",  frm: "opmerk" },
                              { dbs: "res_status_fo_key",           typ: "key",      val: (cost_recharge? 4 : 1) }, // status 1=optie, 4=vervallen
                              { dbs: "res_rsv_ruimte_verwijder",    typ: "sql",      val: "SYSDATE" }
                            ];

            var sql = buildUpdate("res_rsv_ruimte", dbfields, { noValidateToken: true })
                    + "     res_rsv_ruimte_key = " + rsv_ruimte_key;

            // ons eigen tijdstip/zaal is mogelijk gewijzigd waardoor andere *dirty* reserveringen
            // clean wordt. Even onze oude datum onthouden zodat we niet *alles* hoeven te controleren
            var oRs = Oracle.Execute("select res_rsv_ruimte_van,"
                                   + "       res_rsv_ruimte_tot"
                                   + "  from res_rsv_ruimte"
                                   + " where res_rsv_ruimte_key = " + rsv_ruimte_key);
            var oldvan = new Date(oRs("res_rsv_ruimte_van").value);
            var oldtot = new Date(oRs("res_rsv_ruimte_tot").value);
            oRs.Close();

            // Nu de echte update.
            // Daarbij ook diverse controles/ updates uitvoeren op 'andere zaken'
            sql = "BEGIN "
                + sql + ";"
                + "  res.set_ruimte_dirty (" + rsv_ruimte_key + "); " // Zelf dirty geworden?
                + "  res.set_ruimtes_clean (" + oldvan.toSQL() + "); " // Anderen 'clean' geworden
                + "  res.follow_artikel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + "); " // catering mee verplaatsen
                + "  res.follow_deel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + "); " // objecten mee verplaatsen
                + "END;";
            Oracle.Execute( sql );

            shared.trackaction("RESDEL", rsv_ruimte_key);

            // En tenslotte eventueel de reservering zelf nog
            sql = "UPDATE res_reservering "
                + "   SET res_reservering_verwijder=SYSDATE"
                + " WHERE res_reservering_key="+this_res.res_reservering_key
                + "   AND NOT EXISTS (SELECT *"
                + "                     FROM res_v_aanwezigrsv_ruimte"
                + "                    WHERE res_reservering_key=" + this_res.res_reservering_key + ")"

            Oracle.Execute( sql );
        }

    if (rsv_key > 0)
    {
        params.filter = params.filter || {};
        params.filter.id = rsv_key;
        if (! ("include" in params) )
            params.include = { include: ["custom_fields"]};

        var xxx_array = this.REST_GET(params);
        if (!xxx_array.length)
            shared.record_not_found();
       this.data = xxx_array[0];
    }
}
%>