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

    File:               res_edit_objcat_save.asp
    Description:        Opslaan van de gegevens uit res_edit_objcat.asp
                        Note that because of many checks and constraints in the input-form
                        availability is almost guaranteed.
    Parameters:
        rsv_ruimte_key required
                        .... alle andere gePOSTte res_edit_objcat form u2 fields

    Context:            Opsla script voor res_edit_objcat.asp maar ook $.post van anderen
    Note:
*/
var JSON_Result = true;
%>

<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/save2db.inc" -->
<!-- #include file="../shared/kenmerk_common.inc" -->
<!-- #include file="res.inc"-->

<%
protectRequest.validateToken();

var urole = getQParamSafe("urole");
var rsv_ruimte_key = getQParamInt("rsv_ruimte_key");
var verynew = getQParamInt("verynew",0);

var this_res = res.func_enabled(rsv_ruimte_key); // Wat heb ik zoal aan rechten op deze specifieke reservering
user.auth_required_or_abort(this_res.canChangeCV || this_res.canChangeWP); // canChangeWP is voor Reserveringen die via de 'Reserveer Werkplek'-module gereserveerd zijn

res.res_set_dialect(rsv_ruimte_key);

// Zo snel mogelijk relevante basisinformatie opzoeken
sql = "SELECT res_reservering_key, "
    + "       res_rsv_ruimte_van, res_rsv_ruimte_tot,"
    + "       prs_kostenplaats_key,"
    + "       res_ruimte_opstel_key,"
    + "       res_rsv_ruimte_volgnr"
    + "  FROM res_rsv_ruimte"
    + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
oRs = Oracle.Execute(sql);
var res_key = oRs("res_reservering_key").value;
var res_van = new Date(oRs("res_rsv_ruimte_van").value);
var res_tot = new Date(oRs("res_rsv_ruimte_tot").value);
var kp_key = oRs("prs_kostenplaats_key").value || -1;
var res_volgnr = oRs("res_rsv_ruimte_volgnr").Value;
if (oRs("res_ruimte_opstel_key").value != null)
  restype = "R";
else
  restype = "CV";  // of "O", maar dat onderscheid is hier verder niet erg relevant
oRs.Close();

var warning = null;
// Hier wordt de expire getest van de *bestaande* objcat
// Setting "res_cat_with_expired_room": Can catering be edited when room is expired {false | true}.
if ((!S("res_cat_with_expired_room") && res_van < this_res.earliest_expire_change) || (res_van < this_res.earliest_expire_changeCV))
{
  if (this_res.canChangeFEOnly)
      abort_with_warning(L("lcl_res_lastmin")); // te laat
  //later pas..., we weten niet eens of ze iets opengeklapt hebben
  //else
  //    if (urole != "bo")
  //        warning = L("lcl_res_obj_lastminute");
}

// Check of er delen dirty zijn (geworden), en of dat mag, zo niet -> raise_application_error
var check_fail_sql = "";
if (S("res_fe_allow_dirty") != 1 && this_res.canChangeFEOnly) {
  check_fail_sql = " if res.dirty_level_all(" + rsv_ruimte_key + ") <> 0 then"
                 + "    raise_application_error (-20001, " + safe.quoted_sql(L("lcl_res_fe_no_dirty")) + ");"
                 + " end if;"
}

var art_dirty_sql = "";
if (!(S("res_fo_catering_scopeless") && (urole == "fo" || urole == "bo")))
{ // Controle of artikelen dirty zijn.
  art_dirty_sql = " res.set_artikelen_dirty (" + rsv_ruimte_key + ");"
                + check_fail_sql;
}

var _disc_expires = []; // expire tijd per discipline
function is_expired(disc_key, jsDate)
{
  if (disc_key > 0)
  {
    if (!_disc_expires[disc_key])
      _disc_expires[disc_key] = res.discipline_expire(disc_key);
    return (jsDate < _disc_expires[disc_key]);
  }
}

// artikelen die gesubmit zijn
function resObj(res_deel, res_rsv_deel, van_h, van_m, tot_h, tot_m, i_cnt, i_price, res_oms)
{
  this.res_oms = res_oms;
  this.res_deel = res_deel;
  this.res_rsv_deel = res_rsv_deel; // is hier de res_rsv_artikel_key
  this.van_h = van_h;
  this.van_m = van_m;
  this.tot_h = null;
  this.tot_m = null;
  this.cnt = 0;
  if (i_cnt)
    this.cnt = i_cnt;
  this.price = 0;
  if (i_price)
    this.price = i_price;
}

function resObj_add(insObj_arr, cur_res_art, cur_rsv_art, tot_h, tot_m)
{   // Toevoegen van afhaaltijden aan het laatste object in het array.
    var curObj = insObj_arr[insObj_arr.length -1];
    if (curObj.res_deel == cur_res_art && curObj.res_rsv_deel == cur_rsv_art)
    {
        curObj.tot_h = tot_h;
        curObj.tot_m = tot_m;
    }
}

function saveFlexKenmerkenArt(rsv_artikel_key, params)
{
  var currentKenmerkenSQL = "SELECT ka.res_kenmerk_key"
                          + "     , ka.res_kenmerkartikel_waarde"
                          + "     , k.res_kenmerk_toonbaar"
                          + "     , k.fac_functie_key"
                          + "  FROM res_kenmerkartikel ka"
                          + "     , res_kenmerk k"
                          + "     , res_rsv_artikel ra"
                          + " WHERE k.res_kenmerk_key = ka.res_kenmerk_key"
                          + "   AND ra.res_rsv_artikel_key = ka.res_rsv_artikel_key"
                          + "   AND res_kenmerkartikel_verwijder IS NULL"
                          + "   AND ra.res_rsv_artikel_key = " + rsv_artikel_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_artikel_key,
                                    { kenmerkTable: "res_kenmerkartikel",
                                      kenmerkParentKey : "res_rsv_artikel_key",
                                      kenmerkWaarde: "res_kenmerkartikel_waarde",
                                      kenmerkKey: "res_kenmerk_key",
                                      kenmerkToonbaar: "res_kenmerk_toonbaar",
                                      currentKenmerkenSQL: currentKenmerkenSQL,
                                      allKenmerkenSQL: allKenmerkenSQL,
                                      requestQF: Request.Form,
                                      extraserie: true,
                                      nameprefix: params.nameprefix,
                                      isNew: params.isNew,
                                      flexPath: "RESA/R",
                                      tracking: true,
                                      module: "RES"
                                    });
  return flextrack;
}

