<% /*
    $Revision$
    $Id$

    File:           model_report_columns.inc

    Description:    rapport model voor de kolommen van rapporten.

    Parameters:
    Context:

    Notes:
*/

// model_reportcolumns is uitsluitend beschikbaar onder model_reports(x) die de autorisatie dan ook doet
// TODO: neen, security lek FSN#33869
function model_reportcolumns(usrrap_key, params)
{
    var rap_key = getQParamInt("fac_usrrap_key", -1); // JGL: Lelijk

    params = params || {};
    this.table = "fac_usrrap_cols";
    this.primary = "fac_usrrap_cols_key";
    this.records_name = "columns";
    this.record_name = "column";
    this.records_title = L("lcl_rap_columns");
    this.record_title = L("lcl_rap_column");
    this.autfunction = "WEB_UDRMAN",
    this.edit = { modal: true };

    this.fields = {
        "id": {
            dbs: "fac_usrrap_cols_key",
            typ: "key",
            label: "Key",
            filter: "exact",
            seq: "fac_s_fac_usrrap_cols_key"
        },
        "name": {
            dbs: "fac_usrrap_cols_column_name",
            typ: "varchar",
            label: L("lcl_rap_column"),
            required: true,
            insertonly: true
        },
        "datatype": {
            dbs: "fac_usrrap_cols_datatype",
            typ: "varchar",
            label: L("lcl_rap_datatype"),
            required: true,
            LOV: L("lcl_rap_datatypeLOV") // "varchar;Tekst;date;Datum;datetime;Datumtijd;float;Float;currency;Bedrag;number;Getal"
        },
        "sequence": {
            dbs: "fac_usrrap_cols_volgnr",
            typ: "number",
            label: L("lcl_rap_sequence"),
            required: true
        },
        "caption": {
            dbs: "fac_usrrap_cols_caption",
            typ: "varchar",
            label: L("lcl_rap_caption"),
            required: true,
            translate: true
        },
        "filter": {
            dbs: "fac_usrrap_cols_filter",
            typ: "varchar",
            label: L("lcl_rap_filter"),
            LOV: L("lcl_rap_filterLOV") //  "A;Automatisch"
        },
        "filterdefault": {
            dbs: "fac_usrrap_cols_filterdefault",
            typ: "varchar",
            label: L("lcl_rap_filterdefault")
        },
        "visible": {
            dbs: "fac_usrrap_cols_visible",
            typ: "varchar",
            label: L("lcl_rap_visible"),
            required: true,
            LOV: L("lcl_rap_visibleLOV"), // "V;Visible;I;Invisible;H;hidden"
            emptyoption: null
        },
        "group": {
            dbs: "fac_usrrap_cols_group",
            typ: "varchar",
            label: L("lcl_rap_groupby"),
            required: true,
            LOV: L("lcl_rap_groupbyLOV"),
            emptyoption: null
        },
        "fac_usrrap_key": {
            dbs: "fac_usrrap_key",
            typ: "key",
            hidden_fld: true
        },
        "expression": {
            dbs: "fac_usrrap_cols_expression",
            typ: "memo",
            label: "sql-expressie",
            readonly: true
        }
    };

    this.fnrowClass = function _fnrowClass(oRs)
    {
        return "report_" + oRs.Fields("visible").Value.id;
    }

    this.list = {
        columns: [
            "sequence",
            "name",
            "caption",
            "datatype",
            "visible",
            "filter",
            "group"
        ],
        rowClass: this.fnrowClass
    };

    this.hook_pre_edit = function (obj)
    {
        this.fields.filter.LOV = api2.filterLOV(L("lcl_rap_filterLOV"), "A,E,R"); // De meesten mogen L(ike) niet
        if ((obj.datatype && !obj.expression) || !user.has("WEB_FACTAB"))
        {
            switch (obj.datatype.id)
            {
                case "date":
                case "datetime":
                    this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G,D,W,M,Q,Y,H");
                    this.fields.datatype.LOV = api2.filterLOV(L("lcl_rap_datatypeLOV"), "date,datetime,time");
                    break;
                case "time":
                   this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G,H");
                    this.fields.datatype.LOV = api2.filterLOV(L("lcl_rap_datatypeLOV"), "date,datetime,time");
                    break;
                case "varchar":
                    this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G");
                    this.fields.datatype.readonly = true;
                    this.fields.filter.LOV = L("lcl_rap_filterLOV"); // die mag alle filters
                    break;
                case "html":
                    this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G");
                    this.fields.datatype.readonly = true;
                    this.fields.filter.LOV = L("lcl_rap_filterLOV");
                    break;
                case "float":
                case "number":
                case "currency":
                    this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G,S,C,A");
                    this.fields.datatype.LOV = api2.filterLOV(L("lcl_rap_datatypeLOV"), "float,currency,number");
                    break;
                default:
                    this.fields.group.LOV = api2.filterLOV(L("lcl_rap_groupbyLOV"), "G");
            }
        }

        if (user.has("WEB_FACTAB") && (obj.expression || !obj.id))
        {
            this.fields.expression.readonly = false;
            this.fields.name.insertonly = false;
        }
    };

    this.hook_pre_post = function (params, obj)
    {
        if (obj.id == -1)
            this.fields.expression.readonly = false;
    }

    // Maakt de default fac_usrrap_cols records aan aan de hand van de view of werk ze bij
    this._view2columns = function (usrrap_key, lastrefresh, oldcols)
        {
            var sql = "SELECT fac_usrrap_view_name"
                    + "     , fac_usrrap_functie"
                    + "  FROM fac_usrrap"
                    + " WHERE fac_usrrap_key = " + usrrap_key;
            var oRs = Oracle.Execute(sql);
            var view_name = oRs("fac_usrrap_view_name").Value;
            var is_view_name = (oRs("fac_usrrap_functie").Value & 2) == 0;  // niet de procedures
            oRs.Close();

            if (!is_view_name) return;

            // Rapport kan ook zijn XXXX.RAP_YYYYY?
            // PF: jawel, maar dan is het een procedure waarvan je de structuur niet zo op kunt vragen?!
            // Vandaar de regel hiervoor, die zorgt dat we hier al weg zijn. Ik denk dat de shortname dan verder
            // niet meer voorkomt eigenlijk.
            var view_name_short = view_name.toUpperCase();
            var i = view_name_short.indexOf('.');
            if (i != -1)
                view_name_short = view_name_short.substring(i+1);

            var hasFACFAC = user.checkAutorisation("WEB_FACFAC", true); // Die mag ook tabellen doen
            var sql = "SELECT object_name, object_type, last_ddl_time, status"
                    + "  FROM user_objects"
                    + " WHERE  "
                    +  (hasFACFAC ?  "object_type IN  ('VIEW', 'TABLE')" : " object_type = 'VIEW'")
                    + "   AND object_name = UPPER(" + safe.quoted_sql(view_name_short) + ")";
            var oRs = Oracle.Execute(sql);
            if (oRs.Eof)
                abort_with_warning("Unable to determine columns for report view '{0}'!".format(view_name_short));

            if (oRs("status").Value != 'VALID') // Gebeurt nog wel eens na een dump-refresh
            {
                if (oRs("object_type").Value == 'VIEW')
                {
                    __DoLog("Auto compile invalid {0}".format(view_name_short));
                    Oracle.Execute("ALTER VIEW {0} COMPILE".format(view_name_short));
                    oRs.Close();
                    var oRs = Oracle.Execute(sql); // Tweede poging
                }
            }
            if (oRs("status").Value != 'VALID')
                abort_with_warning("Report view '{0}' is INVALID!".format(view_name_short));

            var view_ddl_time = new Date(oRs("last_ddl_time").Value);
            oRs.Close();
            var mustRefresh = (oldcols.length == 0 || !lastrefresh || view_ddl_time > lastrefresh);

            if (!mustRefresh)
                return;

            var sequence = 10;
            var sql = "SELECT INITCAP(column_name) column_name"
                    + "     , data_type"
                    + "     , data_length"
                    + "     , data_precision"
                    + "     , data_scale"
                    + "  FROM user_tab_columns"
                    + " WHERE table_name = UPPER(" + safe.quoted_sql(view_name_short) + ")"
                    + " ORDER BY column_id";
            var oRs = Oracle.Execute(sql);
            var newcols = [];
            while (!oRs.eof)
            {
                var colname = oRs.Fields("column_name").Value.toLowerCase();
                var colpar = { caption: oRs.Fields("column_name").Value, // Met de initcap
                               name: colname,
                               datatype: "varchar",
                               group: 'G',
                               sequence: sequence,
                               visible: 'V',
                               filter: null
                             };
                sequence += 10;
                var ucolname = colname.toUpperCase();

                var special_cols = ucolname.match(/^(HIDE_F_|FCLT_X_|FCLT_3D_|FCLT_KEY|FCLT_F_|HTML_|FCLT_D_|FCLT_C_)/);
                if (special_cols)
                    colpar.caption = colpar.caption.substring(special_cols[0].length);
                colpar.caption = colpar.caption.replace(/\_/g, " "); // underscores er uit

                if (ucolname.match(/^(HIDE_F_|FCLT_3D_|FCLT_KEY)/))
                    colpar.visible = 'H'; // Hidden

                if (ucolname.match(/^(FCLT_X_)/))
                    colpar.visible = 'I'; // Invisible, je mag hem wel aanzetten

                if (view_name_short.match(/_V_UDR/))
                    colpar.visible = 'H'; // Hidden, die hebben te veel kolommen

                if (ucolname.match(/^(FCLT_F_|FCLT_X_)/))
                    colpar.filter = 'A';
                if (ucolname.match(/^(FCLT_D_)/))
                    colpar.filter = 'R'; // Altijd range

                var ora_type = oRs("data_type").Value;
                var ora_length = oRs("data_length").Value;
                var ora_precision = oRs("data_precision").Value;
                var ora_scale = oRs("data_scale").Value;

                if (ucolname.match("^FCLT_C_")) // Currency
                {
                   colpar.datatype = 'currency';
                }
                else if (ucolname.match("^HTML_"))
                {
                   colpar.datatype = 'html';
                }
                else if (ora_type == 'NUMBER')
                {
                   colpar.datatype = 'number';
                   if (ora_scale > 0)
                   {
                       colpar.datatype = 'float';
                   }
                }
                else if (ora_type == "DATE") //
                {
                   colpar.datatype = 'date';
                }

                newcols.push(colpar);
                oRs.MoveNext();
            }
            oRs.Close();

            // Vervallen kolommen
            for (var o = 0; o < oldcols.length; o++)
            {
                var found = false;
                for (var n = 0; !found && n < newcols.length; n++)
                {
                    if (newcols[n].name == oldcols[o].name)
                        found = true;
                }
                if (!found)
                {
                    __Log("Dropping column {0}".format(oldcols[o].name));
                    // We kennen geen REST_DELETE
                    var sql = "DELETE fac_usrrap_cols"
                            + " WHERE fac_usrrap_cols_expression IS NULL"
                            + "   AND fac_usrrap_cols_key = " + oldcols[o].id;
                    Oracle.Execute(sql);
                }
            }

            // Nieuwe en gewijzigde kolommen
            for (var n = 0; n < newcols.length; n++)
            {
                var found = false;
                for (var o = 0; !found && o < oldcols.length; o++)
                {
                    if (newcols[n].name == oldcols[o].name)
                    {
                        found = true;
                        if (newcols[n].datatype == oldcols[o].datatype.id)
                            continue;
                        // wisseling binnen float,currency,number is niet erg
                        if (inArray(newcols[n].datatype, "float,currency,number".split(",")) &&
                            inArray(oldcols[o].datatype.id, "float,currency,number".split(",")))
                            continue;
                        // wisseling binnen date,datetime,time is ook niet erg
                        if (inArray(newcols[n].datatype, "date,datetime,time".split(",")) &&
                            inArray(oldcols[o].datatype.id, "date,datetime,time".split(",")))
                            continue;

                        // datatype aanpassen
                        __Log("Updating datatype column {0} to {1}".format(oldcols[o].name, newcols[n].datatype));
                        this._REST_PUT({}, { datatype: newcols[n].datatype }, oldcols[o].id)
                    }
                }
                if (!found)
                {
                    __Log("Adding column {0}".format(newcols[n].name));
                    var params = { internal: true };
                    this._REST_POST(params, newcols[n], usrrap_key); // internal _REST_POST is er altijd
                }
            }

            // update lastrefresh. reports.REST_PUT is mij te kritisch op rechten
            var sql = "UPDATE fac_usrrap"
                    + "   SET fac_usrrap_col_refresh = SYSDATE"
                    + " WHERE fac_usrrap_key = " + usrrap_key;
            Oracle.Execute(sql);
        },

    this.REST_GET = function _GET(params)
        {
            // De modal detail popup gebruikt dit echter nog wel.
            var query = api2.sqlfields(params, this );
            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 " )
                    + " ORDER BY fac_usrrap_cols_volgnr";

            var json = api2.sql2json (params, sql, this );
            if (!json.length)
            {
                INTERNAL_ERROR_MISSING_COLUMNS;
            }

            return json;
        };
    this.REST_PUT = function (params, jsondata, the_key) /* update columns */
        {
            var valid_char = /^[_0-9a-zA-Z]+$/; // Alleen deze tekens zijn geldig!
            if (!valid_char.test(jsondata.name))
                abort_with_warning(L("lcl_rap_column_invalid"));

            var dbfields = api2.update_fields(params, this, jsondata); // Build updater

            var sql = buildUpdate("fac_usrrap_cols", dbfields, { noValidateToken: true }) + " fac_usrrap_cols_key = " + the_key;
            var err = Oracle.Execute(sql, true);
            if (err.friendlyMsg)
                abort_with_warning(err.friendlyMsg);

            return { key: the_key };
        };
    this._REST_PUT = this.REST_PUT; // Interne is er altijd

    // Only internally used by report_clone and _view2columns
    this._REST_POST = function (params, jsondata, parent_key) /* insert columns */
        {
            var valid_char = /^[_0-9a-zA-Z]+$/;
            if (!valid_char.test(jsondata.name))
                abort_with_warning(L("lcl_rap_column_invalid"));

            var dbfields = api2.update_fields(params, this, jsondata); // Build updater

            dbfields["id"] = this.fields["id"];
            if (parent_key && !jsondata.fac_usrrap_key)
                dbfields["xxx"] = { dbs: "fac_usrrap_key", typ: "key", val: parent_key };

            var rapIns = buildInsert("fac_usrrap_cols", dbfields, { noValidateToken: true });
            var col_key = rapIns.sequences["fac_usrrap_col_key"];

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

            return { key: col_key, warning: "" };
        }
    // REST_DELETE: not supported

    if (rap_key > 0)
    {
        // var rap_data = model_reportsx(rap_key); te veel recursie
        var sql = "SELECT fac_usrrap_functie"
                + "  FROM fac_usrrap"
                + " WHERE fac_usrrap_key = " + rap_key;
        var oRs = Oracle.Execute(sql);
        var functie = oRs("fac_usrrap_functie").Value || 0;
        oRs.Close();
        if (functie & 32) // template
            this.REST_PUT = false;
    }

    if (params.internal || user.has("WEB_FACTAB"))
    {
        this.REST_POST = this._REST_POST; // Voor de clone-report
    }
}

%>