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

    File:               mld_edit_opdr_save.asp
    Description:        Sla de wijzigingen van het mld_edit_opdr formulier op
                        Merk op dat het voor zowel toevoegen als wijzigen gebruikt wordt.
    Parameters:
    Context:            submit van order_input.asp
                        submit van pda/order.asp
                        of $.post vanuit ./melding.asp en pda/melding.asp bij autoorder
    Note:               Eventueel maken we uiteindelijk een print-scherm aan!
                        Een opdracht wordt echt uitgegeven in 2 gevallen:
                         - nieuw en geen fiattering vereist
                         - bestaand en onlangs gefiatteerd (status==4)
*/
var JSON_Result = true;

var opdr_key = getQParamInt("opdr_key", -1);
var LOCKED_USER_OK = { "xmlnode": "opdracht", "key": opdr_key };
%>

<!-- #include file="../Shared/common.inc" -->
<!-- #include file="../Shared/kenmerk_common.inc" -->
<!-- #include file="../shared/save2db.inc" -->
<!-- #include file="../shared/kpl_ksrt_validate.inc" -->
<!-- #include file="../Shared/notes.inc" -->
<!-- #include file="mld.inc" -->
<%
var ismobile = getQParamInt("mobile", 0) == 1;
var isNew = (opdr_key < 0);

var StatusTV = false;
var opdr_status;
var uitvoerende = getFParamInt("uitvoerende", -1);
var old_uitvoerende = -1;
var old_behandelaar = -1;
var autoorder = isNew && getFParamInt("autoorder", 0) == 1;
var act_key = getFParamInt("act_key", -1); // fac_activiteit_key
var volgnr  = getFParamInt("volgnr", 1);
var opdr_ordernr = getFParam("opdr_ordernr", "");
var copyorder_key = getFParamInt("copyorder_key", -1);
var finish = (getFParamInt("finish", 0) == 1);
var shiftdate = (getFParamInt("shiftdate", 0) == 1);
var msg = getFParam("message", "");
var fromofferte = (getFParamInt("fromofferte", 0) == 1);
var vervolg = (getFParamInt("vervolg", 0) == 1);

// Objects...
var obj_arr = getFParamIntArray("r_objs", []);

if (!isNew)
{ // Bestaande opdracht
  var mld_opdr = mld.mld_opdr_info(opdr_key);
  var mld_key = mld_opdr.mld_key;
  var mld_info = mld.mld_melding_info(mld_key);

  var t_opdr = mld_opdr.opdr_type;
  var StatusTV = mld_opdr.opdr_status == 6;
  var opdr_status = mld_opdr.opdr_status;
  var old_uitvoerende = mld_opdr.uitvoerende_key;
  var old_behandelaar = mld_opdr.contactpers_key;
}
else
{
  var mld_key = getFParamInt("mld_key");
  var mld_info = mld.mld_melding_info(mld_key);

  if (autoorder)
  { // dan pakken we per definitie de default. We eisen dat die is ingevuld.
    sql = "SELECT mld_typeopdr_key"
        + "     , mld_stdmelding_autoorderamount"
        + "  FROM mld_stdmelding"
        + " WHERE mld_stdmelding_key = " + mld_info.stdm_key;
    oRs = Oracle.Execute(sql);
    var t_opdr = oRs("mld_typeopdr_key").Value;
    var autoorderamount = oRs("mld_stdmelding_autoorderamount").Value;
    oRs.Close();
    if (!t_opdr)
      // Beter dan error in de IIS log. Zonder de alert blijft het scherm te klein?
      shared.simpel_page(L("lcl_orders_no_ordertype"));
  }
  else
    var t_opdr  = getFParamInt("t_opdr");  // Kan deze wel wijzigen?
}

lcl.set_dialect(t_opdr, "mld_typeopdr_key");
mld.dialectIsSet = true;
var sql = "SELECT mld_typeopdr_slamode"
        + "     , mld_typeopdr_isofferte"
        + "     , mld_typeopdr_confirm_for_send"
        + "     , mld_typeopdr_fvs"
        + "     , mld_typeopdr_gvs"
        + "     , mld_typeopdr_kosten"
        + "     , mld_typeopdr_materiaal"
        + "  FROM mld_typeopdr"
        + " WHERE mld_typeopdr_key = " + t_opdr;
oRs = Oracle.Execute(sql);
var sla_mode = sla_mode = oRs("mld_typeopdr_slamode").Value;
var mld_typeopdr_isofferte = oRs("mld_typeopdr_isofferte").Value;
var mld_typeopdr_confirm_for_send = oRs("mld_typeopdr_confirm_for_send").Value;
var mld_typeopdr_gvs = oRs("mld_typeopdr_gvs").Value || 0;
var mld_typeopdr_fvs = oRs("mld_typeopdr_fvs").Value || 0;
var needKostenplaats = oRs("mld_typeopdr_kosten").Value == 1;
var typeopdr_materiaal = oRs("mld_typeopdr_materiaal").Value;
oRs.Close();

if (typeopdr_materiaal == 3)
{ // Materialen zijn verplicht. Dan ook testen dat ze zijn meegegeven.
  var matkeystrArr = getFParamIntArray("matkeystr", [], true);
  if (matkeystrArr.length == 0)
    abort_with_warning(L("lcl_mld_req_materials"));
}

if (isNew && uitvoerende < 0 && copyorder_key < 0)
{ // Voor automatische opdrachten controleren of uitvoerende bepaald kan worden (ook voor offertes, niet voor kopiëren).
  var locatiekey = mld_info.loc_key;
  if (locatiekey < 0 && mld_info.deladdress_key > 0)
  { // Als we geen locatiekey hebben maar wel een afleveradres, kunnen we mogelijk ook een locatiekey bepalen
    oRs = Oracle.Execute('SELECT alg_locatie_key FROM mld_adres WHERE mld_adres_key = ' + mld_info.deladdress_key);
    if (!oRs.eof && oRs("alg_locatie_key").Value)
      locatiekey = oRs("alg_locatie_key").Value;
    oRs.Close();
  }

  // Zijn er objecten aan de melding gekoppeld? Zet de ins_deel_keys in een array.
  var obj_key_arr = [];
  for (var i=0; i<mld_info.room_obj_arr.length; i++)
  {
    obj_key_arr.push(mld_info.room_obj_arr[i].ins_key);
  }
  uitvoerende = mld.dienst_controle(mld_info.stdm_key, locatiekey, (mld_info.bttype == "B"? mld_info.bld_key : null), mld_info.room_key, obj_key_arr, mld_info.perslid_key_voor, true);
}

if (isNew && copyorder_key < 0 && mld_typeopdr_isofferte == 1)
{ // Meerdere offerte opdrachten aanmaken
  var uitvkeyArray = getFParamIntArray("uitvkeystr", []);
  var uitvcpersArray = getFParamIntArray("uitvcpersstr", [-1]);
  var uitvcntArray = getFParamIntArray("uitvcntstr", [-1]);

  if (uitvkeyArray.length == 0)
    if (uitvoerende > 0)
      uitvkeyArray = [uitvoerende];
    else
      // Er zijn geen uitvoerende mee gegeven en er kon ook niet automatisch een uitvoerende bepaald worden.
      // Alert geven en terug. Beter dan error in de IIS log.
      shared.simpel_page(L("lcl_orders_no_contractor"));
}
else
{ // Het is een nieuwe opdracht die geen offerte is (geen multi uitvoerders) of het is een bestaande opdracht.
  // Alleen bij een nieuwe opdracht moet de uitvoerder bekend zijn. Bij een bestaande opdracht de uitvoerder dan niet opslaan (old_uitvoerende == uitvoerende == -1).
  if (isNew && uitvoerende < 0)
    // Er is geen uitvoerende mee gegeven en er kon ook niet automatisch een uitvoerende bepaald worden.
    // Alert geven en terug. Beter dan error in de IIS log.
    // Als het auto-order is dan ook terug maar zonder alert.
    shared.simpel_page( autoorder ? "" : L("lcl_orders_no_contractor"));
  var uitvkeyArray = [uitvoerende];
  var uitvcpersArray = [getFParamInt("contactpers", -1)];
  var uitvcntArray = [getFParamInt("contract", -1)];
}

var requestApproval = false; // Fiatteren of Goedkeuren.
var voluntaryApproval = getFParamInt("has_fiat", 0) == 1 && (Request.Form("fiat").count == 1); // Vrijwillig fiatteren.

var sql = "";

var authparamsORDBOF = user.checkAutorisation("WEB_ORDBOF", true);
var authparamsEXTORD = user.checkAutorisation("WEB_EXTORD", true);
var authparamsORDBO2 = user.checkAutorisation("WEB_ORDBO2", true);
var authparamsMLDORD = user.checkAutorisation("WEB_MLDORD", true);
var authparamsORDFIN = user.checkAutorisation("WEB_ORDFIN", true); // User mag gegevens aanpassen, zelfs als al TV (FSN#1791/FSN#11381) (mldfin)
var hasBOread = authparamsORDBOF || authparamsEXTORD || authparamsORDBO2 || authparamsMLDORD; // Als je write rechten hebt heb je zeker ook read rechten.

if (isNew)
{ // Nieuwe opdracht
  // Haal gegevens van de opdracht op van wat je weet voor een nieuwe opdracht.
  var params = { uitvoerende_key: uitvoerende,
                 act_key: act_key
               };
  var mld_opdr = mld.mld_opdr_info_new(mld_key, t_opdr, params);
}

// Zijn de totale kosten verplicht en ingevuld? Dit wordt ook gecontroleeed bij de invoer. Ook bij de save controleren.
// Opdrachttype - mld_typeopdr_kosten_verplicht (typeopdr kosten): 0 = Niet verplicht, 1 = Save + 2 = Afmelden, 4 = Afronden.
// Vakgroep - mld_disc_params_opdr_kosten (opdracht kosten):   0 = Niet verplicht, 7 = Aanmaak, 6 = Afmelden, 4 = Afronden.
var isRequired = mld_opdr.tonen_totaal &&
                 ((mld_opdr.typeopdr_kosten_verplicht & 1 || (mld_opdr.typeopdr_kosten_verplicht == 0 && mld_opdr.opdrkosten_verplicht & 1))
                 );
var tot_kosten = autoorder? autoorderamount : getFParamFloat("tot_kosten", null);

user.auth_required_or_abort((autoorder || authparamsORDBOF || authparamsEXTORD || authparamsORDBO2 || authparamsMLDORD) && !(isRequired && tot_kosten == null));

// (default) kostenplaats bepaling
var kostenplaats_key = getFParamInt("account", -1)
if (kostenplaats_key < 0 && autoorder)
{ // Kijk of kostenplaats bij de opdracht verplicht is (Kijken bij opdrachttype naar "Kosten meerekenen", mld_typeopdr_kosten == 1)
  if (needKostenplaats)
  {
    if (mld_info.kostenpl_key < 0)
      // Kostenplaats was bij de melding niet verplicht en niet ingevuld. Kostenplaats is bij opdracht verplicht
      // We kunnen de opdracht niet automatisch aanmaken.
      // Alert geven en terug. Beter dan error in de IIS log.
      shared.simpel_page(L("lcl_orders_no_account"));
    else
      kostenplaats_key = mld_info.kostenpl_key;
  }
}

var kp_fiat = true;
// Combinatievalidatie
if (kostenplaats_key > 0)
{
    if (!kpl_ksrt_validate(kostenplaats_key, mld_info.stdm_ksrt_key, mld_info.opdr_disc_key))
        abort_with_warning(L("lcl_mld_kpks_validatie"));

    // Per kostenplaats kan de fiattering in en uitgeschakeld worden (default is aan).
    var sql = "SELECT prs_kostenplaats_fiat"
            + "  FROM prs_kostenplaats k"
            + " WHERE prs_kostenplaats_key = " + kostenplaats_key;
    var oRs = Oracle.Execute(sql);
    kp_fiat = oRs("prs_kostenplaats_fiat").Value == 1;
    oRs.Close();
}

var uurloon = getFParamFloat("uurtarief", -1);

// Startdatum: indien niet meegegeven, neem dan sysdate of (autoorder:) melding datum
var startdate = getFParamDate("orderdate", (autoorder ? mld_info.melddatum : new Date));