// consumable objects

// artikelen in de database
function artinf(key, rsv_key, pricefixed, disc, quantity, lev_h, lev_m, col_h, col_m, price, bo_status, free_art, art_oms)
{
    this.key = key;
    this.rsv_key = rsv_key;
    this.pricefixed = pricefixed;
    this.disc = disc;
    this.quantity = quantity;
    this.lev_h = lev_h;
    this.lev_m = lev_m;
    this.col_h = col_h;
    this.col_m = col_m;
    this.price = price;
    this.bo_status = bo_status;
    this.free_art = (free_art == 1);
    this.art_oms = art_oms;
}

var backo = this_res.canWrite("WEB_RESBOF");
var fronto = this_res.canWrite("WEB_RESFOF");

var year = res_van.getFullYear();
var month = res_van.getMonth()+1;
var day = res_van.getDate();

succes = true; // igv twijfel

// reservable objects
var aDel = new Array();
var aUpd = new Array();
var aIns = new Array();

var a_arIns = new Array();

var cur_res_free = false;
var cur_res_oms = null;
var cur_res_art = null;
var cur_rsv_art = null;
var cur_lev_h = "";
var cur_lev_m = "";
var cur_col_h = "";
var cur_col_m = "";
var cur_cnt = 0;
var cur_price = 0;

var pos, lpos, npos, upos;
var sstring = "", pstring;

var must_warn = false; // must I warn about last-minute?

//=================================================
// DEEL EEN: PARSEN VAN HET FORMULIER
//
//
// Request.Form is iets als inpb_c_216_2470136=0,inpbh1_c_216_2470136=14,inpbm1_c_216_2470136=30,inpb_c_217_2470138=0,inpbh1_c_217_2470138=14,inpbm1_c_217_2470138=30,inpeh1_c_217_2470138=15,inpem1_c_217_2470138=30,......
// waarbij 216 de res_artikel_key is en 2470136 de res_rsv_artikel_key
// in het geval van catering
for (var i = 1; i <= Request.Form.Count; i++)
{
  var fname = Request.Form.key(i);
  var fval = getFParam(fname, "");
  var sstring = fname.substring(0,7);
  __Log({fname:fname, fval:fval, sstring:sstring});

  if (sstring == "inpbd_c")
  {
    cur_res_free = true;
  }
  pos = lpos = npos = upos = 0;
  if (fval && fval.length)
  {
    switch (sstring)
    {
      case "inpbd_c":
      { // EGR: Parsing catering vrije artikelomschrijving
        if (fname.indexOf("inpbd_c_") >= 0)
        {
          cur_res_oms = fval;
        }
        break;
      }
      case "inpbp_c":
      { // JGL: Parsing catering RES_ARTIKEL_KEY price
        // Will only occur for backoffice
        if (cur_res_art)
        {
          if (fname.indexOf("inpbp_c_") >= 0)
          {
            if (fval == "" || isNaN(parseFloat(fval.replace(/,/g, "."))))
            {
              cur_res_art = null;
              cur_rsv_art = null;
              cur_lev_h = "";
              cur_lev_m = "";
              cur_col_h = "";
              cur_col_m = "";
              cur_price = 0;
              break;
            }
            cur_price = parseFloat(fval.replace(/,/g, "."));
          }
        }
        break;
      }

      //=================================================
      case "inpbh1_":
      { // JGL: Parsing catering hour
        if (cur_res_art)
        {
          if (fname.indexOf("inpbh1_c_") >= 0)
          { // Het is een catering veld
            if (fval == "" || isNaN(parseInt(fval,10)))
            {
              cur_res_art = null;
              cur_rsv_art = null;
              cur_lev_h = "";
              cur_lev_m = "";
              cur_col_h = "";
              cur_col_m = "";
              cur_price = 0;
              break;
            }
            cur_lev_h = parseInt(fval,10);
          }
        }
        break;
      }

      //=================================================
      case "inpbm1_":
      { // JGL: Parsing catering minutes
        if (cur_res_art)
        { // Zal alleen gezet zijn als het aantal ongelijk 0 was
          if (fname.indexOf("inpbm1_c_") >= 0)
          { // Het is een catering veld
            if (fval == "" || isNaN(parseInt(fval,10)))
            {
              cur_res_art = null;
              cur_rsv_art = null;
              cur_lev_h = "";
              cur_lev_m = "";
              cur_col_h = "";
              cur_col_m = "";
              cur_price = 0;
              break;
            }
            cur_lev_m = parseInt(fval,10); // De minuten
          }

          if (a_arIns.length && a_arIns[a_arIns.length-1].res_rsv_deel == cur_rsv_art && a_arIns[a_arIns.length-1].res_deel == cur_res_art)
          { // CAPG#161
            __DoLog("Internal error: Attempting to add " + cur_res_art + " twice", "#FF0000");
            __DoLog("Request.Form: " + String(Request.Form));
          }
          else
          {
            if (!cur_res_free || (cur_res_free && cur_res_oms != null && cur_res_oms != ""))
              a_arIns[a_arIns.length] = new resObj(cur_res_art, cur_rsv_art, cur_lev_h, cur_lev_m, cur_col_h, cur_col_m, cur_cnt, cur_price, cur_res_oms);
            // reset waarden voor volgende regel.
            cur_res_oms = null;
            cur_res_free = false;
          }
        }
        break;
      }

      case "inpb_c_":
      { // JGL: Parsing catering aantal en bepaal catering RES_ARTIKEL_KEY key
        if (cur_res_art)
        {
          cur_res_art = null;
          cur_rsv_art = null;
          cur_lev_h = "";
          cur_lev_m = "";
          cur_col_h = "";
          cur_col_m = "";
          cur_price = 0;
        }

        cur_cnt = parseInt(fval, 10); // Onthouden totdat we het uur en de minuten ook hebben
        if (isNaN(cur_cnt) || (this_res.canReturnCV ? cur_cnt == 0 : cur_cnt <= 0))
        { // is count corrupted or undefined
          i += (backo? 4 : 2); // skip hours and minutes (and for backo also price and total price)
          cur_res_free = false; // dat moeten we dan ook opnieuw bepalen
          continue;
        }
        res_rsv_art = fname.substring(7);
        cur_res_art = res_rsv_art.substring(0,res_rsv_art.indexOf("_")); // Bepaal catering RES_ARTIKEL_KEY key
        cur_rsv_art = res_rsv_art.substring(res_rsv_art.indexOf("_")+1); // Bepaal catering RES_RSV_ARTIKEL_KEY key
        break;
      }

      case "inpb_g_":
      { // Parsing catering artikel geleverd
        if (cur_res_art)
        {
          cur_res_art = null;
          cur_rsv_art = null;
          cur_lev_h = "";
          cur_lev_m = "";
          cur_col_h = "";
          cur_col_m = "";
          cur_price = 0;
          cur_geleverd = 0;
        }
        res_rsv_art = fname.substring(7);
        cur_res_art = res_rsv_art.substring(0,res_rsv_art.indexOf("_")); // Bepaal catering RES_ARTIKEL_KEY key
        cur_rsv_art = res_rsv_art.substring(res_rsv_art.indexOf("_")+1); // Bepaal catering RES_RSV_ARTIKEL_KEY key
        // gelijk maar het veld aanpassen zonder naar de waarde van de checkbox te kijken:
        var sql_geleverd = "UPDATE res_rsv_artikel"
                         + "   SET res_rsv_artikel_geleverd = SYSDATE"
                         + " WHERE res_rsv_artikel_key = " + cur_rsv_art;
        Oracle.Execute(sql_geleverd);
        break;
      }

      //=================================================
      case "inpeh1_":
      { // JGL: Parsing catering collect hour
        if (cur_res_art)
        {
          if (fname.indexOf("inpeh1_c_") >= 0)
          { // Het is een catering veld
            if (fval == "" || isNaN(parseInt(fval,10)))
            {
              cur_col_h = "";
              cur_col_m = "";
              break;
            }
            cur_col_h = parseInt(fval,10);
          }
        }
        break;
      }

      //=================================================
      case "inpem1_":
      { // JGL: Parsing catering collect minutes
        if (cur_res_art)
        { // Zal alleen gezet zijn als het aantal ongelijk 0 was
          if (fname.indexOf("inpem1_c_") >= 0)
          { // Het is een catering veld
            if (fval == "" || isNaN(parseInt(fval,10)))
            {
              cur_col_h = "";
              cur_col_m = "";
              break;
            }
            cur_col_m = parseInt(fval,10); // De minuten
          }
          // Afhaaltijd hoeft niet aanwezig te zijn, maar indien toch dan waarden nog toevoegen.
          resObj_add(a_arIns, cur_res_art, cur_rsv_art, cur_col_h, cur_col_m);
        }
        break;
      }
    }
  }
}

