<%@ language = "JavaScript" %>
<% /*
    $Revision$
    $Id$

    File:           res_edit_rsv_ruimte_save.asp
    Description:    All information to be able to update an existing reservation is available.
                    Update it here!
    Parameters:
    Context:        Vanuit res_edit_rsv_ruimte.asp maar ook anderen zoals PDA-reserveringen
    Note:           Het bestand wordt altijd via $.post() aangeroepen
*/
var JSON_Result = true;
 %>
<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../shared/save2db.inc" -->
<!-- #include file="../shared/kpl_ksrt_validate.inc" -->
<!-- #include file="../shared/kenmerk_common.inc" -->
<!-- #include file="res.inc"-->
<%

var urole          = getQParamSafe("urole");
var rsv_ruimte_key = getQParamInt("rsv_ruimte_key", -1); // de *huidige* ruimte
var isNew = (rsv_ruimte_key == -1);
var rsv_copy_key   = getQParamInt("rsv_copy_key", -1); // de bron ruimte
var adhoc          = getQParamInt("adhoc", 0) == 1; // Ad hoc reservering
var res_pool       = getQParamInt("pool", 0) == 1; //  pool-reservering
var res_undelete   = getQParamInt("undelete", 0) == 1; // undelete reservering

var restype = getFParam("restype", "R");
var act_key = getFParamInt("sel_activity");

var res_ruimte_limiet = null;
var opstel_key = -1;

if (restype == "R")
{
  opstel_key = getFParamInt("ruimte_opstel", -1);
  if (opstel_key < 0) // Ruimte moet gegeven zijn. Zoek default opstelling
  {
      var res_ruimte_key = getFParamInt("res_ruimte_key");
      var sql = " SELECT res_ruimte_opstel_key "
              + "   FROM res_ruimte_opstelling "
              + "  WHERE res_ruimte_key = " + res_ruimte_key
              + "  ORDER BY res_ruimte_opstel_default DESC NULLS LAST " // die met '1' komt vooraan
      oRs = Oracle.Execute(sql);
      opstel_key = oRs("res_ruimte_opstel_key").value;
      oRs.Close();
  }

  var sql = "SELECT rr.res_ruimte_key, res_ruimte_limiet"
          + "  FROM res_ruimte rr, res_ruimte_opstelling rro"
          + " WHERE rro.res_ruimte_key = rr.res_ruimte_key AND res_ruimte_opstel_key = " + opstel_key;
  var oRs = Oracle.Execute(sql);
  if (!oRs.eof) {
    res_ruimte_key = oRs("res_ruimte_key").Value;
    res_ruimte_limiet = oRs("res_ruimte_limiet").Value;
  }
  oRs.Close();
}

var sql = "SELECT ra.res_srtactiviteit_key"
        + "     , ra.res_activiteit_meteindtijd"
        + "     , ra.res_activiteit_cvab_mode"
        + "     , res_activiteit_master_key"
        + "     , res_activiteit_ismaster"
        + "     , COALESCE( LEAST( COALESCE(" + res_ruimte_limiet + ", ra.res_activiteit_limiet)"
                      + "        , COALESCE(ra.res_activiteit_limiet, " + res_ruimte_limiet + ")"
                      + "        )"
                      + " , " + S("res_reservering_limiet")
                      + " ) act_limiet"
        + "     , COALESCE( LEAST( COALESCE(" + res_ruimte_limiet + ", ra.res_activiteit_limiet_fe)"
                      + "        , COALESCE(ra.res_activiteit_limiet_fe, " + res_ruimte_limiet + ")"
                      + "        )"
                      + " , " + S("res_fe_reservering_limiet")
                      + " ) act_limiet_fe"
        + "  FROM res_activiteit ra"
        + " WHERE ra.res_activiteit_key = " + act_key;
var oRs = Oracle.Execute(sql);
lcl.set_dialect(oRs("res_srtactiviteit_key").Value, "RES_SRTACTIVITEIT_KEY");
var met_eindtijd = oRs("res_activiteit_meteindtijd").Value == 1;
var act_cvab_mode = oRs("res_activiteit_cvab_mode").Value;
var eff_act_limiet    = oRs("act_limiet").Value;
var eff_act_limiet_fe = oRs("act_limiet_fe").Value;
var master_act_key = oRs("res_activiteit_master_key").Value;
var ismaster = oRs("res_activiteit_ismaster").Value;
oRs.Close();

var warning = "";
var res_bezoekers = getFParamInt("bezoekers", null);
var local_date_from = getFParamDate("date_from", null);
var server_date_from
  , server_date_to;

var local_tz;
if (!local_date_from)
{   // fe_lastminute kon kalender niet bewerken
    var sql = "SELECT res_rsv_ruimte_van, res_rsv_ruimte_tot"
            + "  FROM res_rsv_ruimte"
            + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
    var oRs = Oracle.Execute(sql);
    server_date_from = new Date(oRs("res_rsv_ruimte_van").Value);
    server_date_to = new Date(oRs("res_rsv_ruimte_tot").Value);
    local_tz = res.getRsvTimezone(rsv_ruimte_key);
    oRs.Close();
    local_date_from = convertToLocalTime(server_date_from, local_tz);
    local_date_to   = convertToLocalTime(server_date_to,   local_tz);
}
else
{
    var local_date_to = getFParamDate("date_to"); // die hebben we vaak nodig
    if (opstel_key < 0) {
        var alg_ruimte_key = getFParamInt("ruimtekey", -1);
        local_tz = res.getTimezoneByRuimte(alg_ruimte_key, "ALG");
    } else {
        local_tz = res.getTimezoneByRuimte(res_ruimte_key, "RES");
    }
    server_date_from = convertToServerTime(local_date_from, local_tz);
    server_date_to   = convertToServerTime(local_date_to,   local_tz);

    if (server_date_from.getSeconds() > 0 || server_date_from.getMilliseconds() > 0)
        server_date_from.setSeconds(59, 1000); // Omhoog afronden op minuten, minimaliseert conflicten
}
// prevent workplace date tampering
var act_limit = ((urole=="fe"&&eff_act_limiet_fe>0)?eff_act_limiet_fe:eff_act_limiet);

if (act_limit > -1)
{
    var dateTamperCheck = new Date();
    dateTamperCheck.setDate(dateTamperCheck.getDate() + parseInt(act_limit, 10) + 1);
    dateTamperCheck.setHours(0, 0, 0, 0);
    if (server_date_from > dateTamperCheck)
    {
        abort_with_warning(L("lcl_res_error_date").format(act_limit));
    }
}