var startwerkdag = parseFloat(S("fac_t_startofworkday"));
var startwerkdag_uur = Math.floor(startwerkdag);
var startwerkdag_min = (startwerkdag - Math.floor(startwerkdag)) * 60;
var startdatebegin = new Date(startdate.getFullYear(), startdate.getMonth(), startdate.getDate(), startwerkdag_uur, startwerkdag_min);
var sysdate = new Date();

var opdrkeyArray = [];
var initopdrkey = opdr_key;
var reapprove = false;
var refiat = false;
var for_approval = false;
var mldUpd = { trackarray: [] };
var orddat_tracking = "";
var ordpld_tracking = ""; // Voor elke uitvkeyArray[u] wordt de parameter met detzelfde inhoud gevuld. Array is niet nodig.
var ordpl2_tracking = ""; // Voor elke uitvkeyArray[u] wordt de parameter met detzelfde inhoud gevuld. Array is niet nodig.
var multiorder_key = -1;
for (var u = 0; u < uitvkeyArray.length; u++)
{
  if (isNew)
  { // nieuwe opdracht
    if (tot_kosten < mld_typeopdr_fvs)
    {
        // for_approval blijft false
    }
    // Persoonlijk Gebonden Budget (PGB) en/of Afdeling Gebonden Budget (AGB) controle
    // mld_disc_info.disc_params_pgb: 0 = Geen van beiden
    //                                1 = Alleen pgb
    //                                2 = Alleen agb
    //                                3 = Beiden, pgb en agb
    else if (mld_opdr.disc_params_pgb > 0)
    { // Persoonlijk Gebonden Budget (PGB) en Afdeling Gebonden Budget (AGB) controle
      // Fiatteringsproces moet gevolgd worden op de naam van de user
      var opdr_disc_key = mld_opdr.opdr_disc_key;
      var exceeds_pgb = mld.exceeds_pgb(tot_kosten, user_key, opdr_disc_key);
      var exceeds_agb = mld.exceeds_agb(tot_kosten, kostenplaats_key, opdr_disc_key, -1);   // Nieuwe opdracht: opdr_key = -1.
      if (exceeds_pgb || exceeds_agb)
      {
        // Voor melding opdrachten heb je altijd BO rechten nodig.
        // Een opdracht kan dus nooit van een FE komen. Aan de setting "mld_pgb_fe_fiat" (zoals bes_pgb_fe_fiat) heb je hier dan niets.
        if (S("mld_pgb_fiattering") == 1)
          // Algemene fiatterings proces noodzakelijk naast PGB fiatering.
          for_approval = true;
        else
        { // Fiatteringsproces moet gevolgd worden op de naam van de user.
          var strState = (exceeds_pgb
                         ? L("lcl_mld_exceeds_pgb") + safe.curr(mld.get_pgb_exceed_amount(tot_kosten, user_key, opdr_disc_key, opdr_key))
                         : "")
                       + (exceeds_pgb && exceeds_agb
                         ? "\n" // Nieuwe regel.
                         : "")
                       + (exceeds_agb
                         ? L("lcl_mld_exceeds_agb") + safe.curr(mld.get_agb_exceed_amount(tot_kosten, kostenplaats_key, opdr_disc_key, opdr_key))
                         : "")
                       + "\n" + L("lcl_mld_adjust_order");
          abort_with_warning(strState);
        }
      }

      // Als het boven mijn profiel is, of ik -als ik budgethouder ben/zou zijn- niet zelf mag goedkeuren
      // Gaan we kijken wie dan wel.
      // Kan ik zelf fiatteren?: true:  totaalbedrag <= can_selfapprove
      //                         false  totaalbedrag > can_selfapprove
      if (voluntaryApproval || // Vrijwillig. Wel even checken of er een fiatteur gevonden kan worden.
          for_approval ||
          (S("mld_use_order_approval") &&
           tot_kosten > 0 &&
           (tot_kosten > S("can_selfapprove"))) // Ik kan niet zelf fiatteren of kosten overschrijden het profiel
         )
      {
        // Wie moet op verzoek goedkeuren voor deze kostenplaats, terwijl het bedrag wellicht onbekend is?
        // Eerste fiatteur (pkey) mag niet fiatteren want het zit boven zijn profiel limiet.
        // Alleen de melder van de melding mag niet zelf goedkeuren boven het bedrag can_selfapprove.
        // Bij een kpg fiatteur moeten we de volgende hebben en niet de laatste, daarom (fiatbedrag >= 0) meegeven.
        approver_key = prs.getFiatteur(kostenplaats_key, -1, tot_kosten, opdr_disc_key, mld_opdr.fiatbedrag);
        for_approval = true;
      }
    }
    else
    { // Standaard fiatteringscontrole
      //
      // ========================== Fiatteringscontrole ==========================
      //

      var approver_key = -1;  // Die gaan we bepalen. Als het niet anders wordt, wordt er niet gefiatteerd
      // Welk bedrag eventueel te controleren?
      // Tegen wiens profiellimiet eventueel te controleren? Als de kostenplaats bij de opdracht
      // overruled is dan wordt altijd tegen het profiel van de BO-medewerker getest.
      var mld_opdr_params = {kp_key: kostenplaats_key,
                             opdr_key: opdr_key,
                             t_cost: tot_kosten,
                             fiatbedrag: 0, // Nieuwe opdracht.
                             opdr_handfiat: voluntaryApproval,
                             kp_fiat: kp_fiat? 1 : 0,
                             opdr_disc_key: mld_opdr.opdr_disc_key,
                             opdr_status: -1, // Nieuwe opdracht.
                             opdr_status_refiat: -1}; // Nieuwe opdracht.
      var approver = mld.getFirstApprover(mld_info, mld_opdr_params);
      approver_key = approver.approver_key;
      for_approval = approver.for_approval;
    }

    if (for_approval && kp_fiat)
    {
      if (approver_key == -1)
        abort_with_warning(L("lcl_bes_exceed_limit"));
      else
        requestApproval = true;
      // Er is nu een altijd een eerste approver_key gevonden (met of zonder voldoende fiatteringsrechten). Anders al een abort.
      // approver_key is oke of indien -1 dan is er geen approval meer nodig (is al geabort hierboven anders)
    }

    // Zetten van de status van de melding naar geaccepteerd en afhandelen van de tracking.
    mld.setmeldingstatus(mld_key, 4); // TODO: De trigger MLD_T_MLD_OPDR_B_IU controleert nog op MLD_STATUS?

    volgsql = "mld.bepaalopdrmeldingvolgnr(" + mld_key +")";

    // Als het uurloon is leeg gelaten, neem dan het uurtarief van de leverancier als deze is ingevuld.
    // Bij invoer gold: externonly = true. Dus alleen bedrijven en geen personen waren selecteerbaar bij "Bedrijf".
    if (uurloon < 0)
    {
      var sqlu = "SELECT COALESCE(prs_bedrijf_uurloon, 0) prs_bedrijf_uurloon"
               + "  FROM prs_bedrijf"
               + " WHERE prs_bedrijf_key = " + uitvkeyArray[u];
      var oRsu = Oracle.Execute(sqlu);
      var mld_opdr_uurloon = (!oRsu.eof ? oRsu("prs_bedrijf_uurloon").Value : 0);
      oRsu.Close();
    }
    else
      var mld_opdr_uurloon = uurloon;

    // Deze velden altijd opslaan
    var fields = [ { dbs: "mld_uitvoerende_keys",        typ: "key",     val: uitvkeyArray[u] },
                   { dbs: "prs_contactpersoon_key",      typ: "key",     val: uitvcpersArray[u] },
                   { dbs: "mld_opdr_materiaal",          typ: "float",   frm: "mat_kosten" },
                   { dbs: "mld_opdr_kosten",             typ: "float",   val: tot_kosten },
                   { dbs: "mld_opdr_uurloon",            typ: "float",   val: mld_opdr_uurloon > 0? mld_opdr_uurloon : "" },
                   { dbs: "mld_opdr_ordernr",            typ: "varchar", frm: "opdr_ordernr" },
                   { dbs: "prs_kostenplaats_key",        typ: "key",     val: kostenplaats_key },
                   { dbs: "prs_kostensoort_key",         typ: "key",     frm: "srtkosten" },
                   { dbs: "cnt_contract_key",            typ: "key",     val: uitvcntArray[u] },
                   { dbs: "mld_opdr_key",                typ: "key",     seq: "mld_s_mld_opdr_key" },
                   { dbs: "mld_melding_key",             typ: "key",     val: mld_key },
                   { dbs: "mld_opdr_module",             typ: "varchar", val: "MLD" },
                   { dbs: "mld_opdr_bedrijfopdr_volgnr", typ: "sql",     val: volgsql },
                   { dbs: "mld_opdr_plandatum",          typ: "datetime",frm: "date_plan" },
                   { dbs: "mld_opdr_plandatum2",         typ: "datetime",frm: "date_plan2" },
                   { dbs: "mld_opdr_id",                 typ: "varchar", frm: "opdr_id" },
                   { dbs: "mld_opdr_handfiat",           typ: "check",   val: (voluntaryApproval? 1 : 0) }
                 ];

    if (fromofferte)
    {
      fields.push({ dbs: "mld_opdr_parent_key",   typ: "key",   frm: "mld_opdr_parent_key", track: L("lcl_mld_opdr_parent") });
    }
    else if (vervolg)
    {
      fields.push({ dbs: "mld_opdr_parent_key",   typ: "key",   val: copyorder_key, track: L("lcl_mld_opdr_parent") });
    }

    // Begindatum nieuwe opdracht (FSN#27648 en WTCA#38303).
    // Begindatum is vandaag:
    // Ja)  begintijd is huidige tijd.
    // Nee) begintijd is 00:00.
    var startdate_is_today = (startdate.midnight().getTime() == sysdate.midnight().getTime());

    var bedrijf_uitvoertijd = mld.getbedrijfuitvoertijd(uitvoerende, mld_key); // vaak nodig
    var melding_uitvoertijd = mld.getmeldinguitvoertijd(mld_key);
    var opdracht_uitvoertijd;
    switch (sla_mode)
    {
      case 1: { opdracht_uitvoertijd = bedrijf_uitvoertijd;
                break;}
      case 2: { opdracht_uitvoertijd = melding_uitvoertijd;
                break;}
      //case 3: Waarde van opdracht_uitvoertijd blijft null.
    }
    if (startdate_is_today)
      startdate = sysdate;
    else
    { // Bepaal of starttijd in uren of in dagen is.
      // Bepaal de uitvoertijd van de uitvoerder. In uren als < 1 anders in dagen.
      // Als uitvoertijd 0 of null is dan in uren (einddatum opdracht wordt gelijk genomen aan einddatum melding).
      if (opdracht_uitvoertijd == null || opdracht_uitvoertijd.eenheid == 'U')
        var uitvoertijdInUren = false;
      else
        var uitvoertijdInUren = true;

      startdate = (uitvoertijdInUren ? startdatebegin : startdate.midnight());
    }
    fields.push({ dbs: "mld_opdr_datumbegin",         typ: "datetime", val: startdate });

    if (S("mld_opdracht_flags") > 0)
      fields.push({ dbs: "mld_opdr_flag", typ: "key" /* geen echte key, wel key-gedrag */, frm: "opdrflag" /* notrack */ });

    if (autoorder)
    {
      if (opdracht_uitvoertijd)
        var einddatum = mld.geteinddatumopdracht(mld_key, opdracht_uitvoertijd, startdate, sla_mode);
      else // Als opdracht_uitvoertijd null of 0 is. Dit is ook het geval voor sla-mode 3.
        var einddatum = mld_info.einddatum;

      var oms = mld_info.mld_onderwerp || "";
      oms += mld_info.mld_onderwerp && mld_info.mld_omschr? "\n" : "";
      oms += mld_info.mld_omschr || "";
      if (S("mld_ord_flexsummary") != 0)
          oms += mld.getFlexSummary(mld_key);
      fields.push({ dbs: "mld_opdr_einddatum",          typ: "datetime",val: einddatum });
      fields.push({ dbs: "mld_typeopdr_key",            typ: "key",     val: t_opdr });

      // Ben ik zelf een geldige behandelaar? Dan invullen, anders leeg laten
      mresult = user.func_enabled2("MLD", { ins_discipline_key: mld_info.disc_key });
      if (mresult.canWrite("WEB_MLDBOF") || mresult.canWrite("WEB_MLDBO3"))
          fields.push({ dbs: "prs_perslid_key",             typ: "key",     val: user_key });

      fields.push({ dbs: "mld_opdr_omschrijving",       typ: "varchar", val: oms });
      fields.push({ dbs: "mld_opdr_uren",               typ: "float",   val: 0 });
    }
    else
    {
      // Einddatum, aanmaak nieuwe opdracht (AALB#32981 en WTCA#38303).
      // 1) Uitvoertijd is in uren:  ingevoerde datum + tijd opslaan.
      // 2) Uitvoertijd is in dagen:
      //   Is ingevoerde datum gelijk aan einddatum melding?
      //   Ja)  Datum + tijd is einddatum melding.
      //   Nee) Tijd is einde werkdag (Setting fac_t_endofworkday).

      // Het veld einddatum was aanwezig.
      // Als het tijdsveld niet aanwezig is en de datum is aangepast/niet het einde van de melding, dan de eindtijd op einde van de werkdag zetten (fac_t_endofworkday).
      // Dan moet ik eerst weten of het tijdsveld aanwezig was.
      // Of het tijdsveld van de einddatum aanwezig was is afhankelijk van de uitvoertijd type van de melding.
      var new_einddatum = getFParamDate("date_done", null); // Nieuwe einddatum opdracht.
      if (mld_info.uitvoertijd_eenheid == "U")
      {
        // Als de einddatum later is dan de opdrachtdatum, trek dan de einddatum gelijk aan de opdrachtdatum (AADO#53823)
        if (startdate.getTime() > new_einddatum.getTime())
        {
            new_einddatum = startdate;
        }
        fields.push({ dbs: "mld_opdr_einddatum",  typ: "datetime", val: new_einddatum, track: L("lcl_opdr_enddate") });
      }
      else
      { // Het tijdsveld was niet aanwezig.
        // Nog even checken of de einddatum van de opdracht gelijk is aan de einddatum van de melding (zonder tijd).
        // Anders is de datum ONTERECHT te laat. Tijd einddatum opdracht (23:59) > tijd einddatum melding.
        var mld_einddatum = mld_info.einddatum; // Einddatum melding.
        if (new_einddatum.midnight().getTime() == mld_einddatum.midnight().getTime())
        {
          // Datum is gelijk aan einddatum melding. Maak de tijd (+ datum) gelijk aan de einddatum melding.
          // Tenzij; opdrachtdatum(tijd) > einddatum(tijd) melding, dan: Maak de tijd (+ datum) gelijk aan de opdrachtdatum(tijd) melding. (AADO#53823)
          if (startdate.getTime() > mld_einddatum.getTime())
          {
              new_einddatum = startdate;
          }
          else
          {
              new_einddatum = mld_einddatum;
          }
          fields.push({ dbs: "mld_opdr_einddatum", typ: "datetime", val: new_einddatum, track: L("lcl_opdr_enddate") });
        }
        else
        { // Zet de eindtijd van de nieuwe einddatum op einde werkdag (fac_t_endofworkday)
          // Dit gebeurt ook bij wijzigen.
          var endofworkday = parseFloat(S("fac_t_endofworkday"));
          new_einddatum.setHours(Math.floor(endofworkday), (endofworkday - Math.floor(endofworkday)) * 60);
          fields.push({ dbs: "mld_opdr_einddatum", typ: "datetime", val: new_einddatum, track: L("lcl_opdr_enddate") });
        }
      }
      fields.push({ dbs: "mld_typeopdr_key",            typ: "key",      frm: "t_opdr" });
      fields.push({ dbs: "prs_perslid_key",             typ: "key",      frm: "behandelaar" });
      fields.push({ dbs: "mld_opdr_omschrijving",       typ: "varchar",  frm: "opdr_omschr" });
      fields.push({ dbs: "mld_opdr_opmerking",          typ: "varchar",  frm: "opdr_opm" });
      fields.push({ dbs: "mld_opdr_uren",               typ: "float",    frm: "uren" });
    }

    if (act_key > 0)
      fields.push({ dbs: "fac_activiteit_key", typ: "key",  val: act_key });

    var mldIns = buildInsert("mld_opdr", fields);
    var opdr_key = mldIns.sequences["mld_opdr_key"];
    opdrkeyArray[opdrkeyArray.length] = opdr_key;
    sql = mldIns.sql;
    var err = Oracle.Execute(sql, true);
    if (err.friendlyMsg)
      abort_with_warning(err.friendlyMsg);

    var status = 5; // (Uitgegeven)
    if (
      S("mld_pessismistisch_hergoedkeuren") == 1
        ? tot_kosten > mld_info.bestellimiet || tot_kosten > mld_typeopdr_gvs
        : tot_kosten > mld_info.bestellimiet && tot_kosten > mld_typeopdr_gvs
    ) {
      status = 10; // Ter goedkeuring.
      requestApproval = true;
    }
    else if (requestApproval)
    {
      if (tot_kosten < mld_typeopdr_fvs) // fvs is eigenlijk al eerder afgehandeld
        requestApproval = false; // Hoge fiat vrijstelling, toch niet nodig
      else
        status = 3; // Ter fiattering.
    }
    // requestApproval heeft nu zijn definitieve waarde, en approver_key is geldig gedefinieerd.

    // Status en tracking altijd met de functie setopdrachtstatus
    // Zetten van de status van een nieuwe opdracht op nieuw en afhandelen van de tracking (status + uitvoerende tracking) en daarmee notificatie.
    mld.setopdrachtstatus(opdr_key, status, uitvkeyArray[u]);

    // Zet de initiele actie(code) bij de Uitvoerder
    if (status === 5) {
        Oracle.Execute("UPDATE mld_opdr SET mld_opdr_actiecode = 4 WHERE mld_opdr_key = " + opdr_key);
    }

    // Refresh meldingstatus zonodig als gevolg van een nieuw uitgegeven opdracht
    mld.updatemeldingstatus(mld_key, 0);

    if (u == 0 && !autoorder)
    {
      var uploadkey_arrM = [];   // key number (k1, k2, ...etc) for uploads
      var uploadkey_arrF = [];   // key number for sigle file uploads
      var index;                // flexparameters index
      var up_index;             // uploadkenmerk index
      // 1) Get flex-parameters (if any) which contains files to be copied
      // in case a flexparameter with upload files contains a k[index]file0 (k1file0/k2file0...etc) parameter
      // Alleen 'M', F en E zijn max 1 file
      for (index=1; index<=getFParamInt("k_all",0); index++)
      {
        kmtype = getFParam("k" + index + "t", "");
        switch (kmtype)
        {
            case "M":   if (getFParam("k" + index + "file0", "") != "")
                        {
                            uploadkey_arrM.push("k" + index);
                        }
                        break;
            case "F":
            case "E":   if (getFParam("k" + index + "val", "off") == "on")
                        {
                            uploadkey_arrF.push("k" + index);
                        }
                        break;
        }
      }

      // 2) retrieve filenames to be copied if applicable (uploadkey_arrM contains an upload kenmerk parameter)
      //    (files with has_k[x]val[y] (has_k0val0, has_k0val1, ...etc) with value=1 will be copied)
      if (uploadkey_arrM.length)
      {
        multiorder_key = (copyorder_key > 0 ? copyorder_key : multiorder_key);
        for (up_index = 0; up_index < uploadkey_arrM.length; up_index++)
        {
          var filter_arr = [];      // array of files to be copied
          index = 0;
          while (getFParam(uploadkey_arrM[up_index] + "file" + index, "") != "")
          {
            if (getFParamInt("has_" + uploadkey_arrM[up_index] + "val" + index, 0) == 1)
            {
              filter_arr.push(getFParam(uploadkey_arrM[up_index] + "file" + index));
            }
            index++;
          }
          // copy selected files to new opdracht upload folder
          if (filter_arr.length)
          {
            var k1key = getFParamInt(uploadkey_arrM[up_index] + "key");                 // kenmerk_key van opdracht
            var k1other_key = getFParamInt(uploadkey_arrM[up_index] + "other_key", -1); // kenmerk_key van melding als k1key > 0, anders van opdracht
            var kenmerk_key = (u == 0 && k1other_key > 0 ? k1other_key : k1key);
            copyUploadFiles(u, opdr_key, multiorder_key, t_opdr, kenmerk_key, filter_arr);
          }
        }
      }

      //
      // 3) in case a flexparameter with files of type F or E: contains a k[index]val and k[index]t parameter
      if (uploadkey_arrF.length)
      {
        multiorder_key = (copyorder_key > 0 ? copyorder_key : multiorder_key);
        for (up_index = 0; up_index < uploadkey_arrF.length; up_index++)
        {
          var k1key = getFParamInt(uploadkey_arrF[up_index] + "key");                 // kenmerk_key van opdracht
          var k1other_key = getFParamInt(uploadkey_arrF[up_index] + "other_key", -1); // kenmerk_key van melding als k1key > 0, anders van opdracht
          var kenmerk_key = (u == 0 && k1other_key > 0 ? k1other_key : k1key);
          copyUploadFiles(u, opdr_key, multiorder_key, t_opdr, kenmerk_key, []); // Geen filter nodig, deze mapt 1 op 1
        }
      }

      multiorder_key = opdr_key; // Bij aanmaken meerdere opdrachten hebben de bijlagen dezelfde verwijzing als de eerste opdracht.
    }
    else
    {
      // copy complete upload folder (if any) in case upload files couldn't be selected
      copyUploadFiles(u, opdr_key, multiorder_key, t_opdr);
    }


    function copyUploadFiles(u, opdr_key, multiorder_key, t_opdr, other_key, filter_arr)
    {
      // Nu de diverse flexfiles van de melding naar de opdracht kopieren
      // voor zover het kenmerksoort overeenstemt
      var first_opdr_key = (u==0 ? opdr_key : 0);
      if (multiorder_key > 0 || u > 0) // kopieren bijlagen van de originele opdracht of eerste offerte
      {
        var copy_from = (multiorder_key > 0 ? multiorder_key : first_opdr_key);
        __Log("Copy flexfiles from order: " + copy_from);
        var flexsql = "SELECT k_old.mld_kenmerk_key k_old"
                    + "     , k_new.mld_kenmerk_key k_new"
                    + "  FROM mld_srtkenmerk t"
                    + "     , mld_kenmerk k_old"
                    + "     , mld_kenmerk k_new"
                    + "     , mld_opdr o_old"
                    + "     , mld_typeopdr s_new"
                    + " WHERE o_old.mld_opdr_key = " + copy_from
                    + "   AND s_new.mld_typeopdr_key = " + t_opdr
                    + "   AND t.mld_srtkenmerk_kenmerktype IN ('F', 'E', 'M')"
                    + "   AND (k_old.mld_typeopdr_key = o_old.mld_typeopdr_key OR k_old.mld_typeopdr_key IS NULL)"
                    + "   AND k_old.mld_kenmerk_niveau IN ('P', 'O')"
                    + "   AND (k_new.mld_typeopdr_key = s_new.mld_typeopdr_key OR k_new.mld_typeopdr_key IS NULL)"
                    + "   AND k_new.mld_kenmerk_niveau IN ('P', 'O')"
                    + "   AND k_old.mld_kenmerk_verwijder IS NULL"
                    + "   AND k_new.mld_kenmerk_verwijder IS NULL"
                    + "   AND k_old.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                    + "   AND k_new.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                    + "   AND t.mld_srtkenmerk_verwijder IS NULL"
                    + "   AND k_old.mld_kenmerk_groep = k_new.mld_kenmerk_groep"
                    + "   AND k_old.mld_kenmerk_niveau = k_new.mld_kenmerk_niveau"
                    + ((other_key && other_key > -1) ? "   AND k_old.mld_kenmerk_key = " + other_key : "");

        if (other_key > 0)
          CopyFlexFiles("MLD", "O", copy_from, "O", opdr_key, flexsql, filter_arr);
        else
          CopyFlexFolders("MLD", "O", copy_from, "O", opdr_key, flexsql);
      }
      else // kopieren van de bijbehorende melding
      {
        __Log("Copy flexfiles from issue: "+ mld_info.mld_key);
        var flexsql = "SELECT k_old.mld_kenmerk_key k_old"
                    + "     , k_new.mld_kenmerk_key k_new"
                    + "  FROM mld_srtkenmerk t"
                    + "     , mld_kenmerk k_old"
                    + "     , mld_kenmerk k_new"
                    + "     , mld_stdmelding s_old"
                    + "     , mld_typeopdr s_new"
                    + "     , mld_discipline d_old"
                    + " WHERE s_old.mld_stdmelding_key = " + mld_info.stdm_key
                    + "   AND (s_new.mld_typeopdr_key = " + t_opdr + " OR s_new.mld_typeopdr_key IS NULL)"
                    + "   AND s_old.mld_ins_discipline_key = d_old.ins_discipline_key"
                    + "   AND t.mld_srtkenmerk_kenmerktype IN ('F', 'E', 'M')"
                    + (other_key
                      ? " AND k_old.mld_kenmerk_key = " + other_key
                      : "")
                    + "   AND ((k_old.mld_stdmelding_key IS NULL"
                    + "         AND k_old.mld_kenmerk_niveau = 'A')"
                    + "     OR (k_old.mld_stdmelding_key = s_old.mld_stdmelding_key"
                    + "         AND k_old.mld_kenmerk_niveau = 'S')"
                    + "     OR (k_old.mld_stdmelding_key = s_old.mld_ins_discipline_key"
                    + "         AND k_old.mld_kenmerk_niveau = 'D')"
                    + "     OR (k_old.mld_stdmelding_key = d_old.ins_srtdiscipline_key "
                    + "         AND k_old.mld_kenmerk_niveau = 'T'))"
                    + "   AND k_new.mld_typeopdr_key = s_new.mld_typeopdr_key(+)"
                    + "   AND k_new.mld_kenmerk_niveau IN ('P', 'O')"
                    + "   AND k_old.mld_kenmerk_verwijder IS NULL"
                    + "   AND k_new.mld_kenmerk_verwijder IS NULL"
                    + "   AND k_old.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                    + "   AND k_new.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                    + "   AND t.mld_srtkenmerk_verwijder IS NULL"
                    + "   AND k_old.mld_kenmerk_groep = k_new.mld_kenmerk_groep"
                    + "   AND COALESCE(k_old.mld_kenmerk_omschrijving, t.mld_srtkenmerk_omschrijving) = "
                    + "       COALESCE(k_new.mld_kenmerk_omschrijving, t.mld_srtkenmerk_omschrijving)";
        // copy individual checked uploads or copy complete folder
        if (other_key > 0)
          CopyFlexFiles("MLD", "M", mld_key, "O", opdr_key, flexsql, filter_arr);
        else
          CopyFlexFolders("MLD", "M", mld_key, "O", opdr_key, flexsql);
      }
    } // einde function copyUploadFiles()


  }
  else
  { // Bestaande opdracht
    // Hardness controle schrijfrechten
    var this_opdr = mld.func_enabled_opdracht(opdr_key); // wat mag ik zoal op deze opdracht
    user.auth_required_or_abort(finish? this_opdr.canFinish : this_opdr.canChange);

    // Bij het klaar melden wordt ook al in function opdr_edit_finish() van bestand mld_edit_opdr.js gecontroleerd. Dan zal je hier niet komen.
    if (this_opdr.iamBedrijfContact && this_opdr.canUrenMatChange && (tot_kosten > S("mld_max_kosten_extern")))
    {
      abort_with_warning(L("lcl_opdr_cost_exceed") + S("mld_max_kosten_extern"));
    }

    // Oorspronkelijke gegevens opdracht ophalen.
    var opdr_info = this_opdr.mld_opdr;
    // Functie die bepaald of de opdracht ter herfiattering aangeboden moet worden.
    function getToReaApprove()
    {
      var pkey = -1;
      var toapprove = false;
      if (tot_kosten > opdr_info.t_cost && tot_kosten > 0 && tot_kosten > mld_typeopdr_fvs)
      { // De totale kosten zijn verhoogd en het bedrag zit boven de FVS-grens.
        var wasSelfapproved = false;
        // Bepaling oorspronkelijke (laatste) fiatteur, als die er was.
        sql = "SELECT pf.prs_perslid_key"
            + "  FROM fac_tracking tr"
            + "     , fac_srtnotificatie str"
            + "     , prs_v_perslid_fullnames_all pf"
            + " WHERE tr.fac_srtnotificatie_key = str.fac_srtnotificatie_key"
            + "   AND tr.prs_perslid_key = pf.prs_perslid_key(+)"
            + "   AND (str.fac_srtnotificatie_code = 'ORDFOK'"
            + "    AND tr.fac_tracking_refkey = " + opdr_key
            + "    AND str.fac_srtnotificatie_xmlnode IN ('opdracht'))"
            + " ORDER BY fac_tracking_datum DESC"
            + "        , fac_tracking_key DESC"
        var oRs = Oracle.Execute(sql);
        if (!oRs.eof)
        {
          pkey = oRs("prs_perslid_key").Value;
        }
        else
        { // Dan waren de opdracht kosten onder het mandaat van mijzelf of de BO-medewerker.
          // Als de kostenplaats bij de opdracht overruled is dan wordt altijd tegen het profiel van de BO-medewerker getest.
          if (mld_info.mld_kk == 1 && mld_info.kostenpl_key == kostenplaats_key && S("mld_opdr_ignore_caller_profile") == 0)
            pkey = mld_info.melder_key;  // Melder van de melding.
          else
          { // Terugzoeken wie *oorspronkelijk* de opdrachtaanmaker was (De user op dat moment).
            var sql = "SELECT fac.gettrackinguserkey('ORDNEW', " + opdr_key + ") opdrachtaanmaker FROM DUAL";
            var oRs = Oracle.Execute(sql);
            pkey = oRs("opdrachtaanmaker").Value || opdr_info.contactpers_key; // De tracker van de ORDNEW tracking of anders de user bij aanmaak van de opdracht (die nog gefiatteerd moet worden).
            oRs.Close();
          }
          wasSelfapproved = true;
        }

        // Het gaat hier om te bepalen of er geherfiatteerd moet worden (opdrachtkosten zijn verhoogd).
        // a) Selfapprove was WEL  van toepassing: Er moet gechecked worden of ik nu weer zelf mag fiatteren.
        // b) Selfapprove was NIET van toepassing: De kp-verantwoordlijke of kpg-verantwoordelijke heeft als laatst gefiatteerd.
        //                                         Kijk of het nieuwe bedrag binnen zijn limiet is gebleven.
        //                                         Er moet dan niet naar de (tot_kosten > S("can_selfapprove")) gekeken worden,
        //                                         want die zal altijd waar zijn.
        if (S("mld_use_order_approval") &&
            ((tot_kosten > opdr_info.fiatbedrag &&  // Als de totale kosten nog steeds onder het reeds gefiatteerde bedrag blijven, hoeven we niet te herfiatteren.
              ((wasSelfapproved && tot_kosten > S("can_selfapprove")) ||
               (S("mld_opdr_reapproval_rate") == -1 && budget.exceeds_profiel(tot_kosten, pkey, mld_opdr.opdr_disc_key))
              )) ||
             (S("mld_opdr_reapproval_rate") >= 0 && tot_kosten > (opdr_info.t_cost * (1 + S("mld_opdr_reapproval_rate") / 100))) // Als de totale kosten een bepaald percentage toeneemt, moet geherfiatteerd worden.
            )
           )
        { // Het bedrag zit boven de profiel limiet van de eerste fiatteur.
          // Er moet (opnieuw) gefiatteerd worden. Is er wel iemand met voldoende mandaat? Indien er niemand gevonden kan worden mag de wijziging niet doorgaan.
          // Het mandaat is al te hoog voor de melder/aanvrager van de melding.
          // Laatste fiatteur of eerste fiatteur (pkey) mag niet fiatteren want het zit boven zijn profiel limiet.
          // De oorspronkelijke (laatste) fiatteur of melder van de melding of opdracht aanmaker/user van de melding (pkey)
          // mag niet zelf goedkeuren boven het bedrag can_selfapprove.
          // totaalbedrag > can_selfapprove => prs_key als exclude key meegeven.
          // Bij een kpg fiatteur moeten we de volgende hebben en niet de laatste, daarom (fiatbedrag >= 0) meegeven.
          approver_key = prs.getFiatteur(kostenplaats_key, (tot_kosten > S("can_selfapprove"))?pkey:-1, tot_kosten, mld_opdr.opdr_disc_key, mld_opdr.fiatbedrag);
          if (kp_fiat)
          {
            if (approver_key == -1)
              abort_with_warning(L("lcl_bes_exceed_limit"));
            else
              toapprove = true;
          }
          else
          { // Volgens de kostenplaats hoeft niet gefiatteerd te worden. Dit wel even tracken.
            if (approver_key == -1)
              var prsname = L("lcl_prs_budgethouder");
            else
            {
              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();
            }
            var mldtrackarray = [];
            if (approver_key == -1)
              mldtrackarray.push(L("lcl_bes_exceed_limit"));
            mldtrackarray.push(L("lcl_mld_no_approval_kp").format(prsname));
            mld.trackopdrachtupdate(opdr_key, mldtrackarray.join("\n"));
          }
        }
      }
      return toapprove;
    }

    opdrkeyArray[opdrkeyArray.length] = opdr_key;

    if (!StatusTV || authparamsORDFIN || this_opdr.canFinish || this_opdr.canChange)
    {
      // PF: Mag ik dit een vage conditie vinden? Beter lijkt me de verschillende .canXXX specifiek te matchen?

      // Let op: de essentie van deze code staat ook in opdr_goedkeur.asp
      // omdat hier gewerkt wordt op basis van de Form-params en daar op basis van een bestaand
      // record was de code lastig samen te voegen.

      // Hergoedkeuring (Ter goedkeuring) wordt (pas) vereist als het opdrachtbedrag hoger is dan al is goedgekeurd en boven de GVS-grens uitkomt.
      // Herfiattering (Ter fiattering) wordt (pas) vereist indien het bedrag boven het mandaat van de oorspronkelijke (laatste) fiatteur uitkomt
      // en boven de FVS-grens uitkomt.
      // Hergoedkeuring en herfiattering kan alleen als het totaalbedrag van de opdracht is verhoogd en alleen in de statussen 5, 8, 6 en 9 (de uitvoeringsfase en de afhandelingsfase).
      // Hergoedkeuring en herfiattering kan leiden tot een tijdelijke terugkeer naar de voorfase status 3 of 10.
      if (tot_kosten > opdr_info.t_cost &&
         (opdr_status == 5 || opdr_status == 6 || opdr_status == 8 || opdr_status == 9))
      { // Hergoedkeuring (Ter goedkeuring) wordt (pas) vereist als het opdrachtbedrag hoger is dan al is goedgekeurd en boven de GVS-grens uitkomt.
        reapprove = (tot_kosten > opdr_info.approved && tot_kosten > mld_typeopdr_gvs);

        // Herfiattering (Ter fiattering) wordt (pas) vereist indien het bedrag boven het mandaat van de oorspronkelijke (laatste) fiatteur uitkomt
        // en boven de FVS-grens uitkomt.
        // Bepaal het mandaat van de oorspronkelijke (laatste) fiatteur.
        // Ook als er de vorige keer geen kosten waren dan moet pas worden geherfiatteerd als (tot_kosten > mandaat),
        // waarbij de mandaat van de Melder/aanvrager van melding of de user is.
        var refiat = getToReaApprove();
        requestApproval = reapprove || refiat;
      }
      var fields = [ { dbs: "prs_contactpersoon_key", typ: "key",     frm: "contactpers" , track: L("lcl_contact_pers"),        foreign: "prs_contactpersoon"  },
                     { dbs: "mld_typeopdr_key",       typ: "key",     frm: "t_opdr",       track: L("lcl_mld_typeopdr"),        foreign: "mld_typeopdr" },
                     { dbs: "prs_perslid_key",        typ: "key",     frm: "behandelaar",  track: L("lcl_mld_behandelaar"),     foreign: "prs_perslid"},
                     { dbs: "mld_opdr_omschrijving",  typ: "varchar", frm: "opdr_omschr",  track: L("lcl_mld_opdr_description") },
                     { dbs: "mld_opdr_opmerking",     typ: "varchar", frm: "opdr_opm",     track: L("lcl_mld_inf_Opmerking") },
                     { dbs: "mld_opdr_uren",          typ: "float",   frm: "uren",         track: L("lcl_mld_hours") },
                     { dbs: "mld_opdr_uurloon",       typ: "float",   frm: "uurtarief",    track: L("lcl_hour_rate")  + " " + L("lcl_valutasign") },
                     { dbs: "mld_opdr_materiaal",     typ: "float",   frm: "mat_kosten",   track: L("lcl_mater_cost") + " " + L("lcl_valutasign") },
                     { dbs: "mld_opdr_kosten",        typ: "float",   frm: "tot_kosten",   track: L("lcl_total_cost") + " " + L("lcl_valutasign") },
                     { dbs: "mld_opdr_ordernr",       typ: "varchar", frm: "opdr_ordernr", track: L("lcl_mld_inf_Ordernr") },
                     { dbs: "cnt_contract_key",       typ: "key",     frm: "contract",     track: L("lcl_contract"),            foreign: "cnt_contract"  },
                     { dbs: "mld_opdr_id",            typ: "varchar", frm: "opdr_id",      track: L("lcl_opdr_id") },
                     { dbs: "prs_kostensoort_key",    typ: "key",     frm: "srtkosten",    track: L("lcl_mld_opdr_alt_charge_type"), foreign: "prs_kostensoort" }
                   ];
      // Opdracht direct electronisch versturen of door putorders laten oppikken (te verzenden veld zetten) bij
      // wijzigen opdrachten als de uitvoerder aangepast is en
      // in de statussen Ter Goedkeurnig(10), Ter Fiattering(3), Gefiatteerd(4), Uitgegeven(5), Geaccepteerd(8).

      // Quicknote
      var note = getFParam("note", "");
      if (note) {
        user.auth_required_or_abort(this_opdr.canEditOpdrNote);

        var fac_note_group_key = getFParamInt("fac_note_group_key", null);
        if (fac_note_group_key == -1)
        {
          fac_note_group_key = null;
        }

        var this_opdr = mld.func_enabled_opdracht(opdr_key); // wat mag ik zoal op deze opdracht

        var saved_note_key = notes.note_save( opdr_key,
                                      { note: note,
                                        note_key: -1,
                                        module: "MLD",
                                        fac_note_group_key: fac_note_group_key,
                                        flag: ((mld_info.fenotes == 1 || mld_info.fenotes == 3) ? 1 : 0),
                                        table: "mld_opdr"
                                      });
      }

      var checkTeVerzenden = old_uitvoerende != uitvoerende &&
                             (opdr_status == 10 || opdr_status == 3 || opdr_status == 4 || opdr_status == 5 || opdr_status == 8);
      if (checkTeVerzenden)
      {
        fields.push({ dbs: "mld_opdr_teverzenden",   typ: "check",   val: "" });
      }

      if (S("mld_opdracht_flags") > 0)
        fields.push({ dbs: "mld_opdr_flag", typ: "key" /* geen echte key, wel key-gedrag */, frm: "opdrflag" /* notrack */ });

      if (authparamsORDBOF)
      {
        fields.push({ dbs: "mld_uitvoerende_keys", typ: "key", frm: "uitvoerende", track: L("lcl_ord_company_uit"), foreign: "mld_uitvoerende", savewhen: "uitvoerende" });
      }

      if (this_opdr.canKostChange && S("mld_ord_show_kostenplaats") != 2) // Bij bestaande melding kostenplaats opslaan als kostenplaats wijzigbaar en zichtbaar was
        fields.push({ dbs: "prs_kostenplaats_key", typ: "key", frm: "account", track: L("lcl_mld_inf_Kostenplaats"), foreign: "prs_kostenplaats" });
      if (!StatusTV) // Alleen als status geen TV is zijn de kalenders niet readonly. authparamsORDFIN en kostentechnisch afmelden (ORDAFR) hebben geen invloed.
      {
        var ddfrom = getFParamDate("orderdate", null);
        var ddto = getFParamDate("date_done", null);
        if (ddfrom && ddto) // (zijn ze soms toch nog readonly?)
        {
          var ddsql = "SELECT MIN (mld_opdr_uren_datum) ddmin, MAX (mld_opdr_uren_datum) ddmax"
                    + "  FROM mld_opdr_uren"
                    + " WHERE mld_opdr_key = " + opdr_key;
          var oRs = Oracle.Execute(ddsql);
          if (oRs("ddmin").Value != null && oRs("ddmax").Value != null)
          {
            var ddmin = new Date(oRs("ddmin").Value);
            var ddmax = new Date(oRs("ddmax").Value);

            // Vallen de geschreven uren (ddmin...ddmax) BINNEN DE WEEK van de looptijd?
            // Daarom ddfrom (begindatum) naar begin van de week (maandag) en ddto (einddatum) naar eind van de week (zondag) verschuiven.
            var ddfrom_day = new Date(ddfrom).getDay();
            if (ddfrom_day == 0) // Zondag.
              ddfrom = new Date(ddfrom - 6);
            else // Maandag(1)...Zaterdag(5).
              //ddfrom = new Date(ddfrom - (ddfrom_day - 1));
              ddfrom.setDate(ddfrom.getDate() - (ddfrom_day - 1));
             var ddto_day = new Date(ddto).getDay();
            if (ddto_day > 0) // Maandag(1)...Zaterdag(5).
              //ddto = new Date(ddto + (7 - ddto_day));
              ddto.setDate(ddto.getDate() + (7 - ddto_day));

            if (ddmin < ddfrom.midnight() || ddmax > ddto.midnight())
              abort_with_warning(L("lcl_mlduren_booked_from") + toDateString(ddmin) + L("lcl_mlduren_booked_until") + toDateString(ddmax));
          }
        }

        // Begindatum bestaande opdracht (FSN#27648 en WTCA#38303).
        // Begindatum is aangepast?
        // Nee) Begindatum niet opslaan.
        // Ja)  Is begindatum gelijk aan de registratiedatum van de opdracht?
        //      Ja)  Datum + tijd is registratiedatum opdracht.
        //      Nee) Tijd is 00:00.
        sql = "SELECT mld_opdr_datumbegin"
            + "  FROM mld_opdr"
            + " WHERE mld_opdr_key = " + opdr_key;
        oRs = Oracle.Execute(sql);
        var oldstartdate = new Date(oRs("mld_opdr_datumbegin").Value);

        // Alleen als datum veranderd is moet de starttijd worden aangepast.
        if (startdate.midnight().getTime() != oldstartdate.midnight().getTime())
        { // Startdatum is aangepast.
          // Als de startdatum is aangepast in registratiedatum, dan wel weer de registratietijd (registratiedatum) pakken.
          // In alle andere gevallen begin van de werkdag pakken.
          sql = "SELECT fac.gettrackingdate('ORDNEW', " + opdr_key + ") registratiedatum FROM DUAL"
          oRs = Oracle.Execute(sql);
          var registratiedatum = new Date(oRs("registratiedatum").Value);

          var startdatum_is_registratiedatum = (startdate.midnight().getTime() == registratiedatum.midnight().getTime());
          startdate = (startdatum_is_registratiedatum ? registratiedatum : startdate.midnight());
        }
        // else startdatum niet aanpassen.
        oRs.Close();
        fields.push({ dbs: "mld_opdr_datumbegin", typ: "datetime", val: startdate, track: L("lcl_orderdate") }); // Opdrachtdatum (begindatum)

        var old_einddatum  = mld_opdr.mld_opdr_einddatum;
        var new_einddatum  = getFParamDate("date_done", null);
        var old_plandatum  = mld_opdr.mld_opdr_plandatum;
        var new_plandatum  = getFParamDate("date_plan", null);
        var old_plandatum2 = mld_opdr.mld_opdr_plandatum2;
        var new_plandatum2 = getFParamDate("date_plan2", null);
        var ins_srtdiscipline_prefix = mld.mld_prefix(mld_key);
        var formattedID = ins_srtdiscipline_prefix + mld_key + "/" + volgnr;

        var diff = (old_plandatum != null || new_plandatum != null)
                    && ((   old_plandatum == null && new_plandatum != null)
                        || (old_plandatum != null && new_plandatum == null)
                        || (old_plandatum.getTime() != new_plandatum.getTime()));
        if (diff)
        {
          fields.push({ dbs: "mld_opdr_plandatum", typ: "datetime", val: new_plandatum });
          ordpld_tracking = L("lcl_ord_is_ordpldtrack").format(formattedID) + ": " + buildTrackText("datetime", old_plandatum, new_plandatum);
        }

        diff = (old_plandatum2 != null || new_plandatum2 != null)
                && ((   old_plandatum2 == null && new_plandatum2 != null)
                    || (old_plandatum2 != null && new_plandatum2 == null)
                    || (old_plandatum2.getTime() != new_plandatum2.getTime()));
        if (diff)
        {
          fields.push({ dbs: "mld_opdr_plandatum2", typ: "datetime", val: new_plandatum2 });
          ordpl2_tracking = L("lcl_ord_is_ordpl2track").format(formattedID) + ": " + buildTrackText("datetime", old_plandatum2, new_plandatum2);
        }

        if (new_einddatum)
        {
          // Einddatum, wijzigen bestaande opdracht (AALB#32981 en WTCA#38303).
          // 1) Uitvoertijd is in uren:  ingevoerde datum + tijd opslaan.
          // 2) Uitvoertijd is in dagen:
          //   Is ingevoerde datum aangepast?
          //   Nee) Einddatum niet opslaan.
          //   Ja)  Is ingevoerde datum gelijk aan einddatum melding?
          //        Ja)  Datum + tijd is einddatum melding.
          //        Nee) Tijd is einde werkdag (Setting fac_t_endofworkday).

          // Het veld einddatum was aanwezig.
          // Als het tijdsveld niet aanwezig is en de datum is aangepast, dan de eindtijd op einde van de werkdag zetten (fac_t_endofworkday).
          // Dan moet ik eerst weten of het tijdsveld aanwezig was.
          // Of het tijdsveld van de einddatum aanwezig was is afhankelijk van de uitvoertijd type van de melding.

          // Als de opdrachtdatum is aangepast tot na de opdr.einddatum, stel dan de einddatum gelijk aan de opdrachtdatum (AADO#53823)
          if (startdate.getTime() > new_einddatum.getTime())
              new_einddatum = startdate;

          // Als de opdracht automatisch is aangemaakt kan de einddatum niet zijn ingevuld. Echter bij wijzigen van een bestaande opdracht is de einddatum altijd verplicht.
          if (mld_info.uitvoertijd_eenheid == "U")
          {
            // Als uitvoertijd in uren is, dan is er een tijdsveld aanwezig geweest en dan wordt de tijd automatisch opgeslagen.
            fields.push({ dbs: "mld_opdr_einddatum",  typ: "datetime", val: new_einddatum });
            // Is de einddatum aangepast?
            if (!old_einddatum || (old_einddatum.getTime() != new_einddatum.getTime()))
              // Tracken en notificeren van de einddatum (ORDDAT).
              orddat_tracking = L("lcl_ord_is_orddattrack").format(formattedID) + ": " + (old_einddatum? toDateTimeString(old_einddatum) : L("lcl_empty")) + L("lcl_trackto") + toDateTimeString(new_einddatum);
          }
          else
          { // Het tijdsveld was niet aanwezig.
            // Is de einddatum aangepast? Vergelijk oude en nieuwe einddatum (zonder tijd)
            var old_einddatum_m = old_einddatum? old_einddatum.midnight() : null;
            var new_einddatum_m = new_einddatum.midnight();
            var sameEndDate = old_einddatum && (old_einddatum_m.getTime() == new_einddatum_m.getTime());
            if (!sameEndDate || startdate.getTime() > old_einddatum.getTime())
            { // Nog even checken of de einddatum van de opdracht gelijk is aan de einddatum van de melding (zonder tijd).
              // Anders is de datum ONTERECHT te laat (Rood). Tijd einddatum opdracht (23:59) > tijd einddatum melding.
              var mld_einddatum = mld_info.einddatum; // Einddatum melding.
              var mld_einddatum_m = mld_einddatum.midnight(); // Einddatum melding.
              if (new_einddatum_m.getTime() == mld_einddatum_m.getTime())
              {
                // Datum is gelijk aan einddatum melding. Maak de tijd (+ datum) gelijk aan de einddatum melding.
                // Tenzij; opdrachtdatum(tijd) > einddatum(tijd) melding, dan: Maak de opdr.eindtijd (+ datum) gelijk aan de opdrachtdatum(tijd) melding. (AADO#53823)
                if (startdate.getTime() > mld_einddatum.getTime())
                {
                    new_einddatum = startdate;
                }
                else
                {
                    new_einddatum = mld_einddatum;
                }
                fields.push({ dbs: "mld_opdr_einddatum", typ: "datetime", val: new_einddatum });
                // Tracken en notificeren van de einddatum (ORDDAT).
                if (sameEndDate) // Komt dit uberhaupt voor?
                    orddat_tracking = L("lcl_ord_is_orddattrack").format(formattedID) + ": " + (old_einddatum? toDateTimeString(old_einddatum) : L("lcl_empty")) + L("lcl_trackto") + toDateTimeString(new_einddatum);
                else
                    orddat_tracking = L("lcl_ord_is_orddattrack").format(formattedID) + ": " + (old_einddatum_m? toDateString(old_einddatum_m) : L("lcl_empty")) + L("lcl_trackto") + toDateString(new_einddatum);
              }
              else
              { // Zet de eindtijd van de nieuwe einddatum op einde werkdag (fac_t_endofworkday)
                var endofworkday = parseFloat(S("fac_t_endofworkday"));
                new_einddatum.setHours(Math.floor(endofworkday), (endofworkday - Math.floor(endofworkday)) * 60);
                fields.push({ dbs: "mld_opdr_einddatum",  typ: "datetime", val: new_einddatum });
                // Tracken en notificeren van de einddatum (ORDDAT).
                orddat_tracking = L("lcl_ord_is_orddattrack").format(formattedID) + ": " + (old_einddatum_m? toDateString(new Date(old_einddatum_m)) : L("lcl_empty")) + L("lcl_trackto") + toDateString(new_einddatum);
              }
            }
            //else: Einddatum is niet gewijzigd. Dan ook de tijd niet aanpassen, ofwel het gehele veld niet opslaan.
          }
        }
      }

      if (reapprove || refiat)
      { // Zet de refiat waarden. Het hergoedkeurings- en/of herfiatteringstraject wordt opnieuw ingegaan. // Tracking hiervan is niet nodig.
        if (opdr_info.hours > 0)
          fields.push({ dbs: "mld_opdr_uren_refiat",       typ: "float",  val: opdr_info.hours });
        if (opdr_info.h_rate > 0)
          fields.push({ dbs: "mld_opdr_uurloon_refiat",    typ: "float",  val: opdr_info.h_rate });
        if (opdr_info.m_cost > 0)
          fields.push({ dbs: "mld_opdr_materiaal_refiat",  typ: "float",  val: opdr_info.m_cost });
        if (opdr_info.t_cost > 0)
          fields.push({ dbs: "mld_opdr_kosten_refiat",     typ: "float",  val: opdr_info.t_cost });

        // Ook voor status 5 wordt refiatstatus gezet zodat bekend is dat het om een herfiattering gaat. De fiatteringsvoorwaarde is dan iets anders.
        fields.push({ dbs: "mld_statusopdr_key_refiat",  typ: "number", val: opdr_info.opdr_status });
        // Goedkeuring opnieuw ingaan kan alleen als het approved bedrag leeg is.
        fields.push({ dbs: "mld_opdr_approved_refiat",   typ: "float",  val: opdr_info.approved });
        fields.push({ dbs: "mld_opdr_approved",          typ: "float",  val: null });
      }

      if (reapprove && S("mld_opdr_reapproval_rate") >= 0)
      { // Zet het oude opdrachtbedrag in het veld mld_opdr_fiatbedrag zodat na het goedkeuren het oude opdrachtbedrag nog bekend is.
        // Het oude opdrachtbedrag wordt namelijk overschreven met het nieuwe opdrachtbedrag.
        // Zo kan ook na het goedkeuren bepaald worden of er geherfiatteerd moet worden.
        fields.push({ dbs: "mld_opdr_fiatbedrag",        typ: "float",  val: opdr_info.t_cost });
      }

      // Met FSN#20132 kan bij bestaande opdrachten via deze weg niet meer het opdrachttype wijzigen
      // Documentfolders hernoemen (RenameFlexFolders) is dus niet meer nodig

      mldUpd = buildTrackingUpdate("mld_opdr", "mld_opdr_key = " + opdr_key, fields);

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

      // tracking doen we gelijk met de flexkenmerken
      // Opdrachtstatus aanpassen i.v.m. hergoedkeuring en/of herfiattering moet na de tracking, dus ook later.
    }

    if (shiftdate)
    {
      sql = "UPDATE mld_melding"
          + "   SET mld_melding_einddatum = " + new_einddatum.toSQL(true)
          + " WHERE mld_melding_key = " + mld_key;
      Oracle.Execute(sql);
      // Wijzigen einddatum tracken.
      var stdm_info = mld.mld_stdmeldinginfo(mld_info.stdm_key);
      var formattedID = stdm_info.ins_srtdiscipline_prefix + mld_key + " (" + stdm_info.discipline_omschrijving + "/" + stdm_info.stdmelding_omschrijving + ")";
      var mldtracking = L("lcl_mld_is_mldupdtrack").format(formattedID) + "\n"
                      + L("lcl_mld_enddate") + ": " + buildTrackText("datetime", mld_opdr.mld_opdr_einddatum, new_einddatum);
      mld.trackmeldingupdate(mld_key, mldtracking);
    }
  } // Bestaande opdracht

  // Als opdrachttype-materiaal een materiaallijst is en er zijn materialen geselecteerd, dan deze materialen opslaan.
  if (mld_opdr.typeopdr_materiaal >= 2)
  {
    if (S("mld_ordermaterial_frame") == 0)
    {
      var matexiststrArr = getFParamIntArray("matexiststr", []);
      var matopdrkeystrArr = getFParamIntArray("matopdrkeystr", []);
      var matkeystrArr = getFParamIntArray("matkeystr", []);
      var matamountstrArr = getFParamArray("matamountstr", []);
      var matunitstrArr = getFParamArray("matunitstr", []);
      var matpricestrArr = getFParamArray("matpricestr", []);
      var matbtwstrArr = getFParamArray("matbtwstr", []);
      var matdescriptstrArr = getFParamArray("matdescriptstr", [], true); // nosplit
      if (!isNew)
      { // Verwijder niet geselecteerde materialen
        sql = "DELETE FROM mld_opdr_materiaal"
            + " WHERE fac_usrdata_key IS NOT NULL"
            + "   AND fac_usrdata_key NOT IN (" + (matkeystrArr.length > 0? matkeystrArr.join(",") : "-1") + ")"
            + "   AND mld_opdr_key = " + opdr_key;
        Oracle.Execute(sql);
        // Verwijderde vrije materialen
        sql = "DELETE FROM mld_opdr_materiaal"
            + " WHERE fac_usrdata_key IS NULL"
            + "   AND mld_opdr_materiaal_key NOT IN (" + (matopdrkeystrArr.length > 0? matopdrkeystrArr.join(",") : "-1") + ")"
            + "   AND mld_opdr_key = " + opdr_key;
        Oracle.Execute(sql);
      }

      for (var i = 0; i < matexiststrArr.length; i++)
      {
        sql = "SELECT mld_opdr_materiaal_key"
            + "  FROM mld_opdr_materiaal"
            + " WHERE mld_opdr_key = " + opdr_key;
        sql += (matexiststrArr[i] == 1
               ? " AND fac_usrdata_key = " + matkeystrArr[i]
               : " AND mld_opdr_materiaal_key = " + (matopdrkeystrArr[i] ? matopdrkeystrArr[i] : -1)
               );

        oRs = Oracle.Execute(sql);
        if (!oRs.eof)
        {
          sql = "UPDATE mld_opdr_materiaal"
              + "   SET mld_opdr_materiaal_aantal = " + parseFloat(matamountstrArr[i])
              + (matexiststrArr[i] == 1? "" : ", mld_opdr_materiaal_eenheid = " + safe.quoted_sql(matunitstrArr[i]))
              + "     , mld_opdr_materiaal_prijs = " + parseFloat(matpricestrArr[i])
              + (matexiststrArr[i] == 1? "" : ", fin_btwtabelwaarde_key = " + parseInt(matbtwstrArr[i], 10))
              + (matexiststrArr[i] == 1? "" : ", mld_opdr_materiaal_omschr = " + safe.quoted_sql(matdescriptstrArr[i]))
              + " WHERE mld_opdr_materiaal_key = " + oRs("mld_opdr_materiaal_key").Value;
          var err = Oracle.Execute(sql, true);
          if (err.friendlyMsg)
              abort_with_warning(err.friendlyMsg);
        }
        else
        {
          sql = "INSERT INTO mld_opdr_materiaal"
              + "     ( mld_opdr_key"
              + (matexiststrArr[i] == 1? " , fac_usrdata_key" : "")
              + "     , mld_opdr_materiaal_aantal"
              + (matexiststrArr[i] == 1? "" : ", mld_opdr_materiaal_eenheid")
              + "     , mld_opdr_materiaal_prijs"
              + (matexiststrArr[i] == 1? "" : ", fin_btwtabelwaarde_key")
              + (matexiststrArr[i] == 1? "" : " , mld_opdr_materiaal_omschr")
              + "     )"
              + " VALUES ( " + opdr_key
              + (matexiststrArr[i] == 1? ", " + matkeystrArr[i] : "")
              + "        , " + parseFloat(matamountstrArr[i])
              + (matexiststrArr[i] == 1? "" : ", " +  safe.quoted_sql(matunitstrArr[i]))
              + "        , " + parseFloat(matpricestrArr[i])
              + (matexiststrArr[i] == 1? "" : ", " + parseInt(matbtwstrArr[i]))
              + (matexiststrArr[i] == 1? "" : ", " + safe.quoted_sql(matdescriptstrArr[i]))
              + "        )";
          var err = Oracle.Execute(sql, true);
          if (err.friendlyMsg)
              abort_with_warning(err.friendlyMsg);
        }
        oRs.Close();
      }
    }
  }

  if (getFParamInt("has_r_objs", 0) == 1) // Voorkom dat we wissen vanuit mobile-save die helemaal geen listbox heeft.
  { // Objecten verwijderen of toevoegen.
    var objtrack = mld.saveobjects(mld_key, obj_arr);
    // Tracken van de objectwijzigingen.
    if (objtrack != "")
    {
      var stdm_info = mld.mld_stdmeldinginfo(mld_info.stdm_key);
      var formattedID = stdm_info.ins_srtdiscipline_prefix + mld_key + " (" + stdm_info.discipline_omschrijving + "/" + stdm_info.stdmelding_omschrijving + ")";
      mld.trackmeldingupdate(mld_key, L("lcl_mld_is_mldupdtrack").format(formattedID) + "\n" + objtrack);
    }
  }

  // readonly had voornamelijk betrekking op de vaste velden.
  // met mldman o.i.d. kan zijn dat alles readonly is behalve bepaalde flexkenmerken
  // Wijzigbare flexkenmerken altijd opslaan
  currentKenmerkenSQL = "SELECT k.mld_kenmerk_key"
                      + "     , ko.mld_kenmerkopdr_waarde"
                      + "     , k.mld_kenmerk_toonbaar"
                      + "     , k.fac_functie_key"
                      + "  FROM mld_kenmerkopdr ko"
                      + "     , mld_kenmerk k"
                      + " WHERE k.mld_kenmerk_key = ko.mld_kenmerk_key"
                      + "   AND mld_kenmerkopdr_verwijder IS NULL"
                      + "   AND mld_opdr_key = " + opdr_key;

  allKenmerkenSQL     = "SELECT COALESCE (" + lcl.xsql("k.mld_kenmerk_omschrijving", "k.mld_kenmerk_key")
                      + "              ,  " + lcl.xsql("t.mld_srtkenmerk_omschrijving", "t.mld_srtkenmerk_key") + ") kenmerk_omschrijving"
                      + "     , t.mld_srtkenmerk_kenmerktype kenmerk_kenmerktype"
                      + "     , t.mld_srtkenmerk_systeem     kenmerk_systeem"
                      + "     , k.mld_kenmerk_toonbaar       kenmerk_readonly"
                      + "     , k.mld_kenmerk_uniek          kenmerk_uniek"
                      + "     , fac_kenmerkdomein_key"
                      + "  FROM mld_kenmerk k"
                      + "     , mld_srtkenmerk t"
                      + " WHERE k.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                      + "   AND k.mld_kenmerk_key = "; /* wordt in saveFlexkenmerken uitgebreid */

  var flextrack =
      saveFlexKenmerken(opdr_key, { kenmerkTable: "mld_kenmerkopdr",
                                    kenmerkParentKey : "mld_opdr_key",
                                    kenmerkWaarde: "mld_kenmerkopdr_waarde",
                                    kenmerkKey: "mld_kenmerk_key",
                                    kenmerkToonbaar: "mld_kenmerk_toonbaar",
                                    nameprefix: getFParam("nameprefix", ""),
                                    extraserie: hasFParam("nameprefix"),
                                    currentKenmerkenSQL: currentKenmerkenSQL,
                                    allKenmerkenSQL: allKenmerkenSQL,
                                    requestQF: Request.Form,
                                    isNew: isNew,
                                    flexPath: "MLD/O",
                                    tracking: true,
                                    module: "MLD"
                                  }
                       );

  // Bij het automatisch aanmaken van een opdracht bij een melding is alleen de melding_key doorgegeven en dus niet de melding kenmerken.
  // Deze kenmerken zijn dus ook niet opgeslagen.
  // De melding kenmerken die overeenkomen met de opdracht kenmerken ophalen en opslaan voor de nieuwe opdracht.
  if (autoorder)
  {
    var sql_kenmerkwaarde = "COALESCE((SELECT v.mld_kenmerkmelding_waarde"
                          + "            FROM mld_kenmerkmelding v"
                          + "               , mld_kenmerk vk"
                          + "               , mld_srtkenmerk vt"
                          + "           WHERE v.mld_melding_key = " + mld_key
                          + "             AND vk.mld_srtkenmerk_key = vt.mld_srtkenmerk_key"
                          + "             AND vk.mld_srtkenmerk_key = k.mld_srtkenmerk_key"
                          + "             AND vk.mld_kenmerk_groep = k.mld_kenmerk_groep"
                          + "             AND v.mld_kenmerk_key = vk.mld_kenmerk_key"
                          + "             AND v.mld_kenmerkmelding_verwijder IS NULL"
                          + "             AND COALESCE(vk.mld_kenmerk_omschrijving, vt.mld_srtkenmerk_omschrijving) = COALESCE(k.mld_kenmerk_omschrijving, t.mld_srtkenmerk_omschrijving))"
                          + "       , k.mld_kenmerk_default)";
    var sql_kenmerkwaarde2 = " SELECT " + opdr_key + " mld_opdr_key"
                           + "      , k.mld_kenmerk_key kenmerk_key"
                           + "      , " + sql_kenmerkwaarde + " kenmerk_waarde"
                           + "   FROM mld_srtkenmerk t"
                           + "      , mld_kenmerk k"
                           + "      , mld_typeopdr s"
                           + "  WHERE (s.mld_typeopdr_key = " + t_opdr + " OR s.mld_typeopdr_key IS NULL)"
                           + "    AND k.mld_typeopdr_key = s.mld_typeopdr_key(+)"
                           + "    AND k.mld_kenmerk_niveau IN ('P', 'O')"
                           + "    AND k.mld_kenmerk_verwijder IS NULL"
                           + "    AND k.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                           + "    AND t.mld_srtkenmerk_verwijder IS NULL"
                           + "    AND t.mld_srtkenmerk_kenmerktype <> 'M'"
                           + "    AND BITAND(k.mld_kenmerk_rolcode, 5) > 0"  // afmeld-only kenmerken (010) uitsluiten, dus x0x
                           + "    AND " + sql_kenmerkwaarde + " IS NOT NULL";
    var sql_insert = "INSERT INTO mld_kenmerkopdr (mld_opdr_key"
                   + "                           , mld_kenmerk_key"
                   + "                           , mld_kenmerkopdr_waarde)"
                   + sql_kenmerkwaarde2
                   + " AND " + sql_kenmerkwaarde + " NOT LIKE '%##EXPR##%'";
    Oracle.Execute(sql_insert);

    var sql_kenmwaarden =
        " INSERT INTO fac_kenmwaarden(fac_kenmwaarden_module,"
      + "                             fac_kenmwaarden_refkey,"
      + "                             fac_kenmwaarden_kenmerk_key,"
      + "                             fac_kenmwaarden_waarde,"
      + "                             fac_kenmwaarden_kenmerktype,"
      + "                             fac_kenmwaarden_kenmerklen,"
      + "                             fac_kenmwaarden_kenmerkdec,"
      + "                             fac_kenmwaarden_isexpr,"
      + "                             prs_perslid_key)"
      + "     SELECT 'OPD',"
      + "            " + opdr_key + ","
      + "            k.mld_kenmerk_key,"
      + "            " + sql_kenmerkwaarde + " kenmerk_waarde,"
      + "            t.mld_srtkenmerk_kenmerktype,"
      + "            t.mld_srtkenmerk_lengte,"
      + "            t.mld_srtkenmerk_dec,"
      + "            CASE"
      + "            WHEN k.mld_kenmerk_default LIKE '%##EXPR##%'"
      + "            THEN 1"
      + "            ELSE 0"
      + "            END fac_kenmwaarden_isexpr,"
      + "            " + user_key
      + "       FROM mld_srtkenmerk t"
      + "          , mld_kenmerk k"
      + "          , mld_typeopdr s"
      + "      WHERE (s.mld_typeopdr_key = " + t_opdr + " OR s.mld_typeopdr_key IS NULL)"
      + "        AND k.mld_typeopdr_key = s.mld_typeopdr_key(+)"
      + "        AND k.mld_kenmerk_niveau IN ('P', 'O')"
      + "        AND k.mld_kenmerk_verwijder IS NULL"
      + "        AND k.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
      + "        AND t.mld_srtkenmerk_verwijder IS NULL"
      + "        AND t.mld_srtkenmerk_kenmerktype <> 'M'"
      + "        AND BITAND(k.mld_kenmerk_rolcode, 5) > 0"  // afmeld-only kenmerken (010) uitsluiten, dus x0x
      + "        AND " + sql_kenmerkwaarde + " IS NOT NULL"
      + "    ORDER BY mld_kenmerk_volgnummer";
    Oracle.Execute(sql_kenmwaarden);

    // Evaluate flex expressions.
    var sqlEvaluate = "BEGIN flx.evaluateflexexpressions('OPD', " + opdr_key + ", " + user_key + ", 1); END;";
    Oracle.Execute(sqlEvaluate);

  }
}