var trackarray = [];
//=================================================
// DEEL TWEE: DE OBJECTEN BIJWERKEN
// Onze checkboxen hebben een hidden veld 'has_xxx' waaraan je kunt zien dat het object gereservereerd was
// in het voorheen.
var durationHours = (res_tot - res_van) / 1000/60/60;
var last_discipline = -1;
var curr_discipline = -1;

// refactoring add and tracking of res_deel from reservation
function addAndTrackRsvDeel(_rsv_ruimte_key, _deel_key, _name, _addCount)
{
  // 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"
          + "     , c.ins_discipline_key"
          + "  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 = " + _deel_key;
  var oRs = Oracle.Execute(sql);
  var bonotify = oRs("res_disc_params_bonotify").Value == 1;
  var maxHours = (oRs.Eof ? 0 : oRs("res_disc_params_maxduur").Value);
  curr_discipline = oRs("ins_discipline_key").Value;
  if ((maxHours > 0) && (maxHours < durationHours))
  {
    abort_with_warning( oRs("ins_discipline_omschrijving").Value + ": " + L("lcl_res_bad_maxperiod").format(maxHours));
  }
  oRs.Close();

  var sql_insDeel = " 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
                  + "  ," + _deel_key
                  + "  ," + res_van.toSQL(true) // Vooralsnog geen eigen tijdstippen
                  + "  ," + res_tot.toSQL(true)
                  + "  , 1);";
  sql = "BEGIN "
      + sql_insDeel
      + " res.set_delen_dirty (" + rsv_ruimte_key + "); "
      + check_fail_sql;

  if (bonotify && (curr_discipline != last_discipline)) {
    sql += "  RES.notifybackoffice(" + rsv_ruimte_key + ", 'RESNEW', 'V', " + deel_key + ");";
    last_discipline = curr_discipline;
  }

  sql += "END;";

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

  __Log("added " + _name + " : " + _deel_key);

  // Tracking toevoegen.
  if (_addCount > 0)
  { // Voor bulk maar 1 keer een regel maar dan wel het aantal vermelden.
    var inserttrack = res.trackobject(_deel_key, "insert") + (_addCount > 1 ? " ("+_addCount+"x)" : "");
    trackarray.push(inserttrack);
  }
}
// refactoring delete and tracking of res_deel from reservation
function deleteAndTrackRsvDeel(_rsv_ruimte_key, _deel_key, _name, _delCount)
{
  var _deleteAndTrackSql = "DELETE from res_rsv_deel"
      + " WHERE res_rsv_ruimte_key=" + _rsv_ruimte_key
      + "   AND res_deel_key = " + _deel_key;
  Oracle.Execute(_deleteAndTrackSql);
  __Log("removed " + _name + " : " + _deel_key);
  //+ "  res.set_delen_clean(" + aDel[i].res_deel + "," + res_van.toSQL() + "); " // Anderen 'clean' geworden vandaag?
  // Tracking toevoegen.
  if (_delCount > 0)
  {
    var deletetrack = res.trackobject(_deel_key, "delete") + (_delCount > 1 ? " ("+_delCount+"x)" : "");
    trackarray.push(deletetrack);
  }
}