// Voor zowel een ruimte als een voorzieningen reservering moet de duur minimaal S("res_block") zijn (DENB#18233)
// te kort voor een ruimte
var durationHours = (server_date_to - server_date_from) / 1000/60/60;
if (met_eindtijd && durationHours < S("res_block") )
{
    abort_with_warning(L("lcl_res_bad_period"));
}

if (!met_eindtijd)
{   // Zet bij reserveringen zonder eindtijd de eindtijd altijd gelijk aan de begintijd.
    // We houden nu geen rekening met de bloktijden en minimale reserveringsduur.
    server_date_to = server_date_from
}

// Is de reserveringstijd aangepast (dus niet voor nieuwe reservering)
var msTeamsChanged = false;
var resdateChanged = true;
var roomChanged = false;
var omsChanged = false;
var opmChanged = false;
if (!isNew)
{
  var sql = "SELECT res_rsv_ruimte_extern_meeting"
          + "     , res_rsv_ruimte_omschrijving"
          + "     , res_rsv_ruimte_opmerking"
          + "     , res_rsv_ruimte_dirtlevel"
          + "     , res_rsv_ruimte_van"
          + "     , res_rsv_ruimte_tot"
          + "     , res_rsv_ruimte_externnr"
          + "     , res_rsv_ruimte_externnr2"
          + "     , alg_ruimte_key"
          + "     , res_ruimte_opstel_key"
          + "     , res_activiteit_key"
          + "  FROM res_rsv_ruimte"
          + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
  var oRs = Oracle.Execute(sql);
  var date_from_old = new Date(oRs("res_rsv_ruimte_van").Value);
  var date_to_old   = new Date(oRs("res_rsv_ruimte_tot").Value);
  var ext_was_dirty = ((oRs("res_rsv_ruimte_dirtlevel").Value || 0) & 512); // Niet beschikbaar op gekozen tijdstip
  var rsv_teams_old = oRs("res_rsv_ruimte_extern_meeting").Value;
  var res_oms_old   = oRs("res_rsv_ruimte_omschrijving").Value;
  var res_opm_old   = oRs("res_rsv_ruimte_opmerking").Value;
  var rsv_ruimte_externnr  = oRs("res_rsv_ruimte_externnr").Value;
  var rsv_ruimte_externnr2 = oRs("res_rsv_ruimte_externnr2").Value;
  var ruimte_key_old  = oRs("alg_ruimte_key").Value;
  var opstel_key_old  = oRs("res_ruimte_opstel_key").Value;
  var act_key_old  = oRs("res_activiteit_key").Value;
  oRs.Close();
  if ((date_from_old.getTime() == server_date_from.getTime()) && (met_eindtijd && date_to_old.getTime() == server_date_to.getTime()))
  {
    var resdateChanged = false;
  }
  if (hasFParam("has_extern_meeting") && (getFParam("extern_meeting", "off") == "on" ? 1 : 0) != rsv_teams_old)
    msTeamsChanged = true;
  if (hasFParam("descript") && getFParam("descript", "") != res_oms_old)
    omsChanged = true;
  if (hasFParam("opmerk") && getFParam("opmerk", "") != (res_opm_old || ""))
    opmChanged = true;

  var sql = "SELECT COUNT(*)"
          + "  FROM res_rsv_artikel"
          + " WHERE res_rsv_artikel_verwijder IS NULL"
          + "   AND res_rsv_ruimte_key = " + rsv_ruimte_key;
  var oRs = Oracle.Execute(sql);
  var hasCatering = oRs(0).Value > 0;
  oRs.Close();

  var tmp_date_from = server_date_from.midnight();
  var tmp_date_to   = server_date_to.midnight();
  var res_meerdaags = met_eindtijd && (tmp_date_from.getTime() != tmp_date_to.getTime());

  if (res_meerdaags && hasCatering && !S("res_allow_multi_cat"))
  {
    abort_with_warning(L("lcl_res_allow_multi_cat")); // Meerdaagse reservering met catering niet toegestaan
  }
  // detect items who are not allowed within the parameters of their catalogue
  sql = "SELECT DISTINCT d.ins_discipline_key, " + lcl.xsqla('d.ins_discipline_omschrijving','d.ins_discipline_key')
      + "  FROM res_v_aanwezigrsv_artikel ara, res_v_aanwezigartikel ra, RES_v_aanwezigDISCIPLINE d, res_disc_params rd"
      + " WHERE ara.RES_RSV_RUIMTE_KEY = " + rsv_ruimte_key
      + "   AND ra.res_artikel_key = ara.res_artikel_key"
      + "   AND d.ins_discipline_key = ra.res_discipline_key"
      + "   AND rd.res_ins_discipline_key = d.ins_discipline_key"
      + "   AND (rd.res_disc_params_min_deelnmrs > " + (res_bezoekers || 0)
      + "      OR rd.res_disc_params_min_duur > " + durationHours + ")";
  oRs = Oracle.Execute(sql);
  if (!oRs.Eof)
  {
    var discArr = [];
    while (!oRs.eof)
    {
      discArr.push(oRs("ins_discipline_omschrijving").Value);
      oRs.MoveNext();
    }
    abort_with_warning(L("lcl_res_invalid_with_cat").format(discArr.join(", "))); // Some items are not allowed with this reservation
  }
  oRs.Close();

  // check whether chosen items can be transfered to the changed reservation
  var new_activiteit_key = getFParamInt("sel_activity", -1);
  sql = "SELECT d.ins_discipline_key"
      + "  FROM RES_v_aanwezigDISCIPLINE d, res_disc_params rd"
      + " WHERE ins_discipline_min_level = 2"
      + "   AND rd.res_ins_discipline_key = d.ins_discipline_key"
      + "   AND EXISTS"
            + "(SELECT res_activiteit_key"
            + "   FROM res_activiteitdiscipline rad"
            + "  WHERE rad.res_discipline_key = d.ins_discipline_key"
            + "    AND rad.res_activiteit_key = " + new_activiteit_key
            + ")";
  oRs = Oracle.Execute(sql);
  var discArr = [-1];
  while (!oRs.eof)
  {
    discArr.push(oRs("ins_discipline_key").Value);
    oRs.Movenext();
  }
  oRs.Close();
  // detect items who are not allowed within the parameters of their catalogue
  sql = "SELECT DISTINCT d.ins_discipline_key, " + lcl.xsqla('d.ins_discipline_omschrijving','d.ins_discipline_key')
      + "  FROM res_v_aanwezigrsv_artikel ara, res_v_aanwezigartikel ra, RES_v_aanwezigDISCIPLINE d, res_disc_params rd"
      + " WHERE ara.RES_RSV_RUIMTE_KEY = " + rsv_ruimte_key
      + "   AND ra.res_artikel_key = ara.res_artikel_key"
      + "   AND d.ins_discipline_key = ra.res_discipline_key"
      + "   AND rd.res_ins_discipline_key = d.ins_discipline_key"
      + "   AND d.ins_discipline_key NOT IN (" + discArr.join(",") + ")";
  oRs = Oracle.Execute(sql);
  if (!oRs.Eof)
  {
    var discOmsArr = [];
    while (!oRs.eof)
    {
      discOmsArr.push(oRs("ins_discipline_omschrijving").Value);
      oRs.MoveNext();
    }
    oRs.Close();
    abort_with_warning(L("lcl_res_invalid_with_itm").format(discOmsArr.join(", "))); // Some items are not allowed with this reservation
  }
  else
  {
    oRs.Close(); // always close the recordset
  }
  var this_res = res.func_enabled(rsv_ruimte_key); // Wat heb ik zoal aan rechten op deze specifieke reservering
}

