<%@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;
%>

<!-- #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="mld.inc" -->
<!-- #include file="../PRS/prs.inc" -->
<!-- #include file="../shared/budget.inc" -->
<!-- #include file="../Shared/json2.js" -->
<%
var opdr_key = getQParamInt("opdr_key", -1);
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 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", "");

// 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;
}
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 FROM mld_stdmelding WHERE mld_stdmelding_key = " + mld_info.stdm;
    oRs = Oracle.Execute(sql);
    var t_opdr = oRs("mld_typeopdr_key").Value;
    if (!t_opdr)
      // Beter dan error in de IIS log. Zonder de alert blijft het scherm te klein?
      shared.simpel_page("Opdracht niet automatisch aangemaakt omdat opdrachttype niet bepaald kon worden");
  }
  else
    var t_opdr  = getFParamInt("t_opdr");  // Kan deze wel wijzigen?
}

lcl.set_dialect(t_opdr, "mld_typeopdr_key");
// Bepaal matchtype van opdracht
var sql = "SELECT mld_typeopdr_matchtype"
        + "     , mld_typeopdr_isofferte"
        + "     , mld_typeopdr_gvs"
        + "  FROM mld_typeopdr"
        + " WHERE mld_typeopdr_key = " + t_opdr;
oRs = Oracle.Execute(sql);
var mld_typeopdr_matchtype = oRs("mld_typeopdr_matchtype").Value;
var mld_typeopdr_isofferte = oRs("mld_typeopdr_isofferte").Value;
var mld_typeopdr_gvs = oRs("mld_typeopdr_gvs").Value || 0;

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)
  { // 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);
    if (!oRs.eof && oRs("alg_locatie_key").Value)
      locatiekey = oRs("alg_locatie_key").Value;
    oRs.Close();
  }
  uitvoerende = mld.dienst_controle(mld_info.stdm, locatiekey, mld_info.bld_key, mld_info.room_key, mld_info.perslid_key_voor);

  if (uitvoerende < 0)
    // Alert geven en terug. Beter dan error in de IIS log.
    shared.simpel_page(L("lcl_orders_no_contractor"));
}

if (isNew && copyorder_key < 0 && mld_typeopdr_isofferte == 1)
{ // Meerdere offerte opdrachten aanmaken
  var uitvkeyArray = getFParamIntArray("uitvkeystr", [uitvoerende]);
  var uitvcpersArray = getFParamIntArray("uitvcpersstr", [-1]);
  var uitvcntArray = getFParamIntArray("uitvcntstr", [-1]);
}
else
{
  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 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 || 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.
// 0 = niet verplicht, 1 = save + 2 = afmelden, 4 = afronden.
var isRequired = mld_opdr.typeopdr_matchtype != 4 &&
                 ((mld_opdr.typeopdr_kosten_verplicht & 1 || (mld_opdr.typeopdr_kosten_verplicht == 0 && mld_opdr.opdrkosten_verplicht & 1)) ||
                   ((mld_opdr.typeopdr_kosten_verplicht & 2 || (mld_opdr.typeopdr_kosten_verplicht == 0 && mld_opdr.opdrkosten_verplicht & 2)) &&
                    (mld_opdr.opdr_status == 5 || mld_opdr.opdr_status == 8)) ||
                   ((mld_opdr.typeopdr_kosten_verplicht & 4 || (mld_opdr.typeopdr_kosten_verplicht == 0 && mld_opdr.opdrkosten_verplicht & 4)) &&
                    mld_opdr.opdr_status == 6))
var tot_kosten = getFParamFloat("tot_kosten", -1);

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

// (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)
  sql = "SELECT mld_typeopdr_kosten FROM mld_typeopdr WHERE mld_typeopdr_key = " + t_opdr;
  oRs = Oracle.Execute(sql);
  var needKostenplaats = oRs("mld_typeopdr_kosten").Value == 1;
  oRs.Close();
  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.disc))
        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;
}

var uurloon = getFParamFloat("uurtarief", -1);
if (uurloon < 0)
{
    var sql = "SELECT b.prs_bedrijf_uurloon"
            + "  FROM prs_bedrijf b"
            + " WHERE b.prs_bedrijf_key = " + uitvoerende;
    var oRs = Oracle.Execute(sql);
    if (!oRs.eof) // Had in theorie ook een persoon kunnen zijn?
    {
      uurloon = oRs("prs_bedrijf_uurloon").Value;
    }
    oRs.Close();
}