for (var i = 1; i <= Request.Form.Count; i++)
{
  deel_key = -1;
  var name = Request.Form.key(i);
  if (name.match(/has_[0-9].*/)) // bestaand object
  {
    var deel_key = parseInt(name.substring(4));
    var present = Request.Form("chk_" + deel_key).Count > 0;
    if (!present)
    {
      // handle removed bulk-items
      var bulk = Request.Form("amt_" + deel_key).Count > 0;
      if (bulk)
      {
        var sqlBulk = "SELECT res_deel_key FROM res_rsv_deel"                         // select items
                    + " WHERE res_rsv_ruimte_key=" + rsv_ruimte_key
                    + "   AND res_deel_key IN ( "
                    + "        SELECT res_deel_key FROM res_deel"
                    + "         WHERE res_deel_omschrijving = ( "                     // with the same description
                    + "                SELECT res_deel_omschrijving FROM res_deel"    //  as the de-selected bulk-item
                    + "                 WHERE res_deel_key = " + deel_key
                    + "         )"
                    + " )";
        var oRsBulk = Oracle.Execute(sqlBulk);
        while (!oRsBulk.eof)
        {
          deleteAndTrackRsvDeel(rsv_ruimte_key, oRsBulk("res_deel_key").Value, name);
          oRsBulk.MoveNext();
        }
        oRsBulk.Close();
      }
      else
      {
          deleteAndTrackRsvDeel(rsv_ruimte_key, deel_key, name);
      }
    }
    else
    {
      // Als we update van de tijdstipjes gaan ondersteunen komt dat hier.
      //+ "  res.set_deel_dirty (" + aUpd[i].res_rsv_deel + "); " // Zelf dirty geworden?
      //+ "  res.set_delen_clean(" + aUpd[i].res_deel + "," + res_van.toSQL() + "); " // Anderen 'clean' geworden vandaag?
    }
  }
  else // misschien nieuw object
  {
    if (name.match(/chk_[0-9].*/))
    {
      var deel_key = parseInt(name.substring(4));
      var old = Request.Form("has_" + deel_key).Count > 0;
      // handle new/updated bulk-items
      var bulk = Request.Form("amt_" + deel_key).Count > 0;
      if (bulk)
      {
        var bulkAmount = Request.Form("amt_" + deel_key);
        // sql for unselected items for the same bulk-group (self excluded)

        var sqlUnsBulk = " SELECT"
                       + "     res_deel_key"
                       + " FROM"
                       + "     res_deel rd"
                       + " WHERE"
                       + "     rd.res_deel_bulkreserveerbaar = 1"
                       + "     AND rd.res_deel_omschrijving = (SELECT res_deel_omschrijving FROM res_deel WHERE res_deel_key = " + deel_key + ")"
                         + "               AND rd.res_deel_key NOT IN ("
                         + "                                            SELECT distinct rrd.res_deel_key"
                         + "                                              FROM res_rsv_ruimte rrr, res_rsv_deel rrd, res_deel rd"
                         + "                                             WHERE rrr.res_rsv_ruimte_van < " + res_tot.toSQL(true)
                         + "                                               AND rrr.res_rsv_ruimte_tot > " + res_van.toSQL(true)
                         + "                                               AND rrd.res_rsv_ruimte_key = rrr.res_rsv_ruimte_key"
                         + "                                               AND rrd.res_deel_key = rd.res_deel_key"
                         + "                                               AND rd.res_deel_bulkreserveerbaar = 1"
                         + "                                          )"
                       + " ORDER BY rd.res_deel_key ASC"; // low to high key
        // read all selected items in reservation for the same bulk-group
        var sqlSelBulk = "SELECT rd.res_deel_key"
                       + "  FROM res_v_aanwezigdeel rd"
                       + "     , res_rsv_deel rrd"
                       + " WHERE rd.res_deel_bulkreserveerbaar = 1"
                       + "   AND rd.res_deel_key = rrd.res_deel_key"
                       + "   AND rrd.res_rsv_ruimte_key=" + rsv_ruimte_key
                       + "   AND rd.res_deel_omschrijving = (SELECT res_deel_omschrijving FROM res_deel WHERE res_deel_key = " + deel_key + ")"
                       + " ORDER BY rd.res_deel_key DESC"; // last in first out order
        // count number of grouped bulk-items in reservation
        var oRsCntBulk = Oracle.Execute("SELECT COUNT(*) total FROM (" + sqlSelBulk + ")");
        var cntBulk = oRsCntBulk("total").Value;
        // remove item(s) from reservation
        if (bulkAmount < cntBulk)
        {
          var oRsSelBulk = Oracle.Execute(sqlSelBulk);
          var cntIndex;
          for (cntIndex = 0; cntIndex < (cntBulk - bulkAmount); cntIndex++)
          {
            var delCount = (cntIndex < (cntBulk - bulkAmount -1) ? 0 : (cntBulk - bulkAmount)); // Alleen het totaal aantal tracken.
            deleteAndTrackRsvDeel(rsv_ruimte_key, oRsSelBulk("res_deel_key").Value, name, delCount);
            oRsSelBulk.MoveNext();
          }
          oRsSelBulk.Close();
        }
        // add item(s) to reservation
        else if (bulkAmount > cntBulk)
        {
          var oRsUnsBulk = Oracle.Execute(sqlUnsBulk);
          for (var cntIndex = 0; cntIndex < (bulkAmount - cntBulk); cntIndex++)
          {
            var addCount = (cntIndex < (bulkAmount - cntBulk -1) ? 0 : (bulkAmount - cntBulk)); // Alleen het totaal aantal tracken.
            addAndTrackRsvDeel(rsv_ruimte_key, oRsUnsBulk("res_deel_key").Value, name, addCount);
            oRsUnsBulk.MoveNext();
          }
          oRsUnsBulk.Close();
        }
        oRsCntBulk.Close();
      }
      else
      {
        if (!old)
        {   // Is geen bulk, dus altijd 1 tracken.
            addAndTrackRsvDeel(rsv_ruimte_key, deel_key, name, 1);
        }
      }
    }
  }

  if (deel_key > 0 && !must_warn)
  {
    // Expire check
    sql = "SELECT res_discipline_key"
        + "  FROM res_deel rd"
        + " WHERE rd.res_deel_key = " + deel_key;
    oRs = Oracle.Execute(sql)
    must_warn = must_warn || is_expired(oRs("res_discipline_key").value, res_van);
    oRs.Close();
  }
}

