<% /* $Revision$ $Id$ File: model_reports.inc Description: rapport model voor *de data* van een specifiek rapport. Zal rapporten uitvoeren dus. Het *bewerken* van rapporten gaat via model_reportsx Parameters: Context: Notes: */ %> <% function model_reports_template() { this.table = "te bepalen"; this.primary = null; this.records_name = "te bepalen"; this.record_name = "te bepalen"; this.fields = {/* te bepalen */}; this.autfunction = null; this.REST_GET = report_GET; this.REST_PUT = false; this.REST_POST = false; this.REST_DELETE = false; // Let op: wordt ook vanuit fac/fac_report.asp gebruikt voor xml-styled rapporten // Ook: de bevroren filtervelden er bij this.addfixedfilters = function _addfixedfilters(filter) { filter.fclt_3d_user_key = user_key; // Die is gemakkelijk for (var fld in this.fields) { var field = this.fields[fld]; if (!field.filterdefault) continue; var fixed = field.filterdefault.substr(0,2) == ':!'; if (!(fld in filter) || fixed) // Filter/fixed? { var defs = scf.parseDefault(field.filterdefault, field); if (field.filter == 'like' || field.filter == 'exact' || !("filter" in field)) { if (fixed || !(fld in filter)) filter[fld] = defs.vmin; } else if (field.filter == "range") { if (fixed || !("start_" + fld in filter)) filter["start_" + fld] = defs.vmin; if (fixed || !("end_" + fld in filter)) filter["end_" + fld] = defs.vmax; } } } }; // Let op: wordt ook vanuit fac/fac_report.asp gebruikt voor xml-styled rapporten // Deze zijn complexer en red ik niet via addfixedfilters this.add3d = function _add3d(wheres) { if (this.autfunction) { var sql = "SELECT fac_functie_code, fac_functie_min_level, fac_functie_discipline" + " FROM fac_functie f" + " WHERE f.fac_functie_key = " + this.authparams.autfunctionkey; var oRs = Oracle.Execute(sql); var bMetALGAuth = (oRs("fac_functie_min_level").Value & 8) == 8; var bMetPRSAuth = (oRs("fac_functie_min_level").Value & 4) == 4; var bMetDiscipline = (oRs("fac_functie_discipline").Value == 1); oRs.Close(); if (bMetALGAuth && "fclt_3d_regio_key" in this.fields && this.authparams.ALGreadlevel > -1) { wheres.push(" fclt_3d_regio_key IN" + "(SELECT alg_regio_key " + " FROM fac_v_my_regions " + " WHERE niveau ="+this.authparams.ALGreadlevel + " AND prs_perslid_key="+user_key+")"); } if (bMetALGAuth && "fclt_3d_district_key" in this.fields && this.authparams.ALGreadlevel > -1) { wheres.push(" fclt_3d_district_key IN" + "(SELECT alg_district_key " + " FROM fac_v_my_districts " + " WHERE niveau ="+this.authparams.ALGreadlevel + " AND prs_perslid_key="+user_key+")"); } if (bMetALGAuth && "fclt_3d_locatie_key" in this.fields && this.authparams.ALGreadlevel > -1) { wheres.push(" fclt_3d_locatie_key IN" + "(SELECT alg_locatie_key FROM fac_v_my_locations " + " WHERE niveau ="+this.authparams.ALGreadlevel + " AND prs_perslid_key="+user_key+")"); } if (bMetALGAuth && "fclt_3d_gebouw_key" in this.fields && this.authparams.ALGreadlevel > -1) { wheres.push(" fclt_3d_gebouw_key IN" + "((SELECT alg_gebouw_key FROM fac_v_my_buildings " + " WHERE niveau ="+this.authparams.ALGreadlevel + " AND prs_perslid_key="+user_key+"))"); } if (bMetPRSAuth && "fclt_3d_afdeling_key" in this.fields) { if (this.authparams.PRSreadlevel == 0) { wheres.push(" fclt_3d_afdeling_key IN" + " (SELECT a.prs_afdeling_key FROM prs_v_afdeling a " + " WHERE a.prs_bedrijf_key = " + user.afdeling().prs_bedrijf_key() + ")"); } else if (this.authparams.PRSreadlevel > 0) { wheres.push(" fclt_3d_afdeling_key IN" + "(SELECT prs_afdeling_key FROM prs_v_afdeling_familie a" + " WHERE a.prs_afdeling_elder_key IN" + " (SELECT aa.prs_afdeling_elder_key" + " FROM prs_v_afdeling_familie aa" + " WHERE aa.prs_afdeling_key = " + user.prs_afdeling_key() + " AND aa.niveau = " + this.authparams.PRSreadlevel + "))"); } } if (bMetDiscipline && "fclt_3d_discipline_key" in this.fields) { var disciplines = []; var sql = "SELECT ins_discipline_key" + " FROM fac_v_my_disciplines " + " WHERE fac_functie_code = " + safe.quoted_sql(this.autfunction) + " AND prs_perslid_key=" + user_key; var oRs = Oracle.Execute(sql); while (!oRs.Eof) { disciplines.push(oRs("ins_discipline_key").Value); oRs.MoveNext(); } oRs.Close(); if (!disciplines.length) disciplines.push(-1); wheres.push("fclt_3d_discipline_key IN (" + disciplines.join(",") + ")"); } } if ("fac_functie_key" in this.fields) { var whereor = ["fac_functie_key IS NULL"] whereor.push("fac_functie_key IN" + " (SELECT w.fac_functie_key" + " FROM fac_v_webgebruiker W" + " WHERE w.prs_perslid_key = " + user_key + ")"); if (user.has("WEB_UDRMAN")) { whereor.push("fac_functie_key NOT IN" + " (SELECT fac_functie_key" + " FROM fac_functie" + " WHERE fac_functie_code IN ('WEB_FACFAC', 'WEB_FACTAB'))"); } wheres.push("(" + whereor.join(" OR ") +")"); } }; this.search = { title: L("lcl_usrrap_report"), filters: [] } } // Voltooi model_reports_template voor een bepaald rapport function model_reports(fac_usrrap_key) { var reportsx = new model_reportsx(fac_usrrap_key, { include: ["columns"], internal: true }); // Internal om wel alle properties te krijgen var usrrap = reportsx.data; user.auth_required_or_abort(usrrap); var model = new model_reports_template(); model.table = usrrap.viewname; if (usrrap.styling && usrrap.styling.id & 8) // Tabelized model.table = "T_" + usrrap.viewname.substring(0,28); // anders mogelijk te lang voor Oracle model.record_name = usrrap.viewname.substring(0,1) + usrrap.viewname.substring(1).toLowerCase(); model.records_name = model.record_name + "s"; model.record_title = usrrap.name; // (initiele) titel bij urllink model.records_title = usrrap.name; model.autorefresh = usrrap.autorefresh; model.styling = usrrap.styling; model.graph_options = usrrap.graph_options; model.graph_type = usrrap.graph_type; model.list = { columns : [], groupby : [], canGroup: true, canCSV: true, autoCount: usrrap.count.id, autoPivot: usrrap.pivot.id, autoGraph: usrrap.graph.id, autoRatio: usrrap.ratio.id, autoCond: usrrap.condition.id }; if (model.styling && (model.styling.id & 1)) model.list.fixedColumns = true; model.search.title = L("lcl_usrrap_report") + " " + usrrap.name; var keyfield = null; if (usrrap.urllink) { var mtc = usrrap.urllink.match(/(.*)\{(.*)\}/); if (!mtc || mtc.length != 3) shared.simpel_page("Urllink {0} in rapport moet formaat 'xxxx/yyy.asp?key={keyveld}' hebben.".format(usrrap.urllink)); model.list.default_url = mtc[1]; // Voorkom dat hij binnen menu-wrapper wordt getoond if (mtc[1].indexOf("?u=") == 0) model.list.default_url = "?internal=1&" + mtc[1].substring(1); keyfield = mtc[2]; } if (usrrap.authorization) { var sql = "SELECT fac_functie_code FROM fac_functie WHERE fac_functie_key = " + usrrap.authorization.id; var oRs = Oracle.Execute(sql); model.autfunction = oRs("fac_functie_code").Value; model.authparams = user.checkAutorisation(model.autfunction); // leesrechten is wel het minste oRs.Close(); } for (var i = 0; i < usrrap.columns.length; i++) { var label = safe.html(usrrap.columns[i].caption); // (lcl) Labels worden altijd geacht html-safe te zijn label = label.replace(/\\n/g, "
"); // Voor overulen van gewone LCL's ligt de verantwoordelijkheid // bij een beheerder maar rapportages wordt misschien wel fe var fixedlabel = false; if (label && label.substr(0,1) == "!") // Als een label begint met een ! wordt hij niet aangepast bij groepperen { fixedlabel = true; label = label.substr(1); } var field = { dbs: usrrap.columns[i].name, label: label, orglabel: label, fixedlabel: fixedlabel, sql: usrrap.columns[i].expression, typ: usrrap.columns[i].datatype.id, orgtyp: usrrap.columns[i].datatype.id, group: usrrap.columns[i].group, visible: usrrap.columns[i].visible.id }; if (field.typ == "number" && field.dbs.match(/_key$/i)) // Vooral de FCLT_3D filters field.typ = "key"; if (field.typ == "number") { field.total = true; field.autofloat = true; // we weten het niet zeker } if (field.typ == "currency") field.total = true; if (field.typ == "varchar") field.autolike = true; if (field.visible == 'H' && field.dbs != keyfield) // Keyfield willen we eigenlijk altijd wel indien mogelijk field.hidden = true; if (usrrap.columns[i].filter) { if (usrrap.columns[i].filter.id == "A") { field.filter = { "date": "range", "datetime": "range", "time": "range", "varchar": "exact" }[ field.typ ] || "exact"; } else { field.filter = api2.splitLOV("E;exact;L;like;R;range")[usrrap.columns[i].filter.id] } field.filterdefault = usrrap.columns[i].filterdefault; } var fld = field.dbs.toLowerCase(); if (fld == keyfield) fld = "id"; model.fields[fld] = field; if (usrrap.columns[i].visible.id == 'V') { model.list.columns.push(fld); model.list.groupby.push(field.group.id); } if (usrrap.columns[i].filter) model.search.filters.push(fld); } if ((model.list.autoCount & 2) == 2) { model.list.columns.push("scf_count"); model.list.groupby.push(""); } if ((model.list.autoRatio & 2) == 2) { model.list.columns.push("scf_ratio"); model.list.groupby.push(""); } return model; } function report_GET(params) { // Als parameters niet in de url zijn meegegeven is waarschijnlijk rechtstreeks de list // aangeroepen. Pak dan de geconfigureerde defaults def(params.filter, "scf_pivot", (this.list.autoPivot & 2)?2:0); def(params.filter, "scf_graph", (this.list.autoGraph & 2) == 2?"on":""); def(params.filter, "scf_cond", (this.list.autoCond & 2) == 2?"on":""); if (!params.columns) { params.columns = this.list.columns; params.groupby = this.list.groupby; } var groupbys = []; var hasAggregate = false; // Als er uitsluitend GROUP BY is zonder ook maar een enkele // aggregate functie zouden effectief dubbelen verwijderd worden. // Dat mag niet zo maar if (params.columns && inArray("scf_count", params.columns)) { if (params.columns.length == 1) shared.simpel_page(L("lcl_empty_rstable")); this.fields["scf_count"] = { dbs: "scf_count", sql: "COUNT(*)", typ: "number", label: L("lcl_count"), orglabel: L("lcl_count"), total: true }; hasAggregate = true; } if (params.columns && inArray("scf_ratio", params.columns)) { if (params.columns.length == 1) shared.simpel_page(L("lcl_empty_rstable")); this.fields["scf_ratio"] = { dbs: "scf_ratio", sql: "RATIO_TO_REPORT(COUNT(*)) OVER() * 100", typ: "float", label: L("lcl_ratio"), orglabel: L("lcl_ratio"), total: true }; hasAggregate = true; } if (params.columns && params.groupby && params.groupby.length > 0) { if (params.columns.length != params.groupby.length) { __DoLog(params); INTERNAL_ERROR_GROUP_MISMATCH; } // Bouw het fields object opnieuw op in de volgorde van params.columns. Goed voor sorteren later. var has_hide_f = false; for (var fld in this.fields) // hide_f sorteer velden maakt het veel te ingewikkeld { if (this.fields[fld].dbs.match(/^hide_f/)) { has_hide_f = true; break; } } if (!has_hide_f) { var newfields = {}; for (var i=0; i < params.columns.length; i++) { var fld = params.columns[i]; if (fld in this.fields) { newfields[fld] = this.fields[fld]; delete this.fields[fld]; } } for (fld in this.fields) newfields[fld] = this.fields[fld]; this.fields = newfields; } for (var fld in this.fields) { if (!inArray(fld, params.columns)) { if (this.list && this.list.default_url && fld == "id" && params.groupby[0] != 'U') groupbys.push(this.fields["id"].dbs); else { if (params.groupby[0] != 'U' && this.fields[fld].dbs.match(/^hide_f/)) // Doorgaans meenemen om te sorteren { this.fields[fld].hidden = false; params.columns.splice(0, 0, fld); params.groupby.splice(0, 0, "G"); } else this.fields[fld].hidden = true; // Onnodige velden niet ophalen } } } for (var i=0; i < params.columns.length; i++) { var fld = params.columns[i]; if (fld == "scf_count" || fld == "scf_ratio" ) continue; var field = this.fields[fld]; var field_expression = (field.sql ? field.sql : field.dbs); var field_group = (field.sql && field.sql.toUpperCase().indexOf("SELECT") > 0 ? i+1 : null); // Voorkom dat er een subselect in de group_by komt te staan. if (!field) shared.internal_error("Column {0} not found in report".format(fld)); field.keepdbsforfilter = true; field.orgtyp = field.typ; // wil grafiek graag weten hasAggregate = hasAggregate || (params.groupby[i] || "G") != "G"; switch (params.groupby[i] || "G") { case "G": if (!field_group) groupbys.push(field_expression); break; case "U": field.sql = "DISTINCT(" + field_expression + ")"; break; case "S": field.sql = "SUM(" + field_expression + ")"; if (!field.fixedlabel) field.label = L("lcl_usrrap_label_SUM").format(field.label); field.newtyp = "float"; field.total = true; break; case "C": field.sql = "COUNT(" + field_expression + ")"; if (!field.fixedlabel) field.label = L("lcl_usrrap_label_CNT").format(field.label); field.newtyp = "number"; field.total = true; break; case "A": field.sql = "AVG(" + field_expression + ")"; if (!field.fixedlabel) field.label = L("lcl_usrrap_label_AVG").format(field.label); field.total = false; field.newtyp = "float"; break; case "H": if (field.typ == "date" || field.typ == "datetime") field.sql = "TO_CHAR(" + field_expression + ", 'YYYY-MM-DD HH24')||':00'"; else field.sql = "TO_CHAR(" + field_expression + ", 'HH24')||':00'"; field.newtyp = "varchar"; groupbys.push(field_group || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_HOUR").format(field.label); break; case "D": field.sql = "TRUNC(" + field_expression + ", 'DD')"; field.newtyp = "date"; groupbys.push(field_group || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_DAY").format(field.label); break; case "W": field.sql = "TO_CHAR(" + field_expression + ", 'YYYY-IW')"; field.newtyp = "varchar"; groupbys.push(field_group || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_WEEK").format(field.label); break; case "M": field.sql = "TO_CHAR(" + field_expression + ", 'YYYY-MM')"; field.newtyp = "varchar"; groupbys.push(field_group || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_MONTH").format(field.label); break; case "Q": field.sql = "TO_CHAR(" + field_expression + ", 'YYYY-Q')"; field.newtyp = "varchar"; groupbys.push(field.sql || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_QUARTER").format(field.label); break; case "Y": field.sql = "TO_CHAR(" + field_expression + ", 'YYYY')"; field.newtyp = "varchar"; groupbys.push(field_group || field.sql); if (!field.fixedlabel) field.label = L("lcl_usrrap_label_YEAR").format(field.label); break; } } } for (var i=0; i < params.columns.length; i++) { var fld = params.columns[i]; var field = this.fields[fld]; if (!field) shared.internal_error("Column {0} not found in report".format(fld)); if (field.dbs.match(/^hide_f/)) // Die willen we uiteindelijk niet zien { params.columns.splice(i, 1); params.groupby.splice(i, 1); i--; } } var query = api2.sqlfields(params, this); if (!query.selects.length) abort_with_warning("No columns defined for report"); if ("addfixedfilters" in this) this.addfixedfilters(params.filter); var wheres = api2.sqlfilter(params, this); if (params.filter.scf_cond == "on") { if (wheres.length) wheres = ["(" + wheres.join(" OR ") + ")"]; } if ("add3d" in this) this.add3d(wheres); query.wheres = query.wheres.concat(wheres); var sql = "SELECT " + query.selects.join(", ") + " FROM " + query.tables.join(", ") + (query.wheres.length ? " WHERE " + query.wheres.join(" AND " ) : "") if (groupbys.length && hasAggregate) sql += " GROUP BY " + groupbys.join(", "); // Dit moet op volgorde van params.columns, niet per se query.selects // Gelukkig hebben we fields ondertussen al in de goede volgorde gezet var order = []; for (var i=0; i < query.selects.length; i++) order.push(i+1) sql += " ORDER BY " + order.join(", "); var maxcnt = params.filter.showall==1?S("qp_maxrows2"): (params.filter.ismobile?S("qp_maxrows_mobile"):S("qp_maxrows")); if (params.filter.scf_pivot == 1) params.filter.nolimit = true; var sql = "SELECT * FROM (" + sql + ")" + (params.filter.nolimit? "" : " WHERE ROWNUM <= " + (maxcnt+1)); // Eentje extra om overflow-melding te triggeren in resultsettable for (var fld in this.fields) { // Overrules voor groeperingen this.fields[fld].typ = this.fields[fld].newtyp || this.fields[fld].typ; } params.errmsg = true; // Eventuele fout in query detecteren. var json = api2.sql2json (params, sql, this); if (params.filter.scf_pivot == 1) // Pivoteren!!! { // De laatste colom beschouwen we als datacol die in de 'cellen' komt // De een-na-laatste colom gaan we pivotteren naar kolomkoppen var datacol = params.columns[params.columns.length - 1]; var datagroup = params.groupby[params.groupby.length - 1]; var pivotcol = params.columns[params.columns.length - 2]; // Eerst een keer door de json data om de unieke pivotcol waardes te bepalen var pcolumns = {}; for (var i = 0; i < json.length; i++) { if (pivotcol in json[i]) { var colname = json[i][pivotcol]; if (colname in pcolumns) { pcolumns[colname].cnt ++; // we doen overigens niets met deze telling } else { var collabel; switch (this.fields[pivotcol].typ) { case "datetime": collabel = toDateTimeString(colname); break; case "date": collabel = toDateString(colname); break; case "time": collabel = toTimeString(colname); break; default: collabel = colname; } pcolumns[colname] = { cnt: 1, label: collabel }; } } } var newdata = []; var newrecord = null; for (var i = 0; i < json.length; i++) { var olddata = json[i]; var equal = !!newrecord; // Is het huidige record nog gelijk aan het vorige record (exclusief pivotcol en datacol) for (var j = 0; equal && j < params.columns.length - 2; j++) { var col = params.columns[j]; equal = String(olddata[col]) == String(newrecord[col]); // Conversie naar String omdat date-objecten // anders niet matchen } if (!equal) { if (newrecord) newdata.push(newrecord); newrecord = {}; // Begin nieuwe regel in het rapport for (var k = 0; k < params.columns.length - 2; k++) { var col = params.columns[k]; newrecord[col] = olddata[col]; } newrecord[datacol] = 0; // als datacol numeriek was bouw hier een regelsom } newrecord[olddata[pivotcol]] = olddata[datacol]; newrecord[datacol] = newrecord[datacol] + olddata[datacol]; } if (newrecord) newdata.push(newrecord); // Originele pivotcol en datacol verwijderen params.columns.splice(params.columns.length - 2, 2); // Nieuwe pivot kolommen ook in fields toevoegen var cols = []; for (var j in pcolumns) cols.push(j); cols.sort(); for (j in cols) { params.columns.push(cols[j]); this.fields[cols[j]] = { label: safe.html(pcolumns[cols[j]].label), typ: this.fields[datacol].typ }; if (this.fields[datacol].typ == 'number' || this.fields[datacol].typ == 'float' || this.fields[datacol].typ == 'currency') { this.fields[cols[j]].total = true; } } // Een numerieke datacol voegen we (gesommeerd) achteraan toe behalve bij grafieken, daar laten we hem er af if (params.filter["scf_graph"] != "on" && params.filter["scf_cond"] != "on" && (this.fields[datacol].typ == 'number' || this.fields[datacol].typ == 'float' || this.fields[datacol].typ == 'currency') && datagroup != 'A' ) params.columns.push(datacol); this.total_count = newdata.length; json = newdata; } // Filterwaarden van de rapportages verzamelen. Filter parameters kunnen dan aan overzicht meegegeven worden zodat ze ook in de printafdruk worden vermeld. json.filterstring = {}; var filterstring = json.filterstring; var filter = params.filter; for (var flt in filter) { // Ik heb nu een parameter die is doorgegeven. // Alleen als een filterveld aanwezig is, wordt die parameter doorgegeven. Alleen dan is field gedefinieerd. var l_flt = flt; if (flt.substr(0, 6) == "start_") l_flt = flt.substr(6); else if (flt.substr(0, 4) == "end_") l_flt = flt.substr(4); var field = this.fields[l_flt]; if (field) { // Field is gedefinieerd, dan is het een parameter van een filterveld. // Bij date of datetime filters wordt er bij het label " Van" en " Tot" achter geplakt. var vantot = ""; var startend = ""; if (flt.substr(0, 6) == "start_") { vantot = " " + L("lcl_from"); startend = "start_"; } else if (flt.substr(0, 4) == "end_") { vantot = " " + L("lcl_to"); startend = "end_"; } var llabel; var lval; switch (field.orgtyp) { case "date": if (filter[flt] == "") lval = ""; else { lval = new Date(parseInt(filter[flt], 10)); lval = toDateString(lval); } llabel = "fclt_f_" + startend + field.orglabel + vantot; break; case "datetime": if (filter[flt] == "") lval = ""; else { lval = new Date(parseInt(filter[flt], 10)); lval = toDateTimeString(lval); } llabel = "fclt_f_" + startend + field.orglabel + vantot; break; default: lval = filter[flt]; llabel = "fclt_f_" + startend + field.orglabel + vantot; } // Als lval uit meerdere waarden bestaat dan is lval een object (array). if (typeof lval == "object") lval = lval.join(", "); filterstring[llabel] = lval; } } // De filterwaarden van de rapportage zitten nu in json.filterstring. // __Log("Response.Write(JSON.stringify(json.filterstring)) = " + JSON.stringify(json.filterstring)); return json; }; %>