<% /*
    $Revision$
    $Id$

    File:           model_mld_opdr.inc

    Description:    Model voor mld_opdr

    Context:

    Notes:
*/
%>
<!-- #include file="../Shared/discx3d.inc" -->
<!-- #include file="../api2/model_fin_factuur.inc" -->
<!-- #include file="./model_custom_fields.inc"-->
<!-- #include file="./model_mld_kenmerk.inc"-->
<!-- #include file="./model_notes.inc" -->
<!-- #include file="./model_issues_mini.inc" -->
<!-- #include file="./model_mld_opdr_materiaal.inc" -->
<!-- #include file="../bgt/bgt_tools.inc" -->
<%

function model_mld_opdr()
{
    this.records_name = "orders";
    this.record_name = "order";
    this.table = "mld_opdr";
    this.trackcode = "ORDUPD";
    this.primary = "mld_opdr_key";
    this.record_title = L("lcl_fin_mld_opdr");
    this.records_title = L("lcl_fin_opdrachten");

    if (S("bgt_enabled"))
    {
        this.autfunction = "WEB_BGTORD";
    }
    else
    {
        var scope = getQParam("scope", "fe");
        // De autfunction is alleen van belang bij GET en POST.
        this.autfunction = { fe: "WEB_MLDORD", fo: "WEB_ORDBOF", bo: "WEB_ORDBOF", b2: ["WEB_EXTORD", "WEB_ORDBO2"], mi: "WEB_MLDBAC" } [scope];
        // TODO: Zouden de rechten WEB_ORDAFR (afgemelde opdrachten financieel afronden) en WEB_ORDUSE niet meegenomen moeten worden bij de autfunction?
        //       Zijn de rechten WEB_MLDORD geen bo rechten?
        //       Dan zouden we het volgende krijgen?:
        // var autfunction = { fe: "WEB_ORDUSE", fo: "WEB_ORDBOF", bo: ["WEB_ORDBOF", "WEB_ORDAFR", "WEB_MLDORD"], b2: ["WEB_EXTORD", "WEB_ORDBO2", "WEB_ORDAFR"], mi: "WEB_MLDBAC" } [scope];
    }

    var fields_main_1 = {};
    var fields_main_2 = {};
    var fields_bgt_1 = {};

    fields_main_1 = {
        "id": {
            "dbs": "mld_opdr_key",
            "label": L("lcl_key"),
            "typ": "key",
            "hidden_fld": true,
            "required": true,
            "seq": "mld_s_mld_opdr_key"
        }
    };
    if (S("bgt_enabled"))
    {
        fields_bgt_1 = {
            "budgetdiscipline": {
                "dbs": "bgt_project.ins_discipline_key",
                "label": L("bgt_discipline_omschrijving"),
                "typ": "key",
                "required": true,
                "foreign": bgt_budgetdiscipline_foreign(),
                "showtransit": true
            },
            "budgetproject": {
                "dbs": "bgt_project.bgt_project_key",
                "label": L("bgt_project_omschrijving"),
                "typ": "key",
                "required": true,
                "foreign": bgt_budgetproject_foreign(),
                "delayload": true,
                "showtransit": true,
                "multiedit" : true
            },
            "budgetcostcategory": {
                "dbs": "bgt_kostenrubriek.bgt_kostenrubriek_key",
                "label": L("bgt_kostenrubriek_oms"),
                "typ": "key",
                "required": true,
                "foreign": bgt_budgetcostcategory_foreign(),
                "delayload": true,
                "showtransit": true,
                "multiedit" : true
            },
            "costtypegroup": {
                "dbs": "prs_kostensoortgrp.prs_kostensoortgrp_key",
                "label": L("prs_kostensoortgrp_key"),
                "typ": "key",
                "required": true,
                "foreign": bgt_costtypegroup_foreign(),
                "delayload": true,
                "showtransit": true,
                "multiedit" : true
            },
            "costtype": {
                "dbs": "prs_kostensoort_key",
                "label": L("prs_kostensoort_key"),
                "typ": "key",
                "required": true,
                "foreign": bgt_costtype_foreign(),
                "delayload": true,
                "showtransit": true,
                "multiedit" : true
            },
            "estimate": {
                "dbs": "estimate",
                "sql": "(BGT.getRaming(mld_opdr.mld_opdr_key, 0, NULL, NULL))",
                "sqlshow": true,
                "hidden_fld": true,
                "label": "Raming",
                "typ": "float",
                "iscurrency": true,
                "total": true
            },
            "contracted": {
                "dbs": "contracted",
                "sql": "(BGT.getGecontracteerd(mld_opdr.mld_opdr_key, 0, NULL, NULL))",
                "sqlshow": true,
                "hidden_fld": true,
                "label": L("bgt_gecontracteerd"),
                "typ": "float",
                "iscurrency": true,
                "total": true
            },
            "invoiced": {
                "dbs": "mld_opdr_gefactureerd",
                "sql": "(BGT.getGefactureerd(mld_opdr.mld_opdr_key, 0, NULL, NULL))",
                "label": L("bgt_facturen"),
                "typ": "float",
                "readonly": true,
                "sqlshow": true,
                "iscurrency": true,
                "total": true
            },
            "tobeinvoiced": {
                "dbs": "mld_opdr_tefactureren",
                "sql": "(BGT.getTefactureren(mld_opdr.mld_opdr_key, 0, NULL, NULL))",
                "label": L("bgt_facturentogo"),
                "typ": "float",
                "readonly": true,
                "sqlshow": true,
                "iscurrency": true,
                "total": true
            },
            "startdate_range": {
                "dbs": "mld_opdr_datumbegin_range",
                "sql": "mld_opdr_datumbegin",
                "label": L("bgt_contractdatum"),
                "typ": "datetime"
            }
        };
    }

    fields_main_2 = {
         "issue": { "dbs": "mld_melding_key",
                    "typ": "key",
                    "foreignsql": "SELECT TO_CHAR(mld_opdr.mld_melding_key) FROM DUAL",
                    "label": L("lcl_complain") },
        "company": {
            "dbs": "mld_uitvoerende_keys",
            "label": L("lcl_ord_company"),
            "typ": "key",
            "required": true,
            "foreign": bgt_company_foreign(),
            "showtransit": true,
            "track": true
        },
        "reference": {
            "dbs": "mld_opdr_id",
            "label": L("lcl_opdr_id"),
            "typ": "varchar",
            "showtransit": true,
            "track": true
        },
         "ordernr_sort": {
            "dbs": "mld_opdr_ordernr_int",
            "label": S("bgt_enabled")? L("bgt_opdr_ordernr") : L("lcl_mld_inf_Ordernr"),
            "sql": "fac.safe_to_number(mld_opdr_ordernr)",
            "typ": "number",
            "readonly": true
        },
        "ordernr": {
            "dbs": "mld_opdr_ordernr",
            "label": S("bgt_enabled")? L("bgt_opdr_ordernr") : L("lcl_mld_inf_Ordernr"),
            "typ": (S("bgt_enabled")? "number" : "varchar"),
            "hidden_fld": true,
            "track": true
        },
        "sequence": {
            "dbs": "mld_opdr_bedrijfopdr_volgnr",
            "label": L("lcl_ins_volgnr"),
            "typ": "number",
            "defaultvalue": 0,
            "filter": "exact"
        },
        "account": {
            "dbs": "prs_kostenplaats_key",
            "label": S("bgt_enabled")? L("bgt_budget_account") : L("lcl_mld_kosten"),
            "typ": "key",
            "foreign": bgt_account_foreign(),
            "showtransit": true,
            "track": true
        },
        "description": {
            "dbs": "mld_opdr_omschrijving",
            "label": L("lcl_descr"),
            "typ": "varchar",
            "track": true
        },
        "startdate": {
            "dbs": "mld_opdr_datumbegin",
            "label": S("bgt_enabled")? L("bgt_contractdatum") : L("lcl_orderdate"),
            "typ": S("bgt_enabled")? "date" : "datetime",
            "filter": (S("bgt_enabled")? "exact" : "range"),
            "showtransit": (S("bgt_enabled")? true : false),
            "track": true
        }
    };

    fields_order_1 = {
        "enddate": {
            "dbs": "mld_opdr_einddatum",
            "label": L("lcl_opdr_enddate"),
            "typ": "datetime",
            "filter": "range",
            "track": true
        },
        "plandate": {
            "dbs": "mld_opdr_plandatum",
            "label": L("lcl_opdr_plandate"),
            "typ": "datetime",
            "filter": "range",
            "track": true
        },
        "plandate2": {
            "dbs": "mld_opdr_plandatum2",
            "label": L("lcl_opdr_plandate2"),
            "typ": "datetime",
            "filter": "range",
            "track": true
        },
        "contact": {
            "dbs": "prs_contactpersoon_key",
            "label": L("lcl_contact_pers"),
            "typ": "key",
            "foreign": mld_contact_foreign(),
            "track": true
        },
        "costtype": {
            "dbs": "prs_kostensoort_key",
            "label": S("mld_opdr_alt_kostensoort") == 1? L("lcl_mld_opdr_alt_charge_type") : L("lcl_charge_type"),
            "typ": "key",
            "foreign": "prs_kostensoort",
            "readonly": S("mld_opdr_alt_kostensoort") == 1? false : true,
            "track": true
        },
        "hours": {
            "dbs": "mld_opdr_uren",
            "label": L("lcl_mld_nrhours"),
            "typ": "float",
            "track": true
        },
        "hourrate": {
            "dbs": "mld_opdr_uurloon",
            "label": L("lcl_hour_rate"),
            "typ": "float",
            "iscurrency": true,
            "track": true
        },
        "materialcost": {
            "dbs": "mld_opdr_materiaal",
            "label": L("lcl_mater_cost"),
            "typ": "float",
            "iscurrency": true,
            "track": true
        }
    };

    fields_main_3 = {
        "amount": {
            "dbs": "mld_opdr_kosten",
            "label": S("bgt_enabled")? L("bgt_gecontracteerd") : L("lcl_total_cost") + " " + L("lcl_valutasign"),
            "typ": "float",
            "iscurrency": true,
            "total": true,
            "track": true
        },
        "vat": {
            "dbs": "mld_opdr_kosten_btw",
            "label": L("lcl_mld_opdr_btw"),
            "typ": "float",
            "iscurrency": true,
            "total": true,
            "track": true
        },
        "amountincl": {
            "dbs": "mld_opdr_kosten_incl",
            "sql": ( S("bgt_enabled")
                   ? " CASE WHEN mld_opdr.mld_statusopdr_key=10 THEN mld_opdr.mld_opdr_kosten ELSE mld_opdr.mld_opdr_kosten + mld_opdr.mld_opdr_kosten_btw END "
                   : "mld_opdr.mld_opdr_kosten + mld_opdr.mld_opdr_kosten_btw"
                   ),
            "label": L("lcl_fin_total_sum"),
            "typ": "float",
            "readonly": true,
            "sqlshow": true,
            "iscurrency": true,
            "total": true,
            "defaultvalue": "0",
            "clone": false,
            "track": true
        },
        "status": {
            "dbs": "mld_statusopdr_key",
            "label": L("lcl_mld_opdr_status"),
            "typ": "key",
            "foreign": bgt_orderstatus_foreign(),
            "defaultvalue": 5,
            "track": true
        },
        "owner": {
            "dbs": "prs_perslid_key",
            "label": L("lcl_mld_starter"),
            "typ": "key",
            "hidden_fld": true,
            "foreign": "prs_perslid",
            "track": true
        },
        "issueordertype": {
            "dbs": "mld_typeopdr_key",
            "label": S("bgt_enabled")? L("mld_typeopdr_typeopdr_key") : L("lcl_mld_typeopdr"),
            "typ": "key",
            "defaultvalue": "5",
            "foreign": {"tbl": "mld_typeopdr",
                        "key": "mld_typeopdr_key",
                        "desc": "mld_typeopdr_omschrijving"},
            "hidden_fld": true,
            "track": true
        },
        "module": {
            "dbs": "mld_opdr_module",
            "label": "module",
            "typ": "varchar",
            "defaultvalue": "MLD",
            "hidden_fld": true
        },
        "additional": {
            "dbs": "mld_opdr_meerwerk",
            "label": L("lcl_mld_opdr_meerwerk"),
            "typ": "check0",
            "defaultvalue": 0
        },
        "remark": {
            "dbs": "mld_opdr_opmerking",
            "label": L("lcl_remark"),
            "typ": "memo",
            "track": true
        },
        "contract": {
            "dbs": "cnt_contract_key",
            "label": L("lcl_contract"),
            "typ": "key",
            "foreign": "cnt_contract",
            "track": true
        },
        "_is_clone": { // Wordt alleen gebruikt als hidden kopie veld.
            "dbs": "",
            "typ": "number",
            "defaultvalue": 0,
            "hidden_fld": true
        },
        "offer": {
            "dbs": "mld_opdr_parent_key",
            "label": L("lcl_mld_opdr_parent"),
            "typ": "key",
            "track": true
        },
        "externalnr": {
            "dbs": "mld_opdr_externnr",
            "label": "externalnr",
            "typ": "key",
            "readonly": S("bgt_enabled") || !(user.has("WEB_FACTAB") || user.has("WEB_FACXNR"))
        },
        "externalsyncdate": {
            "dbs": "mld_opdr_externsyncdate",
            "label": "externalsyncdate",
            "typ": "datetime",
            "readonly": S("bgt_enabled") || !(user.has("WEB_FACTAB") || user.has("WEB_FACXNR"))
        }
    };

    // Backwards compatible
    if ((S("fac_api2_compatibility") & 1) == 1)
        delete fields_main_3.issueordertype["foreign"];

    this.fields = object_merge({}, fields_main_1
                                 , (S("bgt_enabled") ? fields_bgt_1 : {})
                                 , fields_main_2
                                 , (S("bgt_enabled") ? {} : fields_order_1)
                                 , fields_main_3
                              );

    this.includes= {
                    "notes": {
                           model: new model_notes("ORD"),
                           joinfield: "parent"
                         },
                    "custom_fields" : {
                        "model": new model_custom_fields(this, new model_mld_kenmerk('O', { internal: true }), { pNiveau: "O" }),
                        "joinfield": "flexparentkey",
                        "enable_update": true
                    },
                    "materials": {
                        "model": new model_mld_opdr_materiaal(),
                        joinfield: "order"
                    },
                    "issuedetails": {
                        "model": new model_issues_mini(),
                        "isdetailfor": "issue",
                        "joinfunction": function ()
                            {
                                return { tables: ["mld_melding m2"],
                                         where: "mld_opdr.mld_melding_key = m2.mld_melding_key"
                                       }
                            }
                    }
    };

    this.hook_pre_show = function(obj, fld, scf_params)
    {
        if ( S("bgt_enabled"))
        {
            this.fields.vat.sql = " CASE WHEN mld_opdr.mld_statusopdr_key=10 THEN 0 ELSE mld_opdr.mld_opdr_kosten_btw END ";
            obj.amountincl = obj.amount + obj.vat;
        }
    }

    // De pre_multi_edit functie wordt alleen aangeroepen bij scaffolding (scaffolding_edit, scaffolding_m_edit) en niet bij API2 (generic_REST_XXX) aanroepen.
    this.hook_pre_edit = function (obj, fld, scf_params)
    {
        obj.amountincl = ((isNaN(obj.amount) || isNaN(obj.vat)) ? "" : (obj.amount + obj.vat));
        obj._is_clone = getQParamInt("scf_clone", 0);
        var mld_opdr_id = (obj._is_clone == 1 ? getQParamInt("id", -1) : obj.id);
        var btw_data = {};
        if (obj.id > -1)
        {
            fld.ordernr.hidden_fld = false;
        }
        if (obj._is_clone)
        {
            fld.company.hidden_fld = true;
        }
        if (!obj.status)
        {
            fld.status.foreign.tbl = bgt_orderstatus_foreign_tbl(true);
            if (obj._is_clone)
                obj.status = fld.status.defaultvalue;
        }

        if (mld_opdr_id > -1)
        {
            var sql = "SELECT s.prs_kostensoort_btw"
                    + "     , f.fin_btwtabelwaarde_perc"
                    + "     , (SELECT COUNT(*)"
                            + "  FROM prs_bedrijf pb"
                            + "     , fin_btwtabel fb"
                            + " WHERE pb.fin_btwtabel_key = fb.fin_btwtabel_key"
                            + "   AND pb.prs_bedrijf_key = o.mld_uitvoerende_keys"
                            + "   AND fb.fin_btwtabel_default IS NULL"
                            + ") btw_off"
                    + "  FROM mld_opdr o"
                    + "     , prs_kostensoort s"
                    + "     , fin_btwtabelwaarde f"
                    + " WHERE s.fin_btwtabelwaarde_key = f.fin_btwtabelwaarde_key"
                    + "   AND o.prs_kostensoort_key = s.prs_kostensoort_key"
                    + "   AND o.mld_opdr_key = " + mld_opdr_id;
            var oRs = Oracle.Execute(sql);
            if (!oRs.eof)
            {
                btw_data.btw_inc = oRs("prs_kostensoort_btw").Value;
                btw_data.btw_val = oRs("fin_btwtabelwaarde_perc").Value || "0";
                btw_data.btw_off = oRs("btw_off").Value;
            }
            oRs.Close();

/*
TWYN#53173
De bestaande knop "Kopiëren" is komen te vervallen. Daarvoor in de plaats zijn twee nieuwe knoppen toegevoegd: "Nieuwe opdracht" en "nieuwe subopdracht".
Bij een nieuwe subopdracht is het opdrachtnummer gelijk aan die van de opdracht van waaruit gekopieerd wordt en is niet te wijzigen. Het volgnummer staat standaard op volgnr(max+1).
            if (obj._is_clone == 2))  // nieuw subcontract: bepaal hoogste volgnummer bij dit contract, en vul deze alvast in.
            {
                var sql = "SELECT COALESCE(MAX(s.mld_opdr_bedrijfopdr_volgnr), 0) + 1 volgnr"
                        + "  FROM mld_opdr s"
                        + " WHERE s.mld_opdr_ordernr ="
                        + "  (SELECT m.mld_opdr_ordernr"
                        + "     FROM mld_opdr m"
                        + "    WHERE m. mld_opdr_key = " + mld_opdr_id
                        + "  )";
                var oRs = Oracle.Execute(sql);
                obj.sequence = oRs("volgnr").Value;
                oRs.Close();
            }
*/
        }
        else
        {
            if (obj.costtype && obj.costtype.id)
            {
                // Bepaal voor de geselecteerde kostensoort de btw-percentage.
                var sql = "SELECT prs_kostensoort_btw"
                        + "     , fin_btwtabelwaarde_perc"
                        + "  FROM bgt_kostenrubriek r"
                        + "     , prs_kostensoortgrp g"
                        + "     , prs_kostensoort s"
                        + "     , fin_btwtabelwaarde w"
                        + " WHERE r.bgt_kostenrubriek_key = g.bgt_kostenrubriek_key"
                        + "   AND g.prs_kostensoortgrp_key = s.prs_kostensoortgrp_key"
                        + "   AND w.fin_btwtabelwaarde_key = COALESCE(s.fin_btwtabelwaarde_key, r.fin_btwtabelwaarde_key)"
                        + "   AND s.prs_kostensoort_key = " + obj.costtype.id;
                var oRs = Oracle.Execute(sql);
                btw_data.btw_inc = oRs("prs_kostensoort_btw").Value;
                btw_data.btw_val = oRs("fin_btwtabelwaarde_perc").Value || "0";
            }

            if (obj.company && obj.company.id)
            {
                // Is bedrijf waarop gefilterd is vrijgesteld van btw.
                var sql = "SELECT COUNT(*) btw_off"
                        + "  FROM prs_bedrijf pb"
                        + "     , fin_btwtabel fb"
                        + " WHERE pb.fin_btwtabel_key = fb.fin_btwtabel_key"
                        + "   AND pb.prs_bedrijf_key = " + obj.company.id
                        + "   AND fb.fin_btwtabel_default IS NULL";
                var oRs = Oracle.Execute(sql);
                // btw_off=[0|1] : 1 = bedrijf is vrijgesteld van btw (verlegd)
                btw_data.btw_off = oRs("btw_off").Value;
                oRs.Close();
            }
        }
        %>
        <script>
            var btw_data = <%=JSON.stringify(btw_data)%>;
            var budgetdiscipline_key   = <%=(obj.budgetdiscipline   ? obj.budgetdiscipline.id   : -1)%>;
            var budgetproject_key      = <%=(obj.budgetproject      ? obj.budgetproject.id      : -1)%>;
            var budgetcostcategory_key = <%=(obj.budgetcostcategory ? obj.budgetcostcategory.id : -1)%>;
            var costtypegroup_key      = <%=(obj.costtypegroup      ? obj.costtypegroup.id      : -1)%>;
            var costtype_key           = <%=(obj.costtype           ? obj.costtype.id           : -1)%>;
            var company_key            = <%=(obj.company            ? obj.company.id            : -1)%>;
            var account_key            = <%=(obj.account            ? obj.account.id            : -1)%>;
            var mld_opdr_key           = <%=(obj.id                 ? obj.id                    : -1)%>;
        </script>
        <%
    }

    this.hook_pre_multi_edit = function (obj, fld, scf_params)
    {
        // Zet de initiele waarden voor de listboxen van het multi-edit scherm.
        var budgetdiscipline    = getQParamInt("budgetdiscipline", -1);
        var budgetproject       = getQParamInt("budgetproject", -1);
        var budgetcostcategory  = getQParamInt("budgetcostcategory", -1);
        var budgetcosttypegroup = getQParamInt("costtypegroup", -1);
        if (budgetdiscipline == -1)
            abort_with_warning("In de zoekopdracht moet op een project gefilterd zijn.");

        var sql = "SELECT DISTINCT kp.ins_discipline_key discipline"
                + "     , CASE WHEN " + budgetproject       + " = -1 THEN -1 ELSE kp.bgt_project_key        END project"
                + "     , CASE WHEN " + budgetcostcategory  + " = -1 THEN -1 ELSE kr.bgt_kostenrubriek_key  END rubriek"
                + "     , CASE WHEN " + budgetcosttypegroup + " = -1 THEN -1 ELSE kg.prs_kostensoortgrp_key END groep"
                + "  FROM prs_kostensoortgrp kg"
                + "     , bgt_kostenrubriek  kr"
                + "     , bgt_project        kp"
                + " WHERE kg.bgt_kostenrubriek_key = kr.bgt_kostenrubriek_key"
                + "   AND kr.bgt_project_key = kp.bgt_project_key"
                + "   AND kp.ins_discipline_key = " + budgetdiscipline
                + (budgetproject       == -1 ? "" : " AND kp.bgt_project_key = " + budgetproject)
                + (budgetcostcategory  == -1 ? "" : " AND kr.bgt_kostenrubriek_key = " + budgetcostcategory)
                + (budgetcosttypegroup == -1 ? "" : " AND kg.prs_kostensoortgrp_key = " + budgetcosttypegroup)
        var oRs = Oracle.Execute(sql);
        if (!oRs.eof)
        {
            budgetdiscipline    = oRs("discipline").Value;
            budgetproject       = oRs("project").Value;
            budgetcostcategory  = oRs("rubriek").Value;
            budgetcosttypegroup = oRs("groep").Value;
        }
        oRs.Close();

        %>
        <script>
            var budgetdiscipline_overrule = <%=(budgetdiscipline)%>;
            var budgetproject_overrule    = <%=(budgetproject)%>;
            var budgetrubriek_overrule    = <%=(budgetcostcategory)%>;
            var kostensoortgroep_overrule = <%=(budgetcosttypegroup)%>;
        </script>
        <%
    }

    // De pre_post functie wordt alleen aangeroepen bij scaffolding (scaffolding_save) en niet bij API2 POST (generic_REST_POST) aanroepen.
    this.hook_pre_post = function(params, obj)
    {
        var isclone = (obj._is_clone == 1);
        if (!obj.sequence && obj.sequence!=0)
            abort_with_warning("Vul een volgnummer voor het contract in.");

        var v_costtype = (typeof obj.costtype == "object" ? obj.costtype.id : obj.costtype);
        checkAanwezigBudget(v_costtype);
        getBudget(v_costtype);           // Hierin wordt ook gecontroleerd of er een reservebudget bestaat. Zo niet, dan stoppen.

/*
TWYN#53173  : waarschijnlijk pas met 2018.1
obj._is_clone=1 --> nieuwe (hoofd) opdracht
obj._is_clone=2 --> nieuwe sub-opdracht
*/
        if (isclone)  // Bij kopie van opdracht geen nieuw nummer genereren.
        {
            var v_project_key = (typeof obj.budgetproject == "object" ? obj.budgetproject.id : obj.budgetproject);
            // Vul het contractnummer van het oorspronkelijke contract in.
            var parent_key = getQParamInt("id", -1);
            var sql = "SELECT o.mld_opdr_ordernr"
                    + "  FROM mld_opdr o"
                    + " WHERE o.mld_opdr_key = " + parent_key;
            var oRs = Oracle.Execute(sql);
            obj.ordernr = oRs("mld_opdr_ordernr").Value;
            oRs.Close();
            // Kopie contract. Volgnummer moet uniek zijn binnen dit contractnummer.
            checkUniekContractnummer(-1, v_project_key, obj.ordernr, obj.sequence);
        }
        else
        {
            // Genereer een uniek volgnummer binnen het project voor een nieuwe opdracht.
            var prj_key = (obj.budgetproject ? obj.budgetproject : project_key);
            var sql = "SELECT COALESCE(MAX(TO_NUMBER(o.mld_opdr_ordernr)), 0) + 1 new_ordernr"
                    + "  FROM bgt_project p"
                    + "     , bgt_kostenrubriek r"
                    + "     , prs_kostensoortgrp g"
                    + "     , prs_kostensoort s"
                    + "     , mld_opdr o"
                    + " WHERE p.bgt_project_key = r.bgt_project_key"
                    + "   AND r.bgt_kostenrubriek_key = g.bgt_kostenrubriek_key"
                    + "   AND g.prs_kostensoortgrp_key = s.prs_kostensoortgrp_key"
                    + "   AND s.prs_kostensoort_key = o.prs_kostensoort_key"
                    + "   AND p.bgt_project_key = " + prj_key;
            var oRs = Oracle.Execute(sql);
            obj.ordernr = oRs("new_ordernr").Value;
            oRs.Close();
            // Nieuw contract, dus volgnummer is altijd uniek hierbinnen.
        }
    }

    this.hook_post_post = function(params, obj, key)
    {
        var v_prj_changed = false;
        var v_costtype = (typeof obj.costtype == "object" ? obj.costtype.id : obj.costtype);
        var v_budget = getBudget(v_costtype);
        if (obj.additional == 0)
        {
            var v_datamutatie = checkBudgetoverschrijding(obj.id, v_budget, obj.amount, obj.vat, obj.startdate, v_prj_changed);
            insertMutatie( v_datamutatie, "AO" );
        }
        else
        {
            insertMutatie( { budget_res_key: v_budget.budget_res_key
                           , bedrag_exc: -1 * obj.amount
                           , bedrag_btw: -1 * obj.vat
                           , budget_srt_key: v_budget.budget_srt_key
                           , contract_datum: obj.startdate
                           }
                         , "MW"
                         );
        }
    }

    // Deze hook is al multi-ready gemaakt (accepteert een leeg obj)
    // De pre_put functie wordt alleen aangeroepen bij scaffolding (scaffolding_save) en niet bij API2 PUT (generic_REST_PUT) aanroepen.
    this.hook_pre_put = function(params, obj, key)
    {
        // Is de combinatie opdrachtnummer/volgnummer nog steeds uniek?
        // Wat zijn de oude gegevens?
        var sql = "SELECT p.bgt_project_key"
                + "     , o.prs_kostensoort_key"
                + "     , o.mld_opdr_ordernr"
                + "     , o.mld_opdr_meerwerk"
                + "     , o.mld_opdr_kosten"
                + "     , o.mld_opdr_kosten_btw"
                + "     , o.mld_opdr_bedrijfopdr_volgnr"
                + "  FROM bgt_project p"
                + "     , bgt_kostenrubriek r"
                + "     , prs_kostensoortgrp g"
                + "     , prs_kostensoort s"
                + "     , mld_opdr o"
                + " WHERE p.bgt_project_key = r.bgt_project_key"
                + "   AND r.bgt_kostenrubriek_key = g.bgt_kostenrubriek_key"
                + "   AND g.prs_kostensoortgrp_key = s.prs_kostensoortgrp_key"
                + "   AND s.prs_kostensoort_key = o.prs_kostensoort_key"
                + "   AND o.mld_opdr_key = " + key;
        var oRs = Oracle.Execute(sql);
        var v_cur_project_key  = oRs("bgt_project_key").Value;
        var v_cur_costtype_key = oRs("prs_kostensoort_key").Value;
        var v_cur_ordernr      = oRs("mld_opdr_ordernr").Value;
        var v_cur_sequence     = oRs("mld_opdr_bedrijfopdr_volgnr").Value;
        var v_cur_amount       = oRs("mld_opdr_kosten").Value;
        var v_cur_vat          = oRs("mld_opdr_kosten_btw").Value;
        var v_meerwerk         = oRs("mld_opdr_meerwerk").Value;
        oRs.Close();

        // Sommige waarden worden in multiedit niet meegestuurd, deze hier specifiek setten als zijnde de oude waarde
        var id                 = obj.id            || key;
        var v_new_project_key  = obj.budgetproject || v_cur_project_key;
        var v_new_costtype_key = obj.costtype      || v_cur_costtype_key;
        var v_new_ordernr      = (obj.ordernr ? obj.ordernr.toString() : v_cur_ordernr);
        var v_new_sequence     = ((obj.sequence || obj.sequence == 0) ? obj.sequence : v_cur_sequence);
        var v_new_amount       = ((obj.amount   || obj.amount   == 0) ? obj.amount   : v_cur_amount);
        var v_new_vat          = ((obj.vat      || obj.vat      == 0) ? obj.vat      : v_cur_vat);
        var v_new_additional   = obj.additional;
        var v_project_changed  = (v_cur_project_key != v_new_project_key);

        checkAanwezigBudget(v_new_costtype_key);
        checkUniekContractnummer(key, v_new_project_key, v_new_ordernr, v_new_sequence);

        // Om te voorkomen dat er wel budgetmutaties worden doorgevoerd terwijl nog niet alle gegevens van mld_opdr zijn ingevuld
        // eerst controleren op required van database velden. TWYN#65425
        check_required(this, obj);

        // __Log(">> wijzigen op huidig kostensoort");
        if (obj.additional == 1)
        {
            if (v_meerwerk == 1)
            {   // Opdracht is en blijft meer-minderwerk.
                // Mutatie is dan alleen het verschil tussen oude en nieuwe waarde van de opdracht.
                // __Log("W -> W");
                var v_budget_old = getBudget(v_cur_costtype_key);
                var v_ext_dif = v_new_amount - v_cur_amount;
                var v_btw_dif = v_new_vat - v_cur_vat;
                insertMutatie( { budget_res_key: v_budget_old.budget_res_key
                               , budget_srt_key: v_budget_old.budget_srt_key
                               , bedrag_exc: -1 * v_ext_dif
                               , bedrag_btw: -1 * v_btw_dif
                               , contract_datum: obj.startdate
                               }
                             , "MW"
                             );
            }
            else
            {   // Het was een gewone opdracht, maar wordt nu een meer-minderwerk opdracht.
                // Dus eerst kijken of er nog automatische overboeking AO tgv budgetoverschrijding terug geboekt moet worden naar reserve
                // en daarna een meer-minderwerk W mutatie vanuit reserve naar het budget.
                // __Log("A -> W");
                var v_budget_old = getBudget(v_cur_costtype_key);
                var v_datamutatie = checkBudgetoverschrijding(id, v_budget_old, 0, 0, obj.startdate, v_project_changed);
                insertMutatie( v_datamutatie, "AO" );
                //
                insertMutatie( { budget_res_key: v_budget_old.budget_res_key
                               , budget_srt_key: v_budget_old.budget_srt_key
                               , bedrag_exc: -1 * v_new_amount
                               , bedrag_btw: -1 * v_new_vat
                               , contract_datum: obj.startdate
                               }
                             , "MW"
                             );
            }
        }
        else
        {
            if (v_meerwerk == 1)
            {   // Een meer-minderwerk opdracht wordt omgezet naar een gewone opdracht.
                // Boek het meer-minderwerk bedrag terug met een W-mutatie.
                // Kijk daarna of er een automatische overboeking AO nodig is om het budget niet negatief te laten worden.
                // __Log("W -> A");
                var v_budget_old = getBudget(v_cur_costtype_key);
                insertMutatie( { budget_res_key: v_budget_old.budget_res_key
                               , budget_srt_key: v_budget_old.budget_srt_key
                               , bedrag_exc: v_cur_amount
                               , bedrag_btw: v_cur_vat
                               , contract_datum: obj.startdate
                               }
                             , "MW"
                             );
                //
                var v_budget_old = getBudget(v_cur_costtype_key); // Opnieuw: insertMutatie heeft budget zojuist aangepast.
                v_budget_old.meerwerk = obj.additional;
                var v_datamutatie = checkBudgetoverschrijding(id, v_budget_old, v_new_amount, v_new_vat, obj.startdate, v_project_changed);
                insertMutatie( v_datamutatie, "AO" );
            }
            else
            {   // Opdracht is en blijft een gewone opdracht.
                // Kijk of er tgv het wijziging van het bedrag nog een automatische overboeking AO plaats moet vinden
                // __Log("A -> A");
                var v_budget_old = getBudget(v_cur_costtype_key);
                var v_datamutatie = checkBudgetoverschrijding(id, v_budget_old, v_new_amount, v_new_vat, obj.startdate, v_project_changed);
                insertMutatie( v_datamutatie, "AO" );
            }
        }
        // Overige opdrachtgegevens worden nu aangepast.
        // __Log("<< wijzigen op huidig kostensoort");

        // __Log(">> wijzigen kostensoort");
        if (v_cur_costtype_key != v_new_costtype_key) // Opdracht veranderd van kostensoort.
        {
            // Is dit contract het hoofdcontract.
            sql = "SELECT mld_opdr_bedrijfopdr_volgnr"
                + "  FROM mld_opdr"
                + " WHERE mld_opdr_key = " + key;
            var oRs = Oracle.Execute(sql);
            var isHoofdcontract = (oRs("mld_opdr_bedrijfopdr_volgnr").Value == 0);
            oRs.Close();
            if (isHoofdcontract)
            {
            // Heeft dit contract ook nog subcontracten
            var sql = "SELECT so.mld_opdr_key"
                        + "     , sp.bgt_project_key"
                    + "     , so.prs_kostensoort_key"
                    + "     , so.mld_opdr_kosten"
                    + "     , so.mld_opdr_kosten_btw"
                    + "     , so.mld_opdr_meerwerk"
                    + "  FROM mld_opdr co"
                    + "     , mld_opdr so"
                    + "     , bgt_v_project_full cp"
                    + "     , bgt_v_project_full sp"
                    + " WHERE co.prs_kostensoort_key = cp.prs_kostensoort_key"
                    + "   AND so.prs_kostensoort_key = sp.prs_kostensoort_key"
                    + "   AND co.mld_opdr_ordernr = so.mld_opdr_ordernr"
                    + "   AND cp.bgt_project_key = sp.bgt_project_key"
                        + "   AND so.mld_opdr_key <> " + key
                    + "   AND co.mld_opdr_key = " + key;
            var oRs = Oracle.Execute(sql);
            while (!oRs.eof)
            {
                // verplaatsen subcontracten
                    var r_cur_opdr_key = oRs("mld_opdr_key").Value;
                    if (r_cur_opdr_key != key)
                {
                        var r_cur_amount = oRs("mld_opdr_kosten").Value;
                        var r_cur_var    = oRs("mld_opdr_kosten_btw").Value;
                        var r_cur_costtype_key = oRs("prs_kostensoort_key").Value;
                        var r_cur_project_key = oRs("bgt_project_key").Value;
                        var r_cur_additional = oRs("mld_opdr_meerwerk").Value; // meer-minderwerk veranderd hier niet bij wijzigen van kostensoort
                        var v_cur = { additional: r_cur_additional, project_key: r_cur_project_key, costtype_key: r_cur_costtype_key, amount: v_cur_amount, vat: v_cur_vat };
                        var v_new = { additional: v_new_additional, project_key: v_new_project_key, costtype_key: v_new_costtype_key, amount: v_new_amount, vat: v_new_vat };
                        wijzigKostensoortOpdracht(r_cur_opdr_key, obj.startdate, v_cur, v_new);
                    //
                    // subcontracten veranderen van kostensoort
                    var sql = "UPDATE mld_opdr"
                            + "   SET prs_kostensoort_key = " + v_new_costtype_key
                                + "     , mld_opdr_ordernr = " + obj.ordernr
                                + " WHERE mld_opdr_key = " + r_cur_opdr_key;
                    Oracle.Execute(sql);
                }
                oRs.MoveNext();
            }
            oRs.Close();
            }
            // Alleen het huidige (hoofd)contract
            var v_cur = { additional: obj.additional, project_key: v_cur_project_key, costtype_key: v_cur_costtype_key, amount: v_cur_amount, vat: v_cur_vat };
            var v_new = { additional: obj.additional, project_key: v_new_project_key, costtype_key: v_new_costtype_key, amount: v_new_amount, vat: v_new_vat };
            wijzigKostensoortOpdracht(key, obj.startdate, v_cur, v_new);
            // De opdracht zelf wordt nu aangepast, dus ook de kostensoort.
        }
        // __Log("<< wijzigen kostensoort");
    }

    // De pre_delete functie wordt alleen aangeroepen bij scaffolding (scaffolding_delete) en niet bij API2 DELETE (generic_REST_DELETE) aanroepen.
    this.hook_pre_delete = function(params, key)
    {
        var sql = "SELECT MAX(o.mld_opdr_datumbegin) contractdatum"
                + "     , MAX(o.prs_kostensoort_key) kostensoort"
                + "     , MAX(o.mld_opdr_meerwerk)   meerwerk"
                + "     , MAX(mld_opdr_kosten)       contract_exc"
                + "     , MAX(mld_opdr_kosten_btw)   contract_btw"
                + "     , COUNT(f.fin_factuur_key)   aantal"
                + "  FROM mld_opdr    o"
                + "     , fin_factuur f"
                + " WHERE o.mld_opdr_key = f.mld_opdr_key(+)"
                + "   AND o.mld_opdr_key = " + key;
        var oRs = Oracle.Execute(sql);
        var aantal_facturen = oRs("aantal").Value;
        var prs_kostensoort = oRs("kostensoort").Value;
        var meerwerk = oRs("meerwerk").Value == 1;
        var contract_exc = oRs("contract_exc").Value;
        var contract_btw = oRs("contract_btw").Value;
        var contract_datum = new Date(oRs("contractdatum").Value);
        oRs.Close();
        if (aantal_facturen > 0)
            abort_with_warning("Verwijder eerst de facturen bij dit contract.");

        // Eerst proberen of er automatisch terug geboekt moet worden door bedrag te muteren naar 0.
        var v_budget_old = getBudget(prs_kostensoort);
        if (meerwerk)
        {
            insertMutatie( { budget_res_key: v_budget_old.budget_res_key
                           , bedrag_exc: contract_exc
                           , bedrag_btw: contract_btw
                           , budget_srt_key: v_budget_old.budget_srt_key
                           , contract_datum: contract_datum
                           }
                         , "MW"
                         );
        }
        else
        {
            var prj_changed = false;
            var v_datamutatie = checkBudgetoverschrijding(key, v_budget_old, 0, 0, contract_datum, prj_changed);
            insertMutatie( v_datamutatie, "AO" );
        }
    }


    var gparams = {};
    if (S("bgt_enabled") == 1)
    {
        gparams = {
            GET: {
                tables: [
                    "prs_kostensoort",
                    "prs_kostensoortgrp",
                    "bgt_kostenrubriek",
                    "bgt_project"
                ],
                wheres: [
                    "mld_opdr.prs_kostensoort_key = prs_kostensoort.prs_kostensoort_key",
                    "prs_kostensoort.prs_kostensoortgrp_key = prs_kostensoortgrp.prs_kostensoortgrp_key",
                    "prs_kostensoortgrp.bgt_kostenrubriek_key = bgt_kostenrubriek.bgt_kostenrubriek_key",
                    "bgt_kostenrubriek.bgt_project_key = bgt_project.bgt_project_key"
                ]
            }
        };

        var v_budgetdiscipline = getQParamInt("budgetdiscipline", -1);
        if (v_budgetdiscipline == -1)
        {
            gparams.GET.tables.push("ins_tab_discipline");
            gparams.GET.wheres.push("bgt_project.ins_discipline_key = ins_tab_discipline.ins_discipline_key");
            gparams.GET.wheres.push(
                    "bgt_project.ins_discipline_key IN"
                    + " (SELECT w.ins_discipline_key"
                    + "    FROM fac_v_webgebruiker w"
                    + "       , ins_tab_discipline d"
                    + "   WHERE w.ins_discipline_key = d.ins_discipline_key"
                    + "     AND w.prs_perslid_key = " + user_key
                    + "     AND (w.fac_gebruiker_prs_level_read<9 OR w.fac_gebruiker_alg_level_read<9)"
                    + "     AND d.ins_discipline_module = 'BGT'"
                    + "     AND d.ins_discipline_verwijder IS NULL"
                    + " )"
            );
        }
    }

    function mld_contact_foreign()
    {
      var tbl = "(SELECT prs_contactpersoon_key"
              + "      , " + S("prs_contactpers_string") + " naam"
              + "   FROM prs_contactpersoon cp"
              + "  WHERE prs_contactpersoon_verwijder IS NULL)";
      var foreign = {"tbl": tbl,
                     "key": "prs_contactpersoon_key",
                     "desc": "naam"
                    };
      return foreign;
    }

    function _checkAutorisation(pthis, pparams)  // The parameters pthis and pparams are objects. Object are passed by reference.
    {
      var canWrite = false;
      var result = {};
      if (S("bgt_enabled") == 1)
      {
        var autfunction = "WEB_BGTORD";
        pparams.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch
        result.authparams = pparams.authparams;
        result.canRead  = (pparams.authparams.PRSreadlevel  < 9 && pparams.authparams.ALGreadlevel  < 9);
        result.canWrite = (pparams.authparams.PRSwritelevel < 9 && pparams.authparams.ALGwritelevel < 9);
        result.canReadWrite = result.canRead || result.canWrite;
      }
      else
      {
        if (pparams.filter.id > 0)
        {
          var this_opdr = mld.func_enabled_opdracht(pparams.filter.id);
          user.auth_required_or_abort(this_opdr.canReadAny || this_opdr.canChange);
          pthis.autfunction = false;
          result.authparams = null;
          result.canRead  = this_opdr.canReadAny;
          result.canWrite = this_opdr.canChange;
          result.canReadWrite = result.canRead || result.canWrite;
        }
        else
        { // Alleen met een GET en POST kan de id niet meegegeven zijn.
          var scope = pparams.filter.scope || "fe";
          var autfunction = { fe: "WEB_MLDORD", fo: "WEB_ORDBOF", bo: "WEB_ORDBOF", b2: ["WEB_EXTORD","WEB_ORDBO2"], mi: "WEB_MLDBAC" } [scope];
          // TODO: Zouden de rechten WEB_ORDAFR (afgemelde opdrachten financieel afronden) en WEB_ORDUSE niet meegenomen moeten worden bij de autfunction?
          //       Zijn de rechten WEB_MLDORD geen bo rechten?
          //       Dan zouden we het volgende krijgen?:
          //var autfunction = { fe: "WEB_ORDUSE", fo: "WEB_ORDBOF", bo: ["WEB_ORDBOF", "WEB_ORDAFR", "WEB_MLDORD"], b2: ["WEB_EXTORD", "WEB_ORDBO2", "WEB_ORDAFR"], mi: "WEB_MLDBAC" } [scope];
          pparams.authparams = user.checkAutorisation(autfunction, null, null, true); // pessimistisch
          // Hier kom je niet meer terecht als bovenstaande autorisatiecheck niet tot succes leidt.
          result.authparams = pparams.authparams;
          result.canRead  = (pparams.authparams.PRSreadlevel  < 9 && pparams.authparams.ALGreadlevel  < 9);
          result.canWrite = (pparams.authparams.PRSwritelevel < 9 && pparams.authparams.ALGwritelevel < 9);
          result.canReadWrite = result.canRead || result.canWrite;
        }
      }
      return result; // Wordt in model gebruikt om te bepalen of de functies/knoppen Toevoegen en Verwijderen aanwezig moeten zijn.
    }

    function getBudget(data_costtype)
    {
        var sql = "SELECT MAX(b.bgt_budget_key)  budget_s"
                + "     , MAX(c.bgt_budget_key)  budget_r"
                + "     , MAX(COALESCE(b.bgt_budget_bedrag, 0)) budget_exc"
                + "     , MAX(COALESCE(b.bgt_budget_btwbedrag, 0)) budget_btw"
                + "  FROM bgt_budget b"
                + "     , bgt_budgetmutatie m"
                + "     , (SELECT r.bgt_project_key"
                      + "       , bgt_budget_key"
                      + "    FROM bgt_budget r"
                      + "    WHERE r.bgt_budget_isreserve = 1"
                      + " ) c"
                + " WHERE b.bgt_project_key = c.bgt_project_key(+)"
                + "   AND b.prs_kostensoort_key = " + data_costtype;
        var oRs = Oracle.Execute(sql);
        var budget_key_res = oRs("budget_r").Value;
        var budget_key_srt = oRs("budget_s").Value;
        var budget_exc     = oRs("budget_exc").Value;
        var budget_btw     = oRs("budget_btw").Value;
        oRs.Close();
        if (budget_key_res == null)
            abort_with_warning("Er is geen budget met kenmerk 'reserve' aanwezig voor dit project");
        else
            return { budget_res_key: budget_key_res
                   , budget_srt_key: budget_key_srt
                   , costtype_key: data_costtype
                   , bedrag_exc: budget_exc
                   , bedrag_btw: budget_btw
                   };
    }

    function checkUniekContractnummer(p_contract_key, p_project_key, p_contractnr, p_volgnr)
    {
        var sql = "SELECT count(*) aantal"
                + "  FROM bgt_project p"
                + "     , bgt_kostenrubriek r"
                + "     , prs_kostensoortgrp g"
                + "     , prs_kostensoort s"
                + "     , mld_opdr o"
                + " WHERE p.bgt_project_key = r.bgt_project_key"
                + "   AND r.bgt_kostenrubriek_key = g.bgt_kostenrubriek_key"
                + "   AND g.prs_kostensoortgrp_key = s.prs_kostensoortgrp_key"
                + "   AND s.prs_kostensoort_key = o.prs_kostensoort_key"
                + "   AND p.bgt_project_key = " + p_project_key
                + "   AND o.mld_opdr_ordernr = " + safe.quoted_sql(p_contractnr)
                + "   AND o.mld_opdr_bedrijfopdr_volgnr = " + p_volgnr
                + (p_contract_key != -1 ? "   AND o.mld_opdr_key <> " + p_contract_key : "");
        var oRs = Oracle.Execute(sql);
        var uniek_contractnr = (oRs("aantal").Value == 0);
        oRs.Close();
        if (!uniek_contractnr)
            abort_with_warning("Contractnummer {0}.{1} bestaat al in dit project.".format(p_contractnr, p_volgnr));
    }

    function checkAanwezigBudget(p_kostensoort_key)
    {
        // kostensoort moet een budget hebben, anders mag er geen contract aan gekoppeld worden.
        var sql = "SELECT count(*) aantal"
                + "  FROM bgt_budget"
                + " WHERE prs_kostensoort_key = " + p_kostensoort_key;
        var oRs = Oracle.Execute(sql);
        var heeft_budget = oRs("aantal").Value == 1;
        oRs.Close();
        if (!heeft_budget)
        {
            if (false) // Budget niet automatisch aanmaken.
            {
                abort_with_warning("Maak eerst een budget aan voor deze kostensoort voordat er contracten aan gekoppeld worden.");
            }
            else
            {
                var sql_ins = "INSERT INTO bgt_budget"
                            + "( ins_discipline_key"
                            + ", bgt_project_key"
                            + ", bgt_kostenrubriek_key"
                            + ", prs_kostensoortgrp_key"
                            + ", prs_kostensoort_key"
                            + ", bgt_budget_bedrag"
                            + ", bgt_budget_btwbedrag"
                            + ", bgt_budget_isreserve"
                            + ") SELECT ins_discipline_key"
                            + "       , bgt_project_key"
                            + "       , bgt_kostenrubriek_key"
                            + "       , prs_kostensoortgrp_key"
                            + "       , prs_kostensoort_key"
                            + "       , 0"
                            + "       , 0"
                            + "       , 0"
                            + "    FROM bgt_v_project_full"
                            + "   WHERE prs_kostensoort_key = " + p_kostensoort_key;
                 Oracle.Execute(sql_ins);
            }
        }
    }

    function checkBudgetoverschrijding(data_id, data_budget, data_amount, data_vat, contract_datum, prj_changed)
    {
        var amount_as_new  = (typeof data_budget.meerwerk == "undefined" ? false : (data_budget.meerwerk == 0));
        var amount_old_exc = 0;
        var amount_old_btw = 0;
        var sql = "SELECT SUM(o.mld_opdr_kosten)     amount_total_exc"
                + "     , SUM(o.mld_opdr_kosten_btw) amount_total_btw"
                + "  FROM mld_opdr o"
                + " WHERE o.prs_kostensoort_key = " + data_budget.costtype_key;
        var oRs = Oracle.Execute(sql);
        var amount_sum_exc = oRs("amount_total_exc").Value;
        var amount_sum_btw = oRs("amount_total_btw").Value;
        oRs.Close();

        if (data_id > -1)
        {
            var sql = "SELECT COALESCE(t.mld_opdr_kosten, 0) amount_old_exc"
                    + "     , COALESCE(t.mld_opdr_kosten_btw, 0) amount_old_btw"
                    + "  FROM mld_opdr t"
                    + " WHERE t.prs_kostensoort_key = " + data_budget.costtype_key
                    + "   AND t.mld_opdr_key = " + data_id;
            var oRs = Oracle.Execute(sql);
            amount_old_exc = oRs("amount_old_exc").Value;
            amount_old_btw = oRs("amount_old_btw").Value;
            oRs.Close();

            if (amount_as_new)
            {
                amount_sum_exc = amount_sum_exc - amount_old_exc;
                amount_sum_btw = amount_sum_btw - amount_old_btw;
                amount_old_exc = 0;
                amount_old_btw = 0;
            }
        }
        else
        {
            if (!prj_changed)
            {   // Correctie omdat contract al is toegevoegd op de pre_post
                // en we hier de oude waarde moeten weten.
                amount_sum_exc = amount_sum_exc - data_amount;
                amount_sum_btw = amount_sum_btw - data_vat;
            }
        }

        // Haal het totaal aan mutaties op reserve op bij dit budget.
        var sql = "SELECT COALESCE(SUM(m1.bgt_budget_bedrag_van), 0) reserve_exc"
                + "     , COALESCE(SUM(m1.bgt_budget_btwbedrag_van), 0) reserve_btw"
                + "  FROM bgt_budget b1"
                + "     , bgt_budget b2"
                + "     , bgt_budgetmutatie m1"
                + " WHERE b2.bgt_budget_isreserve = 1"
                + "   AND b1.bgt_project_key = b2.bgt_project_key"
                + "   AND m1.bgt_budget_key_van = b2.bgt_budget_key"
                + "   AND m1.bgt_budget_key_naar = b1.bgt_budget_key"
                + "   AND b1.prs_kostensoort_key = " + data_budget.costtype_key;
        var oRs = Oracle.Execute(sql);
        var reserve_exc = oRs("reserve_exc").Value;
        var reserve_btw = oRs("reserve_btw").Value;
        oRs.Close();
        //
        var bedrag_exc = getBudgetverschil( { id: data_id
                                            , amount_new: data_amount
                                            , amount_old: amount_old_exc
                                            , sum_contract: amount_sum_exc
                                            , budget: data_budget.bedrag_exc
                                            , reserve: reserve_exc
                                            });
        var bedrag_btw = getBudgetverschil( { id: data_id
                                            , amount_new: data_vat
                                            , amount_old: amount_old_btw
                                            , sum_contract: amount_sum_btw
                                            , budget: data_budget.bedrag_btw
                                            , reserve: reserve_btw
                                            });

        return { budget_res_key: data_budget.budget_res_key
               , budget_srt_key: data_budget.budget_srt_key
               , bedrag_exc: bedrag_exc
               , bedrag_btw: bedrag_btw
               , contract_id: data_id
               , contract_datum: contract_datum
               };
    }

    function getBudgetverschil(data)
    {
        var amount = 0;
        if (data.id == -1)
        {
            // nieuw contract
            if (data.amount_new >= 0)
            {
                // nieuw contract heeft positieve waarde
                var difference = data.budget - data.sum_contract - (data.amount_new - data.amount_old);
                amount = (difference < 0 ? difference : 0);
            }
            else
            {
                // nieuw contact heeft negatieve waarde
                amount = -1 * data.amount_new;
            }
        }
        else
        {
            // gewijzigd contract
            if (data.amount_old >=0 && data.amount_new >= 0)
            {
                if (data.amount_old < data.amount_new)
                {
                    // verhoging contractwaarde exc
                    var difference = data.budget - data.sum_contract - (data.amount_new - data.amount_old);
                    amount = (difference < 0 ? difference : 0);
                }
                else
                {
                    // verlaging contractwaarde exc
                    var difference = data.reserve - (data.amount_new - data.amount_old);
                    amount = -1 * (difference > 0 ? data.reserve : (data.amount_new - data.amount_old) );
                }
            }
            else
            {
                var difference = data.amount_new - data.amount_old;
                amount = -1 * difference;
            }
        }
        return amount;
    }

    function insertMutatie(data_mutatie, mutatie_soort)
    {
        var voor_debug = false;
        if (voor_debug)
        {
            //__Log("insertMutatie: "+mutatie_soort);
            //__Log(data_mutatie);
            var sql_nr = "SELECT COALESCE(MAX(m.bgt_budgetmutatie_volgnr), 0)+1 volgnr"
                       + "  FROM bgt_budgetmutatie m"
                       + "     , bgt_budget b"
                       + " WHERE (  b.bgt_budget_key = m.bgt_budget_key_naar"
                       + "       OR b.bgt_budget_key = m.bgt_budget_key_van"
                       + "       )"
                       + "   AND b.bgt_project_key =  (SELECT MAX(ba.bgt_project_key)"
                                                   + "   FROM bgt_budget ba"
                                                   + "  WHERE ba.bgt_budget_key = " + data_mutatie.budget_srt_key
                                                   + ")";
            var oRs_nr = Oracle.Execute(sql_nr);
            var v_volgnr = oRs_nr("volgnr").Value;
            oRs_nr.Close();
        }

        // Alleen een budgetmutatie maken als minimaal een van beide (excl of btw) ongelijk 0 is.
        if (data_mutatie.bedrag_exc != 0 || data_mutatie.bedrag_btw != 0)
        {
            var sql = "SELECT prs_kostensoort_altcode"
                    + "  FROM prs_kostensoort"
                    + " WHERE prs_kostensoort_key = "
                        + " (SELECT MIN(prs_kostensoort_key)"
                        + "    FROM bgt_budget"
                        + "   WHERE bgt_budget_key = " + data_mutatie.budget_srt_key
                        + " )";
            var oRs = Oracle.Execute(sql);
            var v_costtype_code = oRs("prs_kostensoort_altcode").Value;
            oRs.Close();

            var mutatie_omschr = "";
            var mutatie_code = "";
            switch (mutatie_soort)
            {
                case "AO": mutatie_omschr = "AO Automatische overboeking";
                           mutatie_code = "A";
                           break;
                case "MW": mutatie_omschr = "MW Meer-, minderwerk van " + v_costtype_code;
                           mutatie_code = "W";
                           break;
            }

            if (!data_mutatie.contract_datum)
                data_mutatie.contract_datum = new Date();
            var sql_m = "INSERT INTO bgt_budgetmutatie"
                      + "( bgt_budgetmutatie_datum"
                      + ", bgt_budgetmutatie_omschrijving"
                      + ", prs_perslid_key" // not null!
                      + ", bgt_budget_key_van"
                      + ", bgt_budget_bedrag_van"
                      + ", bgt_budget_btwbedrag_van"
                      + ", bgt_budget_key_naar"
                      + ", bgt_budget_bedrag_naar"
                      + ", bgt_budget_btwbedrag_naar"
                      + ", bgt_budgetmutatie_reserve"
                      + ", bgt_budgetmutatie_code"
                      + (voor_debug ? ", bgt_budgetmutatie_volgnr" : "")
                      + ") VALUES"
                      + "( " + data_mutatie.contract_datum.toSQL()
                      + ", " + safe.quoted_sql(mutatie_omschr)
                      + ", " + user_key
                      + ", " + data_mutatie.budget_res_key
                      + ", " + data_mutatie.bedrag_exc
                      + ", " + data_mutatie.bedrag_btw
                      + ", " + data_mutatie.budget_srt_key
                      + ", " + (-1 * data_mutatie.bedrag_exc)
                      + ", " + (-1 * data_mutatie.bedrag_btw)
                      + ", 1"
                      + ", " + safe.quoted_sql(mutatie_code)
                      + (voor_debug ? ", " + v_volgnr : "")
                      + ")";
            Oracle.Execute(sql_m);
        }
    }

    function wijzigKostensoortOpdracht(p_opdr_key, p_startdate, p_cur, p_new)
    {
        var v_prj_changed = (p_cur.project_key != p_new.project_key);
        if (p_cur.additional == 1)
        {
            // Terugboeken meer-minderwerk budget naar reserve.
            var v_cur_budget = getBudget(p_cur.costtype_key);
            var v_datamutatie = { budget_res_key: v_cur_budget.budget_res_key
                                , budget_srt_key: v_cur_budget.budget_srt_key
                                , bedrag_exc: p_cur.amount
                                , bedrag_btw: p_cur.vat
                                , contract_datum: p_startdate
                                };
            insertMutatie( v_datamutatie, "MW");
        }
        else
        {
            // Terugboeken van budgetoverschrijding huidig kostensoort naar reserve
            var v_cur_budget = getBudget(p_cur.costtype_key);
            var v_datamutatie = checkBudgetoverschrijding(p_opdr_key, v_cur_budget, 0, 0, p_startdate, v_prj_changed); // bedrag van deze kostensoort verwijderen
            insertMutatie( v_datamutatie, "AO" );
        }

        if (p_new.additional == 1)
        {
            // Reserve aan budget toevoegen wegens meer-minderwerk.
            var v_new_budget = getBudget(p_new.costtype_key);
            var v_datamutatie = { budget_res_key: v_new_budget.budget_res_key
                                , budget_srt_key: v_new_budget.budget_srt_key
                                , bedrag_exc: -1 * p_new.amount
                                , bedrag_btw: -1 * p_new.vat
                                , contract_datum: p_startdate
                                };
            insertMutatie( v_datamutatie, "MW");
        }
        else
        {
            // Reserve toevoegen wegens budgetoverschrijding nieuw kostensoort.
            var v_new_budget = getBudget(p_new.costtype_key);
            var v_datamutatie = checkBudgetoverschrijding(-1, v_new_budget, p_new.amount, p_new.vat, p_startdate, v_prj_changed); // (aangepast) bedrag aan nieuwe kostensoort toevoegen
            insertMutatie( v_datamutatie, "AO" );
        }
    }

   function writeOpdrOpmToMld(pmld_key, popdr_opm)
   { // Afhandelingtekst van de opdracht automatisch kopiëren naar de melding.
     var sql = "SELECT mld_melding_opmerking"
             + "  FROM mld_melding"
             + " WHERE mld_melding_key = " + pmld_key;
     var oRs = Oracle.Execute(sql);
     var new_opm = (oRs("mld_melding_opmerking").Value || "") + "\n" + L("lcl_mld_final_remark") + ":\n" + popdr_opm;
     var fields =  [ { dbs: "mld_melding_opmerking",  typ: "varchar", val: new_opm, track: L("lcl_mld_inf_Opmerking"), len: 4000 } ];
     var mldUpd = buildTrackingUpdate("mld_melding", " mld_melding_key = " + pmld_key, fields, {noValidateToken: true}); // Bij API wordt geen token meegestuurd.
     oRs = Oracle.Execute(mldUpd.sql);
     if (mldUpd.trackarray.length)
       mld.trackmeldingupdate(pmld_key, L("lcl_mld_is_mldupd") + "\n" + mldUpd.trackarray.join("\n"));
   }

   function check_required(model, obj)
   {
     for (fld in model.fields)
     {
       if (model.fields[fld].required && !model.fields[fld].sql && model.fields[fld].dbs.indexOf(".") < 0)
       {
         if (obj[fld] < 0)
           abort_with_warning(L("lcl_shared_validator_missing") + ": " + model.fields[fld].label);
       }
     }
   }

   this.REST_GET = function (params)
    {
      if (S("bgt_enabled") == 1)
      { // autfunction = "WEB_BGTORD".
        var x_params = object_merge({}, params, gparams);
        var authparams_cA = _checkAutorisation(this, params);  // Misschien geen schrijfrechten, maar in ieder geval wel leesrechten.

        return generic_REST_GET(this, x_params)(params);
      }
      else
      {
        var x_params = object_merge({}, params, gparams);
        var authparams_cA = _checkAutorisation(this, params);  // Misschien geen schrijfrechten, maar in ieder geval wel leesrechten.
        // The parameters this and params are objects. Object are passed by reference: In de functie _checkAutorisation wordt de waarde authparams al aan het object params toegewezen.
        //var params.authparams = authparams_cA.authparams;

        var query = api2.sqlfields(params, this);
        query.wheres.push("mld_opdr.fac_activiteit_key IS NULL");

        if (params.filter.id)
        {
          // klaar verder
        }
        else
        {
          var scope = params.filter.scope || "fe";
          if (scope == "fe")
          {
            query.wheres.push("mld_opdr.prs_perslid_key = " + user_key);
          }

          /* You can't go any further back in time than the GUI could (ongeveer) */
          /* Als er wel een start_issuedate is dan wordt deze toegevoegd in api2.sqlfilter() verderop. */
          if (!params.filter.start_issuedate && S("mld_max_history") > 0)
          {
            query.wheres.push("mld_opdr_datumbegin > SYSDATE - " + S("mld_max_history"));

          }
          if (!params.filter.status)
          { /* Limitation: if no status filter, then default to the active statusses (all except 1, 2, 6, 7, 9) */
            // Zou zo kunnen, params.filter.status = [3, 4, 5, 8, 10];, maar eigenlijk is dit logischer
            query.wheres.push("mld_statusopdr_key IN (3, 4, 5, 8, 10)");
          }
        }

        /* we need the prefix for the name */
        query.tables.push("mld_melding");
        query.wheres.push("mld_opdr.mld_melding_key = mld_melding.mld_melding_key");
        query.tables.push("mld_stdmelding");
        query.wheres.push("mld_melding.mld_stdmelding_key = mld_stdmelding.mld_stdmelding_key");
        query.tables.push("mld_discipline");
        query.wheres.push("mld_stdmelding.mld_ins_discipline_key = mld_discipline.ins_discipline_key");

        query.tables.push("ins_srtdiscipline");
        query.wheres.push("mld_discipline.ins_srtdiscipline_key = ins_srtdiscipline.ins_srtdiscipline_key");

        query.tables.push("alg_v_allonroerendgoed");
        query.wheres.push("mld_melding.mld_alg_onroerendgoed_keys = alg_v_allonroerendgoed.alg_onroerendgoed_keys(+)");

        // Een afmelder (ORDBO2 of EXTORD) mag externe opdrachten zien/afmelden onder bepaalde voorwaarden:
        // EXTORD: Hij mag opdrachten zien/afmelden van externe bedrijven waar hij contactpersoon van is,
        //         als hij ALG read/write rechten (WEB_EXTORD) heeft op de plaats van de bijbehorende melding van de opdracht.
        // ORDBO2: Setting mld_typeopdr_afmelden_extern: Afmelder (ORDBO2) mag externe opdrachten (0=Niet zien, 1=Zien, 2=Afmelden)
        //         Als hij een interne (decentraal/intern) uitvoerder is mag hij de externe opdrachten niet zien/afmelden als mld_typeopdr_afmelden_extern==0/(0 of 1).
        //         De voorwaarde en setting geldt niet als:
        //         1) de opdracht toegekend is aan een (intern) persoon (P). Dan mag hij alleen eigen opdrachten zien.
        //         2) de opdracht toegekend is aan het eigen (intern) bedrijf (BI). Opdrachten van het eigen bedrijf mag hij alleen zien.
        var sql_EXTORD = " AND EXISTS (SELECT 'X' " // Ben ik contactpersoon van het uitvoerende externe bedrijf
                       + "               FROM prs_contactpersoon"
                       + "              WHERE prs_perslid_key = " + user_key
                       + "                AND prs_contactpersoon_verwijder IS NULL"
                       + "                AND prs_bedrijf_key = mld_opdr.mld_uitvoerende_keys)";

        query.tables.push("mld_typeopdr");
        query.wheres.push("mld_opdr.mld_typeopdr_key = mld_typeopdr.mld_typeopdr_key");
        var sql_ORDBO2 = " AND (mld_typeopdr_afmelden_extern IN (1, 2)"
                       + "   OR EXISTS (SELECT 'X'" // Toegekend aan een persoon
                       + "                FROM prs_perslid"
                       + "               WHERE prs_perslid_key = mld_opdr.mld_uitvoerende_keys)"
                       + "   OR EXISTS (SELECT 'X'" // Toegekend aan het EIGEN interne bedrijf
                       + "                FROM prs_bedrijf b"
                       + "                   , prs_perslid p"
                       + "                   , prs_v_afdeling a"
                       + "               WHERE b.prs_bedrijf_key = a.prs_bedrijf_key"
                       + "                 AND p.prs_afdeling_key = a.prs_afdeling_key"
                       + "                 AND b.prs_bedrijf_intern = 1"
                       + "                 AND b.prs_bedrijf_key = mld_opdr.mld_uitvoerende_keys"
                       + "                 AND p.prs_perslid_key = " + user_key + ")"
                       //    OR (Ben ik contactpersoon van het uitvoerende externe bedrijf) kan hier weg (is false), want anders was ik wel in de if terecht gekomen.
                       + ")";

        if (authparams_cA && authparams_cA.authparams && authparams_cA.authparams.autfunction == "WEB_MLDORD")
        { // Als ik uitvoerder ben dan mag ik de aan mij toegekende opdrachten zien.
          // Daarnaast kan ik nog plaatsvervanger zijn.
          var prsKeys = user.collegakeylist();
          query.wheres.push("mld_opdr.mld_uitvoerende_keys IN (" + prsKeys + ")");
        }

        // Als er multicompany-achtige relatiebeperkingen gelden, dan moet dat ook hier worden afgedwongen
        var lsql = "SELECT '' FROM prs_bedrijf_bedrijf pbb"
                 + " WHERE prs_bedrijf_key1 = " + user.afdeling().prs_bedrijf_key()
                 + "    OR prs_bedrijf_key2 = " + user.afdeling().prs_bedrijf_key();
        var loRs = Oracle.Execute(lsql);
        if (!loRs.eof)
        {
          sql_multicomp = "mld_opdr.mld_uitvoerende_keys IN (SELECT prs_bedrijf_key2"
                        + "                                    FROM prs_bedrijf_bedrijf"
                        + "                                   WHERE prs_bedrijf_key1 = " + user.afdeling().prs_bedrijf_key()
                        + "                                  UNION"
                        + "                                  SELECT prs_bedrijf_key1"
                        + "                                    FROM prs_bedrijf_bedrijf"
                        + "                                   WHERE prs_bedrijf_key2 = " + user.afdeling().prs_bedrijf_key()
                        + "                                 )";
          query.wheres.push(sql_multicomp);
        }
        loRs.Close();

        if (S("bgt_enabled") != 1 && !params.filter.id)
        {
          if (params.authparams.ALGreadlevel > -1)
          { /* required for 3D ALG scopeing */
            __Log("ALGreadlevel = " + params.authparams.ALGreadlevel);
            query.tables.push("alg_locatie");  /* opletten: outerjoin denk ik? */
            query.tables.push("alg_district");
            query.wheres.push("mld_melding.mld_alg_locatie_key = alg_locatie.alg_locatie_key(+)");
            query.wheres.push("alg_locatie.alg_district_key = alg_district.alg_district_key(+)");
          }
          if (params.authparams.PRSreadlevel > -1)
          { /* required for 3D PRS scopeing */
            __Log("PRSreadlevel = " + params.authparams.PRSreadlevel);
            query.tables.push("prs_perslid");
            query.tables.push("prs_v_afdeling_boom");
            query.wheres.push("mld_melding.prs_perslid_key = prs_perslid.prs_perslid_key");
            query.wheres.push("prs_perslid.prs_afdeling_key = prs_v_afdeling_boom.prs_afdeling_key");
          }
        }

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

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

        if (S("bgt_enabled") != 1 && !params.filter.id)
        { // Autorisatie functie parameter authparams_cA.authparams is gedefinieerd.
          // Over het resultaat moet nog de 3D rasp, altijd.
          var add_prs_restrict_ord = prshasrestrict(authparams_cA.authparams.autfunctions);
          for (var ii = 0; ii < authparams_cA.authparams.autfunctions.length; ii++)
          {
            // Voorwaarden die meegenomen moeten worden voor de autorisaties WEB_EXTORD of WEB_ORDBO2:
            // WEB_EXTORD: sql_EXTORD (Ik ben contactpersoon van externe bedrijf) + 3d autorisatie (ALG).
            // WEB_ORDBO2: sql_ORDBO2 + 3d autorisatie.
            var sql3d = sql;
            if (authparams_cA.authparams.autfunctions[ii] == "WEB_EXTORD")
              sql3d += sql_EXTORD;
            else if (authparams_cA.authparams.autfunctions[ii] == "WEB_ORDBO2")
              sql3d += sql_ORDBO2;

            // apply 3D authorization to the locations and to the organisations (both ALG and PRS)
            // if "disc == 0" or disc is not defined then disc should be ""
            // otherwise de 3d authorization adds a "AND (1=0)" line to the query and you will have never a result
            sql = (ii > 0? sql + " UNION " : "") + discx3d (sql3d,
                                                            "mld_opdr.mld_opdr_discipline_key",
                                                            "alg_district.alg_regio_key",
                                                            "alg_district.alg_district_key",
                                                            "mld_melding.mld_alg_locatie_key",
                                                            "alg_v_allonroerendgoed.alg_gebouw_key",
                                                            "alg_v_allonroerendgoed.alg_verdieping_key",
                                                            "alg_v_allonroerendgoed.alg_ruimte_key",
                                                            "prs_bedrijf_key",
                                                            "prs_afdeling_key",
                                                            authparams_cA.authparams.autfunctions[ii],
                                                            [], /* no additional discipline restrictions */
                                                            (add_prs_restrict_ord ? 2 : 0)
                                                           );
          }
        }

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

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

        return json;
      }
    }

    function _keep_editable_fields(jsondata, opdr_info)
    { // Verwijder voor PUT wat niet gewijzigd mag worden.
      function delete_fld(jsondata, field, condition)
      {
        if (condition)
          delete jsondata[field];
      }

      var this_opdr = mld.func_enabled_opdracht(opdr_info.opdr_key);

      // Nu per veld eventueel verwijderen.
      delete_fld(jsondata, "remark",         !this_opdr.canOpmChange || S("mld_afhandeling_restrict_edit") != 0);
      delete_fld(jsondata, "company",        !this_opdr.canUitvChange);
      delete_fld(jsondata, "contact",        !this_opdr.canVeldChange);
      delete_fld(jsondata, "contract",       !this_opdr.canContractChange);
      delete_fld(jsondata, "owner",          !this_opdr.canVeldChange);
      delete_fld(jsondata, "description",    !this_opdr.canVeldChange);
      delete_fld(jsondata, "startdate",      !this_opdr.canDatesChange);
      delete_fld(jsondata, "enddate",        !this_opdr.canDatesChange);
      delete_fld(jsondata, "plandate",       (!this_opdr.canPlanDatesChange || !(S("mld_ord_edit_plandates") & 1)));
      delete_fld(jsondata, "plandate2",      (!this_opdr.canPlanDatesChange || !(S("mld_ord_edit_plandates") & 1)));
      delete_fld(jsondata, "account",        ((!this_opdr.canKostChange || !opdr_info.tonen_totaal) || S("mld_ord_show_kostenplaats") != 1)); // Setting mld_ord_show_kostenplaats: show kostenplaats field for orders {0=do not show | 1=editable | 2=show}
      delete_fld(jsondata, "costtype",       S("mld_opdr_alt_kostensoort") == 0); // Setting mld_opdr_alt_kostensoort: Enable use of alternative cost category {0|1} default 0
      delete_fld(jsondata, "ordernr",        !this_opdr.canVeldChange);
      delete_fld(jsondata, "hours" ,         !this_opdr.canUrenMatChange);
      delete_fld(jsondata, "hourrate",       !this_opdr.canUrenMatChange);
      delete_fld(jsondata, "materialcost",   (this_opdr.typeopdr_materiaal >= 2 || !this_opdr.canUrenMatChange));
      delete_fld(jsondata, "amount",         (!this_opdr.mld_opdr.tonen_correctie || !this_opdr.canUrenMatChange || this_opdr.mld_opdr.correctie_readonly));
      delete_fld(jsondata, "sequence",       true);
      if ((S("fac_api2_compatibility") & 1) != 1)
        delete_fld(jsondata, "issueordertype", true);
      delete_fld(jsondata, "module", true);
      delete_fld(jsondata, "additional", true);
      delete_fld(jsondata, "offer", true);
    }

    this.REST_PUT = function (params, jsondata, the_key)
    {
        var x_params = object_merge({}, params, gparams);
        var authparams_cA = _checkAutorisation(this, params);
        if (!authparams_cA.canWrite)
            abort_with_warning(L("lcl_shared_no_auth"));

        var old_opdrstatus = -1;
        var opdr_info = mld.mld_opdr_info(the_key); // Bevat alle info van de opdracht.
        var mld_key = opdr_info.mld_key; // De melding key (jsondata.issue.id) is wellicht niet altijd aanwezig.
        if (S("bgt_enabled") != 1)  // BGT doet niets met mld_melding; mld_opdr heeft zelfs geen mld_melding_key!
        {                           // Maar voordat wijzigingen worden opgeslagen moet voor niet-BGT worden gecontroleerd of de gebruiker voldoende rechten heeft.
          // De opdracht key staat in the_key.
          var opdr_type_key = opdr_info.opdr_type;
          var this_opdr = mld.func_enabled_opdracht(the_key);
          old_opdrstatus = opdr_info.opdr_status;
        }
        else
        {
          var sqls = "SELECT mld_statusopdr_key"
                  + "  FROM mld_opdr"
                  + " WHERE mld_opdr_key = " + the_key;
          var oRss = Oracle.Execute(sqls);
          old_opdrstatus = oRss("mld_statusopdr_key").Value;
          oRss.Close();
        }

        // De status pas na het inserten zetten zodat er ook getrackt kan worden met de functie setopdrachtstatus.
        //SyntaxErrors / Foute invoer is al gecheckt.
        var setStatus = false;
        // Is er een status meegegeven?
        if (jsondata.status)
        { // Er is een status meegegeven.
          var setopdrstatus = typeof jsondata.status == "object"? jsondata.status.id : jsondata.status;
          jsondata.status = old_opdrstatus; // In eerste instantie met de REST_PUT de status nog niet aanpassen. Pas daarna met de setopdrachtstatus.
          setStatus = setopdrstatus != old_opdrstatus;
        }

        if (S("bgt_enabled") != 1)
          _keep_editable_fields(jsondata, opdr_info);
        var result = generic_REST_PUT(this)(params, jsondata, the_key);
        var opdr_key = result.key;

        if (setStatus)
        {
          // Voor afmelden heb je canClose rechten nodig.
          user.auth_required_or_abort(((setopdrstatus == 6 && this_opdr.canClose) || (setopdrstatus != 6)));

          var sqlu = "SELECT mld_uitvoerende_keys"
                   + "  FROM mld_opdr"
                   + " WHERE mld_opdr_key = " + opdr_key;
          var oRsu = Oracle.Execute(sqlu);
          var company = oRsu("mld_uitvoerende_keys").Value;
          oRsu.Close();
          // Status en tracking altijd met de functie setopdrachtstatus.
          // Zetten van de nieuwe status van de aangepaste opdracht en afhandelen van de tracking (status + uitvoerende tracking) en daarmee notificatie.
          mld.setopdrachtstatus(opdr_key, setopdrstatus, company);

          if (S("bgt_enabled") != 1)  // BGT doet niets met mld_melding; mld_opdr heeft zelfs geen mld_melding_key!
          {                           // Voor niet-BGT moet de status worden gezet met de juiste tracking en notificatie.
            // Refresh meldingstatus zonodig als gevolg van een nieuw uitgegeven opdracht,
            mld.updatemeldingstatus(mld_key, 0);
          }
        }

        if (S("bgt_enabled") != 1)  // Voor BGT niet nodig.
        {
            // LET OP: De jsondata is anders dan voor de aanroep van generic_REST_PUT. De jsondata bevat nu de gegevens van de opdracht na de update.

            // Als de opdrachtstatus wordt Afgemeld(6) dan controleren of de melding gesloten kan worden.
            // Dit controleren als
            // 1) De opdracht Afgemeld(6) moet worden.
            // 2) De opdrachtstatus is aangepast.
            // 3) De nieuwe status van de opdracht ook daadwerkelijk Afgemeld(6) is geworden.

            // De rechten van de melding ophalen na de update van de opdracht. Dan heb je de actuele rechten.
            var this_mld = mld.func_enabled_melding(mld_key);

            // Hier terugchecken van de status en opmerking.
            var tsql = "SELECT mld_statusopdr_key"
                     + "     , mld_opdr_opmerking"
                     + "  FROM mld_opdr"
                     + " WHERE mld_opdr_key="+ opdr_key;
            var oRs = Oracle.Execute(tsql);
            var new_opdrstatus = oRs("mld_statusopdr_key").Value;
            var opm = oRs("mld_opdr_opmerking").Value? oRs("mld_opdr_opmerking").Value : "";
            oRs.Close();
            if (setopdrstatus == 6 && old_opdrstatus != setopdrstatus && new_opdrstatus == 6)
            {
              // Als er geen lopende opdrachten meer zijn inclusief deze (laatste) opdracht (alle opdrachten zijn afgemeld), dan kijken of de melding gesloten kan worden.
              // Zijn er nog lopende opdrachten
              lsql = "SELECT COUNT ( * ) lopend"
                   + "  FROM mld_opdr"
                   + " WHERE mld_melding_key = " + mld_key
                   + "   AND mld_statusopdr_key NOT IN (1, 2, 6, 7, 9)"; // (1=afgewezen, 2=niet akkoord, 6=afgemeld, 7=verwerkt en 9=afgerond)
              var oRs = Oracle.Execute(lsql);
              var lopend = oRs("lopend").Value;
              oRs.Close();

              if (lopend == 0)
              { // Er zijn alleen opdrachten met status 1 (Afgewezen) of 6/9 (Afgemeld/Afgerond)
                // Check of setting bij opdrachttype automatisch sluiten ALTIJD (= 2) toestaat.
                var ssql = "SELECT mld_typeopdr_sluitmelding FROM mld_typeopdr"
                         + " WHERE mld_typeopdr_key = " + opdr_type_key;
                var oRs = Oracle.Execute(ssql);

                // Afhankelijk van sluitmelding nu melding status zetten (0=nooit, 1=vragen en 2=altijd)
                // De laatste opdracht is afgemeld anders kom je hier niet.
                if ((oRs("mld_typeopdr_sluitmelding").Value == 2 || oRs("mld_typeopdr_sluitmelding").Value == 1) &&
                    this_mld.canClose)
                {
                  mld.setmeldingstatus (mld_key, 5); // Afgemeld
                  var sql = "BEGIN fac.backtrackaction('MLDAFM', " + mld_key + ", " + user_key + ", " + new Date().toSQL(true) +"); END;"
                  Oracle.Execute(sql); // Statuswijzigingen wel even tracken
                  mld.trackmeldingupdate(mld_key, L("lcl_mld_is_mldupd") + "\n" + L("lcl_mld_is_mldafm") + L("lcl_trackto") + toDateTimeString(new Date()));

                  writeOpdrOpmToMld(mld_key, opm);
                  // Handle workflow actions for completion:
                  mld.nextworkflowstep(mld_key, 1);  // 1 = completed
                }
                else
                {
                  // Dan zetten we de Melding hier terug naar Geaccepteerd indien die Uitgegeven was
                  // Zodat die bij de FO weer onder Inbehandeling valt.
                  mld.setmeldingstatus (mld_key, 4); // Geaccepteerd

                  // Als de laatste opdracht is afgemeld en de setting "mld_add_mldopmerk_on_opdrclose" is gezet dan moet de opdracht opmerking naar de melding worden gekopieerd.
                  if (!this_mld.canClose && S("mld_add_mldopmerk_on_opdrclose") == 1 && opm != "")
                  { // Afhandelingtekst van de opdracht automatisch kopiëren naar de melding.
                    writeOpdrOpmToMld(mld_key, opm);
                  }
                }
                oRs.Close();
              }
              // else status van de melding niet aanpassen.
            }
        }

        return result;
    }

    if (S("bgt_enabled"))
    {
      var auth_par = { filter: { id: getQParamInt("id",-1), scope: getQParamSafe("scope","")}};
      var this_par = { autfunction: true};
      var authparams_cA = _checkAutorisation(this_par, auth_par);
      var addpostdelete = authparams_cA.canWrite;
    }
    else
      var addpostdelete = true; // voor API2

    if (addpostdelete)
    {
      this.REST_POST = function (params, jsondata) /* new order */
      {
        if (!S("bgt_enabled"))  // Nu pas voor API2 controleren. Voor BGT is canWrite hier zeker true.
        {
          var auth_par = { filter: { id: getQParamInt("id", -1), scope: getQParamSafe("scope", "")}};
          var this_par = { autfunction: true};
          var authparams_cA = _checkAutorisation(this_par, auth_par);
          if (!authparams_cA.canWrite)
            abort_with_warning(L("lcl_shared_no_auth"));

          // Als het volgnummer van de opdracht niet is meegegeven dan moet deze zelf bepaald worden.
          var mld_key = (typeof jsondata.issue != "object" ? parseInt(jsondata.issue) : parseInt(jsondata.issue.id));
          sql = "SELECT mld.bepaalopdrmeldingvolgnr(" + mld_key + ") volg"
              + " FROM DUAL";
          oRs = Oracle.Execute(sql);
          jsondata.sequence = oRs("volg").Value;
          oRs.Close();
        }

        var setopdrstatus = typeof jsondata.status == "object"? jsondata.status.id : jsondata.status;
        if (setopdrstatus != 8) // XTRF: die kan later niet rechtstreeks
            jsondata.status = null; // In eerste instantie met de REST_POST de status nog niet zetten. Pas daarna met de setopdrachtstatus.

        var result = generic_REST_POST(this)(params, jsondata); // Eerst gewoon opslaan.
        var opdr_key = result.key;

        // 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.
        var company = typeof jsondata.company == "object"? jsondata.company.id : jsondata.company;

        params.mld_opdr = {}; // Zet hier de gegevens uit mld.mld_opdr_info() in.
        params.mld_opdr.uitvoerende = (jsondata.company? api2.get_jdata_refkey(jsondata.company) : -1);
        params.mld_opdr.opdr_type = (jsondata.issueordertype? jsondata.issueordertype : -1);

        // Gegevens van het opdrachttype ophalen.
        var sql = "SELECT mld_typeopdr_confirm_for_send"
                + "     , mld_typeopdr_isofferte"
                + "  FROM mld_typeopdr"
                + " WHERE mld_typeopdr_key = " + params.mld_opdr.opdr_type;
        oRs = Oracle.Execute(sql);
        params.mld_opdr.typeopdr_confirm_for_send = oRs("mld_typeopdr_confirm_for_send").Value;
        params.mld_opdr.mld_typeopdr_isofferte = oRs("mld_typeopdr_isofferte").Value;
        oRs.Close();

        mld.setopdrachtstatus(opdr_key, setopdrstatus, company);

        if (S("bgt_enabled") != 1)  // BGT doet niets met mld_melding; mld_opdr heeft zelfs geen mld_melding_key!
        {                           // Voor niet-BGT moet de status worden gezet met de juiste tracking en notificatie.
          // Refresh meldingstatus zonodig als gevolg van een nieuw uitgegeven opdracht,
          mld.updatemeldingstatus(mld_key, 0);
        }

        if (!S("bgt_enabled"))
        {
          // Vooralsnog alleen voor nieuwe meldingen (POST).
          // Opdracht direct electronisch versturen of door putorders laten oppikken (te verzenden veld zetten) bij nieuwe opdrachten.
          // Nooit als er een activiteits key is meegegeven omdat het dan eigenlijk geen opdracht is. Bij de api is de fac_activiteit_key niet gevuld.

          if (params.mld_opdr.mld_typeopdr_isofferte == 1)
          { // Offerte opdracht.
            if (params.mld_opdr.typeopdr_confirm_for_send < 2)
            { // Zonder bevestiging versturen.
              sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = 1 WHERE mld_opdr_key = " + opdr_key;
              Oracle.Execute(sql);

              if (setopdrstatus == 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 (params.mld_opdr.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;
              Oracle.Execute(sql);
            }
          }
          else
          { // Opdrachten
            var sql = "SELECT prs_bedrijfadres_url"
                    + "  FROM prs_bedrijfadres"
                    + " WHERE prs_bedrijfadres_type = 'O'"
                    + "   AND prs_bedrijfadres_startdatum <= SYSDATE"
                    + "   AND (mld_typeopdr_key IS NULL OR mld_typeopdr_key = " + params.mld_opdr.opdr_type + ")"
                    + "   AND prs_bedrijf_key = " + params.mld_opdr.uitvoerende;
            var 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 && params.mld_opdr.typeopdr_confirm_for_send == 0)
            { // Er is een mldorder_adres en zonder bevestiging versturen.
              sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = 1 WHERE mld_opdr_key = " + opdr_key;
              Oracle.Execute(sql);

              if (setopdrstatus == 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 (!oRs.eof && params.mld_opdr.typeopdr_confirm_for_send == 2)
            { // Er is een mldorder_adres maar nog niet laten oppikken door achtergrond putorders alleen uitstellen.
              sql = "UPDATE mld_opdr SET mld_opdr_teverzenden = NULL WHERE mld_opdr_key = " + opdr_key;
              Oracle.Execute(sql);
            }
            //else geen mldorder_adres, dan niet versturen.
            oRs.Close();
          }
        }
        return result;
     }

      this.REST_DELETE = function (params, the_key)
      {
        if (!S("bgt_enabled"))  // Nu pas voor API2 controleren. Voor BGT is canWrite hier zeker true.
        {
          var auth_par = { filter: { id: getQParamInt("id", -1), scope: getQParamSafe("scope", "")}};
          var this_par = { autfunction: true};
          var authparams_cA = _checkAutorisation(this_par, auth_par);
          if (!authparams_cA.canWrite)
            abort_with_warning(L("lcl_shared_no_auth"));
        }
        return generic_REST_DELETE(this)(params, the_key);
      }
    }
}
%>