// consumable objects

// Haal eerst de (volgens de database) reeds eerder gereserveerde objecten op
// en bewaar die in array r_a_key
// use initial (old) year, month and day when date was changed
sql = "SELECT r_disc.ins_discipline_key"
    + "     , r_art.res_artikel_key"
    + "     , r_r_art.res_rsv_artikel_key"
    + "     , r_r_art.res_rsv_artikel_aantal"
    + "     , TO_CHAR(r_r_art.res_rsv_artikel_levering,'HH24') hh"
    + "     , TO_CHAR(r_r_art.res_rsv_artikel_levering,'MI') mi"
    + "     , TO_CHAR(r_r_art.res_rsv_artikel_afhalen, 'HH24') eh"
    + "     , TO_CHAR(r_r_art.res_rsv_artikel_afhalen, 'MI') em"
    + "     , r_art.res_artikel_prijs_vast vast"
    + "     , COALESCE( CASE WHEN r_art.res_artikel_prijs_vast = 0"
    + "                      THEN r_r_art.res_rsv_artikel_prijs/r_r_art.res_rsv_artikel_aantal"
    + "                      ELSE r_art.res_artikel_prijs"
    + "                 END"
    + "               , 0"
    + "               ) prijs"
    + "     , r_r_art.res_status_BO_key"
    + "     , r_dp.res_disc_params_threshold"
    + "     , (SELECT SUM(res.getartikelprijs(rra1.res_rsv_artikel_key))"
    + "          FROM res_rsv_artikel rra1"
    + "             , res_artikel ra1"
    + "         WHERE rra1.res_artikel_key = ra1.res_artikel_key"
    + "           AND rra1.res_rsv_artikel_dirtlevel = 0"
    + "           AND rra1.res_rsv_ruimte_key = " + rsv_ruimte_key
    + "           AND ra1.res_discipline_key = r_disc.ins_discipline_key) sum_cat_artikelprijs"
    + "     , r_art.res_artikel_vrije_omschrijving"
    + "     , r_r_art.res_rsv_artikel_omschrijving"
    + "  FROM res_v_aanwezigdiscipline r_disc"
    + "     , res_v_aanwezigartikel r_art"
    + "     , res_v_aanwezigrsv_artikel r_r_art"
    + "     , res_v_aanwezigrsv_ruimte r_r_Rmt"
    + "     , res_disc_params r_dp"
    + " WHERE r_r_art.res_rsv_ruimte_key = r_r_Rmt.res_rsv_ruimte_key"
    + "   AND r_r_art.res_rsv_ruimte_key = " + rsv_ruimte_key
    + "   AND r_r_art.res_artikel_key = r_art.res_artikel_key"
    + "   AND r_art.res_discipline_key = r_disc.ins_discipline_key"
    + "   AND r_disc.ins_discipline_key = r_dp.res_ins_discipline_key"
    + " ORDER BY ins_discipline_volgnr, " + lcl.xsql('ins_discipline_omschrijving', 'r_disc.ins_discipline_key') + ", res_artikel_nr";

oRs = Oracle.Execute(sql);
var r_a_key = new Array();
while ( !oRs.eof )
{
  r_a_key[r_a_key.length] = new artinf(oRs("res_artikel_key").Value, oRs("res_rsv_artikel_key").Value, oRs("vast").Value,
                                       oRs("ins_discipline_key").Value, oRs("res_rsv_artikel_aantal").Value,
                                       oRs("hh").Value, oRs("mi").Value, oRs("eh").Value, oRs("em").Value,
                                       oRs("prijs").Value, oRs("res_status_BO_key").Value,
                                       oRs("res_artikel_vrije_omschrijving").Value, oRs("res_rsv_artikel_omschrijving").Value,
                                       oRs("res_disc_params_threshold").Value, oRs("sum_cat_artikelprijs").Value
                                      );
  oRs.MoveNext();
}
oRs.Close();

var cnt;