if (restype == "R")
{
  local_date_to.setFullYear(local_date_from.getFullYear());
  local_date_to.setMonth(local_date_from.getMonth());
  local_date_to.setDate(local_date_from.getDate());
  server_date_to = convertToServerTime(local_date_to, local_tz);

  var kostenplaats_key = getFParamInt("account", -1);
  var sql = "SELECT COALESCE(res_ruimte_begintijd, " + S("res_t1") + ") begintijd"
          + "     , COALESCE(res_ruimte_eindtijd, " + S("res_t2") + ") eindtijd"
          + "     , rr.res_ruimte_begintijdblok"
          + "     , rr.res_ruimte_eindtijdblok"
          + "     , rr.res_discipline_key"
          + "     , rr.res_ruimte_startdatum"
          + "     , rr.res_ruimte_vervaldatum"
          + "     , rr.res_ruimte_min_duur"
          + "     , rr.res_ruimte_extern_id"
          + "  FROM res_ruimte_opstelling rro,"
          + "       res_ruimte rr,"
          + "       res_disc_params rdp,"
          + "       res_discipline rd"
          + " WHERE rro.res_ruimte_opstel_key = " + opstel_key
          + "   AND rr.res_ruimte_key = rro.res_ruimte_key"
          + "   AND rr.res_discipline_key = rd.ins_discipline_key "
          + "   AND rr.res_discipline_key = rdp.res_ins_discipline_key"
  var oRs = Oracle.Execute(sql);

  var disc_key = oRs("res_discipline_key").Value;
  var startdatum = oRs("res_ruimte_startdatum").Value != null? new Date(oRs("res_ruimte_startdatum").Value) : null;
  var vervaldatum = oRs("res_ruimte_vervaldatum").Value != null? new Date(oRs("res_ruimte_vervaldatum").Value) : null;
  var min_duur = oRs("res_ruimte_min_duur").Value;
  var extern_id = oRs("res_ruimte_extern_id").Value;

  // Er moet voor een minimale tijd gereserveerd zijn.
  if (met_eindtijd && min_duur && durationHours < min_duur)
  {
      oRs.Close();
      abort_with_warning(L("lcl_res_room_min_duration").format(min_duur));
  }

  if (startdatum && startdatum >= local_date_to)
  {
      oRs.Close();
      abort_with_warning(L("lcl_res_room_notavailable").format(toDateString(startdatum))); // Deze ruimte is beschikbaar per {0}. Kies een andere ruimte of een ander tijdstip!
  }
  if (vervaldatum && vervaldatum < local_date_to)
  {
      oRs.Close();
      abort_with_warning(L("lcl_res_room_expired").format(toDateString(vervaldatum))); // Deze ruimte is vervallen per {0}. Kies een andere ruimte of een ander tijdstip!
  }
  // Combinatievalidatie ruimte discipline
  if (!kpl_ksrt_validate(kostenplaats_key, null, disc_key))
  {
      oRs.Close();
      abort_with_warning(L("lcl_res_kpks_validatie"));
  }
  // Openingstijden ruimte
  var df = local_date_from.getHours() + local_date_from.getMinutes()/60;
  var dt = local_date_to.getHours() + local_date_to.getMinutes()/60;

  // TODO: De case dat ik mobiel reserveer "tot 10:00" en het is nu nog voor de openingstijd
  // dan wil ik natuurlijk dat het vanaf de openingstijd wordt gereserveerd in plaats
  // van dat-ie moppert dat het <nu> (bv 7:35) nog niet open is. Als je de testcase habt, kunnen we dat hier aanpassen.
  // en zorgen dat dat ook wordt opgeslagen. Hoeft in deze specifieke situatie wmb niet gemeld te worden

  if (df < oRs("begintijd").value || dt > oRs("eindtijd").value)
  {
      oRs.Close();
      abort_with_warning(L("lcl_res_date_room_closed"));
  }
  // Overlap met de blokkade
  if (   met_eindtijd
      && oRs("res_ruimte_eindtijdblok").Value
      && oRs("res_ruimte_begintijdblok").Value
      && df < oRs("res_ruimte_eindtijdblok").Value
      && dt > oRs("res_ruimte_begintijdblok").Value)
  {
      var begintijdblok = toTimeString(oRs("res_ruimte_begintijdblok").Value);
      var eindtijdblok = toTimeString(oRs("res_ruimte_eindtijdblok").Value);
      oRs.Close();
      abort_with_warning(L("lcl_res_date_room_blocked").format(begintijdblok,eindtijdblok));
  }

  // Te last-minute voor de ruimte zelf
  if (rsv_ruimte_key == -1 && !adhoc) // (update controle komt later wel)
  {
      var discipline_expire = res.discipline_expire(disc_key, (S("res_247organisation")==1?2:0), local_tz);
      if (server_date_from < discipline_expire.addFloatHours(0)) // addFloatHours rond af naar voorgaande kwartier
      {
          if (urole == "fe") // nieuwe is nooit canChangeFEExtended
          {
               oRs.Close();
               abort_with_warning(L("lcl_res_lastmin"));
          }
          else
              if ( !(urole == "bo" || (urole == "fo" && S("res_suppress_warnbo"))) )
                  warning = L("lcl_res_obj_lastminute");
      }
  }

  oRs.Close();
  // Mag deze activiteit in deze ruimte wel
  if (met_eindtijd)
  {
    var max_duration_err = res.max_duration_r(act_key, disc_key, durationHours);
    if (max_duration_err.err)
      abort_with_warning(max_duration_err.message);
  }
}

