UNPKG

jsharmony

Version:

Rapid Application Development (RAD) Platform for Node.js Database Application Development

514 lines (458 loc) 23.7 kB
/* Copyright 2017 apHarmony This file is part of jsHarmony. jsHarmony is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jsHarmony is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this package. If not, see <http://www.gnu.org/licenses/>. */ var Helper = require('./lib/Helper.js'); var _ = require('lodash'); var ejsext = require('./lib/ejsext.js'); module.exports = exports = {}; exports.getTabCode = function (req, res, fullmodelid, onComplete) { var _this = this; if (!this.jsh.hasModel(req, fullmodelid)) throw new Error('Error: Model ' + fullmodelid + ' not found in collection.'); var model = this.jsh.getModel(req, fullmodelid); if (!(model.tabcode)) throw new Error('Error: Tabcode required for ' + fullmodelid + ' tabcode lookup.'); var Q = req.query; if (!Helper.hasModelAction(req, model, 'B')) { Helper.GenError(req, res, -11, _this._tP('Invalid Model Access for @fullmodelid', { fullmodelid })); return; } var keylist = this.getKeyNames(model.fields); var tabcodelist = [model.tabcode]; var db = _this.jsh.getModelDB(req, fullmodelid); var dbcontext = _this.jsh.getDBContext(req, model, db); if (req.query.action == 'insert') { return onComplete(); } else if (!_.includes(['update','browse'],req.query.action)) { Helper.GenError(req, res, -9, 'Action not supported'); return; } for (let i = 0; i < keylist.length; i++) { var k = keylist[i]; if (!(k in Q)) { _this.jsh.Log.warning(fullmodelid + ' Tabcode: Missing querystring key ' + k); Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } } var sql_ptypes = []; var sql_params = {}; var verrors = {}; var datalockqueries = []; var selectfields = this.getFieldsByName(model.fields, tabcodelist); var keys = this.getKeys(model.fields); for (let i = 0; i < keys.length; i++) { var field = keys[i]; var fname = field.name; if (fname in Q) { var dbtype = _this.getDBType(field); sql_ptypes.push(dbtype); sql_params[fname] = _this.DeformatParam(field, Q[fname], verrors); } else { _this.jsh.Log.warning(fullmodelid + ' Tabcode: Missing parameter ' + fname); Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } } this.getDataLockSQL(req, model, model.fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockqueries.push(datalockquery); }, null, fullmodelid + ' Tab Code'); verrors = _.merge(verrors, model.xvalidate.Validate('K', sql_params)); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; } var sql = db.sql.getTabCode(_this.jsh, model, selectfields, keys, datalockqueries); this.ExecScalar(dbcontext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err) { _this.jsh.Log.error(err); Helper.GenError(req, res, -99999, 'An unexpected error has occurred'); return; } if (rslt && rslt[0]) { return onComplete(rslt[0]); } else { Helper.GenError(req, res, -1, 'Record not found'); return; } }, undefined, db); }; exports.addTitleTasks = function (req, res, model, Q, dbtasks, targetperm) { var title = undefined; var sql = ''; var fieldlist = []; var nodatalock = null; var db = this.jsh.getModelDB(req, model.id); var dbcontext = this.jsh.getDBContext(req, model, db); if (typeof model.title !== 'undefined'){ if(_.isString(model.title)) title = model.title; else if(model.title.insert && Helper.hasAction(targetperm,'I')){ if(_.isString(model.title.insert)) title = model.title.insert; else if(model.title.insert.sql){ sql = model.title.insert.sql; fieldlist = model.title.insert.sql_params; nodatalock = model.title.insert.nodatalock; } } else if((model.title.update||model.title.browse) && Helper.hasAction(targetperm,'U')){ if(model.title.update){ if(_.isString(model.title.update)) title = model.title.update; else if(model.title.update.sql){ sql = model.title.update.sql; fieldlist = model.title.update.sql_params; nodatalock = model.title.update.nodatalock; } } else if(model.title.browse){ if(_.isString(model.title.browse)) title = model.title.browse; else if(model.title.browse.sql){ sql = model.title.browse.sql; fieldlist = model.title.browse.sql_params; nodatalock = model.title.browse.nodatalock; } } } else if((model.title.update||model.title.browse) && Helper.hasAction(targetperm,'B')){ if(model.title.browse){ if(_.isString(model.title.browse)) title = model.title.browse; else if(model.title.browse.sql){ sql = model.title.browse.sql; fieldlist = model.title.browse.sql_params; nodatalock = model.title.browse.nodatalock; } } else if(model.title.update){ if(_.isString(model.title.update)) title = model.title.update; else if(model.title.update.sql){ sql = model.title.update.sql; fieldlist = model.title.update.sql_params; nodatalock = model.title.update.nodatalock; } } } else if(model.title.sql){ sql = model.title.sql; fieldlist = model.title.sql_params; nodatalock = model.title.nodatalock; } } if(!sql){ if(title) title = Helper.ResolveParams(req, title); dbtasks['_title'] = function(dbtrans, callback, transtbl){ return callback(null, title); }; return; } var _this = this; var fields = this.getFieldsByName(model.fields, fieldlist); var sql_ptypes = []; var sql_params = {}; var verrors = {}; var datalockqueries = []; for (var i = 0; i < fields.length; i++) { var field = fields[i]; var fname = field.name; if (fname in Q){ var dbtype = _this.getDBType(field); sql_ptypes.push(dbtype); sql_params[fname] = _this.DeformatParam(field, Q[fname], verrors); } } //Add DataLock parameters to SQL this.getDataLockSQL(req, model, model.fields, sql_ptypes, sql_params, verrors, function (datalockquery, dfield) { if(Helper.hasAction(targetperm,'I') && dfield.key) return false; datalockqueries.push(datalockquery); }, nodatalock, model.id + ' Title'); verrors = _.merge(verrors, model.xvalidate.Validate('*', sql_params, undefined, undefined, undefined, { ignoreUndefined: true })); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return false; } sql = db.sql.getTitle(_this.jsh, model, sql, datalockqueries); //Add parameters from querystring _this.ApplyQueryParameters(Q, sql, sql_ptypes, sql_params, model); dbtasks['_title'] = function (dbtrans, callback, transtbl) { _this.ApplyTransTblChainedParameters(transtbl, sql, sql_ptypes, sql_params, model.fields); db.Scalar(dbcontext, sql, sql_ptypes, sql_params, dbtrans, function (err, title, stats) { if (err) { _this.jsh.Log.error(err); Helper.GenError(req, res, -99999, 'An unexpected error has occurred'); return; } if(title) title = Helper.ResolveParams(req, title); return callback(null, title, stats); }); }; }; exports.getTitle = function (req, res, fullmodelid, targetperm, onComplete) { var _this = this; if (!this.jsh.hasModel(req, fullmodelid)) throw new Error('Error: Model ' + fullmodelid + ' not found in collection.'); var model = this.jsh.getModel(req, fullmodelid); if (!Helper.hasModelAction(req, model, targetperm)) { targetperm = 'B'; if (!Helper.hasModelAction(req, model, targetperm)) { Helper.GenError(req, res, -11, _this._tP('Invalid Model Access for @fullmodelid', { fullmodelid })); return; } } var dbtasks = {}; if(this.addTitleTasks(req, res, model, req.query, dbtasks, targetperm)===false) return; dbtasks['_title'](undefined, onComplete, null); }; exports.addDefaultTasks = function (req, res, model, Q, dbtasks) { var _this = this; var _defaults = {}; var modeldb = 'default'; if(model.db) modeldb = model.db; //Prepare Default Values Query var defaultCommands = {}; var dflt_verrors = {}; if(model.fields) for (var field_i = 0; field_i < model.fields.length; field_i++) { var field = model.fields[field_i]; if (!field.name) continue; if ('default' in field) { var dflt = field.default; if (_.isString(dflt) && (dflt.substr(0,3)=='js:')) continue; if (_.isString(dflt) || _.isNumber(dflt) || _.isBoolean(dflt)) _defaults[field.name] = field.default; else { var dflt_db = modeldb; if(dflt && dflt.db) dflt_db = dflt.db; if(!(dflt_db in defaultCommands)){ defaultCommands[dflt_db] = { dflt_ptypes: [], dflt_params: {}, dflt_sql_fields: [], }; } let defaultCommand = defaultCommands[dflt_db]; var sql = ''; if ('sql' in dflt) { sql = dflt.sql; } else { Helper.GenError(req, res, -99999, 'Custom Default Value requires SQL'); return false; } var dflt_sql_field_datalockqueries = []; var dflt_sql_field_param_datalocks = []; _this.getDataLockSQL(req, model, [dflt], defaultCommand.dflt_ptypes, defaultCommand.dflt_params, dflt_verrors, function (datalockquery) { dflt_sql_field_datalockqueries.push(datalockquery); }, dflt.nodatalock, field.name + '_' + model.id + '_dflt'); //Add lov parameters if ('sql_params' in dflt) { var dflt_pfields = _this.getFieldsByName(model.fields, dflt.sql_params); for (var i = 0; i < dflt_pfields.length; i++) { var dflt_pfield = dflt_pfields[i]; var dflt_pname = dflt_pfield.name; if (dflt_pname in defaultCommand.dflt_params) continue; defaultCommand.dflt_ptypes.push(_this.getDBType(dflt_pfield)); defaultCommand.dflt_params[dflt_pname] = null; if (dflt_pname in Q) { defaultCommand.dflt_params[dflt_pname] = _this.DeformatParam(dflt_pfield, Q[dflt_pname], dflt_verrors); _this.getDataLockSQL(req, model, model.fields, defaultCommand.dflt_ptypes, defaultCommand.dflt_params, dflt_verrors, function (datalockquery, dfield) { if (dfield != dflt_pfield) return false; dflt_sql_field_param_datalocks.push({ pname: dflt_pname, datalockquery: datalockquery, field: dfield }); return true; }, undefined, field.name + '_' + model.id + '_dflt_key'); dflt_verrors = _.merge(dflt_verrors, model.xvalidate.Validate('*', defaultCommand.dflt_params, dflt_pname)); } } } if (!_.isEmpty(dflt_verrors)) { Helper.GenError(req, res, -2, dflt_verrors[''].join('\n')); return false; } defaultCommand.dflt_sql_fields.push({ name: field.name, field: field, sql: sql, datalockqueries: dflt_sql_field_datalockqueries, param_datalocks: dflt_sql_field_param_datalocks }); } } else if(field.name in Q){ _defaults[field.name] = Q[field.name]; } } if(_.isEmpty(defaultCommands)){ defaultCommands[modeldb] = { dflt_ptypes: [], dflt_params: {}, dflt_sql_fields: [], }; } for(var dbid in defaultCommands){ let defaultCommand = defaultCommands[dbid]; var db = _this.jsh.getDB(dbid); var dbcontext = _this.jsh.getDBContext(req, model, db); var dflt_sql = db.sql.getDefaultTasks(_this.jsh, defaultCommand.dflt_sql_fields); //Add parameters from querystring _this.ApplyQueryParameters(Q, dflt_sql, defaultCommand.dflt_ptypes, defaultCommand.dflt_params, model); //Execute Query dbtasks['_defaults_'+dbid] = function (dbtrans, callback, transtbl) { _this.ApplyTransTblChainedParameters(transtbl, dflt_sql, defaultCommand.dflt_ptypes, defaultCommand.dflt_params, model.fields); if (dflt_sql) { db.Row(dbcontext, dflt_sql, defaultCommand.dflt_ptypes, defaultCommand.dflt_params, dbtrans, function (err, rslt, stats) { if (err == null) { for (var f in rslt) { if (rslt[f] == null) _defaults[f] = ''; else _defaults[f] = rslt[f].toString(); } } callback(err, _defaults, stats); }); } else { callback(null, _defaults); } }; } }; exports.addBreadcrumbTasks = function (req, res, model, Q, dbtasks, targetperm) { var _this = this; var verrors = {}; if (!model.breadcrumbs) return; var sql = null; var fieldlist = []; var nodatalock = null; var db = this.jsh.getModelDB(req, model.id); var dbcontext = this.jsh.getDBContext(req, model, db); if(model.breadcrumbs.insert && Helper.hasAction(targetperm,'I')){ if(model.breadcrumbs.insert.sql){ sql = model.breadcrumbs.insert.sql; fieldlist = model.breadcrumbs.insert.sql_params; nodatalock = model.breadcrumbs.insert.nodatalock; } } else if((model.breadcrumbs.update||model.breadcrumbs.browse) && Helper.hasAction(targetperm,'U')){ if(model.breadcrumbs.update){ if(model.breadcrumbs.update.sql){ sql = model.breadcrumbs.update.sql; fieldlist = model.breadcrumbs.update.sql_params; nodatalock = model.breadcrumbs.update.nodatalock; } } else if(model.breadcrumbs.browse){ if(model.breadcrumbs.browse.sql){ sql = model.breadcrumbs.browse.sql; fieldlist = model.breadcrumbs.browse.sql_params; nodatalock = model.breadcrumbs.browse.nodatalock; } } } else if((model.breadcrumbs.update||model.breadcrumbs.browse) && Helper.hasAction(targetperm,'B')){ if(model.breadcrumbs.browse){ if(model.breadcrumbs.browse.sql){ sql = model.breadcrumbs.browse.sql; fieldlist = model.breadcrumbs.browse.sql_params; nodatalock = model.breadcrumbs.browse.nodatalock; } } else if(model.breadcrumbs.update){ if(model.breadcrumbs.update.sql){ sql = model.breadcrumbs.update.sql; fieldlist = model.breadcrumbs.update.sql_params; nodatalock = model.breadcrumbs.update.nodatalock; } } } else if(model.breadcrumbs.sql){ sql = model.breadcrumbs.sql; fieldlist = model.breadcrumbs.sql_params; nodatalock = model.breadcrumbs.nodatalock; } if(!sql){ return; } var fields = this.getFieldsByName(model.fields, fieldlist); var sql_ptypes = []; var sql_params = {}; var datalockqueries = []; var bcrumb_sql_fields = []; for (var i = 0; i < fields.length; i++) { var field = fields[i]; var fname = field.name; if (fname in Q){ var dbtype = _this.getDBType(field); sql_ptypes.push(dbtype); sql_params[fname] = _this.DeformatParam(field, Q[fname], verrors); } } //Add DataLock parameters to SQL this.getDataLockSQL(req, model, model.fields, sql_ptypes, sql_params, verrors, function (datalockquery, dfield) { if(Helper.hasAction(targetperm,'I') && dfield.key) return false; if(!(dfield.name in sql_params)) return false; bcrumb_sql_fields.push(dfield); datalockqueries.push(datalockquery); }, nodatalock, model.id + ' Breadcrumbs'); verrors = _.merge(verrors, model.xvalidate.Validate('*', sql_params, undefined, undefined, undefined, { ignoreUndefined: true })); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return false; } sql = db.sql.getBreadcrumbTasks(_this.jsh, model, sql, datalockqueries, bcrumb_sql_fields); //Add parameters from querystring _this.ApplyQueryParameters(Q, sql, sql_ptypes, sql_params, model); //Add back additional sql parameters that were not yet defined _.each(fields, function(field){ if(!(field.name in sql_params)){ sql_ptypes.push(_this.getDBType(field)); sql_params[field.name] = null; } }); dbtasks['_bcrumbs'] = function (dbtrans, callback, transtbl) { _this.ApplyTransTblChainedParameters(transtbl, sql, sql_ptypes, sql_params, model.fields); db.Row(dbcontext, sql, sql_ptypes, sql_params, dbtrans, function (err, rslt, stats) { if ((err == null) && (rslt == null)) err = Helper.NewError('Breadcrumbs not found', -14); if (err != null) { err.model = model; err.sql = sql; } if (stats) stats.model = model; callback(err, rslt, stats); }); }; }; exports.addLOVTasks = function (req, res, model, Q, dbtasks, options) { options = _.extend({ action: '' }, options); var _this = this; var jsh = _this.jsh; var modeldb = _this.jsh.getModelDB(req, model.id); var fatalError = false; //Use function loop so that dbtasks works for multiple LOVs _.each(model.fields, function (field) { if(fatalError) return; if (field.lov) { var lov = field['lov']; var lov_ptypes = []; var lov_params = {}; var lov_verrors = {}; var datalockqueries = []; var param_datalocks = []; var truncate_lov = false; var no_lov_required = false; var can_optimize = false; var code_val = null; var lovdb = modeldb; if(lov && lov.db){ lovdb = jsh.getDB(lov.db); } var dbcontext = jsh.getDBContext(req, model, lovdb); //If form and actions="B", do not get the full LOV var tgtactions = ejsext.getActions(req, model, field.actions, options.action); if(!field.lov.always_get_full_lov){ if(field.unbound){ /* Do nothing */ } else if((model.layout=='form')||(model.layout=='form-m')){ if(Helper.hasAction(tgtactions, 'U')){ if(field.readonly) no_lov_required = true; } else if(Helper.hasAction(tgtactions, 'I')){ if(field.name in req.query){ if(field.locked_by_querystring){ code_val = req.query[field.name]; if(code_val) truncate_lov = true; } } } else no_lov_required = true; } else if(model.layout=='grid'){ if(!Helper.hasAction(tgtactions, 'IU')) no_lov_required = true; } else if((model.layout=='exec')||(model.layout=='report')){ if(field.name in req.query){ if(field.locked_by_querystring){ code_val = req.query[field.name]; if(code_val) truncate_lov = true; } } } } can_optimize = (no_lov_required || truncate_lov); if(no_lov_required){ //If sqlselect is enabled, or if it is a code/code2, return if(('sql' in lov) || ('sql2' in lov) || ('sqlmp' in lov)){ if(lov.sqlselect) return; } else return; } if (('sql' in lov) || ('sql2' in lov) || ('sqlmp' in lov)) { _this.getDataLockSQL(req, model, [lov], lov_ptypes, lov_params, lov_verrors, function (datalockquery) { datalockqueries.push(datalockquery); }, lov.nodatalock, field.name + '_' + model.id + '_lov'); //Add lov parameters if ('sql_params' in lov) { var lov_pfields = _this.getFieldsByName(model.fields, lov.sql_params); for (let i = 0; i < lov_pfields.length; i++) { var lov_pfield = lov_pfields[i]; var lov_pname = lov_pfield.name; lov_ptypes.push(_this.getDBType(lov_pfield)); lov_params[lov_pname] = null; if (lov_pname in Q) { lov_params[lov_pname] = _this.DeformatParam(lov_pfield, Q[lov_pname], lov_verrors); _this.getDataLockSQL(req, model, model.fields, lov_ptypes, lov_params, lov_verrors, function (datalockquery, dfield) { if (dfield != lov_pfield) return false; param_datalocks.push({ pname: lov_pname, datalockquery: datalockquery, field: dfield }); return true; }, undefined, field.name + '_' + model.id + '_lov_key'); lov_verrors = _.merge(lov_verrors, model.xvalidate.Validate('*', lov_params, lov_pname)); } } } } if(truncate_lov){ var code_valpname = field.name; //if(field.name in lov_params) { Helper.GenError(req, res, -4, 'Field parameter already in LOV SQL parameters: '+field.name); fatalError = true; return; } if(!(field.name in lov_params)){ lov_ptypes.push(_this.getDBType(field)); lov_params[code_valpname] = _this.DeformatParam(field, code_val, lov_verrors); } } if (!_.isEmpty(lov_verrors)) { Helper.GenError(req, res, -2, lov_verrors[''].join('\n')); fatalError = true; return; } var sql = lovdb.sql.getLOV(_this.jsh, field.name, lov, datalockqueries, param_datalocks, { truncate_lov: truncate_lov, model: model }); //Add parameters from querystring _this.ApplyQueryParameters(Q, sql, lov_ptypes, lov_params, model); var lov_db_function = lovdb.Recordset; if(lov.values) lov_db_function = function(context,sql,ptypes,params,dbtrans,callback){ callback(null, lovdb.util.ParseLOVValues(jsh, lov.values), { notices: [], warnings: [] }); }; dbtasks['_LOV_' + field.name] = function (dbtrans, callback, transtbl) { _this.ApplyTransTblChainedParameters(transtbl, sql, lov_ptypes, lov_params, model.fields); lov_db_function.call(lovdb, dbcontext, sql, lov_ptypes, lov_params, dbtrans, function (err, rslt, stats) { if (err == null) { //Generate warning if the LOV options are too long, and sqlselect, sqltruncate is not defined for the field if(!rslt) rslt = []; if(can_optimize && (rslt.length > 1000) && !lov.values){ jsh.Log.warning(model.id + ' > ' + field.name + ': More than 1000 results returned for LOV query. Please consider implementing lov.sqlselect, lov.sqltruncate, and adding %%%TRUNCATE%%% to the LOV sql to improve performance.'); } if (lov.showcode) { for (let i = 0; i < rslt.length; i++) { rslt[i][jsh.map.code_txt] = rslt[i][jsh.map.code_val]; } } if (('blank' in lov) && (lov.blank || (lov.blank === ''))) { var newlov = {}; newlov[jsh.map.code_val] = ''; newlov[jsh.map.code_txt] = (lov.blank == 1 ? 'Please Select...' : lov.blank); var already_found = false; for(let i=0;i<rslt.length;i++){ if((rslt[i][jsh.map.code_val] == newlov[jsh.map.code_val]) && (rslt[i][jsh.map.code_txt] == newlov[jsh.map.code_txt])){ already_found = true; break; } } if(!already_found) rslt.unshift(newlov); } if ('post_process' in lov){ var base_callback = callback; // eslint-disable-line no-unused-vars var jscmd = '(function(){ var values = rslt; var callback = function(err, rslt){ base_callback(err, rslt); }; return (function(){'+lov.post_process+'})();}).call(lov)'; var jscmdrslt = eval(jscmd); if(jscmdrslt === false) return; } } callback(err, rslt, stats); }); }; } }); if(fatalError) return false; }; return module.exports;