flexbiz-server
Version:
Flexible Server
1,126 lines (1,056 loc) • 36.6 kB
JavaScript
const controller = require('../controllers/controller');
const underscore =require('underscore');
const async = require("async");
const {evalute} = require("../libs/utils");
const utils = require("../libs/utils");
const {isSupperAdmin} = require("../libs/utils");
const PostBook = require('../libs/post-book');
const PostSocai = require('../libs/post-socai');
const moment = require("moment");
const numeral = require("numeral");
const fs = require("fs");
const _ = require("lodash")
const joinModel2 = (arr,id_app, model, joinFields, fn,options={cache:true})=>{
return [...arr].joinModel2(id_app, model, joinFields, fn,options);
}
const fieldScheme = new global.Schema(
{
stt:{type:Number,default:0},
stt_col:{type:Number,default:0},
name:{type: String, required: "Yêu cầu nhập mã trường",index:true,lowercase:true,trim:true},
type:{type: String, default:'String',trim:true},
form:{type: String,trim:true},//user for type array or Mixed
header:{type: String, required: "Yêu cầu nhập tên trường"},
header2:{type: String},
api_description:{type: String},//Miêu tả field trong tài liệu api
sort:Number,
unique:Boolean,
default:global.Schema.Types.Mixed,
lowercase:{type:Boolean,default:false},
uppercase:{type:Boolean,default:false},
required:global.Schema.Types.Mixed,
index:{type:Boolean,default:false},
is_tmp:{type:Boolean,default:false},
is_title:{type:Boolean,default:false},
on_view:{type:Boolean,default:true},
maxlength:Number,
min:global.Schema.Types.Mixed,
max:global.Schema.Types.Mixed,
ref_model:{type: String,trim:true},
ref_field:{type:String,trim:true},
ref_label:{type:String,trim:true},
ref_label_as:{type:String,trim:true},
ref_search_fields:{type:String,trim:true},
ref_data_fields:{type:String,trim:true},
ref_condition:{type:String,trim:true},
multiple:Boolean,
not_display:global.Schema.Types.Mixed,
not_input:global.Schema.Types.Mixed,
min_width_display:{type:Number,default:80},
format:{type:String},
align:{type:String},
color:{type:String},
bold:Boolean,
html_variant_display:{type:String},
html_component_display:{type:String},
html_component_input:{type:String},
render_on_list:String,
render_on_view:String,
render_title:String,
grid_configs:{type:String,trim:true},
handle_value_changed:{type:String,trim:true},
handle_search_condition:{type:String,trim:true},
tab:{type:String,trim:true},
group:{type:String,trim:true},
help_text:{type:String,trim:true},
line:{type:Number,default:0}
}
)
const listinfoSchema = new global.Schema({
menu_position:String,
code: {type: String, required: true,index:true,lowercase:true,trim:true},
title: {type: String, required: true},
title2: {type: String},
mother_code: {type: String,lowercase:true,trim:true},
api_code: {type: String,lowercase:true,trim:true},
model_code: {type: String,lowercase:true,trim:true},
fields:[fieldScheme],
postinfos:[global.Schema.Types.Mixed],
exfields:global.Schema.Types.Mixed,
create_model: {type: Boolean, default: false},
breadcrumbs: {type: String,trim:true},
not_add:Boolean,
not_edit:Boolean,
not_delete:Boolean,
not_copy:Boolean,
//events
handle_cell_clicked:{type: String,trim:true},
expression_check_save:{type:String,trim:true},
on_saved:{type:String,trim:true},
on_deleting:{type:String,trim:true},
on_deleted:{type:String,trim:true},
on_init:{type:String,trim:true},
on_init_update:{type:String,trim:true},
handle_view_server:{type:String,trim:true},
handle_oncreating_server:{type:String,trim:true},
handle_oncreated_server:{type:String,trim:true},
handle_onupdating_server:{type:String,trim:true},
handle_onupdated_server:{type:String,trim:true},
handle_ondeleting_server:{type:String,trim:true},
//condition
search_required:Boolean,
search_form:{type:String,trim:true},
default_condition:{type:String,trim:true},
require_condition:{type:String,trim:true},
//
export_yn:Boolean,
sort_by:String,
not_need_right:{type: Boolean,default:false},//Có cần phân quyền hay không
require_id_app:{type: Boolean,default:true},//Dữ liệu theo công ty hay dữ liệu chung
private_data:Boolean,
view_tabs:[],
allow_users:{type: String,trim:true},
input_users:{type: String,trim:true},
create_menu:Boolean,
show_view_tabs_on_grid:Boolean,
form_size:{type:String,default:"md",trim:true},
//
is_dashboard_item:{type: Boolean, default: false},
dashboard_default:{type: Boolean, default: true},
dashboard_ids:{type: String,trim:true},
dashboard_stt:{type: Number},
//
get_other_fields:String,
options:{},
status: {type: Boolean, default: true},
date_created: {type: Date, default: Date.now},
date_updated: {type: Date, default: Date.now},
user_created: {type: String, default: ''},
user_updated: {type: String, default: ''}
});
if((global.configs||{}).createIndexes){
listinfoSchema.index({code: 1},{unique:true});
listinfoSchema.index({title: 1});
listinfoSchema.index({title2: 1});
listinfoSchema.index({mother_code: 1});
listinfoSchema.index({api_code: 1});
listinfoSchema.index({model_code: 1});
listinfoSchema.index({mother_code: 1});
listinfoSchema.index({is_dashboard_item: 1});
listinfoSchema.index({dashboard_ids: 1});
listinfoSchema.index({mother_code: "text",code:"text",title:"text"},{name:"listinfo_index2_text"});
listinfoSchema.index({status: 1});
listinfoSchema.index({user_created: 1,visible_to: 1,visible_to_users: 1});
}
const model = global.mongoose.models.listinfo || global.mongoose.model('listinfo', listinfoSchema);
const requireFields= model.requireFields ={
id_app:{type:String,required:true},
exfields:global.Schema.Types.Mixed,
listinfo_code:String,
status: {type: Boolean, default: true},
date_created: {type: Date, default: Date.now},
date_updated: {type: Date, default: Date.now},
user_created: {type: String, default: ''},
user_updated: {type: String, default: ''},
visible_to: {type: Number, default: 0},
visible_to_users: [String],
}
model.createSchema = (info)=>{
if(!info.create_model) return null;
let api_code = info.api_code || info.code;
const require_id_app = (info.require_id_app!=false && info.require_id_app!="false");
const _requireFields ={...requireFields}
if(!require_id_app){
delete _requireFields.id_app;
}
let mySchema = new global.Schema(_requireFields,{strict: false});
let require_paths = Object.keys(mySchema.paths);
let fields = {};
for(let f of info.fields.filter(f=>f.type!=="Link" && f.type!=="Files" && f.type!=="Action" && f.type!=="Group" && require_paths.indexOf(f.name)<0)){
if(f.unique){
f.index = true;
}
let _field = Object.assign({},f);
if(_field.type=="DateTime" || _field.type=="Time") _field.type= "Date";
if(_field.type=="File" || _field.type=="OtherFile") _field.type= "Mixed";
if(_field.type=="Array") _field.type= "Mixed";
if(_field.type=="Pdf") _field.type= "String";
if(_field.type=="Image") _field.type= "String";
if(_field.type=="String") _field.trim = true;
_field.required = false;
delete _field.name;
delete _field.header;
delete _field.header2;
delete _field.line;
delete _field.sort;
delete _field.unique;
delete _field._id;
delete _field.stt;
delete _field.not_display;
delete _field.not_search;
delete _field.format;
delete _field.align;
delete _field.color;
delete _field.form;
delete _field.min_width_display;
delete _field.html_component_display;
delete _field.html_variant_display;
delete _field.html_component_input;
delete _field.handle_value_changed;
delete _field.default;
delete _field.multiple;
delete _field.tab;
delete _field.help_text;
delete _field.grid_configs;
delete _field.render_on_list;
delete _field.render_on_view;
delete _field.is_tmp;
delete _field.is_title;
delete _field.on_view;
delete _field.handle_view_server;
if(_field.type=="String" || _field.type=="Number" ){
_field.maxlength = _field.maxlength || 4000;
}else{
delete _field.maxlength
}
fields[f.name] = _field
}
mySchema.add(fields);
//create index for all fields
if((global.configs||{}).createIndexes){
info.fields.filter(f=>!f.unique && f.type!="Array" && f.type!="Mixed" && require_paths.indexOf(f.name)<0).forEach(field=>{
mySchema.index({[field.name]:1});
})
//create unique index
let uq = info.fields.filter(f=>f.unique);
if(uq.length>0){
let indUq = {id_app:1};
uq.forEach((field) => {
indUq[field.name] =field.sort || 1;
});
mySchema.index(indUq,{unique: true });
}
//create text index
let textFields = info.fields.filter(f=>f.type==="String");
if(textFields.length>0){
let index_name = `${api_code}_index_text`;
let indText = {};
textFields.forEach((field) => {
indText[field.name] ="text";
});
mySchema.index(indText,{name:index_name});
//console.log("auto create index",index_name);
}
//create requiere index
const requireIndex ={}
require_paths.forEach(rq=>{
requireIndex[rq] =1;
})
mySchema.index(requireIndex);
}
return mySchema;
}
model.updateModel = async (info,_newModel)=>{
if(info.toObject) info = info.toObject();
if(!info.create_model) return null;
let model_code = info.api_code || info.model_code || info.code;
if(!_newModel) _newModel = global.mongoose.models[model_code];
if(!_newModel) return;
if(!_newModel.schema){
console.error("model does not have schema",_newModel);
return;
}
//
//console.log("update model...",model_code);
const api_code = info.api_code || info.code;
const _newList = global.controllers[api_code.toUpperCase()];
let require_paths = Object.keys(requireFields);
let fields = [...info.fields];
//add ref fields
let fields_have_ref =fields.filter(f=>f.ref_model && f.ref_model.indexOf("[")<0 && f.ref_field && f.type!=="Array" && f.type!=="Mixed" && f.ref_label)
.map(f=>f.ref_label_as || f.ref_label)
.filter(f=>!fields.find(gf=>gf.name===f))
.map(f=>{
return {
name:f,
type:"String"
}
})
//console.log("ref fields",fields_have_ref);
fields = fields.concat(fields_have_ref);
//
fields.filter(f=>f.type!=="Link" && f.type!=="Files" && f.type!=="Action" && f.type!=="Group" && require_paths.indexOf(f.name)<0).forEach(field=>{
if(field.toObject) field = field.toObject();
const _field = {...field};
if(underscore.has(_newModel.schema.paths,field.name)){
if(_newModel.schema.paths[field.name].options && !_newModel.schema.paths[field.name].options.isDynamic){
return;
}
if(field.type==="Array" || field.type==="Mixed"){
return;
}
_newModel.schema.remove(field.name);
}
if(_field.type=="DateTime" || _field.type=="Time") _field.type= "Date";
if(_field.type=="File" || _field.type=="OtherFile") _field.type= "Mixed";
if(_field.type=="Array") _field.type= "Mixed";
if(_field.type=="Pdf") _field.type= "String";
if(_field.type=="Image") _field.type= "String";
if(_field.type=="String") _field.trim = true;
if(model_code!=info.code){
_field.required = false;//phai luon dat la false neu khong se bi dung giua cac api khac dung chung model
}
_field.isDynamic = true;
delete _field.name;
delete _field.header;
delete _field.header2;
delete _field.line;
delete _field.sort;
delete _field.unique;
delete _field._id;
delete _field.stt;
delete _field.not_display;
delete _field.not_search;
delete _field.format;
delete _field.align;
delete _field.color;
delete _field.form;
delete _field.min_width_display;
delete _field.html_component_display;
delete _field.html_variant_display;
delete _field.html_component_input;
delete _field.handle_value_changed;
delete _field.default;
delete _field.multiple;
delete _field.tab;
delete _field.help_text;
delete _field.grid_configs;
delete _field.render_on_list;
delete _field.render_on_view;
delete _field.is_tmp;
delete _field.is_title;
delete _field.on_view;
delete _field.handle_view_server;
if(_field.type=="String" || _field.type=="Number" ){
_field.maxlength = _field.maxlength || 4000;
}else{
delete _field.maxlength
}
_newModel.schema.add({[field.name]:_field});
if(!field.unique && field.type!="Array" && field.type!="Mixed") _newModel.schema.index({[field.name]:1});
})
//create unique index
if((global.configs||{}).createIndexes){
if(_newList && _newList.is_dynamic_list){
let uq = fields.filter(f=>f.unique);
let indUq;
if(uq.length>0){
indUq = {id_app:1};
uq.forEach((field) => {
indUq[field.name] =field.sort || 1;
});
}
if(_newList.options.unique && _newList.options.unique.length>0){
let old_index_unique ={id_app:1};
_newList.options.unique.forEach(u=>{
let field = fields.find(f=>f.name===u);
old_index_unique[u]= (field||{}).sort || 1;
});
//console.log("drop current index unique...",old_index_unique);
_newModel.collection.dropIndex(old_index_unique,()=>{
//if(e) return console.error("Can't drop index unique",old_index_unique,model_code,e);
//create new unique
if(indUq){
//console.log("create new index unique...",indUq);
_newModel.schema.index(indUq,{unique: true });
}
})
}else{
//create new unique
if(indUq){
//console.log("create new index unique...",indUq)
_newModel.schema.index(indUq,{unique: true });
}
}
}
}
}
model.createModel = (info)=>{
if(!info.create_model) return null;
let model_code = info.api_code || info.model_code || info.code;
//models are not allowed to create new APIs
if(global.secu_models.indexOf(model_code)>=0) return null;
//
let exModel = global.mongoose.models[model_code]
if(!exModel){
let model_path = (((global.configs||{}).paths||{}).models || __dirname) + "/" + model_code + ".js"
if(fs.existsSync(model_path)){
exModel = require(model_path);
}
}
if(!exModel){
exModel = global.mongoose.model(model_code, model.createSchema(info));
}else{
model.updateModel(info,exModel);
}
return exModel;
}
const deletePost = (_listInfo,obj)=>{
return Promise.all((_listInfo.postinfos||[]).filter(info=>info.model && info.script && info.condition).map(postInfo=>{
let m = global.getModel(postInfo.model);
let scriptCondition = postInfo.condition.indexOf("return ")<0?`return ${postInfo.condition}`:postInfo.condition;
let condition = evalute(scriptCondition,{master:obj,data:obj,moment});
condition.id_app = obj.id_app;
//console.log("delete post dynamic",postInfo.model,condition);
return m.deleteMany(condition);
}))
}
const post = (_listInfo,obj)=>{
if(obj.toObject) obj = obj.toObject();
return Promise.all((_listInfo.postinfos||[]).filter(info=>info.model && info.script && info.condition).map(postInfo=>{
return (async ()=>{
let script = `return (async ()=>{
try{
${postInfo.script}
}catch(e){
console.error("error post data",e)
return {error:e}
}
})()`
let data = await evalute(script,{master:{...obj},data:{...obj},moment,numeral});
if(data && data.error){
throw data.error
}
//console.log("post dynamic",postInfo.model,data);
return new Promise((resolve,reject)=>{
if(postInfo.model==="socai"){
const postsocai = new PostSocai(obj, data);
postsocai.run((e, rs)=>{
if(e) return reject(e);
resolve(rs);
});
}else{
let m = global.getModel(postInfo.model);
const posttdttno = new PostBook(obj, data, m, function(detail, callback) {
callback(detail);
});
posttdttno.run(function(e, rs) {
//console.log("posted dynamic",rs,e)
if(e) return reject(e);
resolve(rs);
})
}
})
})()
}))
}
const dynamicFinding = (_listInfo,user,condition,next)=>{
if(_listInfo.api_code && _listInfo.api_code!=_listInfo.code){
//don't change condition of original api
return next(null,condition);
}
if(_listInfo.private_data){
condition.user_created = user.email
}
if(_listInfo.require_condition){
try{
let script = _listInfo.require_condition;
if(script.indexOf("return ")<0) script = `return ${script}`;
const require_condition = evalute(script,{user,condition,moment,numeral});
if(require_condition && Object.keys(require_condition).length>0){
for(let key in require_condition){
condition[key] = require_condition[key];
}
}
}catch(e){
console.error(e);
return next(`Error on require condition: ${e.message}`);
}
}
next(null,condition);
}
const dynamicCreating = async (listInfo,user,obj,next)=>{
let _listInfo = await model.findOne({_id:listInfo._id}).lean();
if(!_listInfo) return next("Không tìm thấy danh mục này");
for(let key in _listInfo){
listInfo[key] = _listInfo[key];
}
const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false");
const supportUsers = configs.supportUsers || []
if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email.toLowerCase()) && !isSupperAdmin(user.email.toLowerCase())) {
//console.log("support users",supportUsers,user.email.toLowerCase())
return next('Bạn không có quyền thực hiện thao tác này');
}
if(_listInfo.handle_oncreating_server && _listInfo.handle_oncreating_server.trim()!==""){
if(_listInfo.handle_oncreating_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi tạo yêu cầu gọi function next");
try{
await new Promise((resolve,reject)=>{
async.parallel([
(next)=>{
const func_body = `(async ()=>{
try{
${_listInfo.handle_oncreating_server}
}catch(e){
next(e);
}
})();`
setImmediate(async ()=>{
try{
await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,utils,query:utils.query,async,controller,controllers:global.controllers,joinModel2,next,moment,numeral})
}catch(e){
console.error("error dynamic create",_listInfo.handle_oncreating_server,e)
next(e);
}
})
}
],(e,rs)=>{
if(e){
console.error(e);
return reject(e)
}
resolve(rs);
})
})
}catch(e){
console.error(e);
return next(e);
}
}
next(null,obj);
}
const dynamicCreated = async (_listInfo,user,obj,next)=>{
if(_listInfo.handle_oncreated_server && _listInfo.handle_oncreated_server.trim()!==""){
if(_listInfo.handle_oncreated_server.indexOf("next")<0) return next("Script xử lý dữ liệu sau khi tạo yêu cầu gọi function next");
try{
await new Promise((resolve,reject)=>{
async.parallel([
(next)=>{
const func_body = `(async ()=>{
try{
${_listInfo.handle_oncreated_server}
}catch(e){
next(e);
}
})();`
setImmediate(async ()=>{
try{
await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral})
}catch(e){
console.error("error dynamic created",_listInfo.handle_oncreated_server,e)
next(e)
}
})
}
],(e,rs)=>{
if(e){
console.error(e);
return reject(e)
}
resolve(rs);
})
})
}catch(e){
console.error(e);
return next(e);
}
}
next(null,obj);
}
const dynamicUpdating = async (listInfo,user,data,obj,next)=>{
let _listInfo = await model.findOne({_id:listInfo._id}).lean();
if(!_listInfo) return next("Không tìm thấy danh mục này");
for(let key in _listInfo){
listInfo[key] = _listInfo[key];
}
const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false");
const supportUsers = configs.supportUsers || [];
if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email.toLowerCase()) && !isSupperAdmin(user.email.toLowerCase())) {
//console.log("support users",supportUsers,user.email.toLowerCase())
return next('Bạn không có quyền thực hiện thao tác này');
}
if(_listInfo.handle_onupdating_server && _listInfo.handle_onupdating_server.trim()!==""){
if(_listInfo.handle_onupdating_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi update yêu cầu gọi function next");
try{
await new Promise((resolve,reject)=>{
async.series([
(next)=>{
const func_body = `(async ()=>{
try{
${_listInfo.handle_onupdating_server}
}catch(e){
next(e);
}
})();`
setImmediate(async ()=>{
try{
await evalute(func_body,{data,obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral})
}catch(e){
console.error("error dynamic update",_listInfo.handle_onupdating_server,e)
next(e)
}
})
}
],(e,rs)=>{
if(e){
console.error(e);
return reject(e)
}
resolve(rs);
})
})
}catch(e){
console.error(e);
return next(e);
}
}
next(null,data,obj);
}
const dynamicUpdated = async (_listInfo,user,obj,next)=>{
if(_listInfo.handle_onupdated_server && _listInfo.handle_onupdated_server.trim()!==""){
if(_listInfo.handle_onupdated_server.indexOf("next")<0) return next("Script xử lý dữ liệu sau khi update yêu cầu gọi function next");
try{
await new Promise((resolve,reject)=>{
async.series([
(next)=>{
const func_body = `(async ()=>{
try{
${_listInfo.handle_onupdated_server}
}catch(e){
next(e)
}
})();`
setImmediate(async ()=>{
try{
await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral})
}catch(e){
console.error("error dynamic updated",_listInfo.handle_onupdated_server,e)
next(e)
}
})
}
],(e,rs)=>{
if(e){
console.error(e);
return reject(e)
}
resolve(rs);
})
})
}catch(e){
console.error(e);
return next(e);
}
}
next(null,obj);
}
const dynamicDeleting = async (listInfo,user,obj,next)=>{
let _listInfo = await model.findOne({_id:listInfo._id}).lean();
if(!_listInfo) return next("Không tìm thấy danh mục này");
for(let key in _listInfo){
listInfo[key] = _listInfo[key];
}
const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false");
const supportUsers = configs.supportUsers || [];
if (!require_id_app && (_listInfo.input_users||"").toLowerCase().indexOf(user.email.toLowerCase())<0 && !supportUsers.includes(user.email) && !isSupperAdmin(user.email.toLowerCase())) {
return next('Bạn không có quyền thực hiện thao tác này');
}
if(_listInfo.handle_ondeleting_server && _listInfo.handle_ondeleting_server.trim()!==""){
if(_listInfo.handle_ondeleting_server.indexOf("next")<0) return next("Script xử lý dữ liệu trước khi update yêu cầu gọi function next");
try{
await new Promise((resolve,reject)=>{
async.series([
(next)=>{
const func_body = `(async ()=>{
try{
${_listInfo.handle_ondeleting_server}
}catch(e){
next(e);
}
})();`
setImmediate(async ()=>{
try{
await evalute(func_body,{obj,user,getLib:global.getLib,getModel:global.getModel,controller,controllers:global.controllers,utils,query:utils.query,async,joinModel2,next,moment,numeral})
}catch(e){
console.error("error dynamic delete",_listInfo.handle_ondeleting_server,e)
next(e)
}
})
}
],(e,rs)=>{
if(e){
console.error(e);
return reject(e)
}
resolve(rs);
})
})
}catch(e){
console.error(e);
return next(e);
}
}
next(null,obj);
}
const onView = async (obj,user,items,next)=>{
//private data
if(obj.private_data){
items = items.filter(item=>item.user_created===user.email)
}
//onview
if(obj.handle_view_server && obj.handle_view_server.trim()!==""){
if(obj.handle_view_server.indexOf("next")<0) return next("Script xử lý view yêu cầu gọi function next");
await new Promise(resolve=>{
async.series([
(next)=>{
setImmediate(async ()=>{
const func_body = `(async ()=>{
try{
${obj.handle_view_server}
}catch(e){
next(e);
}
})();`
try{
await evalute(func_body,{items,user,getLib:global.getLib,utils,next,async,joinModel2,moment,numeral})
}catch(e){
console.error("error dynamic view",obj.handle_view_serve,e);
next(e);
}
})
}
],(e,rs)=>{
if(e) console.error(e);
resolve(rs);
})
})
}
//
let ref_model,where,fields,ref_name_model;
//let fields_have_ref = obj.fields.filter(f=>["Number","String"].indexOf(f.type)>0 && f.ref_model && f.ref_model.indexOf("[")<0 && f.ref_field && f.ref_label);
let fields_have_ref = obj.fields.filter(f=>["Number","String"].indexOf(f.type)>0 && f.ref_model && f.ref_field && f.ref_label);
if(fields_have_ref.length===0){
return next(null,items);
}
async.mapSeries(fields_have_ref, (f,cb)=>{
setImmediate(async ()=>{
try{
ref_name_model= f.ref_model;
if(ref_name_model.indexOf("[")>=0 || ref_name_model.indexOf(",")>=0){
try{
if(ref_name_model.indexOf("[")>=0){
ref_model = utils.JSONParser(ref_name_model)
}else{
ref_model = ref_name_model.split(",");
}
}catch(e){
console.error("ref_model is not valid",ref_name_model,e);
return cb(e);
}
}else{
if(ref_name_model==="dmkh") ref_name_model = "customer";
if(ref_name_model==="dmtk") ref_name_model = "account";
if(ref_name_model==="dmnt") ref_name_model = "currency";
if(ref_name_model==="dmnhtask") ref_name_model = "group";
ref_model = global.mongoose.models[ref_name_model];
if(!ref_model){
let _listInofOfRef = await model.findOne({code:ref_name_model});
if(_listInofOfRef && (_listInofOfRef.api_code || _listInofOfRef.model_code)){
let model_code = _listInofOfRef.api_code || _listInofOfRef.model_code;
ref_model = global.mongoose.models[model_code];
}
if(!ref_model){
let ctrl = global.controllers[ref_name_model.trim().toUpperCase()];
if(ctrl) ref_model = ctrl.getProperty("model");
}
}
if(!ref_model) ref_model = global.getModel(ref_name_model);
}
}catch(e){
console.error(e);
return cb(e);
}
let items_will_join = items.filter(item=>item[f.name] || item[f.name]==0)
if(f.ref_label){
fields = {[f.ref_label_as || f.ref_label]:f.ref_label};
//items_will_join = items_will_join.filter(i=>!i[f.ref_label_as || f.ref_label]);
}else{
fields ={}
}
if(f.ref_model==="trangthai"){
fields.color="color";
}
if(!_.isArray(ref_model)){
//create where
where = (item)=>{
let w = {
[f.ref_field]:item[f.name]
}
//other condition
let condition;
if(f.ref_condition && f.ref_condition.indexOf("API")<0){
let func_string = f.ref_condition;
if(func_string.indexOf("return ")<0){
func_string = `return ${func_string}`;
}
let userInfo = {...user};
try{
condition = evalute(func_string,{master:item,data:item,detail:item,userInfo});
}catch(e){
console.error(e,func_string);
}
}
if(condition) w = {...w,...condition};
//check where of participant
if(f.ref_model==="participant"){
delete w.groups;
}
return w;
}
//id app
let id_app;
if(underscore.has(ref_model.schema.paths,"id_app")){
id_app = user.current_id_app;
}
await items_will_join.asyncJoinModel2(id_app,ref_model,{where,fields});
}else{
if(f.ref_field && f.ref_label){
items_will_join.forEach(item=>{
let ref_item = ref_model.find(r=>r[f.ref_field]==item[f.name]);
if(ref_item) item[f.ref_label_as || f.ref_label] = ref_item[f.ref_label]
})
}
}
cb();
})
},(e)=>{
if(e) return next(e);
next(null,items);
})
}
model.createController = (router,_listInfo)=>{
let api_code = _listInfo.api_code || _listInfo.code;
if(!_listInfo.create_model) return null;
let sort={},unique=[],_newModel,_newList;
_listInfo.fields.filter(f=>f.sort).forEach(f=>{
sort[f.name] = f.sort;
})
if(_listInfo.sort_by){
if(_listInfo.sort_by.indexOf("return ")>=0 || _listInfo.sort_by.indexOf("{")===0) {
try{
let func_string = _listInfo.sort_by;
if(func_string.indexOf("return ")<0){
func_string = "return " + func_string;
}
sort = evalute(func_string);
}catch(e){
console.error("error create sort", _listInfo.sort_by,e);
}
}else{
sort = _listInfo.sort_by.split(",").reduce((sort,b)=>sort[b]=1,{})
}
}
_listInfo.fields.filter(f=>f.unique).forEach(f=>{
unique.push(f.name);
})
_newList = global.controllers[api_code.toUpperCase()];
if(!_newList){
if(api_code=="moduleinfo") console.log("create new API",api_code);
if(Object.keys(sort).length===0) sort.date_created = -1;
//create new model
const require_id_app = (_listInfo.require_id_app!=false && _listInfo.require_id_app!="false");
//console.log("create new api",api_code);
_newModel = model.createModel(_listInfo);
if(!_newModel) return;
_newList = new controller(router,_newModel,api_code.toLowerCase(),{
sort: sort,
unique:unique,
notNeedRight:_listInfo.not_need_right,
require_id_app,
onView:(user,items,next)=>{
onView(_listInfo,user,items,next);
},
onCreating:async (user,data,next)=>{
next(null,data);
},
onUpdating:async (user,data,obj,next)=>{
next(null,data,obj);
},
onDeleting:async (user,obj,next)=>{
next(null,obj);
}
})
_newList.is_dynamic_list = true;
_newList.dynamicFinding = (user,condition,next)=>{
dynamicFinding(_listInfo,user,condition,next);
}
_newList.dynamicCreating = (user,obj,next)=>{
dynamicCreating(_listInfo,user,obj,next);
}
_newList.dynamicCreated = (user,obj,next)=>{
dynamicCreated(_listInfo,user,obj,next);
}
_newList.dynamicUpdating = (user,data,obj,next)=>{
dynamicUpdating(_listInfo,user,data,obj,next);
}
_newList.dynamicUpdated = (user,obj,next)=>{
dynamicUpdated(_listInfo,user,obj,next);
}
_newList.dynamicDeleting = (user,obj,next)=>{
dynamicDeleting(_listInfo,user,obj,next);
}
if(_listInfo.fields.find(f=>f.name==="ma_ct") && _listInfo.postinfos && _listInfo.postinfos.length>0){
//console.log("dynamic list",_listInfo.code,"has post")
_newList.dynamicDeletePost = async (obj)=>{
return deletePost(_listInfo,obj)
}
_newList.dynamicPost = async (obj, fn)=>{
if(_newList.post){
_newList.post(obj, async function(e){
if(e) return fn(e);
try{
let rs = await post(_listInfo,obj);
fn(null,rs)
}catch(e){
fn(e.message || e.error || e)
}
})
}else{
try{
let rs = await post(_listInfo,obj);
fn(null,rs)
}catch(e){
fn(e.message || e.error || e)
}
}
}
}
_newList.route();
}else{
if(api_code=="moduleinfo") console.log("update API",api_code);
//console.log("update API",api_code);
//update options
if(Object.keys(sort).length>0) _newList.sort = _newList.options.sort = sort;
//update model
_newModel = _newList.model;
model.updateModel(_listInfo,_newModel);
//set new unique and view
if(_newList.is_dynamic_list || !_newList.unique || _newList.unique.length===0 ){
_newList.unique = _newList.options.unique=unique;
}
const current_dynamicView = _newList.dynamicView;
_newList.dynamicView = (user,items,next)=>{
//console.log("handle dynamicView",_listInfo.code);
if(current_dynamicView){
//console.log("handle current dynamicView",_listInfo.code);
current_dynamicView(user,items,(e,items)=>{
if(e) return next(e);
//console.log("handle dynamicView 2",_listInfo.code);
onView(_listInfo,user,items,next);
})
}else{
//console.log("handle new dynamicView",_listInfo.code);
onView(_listInfo,user,items,next);
}
}
const current_dynamicFinding = _newList.dynamicFinding;
_newList.dynamicFinding = (user,condition,next)=>{
if(current_dynamicFinding){
current_dynamicFinding(user,condition,(e,condition)=>{
if(e) return next(e);
dynamicFinding(_listInfo,user,condition,next);
})
}else{
dynamicFinding(_listInfo,user,condition,next);
}
}
const current_dynamicCreating = _newList.dynamicCreating;
_newList.dynamicCreating = (user,obj,next)=>{
if(current_dynamicCreating){
current_dynamicCreating(user,obj,(e,obj)=>{
//console.log("handle current_dynamicCreating",_listInfo.code,e,obj);
if(e) return next(e);
dynamicCreating(_listInfo,user,obj,next);
//console.log("handle dynamicCreating",_listInfo.code,obj);
})
}else{
dynamicCreating(_listInfo,user,obj,next);
//console.log("handle dynamicCreating",_listInfo.code,obj);
}
}
const current_dynamicCreated = _newList.dynamicCreated;
_newList.dynamicCreated = (user,obj,next)=>{
if(current_dynamicCreated){
current_dynamicCreated(user,obj,(e,obj)=>{
if(e) return next(e);
dynamicCreated(_listInfo,user,obj,next);
})
}else{
dynamicCreated(_listInfo,user,obj,next);
}
}
const current_dynamicUpdating = _newList.dynamicUpdating;
_newList.dynamicUpdating = (user,data,obj,next)=>{
if(current_dynamicUpdating){
current_dynamicUpdating(user,data,obj,(e,data,obj)=>{
if(e) return next(e);
dynamicUpdating(_listInfo,user,data,obj,next);
})
}else{
dynamicUpdating(_listInfo,user,data,obj,next);
}
}
const current_dynamicUpdated = _newList.dynamicUpdated;
_newList.dynamicUpdated = (user,obj,next)=>{
if(current_dynamicUpdated){
current_dynamicUpdated(user,obj,(e,obj)=>{
if(e) return next(e);
dynamicUpdated(_listInfo,user,obj,next);
})
}else{
dynamicUpdated(_listInfo,user,obj,next);
}
}
const current_dynamicDeleting = _newList.dynamicDeleting;
_newList.dynamicDeleting = (user,obj,next)=>{
if(current_dynamicDeleting){
current_dynamicDeleting(user,obj,(e,obj)=>{
if(e) return next(e);
dynamicDeleting(_listInfo,user,obj,next);
})
}else{
dynamicDeleting(_listInfo,user,obj,next);
}
}
if(_listInfo.fields.find(f=>f.name==="ma_ct") && _listInfo.postinfos && _listInfo.postinfos.length>0){
//chi xu lydynamic post mot lan de tranh trung du lieu
if(!_newList.dynamicDeletePost) _newList.dynamicDeletePost = async (obj)=>{
return deletePost(_listInfo,obj)
}
if(!_newList.dynamicPost) _newList.dynamicPost = async (obj, fn)=>{
if(_newList.post){
_newList.post(obj, async function(e){
if(e) return fn(e);
try{
let rs = await post(_listInfo,obj);
fn(null,rs)
}catch(e){
fn(e.message || e.error || e)
}
})
}else{
try{
let rs = await post(_listInfo,obj);
fn(null,rs)
}catch(e){
fn(e.message || e.error || e)
}
}
}
}else{
_newList.dynamicPost = undefined
_newList.dynamicDeletePost = undefined
}
}
return _newList;
}
module.exports = model;