if (met_eindtijd && rsv_ruimte_key > 0)
{ // "CV" ook altijd controleren
  var max_duration_err = res.max_duration_cv(rsv_ruimte_key, durationHours);
  if (max_duration_err.err)
    abort_with_warning(max_duration_err.message);
}

// Volgende voor zowel R als CV controleren

// Uberhaubt te ver in de toekomst of verleden?
var theMinLimiet = ((urole=="fe")?0:(S("res_reservering_maxpast")>=0?S("res_reservering_maxpast"):eff_act_limiet));
var theMaxLimiet = ((urole=="fe"&&eff_act_limiet_fe>0)?eff_act_limiet_fe:eff_act_limiet);

if (resdateChanged)
{
  var mindate = new Date();
  var maxdate = new Date();
  mindate.setDate(mindate.getDate() - theMinLimiet - 1);
  maxdate.setDate(maxdate.getDate() + theMaxLimiet + 1);

  if (theMinLimiet >= 0 && server_date_from < mindate)
    abort_with_warning(L("lcl_res_error_date").format(theMinLimiet));

  if (theMaxLimiet > 0 && server_date_to > maxdate && met_eindtijd)
    abort_with_warning(L("lcl_res_error_date").format(theMaxLimiet));

  // Van reserveringen met children mag je de tijden niet zo krap
  // zetten dat er children buiten vallen
  if (ismaster)
  {
    var sql = "SELECT MIN(res_rsv_ruimte_van) res_rsv_ruimte_van,"
            + "       MAX(res_rsv_ruimte_tot) res_rsv_ruimte_tot,"
            + "       COUNT(*) aantal"
            + "  FROM res_rsv_ruimte"
            + " WHERE res_rsv_ruimte_verwijder IS NULL"
            + "   AND res_rsv_ruimte_parent_key = " + rsv_ruimte_key;
    var oRs = Oracle.Execute(sql);
    if (oRs("aantal").Value > 0)
    {
        var child_van = new Date(oRs("res_rsv_ruimte_van").Value);
        var child_tot = new Date(oRs("res_rsv_ruimte_tot").Value);
        if (server_date_from > child_van || server_date_to < child_tot)
        {
            abort_with_warning(L("lcl_res_children_outsidetime").format(toDateTimeString(child_van), toDateTimeString(child_tot)));
        }
    }
    oRs.Close()
  }
}

var nrbezoekers = getFParam("bezoekers", 1);
if ( (isNaN(parseInt(nrbezoekers))) && (nrbezoekers != ""))
{
  if (nrbezoekers != "")
    abort_with_warning("geen getal");
}
else
{
  if (getFParamInt("bezoekers", 1) <= 0)
  {
    abort_with_warning(L("lcl_res_no_visitors"));
  }
}

// ==========
// ========== Nu het echte bijwerken
// ==========

function countParking(rsv_ruimte_key)
{
  var sqlprk = "SELECT COUNT (*)"
             + "  FROM res_rsv_deel"
             + " WHERE bez_bezoekers_key IN"
               + "     (SELECT B.bez_bezoekers_key"
               + "      FROM   bez_bezoekers B"
               + "      ,      bez_afspraak A"
               + "      WHERE  B.bez_afspraak_key = A.bez_afspraak_key"
               + "        AND  A.res_rsv_ruimte_key = " + rsv_ruimte_key
               + "     )";
  var oRsp = Oracle.Execute(sqlprk);
  var aantal = oRsp(0).value;
  oRsp.close();
  return aantal;
}

function getRuimteOpstelling(opstel_key)
{
  var sqlro = "SELECT ro.res_opstelling_omschrijving"
            + "     , rg.res_ruimte_nr"
            + "     , rrr.res_ruimte_extern_id"
            + "  FROM res_ruimte rrr"
            + "     , res_ruimte_opstelling rro"
            + "     , res_opstelling ro"
            + "     , res_v_alg_ruimte_gegevens rg"
            + " WHERE rrr.res_ruimte_key = rro.res_ruimte_key"
            + "   AND rro.res_opstelling_key = ro.res_opstelling_key"
            + "   AND rro.res_ruimte_key = rg.res_ruimte_key"
            + "   AND rro.res_ruimte_opstel_key = " + opstel_key;
  var oRsRO = Oracle.Execute(sqlro);
  var opstelling_oms = oRsRO("res_opstelling_omschrijving").value;
  var ruimte_oms = oRsRO("res_ruimte_nr").value;
  var extern_id = oRsRO("res_ruimte_extern_id").value;
  oRsRO.close();
  return {
    opstelling_oms: opstelling_oms,
    ruimte_oms:     ruimte_oms,
    extern_id:      extern_id
  };
}


// Deze velden altijd opslaan
var fields =  [ { dbs: "res_ruimte_opstel_key",       typ: "key",      val: opstel_key,     track: true /* afzonderlijke tracking, wel oldjsval */ },
                { dbs: "prs_kostenplaats_key",        typ: "key",      frm: "account",      track: L("lcl_account"), foreign: "prs_kostenplaats" },
                { dbs: "res_rsv_ruimte_host_key",     typ: "key",      frm: "personH",      track: L("lcl_host"), foreign: "prs_perslid" },
                { dbs: "res_activiteit_key",          typ: "key",      frm: "sel_activity", track: L("lcl_activity"), foreign: "res_activiteit" },
                { dbs: "res_status_fo_key",           typ: "number",   frm: "status_fo",    track: true /* afzonderlijke tracking, wel oldjsval */ },
                { dbs: "res_rsv_ruimte_ordernr",      typ: "varchar",  frm: "ordernr",      track: L("lcl_mld_inf_Ordernr") },
                { dbs: "res_rsv_ruimte_korting",      typ: "float",    frm: "ruimte_korting", track: L("lcl_res_ruimtekorting") },
                { dbs: "res_rsv_ruimte_totaal",       typ: "float",    frm: "ruimte_totaal", track: L("lcl_res_ruimtetotaal") },
                { dbs: "res_rsv_ruimte_kosten_klant", typ: "check",    frm: "kostenklant",  track: L("lcl_res_inf_kostenklant") },
                { dbs: "res_rsv_ruimte_contact_key",  typ: "key",      frm: "person",       track: L("lcl_name"), foreign: "prs_perslid" },
                { dbs: "res_rsv_ruimte_bezoekers",    typ: "number",   frm: "bezoekers",    track: L("lcl_visitors") },
                { dbs: "res_rsv_ruimte_extern_meeting", typ: "check0", frm: "extern_meeting", track: L("res_ruimte_microsoft_teams") }
              ];