var CV_has_changed = false;
reserved = 0;
// delete and update are based on the list of previously reserved objects
for (var i = 0; i < r_a_key.length; i++)
{
  art_rsv_key = r_a_key[i].key + "_" + r_a_key[i].rsv_key;
  if (Request.Form("inpb_c_" + art_rsv_key).Count)
  { // De categorie was opgeklapt-->Hij was zichtbaar
    cnt = parseInt(Request.Form("inpb_c_" + art_rsv_key), 10);
    if (!isNaN(cnt) && cnt == 0)
    { // delete maar gewoon echt
      sql = "DELETE FROM res_rsv_artikel"
          + " WHERE res_rsv_ruimte_key=" + rsv_ruimte_key
          + "   AND res_artikel_key = " + r_a_key[i].key
          + "   AND res_rsv_artikel_key = " + r_a_key[i].rsv_key;
      Oracle.Execute(sql);
      // Tracking toevoegen.
      var deletetrack = res.trackartikel(r_a_key[i].key, "delete");
      trackarray.push(deletetrack);
      // Artikel is verwijderd.
      CV_has_changed = true;
      continue;
    }
    cur_price = Request.Form("inpbp_c_" + art_rsv_key);
    cur_price = (cur_price == "" ? "0.00" : cur_price);
    cur_price = parseFloat(String(cur_price).replace(/,/g,"."));
    if (!isNaN(cnt) && (this_res.canReturnCV || cnt > 0) && (!backo || !isNaN(cur_price)))
    { // update, negatieve aantallen en negatieve prijs mogelijk
      var cur_omsch = getFParam("inpbd_c_" + art_rsv_key, null);
      var cur_lev_h = parseInt(Request.Form("inpbh1_c_" + art_rsv_key), 10);
      var cur_lev_m = parseInt(Request.Form("inpbm1_c_" + art_rsv_key), 10);
      var cur_col_h = parseInt(Request.Form("inpeh1_c_" + art_rsv_key), 10);
      var cur_col_m = parseInt(Request.Form("inpem1_c_" + art_rsv_key), 10);
      if (isNaN(cur_lev_m) || isNaN(cur_lev_h))
      { // we do nothing when datetime is corrupted,
        continue; // should report about this error?
      }
      if (isNaN(cur_col_m) || isNaN(cur_col_h))
      { // clear collect time
        cur_col_m = null;
        cur_col_h = null;
      }
      // Never cyclic updates
      var jsDate        = (cur_lev_h != null ? new Date(year, month-1, day, cur_lev_h, cur_lev_m, 0) : null);
      var jsDateAfhalen = (cur_col_h != null ? new Date(year, month-1, day, cur_col_h, cur_col_m, 0) : null);
      must_warn = must_warn || is_expired(r_a_key[i].disc, jsDate);
      var bIsAfgemeld = (r_a_key[i].bo_status == 5 || r_a_key[i].bo_status == 6);
      var oudeAantal = r_a_key[i].quantity;
      var oudeOmschr = r_a_key[i].art_oms;
      var oudeLevH = r_a_key[i].lev_h;
      var oudeLevM = r_a_key[i].lev_m;
      var oudeColH = r_a_key[i].col_h;
      var oudeColM = r_a_key[i].col_m;
      var oudePrijs = r_a_key[i].price;
      var oudeDatum   = (oudeLevH ? new Date(year, month-1, day, oudeLevH, oudeLevM, 0) : null);
      var oudeAfhalen = (oudeColH ? new Date(year, month-1, day, oudeColH, oudeColM, 0) : null);
      // FO kan alleen aantal en tijd aanpassen
      sql = "BEGIN "
          + "UPDATE res_rsv_artikel"
          + " SET res_rsv_artikel_aantal = " + cnt
      // res_rsv_artikel_prijs moet bijgewerkt worden als
      // 1) bij wijzigbare vrije-prijs reserveringen. Alleen de BO kan dat.
      // 2) bij afgemelde reserveringen
      if (backo && r_a_key[i].pricefixed == 0)
      { // De backoffice had het bedrag in beeld dus kunnen we hier direct vermenigvuldigen.
        sql += ",res_rsv_artikel_prijs = " + parseFloat((cnt * cur_price).toFixed(2))
      }
      else if (oudeAantal != cnt)
      { // frontoffice en frontend hebben mogelijk het aantal aangepast. De prijs konden ze niet bij
        // mogelijk is res_rsv_artikel_prijs nog niet ingevuld, dat gaat hier wel goed (NULL==>NULL)
        sql += " ,res_rsv_artikel_prijs = res_rsv_artikel_prijs / " + oudeAantal + " * " + cnt;
      }
      if (r_a_key[i].free_art && (cur_omsch != oudeOmschr))
      {
        sql += ", res_rsv_artikel_omschrijving = " + safe.quoted_sql(cur_omsch);
      }
      sql +=", res_rsv_artikel_levering = " + (jsDate ? jsDate.toSQL(true) : "null")
          + ", res_rsv_artikel_afhalen = " + (jsDateAfhalen && jsDateAfhalen < res_tot && jsDateAfhalen >= res_van ? jsDateAfhalen.toSQL(true) : "null")
          + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key
          + " AND res_artikel_key = " + r_a_key[i].key
          + " AND res_rsv_artikel_key = " + r_a_key[i].rsv_key + ";"
          + art_dirty_sql
          + " END;";
      var err = Oracle.Execute(sql, true);
      if (err.friendlyMsg)
          abort_with_warning(err.friendlyMsg);

      var thisCV_changed = false;

      if (  (oudeAantal != cnt)
         || (oudePrijs != cur_price)
         || ((oudeOmschr != cur_omsch) && r_a_key[i].free_art)
         || (oudeDatum.getTime() != jsDate.getTime())
         || (oudeAfhalen && jsDateAfhalen && oudeAfhalen.getTime() != jsDateAfhalen.getTime())
         || (oudeAfhalen && !jsDateAfhalen)
         || (!oudeAfhalen && jsDateAfhalen)
         )
      { // Artikelregel is gewijzigd. Wijziging tracken.
        var updatetrack = res.trackartikel(r_a_key[i].key,
                                           "update",
                                           { old_number: oudeAantal,
                                             new_number: cnt,
                                             old_price: oudePrijs,
                                             new_price: cur_price,
                                             old_date: oudeDatum,
                                             new_date: jsDate,
                                             old_afhaal: oudeAfhalen,
                                             new_afhaal: jsDateAfhalen,
                                             old_omschr: oudeOmschr,
                                             new_omschr: cur_omsch
                                           });
        trackarray.push(updatetrack);
        thisCV_changed = true;
        CV_has_changed = true;
      }

      flextrack = saveFlexKenmerkenArt(r_a_key[i].rsv_key,
                                       { nameprefix: "art" + r_a_key[i].key + "_" + r_a_key[i].rsv_key + "_",
                                         isNew: false
                                       });  // The array r_a_key[i] is a list of previously reserved objects.
      if (flextrack.length > 0)
      {
        if (!thisCV_changed)
        { // Alsnog naam van artikel ophalen omdat er geen wijzigingen in het artikel zelf zijn.
          var sql = "SELECT " + lcl.xsql("ra.res_artikel_omschrijving", "ra.res_artikel_key") + " art_oms"
                  + "  FROM res_artikel ra"
                  + " WHERE ra.res_artikel_key = " + r_a_key[i].key;
          var oRs = Oracle.Execute(sql);
          trackarray.push(L("lcl_res_artupdate").format(oRs("art_oms").Value));
          oRs.Close();
        }
        trackarray = trackarray.concat(flextrack);
      }
      reserved++;
      continue;
    }
  }
}