if (!isNew)
{
  if ((mldUpd.trackarray.length || flextrack.length))
  {
    var ins_srtdiscipline_prefix = mld.mld_prefix(mld_key);
    var formattedID = ins_srtdiscipline_prefix + mld_key + "/" + volgnr;
    mld.trackopdrachtupdate(opdr_key, L("lcl_ord_is_ordupdtrack").format(formattedID) + "\n" + mldUpd.trackarray.concat(flextrack).join("\n"));
  }
  // Tracken en notificeren van de einddatum (ORDDAT) (notificeren aan de gebouwverantwoordelijke van het betreffende gebouw van de onderliggende melding).
  if (orddat_tracking != "")
  {
    mld.trackopdreinddatumupdate(opdr_key, orddat_tracking);
  }
  // Tracken en notificeren van de geplande aanvang datum (ORDPLD) (notificeren aan interne contactpersoon).
  if (ordpld_tracking != "")
  {
    mld.trackopdrplandatumupdate(opdr_key, ordpld_tracking);
  }
  // Tracken en notificeren van de geplande einddatum (ORDPL2) (notificeren aan interne contactpersoon).
  if (ordpl2_tracking != "")
  {
    mld.trackopdrplandatum2update(opdr_key, ordpl2_tracking);
  }
  // Bij update van uitvoerende en als status 5 is, dan uitvoerende tracken (ORDMLI (intern) of ORDMLE (extern)
  // Tekstueel is dit (ook) wel wel vermeld i trackopdrachtupdate maar de ORDMLI of MLE notificatie moet nog
  if (uitvoerende > -1 && old_uitvoerende != uitvoerende && opdr_status == 5)
    mld.trackuitvoerende(opdr_key, uitvoerende, opdr_status);

  // Bij status "Geaccepteerd(8)":
  // a) Als behandelaar verandert moeten een actieve opdracht worden onderbroken.
  // b) zorgen dat er maar 1 opdracht van het opdrachttype van deze opdracht actief mag zijn.
  var new_behandelaar = getFParamInt("behandelaar", -1);
  if ((mld_opdr.typeopdr_sequential&1)==1 && ((new_behandelaar != old_behandelaar) || ((mld_opdr.typeopdr_sequential&2)==2 && opdr_status == 8)))
  {
    var extendedopdrstatus = mld.getextendedopdrstatus(opdr_status, opdr_key);
    if (extendedopdrstatus == 8 || extendedopdrstatus == 82)
    {
      if (new_behandelaar != old_behandelaar)
      { // Als de opdracht actief is dan moet deze onderbroken worden.
        // Dan hoef ik niet meer te controleren of er nog een actieve opdracht is.
        shared.trackaction("ORDHLT", opdr_key);
        Oracle.Execute("UPDATE mld_opdr SET mld_opdr_halted = 1 WHERE mld_opdr_key = " + opdr_key);
        msg += (msg != ""? "\n" : "") + L("lcl_mld_opdr_halted_beh").format(mld_opdr.opdr_type_omschr);
      }
      else
      { // Was er al een actieve opdracht voor de opdrachttype van deze opdracht.
        // Als de opdrachten niet sequentieel uitgevoerd moeten worden, dan wordt er ook geen actieve opdracht gevonden.
        var activeopdr = mld.getactiveopdracht(opdr_key);
        if (activeopdr.opdr_key > 0)
        { // Deze (actieve) opdracht dan maar onderbreken, want er mag er maar 1 actief zijn.
          shared.trackaction("ORDHLT", opdr_key);
          Oracle.Execute("UPDATE mld_opdr SET mld_opdr_halted = 1 WHERE mld_opdr_key = " + opdr_key);
          msg += (msg != ""? "\n" : "") + L("lcl_mld_opdr_already_active").format(mld_opdr.opdr_type_omschr);
        }
      }
    }
  }

  // De tracking van de wijziging is geweest. Nu kan de statusverandering voor de hergoedkeuring en/of herfiattering ook uitgevoerd worden.
  if (reapprove)
  { // Zetten van de status van de gewijzigde opdracht op "Ter goedkeuring" en afhandelen van de tracking (status + uitvoerende tracking) en daarmee notificatie.
    status = 10; // Ter goedkeuring.
    mld.setopdrachtstatus(opdr_key, status, uitvkeyArray[u]);
    // Hergoedkeuring: de goedkeuring begint weer opnieuw alsof het een nieuwe opdracht betreft.
    var sql = "UPDATE mld_opdr SET mld_opdr_approved = NULL WHERE mld_opdr_key = " + opdr_key;
    Oracle.Execute(sql);
  }
  else if (refiat)
  { // Zetten van de status van de gewijzigde opdracht op "Ter fiattering" en afhandelen van de tracking (status + uitvoerende tracking) en daarmee notificatie.
    status = 3; // Ter fiattering.
    mld.setopdrachtstatus(opdr_key, status, uitvkeyArray[u]);
  }
  else if (opdr_status == 10 &&
           // Bij opvolgende (tweede, enz.) hergoedkeuring moet gekeken worden naar het tot nu toe goedgekeurde bedrag (mld_opdr.approved)
           // dat dan hoger is als het bedrag dat goedgekeurd is voor de hergoedkeuring (approved_refiat). Dan hier ook op mld_opdr.approved controleren.
           // Bij bepaling van hergoedkeuring wordt ook gekeken naar mld_opdr.approved, dan moet je hier ook op mld_opdr.approved controleren.
           // Alle voorwaarden moesten gelden om hergoedkeuring af te dwingen. Dan moet 1 van de voorwaarden niet meer gelden om te stoppen met goedkeuren. => OR
           (tot_kosten < mld_typeopdr_gvs || tot_kosten < mld_opdr.approved) &&
           mld_opdr.opdr_status_refiat > 0)
  { // Opdracht staat ter hergoedkeuring/herfiattering. Opdracht is al een keer uitgegeven(5) geweest.
    // De opdracht staat ter goedkeuring en
    // 1) de opdrachtkosten zijn verlaagd tot onder de gvs limiet en/of
    // 2) de opdrachtkosten zijn verlaagd tot onder het al goedgekeurde bedrag,
    // dan was hergoedkeuring helemaal niet nodig.
    // Dan kan de status naar "Ter fiattering(3)" of weer terug naar de oorspronkelijke status.
    var refiat = getToReaApprove();
    requestApproval = reapprove || refiat; // Opnieuw bepalen omdat refiat aangepast kan zijn.
    mld.setopdrachtstatus(opdr_key, refiat? 3 : mld_opdr.opdr_status_refiat, mld_opdr.uitvoerende_key); // Fiatteren of naar de status voor hergoedkeuren/herfiattering.

    if (!refiat)
    { // Refiat opdracht kosten en refiat status weer op NULL zetten.
      sql = "UPDATE mld_opdr"
          + "   SET mld_opdr_uren_refiat = NULL"
          + "     , mld_opdr_uurloon_refiat = NULL"
          + "     , mld_opdr_materiaal_refiat = NULL"
          + "     , mld_opdr_kosten_refiat = NULL"
          + "     , mld_statusopdr_key_refiat = NULL"
          + "     , mld_opdr_approved_refiat = NULL"
          + " WHERE mld_opdr_key = " + opdr_key;
      Oracle.Execute(sql);
    }
  }
  else if (opdr_status == 10 &&
           // Setting mld_pessismistisch_hergoedkeuren is 0: Alle voorwaarden moesten gelden om goedkeuring af te dwingen. Dan moet 1 van de voorwaarden niet meer gelden om te stoppen met goedkeuren. => OR
           // Setting mld_pessismistisch_hergoedkeuren is 1: Één van de voorwaarden moest gelden om goedkeuring af te dwingen. Dan moeten alle voorwaarden niet meer gelden om te stoppen met goedkeuren. => AND
           // Bij opvolgende (tweede, enz.) goedkeuring hoeft niet meer naar de goedkeuringsvrijstelling (mld_typeopdr_gvs) te worden gekeken. Goedkeuring is dan al geweest en aan alle voorwaarden zijn toen voldaan.
           // Zie: BLCC#89485 en BLCC#64289.
           ((S("mld_pessismistisch_hergoedkeuren") == 0 && (tot_kosten < mld_opdr.approved ||                                           // Na de eerste goedkeuring (van meerdere) is mld_opdr.approved gevuld en is deze voorwaarde van belang.
                                                            tot_kosten < mld_typeopdr_gvs  || tot_kosten < mld_opdr.bestellimiet)) ||   // OR.
            (S("mld_pessismistisch_hergoedkeuren") == 1 && (tot_kosten < mld_opdr.approved ||                                           // Na de eerste goedkeuring (van meerdere) is mld_opdr.approved gevuld en is deze voorwaarde van belang.
                                                            (tot_kosten < mld_typeopdr_gvs && tot_kosten < mld_opdr.bestellimiet)))) && // AND en geen gvs voorwaarde bij opvolgende (tweede, enz.) goedkeuring.
           mld_opdr.opdr_status_refiat == 0)
  { // Nieuwe opdracht staat ter goedkeuring. Opdracht is nog niet uitgegeven(5) geweest (GEEN hergoedkeuring/herfiattering).
    // Als de opdracht ter goedkeuring staat en
    // 1) de opdrachtkosten zit onder het al goedgekeurde bedrag of
    // 2) de opdrachtkosten zijn verlaagd tot onder de eerste bestellimiet,
    // dan is er geen reden om (meer) goed te keuren.
    var refiat = getToReaApprove();
    requestApproval = reapprove || refiat; // Opnieuw bepalen omdat refiat aangepast kan zijn.
    mld.setopdrachtstatus(opdr_key, refiat? 3 : 5, mld_opdr.uitvoerende_key); // Fiatteren of naar Uitgegeven.
  }
}