if (master_act_key > 0) // we zouden kunnen controleren of master_key wijst naar een reservering
                        // met activiteit master_act_key maar dat is nog even overkill
{
    fields.push({ dbs: "res_rsv_ruimte_parent_key", typ: "key", frm: "master_key" });
}


if (!rsv_ruimte_externnr || (S("msgraph_sync_level") & 6)) // Begin- en enddatum van reserveringen die extern (via Exchange) zijn gemaakt niet aanpassen, tenzij ReadWrite koppeling
{
    fields.push({ dbs: "res_rsv_ruimte_van",          typ: "datetime", val: server_date_from, track: (restype=="CV"? L("lcl_res_starttime_CV") : L("lcl_res_starttime")), track_in_tz: local_tz });
    fields.push({ dbs: "res_rsv_ruimte_tot",          typ: "datetime", val: server_date_to,   track: (restype=="CV"? L("lcl_res_endtime_CV") : L("lcl_res_endtime")),     track_in_tz: local_tz });
    fields.push({ dbs: "res_rsv_ruimte_omschrijving", typ: "varchar",  frm: "descript",       track: L("lcl_descr") });
    fields.push({ dbs: "res_rsv_ruimte_opmerking",    typ: "varchar",  frm: "opmerk",         track: L("lcl_remark"), len: 2000 });
}

if (opstel_key < 0) // Voorkomen dat beide gezet raken
    fields.push({ dbs: "alg_ruimte_key",
                  typ: "key",
                  frm: "ruimtekey",    track: L("lcl_room"),
                  foreign: { tbl: "alg_v_plaatsaanduiding",
                             key: "alg_onroerendgoed_keys",
                             desc: "alg_plaatsaanduiding",
                             where: "alg_onroerendgoed_type = 'R'" }  // Het type ook meegeven want anders kunnen er twee waarden in de tabel zijn met dezelfde alg_onroerendgoed_keys voor Locatie en Ruimte (UWVA#61554).
                });

if (res_undelete)
{
  fields.push( {dbs: "res_rsv_ruimte_verwijder", typ: "datetime", val: null, track: L("lcl_res_track_room_undeleted") });
  fields.push( {dbs: "res_rsv_ruimte_externnr", typ: "varchar", val: null });
  fields.push( {dbs: "res_rsv_ruimte_externnr2", typ: "varchar", val: null });
  fields.push( {dbs: "res_rsv_ruimte_externsyncdate", typ: "varchar", val: null });
}

if (S("res_reservering_flags") > 0)
  fields.push({ dbs: "res_rsv_ruimte_flag", typ: "number", frm: "resflag" /* notrack */ });


// Afhalen of bezorgen alleen voor CV reserveringen
if (restype == "CV")
{
  var cvab_mode = 0;
  if ( hasFParam("ab_mode") )
    cvab_mode = getFParamInt("ab_mode"); // Let op: ab_mode kan hier ook null zijn!
  else
    cvab_mode = ( (act_cvab_mode & 2) == 2
                ? act_cvab_mode
                : ( getFParamSafe("ab_mode_filtcode", "") == "RESA" ? 0 : 1 )
                );
  fields.push({ dbs: "res_rsv_ruimte_cvab_mode", typ: "number", val: cvab_mode, track: (L("lcl_res_bezorgen_fixed") +"/" + L("lcl_res_afhalen_fixed")) });
}

// We kennen de checkbox res_noshow en invulveld bezshown (RWSN#19201)
var setTrackaction_RESNOS = false;
var setNoshowEnddate = false;
if (S("res_noshow_detailed") == 1) // aantal methode
{
    fields.push({ dbs: "res_rsv_ruimte_bezoekers_shown",  typ: "number",   frm: "bezshown", track: L("lcl_res_real_visitors") });
    var werkelijk_aantal = getFParam("bezshown", "");
    if (werkelijk_aantal != "" && parseInt(werkelijk_aantal) == 0)
    {
        setNoshowEnddate = true;
        setTrackaction_RESNOS = true;
    }
}
else // vinkje methode
{
    fields.push({ dbs: "res_rsv_ruimte_noshow",           typ: "check",    frm: "res_noshow", track: L("lcl_res_no_show") });
    //fields.push({ dbs: "res_rsv_ruimte_bezoekers_shown",  typ: "number",   val: 0 })
    if (rsv_ruimte_key > 0)
    {
        if (Request.Form("has_res_noshow").count==1)
        {
            var newnoshow = getFParam("res_noshow", "off") == "on";
            oRs = Oracle.Execute("SELECT res_rsv_ruimte_noshow"
                               + "  FROM res_rsv_ruimte"
                               + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key);
            var oldnoshow = oRs("res_rsv_ruimte_noshow").Value == 1;
            oRs.Close();
            if (!oldnoshow && newnoshow)
                setNoshowEnddate = true;
        }
    }
}

if (setNoshowEnddate)
{
        var nu = new Date();
        nu.setSeconds(0);
        if (nu < server_date_to) server_date_to = nu;
        if (server_date_from > server_date_to) server_date_to = server_date_from;
        fields[4].val = server_date_to;
}

var pResult = {};
if (!custfunc.res_presave(pResult))
{
  abort_with_warning(pResult.errmsg);
}

// tamper check
if (S("fac_tamper_check") > 0 && !user.has(["WEB_RESFOF", "WEB_RESBOF"]))
{
    __Log("Tamper checking frontend");
    var person_key = getFParamInt("person", user_key);
    user.auth_required_or_abort(user.isCollega(person_key));

    var personH_key = getFParamInt("personH", user_key);
    if (personH_key != user_key) // geen onnodig dure controle
    {
        var bresult = user.func_enabled2("RES", {  // We nemen geen disciplines mee, dat is onbegonnen werk
                                                   // alg_key: getFParamInt("ruimtekey", null),
                                                   prs_key: personH_key
                                                }
                                        )
        user.auth_required_or_abort(bresult.canWrite("WEB_RESUSE"));
    }
}

