jsharmony-workflow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +47 -0
- package/.eslintrc_models.js +64 -0
- package/LICENSE +165 -0
- package/README.md +29 -0
- package/index.js +20 -0
- package/jsHarmonyWorkflow.js +55 -0
- package/jsHarmonyWorkflowConfig.js +43 -0
- package/jsHarmonyWorkflowProcessor.js +517 -0
- package/jsHarmonyWorkflowTransform.Classic.js +83 -0
- package/jsHarmonyWorkflowTransform.js +80 -0
- package/lint.cmd +2 -0
- package/models/Admin/TaskDef.json +26 -0
- package/models/Admin/TaskDef_Func.json +21 -0
- package/models/Admin/TaskDef_Listing.json +28 -0
- package/models/Admin/TaskDef_Tabs.json +19 -0
- package/models/Admin/WkflowDef.json +25 -0
- package/models/Admin/WkflowDef_Listing.json +26 -0
- package/models/Admin/WkflowDef_State.json +26 -0
- package/models/Admin/WkflowDef_State_Action.json +25 -0
- package/models/Admin/WkflowDef_State_Action_Listing.json +25 -0
- package/models/Admin/WkflowDef_State_Listing.json +45 -0
- package/models/Admin/WkflowDef_State_Tabs.json +20 -0
- package/models/Admin/WkflowDef_State_Tran.json +28 -0
- package/models/Admin/WkflowDef_State_Tran_Listing.json +27 -0
- package/models/Admin/WkflowDef_Tabs.json +20 -0
- package/models/Admin/WkflowDef_Tran_Start.json +28 -0
- package/models/Admin/WkflowDef_Tran_Start_Listing.json +34 -0
- package/models/Wkflow/Task.json +27 -0
- package/models/Wkflow/Task_Listing.json +45 -0
- package/models/Wkflow/Task_Tabs.json +18 -0
- package/models/Wkflow/Wkflow.json +31 -0
- package/models/Wkflow/Wkflow_Listing.json +38 -0
- package/models/Wkflow/Wkflow_Tabs.json +18 -0
- package/models/_config.json +48 -0
- package/models/sql/objects/task.json +152 -0
- package/models/sql/objects/wkflow.json +383 -0
- package/package.json +21 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 apHarmony
|
|
3
|
+
|
|
4
|
+
This file is part of jsHarmony.
|
|
5
|
+
|
|
6
|
+
jsHarmony is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
jsHarmony is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with this package. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
var _ = require('lodash');
|
|
21
|
+
var async =require('async');
|
|
22
|
+
var Helper = require('jsharmony/Helper');
|
|
23
|
+
|
|
24
|
+
module.exports = exports = function jsHarmonyWorkflowProcessor(module){
|
|
25
|
+
var _this = this;
|
|
26
|
+
var jsh = module.jsh;
|
|
27
|
+
|
|
28
|
+
_this.isRunning = false;
|
|
29
|
+
_this.runNextLoopImmediately = false;
|
|
30
|
+
_this.taskTimer = null;
|
|
31
|
+
|
|
32
|
+
_this.run = function(){
|
|
33
|
+
if(_this.isRunning){
|
|
34
|
+
_this.runNextLoopImmediately = true;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
_this.isRunning = true;
|
|
38
|
+
|
|
39
|
+
if(_this.taskTimer) clearTimeout(_this.taskTimer);
|
|
40
|
+
_this.taskTimer = null;
|
|
41
|
+
|
|
42
|
+
function scheduleNextLoop(timeout){
|
|
43
|
+
_this.taskTimer = setTimeout(function(){ _this.run(); }, (_this.runNextLoopImmediately ? 1 : timeout));
|
|
44
|
+
_this.runNextLoopImmediately = false;
|
|
45
|
+
_this.isRunning = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (module.Config.disabled){
|
|
49
|
+
return scheduleNextLoop(module.Config.checkDelay);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_this.processWorkflows(function(err, rerun){
|
|
53
|
+
if(rerun) _this.runNextLoopImmediately = true;
|
|
54
|
+
return scheduleNextLoop(module.Config.checkDelay);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
_this.processWorkflows = function(callback /* function(err, rerun){} */){
|
|
59
|
+
callback = callback || function(err){ };
|
|
60
|
+
|
|
61
|
+
var appsrv = jsh.AppSrv;
|
|
62
|
+
var dbtypes = appsrv.DB.types;
|
|
63
|
+
var db = jsh.getDB('default');
|
|
64
|
+
|
|
65
|
+
var hasInvalidWorkflows = false;
|
|
66
|
+
var hasTerminalWorkflows = false;
|
|
67
|
+
var wkflows = {
|
|
68
|
+
/*
|
|
69
|
+
wkflow_id: {
|
|
70
|
+
wkflow_id,
|
|
71
|
+
wkflow_def_id,
|
|
72
|
+
wkflow_def_code,
|
|
73
|
+
wd.wkflow_def_name,
|
|
74
|
+
wd.wkflow_def_sts,
|
|
75
|
+
wsd.wkflow_def_state_code,
|
|
76
|
+
wsd.wkflow_def_state_name,
|
|
77
|
+
wsd.wkflow_def_state_type,
|
|
78
|
+
wsd.wkflow_def_state_sts,
|
|
79
|
+
tran: {
|
|
80
|
+
wkflow_def_state_id_dst: [
|
|
81
|
+
{
|
|
82
|
+
id,
|
|
83
|
+
wkflow_def_state_tran_cond_type,
|
|
84
|
+
wkflow_def_state_tran_cond_param,
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
next_state_id,
|
|
89
|
+
next_state_actions,
|
|
90
|
+
}
|
|
91
|
+
*/
|
|
92
|
+
};
|
|
93
|
+
var wkflow_trans = [
|
|
94
|
+
// id
|
|
95
|
+
// sql
|
|
96
|
+
];
|
|
97
|
+
var wkflow_next = {
|
|
98
|
+
//wkflow_id: wkflow
|
|
99
|
+
};
|
|
100
|
+
var wkflow_trans_start = [
|
|
101
|
+
// wkflow_id
|
|
102
|
+
// sql
|
|
103
|
+
];
|
|
104
|
+
var code_wkflow_var_name = {
|
|
105
|
+
// code_val: { code_val, code_txt }
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
async.waterfall([
|
|
109
|
+
|
|
110
|
+
//Get all active workflows
|
|
111
|
+
function(process_cb){
|
|
112
|
+
let sql = [
|
|
113
|
+
//rs_wkflow
|
|
114
|
+
'select',
|
|
115
|
+
' w.wkflow_id,',
|
|
116
|
+
' w.wkflow_def_id,',
|
|
117
|
+
' w.wkflow_def_state_id,',
|
|
118
|
+
' w.wkflow_tran_sts,',
|
|
119
|
+
' wd.wkflow_def_code,',
|
|
120
|
+
' wd.wkflow_def_name,',
|
|
121
|
+
' wd.wkflow_def_sts,',
|
|
122
|
+
' wsd.wkflow_def_state_code,',
|
|
123
|
+
' wsd.wkflow_def_state_name,',
|
|
124
|
+
' wsd.wkflow_def_state_type,',
|
|
125
|
+
' wsd.wkflow_def_state_sts',
|
|
126
|
+
' from {schema}.wkflow w',
|
|
127
|
+
' left outer join {schema}.wkflow_def wd on wd.wkflow_def_id = w.wkflow_def_id',
|
|
128
|
+
' left outer join {schema}.wkflow_def_state wsd on wsd.wkflow_def_state_id = w.wkflow_def_state_id',
|
|
129
|
+
" where w.wkflow_sts='ACTIVE';",
|
|
130
|
+
|
|
131
|
+
//rs_code_wkflow_var_name
|
|
132
|
+
'select ',
|
|
133
|
+
' '+jsh.map.code_val+', '+jsh.map.code_txt+'',
|
|
134
|
+
' from {schema}.' + module.transform.mapping['code_wkflow_var_name'],
|
|
135
|
+
' where (' + jsh.map.code_end_date + ' is null or ' + jsh.map.code_end_date + '>%%%'+jsh.map.timestamp+'%%%);',
|
|
136
|
+
|
|
137
|
+
//rs_wkflow_var
|
|
138
|
+
'select',
|
|
139
|
+
' w.wkflow_id,',
|
|
140
|
+
' wv.wkflow_var_name,',
|
|
141
|
+
' wv.wkflow_var_val',
|
|
142
|
+
' from {schema}.wkflow w',
|
|
143
|
+
' inner join {schema}.wkflow_var wv on wv.wkflow_id = w.wkflow_id',
|
|
144
|
+
" where w.wkflow_sts='ACTIVE';",
|
|
145
|
+
|
|
146
|
+
//rs_wkflow_tran
|
|
147
|
+
'select',
|
|
148
|
+
' w.wkflow_id,',
|
|
149
|
+
' wstd.wkflow_def_state_id_src,',
|
|
150
|
+
' wstd.wkflow_def_state_id_dst,',
|
|
151
|
+
' wstd.wkflow_def_state_tran_cond_type,',
|
|
152
|
+
' wstd.wkflow_def_state_tran_cond_param',
|
|
153
|
+
' from {schema}.wkflow w',
|
|
154
|
+
" inner join {schema}.wkflow_def_state_tran wstd on wstd.wkflow_def_state_id_src = w.wkflow_def_state_id and wstd.wkflow_def_state_tran_sts='ACTIVE'",
|
|
155
|
+
" where w.wkflow_sts='ACTIVE' and w.wkflow_tran_sts='COMPLETE' and w.wkflow_def_state_id is not null;",
|
|
156
|
+
|
|
157
|
+
//rs_wkflow_tran_start
|
|
158
|
+
'select',
|
|
159
|
+
' wd.wkflow_def_id,',
|
|
160
|
+
' wd.wkflow_def_code,',
|
|
161
|
+
' wd.wkflow_def_name,',
|
|
162
|
+
' wstd.wkflow_def_state_id_src,',
|
|
163
|
+
' wstd.wkflow_def_state_id_dst,',
|
|
164
|
+
' wstd.wkflow_def_state_tran_cond_type,',
|
|
165
|
+
' wstd.wkflow_def_state_tran_cond_param',
|
|
166
|
+
' from {schema}.wkflow_def wd',
|
|
167
|
+
" inner join {schema}.wkflow_def_state wsd on wsd.wkflow_def_id = wd.wkflow_def_id and wsd.wkflow_def_state_sts='ACTIVE'",
|
|
168
|
+
" inner join {schema}.wkflow_def_state_tran wstd on wstd.wkflow_def_state_id_src is null and wstd.wkflow_def_state_id_dst = wsd.wkflow_def_state_id and wstd.wkflow_def_state_tran_sts='ACTIVE'",
|
|
169
|
+
" where wd.wkflow_def_sts='ACTIVE';",
|
|
170
|
+
].join(' ');
|
|
171
|
+
appsrv.ExecMultiRecordset('wkflow', module.replaceSchema(sql), [], {}, function (err, rslt) {
|
|
172
|
+
if(err){ err.sql = sql; return process_cb(err); }
|
|
173
|
+
if(!rslt || !rslt[0] || (rslt[0].length != 5)) return process_cb(new Error('Unexpected database error has occurred'));
|
|
174
|
+
var rs_wkflow = rslt[0][0] || [];
|
|
175
|
+
var rs_code_wkflow_var_name = rslt[0][1] || [];
|
|
176
|
+
var rs_wkflow_var = rslt[0][2] || [];
|
|
177
|
+
var rs_wkflow_tran = rslt[0][3] || [];
|
|
178
|
+
var rs_wkflow_tran_start = rslt[0][4] || [];
|
|
179
|
+
_.each(rs_wkflow, function(row){
|
|
180
|
+
row.wkflow_tran_sts = (row.wkflow_tran_sts||'').toString().toUpperCase();
|
|
181
|
+
row.wkflow_def_sts = (row.wkflow_def_sts||'').toString().toUpperCase();
|
|
182
|
+
row.wkflow_def_state_sts = (row.wkflow_def_state_sts||'').toString().toUpperCase();
|
|
183
|
+
row.wkflow_def_state_type = (row.wkflow_def_state_type||'').toString().toUpperCase();
|
|
184
|
+
if(row.wkflow_def_sts!='ACTIVE'){ hasInvalidWorkflows = true; return; }
|
|
185
|
+
if(row.wkflow_def_state_sts!='ACTIVE'){ hasInvalidWorkflows = true; return; }
|
|
186
|
+
if((row.wkflow_def_state_type=='TERIMNAL') && (row.wkflow_tran_sts=='COMPLETE')){ hasTerminalWorkflows = true; return; }
|
|
187
|
+
if(!wkflows[row.wkflow_id]){
|
|
188
|
+
wkflows[row.wkflow_id] = _.extend(row, {
|
|
189
|
+
tran: {},
|
|
190
|
+
tran_start: {},
|
|
191
|
+
next_state_id: null,
|
|
192
|
+
next_state_actions: [],
|
|
193
|
+
var: {},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
//Sort workflow variable definitions by length (longest first)
|
|
198
|
+
rs_code_wkflow_var_name.sort(function(a,b){
|
|
199
|
+
var aval = (a[jsh.map.code_val] || '').length;
|
|
200
|
+
var bval = (b[jsh.map.code_val] || '').length;
|
|
201
|
+
if(aval < bval) return 1;
|
|
202
|
+
if(aval > bval) return -1;
|
|
203
|
+
return 0;
|
|
204
|
+
});
|
|
205
|
+
//Process workflow variable definitions
|
|
206
|
+
_.each(rs_code_wkflow_var_name, function(row){
|
|
207
|
+
row.wkflow_var_name = row[jsh.map.code_val];
|
|
208
|
+
code_wkflow_var_name[(row.wkflow_var_name||'').toString().toUpperCase()] = row;
|
|
209
|
+
});
|
|
210
|
+
//Process workflow variables
|
|
211
|
+
for(var i=0;i<rs_wkflow_var.length;i++){
|
|
212
|
+
let row = rs_wkflow_var[i];
|
|
213
|
+
var wkflow = wkflows[row.wkflow_id];
|
|
214
|
+
if(!wkflow) return process_cb(new Error('Workflow '+row.wkflow_id+' missing from recordset'));
|
|
215
|
+
var wkflow_var_name_ucase = (row.wkflow_var_name||'').toString().toUpperCase();
|
|
216
|
+
wkflow.var[wkflow_var_name_ucase] = row.wkflow_var_val;
|
|
217
|
+
}
|
|
218
|
+
function processTran(row){
|
|
219
|
+
var wkflow_tran = _.extend({}, row);
|
|
220
|
+
wkflow_tran.wkflow_def_state_tran_cond_type = (wkflow_tran.wkflow_def_state_tran_cond_type || '').toString().toUpperCase();
|
|
221
|
+
var tran_sql = '';
|
|
222
|
+
if(wkflow_tran.wkflow_def_state_tran_cond_type == 'SQL'){
|
|
223
|
+
tran_sql = wkflow_tran.wkflow_def_state_tran_cond_param;
|
|
224
|
+
}
|
|
225
|
+
else if(wkflow_tran.wkflow_def_state_tran_cond_type == 'TASKCMPL'){
|
|
226
|
+
tran_sql = [
|
|
227
|
+
'select task_id',
|
|
228
|
+
' from {schema}.task',
|
|
229
|
+
' inner join {schema}.task_def on task.task_def_id = task_def.task_def_id',
|
|
230
|
+
" where wkflow_id=@wkflow_id and task_def_code=@task_def_code and task_sts='COMPLETE';"
|
|
231
|
+
].join(' ');
|
|
232
|
+
tran_sql = Helper.ReplaceAll(tran_sql, '@task_def_code', "'" + db.sql.escape(wkflow_tran.wkflow_def_state_tran_cond_param||'') + "'");
|
|
233
|
+
}
|
|
234
|
+
if(row.wkflow_id) tran_sql = Helper.ReplaceAll(tran_sql, '@wkflow_id', db.sql.escape(parseInt(row.wkflow_id)));
|
|
235
|
+
wkflow_tran.sql = tran_sql;
|
|
236
|
+
wkflow_tran.id = wkflow_trans.length;
|
|
237
|
+
return wkflow_tran;
|
|
238
|
+
}
|
|
239
|
+
//Process state transitions
|
|
240
|
+
for(let i=0;i<rs_wkflow_tran.length;i++){
|
|
241
|
+
let row = rs_wkflow_tran[i];
|
|
242
|
+
var wkflow = wkflows[row.wkflow_id];
|
|
243
|
+
if(!wkflow) return process_cb(new Error('Workflow '+row.wkflow_id+' missing from recordset'));
|
|
244
|
+
if(row.wkflow_def_state_id_dst){
|
|
245
|
+
if(!wkflow.tran[row.wkflow_def_state_id_dst]){
|
|
246
|
+
wkflow.tran[row.wkflow_def_state_id_dst] = [];
|
|
247
|
+
}
|
|
248
|
+
let wkflow_tran = processTran(rs_wkflow_tran[i]);
|
|
249
|
+
for(var key in code_wkflow_var_name){
|
|
250
|
+
wkflow_tran.sql = Helper.ReplaceAll(wkflow_tran.sql, '@wkflow_var_' + code_wkflow_var_name[key].wkflow_var_name, "'" + db.sql.escape(wkflow.var[key]||'') + "'");
|
|
251
|
+
}
|
|
252
|
+
wkflow.tran[row.wkflow_def_state_id_dst].push(wkflow_tran);
|
|
253
|
+
wkflow_trans.push(wkflow_tran);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//Process start transitions
|
|
257
|
+
for(let i=0;i<rs_wkflow_tran_start.length;i++){
|
|
258
|
+
let row = rs_wkflow_tran_start[i];
|
|
259
|
+
if(row.wkflow_def_state_id_dst){
|
|
260
|
+
let wkflow_tran = processTran(rs_wkflow_tran_start[i]);
|
|
261
|
+
wkflow_trans_start.push(wkflow_tran);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return process_cb();
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
//Cancel invalid workflows
|
|
269
|
+
function(process_cb){
|
|
270
|
+
if(!hasInvalidWorkflows) return process_cb();
|
|
271
|
+
jsh.Log.info('Workflow canceled');
|
|
272
|
+
let sql = [
|
|
273
|
+
"update {schema}.wkflow set wkflow_sts='CANCELED'",
|
|
274
|
+
" where wkflow_sts='ACTIVE' and wkflow_id in (",
|
|
275
|
+
' select w.wkflow_id',
|
|
276
|
+
' from {schema}.wkflow w',
|
|
277
|
+
' left outer join {schema}.wkflow_def wd on wd.wkflow_def_id = w.wkflow_def_id',
|
|
278
|
+
' left outer join {schema}.wkflow_def_state wsd on wsd.wkflow_def_state_id = w.wkflow_def_state_id',
|
|
279
|
+
" where wkflow_sts='ACTIVE' and (",
|
|
280
|
+
" (wkflow_def_sts is null or wkflow_def_sts <> 'ACTIVE') or",
|
|
281
|
+
" (wkflow_def_state_sts is null or wkflow_def_state_sts <> 'ACTIVE')",
|
|
282
|
+
' )',
|
|
283
|
+
' )',
|
|
284
|
+
].join(' ');
|
|
285
|
+
appsrv.ExecCommand('wkflow', module.replaceSchema(sql), [], {}, function (err) {
|
|
286
|
+
if(err){ err.sql = sql; return process_cb(err); }
|
|
287
|
+
return process_cb();
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
//Complete terminal workflows
|
|
292
|
+
function(process_cb){
|
|
293
|
+
if(!hasTerminalWorkflows) return process_cb();
|
|
294
|
+
jsh.Log.info('Workflow complete');
|
|
295
|
+
let sql = [
|
|
296
|
+
"update {schema}.wkflow set wkflow_sts='COMPLETE'",
|
|
297
|
+
" where wkflow_sts='ACTIVE' and wkflow_tran_sts='COMPLETE' and wkflow_id in (",
|
|
298
|
+
' select w.wkflow_id',
|
|
299
|
+
' from {schema}.wkflow w',
|
|
300
|
+
' left outer join {schema}.wkflow_def wd on wd.wkflow_def_id = w.wkflow_def_id',
|
|
301
|
+
' left outer join {schema}.wkflow_def_state wsd on wsd.wkflow_def_state_id = w.wkflow_def_state_id',
|
|
302
|
+
" where wkflow_sts='ACTIVE' and wkflow_state_def_type = 'TERMINAL'",
|
|
303
|
+
' )',
|
|
304
|
+
].join(' ');
|
|
305
|
+
appsrv.ExecCommand('wkflow', module.replaceSchema(sql), [], {}, function (err) {
|
|
306
|
+
if(err){ err.sql = sql; return process_cb(err); }
|
|
307
|
+
return process_cb();
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
//Evaluate Pending Conditions
|
|
312
|
+
function(process_cb){
|
|
313
|
+
if(_.isEmpty(wkflows)) return process_cb();
|
|
314
|
+
|
|
315
|
+
//Add Starting States
|
|
316
|
+
_.each(wkflows, function(wkflow){
|
|
317
|
+
if(wkflow.wkflow_tran_sts == 'PENDING'){
|
|
318
|
+
wkflow.next_state_id = wkflow.wkflow_def_state_id;
|
|
319
|
+
wkflow_next[wkflow.wkflow_id] = wkflow;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
//Evaluate Conditions
|
|
324
|
+
let sql = '';
|
|
325
|
+
_.each(wkflow_trans, function(wkflow_tran){
|
|
326
|
+
var sql_stmt = wkflow_tran.sql.trim();
|
|
327
|
+
if(!sql_stmt) sql_stmt = 'select 1 where 0=1';
|
|
328
|
+
if(!Helper.endsWith(sql_stmt, ';')) sql_stmt += ';';
|
|
329
|
+
sql += sql_stmt;
|
|
330
|
+
});
|
|
331
|
+
appsrv.ExecMultiRecordset('wkflow', module.replaceSchema(sql), [], {}, function (err, rslt) {
|
|
332
|
+
if(err){ err.sql = sql; return process_cb(err); }
|
|
333
|
+
_.each(wkflows, function(wkflow){
|
|
334
|
+
for(var wkflow_def_state_id_dst in wkflow.tran){
|
|
335
|
+
_.each(wkflow.tran[wkflow_def_state_id_dst], function(wkflow_tran){
|
|
336
|
+
wkflow_tran.rslt = !!rslt[0][wkflow_tran.id].length;
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
_.each(wkflows, function(wkflow){
|
|
341
|
+
for(var wkflow_def_state_id_dst in wkflow.tran){
|
|
342
|
+
if(!wkflow.tran[wkflow_def_state_id_dst].length) continue;
|
|
343
|
+
var all_tran = true;
|
|
344
|
+
_.each(wkflow.tran[wkflow_def_state_id_dst], function(wkflow_tran){
|
|
345
|
+
if(!wkflow_tran.rslt) all_tran = false;
|
|
346
|
+
});
|
|
347
|
+
if(all_tran){
|
|
348
|
+
wkflow.next_state_id = wkflow_def_state_id_dst;
|
|
349
|
+
wkflow_next[wkflow.wkflow_id] = wkflow;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
return process_cb();
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
//Get actions for any state transitions
|
|
359
|
+
function(process_cb){
|
|
360
|
+
if(_.isEmpty(wkflow_next)) return process_cb();
|
|
361
|
+
let sql = [
|
|
362
|
+
'select',
|
|
363
|
+
' wdsa.wkflow_def_state_id,',
|
|
364
|
+
' wdsa.wkflow_def_state_action_type,',
|
|
365
|
+
' wdsa.wkflow_def_state_action_param',
|
|
366
|
+
' from {schema}.wkflow_def_state_action wdsa',
|
|
367
|
+
' where wdsa.wkflow_def_state_id in ('+_.map(wkflow_next, function(wkflow){ return db.sql.escape(parseInt(wkflow.next_state_id)); }).join(',')+") and wdsa.wkflow_def_state_action_sts='ACTIVE'",
|
|
368
|
+
].join(' ');
|
|
369
|
+
appsrv.ExecRecordset('wkflow', module.replaceSchema(sql), [], {}, function (err, rslt) {
|
|
370
|
+
if(err){ err.sql = sql; return process_cb(err); }
|
|
371
|
+
var wkflow_actions = {
|
|
372
|
+
//wkflow_def_state_id : { wkflow_def_state_action_type, wkflow_def_state_action_param }
|
|
373
|
+
};
|
|
374
|
+
_.each(rslt && rslt[0], function(row){
|
|
375
|
+
if(!(row.wkflow_def_state_id in wkflow_actions)) wkflow_actions[row.wkflow_def_state_id] = [];
|
|
376
|
+
row.wkflow_def_state_action_type = (row.wkflow_def_state_action_type || '').toString().toUpperCase();
|
|
377
|
+
wkflow_actions[row.wkflow_def_state_id].push(row);
|
|
378
|
+
});
|
|
379
|
+
_.each(wkflow_next, function(wkflow){
|
|
380
|
+
_.each(wkflow_actions[wkflow.next_state_id], function(wkflow_action){
|
|
381
|
+
var next_action = _.extend({}, wkflow_action);
|
|
382
|
+
var action_sql = '';
|
|
383
|
+
if(wkflow_action.wkflow_def_state_action_type == 'SQL'){
|
|
384
|
+
action_sql = wkflow_action.wkflow_def_state_action_param;
|
|
385
|
+
}
|
|
386
|
+
else if(wkflow_action.wkflow_def_state_action_type == 'NEWTASK'){
|
|
387
|
+
action_sql = [
|
|
388
|
+
'insert into {schema}.task(task_def_id, task_sts, wkflow_id, task_due)',
|
|
389
|
+
" select task_def_id, 'ACTIVE', @wkflow_id,",
|
|
390
|
+
' case',
|
|
391
|
+
' when (task_def_dur_type is null) or (task_def_dur is null) then null',
|
|
392
|
+
" when task_def_dur_type='CALDAYS' then $addDays(jsharmony."+module.transform.mapping['my_now']+'(),task_def_dur)',
|
|
393
|
+
" when task_def_dur_type='BUSDAYS' then $addBusinessDays(jsharmony."+module.transform.mapping['my_now']+'(),task_def_dur)',
|
|
394
|
+
' else null',
|
|
395
|
+
' end',
|
|
396
|
+
' from {schema}.task_def td',
|
|
397
|
+
" where td.task_def_sts='ACTIVE' and td.task_def_code = @task_def_code;",
|
|
398
|
+
].join(' ');
|
|
399
|
+
action_sql = Helper.ReplaceAll(action_sql, '@task_def_code', "'" + db.sql.escape(wkflow_action.wkflow_def_state_action_param||'') + "'");
|
|
400
|
+
}
|
|
401
|
+
action_sql = Helper.ReplaceAll(action_sql, '@wkflow_id', db.sql.escape(parseInt(wkflow.wkflow_id)));
|
|
402
|
+
for(var key in code_wkflow_var_name){
|
|
403
|
+
action_sql = Helper.ReplaceAll(action_sql, '@wkflow_var_' + code_wkflow_var_name[key].wkflow_var_name, "'" + db.sql.escape(wkflow.var[key]||'') + "'");
|
|
404
|
+
}
|
|
405
|
+
next_action.sql = action_sql;
|
|
406
|
+
wkflow.next_state_actions.push(next_action);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
return process_cb();
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
//Execute state transitions
|
|
414
|
+
function(process_cb){
|
|
415
|
+
if(_.isEmpty(wkflow_next)) return process_cb();
|
|
416
|
+
|
|
417
|
+
async.eachSeries(wkflow_next, function(wkflow, wkflow_cb){
|
|
418
|
+
jsh.Log.info('Workflow '+(wkflow.wkflow_id||'').toString()+' transitioning to state '+(wkflow.next_state_id||'').toString());
|
|
419
|
+
let sql = "update {schema}.wkflow set wkflow_def_state_id=@wkflow_def_state_id, wkflow_tran_sts='COMPLETE' where wkflow_id=@wkflow_id;";
|
|
420
|
+
let sql_ptypes = [dbtypes.BigInt, dbtypes.BigInt];
|
|
421
|
+
let sql_params = { wkflow_id: wkflow.wkflow_id, wkflow_def_state_id: wkflow.next_state_id };
|
|
422
|
+
_.each(wkflow.next_state_actions, function(next_action){
|
|
423
|
+
var sql_stmt = next_action.sql.trim();
|
|
424
|
+
if(!sql_stmt) return;
|
|
425
|
+
if(!Helper.endsWith(sql_stmt, ';')) sql_stmt += ';';
|
|
426
|
+
sql += sql_stmt;
|
|
427
|
+
});
|
|
428
|
+
appsrv.ExecCommandTrans('wkflow', module.replaceSchema(sql), sql_ptypes, sql_params, function (err) {
|
|
429
|
+
if(err){ err.sql = sql; jsh.Log.error(err); }
|
|
430
|
+
return wkflow_cb();
|
|
431
|
+
});
|
|
432
|
+
}, process_cb);
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
//Create new workflows, where appropriate
|
|
436
|
+
function(process_cb){
|
|
437
|
+
if(_.isEmpty(wkflows)) return process_cb();
|
|
438
|
+
|
|
439
|
+
var newWorkflows = 0;
|
|
440
|
+
async.eachSeries(wkflow_trans_start, function(wkflow_tran, wkflow_tran_cb){
|
|
441
|
+
let sql = (wkflow_tran.sql||'').trim();
|
|
442
|
+
if(!sql) return wkflow_tran_cb();
|
|
443
|
+
if(newWorkflows > module.Config.maxNewWorkflowsPerCycle) return wkflow_tran_cb();
|
|
444
|
+
appsrv.ExecRecordset('wkflow', module.replaceSchema(sql), [], {}, function (err, rslt) {
|
|
445
|
+
if(err){ err.sql = sql; jsh.Log.error(err); return wkflow_tran_cb(); }
|
|
446
|
+
if(rslt && rslt[0] && rslt[0].length) jsh.Log.info('Creating new "'+wkflow_tran.wkflow_def_code + '" workflows ('+rslt[0].length+' found)');
|
|
447
|
+
async.eachSeries(rslt && rslt[0], function(row, row_cb){
|
|
448
|
+
if(!row) return row_cb();
|
|
449
|
+
if(newWorkflows >= module.Config.maxNewWorkflowsPerCycle) return row_cb();
|
|
450
|
+
newWorkflows++;
|
|
451
|
+
var dbtasks = {};
|
|
452
|
+
var dbtaskid = 0;
|
|
453
|
+
dbtasks['wkflow'] = function (dbtrans, db_callback, transtbl) {
|
|
454
|
+
let sql = [
|
|
455
|
+
'$getInsertKey(wkflow, wkflow_id, ',
|
|
456
|
+
" insert into {schema}.wkflow(wkflow_def_id, wkflow_def_state_id, wkflow_sts, wkflow_tran_sts) values (@wkflow_def_id, @wkflow_def_state_id_dst, 'ACTIVE', 'PENDING')",
|
|
457
|
+
');',
|
|
458
|
+
].join(' ');
|
|
459
|
+
let sql_ptypes = [
|
|
460
|
+
dbtypes.BigInt,
|
|
461
|
+
dbtypes.BigInt,
|
|
462
|
+
];
|
|
463
|
+
let sql_params = {
|
|
464
|
+
wkflow_def_id: wkflow_tran.wkflow_def_id,
|
|
465
|
+
wkflow_def_state_id_dst: wkflow_tran.wkflow_def_state_id_dst,
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
db.Scalar('wkflow', module.replaceSchema(sql), sql_ptypes, sql_params, dbtrans, function (err, rslt) {
|
|
469
|
+
if (err) { err.sql = sql; }
|
|
470
|
+
db_callback(err, rslt);
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
var wkflow_var = {};
|
|
474
|
+
for(var key in code_wkflow_var_name){
|
|
475
|
+
var wkflow_var_name = code_wkflow_var_name[key].wkflow_var_name;
|
|
476
|
+
if(!wkflow_var_name) continue;
|
|
477
|
+
var wkflow_var_sqlparam = 'wkflow_var_'+wkflow_var_name;
|
|
478
|
+
var wkflow_var_val = row[wkflow_var_sqlparam];
|
|
479
|
+
if(!wkflow_var_val) continue;
|
|
480
|
+
wkflow_var[wkflow_var_name] = wkflow_var_val;
|
|
481
|
+
//Workflow Variables
|
|
482
|
+
dbtasks['wkflow_var_'+(++dbtaskid).toString()] = function (dbtrans, db_callback, transtbl) {
|
|
483
|
+
let sql = [
|
|
484
|
+
'insert into {schema}.wkflow_var(wkflow_id, wkflow_var_name, wkflow_var_val) values(@wkflow_id, @wkflow_var_name, @wkflow_var_val)'
|
|
485
|
+
].join(' ');
|
|
486
|
+
let sql_ptypes = [
|
|
487
|
+
dbtypes.BigInt,
|
|
488
|
+
dbtypes.VarChar(32),
|
|
489
|
+
dbtypes.VarChar(255),
|
|
490
|
+
];
|
|
491
|
+
let sql_params = {
|
|
492
|
+
wkflow_id: transtbl.wkflow,
|
|
493
|
+
wkflow_var_name: wkflow_var_name,
|
|
494
|
+
wkflow_var_val: wkflow_var_val,
|
|
495
|
+
};
|
|
496
|
+
db.Command('wkflow', module.replaceSchema(sql), sql_ptypes, sql_params, dbtrans, function (err, rslt) {
|
|
497
|
+
if (err) { err.sql = sql; }
|
|
498
|
+
db_callback(err, rslt);
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
jsh.Log.info('Creating Workflow "'+wkflow_tran.wkflow_def_code + '"' + (!_.isEmpty(wkflow_var) ? ' with '+JSON.stringify(wkflow_var).toString() : ''));
|
|
503
|
+
db.ExecTransTasks(dbtasks, function (err, rslt) {
|
|
504
|
+
if (err != null) { jsh.Log.error(err); return row_cb(); }
|
|
505
|
+
return row_cb();
|
|
506
|
+
});
|
|
507
|
+
}, wkflow_tran_cb);
|
|
508
|
+
});
|
|
509
|
+
}, process_cb);
|
|
510
|
+
},
|
|
511
|
+
], function(err){
|
|
512
|
+
if(err) jsh.Log.error(err);
|
|
513
|
+
var rerun = !_.isEmpty(wkflow_next);
|
|
514
|
+
return callback(err, rerun);
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 apHarmony
|
|
3
|
+
|
|
4
|
+
This file is part of jsHarmony.
|
|
5
|
+
|
|
6
|
+
jsHarmony is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
jsHarmony is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with this package. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
var _ = require('lodash');
|
|
20
|
+
|
|
21
|
+
var transform = { };
|
|
22
|
+
|
|
23
|
+
transform.tables = {
|
|
24
|
+
'code_wkflow_def_sts': 'ucod_wkflow_def_sts',
|
|
25
|
+
'code_wkflow_def_state_sts': 'ucod_wkflow_def_state_sts',
|
|
26
|
+
'code_wkflow_def_state_type': 'ucod_wkflow_def_state_type',
|
|
27
|
+
'code_wkflow_def_state_tran_sts': 'ucod_wkflow_def_state_tran_sts',
|
|
28
|
+
'code_wkflow_def_state_tran_cond_type': 'ucod_wkflow_def_state_tran_cond_type',
|
|
29
|
+
'code_wkflow_def_state_action_sts': 'ucod_wkflow_def_state_action_sts',
|
|
30
|
+
'code_wkflow_def_state_action_type': 'ucod_wkflow_def_state_action_type',
|
|
31
|
+
'code_wkflow_sts': 'ucod_wkflow_sts',
|
|
32
|
+
'code_wkflow_tran_sts': 'ucod_wkflow_tran_sts',
|
|
33
|
+
'code_wkflow_var_name': 'ucod_wkflow_var_name',
|
|
34
|
+
'code_task_sts': 'ucod_task_sts',
|
|
35
|
+
'code_task_def_sts': 'ucod_task_def_sts',
|
|
36
|
+
'code_task_def_dur_type': 'ucod_task_def_dur_type',
|
|
37
|
+
'code_sys': 'ucod_h',
|
|
38
|
+
'sys_func': 'sf',
|
|
39
|
+
'sys_user_func': 'spef',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
transform.fields = {
|
|
43
|
+
'menu_name': 'sm_name',
|
|
44
|
+
'menu_desc': 'sm_desc',
|
|
45
|
+
'menu_cmd': 'sm_cmd',
|
|
46
|
+
'menu_subcmd': 'sm_subcmd',
|
|
47
|
+
'menu_seq': 'sm_seq',
|
|
48
|
+
'menu_id': 'sm_id',
|
|
49
|
+
'menu_parent_name': 'sm_parent_name',
|
|
50
|
+
|
|
51
|
+
'code_txt': 'codetxt',
|
|
52
|
+
'code_val': 'codeval',
|
|
53
|
+
'code_val1': 'codeval1',
|
|
54
|
+
'code_val2': 'codeval2',
|
|
55
|
+
'code_parent': 'codeparent',
|
|
56
|
+
'code_seq': 'codseq',
|
|
57
|
+
'code_icon': 'codeicon',
|
|
58
|
+
'code_id': 'codeid',
|
|
59
|
+
'code_parent_id': 'codeparentid',
|
|
60
|
+
'code_type': 'codetype',
|
|
61
|
+
'code_code': 'codecode',
|
|
62
|
+
'code_end_dt': 'codetdt',
|
|
63
|
+
|
|
64
|
+
'my_sys_user_id': 'mype',
|
|
65
|
+
'sys_user_id': 'pe_id',
|
|
66
|
+
'sys_func_name': 'sf_name',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
transform.sql = {
|
|
70
|
+
'my_db_user_fmt': 'mycuser_fmt',
|
|
71
|
+
'my_now': 'mynow',
|
|
72
|
+
'my_sys_user_id': 'mype',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
var baseTransform = new (require('./jsHarmonyWorkflowTransform.js'))();
|
|
76
|
+
for(var transformType in transform){
|
|
77
|
+
var keys = _.keys(transform[transformType]);
|
|
78
|
+
_.each(keys, function(key){
|
|
79
|
+
if(!(key in baseTransform[transformType])) delete transform[transformType][key];
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
exports = module.exports = transform;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 apHarmony
|
|
3
|
+
|
|
4
|
+
This file is part of jsHarmony.
|
|
5
|
+
|
|
6
|
+
jsHarmony is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
jsHarmony is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with this package. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
var jsHarmonyModuleTransform = require('jsharmony/jsHarmonyModuleTransform');
|
|
21
|
+
|
|
22
|
+
function jsHarmonyWorkflowTransform(module){
|
|
23
|
+
this.tables = {
|
|
24
|
+
'code_wkflow_def_sts': 'code_wkflow_def_sts',
|
|
25
|
+
'code_wkflow_def_state_sts': 'code_wkflow_def_state_sts',
|
|
26
|
+
'code_wkflow_def_state_type': 'code_wkflow_def_state_type',
|
|
27
|
+
'code_wkflow_def_state_tran_sts': 'code_wkflow_def_state_tran_sts',
|
|
28
|
+
'code_wkflow_def_state_tran_cond_type': 'code_wkflow_def_state_tran_cond_type',
|
|
29
|
+
'code_wkflow_def_state_action_sts': 'code_wkflow_def_state_action_sts',
|
|
30
|
+
'code_wkflow_def_state_action_type': 'code_wkflow_def_state_action_type',
|
|
31
|
+
'code_wkflow_sts': 'code_wkflow_sts',
|
|
32
|
+
'code_wkflow_tran_sts': 'code_wkflow_tran_sts',
|
|
33
|
+
'code_wkflow_var_name': 'code_wkflow_var_name',
|
|
34
|
+
'code_task_sts': 'code_task_sts',
|
|
35
|
+
'code_task_def_sts': 'code_task_def_sts',
|
|
36
|
+
'code_task_def_dur_type': 'code_task_def_dur_type',
|
|
37
|
+
'code_sys': 'code',
|
|
38
|
+
'sys_func': 'sys_func',
|
|
39
|
+
'sys_user_func': 'sys_user_func',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
this.fields = {
|
|
43
|
+
'menu_name': 'menu_name',
|
|
44
|
+
'menu_desc': 'menu_desc',
|
|
45
|
+
'menu_cmd': 'menu_cmd',
|
|
46
|
+
'menu_subcmd': 'menu_subcmd',
|
|
47
|
+
'menu_seq': 'menu_seq',
|
|
48
|
+
'menu_id': 'menu_id',
|
|
49
|
+
'menu_parent_name': 'menu_parent_name',
|
|
50
|
+
|
|
51
|
+
'code_txt': 'code_txt',
|
|
52
|
+
'code_val': 'code_val',
|
|
53
|
+
'code_val1': 'code_val1',
|
|
54
|
+
'code_val2': 'code_val2',
|
|
55
|
+
'code_parent': 'code_parent',
|
|
56
|
+
'code_seq': 'code_seq',
|
|
57
|
+
'code_icon': 'code_icon',
|
|
58
|
+
'code_id': 'code_id',
|
|
59
|
+
'code_parent_id': 'code_parent_id',
|
|
60
|
+
'code_type': 'code_type',
|
|
61
|
+
'code_code': 'code_code',
|
|
62
|
+
'code_end_dt': 'code_end_dt',
|
|
63
|
+
|
|
64
|
+
'my_sys_user_id': 'my_sys_user_id',
|
|
65
|
+
'sys_user_id': 'sys_user_id',
|
|
66
|
+
'sys_func_name': 'sys_func_name',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
this.sql = {
|
|
70
|
+
'my_db_user_fmt': 'my_db_user_fmt',
|
|
71
|
+
'my_now': 'my_now',
|
|
72
|
+
'my_sys_user_id': 'my_sys_user_id',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
jsHarmonyModuleTransform.call(this, module);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
jsHarmonyWorkflowTransform.prototype = new jsHarmonyModuleTransform();
|
|
79
|
+
|
|
80
|
+
exports = module.exports = jsHarmonyWorkflowTransform;
|
package/lint.cmd
ADDED