var doneParams = { opdr_key: opdrkeyArray[0], mld_key: mld_key, autoprint: false, asksend: false, message: msg, success: true };

// Opdracht direct electronisch versturen of door putorders laten oppikken (te verzenden veld zetten) bij:
// 1) nieuwe opdrachten.
// 2) wijzigen opdrachten als de uitvoerder aangepast is en in de statussen Ter Goedkeuring(10), Ter Fiattering(3), Gefiatteerd(4), Uitgegeven(5), Geaccepteerd(8).
// Nooit als er een activiteits key is meegegeven omdat het dan eigenlijk geen opdracht is.
if (isNew ||
    (!isNew && checkTeVerzenden) &&
    act_key <= 0)
{
  if (initopdrkey < 0 && copyorder_key < 0 && mld_typeopdr_isofferte == 1)
  { // Meerdere offerte opdrachten aanmaken
    for (var u = 0; u < uitvkeyArray.length; u++)
    {
      if (autoorder || mld_typeopdr_confirm_for_send < 2)
      { // Oppikken door scheduled via putorders
        sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = 1 WHERE mld_opdr_key = " + opdrkeyArray[u];
        Oracle.Execute(sql);

        if (status == 5)
        { // Bij ORDNEW tracking wordt er niet genotificeerd. Dat moet als je mld_opdr_teverzenden op 1 zet nog gebeuren.
          Oracle.Execute("BEGIN fac.notifytrackingbedrijven ('ORDNEW', " + opdrkeyArray[u] + "); END;");
        }
      }
      else if (mld_typeopdr_confirm_for_send == 2)
      { // Nog niet laten oppikken door achtergrond putorders alleen uitstellen.
        sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = NULL WHERE mld_opdr_key = " + opdr_key; // Is dit wel nodig? Bij (nieuwe) opdracht is de waarde al niet gezet.
        Oracle.Execute(sql);
      }
    }
  }
  else
  {
    //var uitvoerende = getFParamInt("uitvoerende", -1);
    // In geval van een voor-de-uitvoerder-nieuwe
    sql = "SELECT prs_bedrijfadres_url"
        + "  FROM prs_bedrijfadres ba"
        + " WHERE prs_bedrijfadres_type = 'O'"
        + "   AND prs_bedrijfadres_startdatum <= SYSDATE"
        + "   AND (mld_typeopdr_key IS NULL OR mld_typeopdr_key = " + t_opdr + ")"
        + "   AND (alg_locatie_key = " + mld_info.loc_key + " OR alg_locatie_key IS NULL)"
        + "   AND (ba.ins_discipline_key = " + mld_info.opdr_disc_key + " OR ba.ins_discipline_key IS NULL)"
        + "   AND (alg_district_key = (SELECT d.alg_district_key"
        + "                              FROM alg_locatie d"
        + "                             WHERE d.alg_locatie_key = " + mld_info.loc_key + ")"
        + "    OR alg_district_key IS NULL)"
        + "   AND prs_bedrijf_key = " + uitvoerende;
    oRs = Oracle.Execute(sql);
    // Als (1) externe uitvoerder met (2) prs_bedrijfadres_url type O dan vragen
    // of de opdracht elektrisch verstuurd moet worden. Zo ja, dan
    if (!oRs.eof)
    {
      if (autoorder || mld_typeopdr_confirm_for_send == 0) // meldracht
      { // Oppikken door achtergrond putorders
        sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = 1 WHERE mld_opdr_key = " + opdr_key;
        Oracle.Execute(sql);

        if (status == 5)
        { // Bij ORDNEW tracking wordt er niet genotificeerd. Dat moet als je mld_opdr_teverzenden op 1 zet nog gebeuren.
          Oracle.Execute("BEGIN fac.notifytrackingbedrijven ('ORDNEW', " + opdr_key + "); END;");
        }
      }
      else if (mld_typeopdr_confirm_for_send == 1)
      { // Gaat straks vragen: "Zal ik electronisch versturen?"
        // Let op: zelfs gevraagd als nog gefiatteerd moet worden
        // Putorders negeert hem nog zolang hij ter fiattering staat!
        doneParams.asksend = true;
      }
      else if (mld_typeopdr_confirm_for_send == 2)
      { // Nog niet laten oppikken door achtergrond putorders alleen uitstellen.
        sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = NULL WHERE mld_opdr_key = " + opdr_key; // Is dit wel nodig? Bij (nieuwe) opdracht is de waarde al niet gezet.
        Oracle.Execute(sql);
      }
    }
    else
    {
      // Als via setting mogelijkheid/workaround enabled om zelfs als er geen mldorder_adres
      // is gedefinieerd toch een opdracht per e-mail te kunnen versturen, dan vragen naar
      // welk adres en met welk subject. Eea. natuurlijk alleen als opdracht geen fiattering
      // vereist of inmiddels gefiatteerd is.
      // Daarnaast ook geen email versturen bij interne uitvoerders.

      // Is het een interne uitvoerder?
      var interne_uitvoerder = false;
      sql = "SELECT prs_perslid_key"
          + "  FROM prs_perslid"
          + " WHERE prs_perslid_key = " + uitvoerende;
      oRsi = Oracle.Execute(sql);
      if (!oRsi.eof)
        interne_uitvoerder = true;
      oRsi.Close();

      if (S("mld_opdr_ask_mail_if_unknown") && !requestApproval && !interne_uitvoerder)
      {
        var subjectMail = L("lcl_mld_opdr_description") + " " + mld_key + "/" + volgnr;
        var urlMail = "../shared/queuemail.asp?pcode=ORDMAI&defemail_key=-1&key=" + opdr_key
                    + "&pshowcompany=1"
                    + "&subject=" + Server.URLencode(subjectMail)
                    + "&extratext=" + Server.URLencode(L("lcl_mld_opdr_ask_mail_if_unknown_hint"));
                    + "&mailfrom=" + (hasBOread? 1 : 0);

        var url = protectQS.create(urlMail);
        // Net als autoprint en asksend de parameter queuemail met doneParams mee geven zodat
        // bij het sluiten ../shared/queuemail.asp wordt aangeroepen in CloseDetail.
        // Parameter queuemail wordt in de functie closeDetail() opgepakt
        doneParams.queuemail = url;
      }
    }
    oRs.Close();
  }
}