if (isNew) // Nieuw, dan moeten we ook nog een res_reservering record aanmaken
{
    if (rsv_copy_key == -1)
    {
        var resfields = [ { dbs: "res_reservering_key",      typ: "key",      seq: "res_s_res_reservering_key" },
                          { dbs: "mld_melding_key",          typ: "key",      frm: "mld_melding_key"           },
                          { dbs: "res_reservering_aanmaak",  typ: "datetime", val: new Date()                  },
                          { dbs: "res_reservering_ispool",   typ: "number",   val: res_pool?1:0                }
                        ];
        var resIns = buildInsert("res_reservering", resfields);
        var reservering_key = resIns.sequences["res_reservering_key"];
        var volgnr = 1;
        Oracle.Execute(resIns.sql);
    }
    else
    {
        var sql = "SELECT res_reservering_key"
                + "  FROM res_rsv_ruimte"
                + " WHERE res_rsv_ruimte_key = " + rsv_copy_key;
        var oRs = Oracle.Execute(sql);
        var reservering_key = oRs("res_reservering_key").Value;
        var sql = "SELECT MAX(res_rsv_ruimte_volgnr) volgnr"
                + "  FROM res_rsv_ruimte"
                + " WHERE res_reservering_key = " + reservering_key
        var oRs = Oracle.Execute(sql);
        var volgnr = oRs("volgnr").Value + 1;
    }

    var status_fo = getFParamInt("status_fo", -1);
    if (status_fo == -1)
    {   // Pas op: dus alleen als er geen waarde meekomt voegen we deze status
        // hier toe. In het andere geval was deze kolom hiervoor al gesaved
        // en mag dat hier niet nogmaals
        // In de frontend kan de initiële status afhankelijk zijn van de res_ruimte.
        // Deze wordt sinds LOGI#31433 voor de frontend bijgehouden en meegegeven waardoor status_fo dan altijd een waarde heeft.
        status_fo = S("res_default_fo_status_key");
        fields = fields.concat(
                    [ { dbs: "res_status_fo_key",     typ: "key",    val: status_fo }]);
    }

    fields.push({ dbs: "mld_opdr_key",       typ: "key",      frm: "mld_opdr_key" });
    fields = fields.concat(
                    [ { dbs: "res_rsv_ruimte_key",        typ: "key",    seq: "res_s_res_rsv_ruimte_key" },
                      { dbs: "res_reservering_key",       typ: "key",    val: reservering_key },
                      { dbs: "res_rsv_ruimte_volgnr",     typ: "number", val: volgnr }
                   // { dbs: "res_rsv_ruimte_visibility", typ: "check0", frm: "visibility" } Nog niet muteerbaar vanuit Facilitor
                    ]);

    var res_deel_key = getQParamInt("res_deel_key", -1); // Meegegeven vanuit planbord, touchscreen of zoekfunctie?
    var res_artikel_key = getQParamInt("res_artikel_key", -1);
    var bonotify = false;

    if (res_deel_key > -1)
    {
        // Hoe lang mag ik dit deel reserveren?
        var sql = "SELECT p.res_disc_params_maxduur"
                + "     , " + lcl.xsqla("c.ins_discipline_omschrijving", "c.ins_discipline_key")
                + "     , p.res_disc_params_bonotify"
                + "  FROM res_deel d"
                + "     , res_disc_params p"
                + "     , res_discipline c"
                + " WHERE d.res_discipline_key = p.res_ins_discipline_key"
                + "   AND d.res_discipline_key = c.ins_discipline_key"
                + "   AND d.res_deel_key = " + res_deel_key;
        var oRs = Oracle.Execute(sql);
        bonotify = oRs("res_disc_params_bonotify").Value == 1;
        var maxHours = (oRs.Eof ? 0 : oRs("res_disc_params_maxduur").Value);
        if ((maxHours > 0) && (maxHours < durationHours))
        {
           abort_with_warning( oRs("ins_discipline_omschrijving").Value + ": " + L("lcl_res_bad_maxperiod").format(maxHours));
        }
        oRs.Close();
    }

    if (restype == "R")
    {
        // PF: volgens mij is het verhaal dat het voor CV reserveringen wordt bepaald door de
        //     statussen van de onderliggende items.
        fields = fields.concat(
                        [ { dbs: "res_status_bo_key",         typ: "key",      val: 2 } // Geregistreerd/Ingevoerd.
                        ]);
    }
    else // CV
    {   // een ruimte zonder catering heeft ook een dirtlevel 16. De code doet er verder niet echt iets mee.
        fields = fields.concat(
                        [ { dbs: "res_rsv_ruimte_dirtlevel", typ: "number", val: (res_deel_key > 0 ? 0 : res.dirtlevel.ruimte.nocatering) }
                        ]);
    }

    var resIns = buildInsert("res_rsv_ruimte", fields);
    var rsv_ruimte_key = resIns.sequences["res_rsv_ruimte_key"];

    // Nu pas hebben we de rsv_ruimte_key
    // CV kan nog niet echt dirty zijn? Jawel, als je planbord lang laat staan
    // dirty is te forceren door nodirty mee te submitten
    var check_fail_sql = "";
    if (getFParamInt("nodirty",0) == 1 || (S("res_fe_allow_dirty") != 1 && urole == 'fe') || extern_id) // Ook Externe reserveringen mogen niet dirty worden
    {
        check_fail_sql = " if res.dirty_level_all(" + rsv_ruimte_key + ") <> 0 then"
                       + "    raise_application_error (-20001, " + safe.qL("lcl_res_fe_no_dirty") + ");"
                       + " end if;"
    }

    var deel_sql = "";
    if (res_deel_key > 0) // vanuit planbord waarschijnlijk. Direct toevoegen
    {
         deel_sql = "INSERT INTO res_rsv_deel"
               + " (res_rsv_ruimte_key"
               + ", res_deel_key"
               + ", res_rsv_deel_van"
               + ", res_rsv_deel_tot"
               + ", res_rsv_deel_aantal)"
               + " VALUES(" + rsv_ruimte_key
               + "       ," + res_deel_key
               + "       ," + server_date_from.toSQL(true, true) // Vooralsnog geen eigen tijdstippen
               + "       ," + server_date_to.toSQL(true, true)
               + "       , 1);"
               + " res.set_delen_dirty (" + rsv_ruimte_key + "); " // Zelf dirty geworden?
    }

    // Nu de echte insert.
    // Daarna ook diverse controles/ updates uitvoeren op 'andere zaken'
    sql = "BEGIN "
        + resIns.sql + ";"
        + "\n" + deel_sql
        + "\n  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
        + "\n" + check_fail_sql;
    //if (bonotify)
    //    sql += " RES.notifybackoffice(" + rsv_ruimte_key + ", 'RESNEW');";

    sql += " END;";
    var err = Oracle.Execute(sql, true);
    if (err.friendlyMsg)
        abort_with_warning(err.friendlyMsg);
    else
    {
        if (restype == "R" || res_deel_key > 0) // CV komt later pas
        {   // Status geblokkeerd eventueel niet notificeren.
            shared.trackaction((status_fo == 3 && !S("res_reminder_noti_blocked") ? "#" : "") + "RESNEW", rsv_ruimte_key);
        }
    }
}
else // update
{
    // Hardness controle schrijfrechten
    // canChangeWP is voor Reserveringen die via de 'Reserveer Werkplek'-module gereserveerd zijn
    user.auth_required_or_abort(this_res.canChange || this_res.canChangeFEExtended || this_res.canChangeWP);

    if (restype == "R" && urole == "fe" && (act_key != act_key_old || opstel_key != opstel_key_old))
    {
      var discipline_expire = res.discipline_expire(disc_key, (S("res_247organisation")==1?2:0));
      if (server_date_from < discipline_expire.addFloatHours(0)) // addFloatHours rond af naar voorgaande kwartier
          abort_with_warning(L("lcl_res_lastmin")); // te laat
    }
    // Bij wijzigen tijdstip ruimte (naar voren) schuiven voorzieningen mogelijk mee.
    if (server_date_from < this_res.earliest_expire_change && this_res.canChange) // canChangeFEExtended kon toch niet bewerken
    {
        if (this_res.canChangeFEOnly)
            abort_with_warning(L("lcl_res_lastmin")); // te laat
        else
            if ( !(urole == "bo" || (urole == "fo" && S("res_suppress_warnbo"))) )
                warning = L("lcl_res_obj_lastminute");
    }

    // 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_verwijder"
                       + "     , 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 olddel = new Date(oRs("res_rsv_ruimte_verwijder").Value);
    var oldnoshow = oRs("res_rsv_ruimte_noshow").Value == 1;
    var volgnr = oRs("res_rsv_ruimte_volgnr").Value;
    oRs.Close(); // always close the recordset

    /* resUpd heeft na afloop handige oldjsvals */
    var resUpd = buildTrackingUpdate("res_rsv_ruimte", " res_rsv_ruimte_key = " + rsv_ruimte_key, fields);

    var check_fail_sql = "";
    if (S("res_fe_allow_dirty") != 1 && urole == 'fe' || extern_id) // Ook Externe reserveringen mogen niet dirty worden
    {
        check_fail_sql = " if res.dirty_level_all(" + rsv_ruimte_key + ") <> 0 then"
                       + "    raise_application_error (-20001, " + safe.qL("lcl_res_fe_no_dirty") + ");"
                       + " end if;"
    }

    // Undelete van reservering.
    var sql_res_res  = "";
    var sql_res_art  = "";
    var sql_res_deel = "";
    if (res_undelete && this_res.canDelete && S("res_allow_undelete"))
    {
      // Ook de verwijderdatum bij eventuele delen en artikelen moeten verwijder worden.
      // Helaas niet met .toSQL() omdat zonder parameter alleen de datum wordt genomen, en met parameter withTime alleen de uren en minuten erbij.
      // De verwijderdatum bevat echter ook seconden.
      var str = padout(olddel.getDate()) + "-" + padout(olddel.getMonth() + 1) + "-" + padout(olddel.getFullYear());
      str += " " + padout(olddel.getHours()) + ":" + padout(olddel.getMinutes()) + ":" + padout(olddel.getSeconds());
      str = "TO_DATE(" + safe.quoted_sql(str) + ", 'DD-MM-YYYY HH24:MI:SS')";

      sql_res_res  = "\n UPDATE res_reservering"
                   + "     SET res_reservering_verwijder = NULL"
                   + "   WHERE res_reservering_key = " + reservering_key + ";";
      sql_res_art  = "\n UPDATE res_rsv_artikel"
                   + "     SET res_rsv_artikel_verwijder = NULL"
                   + "   WHERE res_rsv_artikel_verwijder = " + str   // Alleen de artikelen die gelijk met de reservering zijn verwijderd.
                   + "     AND res_rsv_ruimte_key = " + rsv_ruimte_key + ";";
      sql_res_deel = "\n UPDATE res_rsv_deel"
                   + "     SET res_rsv_deel_verwijder = NULL"
                   + "   WHERE res_rsv_deel_verwijder = " + str      // Alleen de delen die gelijk met de reservering zijn verwijderd.
                   + "     AND res_rsv_ruimte_key = " + rsv_ruimte_key + ";";
    }

    // Nu de echte update.
    // Daarbij ook diverse controles/ updates uitvoeren op 'andere zaken'
    var Pvoor = countParking(rsv_ruimte_key); // Aantal gereserveerde parkeerplaatsen voor update
    var check_scope = ((S("res_fo_catering_scopeless") && (urole == "bo" || urole == "fo")) ? 0 : 1)
    sql = "BEGIN "
        + resUpd.sql + ";"
        + sql_res_res
        + sql_res_art
        + sql_res_deel
        + "\n  res.set_ruimte_dirty (" + rsv_ruimte_key + "); " // Zelf dirty geworden?
        + "\n  res.set_ruimtes_clean (" + oldvan.toSQL() + "); " // Anderen 'clean' geworden
        + "\n  res.follow_artikel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + ", " + check_scope + "); " // catering mee verplaatsen
        + "\n  res.follow_deel (" + rsv_ruimte_key + ", " + oldvan.toSQL(true) + ", " + oldtot.toSQL(true) + "); " // objecten mee verplaatsen
        + "\n"+ check_fail_sql
        + "END;";

    var err = Oracle.Execute(sql, true);
    if (err.friendlyMsg)
        abort_with_warning(err.friendlyMsg);

    var Pna = countParking(rsv_ruimte_key); // Aantal gereserveerde parkeerplaatsen na update

    if (Pna < Pvoor)
    { // De melding dat niet alle parkeerplaatsen gereserveerd konden worden aan de tracking toevoegen.
      alert_text = Pna + L("lcl_park_alert1") + Pvoor + L("lcl_park_alert2");
      resUpd.trackarray.push(alert_text);
    }

    var newstatus = getFParamInt("status_fo", -1)
    if (newstatus > 0 && newstatus != resUpd.oldjsvals["res_status_fo_key"])
    {
        var noti = { 1: "RESOPT",
                     2: "RESBEV",
                     3: "RESBLK", // De fac_notificatie_status voor RESBLK is 0 zodat niet wordt genotificeerd
                     4: "RESDEL"
                   }[newstatus];

        if (noti)
        {
            shared.trackaction((newstatus == 3 && !S("res_reminder_noti_blocked") ? "#" : "") + noti, rsv_ruimte_key);
        }
    }

    if (setTrackaction_RESNOS)
        shared.trackaction("RESNOS", rsv_ruimte_key);

  if (restype == 'R')
  {
    new_opstel_key = opstel_key;
    old_opstel_key = resUpd.oldjsvals["res_ruimte_opstel_key"];
    if (new_opstel_key != old_opstel_key)
    {
      new_opst_geg = getRuimteOpstelling(new_opstel_key);
      old_opst_geg = getRuimteOpstelling(old_opstel_key);
      if (new_opst_geg.ruimte_oms != old_opst_geg.ruimte_oms)
        resUpd.trackarray.push(L("lcl_room") + ": " + buildTrackText("varchar", old_opst_geg.ruimte_oms, new_opst_geg.ruimte_oms, { nodiff: true }));
      if (new_opst_geg.opstelling_oms != old_opst_geg.opstelling_oms)
        resUpd.trackarray.push(L("lcl_config") + ": " + buildTrackText("varchar", old_opst_geg.opstelling_oms, new_opst_geg.opstelling_oms, { nodiff: true }));
      if (old_opst_geg.extern_id != new_opst_geg.extern_id) {
        roomChanged = true;
      }
    }
  }

  res.keepFlexDocuments(rsv_ruimte_key, resUpd.oldjsvals["res_activiteit_key"], act_key);
}