// insert is based on the scanned list of currently reserved objects
var last_discipline = -1;
var curr_discipline = -1;
for (var i = 0; i < a_arIns.length; i++)
{ // Catalogus artikelen.
  for (var j = 0; j < r_a_key.length; j++ ) { // r_a_key bevat records die al in de database zitten
    if( r_a_key[j].rsv_key == a_arIns[i].res_rsv_deel ) {
      break; // Deze was al eerder gereserveerd en hebben we als het goed is al bijgewerkt
    }
  }
  if (j == r_a_key.length)
  { // Article was not reserved yet
    var jsDate = new Date(year,month-1,day,a_arIns[i].van_h, a_arIns[i].van_m);
    var jsDateAfhalen = new Date(year, month-1,day, a_arIns[i].tot_h, a_arIns[i].tot_m);

    sql = "SELECT ra.res_artikel_prijs_vast"
        + "     , ra.res_discipline_key"
        + "     , ra.res_artikel_vrije_omschrijving"
        + "     , rdp.res_disc_params_bonotify"
        + "     , rdp.res_disc_params_threshold"
        + "  FROM res_artikel ra"
        + "     , res_disc_params rdp"
        + " WHERE ra.res_discipline_key = rdp.res_ins_discipline_key"
        + "   AND ra.res_artikel_key = " + a_arIns[i].res_deel;
    oRs = Oracle.Execute(sql);
    var free_art = oRs("res_artikel_vrije_omschrijving").Value == 1;
    var prijs_vast = oRs("res_artikel_prijs_vast").Value == 1;
    var disc_key = oRs("res_discipline_key").Value;
    var bonotify = oRs("res_disc_params_bonotify").Value == 1;
    curr_discipline = disc_key;
    oRs.Close();

    if (!must_warn)
      must_warn = must_warn || is_expired(disc_key, jsDate);

    // Niets aan de hand, toevoegen
    var fields = [ { dbs: "res_artikel_key",          typ: "key",      val: a_arIns[i].res_deel },
                   { dbs: "res_rsv_artikel_aantal",   typ: "key",      val: a_arIns[i].cnt },
                   { dbs: "res_rsv_ruimte_key",       typ: "key",      val: rsv_ruimte_key },
                   { dbs: "res_rsv_artikel_levering", typ: "datetime", val: jsDate }
                 ];
    if (free_art)
    {
      fields.push( { dbs: "res_rsv_artikel_omschrijving", typ: "varchar", val: a_arIns[i].res_oms } );
    }
    if (jsDateAfhalen && jsDateAfhalen != res_tot && jsDateAfhalen > res_van)
    {
      fields.push( { dbs: "res_rsv_artikel_afhalen",  typ: "datetime", val: jsDateAfhalen } );
    }

    // Backo had mogelijk flexprijs in beeld. Sla die dan op
    // Prijs alleen aan te passen als geen vaste prijs (flexprijs)
    if (backo && !isNaN(a_arIns[i].price) && !prijs_vast) // De prijs was in beeld en wijzigbaar
    {
      fields.push({   dbs: "res_rsv_artikel_prijs",    typ: "float",    val: parseFloat((a_arIns[i].cnt * a_arIns[i].price).toFixed(2))});
    }
    //else // Fronto: of geen prijs in beeld (vaste prijs artikel)

    fields.push({     dbs: "res_rsv_artikel_key",      typ: "key",      seq: "res_s_res_rsv_artikel_key" });
    var resArtIns = buildInsert("res_rsv_artikel", fields);
    var rsv_artikel_key = resArtIns.sequences["res_rsv_artikel_key"];
    var sql_insArt = resArtIns.sql;
    var sql = "BEGIN "
            +   sql_insArt + "; "
            +   art_dirty_sql
            + "END;";
    var err = Oracle.Execute(sql, true);
    if (err.friendlyMsg)
        abort_with_warning(err.friendlyMsg);

    // Artikel is toegevoegd.
    CV_has_changed = true;

    if (bonotify && (curr_discipline != last_discipline))
    {
      sql = "BEGIN "
          + " RES.notifybackoffice(" + rsv_ruimte_key + ", 'RESNEW', 'C', " + a_arIns[i].res_deel + ");"
          + " END;";
      last_discipline = curr_discipline;
      Oracle.Execute(sql);
    }

    // Tracking toevoegen.
    var inserttrack = res.trackartikel(a_arIns[i].res_deel, "insert");
    trackarray.push(inserttrack);

    var res_rsv = (a_arIns[i].res_rsv_deel ? a_arIns[i].res_rsv_deel.replace("-", "_") : 1);
    flextrack = saveFlexKenmerkenArt(rsv_artikel_key,
                                     { nameprefix: "art" + a_arIns[i].res_deel + "_" + res_rsv + "_",
                                       isNew: true
                                     });  // rsv_artikel_key is a currently reserved article.
                // _saveFlexKenmerken() voegt geen tracking toe aan een nieuw artikel!
    trackarray = trackarray.concat(flextrack);
    reserved++;
  }
}