if (initopdrkey > 0 || copyorder_key > 0 || mld_typeopdr_isofferte == 0) // !(opdr_key < 0 && !copy && mld_opdr.typeopdr_isofferte == 1)
{
  var info_txt = "";
  if (requestApproval)
  {
    if (status == 3) // Ter Fiattering
      var info_txt = mld.sendNeedApprovalNotification(opdr_key, approver_key);
    else // status == 10
      var info_txt = L("lcl_mld_goedkeuring"); // we weten niet wie, iedereen met WEB_ORDGOE
    doneParams.warning = info_txt;
  }
  else // Straks bon tonen voor intern en extern order
  {
    if ((isNew && S("mld_order_autoprint") !=3) || S("mld_order_autoprint") == 1) // nieuwe opdracht of setting
    {
      if (act_key <= 0 && uitvoerende > 0)
      { // RWSN#15010: Bij het opslaan van een nieuwe opdracht GEEN bon tonen/printen als
        // de uitvoerende een INTERNE uitvoerende is (S("mld_order_autoprint") == 2).
        sql = "SELECT mu.intern"
            + "  FROM mld_v_uitvoerende mu"
            + " WHERE mu.mld_uitvoerende_key = " + uitvoerende;
        oRs = Oracle.Execute(sql);
        var intern = oRs("intern").Value == 1;
        oRs.Close();

        // Als S("mld_order_autoprint") == 2 dan geen bon bij prs_perslid (interne uitvoerende) en intern bedrijf (prs_bedrijf_intern == 1).
        if ((S("mld_order_autoprint") == 2 && !intern) || (S("mld_order_autoprint") != 2))
        {
          // parameter autoprint wordt in de functie closeDetail() opgepakt
          doneParams.autoprint = true;
        }
      }
    }
  }
}

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

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