var currentKenmerkenSQL = "SELECT kl.res_kenmerk_key"
                        + "     , kl.res_kenmerkreservering_waarde"
                        + "     , k.res_kenmerk_toonbaar"
                        + "     , k.fac_functie_key"
                        + "  FROM res_kenmerkwaarde kl"
                        + "     , res_kenmerk k"
                        + "     , res_rsv_ruimte rr"
                        + "     , res_activiteit ra"
                        + "     , res_srtactiviteit rsa"
                        + " WHERE k.res_kenmerk_key = kl.res_kenmerk_key"
                        + "   AND k.res_activiteit_key = ra.res_activiteit_key"
                        + "   AND rr.res_activiteit_key = ra.res_activiteit_key"
                        + "   AND rr.res_rsv_ruimte_key = kl.res_rsv_ruimte_key"
                        + "   AND ra.res_srtactiviteit_key = rsa.res_srtactiviteit_key"
                        + "   AND res_kenmerkwaarde_verwijder IS NULL"
                        + "   AND rr.res_rsv_ruimte_key = " + rsv_ruimte_key;

var allKenmerkenSQL = "SELECT COALESCE (" + lcl.xsql("k.res_kenmerk_omschrijving", "k.res_kenmerk_key")
                    + "              ,  " + lcl.xsql("t.res_srtkenmerk_omschrijving", "t.res_srtkenmerk_key") + ") kenmerk_omschrijving"
                    + "     , t.res_srtkenmerk_kenmerktype kenmerk_kenmerktype"
                    + "     , t.res_srtkenmerk_systeem     kenmerk_systeem"
                    + "     , k.res_kenmerk_toonbaar       kenmerk_readonly"
                    + "     , k.res_kenmerk_uniek          kenmerk_uniek"
                    + "     , fac_kenmerkdomein_key"
                    + "  FROM res_kenmerk k"
                    + "     , res_srtkenmerk t"
                    + " WHERE k.res_srtkenmerk_key = t.res_srtkenmerk_key"
                    + "   AND k.res_kenmerk_key = "; /* wordt in saveFlexkenmerken uitgebreid */