if (must_warn && urole != "bo")
{
  warning = L("lcl_res_obj_lastminute");
}

// ========================================
// Is er wel iets echts overgebleven bij CV
// ========================================
sql = "SELECT 1"
    + "  FROM res_rsv_deel"
    + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key
    + "   AND res_rsv_deel_verwijder IS NULL"
    + " UNION"
    + " SELECT 2"
    + "   FROM res_rsv_artikel"
    + "  WHERE res_rsv_ruimte_key = " + rsv_ruimte_key
    + "    AND res_rsv_artikel_verwijder IS NULL";
oRs = Oracle.Execute(sql);
var hasAnyCV = !oRs.Eof;
oRs.Close();

var approve = false;
if (hasAnyCV && CV_has_changed)
{ // Moet de gehele catering (opnieuw) gefiatteerd worden?
  // Fiatteren moet gebeuren als voor één catalogus/discipline de totaalprijs boven de drempelwaarde uitkomt.
  // Dan moet alles (opnieuw) gefiatteerd worden.
  // Aanpassingen indien er (opnieuw) gefiatteerd moet worden: De velden res_rsv_artikel_approved en res_rsv_ruimte_cat_appr opnieuw zetten.
  var sql_f = "SELECT ra1.res_discipline_key"
            + "     , rdp.res_disc_params_threshold"
            + "     , SUM(res.getartikelprijs(rra1.res_rsv_artikel_key)) sum_cat_artprijs"
            + "  FROM res_rsv_artikel rra1"
            + "     , res_artikel ra1"
            + "     , res_v_aanwezigdiscipline rd"
            + "     , res_disc_params rdp"
            + " WHERE rra1.res_artikel_key = ra1.res_artikel_key"
            + "   AND rra1.res_rsv_artikel_dirtlevel = 0"
            + "   AND rra1.res_rsv_ruimte_key = " + rsv_ruimte_key
            + "   AND ra1.res_discipline_key = rd.ins_discipline_key"
            + "   AND rd.ins_discipline_key = rdp.res_ins_discipline_key"
            + " GROUP BY ra1.res_discipline_key"
            + "        , rdp.res_disc_params_threshold";
  oRs_f = Oracle.Execute(sql_f);
  while (!oRs_f.Eof)
  {
    approve = approve || oRs_f("sum_cat_artprijs").Value > oRs_f("res_disc_params_threshold").Value;
    oRs_f.MoveNext();
  }
  oRs_f.Close();

  // De res_rsv_artikel_approved velden aanpassen.
  // Rsv artikel setting res_rsv_artikel_approved: De artikelregel is 0=Niet gefiatteerd, 1=Goedgekeurd, 2=Afgewezen.
  sql = "UPDATE res_rsv_artikel"
      + "   SET res_rsv_artikel_approved = " + (approve? 0 : 1)
      + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
  Oracle.Execute(sql);

  // Het res_rsv_ruimte_cat_appr veld aanpassen.
  // Er moet worden gefiatteerd als approve true is. Er zijn dan catalogussen met een totaal bestelbedrag die boven de drempelwaarde ligt.
  // Catalogus setting res_rsv_ruimte_cat_appr: Artikelregels wel of niet fiatteren {0=Nee of 1=Ja}.
  sql = "UPDATE res_rsv_ruimte"
      + "   SET res_rsv_ruimte_cat_appr = " + (approve? 1 : 0)
      + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
  Oracle.Execute(sql);
}

if (restype == "R")
{
  if (trackarray.length > 0)
    res.trackreserveringupdate(rsv_ruimte_key, trackarray.length ? L("lcl_res_is_resupdtrack").format(res_key + "/" + res_volgnr) + "\n" + trackarray.join("\n") : null);
}
else
{ // CV
  sql = "UPDATE res_rsv_ruimte"
      + "   SET res_rsv_ruimte_dirtlevel = " + (hasAnyCV? 0 : res.dirtlevel.ruimte.nocatering)
      + " WHERE res_rsv_ruimte_key = " + rsv_ruimte_key;
  Oracle.Execute(sql);
  if (hasAnyCV || CV_has_changed)
  {
    if (verynew == 1 && !this_res.min_bo) // misbruik this_res.min_bo: er was niets
      shared.trackaction("RESNEW", rsv_ruimte_key);
    else
      res.trackreserveringupdate(rsv_ruimte_key, trackarray.length ? L("lcl_res_is_resupdtrack").format(res_key + "/" + res_volgnr) + "\n" + trackarray.join("\n") : null);
  }
}

doneParams = { hasAnyCV: hasAnyCV,
               warning: warning,
               success: true,
               stillnew: verynew && restype != "R" && !hasAnyCV
             };

if (approve)
{
  var approver_key = res.getApprover(rsv_ruimte_key);

  if (approver_key > 0)
  {
    doneParams.warning = res.sendNeedApprovalNotification(rsv_ruimte_key, approver_key);
    var sql = "SELECT prs_perslid_naam_full FROM prs_v_perslid_fullnames_all WHERE prs_perslid_key = " + approver_key;
    var oRs = Oracle.Execute(sql);
    var prsname = oRs("prs_perslid_naam_full").Value;
    oRs.Close();
    shared.trackaction("RESFIA", rsv_ruimte_key, L("lcl_res_notification_sent_to").format("RESFIA", prsname));
  }
  else
  { // Geen notificatie mogelijk. Melding geven dat er geen budgethouder gevonden is en dat er niet gefiatteerd kan worden.
    // Kan zijn dat er geen kostenplaats is gekozen of dat er geen budgethouder bij de kostenplaats is gekozen.
    doneParams.warning = L("lcl_res_no_fiatteur");
  }
}

Response.Write(JSON.stringify(doneParams));
%>
<% ASPPAGE_END(); %>