// 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;
for (var u = 0; u < uitvkeyArray.length; u++)
{
  if (isNew)
  { // nieuwe opdracht

    // 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
    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 disc_key = mld_opdr.dis_key;
      var exceeds_pgb = mld.exceeds_pgb(tot_kosten, user_key, disc_key)
      var exceeds_agb = mld.exceeds_agb(tot_kosten, kostenplaats_key, disc_key, opdr_key)
      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, 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, 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/aanmaker van de melding mag niet zelf goedkeuren boven het bedrag can_selfapprove.
        approver_key = prs.getKpVerantwoordelijke(kostenplaats_key, tot_kosten, -1);
        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 = {kp_key: kostenplaats_key,
                      opdr_key: opdr_key,
                      t_cost: tot_kosten,
                      opdr_handfiat: voluntaryApproval};
      var approver = mld.getFirstApprover(mld_info, mld_opdr);
      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 +")";

    // 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",   frm: "tot_kosten" },
                   { dbs: "mld_opdr_uurloon",            typ: "float",   val: (uurloon > 0? uurloon : 0) }, // I.v.m. de bon moet het uurloon i.i.g. een waarde hebben zodat niet NaN wordt weergegeven.
                   { dbs: "mld_opdr_ordernr",            typ: "varchar", frm: "opdr_ordernr" },
                   { dbs: "prs_kostenplaats_key",        typ: "key",     val: kostenplaats_key },
                   { 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_id",                 typ: "varchar", frm: "opdr_id" },
                   { dbs: "mld_opdr_handfiat",           typ: "check",   val: (voluntaryApproval? 1 : 0) }
                 ];

    // Startdatum is vandaag: starttijd is huidige tijd.
    // Startdatum is niet vandaag: 1) Uitvoertijd is in uren => starttijd is begin werkdag (bijv. 7:00).
    //                             2) Uitvoertijd is in dagen => starttijd is 0:00.
    var startdate_is_today = (startdate.midnight().getTime() == sysdate.midnight().getTime());

    var bedrijf_uitvoertijd = mld.getbedrijfuitvoertijd(uitvoerende); // vaak nodig
    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 (bedrijf_uitvoertijd == null || bedrijf_uitvoertijd.eenheid == 'U')
        var uitvoertijdInUren = false;
      else
        var uitvoertijdInUren = true;

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

    if (autoorder)
    {
      if (bedrijf_uitvoertijd)
        var einddatum = mld.geteinddatumopdracht(mld_key, bedrijf_uitvoertijd, startdate);
      else
        var einddatum = mld_info.einddatum;

      var oms = mld_info.mld_omschr || "";
      if (S("mld_ord_flexsummary") == 1)
          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 });
      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
    {
      fields.push({ dbs: "mld_opdr_einddatum",          typ: "datetime", frm: "date_done" });
      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 (tot_kosten > mld_info.bestellimiet || tot_kosten > mld_typeopdr_gvs)
    {
      status = 10; // Ter goedkeuring.
      requestApproval = true;
    }
    else if (requestApproval)
      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]);
    // Refresh meldingstatus zonodig als gevolg van een nieuw uitgegeven opdracht
    mld.updatemeldingstatus(mld_key, 0);

    // Nu de diverse flexfiles van de melding naar de opdracht kopieren
    // voor zover het kenmerksoort overeenstemt
    if (u == 0)
        first_opdr_key = opdr_key;
    if (copyorder_key > 0 || u > 0) // kopieren bijlagen van de originele opdracht of eerste offerte
    {
      var copy_from = (copyorder_key > 0 ? copyorder_key : first_opdr_key);
      __Log("Copy flexfiles from: " + 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"
                  + "   AND k_old.mld_kenmerk_niveau = 'O'"
                  + "   AND k_new.mld_typeopdr_key = s_new.mld_typeopdr_key"
                  + "   AND k_new.mld_kenmerk_niveau = '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";
      CopyFlexFolders("MLD/" + subfolderKey("O", copy_from), "MLD/" + subfolderKey("O", opdr_key), flexsql);
    }
    else // kopieren van de bijbehorende melding
    {
      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
                  + "   AND s_new.mld_typeopdr_key = " + t_opdr
                  + "   AND s_old.mld_ins_discipline_key = d_old.ins_discipline_key"
                  + "   AND t.mld_srtkenmerk_kenmerktype IN ('F', 'E', 'M')"
                  + "   AND (   (    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 = '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";
      CopyFlexFolders("MLD/" + subfolderKey("M", mld_key), "MLD/" + subfolderKey("O", opdr_key), flexsql);
    }
  }
  else
  { // Bestaande opdracht
    // Hardness controle schrijfrechten
    var this_opdr = mld.func_enabled_opdracht(opdr_key); // wat mag ik zoal op deze opdracht
    // Heb ik net de opdracht financieel voltooid, dan moeten de wijzigingen nog opgeslagen worden.
    // De parameter finish geeft aan dat de opdracht net financieel voltooid is.
    // Als extra controle wordt gekeken of ik inderdaad de rechten voor het financieel voltooien had.
    user.auth_required_or_abort(finish? this_opdr.hadFinishRights : this_opdr.canChange);

    if (this_opdr.iamBedrijfContact &&
        ((!finish && this_opdr.canUrenMatChange) || (finish && this_opdr.hadUrenMatChange)) &&
        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 = mld.mld_opdr_info(opdr_key);
    // Functie die bepaald of de opdracht ter fiattering aangeboden moet worden.
    function getToApprove()
    {
      var pkey = -1;
      var toapprove = false;
      if (tot_kosten > opdr_info.t_cost && tot_kosten > 0 && tot_kosten > mld_typeopdr_gvs)
      { // De totale kosten zijn verhoogd en het bedrag zit boven de GVS-grens.
        // 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)
            pkey = mld_info.melder_key;  // Melder/aanvrager van melding
          else
            pkey = user_key; //getFParamInt("behandelaar"); // BO medewerker? dat is gewoon de user
        }

        if (S("mld_use_order_approval") && (tot_kosten > S("can_selfapprove") || budget.exceeds_profiel(tot_kosten, pkey, mld_info.disc)))
        { // 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.
          // Alleen de melder/aanmaker van de melding mag niet zelf goedkeuren boven het bedrag can_selfapprove.
          approver_key = prs.getKpVerantwoordelijke(kostenplaats_key, tot_kosten, pkey);
          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 GVS-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 GVS-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 = getToApprove();
      }

      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") },
                     { dbs: "mld_opdr_materiaal",     typ: "float",   frm: "mat_kosten",   track: L("lcl_mater_cost") },
                     { dbs: "mld_opdr_kosten",        typ: "float",   frm: "tot_kosten",   track: L("lcl_total_cost") },
                     { 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_plandatum",     typ: "datetime",frm: "date_plan",    track: L("lcl_opdr_plandate") },
                     { dbs: "mld_opdr_id",            typ: "varchar", frm: "opdr_id",      track: L("lcl_opdr_id") }
                   ];
      // 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).

      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 (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));
          }
        }

        // Startdatum bestaande opdracht
        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 : startdatebegin);
        }
        // else startdatum niet aanpassen.
        oRs.Close();
        fields.push({ dbs: "mld_opdr_datumbegin", typ: "datetime", val: startdate, track: L("lcl_orderdate") }); // Opdrachtdatum (begindatum)

        var new_einddatum = getFParamDate("date_done", null);
        if (new_einddatum)
        { // 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.
          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", frm: "date_done", track: L("lcl_opdr_enddate") });
          else
          { // Het tijdsveld was niet aanwezig.
            // Is de einddatum aangepast? Vergelijk oude en nieuwe einddatum (zonder tijd)
            var old_einddatum_m = mld_opdr.mld_opdr_einddatum.midnight().getTime();
            var new_einddatum_m = new_einddatum.midnight().getTime();

            if (old_einddatum_m != new_einddatum_m)
            { // 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, track: L("lcl_opdr_enddate") });
            }
            //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 });
      }
      // Met FSN#20132 kan bij bestaande opdrachten via deze weg niet meer het opdrachttype wijzigen
      // Documentfolders hernoemen (RenameFlexFolders) is dus niet meer nodig

      var 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.
    }
    else
    {
      mldUpd = { trackarray: [] };
    }

    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);
      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 het matchtype 5 is en er zijn materialen geselecteerd, dan deze materialen opslaan
  if (mld_typeopdr_matchtype == 5)
  {
    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;
      oRs = 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;
      oRs = 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);
      }
    }
  }

  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);
      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, mld_kenmerkopdr_waarde"
                      + "   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 " + lcl.xsql('t.mld_srtkenmerk_omschrijving', 't.mld_srtkenmerk_key') +" kenmerk_omschrijving"
                      + ",      m.mld_srtkenmerk_key srtkenmerk_key"
                      + ",      fac_kenmerkdomein_key"
                      + "  FROM mld_kenmerk m, "
                      + "       mld_srtkenmerk t"
                      + " WHERE m.mld_srtkenmerk_key = t.mld_srtkenmerk_key"
                      + "   AND m.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",
                                    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),"
                          + "         k.mld_kenmerk_default)";

    sql_insert = "INSERT INTO mld_kenmerkopdr (mld_opdr_key"
               + "                           , mld_kenmerk_key"
               + "                           , mld_kenmerkopdr_waarde)"
               + "                      SELECT " + 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
               + "                         AND k.mld_typeopdr_key = s.mld_typeopdr_key"
               + "                         AND k.mld_kenmerk_niveau = '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 (k.mld_kenmerk_volgnummer <= 100"
               + "                              OR k.mld_kenmerk_volgnummer >= 900)" // Alleen bij of na afmelden mogen afmeld-kenmerken tussen de 100..900
               + "                         AND " + sql_kenmerkwaarde + " IS NOT NULL";
    Oracle.Execute(sql_insert);
  }
}

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"));
  }
  // 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);

  // 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 && tot_kosten < mld_typeopdr_gvs && mld_opdr.opdr_status_refiat > 0)
  { // De opdracht zit in de hergoedkeuring/herfiattering en staat ter goedkeuring.
    // Als de opdracht ter goedkeuring staat en de opdrachtkosten zijn verlaagd tot onder de gvs limiet,
    // dan was hergoedkeuring/herfiattering helemaal niet nodig en kan de status weer terug naar de oorspronkelijke status.
    mld.setopdrachtstatus(opdr_key, mld_opdr.opdr_status_refiat, mld_opdr.uitvoerende_key); // Naar de herfiatteringstatus.

    // 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 && tot_kosten < mld_typeopdr_gvs && (tot_kosten < mld_opdr.approved || tot_kosten < mld_opdr.bestellimiet))
  { // Als de opdracht ter goedkeuring staat en de opdrachtkosten zijn verlaagd tot onder de gvs limiet of onder het al goedgekeurde bedrag,
    // dan is er geen reden meer om goed te keuren.
    var fiat = getToApprove();
    mld.setopdrachtstatus(opdr_key, fiat? 3 : 5, mld_opdr.uitvoerende_key); // Naar de herfiatteringstatus.
  }
}

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++)
    {
      // putorders.sendMLDorders doen we hier niet, altijd scheduled via putorders
      sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = 1 WHERE mld_opdr_key = " + opdrkeyArray[u];
      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"
        + " WHERE prs_bedrijfadres_type = 'O'"
        + "   AND (mld_typeopdr_key IS NULL OR mld_typeopdr_key = " + t_opdr + ")"
        + "   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 || S("mld_opdr_confirm_for_send") != 1) // meldracht
      {
        // Code uit order_mail.asp komen we anders niet.
        var puo_result = putorders.sendMLDorders(opdr_key);
        if (!puo_result)
        {   // mld_opdr_teverzenden is op 1 gezet door sendMLDorders, putorders schedule probeert wel weer
            doneParams.warning = L("lcl_puo_order_failfirst");
        }
      }
      else if (S("mld_opdr_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
    {
      // 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;
      oRs = Oracle.Execute(sql);
      if (!oRs.eof)
        interne_uitvoerder = true;

      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)
                    + "&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;
      }
    }
  }
}

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 'P' pbtype"
            + " FROM prs_perslid p"
            + " WHERE p.prs_perslid_key = " + uitvoerende
            + " UNION"
            + " SELECT 'B' pbtype"
            + " FROM prs_v_aanwezigbedrijf d"
            + " WHERE d.prs_bedrijf_key = " + uitvoerende;
        oRs = Oracle.Execute(sql);
        var pbtype = oRs("pbtype").Value;
        oRs.Close();

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

Response.Write(JSON.stringify(doneParams));
Response.End;
%>