var flextrack = saveFlexKenmerken(rsv_ruimte_key,
                                  { kenmerkTable: "res_kenmerkwaarde",
                                    kenmerkParentKey : "res_rsv_ruimte_key",
                                    kenmerkWaarde: "res_kenmerkreservering_waarde",
                                    kenmerkKey: "res_kenmerk_key",
                                    kenmerkToonbaar: "res_kenmerk_toonbaar",
                                    currentKenmerkenSQL: currentKenmerkenSQL,
                                    allKenmerkenSQL: allKenmerkenSQL,
                                    requestQF: Request.Form,
                                    isNew: isNew,
                                    flexPath: "RES/R",
                                    tracking: true,
                                    module: "RES"
                                  });

/* Update een evt. Exchange event (via MS Graph) */
var newExternal   = (isNew || res_undelete || rsv_ruimte_externnr == null && rsv_ruimte_externnr2 == null) && extern_id != null; // Nooit verzonden, wel een externe ruimte
var roomExternal  = (roomChanged || extern_id != null); // Deze res_ruimte of de oude is/was een externe ruimte
if (    restype == "R"                // Dit is een Reservering
    && (S("msgraph_sync_level") & 6)  // En er is een Exchange koppeling geconfigureerd die ook naar Outlook schrijft
    && (newExternal ||                                                                                                                 // En ik ben nieuw, en anders ben ik;
          (   roomExternal                                                                                                             // 1) Extern
            && (roomChanged || resdateChanged || msTeamsChanged || omsChanged || opmChanged || rsv_ruimte_externnr2 == "##PENDING##")  // 2) én veranderd (of '##PENDING##'; die gaan we fixen)
            && this_res.canWriteExtern)))                                                                                              // 3) én ik mag schrijven
{
    var DEZE = this;
    var hook = hookfunc.gethook("utils/exchange/res_to_graph.wsc");
    hook.initialize({
      DEZE: DEZE,
      custabspath: Server.MapPath(custpath)
    });
    var params = {};
    if (roomChanged) {
      params.old_extern_id = old_opst_geg.extern_id; // Deze is nog nodig omdat we de oude ook willen verwijderen
    }
    hook.resToGraph(rsv_ruimte_key, (newExternal ? "C" : "U"), params);
    hook = null;
}

sql = "BEGIN "
    + "  res.follow_afspraak (" + rsv_ruimte_key + ", " + S("res_copy_to_bez") + "); " // bezoekers mee verplaatsen
    + "END;";
var err = Oracle.Execute(sql, true);
if (err.friendlyMsg)
    abort_with_warning(err.friendlyMsg);

// Nu nog de tracking.
if (!isNew)
  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);

// Klantspecifieke check functie (hookfunction) voor de invoer
var pResult = new HookResult();
if (!custfunc.res_postsave(rsv_ruimte_key, pResult))
{
  abort_with_warning(pResult.errmsg);
}

doneParams = { rsv_ruimte_key: rsv_ruimte_key,
               isnew: isNew,
               park_voor: Pvoor,
               park_na: Pna,
               reservering_key: reservering_key,
               reservering_volgnr: volgnr,
               warning: warning,
               success: true // anders hadden we wel abort_with_warning gedaan.
             };
Response.Write(JSON.stringify(doneParams));
%><% ASPPAGE_END(); %>
