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.
Files changed (37) hide show
  1. package/.eslintrc.js +47 -0
  2. package/.eslintrc_models.js +64 -0
  3. package/LICENSE +165 -0
  4. package/README.md +29 -0
  5. package/index.js +20 -0
  6. package/jsHarmonyWorkflow.js +55 -0
  7. package/jsHarmonyWorkflowConfig.js +43 -0
  8. package/jsHarmonyWorkflowProcessor.js +517 -0
  9. package/jsHarmonyWorkflowTransform.Classic.js +83 -0
  10. package/jsHarmonyWorkflowTransform.js +80 -0
  11. package/lint.cmd +2 -0
  12. package/models/Admin/TaskDef.json +26 -0
  13. package/models/Admin/TaskDef_Func.json +21 -0
  14. package/models/Admin/TaskDef_Listing.json +28 -0
  15. package/models/Admin/TaskDef_Tabs.json +19 -0
  16. package/models/Admin/WkflowDef.json +25 -0
  17. package/models/Admin/WkflowDef_Listing.json +26 -0
  18. package/models/Admin/WkflowDef_State.json +26 -0
  19. package/models/Admin/WkflowDef_State_Action.json +25 -0
  20. package/models/Admin/WkflowDef_State_Action_Listing.json +25 -0
  21. package/models/Admin/WkflowDef_State_Listing.json +45 -0
  22. package/models/Admin/WkflowDef_State_Tabs.json +20 -0
  23. package/models/Admin/WkflowDef_State_Tran.json +28 -0
  24. package/models/Admin/WkflowDef_State_Tran_Listing.json +27 -0
  25. package/models/Admin/WkflowDef_Tabs.json +20 -0
  26. package/models/Admin/WkflowDef_Tran_Start.json +28 -0
  27. package/models/Admin/WkflowDef_Tran_Start_Listing.json +34 -0
  28. package/models/Wkflow/Task.json +27 -0
  29. package/models/Wkflow/Task_Listing.json +45 -0
  30. package/models/Wkflow/Task_Tabs.json +18 -0
  31. package/models/Wkflow/Wkflow.json +31 -0
  32. package/models/Wkflow/Wkflow_Listing.json +38 -0
  33. package/models/Wkflow/Wkflow_Tabs.json +18 -0
  34. package/models/_config.json +48 -0
  35. package/models/sql/objects/task.json +152 -0
  36. package/models/sql/objects/wkflow.json +383 -0
  37. 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
@@ -0,0 +1,2 @@
1
+ call eslint --fix *.js
2
+ call eslint --fix models\_*.js